diff --git a/Cargo.lock b/Cargo.lock index f55daf9e01c5f..e2df1f05ed3cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4509,6 +4509,7 @@ dependencies = [ "rustc_middle", "rustc_session", "rustc_span", + "rustc_symbol_mangling", "rustc_target", "serde", "serde_json", diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 2218780092287..521043e3f6c1a 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -1132,6 +1132,11 @@ impl LayoutCalculator { // If `-Z randomize-layout` was enabled for the type definition we can shuffle // the field ordering to try and catch some code making assumptions about layouts // we don't guarantee. + // In the future, we might do more than shuffle field order (e.g. introduce extra padding), + // but never for `repr(Rust)` structs with only zero-sized fields, single-variant + // `repr(Rust)` enums with only zero-sized fields, or zero-variant `repr(Rust)` enums, + // which must remain zero-sized as per T-lang decisions in + // https://github.com/rust-lang/reference/pull/2262 and https://github.com/rust-lang/reference/pull/2293 if repr.can_randomize_type_layout() && cfg!(feature = "randomize") { #[cfg(feature = "randomize")] { diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 9ca4aa2254547..c5d8d758c4733 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -284,6 +284,27 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { found } + /// Whether this type/layout has any padding that is dependent on a variant, i.e. has bytes that + /// are padding for some, but not all, valid values of this type. + pub fn has_variant_dependent_padding(&self, cx: &C) -> bool + where + Ty: TyAbiInterface<'a, C> + Copy, + { + match self.variants { + Variants::Multiple { .. } => true, + Variants::Empty => false, + Variants::Single { .. } => match &self.fields { + FieldsShape::Primitive | FieldsShape::Union(_) => false, + FieldsShape::Array { count, .. } => { + *count > 0 && self.field(cx, 0).has_variant_dependent_padding(cx) + } + FieldsShape::Arbitrary { offsets, .. } => { + (0..offsets.len()).any(|i| self.field(cx, i).has_variant_dependent_padding(cx)) + } + }, + } + } + /// The ranges of bytes that are always ignored by the representation relation of this type. /// /// In other words, for any sequence of bytes, if we reset the these padding bytes to uninit, @@ -291,7 +312,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { /// This is the "guaranteed" padding. There may be more bytes that are padding for some /// but not all variants of this type; those are not included. /// (E.g. `Option` has no guaranteed padding so the empty range set is returned, but its `None` value still has padding). - pub fn padding_ranges(&self, cx: &C) -> Vec> + pub fn variant_independent_padding_ranges(&self, cx: &C) -> Vec> where Ty: TyAbiInterface<'a, C> + Copy, { @@ -316,6 +337,45 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { uninit_ranges } + /// The ranges of bytes that are ignored by the representation relation of this variant. + /// + /// The result does not include variant-independent padding. + pub fn variant_dependent_padding_ranges( + &self, + cx: &C, + variant_index: VariantIdx, + ) -> Vec> + where + Ty: TyAbiInterface<'a, C> + Copy, + { + let Variants::Multiple { .. } = self.variants else { + return Vec::new(); + }; + + // Bytes that are data in some variant. + let mut any = RangeSet::new(); + self.add_data_ranges(cx, Size::ZERO, &mut any); + + // Bytes that are data in this variant. + let mut this = RangeSet::new(); + + // The variants do not contain e.g. the discriminant or coroutine upvars. + let FieldsShape::Arbitrary { offsets, in_memory_order: _ } = &self.fields else { + unreachable!("a multi-variant layout should have `Arbitrary` fields") + }; + + // So add them explicitly. + for (field, &offset) in offsets.iter_enumerated() { + let field = self.field(cx, field.as_usize()); + field.add_data_ranges(cx, offset, &mut this); + } + + self.for_variant(cx, variant_index).add_data_ranges(cx, Size::ZERO, &mut this); + + // Padding specific to this variant: data in some variant, but not in this one. + any.difference(&this).0.iter().map(|&(offset, size)| offset..offset + size).collect() + } + /// Extend `out` with all ranges of bytes that *may* carry relevant data for values of this type. /// For enums and unions there are offsets that are initialized for some /// variants but not for others; those offset *will* get added to `out`. @@ -327,39 +387,42 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { return; } - match &self.variants { - Variants::Empty => { /* done */ } - Variants::Single { index: _ } => match &self.fields { - FieldsShape::Primitive => { - out.add_range(base_offset, self.size); - } - &FieldsShape::Union(field_count) => { - for field in 0..field_count.get() { - let field = self.field(cx, field); - field.add_data_ranges(cx, base_offset, out); - } + // Visit the fields of this value. For enum values the fields include the discriminant. + match &self.fields { + FieldsShape::Primitive => { + out.add_range(base_offset, self.size); + } + &FieldsShape::Union(field_count) => { + for field in 0..field_count.get() { + let field = self.field(cx, field); + field.add_data_ranges(cx, base_offset, out); } - &FieldsShape::Array { stride, count } => { - let elem = self.field(cx, 0); - - // For scalars we know there is no padding between the elements, - // so the entire array is a single big data range. - if elem.backend_repr.is_scalar() { - out.add_range(base_offset, elem.size * count); - } else { - // FIXME: this is really inefficient for large arrays. - for idx in 0..count { - elem.add_data_ranges(cx, base_offset + idx * stride, out); - } + } + &FieldsShape::Array { stride, count } => { + let elem = self.field(cx, 0); + + // For scalars we know there is no padding between the elements, + // so the entire array is a single big data range. + if elem.backend_repr.is_scalar() { + out.add_range(base_offset, elem.size * count); + } else { + // FIXME: this is really inefficient for large arrays. + for idx in 0..count { + elem.add_data_ranges(cx, base_offset + idx * stride, out); } } - FieldsShape::Arbitrary { offsets, in_memory_order: _ } => { - for (field, &offset) in offsets.iter_enumerated() { - let field = self.field(cx, field.as_usize()); - field.add_data_ranges(cx, base_offset + offset, out); - } + } + FieldsShape::Arbitrary { offsets, in_memory_order: _ } => { + for (field, &offset) in offsets.iter_enumerated() { + let field = self.field(cx, field.as_usize()); + field.add_data_ranges(cx, base_offset + offset, out); } - }, + } + } + + // Visit the fields of each variant. + match &self.variants { + Variants::Empty | Variants::Single { index: _ } => { /* done */ } Variants::Multiple { variants, .. } => { for variant in variants.indices() { let variant = self.for_variant(cx, variant); diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index bff4c9bdf47ef..679523341c7e3 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -90,6 +90,10 @@ bitflags! { /// If true, the type's crate has opted into layout randomization. /// Other flags can still inhibit reordering and thus randomization. /// The seed stored in `ReprOptions.field_shuffle_seed`. + /// + /// `repr(Rust)` structs with only zero-sized fields, single-variant `repr(Rust)` enums with only + /// zero-sized fields, and zero-variant `repr(Rust)` enums must remain zero-sized as per + /// T-lang decisions in https://github.com/rust-lang/reference/pull/2262 and https://github.com/rust-lang/reference/pull/2293 const RANDOMIZE_LAYOUT = 1 << 4; /// If true, the type is always passed indirectly by non-Rustic ABIs. /// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details. diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index fe1da820cf74f..a777cbc5f9a38 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1202,6 +1202,48 @@ impl<'a> AstValidator<'a> { self.visit_vis(vis); self.visit_ident(ident); } + + // Check EII implementation attributes against an allowlist. + fn check_eii_impl_attrs(&self, attrs: &[Attribute], eii_impls: &[EiiImpl]) { + if eii_impls.is_empty() { + return; + } + + let allowed_attrs: &[Symbol] = &[ + sym::allow, + sym::warn, + sym::deny, + sym::forbid, + sym::expect, + sym::doc, + sym::inline, + sym::cold, + sym::optimize, + sym::coverage, + sym::sanitize, + sym::must_use, + sym::deprecated, + ]; + + for attr in attrs { + let AttrKind::Normal(normal) = &attr.kind else { + continue; + }; + if attr.has_any_name(allowed_attrs) { + continue; + } + + let attr_name = pprust::path_to_string(&normal.item.path); + for eii_impl in eii_impls { + self.dcx().emit_err(diagnostics::EiiImplAttributeNotSupported { + attr_span: attr.span, + attr_name: &attr_name, + eii_span: eii_impl.span, + eii_name: pprust::path_to_string(&eii_impl.eii_macro_path), + }); + } + } + } } /// Checks that generic parameters are in the correct order, @@ -1391,6 +1433,7 @@ impl Visitor<'_> for AstValidator<'_> { for EiiImpl { eii_macro_path, .. } in eii_impls { self.visit_path(eii_macro_path); } + self.check_eii_impl_attrs(&item.attrs, eii_impls); let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic)); if body.is_none() && !is_intrinsic && !self.is_sdylib_interface { @@ -1566,8 +1609,9 @@ impl Visitor<'_> for AstValidator<'_> { visit::walk_item(self, item); } - ItemKind::Static(StaticItem { expr, safety, .. }) => { + ItemKind::Static(StaticItem { expr, safety, eii_impls, .. }) => { self.check_item_safety(item.span, *safety); + self.check_eii_impl_attrs(&item.attrs, eii_impls); if matches!(safety, Safety::Unsafe(_)) { self.dcx().emit_err(diagnostics::UnsafeStatic { span: item.span }); } diff --git a/compiler/rustc_ast_passes/src/diagnostics.rs b/compiler/rustc_ast_passes/src/diagnostics.rs index 0cf7ef5c1de19..5bcffe96f6086 100644 --- a/compiler/rustc_ast_passes/src/diagnostics.rs +++ b/compiler/rustc_ast_passes/src/diagnostics.rs @@ -189,6 +189,17 @@ pub(crate) struct FnParamForbiddenAttr { pub span: Span, } +#[derive(Diagnostic)] +#[diag("`#[{$eii_name}]` is not allowed to have `#[{$attr_name}]`")] +pub(crate) struct EiiImplAttributeNotSupported<'a> { + #[primary_span] + pub attr_span: Span, + pub attr_name: &'a str, + pub eii_name: String, + #[label("`#[{$eii_name}]` is not allowed to have `#[{$attr_name}]`")] + pub eii_span: Span, +} + #[derive(Diagnostic)] #[diag("`self` parameter is only allowed in associated functions")] #[note("associated functions are those in `impl` or `trait` definitions")] diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 769c008c13f75..a64545be57efe 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -1,12 +1,14 @@ //! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a //! standalone executable. +use std::fmt::Write as _; use std::io::Write; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::traits::{AsmCodegenMethods, GlobalAsmOperandRef}; +use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar as ConstScalar}; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers, @@ -107,29 +109,57 @@ fn codegen_global_asm_inner<'tcx>( InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span } => { use rustc_codegen_ssa::back::symbol_export::escape_symbol_name; match operands[operand_idx] { - GlobalAsmOperandRef::Const { ref string } => { - global_asm.push_str(string); - } - GlobalAsmOperandRef::SymFn { instance } => { - if cfg!(not(feature = "inline_asm_sym")) { - tcx.dcx().span_err( - span, - "asm! and global_asm! sym operands are not yet supported", - ); - } + GlobalAsmOperandRef::Const { value, ty } => { + match value { + ConstScalar::Int(int) => { + let string = rustc_codegen_ssa::common::asm_const_to_str( + tcx, + span, + int, + FullyMonomorphizedLayoutCx(tcx).layout_of(ty), + ); + global_asm.push_str(&string); + } - let symbol = tcx.symbol_name(instance); - let symbol_name = if tcx.sess.target.is_like_darwin { - format!("_{}", symbol.name) - } else { - symbol.name.to_owned() - }; + ConstScalar::Ptr(ptr, _) => { + if cfg!(not(feature = "inline_asm_sym")) { + tcx.dcx().span_err( + span, + "asm! and global_asm! sym operands are not yet supported", + ); + } - // FIXME handle the case where the function was made private to the - // current codegen unit - global_asm.push_str(&escape_symbol_name(tcx, &symbol_name, span)); + let (prov, offset) = ptr.prov_and_relative_offset(); + let global_alloc = tcx.global_alloc(prov.alloc_id()); + let symbol = match global_alloc { + GlobalAlloc::Function { instance } => { + // FIXME handle the case where the function was made private to the + // current codegen unit + tcx.symbol_name(instance) + } + GlobalAlloc::Static(def_id) => { + let instance = Instance::mono(tcx, def_id); + tcx.symbol_name(instance) + } + GlobalAlloc::Memory(_) + | GlobalAlloc::VTable(..) + | GlobalAlloc::TypeId { .. } => unreachable!(), + }; + let symbol_name = if tcx.sess.target.is_like_darwin { + format!("_{}", symbol.name) + } else { + symbol.name.to_owned() + }; + global_asm.push_str(&escape_symbol_name(tcx, &symbol_name, span)); + + if offset != Size::ZERO { + let offset = tcx.sign_extend_to_target_isize(offset.bytes()); + write!(global_asm, "{offset:+}").unwrap(); + } + } + } } - GlobalAsmOperandRef::SymStatic { def_id } => { + GlobalAsmOperandRef::SymThreadLocalStatic { def_id } => { if cfg!(not(feature = "inline_asm_sym")) { tcx.dcx().span_err( span, diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 1c6888507b65f..03fd11afa3f10 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -96,10 +96,18 @@ pub(crate) fn codegen_inline_asm_terminator<'tcx>( } InlineAsmOperand::Const { ref value } => { let (const_value, ty) = crate::constant::eval_mir_constant(fx, value); + let mir::ConstValue::Scalar(scalar) = const_value else { + span_bug!( + span, + "expected Scalar for promoted asm const, but got {:#?}", + const_value + ) + }; + let value = rustc_codegen_ssa::common::asm_const_to_str( fx.tcx, span, - const_value, + scalar.assert_scalar_int(), fx.layout_of(ty), ); CInlineAsmOperand::Const { value } diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 6fd7188f656c5..23aeb1e06463c 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -1,8 +1,10 @@ // cSpell:ignoreRegExp [afkspqvwy]reg use std::borrow::Cow; +use std::fmt::Write; use gccjit::{LValue, RValue, ToRValue, Type}; +use rustc_abi::Size; use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::mir::operand::OperandValue; use rustc_codegen_ssa::mir::place::PlaceRef; @@ -11,7 +13,9 @@ use rustc_codegen_ssa::traits::{ GlobalAsmOperandRef, InlineAsmOperandRef, }; use rustc_middle::bug; +use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::ty::Instance; +use rustc_middle::ty::layout::LayoutOf; use rustc_span::{DUMMY_SP, Span}; use rustc_target::asm::*; @@ -143,6 +147,9 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { // Clobbers collected from `out("explicit register") _` and `inout("explicit_reg") var => _` let mut clobbers = vec![]; + // Symbols name that needs to be inserted to asm const ptr template string. + let mut const_syms = vec![]; + // We're trying to preallocate space for the template let mut constants_len = 0; @@ -303,17 +310,12 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { } } - InlineAsmOperandRef::Const { ref string } => { - constants_len += string.len() + att_dialect as usize; + InlineAsmOperandRef::Const { .. } => { + // We don't know the size at this point, just some estimate. + constants_len += 20; } - InlineAsmOperandRef::SymFn { instance } => { - // FIXME(@Amanieu): Additional mangling is needed on - // some targets to add a leading underscore (Mach-O) - // or byte count suffixes (x86 Windows). - constants_len += self.tcx.symbol_name(instance).name.len(); - } - InlineAsmOperandRef::SymStatic { def_id } => { + InlineAsmOperandRef::SymThreadLocalStatic { def_id } => { // FIXME(@Amanieu): Additional mangling is needed on // some targets to add a leading underscore (Mach-O). constants_len += @@ -402,24 +404,22 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { // processed in the previous pass } - InlineAsmOperandRef::SymFn { instance } => { - inputs.push(AsmInOperand { - constraint: "X".into(), - rust_idx, - val: get_fn(self.cx, instance).get_address(None), - }); - } - - InlineAsmOperandRef::SymStatic { def_id } => { - inputs.push(AsmInOperand { - constraint: "X".into(), - rust_idx, - val: self.cx.get_static(def_id).get_address(None), - }); - } + InlineAsmOperandRef::Const { value, ty: _ } => match value { + Scalar::Int(_) => (), + Scalar::Ptr(ptr, _) => { + let (prov, _) = ptr.prov_and_relative_offset(); + let global_alloc = self.tcx.global_alloc(prov.alloc_id()); + let (val, sym) = self.cx.alloc_to_backend(global_alloc, true).unwrap(); + const_syms.push(sym.unwrap()); + inputs.push(AsmInOperand { constraint: "X".into(), rust_idx, val }); + } + }, - InlineAsmOperandRef::Const { .. } => { - // processed in the previous pass + InlineAsmOperandRef::SymThreadLocalStatic { def_id } => { + // FIXME(@Amanieu): Additional mangling is needed on + // some targets to add a leading underscore (MachO). + constants_len += + self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name.len(); } InlineAsmOperandRef::Label { .. } => { @@ -453,7 +453,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { template_str.push_str(escaped_char); } } - InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => { + InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } => { let mut push_to_template = |modifier, gcc_idx| { use std::fmt::Write; @@ -495,15 +495,37 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { push_to_template(modifier, gcc_index); } - InlineAsmOperandRef::SymFn { instance } => { - // FIXME(@Amanieu): Additional mangling is needed on - // some targets to add a leading underscore (Mach-O) - // or byte count suffixes (x86 Windows). - let name = self.tcx.symbol_name(instance).name; - template_str.push_str(name); + InlineAsmOperandRef::Const { value, ty } => { + match value { + Scalar::Int(int) => { + // Const operands get injected directly into the template + let string = rustc_codegen_ssa::common::asm_const_to_str( + self.tcx, + span, + int, + self.layout_of(ty), + ); + template_str.push_str(&string); + } + + Scalar::Ptr(ptr, _) => { + let (_, offset) = ptr.prov_and_relative_offset(); + let sym = const_syms.remove(0); + // FIXME(@Amanieu): Additional mangling is needed on + // some targets to add a leading underscore (Mach-O) + // or byte count suffixes (x86 Windows). + template_str.push_str(sym.name); + + if offset != Size::ZERO { + let offset = + self.sign_extend_to_target_isize(offset.bytes()); + write!(template_str, "{offset:+}").unwrap(); + } + } + } } - InlineAsmOperandRef::SymStatic { def_id } => { + InlineAsmOperandRef::SymThreadLocalStatic { def_id } => { // FIXME(@Amanieu): Additional mangling is needed on // some targets to add a leading underscore (Mach-O). let instance = Instance::mono(self.tcx, def_id); @@ -511,10 +533,6 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { template_str.push_str(name); } - InlineAsmOperandRef::Const { ref string } => { - template_str.push_str(string); - } - InlineAsmOperandRef::Label { label } => { let label_gcc_index = labels.iter().position(|&l| l == label).expect("wrong rust index"); @@ -933,26 +951,55 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { .unwrap_or(string.len()); } } - InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: _ } => { + InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span } => { match operands[operand_idx] { - GlobalAsmOperandRef::Const { ref string } => { - // Const operands get injected directly into the - // template. Note that we don't need to escape % - // here unlike normal inline assembly. - template_str.push_str(string); - } + GlobalAsmOperandRef::Const { value, ty } => { + match value { + Scalar::Int(int) => { + // Const operands get injected directly into the + // template. Note that we don't need to escape % + // here unlike normal inline assembly. + let string = rustc_codegen_ssa::common::asm_const_to_str( + self.tcx, + span, + int, + self.layout_of(ty), + ); + template_str.push_str(&string); + } - GlobalAsmOperandRef::SymFn { instance } => { - let function = get_fn(self, instance); - self.add_used_function(function); - // FIXME(@Amanieu): Additional mangling is needed on - // some targets to add a leading underscore (Mach-O) - // or byte count suffixes (x86 Windows). - let name = self.tcx.symbol_name(instance).name; - template_str.push_str(name); + Scalar::Ptr(ptr, _) => { + let (prov, offset) = ptr.prov_and_relative_offset(); + let global_alloc = self.tcx.global_alloc(prov.alloc_id()); + let symbol_name = match global_alloc { + GlobalAlloc::Function { instance } => { + let function = get_fn(self, instance); + self.add_used_function(function); + // FIXME(@Amanieu): Additional mangling is needed on + // some targets to add a leading underscore (Mach-O) + // or byte count suffixes (x86 Windows). + self.tcx.symbol_name(instance) + } + _ => { + let (_, syms) = + self.alloc_to_backend(global_alloc, true).unwrap(); + // FIXME(antoyo): set the global variable as used. + // FIXME(@Amanieu): Additional mangling is needed on + // some targets to add a leading underscore (Mach-O). + syms.unwrap() + } + }; + template_str.push_str(symbol_name.name); + + if offset != Size::ZERO { + let offset = + self.sign_extend_to_target_isize(offset.bytes()); + write!(template_str, "{offset:+}").unwrap(); + } + } + } } - - GlobalAsmOperandRef::SymStatic { def_id } => { + GlobalAsmOperandRef::SymThreadLocalStatic { def_id } => { // FIXME(antoyo): set the global variable as used. // FIXME(@Amanieu): Additional mangling is needed on // some targets to add a leading underscore (Mach-O). diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index e73b8aab54d73..6bd186f1121fc 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -1,4 +1,4 @@ -use gccjit::{LValue, RValue, ToRValue, Type}; +use gccjit::{GlobalKind, LValue, RValue, ToRValue, Type}; use rustc_abi::Primitive::Pointer; use rustc_abi::{self as abi, HasDataLayout}; use rustc_codegen_ssa::traits::{ @@ -7,6 +7,7 @@ use rustc_codegen_ssa::traits::{ use rustc_middle::mir::Mutability; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{Instance, SymbolName}; use rustc_session::PointerAuthSchema; use crate::consts::const_alloc_to_gcc; @@ -47,6 +48,81 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { // SIMD builtins require a constant value. self.bitcast_if_needed(value, typ) } + + pub(crate) fn alloc_to_backend( + &self, + global_alloc: GlobalAlloc<'tcx>, + need_symbol_name: bool, + ) -> Result<(RValue<'gcc>, Option>), u64> { + let alloc = match global_alloc { + GlobalAlloc::Function { instance, .. } => { + return Ok(( + self.get_fn_addr(instance, None), + need_symbol_name.then(|| self.tcx.symbol_name(instance)), + )); + } + GlobalAlloc::Static(def_id) => { + assert!(self.tcx.is_static(def_id)); + return Ok(( + self.get_static(def_id).get_address(None), + need_symbol_name + .then(|| self.tcx.symbol_name(Instance::mono(self.tcx, def_id))), + )); + } + GlobalAlloc::TypeId { .. } => { + // Drop the provenance, the offset contains the bytes of the hash, so + // just return 0 as base address. + return Err(0); + } + + GlobalAlloc::Memory(alloc) => { + if alloc.inner().len() == 0 { + // For ZSTs directly codegen an aligned pointer. + // This avoids generating a zero-sized constant value and actually needing a + // real address at runtime. + return Err(alloc.inner().align.bytes()); + } + + alloc + } + + GlobalAlloc::VTable(ty, dyn_ty) => { + self.tcx + .global_alloc(self.tcx.vtable_allocation(( + ty, + dyn_ty.principal().map(|principal| { + self.tcx.instantiate_bound_regions_with_erased(principal) + }), + ))) + .unwrap_memory() + } + }; + + if need_symbol_name { + let name = self.generate_global_symbol_name(); + + let init = crate::consts::const_alloc_to_gcc_uncached(self, alloc); + let alloc = alloc.inner(); + let typ = self.val_ty(init).get_aligned(alloc.align.bytes()); + + let global = self.declare_global_with_linkage(&name, typ, GlobalKind::Internal); + + global.global_set_initializer_rvalue(init); + return Ok((global.get_address(None), Some(SymbolName::new(self.tcx, &name)))); + } + + let value = match alloc.inner().mutability { + Mutability::Mut => { + self.static_addr_of_mut(const_alloc_to_gcc(self, alloc), alloc.inner().align, None) + } + _ => self.static_addr_of(alloc, None), + }; + if !self.sess().fewer_names() { + // FIXME(antoyo): set value name. + } + + Ok((value, None)) + } } pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> RValue<'gcc> { @@ -269,57 +345,17 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { Scalar::Ptr(ptr, _size) => { let (prov, offset) = ptr.prov_and_relative_offset(); let alloc_id = prov.alloc_id(); - let base_addr = match self.tcx.global_alloc(alloc_id) { - GlobalAlloc::Memory(alloc) => { - // For ZSTs directly codegen an aligned pointer. - // This avoids generating a zero-sized constant value and actually needing a - // real address at runtime. - if alloc.inner().len() == 0 { - let val = alloc.inner().align.bytes().wrapping_add(offset.bytes()); - let val = self.const_usize(self.tcx.truncate_to_target_usize(val)); - return if matches!(layout.primitive(), Pointer(_)) { - self.context.new_cast(None, val, ty) - } else { - self.const_bitcast(val, ty) - }; - } - - let value = match alloc.inner().mutability { - Mutability::Mut => self.static_addr_of_mut( - const_alloc_to_gcc(self, alloc), - alloc.inner().align, - None, - ), - _ => self.static_addr_of(alloc, None), + let base_addr = match self.alloc_to_backend(self.tcx.global_alloc(alloc_id), false) + { + Ok((base_addr, _)) => base_addr, + Err(base_addr) => { + let val = base_addr.wrapping_add(offset.bytes()); + let val = self.const_usize(self.tcx.truncate_to_target_usize(val)); + return if matches!(layout.primitive(), Pointer(_)) { + self.context.new_cast(None, val, ty) + } else { + self.const_bitcast(val, ty) }; - if !self.sess().fewer_names() { - // FIXME(antoyo): set value name. - } - value - } - GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance, None), - GlobalAlloc::VTable(ty, dyn_ty) => { - let alloc = self - .tcx - .global_alloc(self.tcx.vtable_allocation(( - ty, - dyn_ty.principal().map(|principal| { - self.tcx.instantiate_bound_regions_with_erased(principal) - }), - ))) - .unwrap_memory(); - self.static_addr_of(alloc, None) - } - GlobalAlloc::TypeId { .. } => { - let val = self.const_usize(offset.bytes()); - // This is still a variable of pointer type, even though we only use the provenance - // of that pointer in CTFE and Miri. But to make LLVM's type system happy, - // we need an int-to-ptr cast here (it doesn't matter at all which provenance that picks). - return self.context.new_cast(None, val, ty); - } - GlobalAlloc::Static(def_id) => { - assert!(self.tcx.is_static(def_id)); - self.get_static(def_id).get_address(None) } }; let ptr_type = base_addr.get_type(); diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 184db4cb25778..8045e8ae9d28f 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -117,6 +117,9 @@ pub struct CodegenCx<'gcc, 'tcx> { /// A counter that is used for generating local symbol names local_gen_sym_counter: Cell, + /// A counter that is used for generating global symbol names + global_gen_sym_counter: Cell, + eh_personality: Cell>>, #[cfg(feature = "master")] pub rust_try_fn: Cell, Function<'gcc>)>>, @@ -296,6 +299,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { tcx, struct_types: Default::default(), local_gen_sym_counter: Cell::new(0), + global_gen_sym_counter: Cell::new(0), eh_personality: Cell::new(None), #[cfg(feature = "master")] rust_try_fn: Cell::new(None), @@ -599,6 +603,24 @@ impl<'b, 'tcx> CodegenCx<'b, 'tcx> { name.push_str(&(idx as u64 + ALPHANUMERIC_ONLY as u64).to_base(ALPHANUMERIC_ONLY)); name } + + /// Generates a new global symbol name with the given prefix. This symbol name must + /// only be used for definitions with `internal` or `private` linkage. + pub fn generate_global_symbol_name(&self) -> String { + let idx = self.global_gen_sym_counter.get(); + self.global_gen_sym_counter.set(idx + 1); + + let sym = self.codegen_unit.symbol_name(); + let prefix = sym.as_str(); + let mut name = String::with_capacity(prefix.len() + 6); + name.push_str(prefix); + name.push('.'); + // Offset the index by the base so that always at least two characters + // are generated. This avoids cases where the suffix is interpreted as + // size by the assembler (for m68k: .b, .w, .l). + name.push_str(&(idx as u64 + ALPHANUMERIC_ONLY as u64).to_base(ALPHANUMERIC_ONLY)); + name + } } fn to_gcc_tls_mode(tls_model: TlsModel) -> gccjit::TlsModel { diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 8f192a5a73b63..1efab9b7c496d 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -1,10 +1,12 @@ use std::assert_matches; +use std::fmt::Write; -use rustc_abi::{BackendRepr, Float, Integer, Primitive, Scalar}; +use rustc_abi::{BackendRepr, Float, Integer, Primitive, Scalar, Size}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::mir::operand::OperandValue; use rustc_codegen_ssa::traits::*; use rustc_data_structures::fx::FxHashMap; +use rustc_middle::mir::interpret::{PointerArithmetic, Scalar as ConstScalar}; use rustc_middle::ty::Instance; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{bug, span_bug}; @@ -157,12 +159,18 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { constraints.push(format!("{}", op_idx[&idx])); } } - InlineAsmOperandRef::SymFn { instance } => { - inputs.push(self.cx.get_fn(instance)); - op_idx.insert(idx, constraints.len()); - constraints.push("s".to_string()); - } - InlineAsmOperandRef::SymStatic { def_id } => { + InlineAsmOperandRef::Const { value, ty: _ } => match value { + ConstScalar::Int(_) => (), + ConstScalar::Ptr(ptr, _) => { + let (prov, _) = ptr.prov_and_relative_offset(); + let global_alloc = self.tcx.global_alloc(prov.alloc_id()); + let value = self.cx.alloc_to_backend(global_alloc, false, None).unwrap(); + inputs.push(value); + op_idx.insert(idx, constraints.len()); + constraints.push("s".to_string()); + } + }, + InlineAsmOperandRef::SymThreadLocalStatic { def_id } => { inputs.push(self.cx.get_static(def_id)); op_idx.insert(idx, constraints.len()); constraints.push("s".to_string()); @@ -189,7 +197,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { template_str.push_str(s) } } - InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => { + InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } => { match operands[operand_idx] { InlineAsmOperandRef::In { reg, .. } | InlineAsmOperandRef::Out { reg, .. } @@ -204,12 +212,34 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { template_str.push_str(&format!("${{{}}}", op_idx[&operand_idx])); } } - InlineAsmOperandRef::Const { ref string } => { - // Const operands get injected directly into the template - template_str.push_str(string); + InlineAsmOperandRef::Const { value, ty } => { + match value { + ConstScalar::Int(int) => { + // Const operands get injected directly into the template + let string = rustc_codegen_ssa::common::asm_const_to_str( + self.tcx, + span, + int, + self.layout_of(ty), + ); + template_str.push_str(&string); + } + ConstScalar::Ptr(ptr, _) => { + let (_, offset) = ptr.prov_and_relative_offset(); + + // Only emit the raw symbol name + template_str + .push_str(&format!("${{{}:c}}", op_idx[&operand_idx])); + + if offset != Size::ZERO { + let offset = + self.sign_extend_to_target_isize(offset.bytes()); + write!(template_str, "{offset:+}").unwrap(); + } + } + } } - InlineAsmOperandRef::SymFn { .. } - | InlineAsmOperandRef::SymStatic { .. } => { + InlineAsmOperandRef::SymThreadLocalStatic { .. } => { // Only emit the raw symbol name template_str.push_str(&format!("${{{}:c}}", op_idx[&operand_idx])); } @@ -405,22 +435,44 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span } => { use rustc_codegen_ssa::back::symbol_export::escape_symbol_name; match operands[operand_idx] { - GlobalAsmOperandRef::Const { ref string } => { - // Const operands get injected directly into the - // template. Note that we don't need to escape $ - // here unlike normal inline assembly. - template_str.push_str(string); - } - GlobalAsmOperandRef::SymFn { instance } => { - let llval = self.get_fn(instance); - self.add_compiler_used_global(llval); - let symbol = llvm::build_string(|s| unsafe { - llvm::LLVMRustGetMangledName(llval, s); - }) - .expect("symbol is not valid UTF-8"); - template_str.push_str(&escape_symbol_name(self.tcx, &symbol, span)); + GlobalAsmOperandRef::Const { value, ty } => { + match value { + ConstScalar::Int(int) => { + // Const operands get injected directly into the + // template. Note that we don't need to escape $ + // here unlike normal inline assembly. + let string = rustc_codegen_ssa::common::asm_const_to_str( + self.tcx, + span, + int, + self.layout_of(ty), + ); + template_str.push_str(&string); + } + + ConstScalar::Ptr(ptr, _) => { + let (prov, offset) = ptr.prov_and_relative_offset(); + let global_alloc = self.tcx.global_alloc(prov.alloc_id()); + let llval = + self.alloc_to_backend(global_alloc, true, None).unwrap(); + + self.add_compiler_used_global(llval); + let symbol = llvm::build_string(|s| unsafe { + llvm::LLVMRustGetMangledName(llval, s); + }) + .expect("symbol is not valid UTF-8"); + template_str + .push_str(&escape_symbol_name(self.tcx, &symbol, span)); + + if offset != Size::ZERO { + let offset = + self.sign_extend_to_target_isize(offset.bytes()); + write!(template_str, "{offset:+}").unwrap(); + } + } + } } - GlobalAsmOperandRef::SymStatic { def_id } => { + GlobalAsmOperandRef::SymThreadLocalStatic { def_id } => { let llval = self .renamed_statics .borrow() diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 205e2e9a14701..e900bc1aecd46 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -174,6 +174,94 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { } } +impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { + pub(crate) fn alloc_to_backend( + &self, + global_alloc: GlobalAlloc<'tcx>, + need_symbol_name: bool, + schema: Option<&PointerAuthSchema>, + ) -> Result<&'ll Value, u64> { + let alloc = match global_alloc { + GlobalAlloc::Function { instance, .. } => { + return Ok(self.get_fn_addr(instance, schema)); + } + GlobalAlloc::Static(def_id) => { + assert!(self.tcx.is_static(def_id)); + assert!(!self.tcx.is_thread_local_static(def_id)); + return Ok( + // `alloc_to_backend` might be called by `global_asm!` codegen. In which case + // `global_asm!` would need to find the renamed statics to use for symbol name. + self.renamed_statics + .borrow() + .get(&def_id) + .copied() + .unwrap_or_else(|| self.get_static(def_id)), + ); + } + GlobalAlloc::TypeId { .. } => { + // Drop the provenance, the offset contains the bytes of the hash, so + // just return 0 as base address. + return Err(0); + } + + GlobalAlloc::Memory(alloc) => { + if alloc.inner().len() == 0 { + // For ZSTs directly codegen an aligned pointer. + // This avoids generating a zero-sized constant value and actually needing a + // real address at runtime. + return Err(alloc.inner().align.bytes()); + } + + alloc + } + GlobalAlloc::VTable(ty, dyn_ty) => { + self.tcx + .global_alloc(self.tcx.vtable_allocation(( + ty, + dyn_ty.principal().map(|principal| { + self.tcx.instantiate_bound_regions_with_erased(principal) + }), + ))) + .unwrap_memory() + } + }; + + let init = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No); + let alloc = alloc.inner(); + + if need_symbol_name { + // If a symbol name is needed, use `static_addr_of_mut` so we can give it unique symbol names. + let value = self.static_addr_of_mut(init, alloc.align, None); + if alloc.mutability.is_not() { + llvm::set_global_constant(value, true); + } + + // Even though we're generating with internal linkage, this symbol name still needs to + // be globally unique. LTO can rename symbol names if there are duplicates, but the + // names inserted into global asm as text cannot be updated. + let name = self.generate_global_symbol_name(); + llvm::set_value_name(value, name.as_bytes()); + llvm::set_linkage(value, llvm::Linkage::InternalLinkage); + return Ok(value); + } + + let value = match alloc.mutability { + Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), + _ => self.static_addr_of_impl(init, alloc.align, None), + }; + if !self.sess().fewer_names() && llvm::get_value_name(value).is_empty() { + let hash = self.tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + alloc.stable_hash(&mut hcx, &mut hasher); + hasher.finish::() + }); + llvm::set_value_name(value, format!("alloc_{hash:032x}").as_bytes()); + } + + Ok(value) + } +} + impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { fn const_null(&self, t: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMConstNull(t) } @@ -333,77 +421,19 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { Scalar::Ptr(ptr, _size) => { let (prov, offset) = ptr.prov_and_relative_offset(); let global_alloc = self.tcx.global_alloc(prov.alloc_id()); - let base_addr = match global_alloc { - GlobalAlloc::Memory(alloc) => { - // For ZSTs directly codegen an aligned pointer. - // This avoids generating a zero-sized constant value and actually needing a - // real address at runtime. - if alloc.inner().len() == 0 { - let val = alloc.inner().align.bytes().wrapping_add(offset.bytes()); - let llval = self.const_usize(self.tcx.truncate_to_target_usize(val)); - return if matches!(layout.primitive(), Pointer(_)) { - unsafe { llvm::LLVMConstIntToPtr(llval, llty) } - } else { - self.const_bitcast(llval, llty) - }; + let base_addr_space = global_alloc.address_space(self); + let base_addr = match self.alloc_to_backend(global_alloc, false, schema) { + Ok(base_addr) => base_addr, + Err(base_addr) => { + let val = base_addr.wrapping_add(offset.bytes()); + let llval = self.const_usize(self.tcx.truncate_to_target_usize(val)); + return if matches!(layout.primitive(), Pointer(_)) { + unsafe { llvm::LLVMConstIntToPtr(llval, llty) } } else { - let init = const_alloc_to_llvm( - self, - alloc.inner(), - IsStatic::No, - IsInitOrFini::No, - ); - let alloc = alloc.inner(); - let value = match alloc.mutability { - Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), - _ => self.static_addr_of_impl(init, alloc.align, None), - }; - if !self.sess().fewer_names() && llvm::get_value_name(value).is_empty() - { - let hash = self.tcx.with_stable_hashing_context(|mut hcx| { - let mut hasher = StableHasher::new(); - alloc.stable_hash(&mut hcx, &mut hasher); - hasher.finish::() - }); - llvm::set_value_name( - value, - format!("alloc_{hash:032x}").as_bytes(), - ); - } - value - } - } - GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance, schema), - GlobalAlloc::VTable(ty, dyn_ty) => { - let alloc = self - .tcx - .global_alloc(self.tcx.vtable_allocation(( - ty, - dyn_ty.principal().map(|principal| { - self.tcx.instantiate_bound_regions_with_erased(principal) - }), - ))) - .unwrap_memory(); - let init = const_alloc_to_llvm( - self, - alloc.inner(), - IsStatic::No, - IsInitOrFini::No, - ); - self.static_addr_of_impl(init, alloc.inner().align, None) - } - GlobalAlloc::Static(def_id) => { - assert!(self.tcx.is_static(def_id)); - assert!(!self.tcx.is_thread_local_static(def_id)); - self.get_static(def_id) - } - GlobalAlloc::TypeId { .. } => { - // Drop the provenance, the offset contains the bytes of the hash - let llval = self.const_usize(offset.bytes()); - return unsafe { llvm::LLVMConstIntToPtr(llval, llty) }; + self.const_bitcast(llval, llty) + }; } }; - let base_addr_space = global_alloc.address_space(self); let llval = unsafe { llvm::LLVMConstInBoundsGEP2( self.type_i8(), diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index c018ab23c849a..8f1910eaced13 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -142,6 +142,9 @@ pub(crate) struct FullCx<'ll, 'tcx> { /// A counter that is used for generating local symbol names local_gen_sym_counter: Cell, + /// A counter that is used for generating global symbol names + global_gen_sym_counter: Cell, + /// `codegen_static` will sometimes create a second global variable with a /// different type and clear the symbol name of the original global. /// `global_asm!` needs to be able to find this new global so that it can @@ -679,6 +682,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { rust_try_fn: Cell::new(None), intrinsics: Default::default(), local_gen_sym_counter: Cell::new(0), + global_gen_sym_counter: Cell::new(0), renamed_statics: Default::default(), objc_class_t: Cell::new(None), objc_classrefs: Default::default(), @@ -1122,6 +1126,20 @@ impl CodegenCx<'_, '_> { name.push_str(&(idx as u64).to_base(ALPHANUMERIC_ONLY)); name } + + /// Generates a new global symbol name with the given prefix. + pub(crate) fn generate_global_symbol_name(&self) -> String { + let idx = self.global_gen_sym_counter.get(); + self.global_gen_sym_counter.set(idx + 1); + + let sym = self.codegen_unit.symbol_name(); + let prefix = sym.as_str(); + let mut name = String::with_capacity(prefix.len() + 6); + name.push_str(prefix); + name.push('.'); + name.push_str(&(idx as u64).to_base(ALPHANUMERIC_ONLY)); + name + } } impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 61d9434a4ef6a..339c0a968e9d3 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -22,12 +22,12 @@ use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::{Dependencies, Linkage}; use rustc_middle::middle::exported_symbols::{self, SymbolExportKind}; use rustc_middle::middle::lang_items; -use rustc_middle::mir::BinOp; -use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, ErrorHandled, Scalar}; +use rustc_middle::mir::{BinOp, ConstValue}; use rustc_middle::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem, MonoItemPartitions}; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; -use rustc_middle::ty::{self, Instance, PatternKind, Ty, TyCtxt, Unnormalized}; +use rustc_middle::ty::{self, Instance, PatternKind, Ty, TyCtxt, UintTy, Unnormalized}; use rustc_middle::{bug, span_bug}; use rustc_session::Session; use rustc_session::config::{self, CrateType, EntryFnType}; @@ -419,20 +419,26 @@ where Ok(const_value) => { let ty = cx.tcx().typeck_body(anon_const.body).node_type(anon_const.hir_id); - let string = common::asm_const_to_str( - cx.tcx(), - *op_sp, - const_value, - cx.layout_of(ty), - ); - GlobalAsmOperandRef::Const { string } + let ConstValue::Scalar(scalar) = const_value else { + span_bug!( + *op_sp, + "expected Scalar for promoted asm const, but got {:#?}", + const_value + ) + }; + GlobalAsmOperandRef::Const { + value: common::asm_const_ptr_clean(cx.tcx(), scalar), + ty, + } } Err(ErrorHandled::Reported { .. }) => { // An error has already been reported and // compilation is guaranteed to fail if execution - // hits this path. So an empty string instead of - // a stringified constant value will suffice. - GlobalAsmOperandRef::Const { string: String::new() } + // hits this path. So anything will suffice. + GlobalAsmOperandRef::Const { + value: Scalar::from_u32(0), + ty: Ty::new_uint(cx.tcx(), UintTy::U32), + } } Err(ErrorHandled::TooGeneric(_)) => { span_bug!(*op_sp, "asm const cannot be resolved; too generic") @@ -452,10 +458,26 @@ where _ => span_bug!(*op_sp, "asm sym is not a function"), }; - GlobalAsmOperandRef::SymFn { instance } + GlobalAsmOperandRef::Const { + value: Scalar::from_pointer( + cx.tcx().reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT).into(), + cx, + ), + ty: Ty::new_fn_ptr(cx.tcx(), ty.fn_sig(cx.tcx())), + } } rustc_hir::InlineAsmOperand::SymStatic { path: _, def_id } => { - GlobalAsmOperandRef::SymStatic { def_id } + if cx.tcx().is_thread_local_static(def_id) { + GlobalAsmOperandRef::SymThreadLocalStatic { def_id } + } else { + GlobalAsmOperandRef::Const { + value: Scalar::from_pointer( + cx.tcx().reserve_and_set_static_alloc(def_id).into(), + cx, + ), + ty: cx.tcx().static_ptr_ty(def_id, cx.typing_env()), + } + } } rustc_hir::InlineAsmOperand::In { .. } | rustc_hir::InlineAsmOperand::Out { .. } diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index ae72258a87c86..777f3f6b53fd4 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -2,9 +2,10 @@ use rustc_hir::LangItem; use rustc_hir::attrs::PeImportNameType; +use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{self, Instance, TyCtxt}; -use rustc_middle::{bug, mir, span_bug}; +use rustc_middle::ty::{self, Instance, ScalarInt, TyCtxt}; +use rustc_middle::{bug, span_bug}; use rustc_session::cstore::{DllCallingConvention, DllImport, DllImportSymbolType}; use rustc_span::Span; use rustc_target::spec::{CfgAbi, Env, Os, Target}; @@ -153,13 +154,10 @@ pub(crate) fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( pub fn asm_const_to_str<'tcx>( tcx: TyCtxt<'tcx>, sp: Span, - const_value: mir::ConstValue, + scalar: ScalarInt, ty_and_layout: TyAndLayout<'tcx>, ) -> String { - let mir::ConstValue::Scalar(scalar) = const_value else { - span_bug!(sp, "expected Scalar for promoted asm const, but got {:#?}", const_value) - }; - let value = scalar.assert_scalar_int().to_bits(ty_and_layout.size); + let value = scalar.to_bits(ty_and_layout.size); match ty_and_layout.ty.kind() { ty::Uint(_) => value.to_string(), ty::Int(int_ty) => match int_ty.normalize(tcx.sess.target.pointer_width) { @@ -170,10 +168,38 @@ pub fn asm_const_to_str<'tcx>( ty::IntTy::I128 => (value as i128).to_string(), ty::IntTy::Isize => unreachable!(), }, + // For pointers without provenance, just print the unsigned value + ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..) => value.to_string(), _ => span_bug!(sp, "asm const has bad type {}", ty_and_layout.ty), } } +/// "Clean" a const pointer by removing values where the resulting ASM will not be +/// ` + `. +/// +/// These values are converted to `ScalarInt`. +pub fn asm_const_ptr_clean<'tcx>(tcx: TyCtxt<'tcx>, scalar: Scalar) -> Scalar { + let Scalar::Ptr(ptr, _) = scalar else { + return scalar; + }; + let (prov, offset) = ptr.prov_and_relative_offset(); + let global_alloc = tcx.global_alloc(prov.alloc_id()); + match global_alloc { + GlobalAlloc::TypeId { .. } => { + // `TypeId` provenances are not a thing in codegen. Just erase and replace with scalar offset. + Scalar::from_u64(offset.bytes()) + } + GlobalAlloc::Memory(alloc) if alloc.inner().len() == 0 => { + // ZST const allocations don't actually get global defined when lowered. + // Turn them into integer without provenances now. + let val = alloc.inner().align.bytes().wrapping_add(offset.bytes()); + Scalar::from_target_usize(tcx.truncate_to_target_usize(val), &tcx) + } + // Other types of `GlobalAlloc` are fine. + _ => scalar, + } +} + pub fn is_mingw_gnu_toolchain(target: &Target) -> bool { target.os == Os::Windows && target.env == Env::Gnu && target.cfg_abi == CfgAbi::Unspecified } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 5bdb6a707fd7a..5dbaae44a441e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -2,7 +2,8 @@ use std::cmp; use std::ops::Range; use rustc_abi::{ - Align, ArmCall, BackendRepr, CanonAbi, ExternAbi, HasDataLayout, Reg, Size, WrappingRange, + Align, ArmCall, BackendRepr, CanonAbi, ExternAbi, FieldsShape, HasDataLayout, Reg, Size, + VariantIdx, Variants, WrappingRange, }; use rustc_ast as ast; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; @@ -10,8 +11,9 @@ use rustc_data_structures::packed::Pu128; use rustc_hir::attrs::AttributeKind; use rustc_hir::lang_items::LangItem; use rustc_lint_defs::builtin::TAIL_CALL_TRACK_CALLER; +use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, Scalar}; use rustc_middle::mir::{self, AssertKind, InlineAsmMacro, SwitchTargets, UnwindTerminateReason}; -use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; +use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout, 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::{bug, span_bug}; @@ -618,15 +620,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if self.fn_abi.conv == CanonAbi::Arm(ArmCall::CCmseNonSecureEntry) { // The return value of an `extern "cmse-nonsecure-entry"` function crosses the - // secure boundary. Zero padding bytes so information does not leak. - // - // This only zeroes "guaranteed" padding. There may be more bytes that are - // padding for some but not all variants of this type; those are not zeroed. - // - // Returning a value with value-dependent padding will instead trigger a lint. + // secure boundary. Clear any padding bytes so information does not leak. let ret_layout = self.fn_abi.ret.layout; - let uninit_ranges = ret_layout.padding_ranges(bx.cx()); - self.zero_byte_ranges(bx, llslot, ret_layout.size, &uninit_ranges); + self.clear_padding_cmse(bx, llslot, ret_layout.size, ret_layout); } load_cast(bx, cast_ty, llslot, self.fn_abi.ret.layout.align.abi) @@ -1494,13 +1490,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::InlineAsmOperand::Const { ref value } => { let const_value = self.eval_mir_constant(value); - let string = common::asm_const_to_str( - bx.tcx(), - span, - const_value, - bx.layout_of(value.ty()), - ); - InlineAsmOperandRef::Const { string } + let mir::ConstValue::Scalar(scalar) = const_value else { + span_bug!( + span, + "expected Scalar for promoted asm const, but got {:#?}", + const_value + ) + }; + InlineAsmOperandRef::Const { + value: common::asm_const_ptr_clean(bx.tcx(), scalar), + ty: value.ty(), + } } mir::InlineAsmOperand::SymFn { ref value } => { let const_ = self.monomorphize(value.const_); @@ -1512,13 +1512,30 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args.no_bound_vars().unwrap(), ) .unwrap(); - InlineAsmOperandRef::SymFn { instance } + + InlineAsmOperandRef::Const { + value: Scalar::from_pointer( + bx.tcx().reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT).into(), + bx, + ), + ty: Ty::new_fn_ptr(bx.tcx(), const_.ty().fn_sig(bx.tcx())), + } } else { span_bug!(span, "invalid type for asm sym (fn)"); } } mir::InlineAsmOperand::SymStatic { def_id } => { - InlineAsmOperandRef::SymStatic { def_id } + if bx.tcx().is_thread_local_static(def_id) { + InlineAsmOperandRef::SymThreadLocalStatic { def_id } + } else { + InlineAsmOperandRef::Const { + value: Scalar::from_pointer( + bx.tcx().reserve_and_set_static_alloc(def_id).into(), + bx, + ), + ty: bx.tcx().static_ptr_ty(def_id, bx.typing_env()), + } + } } mir::InlineAsmOperand::Label { target_index } => { InlineAsmOperandRef::Label { label: self.llbb(targets[target_index]) } @@ -1745,22 +1762,166 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } + /// When using CMSE, values that cross the secure boundary from secure to non-secure mode can + /// contain stale secure data in their padding bytes. This function clears that data. This is + /// required when a value is: + /// + /// - passed to an `extern "cmse-nonsecure-call"` function + /// - returned from an `extern "cmse-nonsecure-entry"` function + /// + /// This function clears both: + /// + /// - variant-independent padding, bytes that are padding for all valid values of the type + /// - variant-dependent padding, bytes that are padding for some but not all values of the type + /// + /// Clearing variant-dependent padding requires looking at the data at runtime to determine what + /// bytes to clear. + fn clear_padding_cmse( + &mut self, + bx: &mut Bx, + base_ptr: Bx::Value, + limit: Size, + layout: TyAndLayout<'tcx>, + ) { + // First clear variant-independent padding, a series of memsets. + let variant_independent = layout.variant_independent_padding_ranges(self.cx); + self.zero_byte_ranges(bx, base_ptr, Size::ZERO, limit, &variant_independent); + + // Then clear the extra padding of the active variant of any (nested) enum. + self.clear_variant_dependent_padding(bx, base_ptr, Size::ZERO, limit, layout); + } + + fn clear_variant_dependent_padding( + &mut self, + bx: &mut Bx, + base_ptr: Bx::Value, + base_offset: Size, + limit: Size, + layout: TyAndLayout<'tcx>, + ) { + let cx = self.cx; + + if !layout.has_variant_dependent_padding(cx) { + return; + } + + // Recurse into aggregate fields/elements to reach any nested enums. + match layout.fields { + FieldsShape::Array { stride, count } => { + let elem = layout.field(cx, 0); + if elem.has_variant_dependent_padding(cx) { + for idx in 0..count { + let off = base_offset + idx * stride; + self.clear_variant_dependent_padding(bx, base_ptr, off, limit, elem); + } + } + } + FieldsShape::Arbitrary { .. } => { + for i in 0..layout.fields.count() { + let field = layout.field(cx, i); + if field.has_variant_dependent_padding(cx) { + let off = base_offset + layout.fields.offset(i); + self.clear_variant_dependent_padding(bx, base_ptr, off, limit, field); + } + } + } + FieldsShape::Primitive | FieldsShape::Union(_) => { /* nothing to visit */ } + } + + // If this is not a multi-variant enum, we're done. + let Variants::Multiple { ref variants, .. } = layout.variants else { + return; + }; + + // Collect variants that will need padding cleared. + let mut work = Vec::with_capacity(variants.len()); + for i in 0..variants.len() { + let idx = VariantIdx::from_usize(i); + let variant = layout.for_variant(cx, idx); + + // Don't consider uninhabited variants. + if variant.is_uninhabited() { + continue; + } + + let variant_dependent = layout.variant_dependent_padding_ranges(cx, idx); + let has_nested_variant_dependent = (0..variant.fields.count()) + .any(|i| variant.field(cx, i).has_variant_dependent_padding(cx)); + + if !variant_dependent.is_empty() || has_nested_variant_dependent { + work.push((idx, variant, variant_dependent)); + } + } + + if work.is_empty() { + return; + } + + // Build the switch and clear the appropriate padding for each variant. + let root_block = bx.llbb(); + let join_block = bx.append_sibling_block("cmse_pad_join"); + let mut cases = Vec::with_capacity(work.len()); + + for (idx, variant, variant_dependent) in work.into_iter() { + let Some(discr) = layout.ty.discriminant_for_variant(bx.tcx(), idx) else { + bug!("multi-variant layout on a type without discriminants"); + }; + + let variant_block = bx.append_sibling_block("cmse_pad_variant"); + bx.switch_to_block(variant_block); + + // Clear the padding of this variant. + self.zero_byte_ranges(bx, base_ptr, base_offset, limit, &variant_dependent); + + // Recurse into the fields. + for i in 0..variant.fields.count() { + let field = variant.field(cx, i); + let off = base_offset + variant.fields.offset(i); + self.clear_variant_dependent_padding(bx, base_ptr, off, limit, field); + } + + bx.br(join_block); + cases.push((discr.val, variant_block)); + } + + // Construct the dispatch. + bx.switch_to_block(root_block); + + let discr_ty = layout.ty.discriminant_ty(bx.tcx()); + let enum_ptr = bx.inbounds_ptradd(base_ptr, bx.const_usize(base_offset.bytes())); + let operand = OperandRef { + val: OperandValue::Ref(PlaceValue::new_sized(enum_ptr, layout.align.abi)), + layout, + move_annotation: None, + }; + let discr = operand.codegen_get_discr(self, bx, discr_ty); + + // Default to the join block (for variants without variant-dependent padding). + bx.switch(discr, join_block, cases.into_iter()); + + bx.switch_to_block(join_block); + } + fn zero_byte_ranges( &mut self, bx: &mut Bx, ptr: Bx::Value, + offset: Size, limit: Size, ranges: &[Range], ) { let zero = bx.const_u8(0); for range in ranges { - let end = cmp::min(range.end, limit); + let start = range.start + offset; + let end = range.end + offset; + + let end = cmp::min(end, limit); if range.start >= end { continue; } - let offset = bx.const_usize(range.start.bytes()); - let len = bx.const_usize((end - range.start).bytes()); + let offset = bx.const_usize(start.bytes()); + let len = bx.const_usize((end - start).bytes()); let ptr = bx.inbounds_ptradd(ptr, offset); bx.memset(ptr, zero, len, Align::ONE, MemFlags::empty()); } @@ -1902,18 +2063,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ); // The arguments of an `extern "cmse-nonsecure-call"` function cross the secure - // boundary. Zero padding bytes so information does not leak. - // - // This only zeroes "guaranteed" padding. There may be more bytes that are - // padding for some but not all variants of this type; those are not zeroed. - // - // Passing an argument with value-dependent padding will instead trigger a lint. + // boundary. Clear any padding bytes so information does not leak. if conv == CanonAbi::Arm(ArmCall::CCmseNonSecureCall) { - self.zero_byte_ranges( + self.clear_padding_cmse( bx, llscratch, Size::from_bytes(copy_bytes), - &arg.layout.padding_ranges(bx.cx()), + arg.layout, ); } diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index c33c228cc5b2d..131a345fe557d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,11 +1,12 @@ use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind}; use rustc_hir::attrs::{InstructionSetAttr, Linkage}; use rustc_hir::def_id::LOCAL_CRATE; -use rustc_middle::mir::{InlineAsmOperand, START_BLOCK}; +use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, Scalar}; +use rustc_middle::mir::{self, InlineAsmOperand, START_BLOCK}; use rustc_middle::mono::{MonoItemData, Visibility}; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; use rustc_middle::ty::{Instance, Ty, TyCtxt, TypeVisitableExt}; -use rustc_middle::{bug, ty}; +use rustc_middle::{bug, span_bug, ty}; use rustc_span::sym; use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; use rustc_target::spec::{Arch, BinaryFormat, Env, Os}; @@ -77,15 +78,18 @@ fn inline_to_global_operand<'a, 'tcx, Cx: LayoutOf<'tcx, LayoutOfResult = TyAndL cx.typing_env(), ty::EarlyBinder::bind(cx.tcx(), value.ty()), ); + let mir::ConstValue::Scalar(scalar) = const_value else { + span_bug!( + value.span, + "expected Scalar for promoted asm const, but got {:#?}", + const_value + ) + }; - let string = common::asm_const_to_str( - cx.tcx(), - value.span, - const_value, - cx.layout_of(mono_type), - ); - - GlobalAsmOperandRef::Const { string } + GlobalAsmOperandRef::Const { + value: common::asm_const_ptr_clean(cx.tcx(), scalar), + ty: mono_type, + } } InlineAsmOperand::SymFn { value } => { let mono_type = instance.instantiate_mir_and_normalize_erasing_regions( @@ -105,10 +109,26 @@ fn inline_to_global_operand<'a, 'tcx, Cx: LayoutOf<'tcx, LayoutOfResult = TyAndL _ => bug!("asm sym is not a function"), }; - GlobalAsmOperandRef::SymFn { instance } + GlobalAsmOperandRef::Const { + value: Scalar::from_pointer( + cx.tcx().reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT).into(), + cx, + ), + ty: Ty::new_fn_ptr(cx.tcx(), mono_type.fn_sig(cx.tcx())), + } } InlineAsmOperand::SymStatic { def_id } => { - GlobalAsmOperandRef::SymStatic { def_id: *def_id } + if cx.tcx().is_thread_local_static(*def_id) { + GlobalAsmOperandRef::SymThreadLocalStatic { def_id: *def_id } + } else { + GlobalAsmOperandRef::Const { + value: Scalar::from_pointer( + cx.tcx().reserve_and_set_static_alloc(*def_id).into(), + cx, + ), + ty: cx.tcx().static_ptr_ty(*def_id, cx.typing_env()), + } + } } InlineAsmOperand::In { .. } | InlineAsmOperand::Out { .. } diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index cc7a6a3f19e9e..85a2fe09ba414 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -1,6 +1,7 @@ use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_hir::def_id::DefId; -use rustc_middle::ty::Instance; +use rustc_middle::mir::interpret::Scalar; +use rustc_middle::ty::{Instance, Ty}; use rustc_span::Span; use rustc_target::asm::InlineAsmRegOrRegClass; @@ -26,12 +27,11 @@ pub enum InlineAsmOperandRef<'tcx, B: BackendTypes + ?Sized> { out_place: Option>, }, Const { - string: String, + value: Scalar, + /// Type of the constant. This is needed to extract width and signedness. + ty: Ty<'tcx>, }, - SymFn { - instance: Instance<'tcx>, - }, - SymStatic { + SymThreadLocalStatic { def_id: DefId, }, Label { @@ -41,9 +41,14 @@ pub enum InlineAsmOperandRef<'tcx, B: BackendTypes + ?Sized> { #[derive(Debug)] pub enum GlobalAsmOperandRef<'tcx> { - Const { string: String }, - SymFn { instance: Instance<'tcx> }, - SymStatic { def_id: DefId }, + Const { + value: Scalar, + /// Type of the constant. This is needed to extract width and signedness. + ty: Ty<'tcx>, + }, + SymThreadLocalStatic { + def_id: DefId, + }, } pub trait AsmBuilderMethods<'tcx>: BackendTypes { diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 57ccbb8ff10ea..b2c6663e41085 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -44,6 +44,7 @@ fn retry_codegen_mode_with_postanalysis<'tcx, K: TypeVisitable>, V> | ty::TypingMode::Typeck { .. } | ty::TypingMode::PostTypeckUntilBorrowck { .. } | ty::TypingMode::PostBorrowck { .. } + | ty::TypingMode::Reflection | ty::TypingMode::PostAnalysis => {} } diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index cbef1de487c43..1b23432c8c57a 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -29,7 +29,10 @@ pub(crate) fn type_implements_dyn_trait<'tcx, M: Machine<'tcx>>( ); }; - let (infcx, param_env) = ecx.tcx.infer_ctxt().build_with_typing_env(ecx.typing_env); + let (infcx, param_env) = ecx.tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::new( + ecx.typing_env.param_env, + ty::TypingMode::Reflection, + )); let ocx = ObligationCtxt::new(&infcx); ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| { diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 5b63379edd332..7fe32b4e75ffb 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -33,6 +33,7 @@ fn assert_typing_mode(typing_mode: ty::TypingMode<'_>) { // `InterpCx::new` for more details. ty::TypingMode::Coherence | ty::TypingMode::Typeck { .. } + | ty::TypingMode::Reflection | ty::TypingMode::PostTypeckUntilBorrowck { .. } | ty::TypingMode::PostBorrowck { .. } => bug!( "Const eval should always happens in PostAnalysis or Codegen mode. See the comment on `assert_typing_mode` for more details." diff --git a/compiler/rustc_data_structures/src/range_set.rs b/compiler/rustc_data_structures/src/range_set.rs index 514946a0fb2de..bd18728e20da5 100644 --- a/compiler/rustc_data_structures/src/range_set.rs +++ b/compiler/rustc_data_structures/src/range_set.rs @@ -56,4 +56,44 @@ where v.insert(idx, (offset, size)); } } + + /// The ranges from `self` with any intersection with `other` removed. + pub fn difference(&self, other: &Self) -> Self { + let (a, b) = (self, other); + let mut out = Vec::new(); + + let mut j = 0; + for &(a_offset, a_size) in a.0.iter() { + let mut cursor = a_offset; + let a_end = a_offset + a_size; + + // Skip ranges of `b` that end before this range of `a` begins. + // both sequences are sorted they cannot overlap any later range of `a` either. + while let Some(&(b_offset, b_size)) = b.0.get(j) + && b_offset + b_size <= cursor + { + j += 1; + } + + // Carve out each range of `b` that overlaps this range of `a`. A range of `b` may extend + // past `a_end` and overlap the next range of `a`, so leave `j` pointing at it. + let mut k = j; + while let Some(&(b_offset, b_size)) = b.0.get(k) + && b_offset < a_end + { + if b_offset > cursor { + out.push((cursor, b_offset)); + } + cursor = Ord::max(cursor, b_offset + b_size); + k += 1; + } + + // Keep the remainder of the `a`'s range. + if cursor < a_end { + out.push((cursor, a_end)); + } + } + + Self(out) + } } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 3649cf24ea822..6505cca2473f8 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -398,6 +398,8 @@ declare_features! ( (unstable, arbitrary_self_types_pointers, "1.83.0", Some(44874)), /// Target features on arm. (unstable, arm_target_feature, "1.27.0", Some(150246)), + /// Allows using `const` operands with pointer in inline assembly. + (unstable, asm_const_ptr, "CURRENT_RUSTC_VERSION", Some(128464)), /// Enables experimental inline assembly support for additional architectures. (unstable, asm_experimental_arch, "1.58.0", Some(93335)), /// Enables experimental register support in inline assembly. diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 9d1cf7046f4b0..1481cb0726082 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -4,7 +4,7 @@ use std::fmt::Debug; use rustc_ast as ast; use rustc_ast::NodeId; -use rustc_data_structures::unord::UnordMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_span::Symbol; @@ -962,4 +962,6 @@ pub enum LifetimeRes { ElidedAnchor { start: NodeId, end: NodeId }, } -pub type DocLinkResMap = UnordMap<(Symbol, Namespace), Option>>; +// FxIndexMap is necessary because its data ends up in .rmeta files, +// so its iteration order must be consistent. See #159677 for context. +pub type DocLinkResMap = FxIndexMap<(Symbol, Namespace), Option>>; diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 1592dfdde4e6f..92f48cda10f9f 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -194,6 +194,8 @@ language_item_table! { CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1); DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1); + TryAsDyn, sym::try_as_dyn, try_as_dyn, Target::Trait, GenericRequirement::Exact(1); + // lang items relating to transmutability TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0); TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(2); diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index b520ea106dfa0..605b6e3751ef3 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -85,6 +85,7 @@ pub(crate) fn provide(providers: &mut Providers) { adt_def, fn_sig, impl_trait_header, + impl_is_fully_generic_for_reflection, coroutine_kind, coroutine_for_closure, opaque_ty_origin, @@ -1395,6 +1396,11 @@ pub fn suggest_impl_trait<'tcx>( None } +fn impl_is_fully_generic_for_reflection(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + tcx.impl_trait_header(def_id).is_fully_generic_for_reflection() + && tcx.explicit_predicates_of(def_id).is_fully_generic_for_reflection() +} + fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::ImplTraitHeader<'_> { let icx = ItemCtxt::new(tcx, def_id); let item = tcx.hir_expect_item(def_id); diff --git a/compiler/rustc_hir_typeck/src/diagnostics.rs b/compiler/rustc_hir_typeck/src/diagnostics.rs index a5c39bd1584a1..1a6df92957d00 100644 --- a/compiler/rustc_hir_typeck/src/diagnostics.rs +++ b/compiler/rustc_hir_typeck/src/diagnostics.rs @@ -18,6 +18,13 @@ use rustc_span::{Ident, Span, Spanned, Symbol}; use crate::FnCtxt; +#[derive(Diagnostic)] +#[diag("using pointers in asm `const` operand is experimental")] +pub(crate) struct AsmConstPtrUnstable { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("base expression required after `..`", code = E0797)] pub(crate) struct BaseExpressionDoubleDot { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 3bcad2460e78f..6ffbbed8fd642 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3731,7 +3731,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } hir::InlineAsmOperand::Const { ref anon_const } => { - self.check_expr_const_block(anon_const, Expectation::NoExpectation); + // This is mostly similar to type-checking of inline const expressions `const { ... }`, however + // asm const has special coercion rules (per RFC 3848) where function items and closures are coerced to + // function pointers (while pointers and integer remain as-is). + let body = self.tcx.hir_body(anon_const.body); + + let fcx = FnCtxt::new(self, self.param_env, anon_const.def_id); + let ty = fcx.check_expr(body.value); + let target_ty = match self.structurally_resolve_type(body.value.span, ty).kind() + { + ty::FnDef(..) => { + let fn_sig = ty.fn_sig(self.tcx()); + Ty::new_fn_ptr(self.tcx(), fn_sig) + } + ty::Closure(_, args) => { + let closure_sig = args.as_closure().sig(); + let fn_sig = + self.tcx().signature_unclosure(closure_sig, hir::Safety::Safe); + Ty::new_fn_ptr(self.tcx(), fn_sig) + } + _ => ty, + }; + + if let Err(diag) = + self.demand_coerce_diag(&body.value, ty, target_ty, None, AllowTwoPhase::No) + { + diag.emit(); + } + + fcx.require_type_is_sized( + target_ty, + body.value.span, + ObligationCauseCode::SizedConstOrStatic, + ); + fcx.write_ty(anon_const.hir_id, target_ty); } hir::InlineAsmOperand::SymFn { expr } => { self.check_expr(expr); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 9a1b1f8300957..b413d0c0bb2de 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -142,8 +142,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// version (resolve_vars_if_possible), this version will /// also select obligations if it seems useful, in an effort /// to get more type information. - // FIXME(-Znext-solver): A lot of the calls to this method should - // probably be `resolve_vars_with_obligations` or `structurally_resolve_type` instead. #[instrument(skip(self), level = "debug", ret)] pub(crate) fn resolve_vars_with_obligations>>( &self, @@ -696,6 +694,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { defining_opaque_types_and_generators } ty::TypingMode::Coherence + | ty::TypingMode::Reflection | ty::TypingMode::PostTypeckUntilBorrowck { .. } | ty::TypingMode::PostBorrowck { .. } | ty::TypingMode::PostAnalysis diff --git a/compiler/rustc_hir_typeck/src/inline_asm.rs b/compiler/rustc_hir_typeck/src/inline_asm.rs index 9dfbcd9dda760..b720a75303c47 100644 --- a/compiler/rustc_hir_typeck/src/inline_asm.rs +++ b/compiler/rustc_hir_typeck/src/inline_asm.rs @@ -17,7 +17,7 @@ use rustc_target::asm::{ use rustc_trait_selection::infer::InferCtxtExt; use crate::FnCtxt; -use crate::diagnostics::RegisterTypeUnstable; +use crate::diagnostics::{AsmConstPtrUnstable, RegisterTypeUnstable}; pub(crate) struct InlineAsmCtxt<'a, 'tcx> { target_features: &'tcx FxIndexSet, @@ -548,7 +548,36 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { match ty.kind() { ty::Error(_) => {} _ if ty.is_integral() => {} + ty::FnPtr(..) => { + if !self.tcx().features().asm_const_ptr() { + self.tcx() + .sess + .create_feature_err( + AsmConstPtrUnstable { span: op_sp }, + sym::asm_const_ptr, + ) + .emit(); + } + } + ty::RawPtr(pointee, _) | ty::Ref(_, pointee, _) + if self.is_thin_ptr_ty(*pointee) => + { + if !self.tcx().features().asm_const_ptr() { + self.tcx() + .sess + .create_feature_err( + AsmConstPtrUnstable { span: op_sp }, + sym::asm_const_ptr, + ) + .emit(); + } + } _ => { + let const_possible_ty = if !self.tcx().features().asm_const_ptr() { + "integer" + } else { + "integer or thin pointer" + }; self.fcx .dcx() .struct_span_err(op_sp, "invalid type for `const` operand") @@ -556,7 +585,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { self.tcx().def_span(anon_const.def_id), format!("is {} `{}`", ty.kind().article(), ty), ) - .with_help("`const` operands must be of an integer type") + .with_help(format!( + "`const` operands must be of an {const_possible_ty} type" + )) .emit(); } } diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index 797077d97c133..17e193d7f44ab 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -103,6 +103,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { defining_opaque_types_and_generators } ty::TypingMode::Coherence + | ty::TypingMode::Reflection | ty::TypingMode::PostTypeckUntilBorrowck { .. } | ty::TypingMode::PostBorrowck { .. } | ty::TypingMode::PostAnalysis diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index af1f5de717a60..72820a31a95b9 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -354,6 +354,7 @@ impl<'tcx> Drop for InferCtxt<'tcx> { TypingMode::Coherence | TypingMode::Typeck { .. } | TypingMode::PostBorrowck { .. } + | TypingMode::Reflection | TypingMode::PostAnalysis | TypingMode::Codegen => {} // In erased mode, the opaque type storage is always empty @@ -1171,6 +1172,7 @@ impl<'tcx> InferCtxt<'tcx> { // and post-borrowck analysis mode. We may need to modify its uses // to support PostBorrowck in the old solver as well. TypingMode::Coherence + | TypingMode::Reflection | TypingMode::PostBorrowck { .. } | TypingMode::PostAnalysis | TypingMode::Codegen => false, @@ -1505,6 +1507,7 @@ impl<'tcx> InferCtxt<'tcx> { mode @ (ty::TypingMode::Coherence | ty::TypingMode::PostBorrowck { .. } | ty::TypingMode::PostAnalysis + | ty::TypingMode::Reflection | ty::TypingMode::Codegen) => mode, ty::TypingMode::ErasedNotCoherence(MayBeErased) => unreachable!(), }; diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index 2d05e33e44891..08c7c49417124 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -285,7 +285,8 @@ impl<'tcx> InferCtxt<'tcx> { } mode @ (ty::TypingMode::PostBorrowck { .. } | ty::TypingMode::PostAnalysis - | ty::TypingMode::Codegen) => { + | ty::TypingMode::Codegen + | ty::TypingMode::Reflection) => { bug!("insert hidden type in {mode:?}") } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 2a138154f7020..9acebc1a4822f 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -241,6 +241,7 @@ provide! { tcx, def_id, other, cdata, fn_sig => { table } codegen_fn_attrs => { table } impl_trait_header => { table } + impl_is_fully_generic_for_reflection => { table_direct } const_param_default => { table } object_lifetime_default => { table } thir_abstract_const => { table } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 8fa0c1b2dcdd8..602c5ee0201dd 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2197,6 +2197,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let header = tcx.impl_trait_header(def_id); record!(self.tables.impl_trait_header[def_id] <- header); + let impl_is_fully_generic_for_reflection = + tcx.impl_is_fully_generic_for_reflection(def_id); + self.tables + .impl_is_fully_generic_for_reflection + .set(def_id.index, impl_is_fully_generic_for_reflection); + self.tables.defaultness.set(def_id.index, tcx.defaultness(def_id)); let trait_ref = header.trait_ref.instantiate_identity().skip_norm_wip(); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 4847ddda90fd1..237672878bb4c 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -413,6 +413,7 @@ define_tables! { constness: Table, safety: Table, defaultness: Table, + impl_is_fully_generic_for_reflection: Table, - optional: attributes: Table>, diff --git a/compiler/rustc_middle/src/mono.rs b/compiler/rustc_middle/src/mono.rs index bad6986a2630c..00bb3a2ee66a4 100644 --- a/compiler/rustc_middle/src/mono.rs +++ b/compiler/rustc_middle/src/mono.rs @@ -124,7 +124,7 @@ impl<'tcx> MonoItem<'tcx> { MonoItem::Fn(instance) => tcx.symbol_name(instance), MonoItem::Static(def_id) => tcx.symbol_name(Instance::mono(tcx, def_id)), MonoItem::GlobalAsm(item_id) => { - SymbolName::new(tcx, &format!("global_asm_{:?}", item_id.owner_id)) + tcx.symbol_name(Instance::mono(tcx, item_id.owner_id.to_def_id())) } } } @@ -349,6 +349,11 @@ pub struct CodegenUnit<'tcx> { /// contain something unique to this crate (e.g., a module path) /// as well as the crate name and disambiguator. name: Symbol, + + /// Symbol name for this CGU. Backend may emit symbols prefixed with this name + /// and assume uniqueness. + symbol_name: Option, + items: FxIndexMap, MonoItemData>, size_estimate: usize, primary: bool, @@ -405,6 +410,7 @@ impl<'tcx> CodegenUnit<'tcx> { pub fn new(name: Symbol) -> CodegenUnit<'tcx> { CodegenUnit { name, + symbol_name: None, items: Default::default(), size_estimate: 0, primary: false, @@ -445,6 +451,14 @@ impl<'tcx> CodegenUnit<'tcx> { self.is_code_coverage_dead_code_cgu = true; } + pub fn symbol_name(&self) -> Symbol { + self.symbol_name.expect("CGU symbol name accessed before setting") + } + + pub fn set_symbol_name(&mut self, name: Symbol) { + self.symbol_name = Some(name); + } + pub fn mangle_name(human_readable_name: &str) -> BaseNString { let mut hasher = StableHasher::new(); human_readable_name.hash(&mut hasher); diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index cbd54ec959c6a..387bf3f6f8c1f 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -1098,6 +1098,15 @@ rustc_queries! { separate_provide_extern } + /// Whether all generic parameters of the type are unique unconstrained generic parameters + /// of the impl. `Bar<'static>` or `Foo<'a, 'a>` or outlives bounds on the lifetimes cause + /// this boolean to be false and `try_as_dyn` to return `None`. + query impl_is_fully_generic_for_reflection(impl_id: DefId) -> bool { + desc { "computing trait implemented by `{}`", tcx.def_path_str(impl_id) } + cache_on_disk + separate_provide_extern + } + /// Given an `impl_def_id`, return true if the self type is guaranteed to be unsized due /// to either being one of the built-in unsized types (str/slice/dyn) or to be a struct /// whose tail is one of those types. diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 92f7ed0cb19f6..a2eecebcc3501 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -180,6 +180,8 @@ pub enum SelectionCandidate<'tcx> { BuiltinUnsizeCandidate, BikeshedGuaranteedNoDropCandidate, + + TryAsDynCandidate, } /// The result of trait evaluation. The order is important diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 052a937cf5017..0f5ec3e04ddce 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -703,6 +703,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.impl_polarity(impl_def_id) } + fn is_fully_generic_for_reflection(self, impl_def_id: Self::ImplId) -> bool { + self.impl_is_fully_generic_for_reflection(impl_def_id) + } + fn trait_is_auto(self, trait_def_id: DefId) -> bool { self.trait_is_auto(trait_def_id) } @@ -934,6 +938,7 @@ bidirectional_lang_item_map! { Sized, TransmuteTrait, TrivialClone, + TryAsDyn, Tuple, Unpin, Unsize, diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 02ac0586c33ae..9a6b3dbea1ef3 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -1,14 +1,16 @@ +use std::ops::ControlFlow; + use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_macros::{StableHash, TyDecodable, TyEncodable}; use rustc_span::{Span, Symbol, kw}; +use rustc_type_ir::{TypeSuperVisitable as _, TypeVisitable, TypeVisitor}; use tracing::instrument; use super::{Clause, InstantiatedPredicates, ParamConst, ParamTy, Ty, TyCtxt, Unnormalized}; -use crate::ty; use crate::ty::region::RegionExt; -use crate::ty::{EarlyBinder, GenericArgsRef}; +use crate::ty::{self, ClauseKind, EarlyBinder, GenericArgsRef, Region, RegionKind, TyKind}; #[derive(Clone, Debug, TyEncodable, TyDecodable, StableHash)] pub enum GenericParamDefKind { @@ -456,6 +458,76 @@ impl<'tcx> GenericPredicates<'tcx> { instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| Unnormalized::new(*p))); instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s)); } + + /// Allow simple where bounds like `T: Debug`, but prevent any kind of + /// outlives bounds or uses of generic parameters on the right hand side. + /// + /// We allow simple bounds because when the `T` actually gets substituted with a concrete type + /// during monomorphization, we will be checking its `Debug` impl for fully_generic_for_reflection. + /// + /// Constants (associated or generic) are irrelevant for this analysis, as their value is neither + /// affected by lifetimes, nor do they affect lifetimes. + pub fn is_fully_generic_for_reflection(self) -> bool { + struct ParamChecker; + impl<'tcx> TypeVisitor> for ParamChecker { + type Result = ControlFlow<()>; + fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result { + match r.kind() { + RegionKind::ReEarlyParam(_) | RegionKind::ReStatic | RegionKind::ReError(_) => { + ControlFlow::Break(()) + } + RegionKind::ReVar(_) + | RegionKind::RePlaceholder(_) + | RegionKind::ReErased + | RegionKind::ReLateParam(_) => { + bug!("unexpected lifetime in impl: {r:?}") + } + RegionKind::ReBound(..) => ControlFlow::Continue(()), + } + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + match t.kind() { + TyKind::Param(_p) => { + // Reject using parameters used in the type in where bounds + return ControlFlow::Break(()); + } + TyKind::Alias(..) => return ControlFlow::Break(()), + _ => (), + } + t.super_visit_with(self) + } + } + + // Pessimistic: if any of the parameters have where bounds + // don't allow this impl to be used. + self.predicates.iter().all(|(clause, _)| { + match clause.kind().skip_binder() { + ClauseKind::Trait(trait_predicate) => { + // In a `T: Trait`, if the rhs bound does not contain any generic params + // or 'static lifetimes, then it cannot transitively cause such requirements, + // considering we apply the fully-generic-for-reflection rules to any impls for + // that trait, too. + if matches!(trait_predicate.self_ty().kind(), ty::Param(_)) + && trait_predicate.trait_ref.args[1..] + .iter() + .all(|arg| arg.visit_with(&mut ParamChecker).is_continue()) + { + return true; + } + } + ClauseKind::RegionOutlives(_) + | ClauseKind::TypeOutlives(_) + | ClauseKind::Projection(_) + | ClauseKind::ConstArgHasType(_, _) + | ClauseKind::WellFormed(_) + | ClauseKind::ConstEvaluatable(_) + | ClauseKind::HostEffect(_) + | ClauseKind::UnstableFeature(_) => {} + } + clause.visit_with(&mut ParamChecker).is_continue() + }) + } } /// `[const]` bounds for a given item. This is represented using a struct much like diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 9b582eeb2c520..151ca10b85ef6 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -16,6 +16,7 @@ use std::fmt::Debug; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::num::NonZero; +use std::ops::ControlFlow; use std::ptr::NonNull; use std::{assert_matches, fmt, iter, str}; @@ -30,7 +31,7 @@ use rustc_abi::{ use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, NodeId}; pub use rustc_ast_ir::{Movability, Mutability, try_visit}; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher}; use rustc_data_structures::steal::Steal; @@ -309,6 +310,64 @@ pub struct ImplTraitHeader<'tcx> { pub constness: hir::Constness, } +impl<'tcx> ImplTraitHeader<'tcx> { + /// For trait impls, checks whether + /// * the type and trait only use generic lifetime arguments (and no concrete ones like `'static`), and + /// * uses any generic param (lifetime or type) only once. + /// + /// This is a pessimistic analysis, so it will reject alias types + /// and other types that may be actually ok. We can allow more in the future. + /// + /// Constants (associated or generic) are irrelevant for this analysis, as their value is neither + /// affected by lifetimes, nor do they affect lifetimes. + pub fn is_fully_generic_for_reflection(self) -> bool { + #[derive(Default)] + struct ParamFinder { + seen: FxHashSet, + } + + impl<'tcx> TypeVisitor> for ParamFinder { + type Result = ControlFlow<()>; + fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result { + match r.kind() { + RegionKind::ReEarlyParam(param) => { + if self.seen.insert(param.index) { + ControlFlow::Continue(()) + } else { + ControlFlow::Break(()) + } + } + RegionKind::ReBound(..) => ControlFlow::Continue(()), + RegionKind::ReStatic | RegionKind::ReError(_) => ControlFlow::Break(()), + RegionKind::ReVar(_) + | RegionKind::RePlaceholder(_) + | RegionKind::ReErased + | RegionKind::ReLateParam(_) => bug!("unexpected lifetime in impl: {r:?}"), + } + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + match t.kind() { + TyKind::Param(p) => { + // Reject using a parameter twice (e.g. in `Foo`) + if !self.seen.insert(p.index) { + return ControlFlow::Break(()); + } + } + TyKind::Alias(..) => return ControlFlow::Break(()), + _ => (), + } + t.super_visit_with(self) + } + } + self.trait_ref + .instantiate_identity() + .skip_norm_wip() + .visit_with(&mut ParamFinder::default()) + .is_continue() + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, StableHash, Debug)] #[derive(TypeFoldable, TypeVisitable, Default)] pub enum Asyncness { @@ -1178,6 +1237,7 @@ impl<'tcx> TypingEnv<'tcx> { let TypingEnv { typing_mode, param_env } = self; match typing_mode.0.assert_not_erased() { TypingMode::Coherence + | TypingMode::Reflection | TypingMode::Typeck { .. } | TypingMode::PostTypeckUntilBorrowck { .. } | TypingMode::PostBorrowck { .. } => {} @@ -1194,6 +1254,7 @@ impl<'tcx> TypingEnv<'tcx> { let TypingEnv { typing_mode, param_env } = self; match typing_mode.0.assert_not_erased() { TypingMode::Coherence + | TypingMode::Reflection | TypingMode::Typeck { .. } | TypingMode::PostTypeckUntilBorrowck { .. } | TypingMode::PostBorrowck { .. } diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 2b786b7e9e1a2..7adeebd235384 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -243,7 +243,7 @@ fn evaluate_candidate<'tcx>( return None; } - // We only handle: + // For now, we only handle: // ``` // bb4: { // _8 = discriminant((_3.1: Enum1)); @@ -262,41 +262,8 @@ fn evaluate_candidate<'tcx>( // When thie BB has exactly one statement, this statement should be discriminant. let need_hoist_discriminant = bbs[child].statements.len() == 1; + let otherwise_is_empty_unreachable = bbs[targets.otherwise()].is_empty_unreachable(); let child_place = if need_hoist_discriminant { - if !bbs[targets.otherwise()].is_empty_unreachable() { - // Someone could write code like this: - // ```rust - // let Q = val; - // if discriminant(P) == otherwise { - // let ptr = &mut Q as *mut _ as *mut u8; - // // It may be difficult for us to effectively determine whether values are valid. - // // Invalid values can come from all sorts of corners. - // unsafe { *ptr = 10; } - // } - // - // match P { - // A => match Q { - // A => { - // // code - // } - // _ => { - // // don't use Q - // } - // } - // _ => { - // // don't use Q - // } - // }; - // ``` - // - // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an - // invalid value, which is UB. - // In order to fix this, **we would either need to show that the discriminant computation of - // `place` is computed in all branches**. - // FIXME(#95162) For the moment, we adopt a conservative approach and - // consider only the `otherwise` branch has no statements and an unreachable terminator. - return None; - } // Handle: // ``` // bb4: { @@ -325,8 +292,7 @@ fn evaluate_candidate<'tcx>( }; *child_place }; - let destination = if need_hoist_discriminant || bbs[targets.otherwise()].is_empty_unreachable() - { + let destination = if otherwise_is_empty_unreachable { child_targets.otherwise() } else { targets.otherwise() @@ -340,6 +306,7 @@ fn evaluate_candidate<'tcx>( child_place, destination, need_hoist_discriminant, + otherwise_is_empty_unreachable, ) { return None; } @@ -359,11 +326,67 @@ fn verify_candidate_branch<'tcx>( place: Place<'tcx>, destination: BasicBlock, need_hoist_discriminant: bool, + otherwise_is_empty_unreachable: bool, ) -> bool { // In order for the optimization to be correct, the terminator must be a `SwitchInt`. let TerminatorKind::SwitchInt { discr: switch_op, targets } = &branch.terminator().kind else { return false; }; + if !otherwise_is_empty_unreachable { + // Someone could write code like this: + // ```rust + // let Q = val; + // if discriminant(P) == otherwise { + // let ptr = &mut Q as *mut _ as *mut u8; + // // It may be difficult for us to effectively determine whether values are valid. + // // Invalid values can come from all sorts of corners. + // unsafe { *ptr = 10; } + // } + // + // match P { + // A => match Q { + // A => { + // // code + // } + // _ => { + // // don't use Q + // } + // } + // _ => { + // // don't use Q + // } + // }; + // ``` + // + // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an + // invalid value, which is UB. + // In order to fix this, **we would either need to show that the discriminant computation of + // `place` is computed in all branches**. + // For , we adopt a conservative approach and + // consider only the `otherwise` branch has no statements and an unreachable terminator. + if need_hoist_discriminant { + return false; + } + // For : + // ``` + // bb0: { + // switchInt(copy _1) -> [1: bb1, 2: bb2, otherwise: bb5]; + // } + // bb1: { + // switchInt(copy (*_2)) -> [1: bb3, otherwise: bb5]; + // } + // bb2: { + // switchInt(copy (*_2)) -> [2: bb4, otherwise: bb5]; + // } + // ``` + // We cannot hoist the dereference of `_2` to `bb0`, + // because execution can reach `bb5` without dereferencing `_2`. + if let Some(place) = switch_op.place() + && !place.is_stable_offset() + { + return false; + } + } if need_hoist_discriminant { // If we need hoist discriminant, the branch must have exactly one statement. let [statement] = branch.statements.as_slice() else { diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index 8b21e8284476b..c2c702dbf2470 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -802,6 +802,7 @@ where match self.elaborator.typing_env().typing_mode().assert_not_erased() { ty::TypingMode::PostAnalysis | ty::TypingMode::Codegen => {} ty::TypingMode::Coherence + | ty::TypingMode::Reflection | ty::TypingMode::Typeck { .. } | ty::TypingMode::PostTypeckUntilBorrowck { .. } | ty::TypingMode::PostBorrowck { .. } => { diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index 552c092ef7c46..58ccf77903bab 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -14,6 +14,7 @@ rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } serde = "1" serde_json = "1" diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index c0059ca2ba852..220be82574db7 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -503,10 +503,18 @@ fn collect_items_rec<'tcx>( if let hir::ItemKind::GlobalAsm { asm, .. } = item.kind { for (op, op_sp) in asm.operands { match *op { - hir::InlineAsmOperand::Const { .. } => { - // Only constants which resolve to a plain integer - // are supported. Therefore the value should not - // depend on any other items. + hir::InlineAsmOperand::Const { anon_const } => { + match tcx.const_eval_poly(anon_const.def_id.to_def_id()) { + Ok(val) => { + collect_const_value(tcx, val, &mut used_items); + } + Err(ErrorHandled::TooGeneric(..)) => { + span_bug!(*op_sp, "asm const cannot be resolved; too generic") + } + Err(ErrorHandled::Reported(..)) => { + continue; + } + } } hir::InlineAsmOperand::SymFn { expr } => { let fn_ty = tcx.typeck(item_id.owner_id).expr_ty(expr); diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index fa09e7b49de76..5cfae525d7e5e 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -98,6 +98,7 @@ use std::fs::{self, File}; use std::io::Write; use std::path::{Path, PathBuf}; +use rustc_data_structures::either::Either; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sync::par_join; use rustc_data_structures::unord::{UnordMap, UnordSet}; @@ -457,6 +458,13 @@ fn merge_codegen_units<'tcx>( }; cgu.set_name(new_cgu_name); } + + // Assign symbol name to each CGU units. + cgu.set_symbol_name(Symbol::intern(&rustc_symbol_mangling::mangle_cgu( + cx.tcx, + LOCAL_CRATE, + Either::Right(cgu.name().as_str()), + ))); } // A sorted order here ensures what follows can be deterministic. @@ -491,6 +499,12 @@ fn merge_codegen_units<'tcx>( let numbered_codegen_unit_name = cgu_name_builder.build_cgu_name_no_mangle(LOCAL_CRATE, &["cgu"], Some(suffix)); cgu.set_name(numbered_codegen_unit_name); + + cgu.set_symbol_name(Symbol::intern(&rustc_symbol_mangling::mangle_cgu( + cx.tcx, + LOCAL_CRATE, + Either::Left(index.try_into().unwrap()), + ))); } } } diff --git a/compiler/rustc_next_trait_solver/src/placeholder.rs b/compiler/rustc_next_trait_solver/src/placeholder.rs index 04247a17edcca..83b2eb6ac6295 100644 --- a/compiler/rustc_next_trait_solver/src/placeholder.rs +++ b/compiler/rustc_next_trait_solver/src/placeholder.rs @@ -47,6 +47,7 @@ where IndexMap, ty::BoundTy>, IndexMap, ty::BoundConst>, ) { + let old_universes = universe_indices.clone(); let mut replacer = BoundVarReplacer { infcx, mapped_regions: Default::default(), @@ -57,8 +58,29 @@ where }; let value = value.fold_with(&mut replacer); + let BoundVarReplacer { + mapped_regions, + mapped_types, + mapped_consts, + universe_indices, + infcx: _, + current_index: _, + } = replacer; + + if infcx.cx().assumptions_on_binders() { + for (old, new) in old_universes.into_iter().zip(universe_indices.iter()) { + if let (None, Some(new)) = (old, new) { + // FIXME(-Zassumptions-on-binders): `replace_bound_vars` does not have enough + // context to compute placeholder assumptions for the binders it enters. + infcx.insert_placeholder_assumptions( + *new, + Some(rustc_type_ir::region_constraint::Assumptions::empty()), + ); + } + } + } - (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts) + (value, mapped_regions, mapped_types, mapped_consts) } fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex { diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 6d111ff44d415..040f98de7bcfd 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -365,6 +365,11 @@ where goal: Goal, ) -> Result, NoSolutionOrRerunNonErased>; + fn consider_builtin_try_as_dyn_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolutionOrRerunNonErased>; + /// Consider (possibly several) candidates to upcast or unsize a type to another /// type, excluding the coercion of a sized type into a `dyn Trait`. /// @@ -483,6 +488,7 @@ where TypingMode::Coherence => true, TypingMode::Typeck { .. } | TypingMode::PostTypeckUntilBorrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowck { .. } | TypingMode::PostAnalysis | TypingMode::Codegen @@ -568,6 +574,14 @@ where let cx = self.cx(); let trait_def_id = goal.predicate.trait_def_id(cx); + // Builtin impls regularly are not `is_fully_generic_for_reflection`, so instead + // of trying to handle these manually, we just reject all builtin impls in reflection + // mode. We can probably lift this restriction for specific cases, but this is safer. + // See `try_as_dyn_builtin_impl` for how just allowing all builtin impls is unsound. + if self.typing_mode().is_reflection() { + return Ok(()); + } + // N.B. When assembling built-in candidates for lang items that are also // `auto` traits, then the auto trait candidate that is assembled in // `consider_auto_trait_candidate` MUST be disqualified to remain sound. @@ -660,6 +674,9 @@ where Some(SolverTraitLangItem::BikeshedGuaranteedNoDrop) => { G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal) } + Some(SolverTraitLangItem::TryAsDyn) => { + G::consider_builtin_try_as_dyn_candidate(self, goal) + } Some(SolverTraitLangItem::Field) => G::consider_builtin_field_candidate(self, goal), _ => Err(NoSolution.into()), } @@ -872,6 +889,14 @@ where return; } + // Builtin impls regularly are not `is_fully_generic_for_reflection`, so instead + // of trying to handle these manually, we just reject all builtin impls in reflection + // mode. We can probably lift this restriction for specific cases, but this is safer. + // See `try_as_dyn_builtin_impl` for how just allowing all builtin impls is unsound. + if self.typing_mode().is_reflection() { + return; + } + let self_ty = goal.predicate.self_ty(); let bounds = match self_ty.kind() { ty::Bool @@ -1066,6 +1091,7 @@ where | TypingMode::PostTypeckUntilBorrowck { .. } | TypingMode::PostBorrowck { .. } | TypingMode::PostAnalysis + | TypingMode::Reflection | TypingMode::Codegen => vec![], TypingMode::ErasedNotCoherence(MayBeErased) => { self.opaque_accesses diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 1c53bc7711ed8..df9758d5d2ab7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -451,6 +451,13 @@ where unreachable!("BikeshedGuaranteedNoDrop is not const"); } + fn consider_builtin_try_as_dyn_candidate( + _ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolutionOrRerunNonErased> { + unreachable!("`TryAsDynCompat` is not const: {:?}", goal) + } + fn consider_structural_builtin_unsize_candidates( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index c3ccb46069063..16f8f3496d9e7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -566,28 +566,34 @@ where .entered(); let (result, orig_values, canonical_goal, succeeded_in_erased) = 'retry_canonicalize: { - let skip_erased_attempt = if typing_mode.is_coherence() { - true - } else { - let mut skip = false; - if opaque_types.iter().any(|(_, ty)| ty.is_ty_var()) - && let PredicateKind::Clause(ClauseKind::Trait(..)) = + let skip_erased_attempt = match typing_mode { + TypingMode::Reflection | TypingMode::Coherence => true, + TypingMode::Typeck { .. } + | TypingMode::PostTypeckUntilBorrowck { .. } + | TypingMode::PostBorrowck { .. } + | TypingMode::Codegen + | TypingMode::PostAnalysis + | TypingMode::ErasedNotCoherence(_) => { + let mut skip = false; + if opaque_types.iter().any(|(_, ty)| ty.is_ty_var()) + && let PredicateKind::Clause(ClauseKind::Trait(..)) = + goal.predicate.kind().skip_binder() + { + skip = true; + } + + if let PredicateKind::Clause(ClauseKind::Trait(tr)) = goal.predicate.kind().skip_binder() - { - skip = true; - } + && tr.self_ty().has_coroutines() + && self.cx().trait_is_auto(tr.trait_ref.def_id) + { + // FIXME(#155443): this doesn't make a difference now, but with eager normalization + // it likely will. + // skip_erased_attempt = true; + } - if let PredicateKind::Clause(ClauseKind::Trait(tr)) = - goal.predicate.kind().skip_binder() - && tr.self_ty().has_coroutines() - && self.cx().trait_is_auto(tr.trait_ref.def_id) - { - // FIXME(#155443): this doesn't make a difference now, but with eager normalization - // it likely will. - // skip_erased_attempt = true; + skip } - - skip }; if skip_erased_attempt { @@ -1649,9 +1655,10 @@ fn should_rerun_after_erased_canonicalization( // ============================= (RerunCondition::Always, _) => RerunDecision::Yes, // ============================= - (RerunCondition::OpaqueInStorage(..), TypingMode::PostAnalysis | TypingMode::Codegen) => { - RerunDecision::Yes - } + ( + RerunCondition::OpaqueInStorage(..), + TypingMode::PostAnalysis | TypingMode::Codegen | TypingMode::Reflection, + ) => RerunDecision::Yes, ( RerunCondition::OpaqueInStorage(defids), TypingMode::PostBorrowck { defined_opaque_types: opaques } @@ -1667,12 +1674,13 @@ fn should_rerun_after_erased_canonicalization( TypingMode::PostBorrowck { .. } | TypingMode::PostAnalysis | TypingMode::Codegen + | TypingMode::Reflection | TypingMode::PostTypeckUntilBorrowck { .. }, ) => RerunDecision::No, // ============================= ( RerunCondition::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(_), - TypingMode::PostAnalysis | TypingMode::Codegen, + TypingMode::PostAnalysis | TypingMode::Codegen | TypingMode::Reflection, ) => RerunDecision::Yes, ( RerunCondition::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(defids), diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs index 582cd122c8ecf..069102f3db50b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs @@ -348,6 +348,7 @@ where | ty::TypingMode::PostTypeckUntilBorrowck { .. } | ty::TypingMode::PostBorrowck { .. } | ty::TypingMode::PostAnalysis + | ty::TypingMode::Reflection | ty::TypingMode::Codegen => { ecx.instantiate_normalizes_to_as_rigid(goal)?; return ecx.evaluate_added_goals_and_make_canonical_response( @@ -1076,6 +1077,13 @@ where ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } + + fn consider_builtin_try_as_dyn_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal, + ) -> Result, NoSolutionOrRerunNonErased> { + unreachable!("try_as_dyn helper trait doesn't have assoc types") + } } impl EvalCtxt<'_, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs index 7524b2021c015..3387c8599b911 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs @@ -106,6 +106,7 @@ where TypingMode::Coherence | TypingMode::PostBorrowck { .. } | TypingMode::PostAnalysis + | TypingMode::Reflection | TypingMode::Codegen => unreachable!(), } } @@ -149,7 +150,8 @@ where self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into) } - TypingMode::PostAnalysis | TypingMode::Codegen => { + // FIXME(try_as_dyn): probably want to treat opaques opaquely and rigid + TypingMode::Reflection | TypingMode::PostAnalysis | TypingMode::Codegen => { // FIXME: Add an assertion that opaque type storage is empty. let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args); let actual = self.normalize(GoalSource::Misc, goal.param_env, actual)?; diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 778826ba60aee..4918258350bf3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -70,6 +70,7 @@ where } TypingMode::Typeck { .. } | TypingMode::PostTypeckUntilBorrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowck { .. } | TypingMode::PostAnalysis | TypingMode::Codegen diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 2ebea5d3eb188..f29df578cd97b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -10,9 +10,9 @@ use rustc_type_ir::solve::{ RerunReason, RerunResultExt, SizedTraitKind, }; use rustc_type_ir::{ - self as ty, FieldInfo, Interner, MayBeErased, Movability, PredicatePolarity, Region, - TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode, Unnormalized, Upcast as _, - elaborate, + self as ty, ExistentialPredicate, FieldInfo, Interner, MayBeErased, Movability, + PredicatePolarity, Region, TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode, + Unnormalized, Upcast as _, elaborate, }; use tracing::{debug, instrument, trace, warn}; @@ -87,7 +87,15 @@ where // Impl matches polarity (ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive) - | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => Certainty::Yes, + | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => { + if ecx.typing_mode().is_reflection() + && !cx.is_fully_generic_for_reflection(impl_def_id) + { + return Err(NoSolution.into()); + } else { + Certainty::Yes + } + } // Impl doesn't match polarity (ty::ImplPolarity::Positive, ty::PredicatePolarity::Negative) @@ -865,6 +873,49 @@ where } } + fn consider_builtin_try_as_dyn_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolutionOrRerunNonErased> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution.into()); + } + let cx = ecx.cx(); + + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + let self_ty = goal.predicate.self_ty(); + let ty_lifetime = goal.predicate.trait_ref.args.region_at(1); + match self_ty.kind() { + ty::Dynamic(bounds, lifetime) => { + for bound in bounds.iter() { + match bound.skip_binder() { + ExistentialPredicate::Trait(_) => {} + // FIXME(try_as_dyn): check what kind of projections we can allow + ExistentialPredicate::Projection(_) => return Err(NoSolution.into()), + // Auto traits do not affect lifetimes outside of specialization, + // which is disabled in reflection. + ExistentialPredicate::AutoTrait(_) => {} + } + } + ecx.add_goal( + GoalSource::Misc, + goal.with(cx, ty::OutlivesPredicate(ty_lifetime, lifetime)), + )?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + ty::Bound(..) + | ty::Infer( + ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_), + ) => { + panic!("unexpected type `{self_ty:?}`") + } + + _ => Err(NoSolution.into()), + } + }) + } + fn consider_builtin_field_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, @@ -1607,6 +1658,7 @@ where } TypingMode::Coherence | TypingMode::PostAnalysis + | TypingMode::Reflection | TypingMode::Codegen | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: _ } | TypingMode::PostBorrowck { defined_opaque_types: _ } => {} diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 032cbcbc1e794..920cce82dea18 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -781,22 +781,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { sig_span: sig.span, }); } - - if let Some(impls) = find_attr!(attrs, EiiImpls(impls) => impls) { - let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); - for i in impls { - let name = match i.resolution { - EiiImplResolution::Macro(def_id) => self.tcx.item_name(def_id), - EiiImplResolution::Known(def_id) => self.tcx.item_name(def_id), - EiiImplResolution::Error(_eg) => continue, - }; - self.dcx().emit_err(diagnostics::EiiWithTrackCaller { - attr_span, - name, - sig_span: sig.span, - }); - } - } } _ => {} } diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index bd58ea7142e5b..34fa0b264319d 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -1082,16 +1082,6 @@ pub(crate) struct EiiImplRequiresUnsafeSuggestion { pub right: Span, } -#[derive(Diagnostic)] -#[diag("`#[{$name}]` is not allowed to have `#[track_caller]`")] -pub(crate) struct EiiWithTrackCaller { - #[primary_span] - pub attr_span: Span, - pub name: Symbol, - #[label("`#[{$name}]` is not allowed to have `#[track_caller]`")] - pub sig_span: Span, -} - #[derive(Diagnostic)] #[diag("`#[{$name}]` {$kind} required, but not found")] pub(crate) struct EiiWithoutImpl { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7917478cc2c60..4b0255fee8b0f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -426,6 +426,7 @@ symbols! { asm, asm_cfg, asm_const, + asm_const_ptr, asm_experimental_arch, asm_experimental_reg, asm_goto, @@ -2139,6 +2140,7 @@ symbols! { truncf32, truncf64, truncf128, + try_as_dyn, try_blocks, try_blocks_heterogeneous, try_capture, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 4cce56cf90426..a275c68bbdc17 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -37,6 +37,11 @@ pub(super) fn mangle<'tcx>( debug!(?instance_ty); break; } + DefPathData::GlobalAsm => { + // `global_asm!` doesn't have a type. + instance_ty = tcx.types.unit; + break; + } _ => { // if we're making a symbol for something, there ought // to be a value or type-def or something in there diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index d61437212a266..ea65baf610b9a 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -103,7 +103,7 @@ mod v0; pub mod test; -pub use v0::mangle_internal_symbol; +pub use v0::{mangle_cgu, mangle_internal_symbol}; /// This function computes the symbol name for the given `instance` and the /// given instantiating crate. That is, if you know that instance X is diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index a0fe69ca2c59c..6294b3272d497 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -5,6 +5,7 @@ use std::ops::Range; use rustc_abi::{ExternAbi, Integer}; use rustc_data_structures::base_n::ToBaseN; +use rustc_data_structures::either::Either; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hash::StableHasher; @@ -90,6 +91,42 @@ pub(super) fn mangle<'tcx>( std::mem::take(&mut p.out) } +pub fn mangle_cgu<'tcx>(tcx: TyCtxt<'tcx>, krate: CrateNum, cgu_name: Either) -> String { + let prefix = "_R"; + let mut p: V0SymbolMangler<'_> = V0SymbolMangler { + tcx, + start_offset: prefix.len(), + is_exportable: false, + paths: FxHashMap::default(), + types: FxHashMap::default(), + consts: FxHashMap::default(), + binders: vec![], + out: String::from(prefix), + }; + + match cgu_name { + Either::Left(cgu_index) => { + // If we have a CGU index, we can easily encode this with the shim mechanism. + p.path_append_ns(|p| p.print_def_path(krate.as_def_id(), &[]), 'S', cgu_index, "cgu") + .unwrap(); + } + Either::Right(name) => { + // In incremental compilation we just have a name and no index. Encode this as a str-typed generic argument to cgu shim for now. + p.out.push('I'); + p.path_append_ns(|p| p.print_def_path(krate.as_def_id(), &[]), 'S', 0, "cgu").unwrap(); + p.push("KRe"); + + for byte in name.as_bytes() { + let _ = write!(p.out, "{byte:02x}"); + } + + p.push("_E"); + } + } + + std::mem::take(&mut p.out) +} + pub fn mangle_internal_symbol<'tcx>(tcx: TyCtxt<'tcx>, item_name: &str) -> String { match item_name { // rust_eh_personality must not be renamed as LLVM hard-codes the name diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 605f4d6758a5a..f5a30d2b26563 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -384,6 +384,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< TypingMode::Coherence | TypingMode::Typeck { .. } | TypingMode::PostTypeckUntilBorrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowck { .. } => false, TypingMode::PostAnalysis | TypingMode::Codegen => { let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref); diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 8d5d8f26f9dce..a61c679c9870f 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -340,6 +340,7 @@ where TypingMode::Coherence | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: _ } | TypingMode::PostBorrowck { defined_opaque_types: _ } + | TypingMode::Reflection | TypingMode::PostAnalysis | TypingMode::Codegen => return Default::default(), }; diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 95f0c3dc9881d..4ec21b9f8a4cd 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -178,6 +178,7 @@ where TypingMode::Coherence | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: _ } | TypingMode::PostBorrowck { defined_opaque_types: _ } + | TypingMode::Reflection | TypingMode::PostAnalysis | TypingMode::Codegen => return Default::default(), }; diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 9364482a87cb8..b822c713bfabb 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -144,7 +144,7 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable>>( | TypingMode::Typeck { .. } | TypingMode::PostTypeckUntilBorrowck { .. } | TypingMode::PostBorrowck { .. } => flags.remove(ty::TypeFlags::HAS_TY_OPAQUE), - TypingMode::PostAnalysis | TypingMode::Codegen => {} + TypingMode::Reflection | TypingMode::PostAnalysis | TypingMode::Codegen => {} } value.has_type_flags(flags) @@ -433,7 +433,7 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx | TypingMode::Typeck { .. } | TypingMode::PostTypeckUntilBorrowck { .. } | TypingMode::PostBorrowck { .. } => ty.super_fold_with(self), - TypingMode::PostAnalysis | TypingMode::Codegen => { + TypingMode::Reflection | TypingMode::PostAnalysis | TypingMode::Codegen => { let recursion_limit = self.cx().recursion_limit(); if !recursion_limit.value_within_limit(self.depth) { self.selcx.infcx.err_ctxt().report_overflow_error( diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index e0edc76682a3b..79b531afbe2ae 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -991,6 +991,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( TypingMode::Coherence | TypingMode::Typeck { .. } | TypingMode::PostTypeckUntilBorrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowck { .. } => { debug!( assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id), diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index ac6100747970e..fb15ed8a74c07 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -220,7 +220,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { | TypingMode::PostTypeckUntilBorrowck { .. } | TypingMode::PostBorrowck { .. } => ty.try_super_fold_with(self)?, - TypingMode::PostAnalysis | TypingMode::Codegen => { + TypingMode::Reflection | TypingMode::PostAnalysis | TypingMode::Codegen => { let args = data.args.try_fold_with(self)?; let recursion_limit = self.cx().recursion_limit(); diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index a4f751e23d799..d4027fcf388b1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -15,7 +15,8 @@ use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind}; use rustc_infer::traits::{Obligation, PolyTraitObligation, PredicateObligation, SelectionError}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{ - self, FieldInfo, SizedTraitKind, TraitRef, Ty, TypeVisitableExt, elaborate, + self, ExistentialPredicate, FieldInfo, SizedTraitKind, TraitRef, Ty, TypeVisitableExt, + elaborate, }; use rustc_middle::{bug, span_bug}; use rustc_span::DUMMY_SP; @@ -132,6 +133,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut candidates, ); } + Some(LangItem::TryAsDyn) => { + self.assemble_candidates_for_try_as_dyn(obligation, &mut candidates); + } Some(LangItem::Field) => { self.assemble_candidates_for_field_trait(obligation, &mut candidates); } @@ -1457,6 +1461,34 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + fn assemble_candidates_for_try_as_dyn( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + match *obligation.predicate.self_ty().skip_binder().kind() { + ty::Dynamic(bounds, _lifetime) => { + for bound in bounds { + match bound.skip_binder() { + ExistentialPredicate::Trait(_) => {} + // FIXME(try_as_dyn): check what kind of projections we can allow + ExistentialPredicate::Projection(_) => return, + // Auto traits do not affect lifetimes outside of specialization, + // which is disabled in reflection. + ExistentialPredicate::AutoTrait(_) => {} + } + } + candidates.vec.push(TryAsDynCandidate); + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + candidates.ambiguous = true; + } + + _ => {} + } + } + fn assemble_candidates_for_field_trait( &mut self, obligation: &PolyTraitObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index a7c84e71a68c5..49c277b39289b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -138,6 +138,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { BikeshedGuaranteedNoDropCandidate => { self.confirm_bikeshed_guaranteed_no_drop_candidate(obligation) } + + TryAsDynCandidate => self.confirm_try_as_dyn_candidate(obligation), }) } @@ -1315,4 +1317,35 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, obligations) } + + fn confirm_try_as_dyn_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> ImplSource<'tcx, PredicateObligation<'tcx>> { + let tcx = self.tcx(); + + let mut obligations = PredicateObligations::new(); + + let self_ty = obligation.predicate.self_ty(); + let ty_lifetime = obligation.predicate.map_bound(|p| p.trait_ref.args.region_at(1)); + + match *self_ty.skip_binder().kind() { + ty::Dynamic(_bounds, lifetime) => { + obligations.push( + obligation.with( + tcx, + ty_lifetime + .map_bound(|ty_lifetime| ty::OutlivesPredicate(ty_lifetime, lifetime)), + ), + ); + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + panic!("unexpected type `{self_ty:?}`") + } + + _ => {} + } + ImplSource::Builtin(BuiltinImplSource::Misc, obligations) + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 7c9ae9246c96b..58f2d0c7f33ac 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -17,7 +17,7 @@ use rustc_infer::infer::BoundRegionConversionTime::{self, HigherRankedType}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::relate::TypeRelation; -use rustc_infer::traits::{PredicateObligations, TraitObligation}; +use rustc_infer::traits::{ImplSource, PredicateObligations, TraitObligation}; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::bug; use rustc_middle::dep_graph::{DepKind, DepNodeIndex}; @@ -282,6 +282,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Err(SelectionError::Overflow(OverflowError::Canonical)) } Err(e) => Err(e), + Ok(ImplSource::Builtin(..)) if self.typing_mode().is_reflection() => { + // Builtin impls regularly don't satisfy the try_as_dyn requirements, so + // we just reject all of them. + Err(SelectionError::Unimplemented) + } Ok(candidate) => Ok(Some(candidate)), } } @@ -1299,6 +1304,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match this.confirm_candidate(stack.obligation, candidate.clone()) { Ok(selection) => { debug!(?selection); + if let ImplSource::Builtin(..) = selection + && this.typing_mode().is_reflection() + { + return Ok(EvaluatedToErr); + } this.evaluate_predicates_recursively( stack.list(), selection.nested_obligations().into_iter(), @@ -1485,6 +1495,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { TypingMode::Coherence => {} TypingMode::Typeck { .. } | TypingMode::PostTypeckUntilBorrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowck { .. } | TypingMode::PostAnalysis | TypingMode::Codegen => return Ok(()), @@ -1536,6 +1547,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { defining_opaque_types.is_empty() || (!pred.has_opaque_types() && !pred.has_coroutines()) } + // Impls that are not fully generic are completely ignored as "nonexistent" + // in this mode, so the results wildly differ from normal trait solving. + TypingMode::Reflection => false, // The hidden types of `defined_opaque_types` is not local to the current // inference context, so we can freely move this to the global cache. TypingMode::PostBorrowck { .. } => true, @@ -2059,6 +2073,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TryAsDynCandidate | BikeshedGuaranteedNoDropCandidate => false, // Non-global param candidates have already been handled, global // where-bounds get ignored. @@ -2569,11 +2584,27 @@ impl<'tcx> SelectionContext<'_, 'tcx> { })?; nested_obligations.extend(obligations); - if impl_trait_header.polarity == ty::ImplPolarity::Reservation - && !self.typing_mode().is_coherence() - { - debug!("reservation impls only apply in intercrate mode"); - return Err(()); + match self.typing_mode() { + TypingMode::Coherence => {} + TypingMode::Reflection + if !self.tcx().impl_is_fully_generic_for_reflection(impl_def_id) => + { + debug!("reflection mode only allows fully generic impls"); + return Err(()); + } + + TypingMode::Typeck { .. } + | TypingMode::PostTypeckUntilBorrowck { .. } + | TypingMode::PostBorrowck { .. } + | TypingMode::Codegen + | TypingMode::ErasedNotCoherence(_) + | TypingMode::Reflection + | TypingMode::PostAnalysis => { + if impl_trait_header.polarity == ty::ImplPolarity::Reservation { + debug!("reservation impls only apply in intercrate mode"); + return Err(()); + } + } } Ok(Normalized { value: impl_args, obligations: nested_obligations }) @@ -2916,6 +2947,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } TypingMode::Coherence | TypingMode::PostAnalysis + | TypingMode::Reflection | TypingMode::Codegen | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: _ } | TypingMode::PostBorrowck { defined_opaque_types: _ } => false, diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index d9069ac127729..b5a82793dcc97 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -167,6 +167,7 @@ fn resolve_associated_item<'tcx>( ty::TypingMode::Coherence | ty::TypingMode::Typeck { .. } | ty::TypingMode::PostTypeckUntilBorrowck { .. } + | ty::TypingMode::Reflection | ty::TypingMode::PostBorrowck { .. } => false, ty::TypingMode::PostAnalysis | ty::TypingMode::Codegen => { !trait_ref.still_further_specializable() diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index abec1850502b6..7a53fe1e0cfc8 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -87,6 +87,7 @@ fn layout_of<'tcx>( | ty::TypingMode::Typeck { .. } | ty::TypingMode::PostTypeckUntilBorrowck { .. } | ty::TypingMode::PostBorrowck { .. } + | ty::TypingMode::Reflection | ty::TypingMode::ErasedNotCoherence(_) | ty::TypingMode::PostAnalysis => {} } @@ -552,6 +553,7 @@ fn layout_of_uncached<'tcx>( | ty::TypingMode::Typeck { .. } | ty::TypingMode::PostTypeckUntilBorrowck { .. } | ty::TypingMode::PostBorrowck { .. } + | ty::TypingMode::Reflection | ty::TypingMode::ErasedNotCoherence(_) | ty::TypingMode::PostAnalysis => { return Err(error(cx, LayoutError::TooGeneric(ty))); diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 75f906e498eda..c652119957dd4 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -123,6 +123,10 @@ pub enum TypingMode { /// This is currently only used by the new solver, but should be implemented in /// the old solver as well. PostBorrowck { defined_opaque_types: I::LocalDefIds }, + /// During the evaluation of reflection logic that ignores lifetimes, we can only + /// handle impls that are fully generic over all lifetimes without constraints on + /// those lifetimes (other than implied bounds). + Reflection, /// After analysis, mostly during MIR optimizations, we're able to /// reveal all opaque types. As the hidden type should *never* be observable /// directly by the user, this should not be used by checks which may expose @@ -173,6 +177,7 @@ impl PartialEq for TypingModeEqWrapper { fn eq(&self, other: &Self) -> bool { match (self.0, other.0) { (TypingMode::Coherence, TypingMode::Coherence) => true, + (TypingMode::Reflection, TypingMode::Reflection) => true, ( TypingMode::Typeck { defining_opaque_types_and_generators: l }, TypingMode::Typeck { defining_opaque_types_and_generators: r }, @@ -193,6 +198,7 @@ impl PartialEq for TypingModeEqWrapper { ) => true, ( TypingMode::Coherence + | TypingMode::Reflection | TypingMode::Typeck { .. } | TypingMode::PostTypeckUntilBorrowck { .. } | TypingMode::PostBorrowck { .. } @@ -218,6 +224,25 @@ impl TypingMode { TypingMode::Coherence => true, TypingMode::Typeck { .. } | TypingMode::PostTypeckUntilBorrowck { .. } + | TypingMode::Reflection + | TypingMode::PostBorrowck { .. } + | TypingMode::PostAnalysis + | TypingMode::Codegen + | TypingMode::ErasedNotCoherence(_) => false, + } + } + + /// There are a bunch of places in the compiler where we single out `Reflection`, + /// and alter behavior. We'd like to *always* match on `TypingMode` exhaustively, + /// but not having this method leads to a bunch of noisy code. + /// + /// See also the documentation on [`TypingMode`] about exhaustive matching. + pub fn is_reflection(&self) -> bool { + match self { + TypingMode::Reflection => true, + TypingMode::Typeck { .. } + | TypingMode::PostTypeckUntilBorrowck { .. } + | TypingMode::Coherence | TypingMode::PostBorrowck { .. } | TypingMode::PostAnalysis | TypingMode::Codegen @@ -236,6 +261,7 @@ impl TypingMode { TypingMode::Coherence | TypingMode::Typeck { .. } | TypingMode::PostTypeckUntilBorrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowck { .. } | TypingMode::PostAnalysis | TypingMode::Codegen => false, @@ -262,6 +288,7 @@ impl TypingMode { } TypingMode::PostAnalysis => TypingMode::PostAnalysis, TypingMode::Codegen => TypingMode::Codegen, + TypingMode::Reflection => TypingMode::Reflection, TypingMode::ErasedNotCoherence(MayBeErased) => panic!( "Called `assert_not_erased` from a place that can be called by the trait solver in `TypingMode::ErasedNotCoherence`. `TypingMode` is `ErasedNotCoherence` in a place where that should be impossible" ), @@ -327,6 +354,7 @@ impl From> for TypingMode TypingMode::PostAnalysis, TypingMode::Codegen => TypingMode::Codegen, + TypingMode::Reflection => TypingMode::Reflection, } } } @@ -563,6 +591,7 @@ where TypingMode::Coherence | TypingMode::Typeck { .. } | TypingMode::PostTypeckUntilBorrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowck { .. } | TypingMode::PostAnalysis => infcx.cx().features().feature_bound_holds_in_crate(symbol), TypingMode::Codegen => true, diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 565414fdd58e3..bfa1c982bd0b7 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -423,6 +423,8 @@ pub trait Interner: fn impl_polarity(self, impl_def_id: Self::ImplId) -> ty::ImplPolarity; + fn is_fully_generic_for_reflection(self, impl_def_id: Self::ImplId) -> bool; + fn trait_is_auto(self, trait_def_id: Self::TraitId) -> bool; fn trait_is_coinductive(self, trait_def_id: Self::TraitId) -> bool; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 05f9bb382dc38..1671128e67a3a 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -54,6 +54,7 @@ pub enum SolverTraitLangItem { Sized, TransmuteTrait, TrivialClone, + TryAsDyn, Tuple, Unpin, Unsize, diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index b4ec6dbf5126d..e42e96901b74d 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -914,6 +914,7 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< | TypingMode::ErasedNotCoherence { .. } | TypingMode::PostTypeckUntilBorrowck { .. } | TypingMode::PostBorrowck { .. } + | TypingMode::Reflection | TypingMode::PostAnalysis | TypingMode::Codegen => (), }; diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 9ac4cd2dddc81..66eb27074ee30 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -180,6 +180,7 @@ where Ok(a) } TypingMode::Typeck { .. } + | TypingMode::Reflection | TypingMode::PostTypeckUntilBorrowck { .. } | TypingMode::PostBorrowck { .. } | TypingMode::PostAnalysis diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 4ee41bc807243..58996703023ce 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -246,7 +246,8 @@ pub struct Box< #[rustc_no_mir_inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[cfg(not(no_global_oom_handling))] -fn box_new_uninit(layout: Layout) -> *mut u8 { +#[rustc_const_unstable(feature = "const_heap", issue = "79597")] +const fn box_new_uninit(layout: Layout) -> *mut u8 { match Global.allocate(layout) { Ok(ptr) => ptr.as_mut_ptr(), Err(_) => handle_alloc_error(layout), @@ -258,10 +259,11 @@ fn box_new_uninit(layout: Layout) -> *mut u8 { /// This is unsafe, but has to be marked as safe or else we couldn't use it in `vec!`. #[doc(hidden)] #[unstable(feature = "liballoc_internals", issue = "none")] +#[rustc_const_unstable(feature = "const_heap", issue = "79597")] #[inline(always)] #[cfg(not(no_global_oom_handling))] #[rustc_diagnostic_item = "box_assume_init_into_vec_unsafe"] -pub fn box_assume_init_into_vec_unsafe( +pub const fn box_assume_init_into_vec_unsafe( b: Box>, ) -> crate::vec::Vec { unsafe { (b.assume_init() as Box<[T]>).into_vec() } @@ -307,10 +309,11 @@ impl Box { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "new_uninit", since = "1.82.0")] + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] #[must_use] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub fn new_uninit() -> Box> { + pub const fn new_uninit() -> Box> { // This is the same as `Self::new_uninit_in(Global)`, but manually inlined (just like // `Box::new`). @@ -1197,8 +1200,9 @@ impl Box, A> { /// assert_eq!(*five, 5) /// ``` #[stable(feature = "new_uninit", since = "1.82.0")] + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] #[inline(always)] - pub unsafe fn assume_init(self) -> Box { + pub const unsafe fn assume_init(self) -> Box { // This is used in the `vec!` macro, so we optimize for minimal IR generation // even in debug builds. // SAFETY: `Box` and `Box>` have the same layout. @@ -1668,8 +1672,9 @@ impl Box { /// [memory layout]: self#memory-layout #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] #[inline] - pub fn into_raw_with_allocator(b: Self) -> (*mut T, A) { + pub const fn into_raw_with_allocator(b: Self) -> (*mut T, A) { let mut b = mem::ManuallyDrop::new(b); // We carefully get the raw pointer out in a way that Miri's aliasing model understands what // is happening: using the primitive "deref" of `Box`. In case `A` is *not* `Global`, we diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 39e72e383eacb..da9b40c2c3ce0 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -476,8 +476,9 @@ impl [T] { /// ``` #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] #[inline] - pub fn into_vec(self: Box) -> Vec { + pub const fn into_vec(self: Box) -> Vec { unsafe { let len = self.len(); let (b, alloc) = Box::into_raw_with_allocator(self); diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 96dcde71fc071..296d0708a5f0e 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -8,6 +8,7 @@ #![feature(binary_heap_pop_if)] #![feature(casefold)] #![feature(const_btree_len)] +#![feature(const_cmp)] #![feature(const_heap)] #![feature(const_trait_impl)] #![feature(core_intrinsics)] diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index fc58e4364fe66..4620a9f373d6c 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -2807,3 +2807,26 @@ fn const_make_global_empty_or_zst_regression() { assert_eq!(ZST_SLICE, &[(), (), ()]); } + +#[test] +fn const_heap_vec_macro() { + const X: &'static [u32] = { + let x: Vec = vec![]; + assert!(x == []); + x.const_make_global() + }; + + const Y: &'static [u32] = { + let y: Vec = vec![1, 2, 3]; + assert!(y == [1, 2, 3]); + y.const_make_global() + }; + + // This arm isn't const yet. + // const Z: &'static [u32] = { + // vec![4; 2].const_make_global() + // }; + + assert_eq!(X, []); + assert_eq!(Y, [1, 2, 3]); +} diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 6c8cb114b9ef1..85ff2fe1dd6ee 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -86,7 +86,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::intrinsics::{self, type_id_vtable}; +use crate::intrinsics::{self, type_id, type_id_vtable}; use crate::mem::transmute; use crate::mem::type_info::{TraitImpl, TypeKind}; use crate::{fmt, hash, ptr}; @@ -786,12 +786,10 @@ impl TypeId { #[unstable(feature = "type_info", issue = "146922")] #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] - pub fn trait_info_of> + ?Sized + 'static>( - self, - ) -> Option> { + pub fn trait_info_of<'a, T: TryAsDynCompatible<'a> + ?Sized>(self) -> Option> { // SAFETY: The vtable was obtained for `T`, so it is guaranteed to be `DynMetadata`. // The intrinsic can't infer this because it is designed to work with arbitrary TypeIds. - unsafe { transmute(self.trait_info_of_trait_type_id(const { TypeId::of::() })) } + unsafe { transmute(self.trait_info_of_trait_type_id(const { type_id::() })) } } /// Checks if the [TypeId] implements the trait of `trait_represented_by_type_id`. If it does it returns [TraitImpl] which can be used to build a fat pointer. @@ -948,12 +946,72 @@ pub const fn type_name_of_val(_val: &T) -> &'static str { type_name::() } -/// Returns `Some(&U)` if `T` can be coerced to the trait object type `U`. Otherwise, it returns `None`. +/// Trait that is automatically implemented for all `dyn Trait<'b, C> + 'a` without assoc type bounds. +/// The lifetime parameter should be the same that is used to constrain generic type parameters +/// that are turned into the dyn trait constrained by `TryAsDynCompatible`. +/// +/// This is required for `try_as_dyn` to be able to soundly convert non-static +/// types to `dyn Trait`. +/// +/// Note: these requirements are sufficient for soundness, but it is unclear +/// if they are all necessary. We may be able to lift some requirements in favor +/// of more precise ones. +/// +#[unstable(feature = "try_as_dyn", issue = "144361")] +#[lang = "try_as_dyn"] +#[rustc_deny_explicit_impl] +pub trait TryAsDynCompatible<'a>: ptr::Pointee> {} + +/// Returns `Some(&U)` if `T` can be coerced to the dyn trait type `U`. Otherwise, it returns `None`. +/// +/// # Run-time failures +/// +/// There are multiple ways to get a `None`, and you need to manually analyze which one it is, as the +/// compiler does not provide any help here. +/// +/// * `T` does not implement `Trait` at all, +/// * `T`'s impl for `Trait` is not fully generic, +/// * `T`'s impl for `Trait` is a builtin impl (e.g. `dyn Debug` implements `Debug`) +/// +/// There is some detailed documentation about this feature at +/// +/// But the gist is summarized below: +/// +/// ## Lifetime-independent impls +/// +/// `try_as_dyn` does not have access to lifetime information, thus it cannot differentiate between +/// `'static`, other lifetimes, and can't reason about outlives bounds on impls. Thus we can only accept +/// impls that do not have `'static` lifetimes, or outlives bounds of any kind. You can have simple +/// trait bounds, and the compiler will transitively only use impls of those simple trait bounds that satisfy +/// the same rules as the main trait you're converting to. +/// +/// An example of a legal impl is: +/// +/// ```rust +/// # trait Trait<'a, T> {} +/// # struct Type<'b, U>(&'b U); +/// # use std::fmt::{Debug, Display}; +/// impl<'a, 'b, T: Debug, U: Display> Trait<'a, T> for Type<'b, U> {} +/// ``` +/// +/// Impls without generic parameters at all are also legal, as long as they contain no `'static` lifetimes. +/// +/// ## Builtin impls +/// +/// Builtin impls (like `impl Debug for dyn Debug`) have various obscure rules and often are not fully generic. +/// To simplify reasoning about what is allowed and what not, all builtin impls are rejected and will neither +/// directly nor indirectly contribute to a `Some` result. /// /// # Compile-time failures -/// Determining whether `T` can be coerced to the trait object type `U` requires compiler trait resolution. +/// Determining whether `T` can be coerced to the dyn trait type `U` requires compiler trait resolution. /// In some cases, that resolution can exceed the recursion limit, /// and compilation will fail instead of this function returning `None`. +/// +/// The input type `T` must outlive the lifetime `'a` on the `dyn Trait + 'a`. +/// This is basically the same rule that forbids `let x: &dyn Trait + 'static = &&some_local_variable;` +/// So if you see borrow check errors around `try_as_dyn`, think about whether a normal unsizing +/// coercion would be possible at all if you were using concrete types or had bounds on the input type. +/// /// # Examples /// /// ```rust @@ -983,17 +1041,14 @@ pub const fn type_name_of_val(_val: &T) -> &'static str { /// ``` #[must_use] #[unstable(feature = "try_as_dyn", issue = "144361")] -pub const fn try_as_dyn< - T: Any + ?Sized + 'static, - U: ptr::Pointee> + ?Sized + 'static, ->( +pub const fn try_as_dyn<'a, T: ?Sized + 'a, U: TryAsDynCompatible<'a> + ?Sized>( t: &T, ) -> Option<&U> { // For unsized `T`, `trait_info_of` always returns `None` (vtable lookup is // only supported for sized types). The function therefore unconditionally // returns `None` in that case. let vtable: Option> = - const { TypeId::of::().trait_info_of::().as_ref().map(TraitImpl::get_vtable) }; + const { type_id::().trait_info_of::().as_ref().map(TraitImpl::get_vtable) }; match vtable { Some(dyn_metadata) => { let pointer = ptr::from_raw_parts(t as *const T as *const (), dyn_metadata); @@ -1009,50 +1064,17 @@ pub const fn try_as_dyn< /// Returns `Some(&mut U)` if `T` can be coerced to the trait object type `U`. Otherwise, it returns `None`. /// -/// # Compile-time failures -/// Determining whether `T` can be coerced to the trait object type `U` requires compiler trait resolution. -/// In some cases, that resolution can exceed the recursion limit, -/// and compilation will fail instead of this function returning `None`. -/// # Examples -/// -/// ```rust -/// #![feature(try_as_dyn)] -/// -/// use core::any::try_as_dyn_mut; -/// -/// trait Animal { -/// fn speak(&self) -> &'static str; -/// } -/// -/// struct Dog; -/// impl Animal for Dog { -/// fn speak(&self) -> &'static str { "woof" } -/// } -/// -/// struct Rock; // does not implement Animal -/// -/// let mut dog = Dog; -/// let mut rock = Rock; -/// -/// let as_animal: Option<&mut dyn Animal> = try_as_dyn_mut::(&mut dog); -/// assert_eq!(as_animal.unwrap().speak(), "woof"); -/// -/// let not_an_animal: Option<&mut dyn Animal> = try_as_dyn_mut::(&mut rock); -/// assert!(not_an_animal.is_none()); -/// ``` +/// See documentation of [try_as_dyn] for details about the behaviour and limitations. #[must_use] #[unstable(feature = "try_as_dyn", issue = "144361")] -pub const fn try_as_dyn_mut< - T: Any + ?Sized + 'static, - U: ptr::Pointee> + ?Sized + 'static, ->( +pub const fn try_as_dyn_mut<'a, T: ?Sized + 'a, U: TryAsDynCompatible<'a> + ?Sized>( t: &mut T, ) -> Option<&mut U> { // For unsized `T`, `trait_info_of` always returns `None` (vtable lookup is // only supported for sized types). The function therefore unconditionally // returns `None` in that case. let vtable: Option> = - const { TypeId::of::().trait_info_of::().as_ref().map(TraitImpl::get_vtable) }; + const { type_id::().trait_info_of::().as_ref().map(TraitImpl::get_vtable) }; match vtable { Some(dyn_metadata) => { let pointer = ptr::from_raw_parts_mut(t as *mut T as *mut (), dyn_metadata); diff --git a/library/coretests/tests/mem/trait_info_of.rs b/library/coretests/tests/mem/trait_info_of.rs index c723a96095815..4fd4a013693d9 100644 --- a/library/coretests/tests/mem/trait_info_of.rs +++ b/library/coretests/tests/mem/trait_info_of.rs @@ -10,6 +10,7 @@ impl Blah for Garlic { self.0 * 21 } } +unsafe impl Send for Garlic {} #[test] fn test_implements_trait() { diff --git a/src/ci/docker/host-x86_64/optional-x86_64-gnu-autodiff/Dockerfile b/src/ci/docker/host-x86_64/optional-x86_64-gnu-autodiff/Dockerfile new file mode 100644 index 0000000000000..a7b6dc620e967 --- /dev/null +++ b/src/ci/docker/host-x86_64/optional-x86_64-gnu-autodiff/Dockerfile @@ -0,0 +1,39 @@ +FROM ubuntu:24.04 + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + pkg-config \ + xz-utils \ + zlib1g-dev \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV NO_DOWNLOAD_CI_LLVM 1 +ENV CODEGEN_BACKENDS llvm + +ENV RUST_CONFIGURE_ARGS \ + --build=x86_64-unknown-linux-gnu \ + --enable-llvm-enzyme \ + --enable-llvm-link-shared \ + --enable-ninja \ + --enable-option-checking \ + --disable-docs \ + --set llvm.download-ci-llvm=false + +ENV SCRIPT="../x.py test --stage 2 --no-fail-fast \ + tests/codegen-llvm/autodiff \ + tests/pretty/autodiff \ + tests/ui/autodiff \ + tests/ui/feature-gates/feature-gate-autodiff.rs" diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index a236effaa9b40..eddd1182abbcb 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -469,6 +469,11 @@ auto: - name: x86_64-gnu-miri <<: *job-linux-4c + - name: optional-x86_64-gnu-autodiff + continue_on_error: true + doc_url: https://rustc-dev-guide.rust-lang.org/tests/autodiff-ci-job.html + <<: *job-linux-4c + #################### # macOS Builders # #################### diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index f930573748e28..25a5238f538ee 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -7fb284d9037fa54f6a9b24261c82b394472cbfd7 +390279b302ca98ae270f434100ae3730531d1246 diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index bf2de84575d69..8b2cf76c4104d 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -35,6 +35,7 @@ - [Cranelift codegen backend](./tests/codegen-backend-tests/cg_clif.md) - [GCC codegen backend](./tests/codegen-backend-tests/cg_gcc.md) - [Performance testing](./tests/perf.md) + - [Autodiff CI job](./tests/autodiff-ci-job.md) - [Pre-stabilization CI job for the next solver and polonius alpha](./tests/x86_64-gnu-next-trait-solver-polonius-ci-job.md) - [Misc info](./tests/misc.md) - [Debugging the compiler](./compiler-debugging.md) @@ -86,7 +87,7 @@ - [Debugging bootstrap](./building/bootstrapping/debugging-bootstrap.md) - [cfg(bootstrap) in dependencies](./building/bootstrapping/bootstrap-in-dependencies.md) -# High-level Compiler Architecture +# High-level compiler architecture - [Prologue](./part-2-intro.md) - [Overview of the compiler](./overview.md) @@ -115,7 +116,7 @@ - [Autodiff flags](./autodiff/flags.md) - [Type Trees](./autodiff/type-trees.md) -# Source Code Representation +# Source code representation - [Prologue](./part-3-intro.md) - [Syntax and the AST](./syntax-intro.md) @@ -140,7 +141,7 @@ - [MIR queries and passes: getting the MIR](./mir/passes.md) - [Inline assembly](./asm.md) -# Supporting Infrastructure +# Supporting infrastructure - [Command-line arguments](./cli.md) - [rustc_driver and rustc_interface](./rustc-driver/intro.md) @@ -167,8 +168,8 @@ - [ADTs and Generic Arguments](./ty-module/generic-arguments.md) - [Parameter types/consts/regions](./ty-module/param-ty-const-regions.md) - [`TypeFolder` and `TypeFoldable`](./ty-fold.md) -- [Aliases and Normalization](./normalization.md) -- [Typing/Param Envs](./typing-parameter-envs.md) +- [Aliases and normalization](./normalization.md) +- [Typing/Param envs](./typing-parameter-envs.md) - [Type inference](./type-inference.md) - [Trait solving](./traits/resolution.md) - [Higher-ranked trait bounds](./traits/hrtb.md) @@ -199,7 +200,7 @@ - [HIR Type checking](./hir-typeck/summary.md) - [Coercions](./hir-typeck/coercions.md) - [Method lookup](./hir-typeck/method-lookup.md) -- [Const Generics](./const-generics.md) +- [Const generics](./const-generics.md) - [Opaque types](./opaque-types-type-alias-impl-trait.md) - [Inference details](./opaque-types-impl-trait-inference.md) - [Return Position Impl Trait In Trait](./return-position-impl-trait-in-trait.md) @@ -239,18 +240,18 @@ - [Debugging LLVM](./backend/debugging.md) - [Backend Agnostic Codegen](./backend/backend-agnostic.md) - [Implicit caller location](./backend/implicit-caller-location.md) -- [Debug Info](./debuginfo/intro.md) - - [Rust Codegen](./debuginfo/rust-codegen.md) - - [LLVM Codegen](./debuginfo/llvm-codegen.md) - - [Debugger Internals](./debuginfo/debugger-internals.md) - - [LLDB Internals](./debuginfo/lldb-internals.md) - - [GDB Internals](./debuginfo/gdb-internals.md) - - [Debugger Visualizers](./debuginfo/debugger-visualizers.md) +- [Debug info](./debuginfo/intro.md) + - [Rust codegen](./debuginfo/rust-codegen.md) + - [LLVM codegen](./debuginfo/llvm-codegen.md) + - [Debugger internals](./debuginfo/debugger-internals.md) + - [LLDB internals](./debuginfo/lldb-internals.md) + - [GDB internals](./debuginfo/gdb-internals.md) + - [Debugger visualizers](./debuginfo/debugger-visualizers.md) - [LLDB - Python Providers](./debuginfo/lldb-visualizers.md) - [GDB - Python Providers](./debuginfo/gdb-visualizers.md) - [CDB - Natvis](./debuginfo/natvis-visualizers.md) - [Testing](./debuginfo/testing.md) - - [(Lecture Notes) Debugging support in the Rust compiler](./debugging-support-in-rustc.md) + - [(Lecture notes) Debugging support in the Rust compiler](./debugging-support-in-rustc.md) - [Libraries and metadata](./backend/libs-and-metadata.md) - [Profile-guided optimization](./profile-guided-optimization.md) - [LLVM source-based code coverage](./llvm-coverage-instrumentation.md) diff --git a/src/doc/rustc-dev-guide/src/about-this-guide.md b/src/doc/rustc-dev-guide/src/about-this-guide.md index 56fbc1f6b7a85..6282a66e13b07 100644 --- a/src/doc/rustc-dev-guide/src/about-this-guide.md +++ b/src/doc/rustc-dev-guide/src/about-this-guide.md @@ -1,5 +1,7 @@ # About this guide + + This guide is meant to help document how rustc – the Rust compiler – works, as well as to help new contributors get involved in rustc development. @@ -14,18 +16,18 @@ There are several parts to this guide: 1. [Bootstrapping][p3]: Describes how the Rust compiler builds itself using previous versions, including an introduction to the bootstrap process and debugging methods. -1. [High-level Compiler Architecture][p4]: +1. [High-level compiler architecture][p4]: Discusses the high-level architecture of the compiler and stages of the compile process. -1. [Source Code Representation][p5]: +1. [Source code representation][p5]: Describes the process of taking raw source code from the user and transforming it into various forms that the compiler can work with easily. -1. [Supporting Infrastructure][p6]: +1. [Supporting infrastructure][p6]: Covers command-line argument conventions, compiler entry points like rustc_driver and rustc_interface, and the design and implementation of errors and lints. 1. [Analysis][p7]: Discusses the analyses that the compiler uses to check various properties of the code and inform later stages of the compile process (e.g., type checking). -1. [MIR to Binaries][p8]: How linked executable machine code is generated. +1. [Mir to binaries][p8]: How linked executable machine code is generated. 1. [Appendices][p9] at the end with useful reference information. There are a few of these with different information, including a glossary. diff --git a/src/doc/rustc-dev-guide/src/autodiff/installation.md b/src/doc/rustc-dev-guide/src/autodiff/installation.md index c0da9621f9777..acbb7c9eb09f3 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/installation.md +++ b/src/doc/rustc-dev-guide/src/autodiff/installation.md @@ -20,7 +20,7 @@ rustup +nightly component add enzyme Older rustup versions are not aware of this component, so if you run into issues try updating rustup itself: ```console -rustup update +rustup self update rustup +nightly component add enzyme ``` diff --git a/src/doc/rustc-dev-guide/src/const-generics.md b/src/doc/rustc-dev-guide/src/const-generics.md index 3f84b99fb637e..9e26a174d8cf6 100644 --- a/src/doc/rustc-dev-guide/src/const-generics.md +++ b/src/doc/rustc-dev-guide/src/const-generics.md @@ -1,4 +1,4 @@ -# Const Generics +# Const generics ## Kinds of const arguments @@ -15,9 +15,9 @@ Inference Variables are quite boring and treated equivalently to type inference Const Parameters are also similarly boring and equivalent to uses of type parameters almost everywhere. However, there are some interesting subtleties with how they are handled during parsing, name resolution, and AST lowering: [ambig-unambig-ty-and-consts]. -## Anon Consts +## Anon consts -Anon Consts (short for anonymous const items) are how arbitrary expression are represented in const generics, for example an array length of `1 + 1` or `foo()` or even just `0`. +Anon consts (short for anonymous const items) are how arbitrary expression are represented in const generics, for example an array length of `1 + 1` or `foo()` or even just `0`. These are unique to const generics and have no real type equivalent. ### Desugaring @@ -85,7 +85,7 @@ After all of this desugaring has taken place the final representation in the typ This allows the representation for const "aliases" to be the same as the representation of `TyKind::Alias`. Having a proper HIR body also allows for a *lot* of code re-use, e.g. we can reuse HIR typechecking and all of the lowering steps to MIR where we can then reuse const eval. -### Enforcing lack of Generic Parameters +### Enforcing lack of generic parameters There are three ways that we enforce anon consts can't use generic parameters: 1. Name Resolution will not resolve paths to generic parameters when inside of an anon const @@ -134,7 +134,9 @@ fn foo() { } ``` -However, to avoid most of the problems involved in allowing generic parameters in anon const const arguments we require that the constant be evaluated before monomorphization (e.g. during type checking). In some sense we only allow generic parameters here when they are semantically unused. +However, to avoid most of the problems involved in allowing generic parameters in anon const const arguments, +we require that the constant be evaluated before monomorphization (e.g. during type checking). +In some sense we only allow generic parameters here when they are semantically unused. In the previous example the anon const can be evaluated for any type parameter `T` because raw pointers to sized types always have the same size (e.g. `8` on 64bit platforms). @@ -182,7 +184,7 @@ It is currently unclear what the right way to make `generic_const_parameter_type `min_generic_const_args` will allow for some expressions (for example array construction) to be representable without an anon const and therefore without running into these issues, though whether this is *enough* has yet to be determined. -## Checking types of Const Arguments +## Checking types of const arguments In order for a const argument to be well formed it must have the same type as the const parameter it is an argument to. For example, a const argument of type `bool` for an array length is not well formed, as an array's length parameter has type `usize`. diff --git a/src/doc/rustc-dev-guide/src/debuginfo/CodeView.pdf b/src/doc/rustc-dev-guide/src/debuginfo/CodeView.pdf deleted file mode 100644 index f899d25178458..0000000000000 Binary files a/src/doc/rustc-dev-guide/src/debuginfo/CodeView.pdf and /dev/null differ diff --git a/src/doc/rustc-dev-guide/src/debuginfo/debugger-internals.md b/src/doc/rustc-dev-guide/src/debuginfo/debugger-internals.md index 114ce8a998c58..3da43fe898dae 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/debugger-internals.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/debugger-internals.md @@ -1,14 +1,16 @@ -# Debugger Internals +# Debugger internals -It is the debugger's job to convert the debug info into an in-memory representation. Both the -interpretation of the debug info and the in-memory representation are arbitrary; anything will do -so long as meaningful information can be reconstructed while the program is running. The pipeline -from raw debug info to usable types can be quite complicated. +It is the debugger's job to convert the debug info into an in-memory representation. +Both the interpretation of the debug info and the in-memory representation are arbitrary; +anything will do, +so long as meaningful information can be reconstructed while the program is running. +The pipeline from raw debug info to usable types can be quite complicated. Once the information is in a workable format, the debugger front-end then must provide a way to interpret and display the data, a way for users to interact with it, and an API for extensibility. -Debuggers are vast systems and cannot be covered completely here. This section will provide a brief -overview of the subsystems directly relevant to the Rust debugging experience. +Debuggers are vast systems and cannot be covered completely here. +This section will provide a brief overview of the subsystems +directly relevant to the Rust debugging experience. -Microsoft's debugging engine is closed source, so it will not be covered here. \ No newline at end of file +Microsoft's debugging engine is closed source, so it will not be covered here. diff --git a/src/doc/rustc-dev-guide/src/debuginfo/debugger-visualizers.md b/src/doc/rustc-dev-guide/src/debuginfo/debugger-visualizers.md index 831acbd2f8f8e..10167bf69cedc 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/debugger-visualizers.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/debugger-visualizers.md @@ -1,42 +1,47 @@ -# Debugger Visualizers +# Debugger visualizers These are typically the last step before the debugger displays the information, but the results may be piped through a debug adapter such as an IDE's debugger API. -The term "Visualizer" is a bit of a misnomer. The real goal isn't just to prettify the output, but -to provide an interface for the user to interact with that is as useful as possible. In many cases -this means reconstructing the original type as closely as possible to its Rust representation, but -not always. +The term "Visualizer" is a bit of a misnomer. +The real goal isn't just to prettify the output, but +to provide an interface for the user to interact with that is as useful as possible. +In many cases, +this means reconstructing the original type as closely as possible to its Rust representation, +but not always. The visualizer interface allows generating "synthetic children" - fields that don't exist in the -debug info, but can be derived from invariants about the language and the type itself. A simple -example is allowing one to interact with the elements of a `Vec` instead of just it's `*mut u8` -heap pointer, length, and capacity. +debug info, but can be derived from invariants about the language and the type itself. +A simple example is allowing one to interact with the elements of a `Vec` +instead of just it's `*mut u8` heap pointer, length, and capacity. ## `rust-lldb`, `rust-gdb`, and `rust-windbg.cmd` -These support scripts are distributed with Rust toolchains. They locate the appropriate debugger and +These support scripts are distributed with Rust toolchains. +They locate the appropriate debugger and the toolchain's visualizer scripts, then launch the debugger with the appropriate arguments to load the visualizer scripts before a debugee is launched/attached to. ## `#![debugger_visualizer]` [This attribute][dbg_vis_attr] allows Rust library authors to include pretty printers for their -types within the library itself. These pretty printers are of the same format as typical -visualizers, but are embedded directly into the compiled binary. These scripts are loaded -automatically by the debugger, allowing a seamless experience for users. This attribute currently -works for GDB and natvis scripts. +types within the library itself. +These pretty printers are of the same format as typical +visualizers, but are embedded directly into the compiled binary. +These scripts are loaded automatically by the debugger, allowing a seamless experience for users. +This attribute currently works for GDB and natvis scripts. [dbg_vis_attr]: https://doc.rust-lang.org/reference/attributes/debugger.html#the-debugger_visualizer-attribute -GDB python scripts are embedded in the `.debug_gdb_scripts` section of the binary. More information -can be found [here](https://sourceware.org/gdb/current/onlinedocs/gdb.html/dotdebug_005fgdb_005fscripts-section.html). Rustc accomplishes this in [`rustc_codegen_llvm/src/debuginfo/gdb.rs`][gdb_rs] +GDB python scripts are embedded in the `.debug_gdb_scripts` section of the binary ([more info]). +Rustc accomplishes this in [`rustc_codegen_llvm/src/debuginfo/gdb.rs`][gdb_rs]. +[more info]: https://sourceware.org/gdb/current/onlinedocs/gdb.html/dotdebug_005fgdb_005fscripts-section.html [gdb_rs]: https://github.com/rust-lang/rust/blob/main/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs Natvis files can be embedded in the PDB debug info using the [`/NATVIS` linker option][linker_opt], -and have the [highest priority][priority] when a type is resolving which visualizer to use. The -files specified by the attribute are collected into +and have the [highest priority][priority] when a type is resolving which visualizer to use. +The files specified by the attribute are collected into [`CrateInfo::natvis_debugger_visualizers`][natvis] which are then added as linker arguments in [`rustc_codegen_ssa/src/back/linker.rs`][linker_rs] @@ -46,27 +51,32 @@ files specified by the attribute are collected into [linker_rs]: https://github.com/rust-lang/rust/blob/main/compiler/rustc_codegen_ssa/src/back/linker.rs#L1106 LLDB is not currently supported, but there are a few methods that could potentially allow support in -the future. Officially, the intended method is via a [formatter bytecode][bytecode]. This was -created to offer a comparable experience to GDB's, but without the safety concerns associated with -embedding an entire python script. The opcodes are limited, but it works with `SBValue` and `SBType` -in roughly the same way as python visualizer scripts. Implementing this would require writing some -sort of DSL/mini compiler. +the future. +Officially, the intended method is via a [formatter bytecode][bytecode]. +This was created to offer a comparable experience to GDB's, +but without the safety concerns associated with embedding an entire Python script. +The opcodes are limited, but it works with `SBValue` and `SBType` +in roughly the same way as Python visualizer scripts. +Implementing this would require writing some sort of DSL/mini compiler. [bytecode]: https://lldb.llvm.org/resources/formatterbytecode.html Alternatively, it might be possible to copy GDB's strategy entirely: create a bespoke section in the -binary and embed a python script in it. LLDB will not load it automatically, but the python API does -allow one to access the [raw sections of the debug info][SBSection]. With this, it may be possible -to extract the python script from our bespoke section and then load it in during the startup of -Rust's visualizer scripts. +binary and embed a python script in it. +LLDB will not load it automatically, but the Python API does +allow one to access the [raw sections of the debug info][SBSection]. +With this, it may be possible to extract the Python script from our bespoke section +and then load it in during the startup of Rust's visualizer scripts. [SBSection]: https://lldb.llvm.org/python_api/lldb.SBSection.html#sbsection ## Performance Before tackling the visualizers themselves, it's important to note that these are part of a -performance-sensitive system. Please excuse the break in formality, but: if I have to spend -significant time debugging, I'm annoyed. If I have to *wait on my debugger*, I'm pissed. +performance-sensitive system. +Please excuse the break in formality, but: if I have to spend +significant time debugging, I'm annoyed. +If I have to *wait on my debugger*, I'm pissed. Every millisecond spent in these visualizers is a millisecond longer for the user to see output. This can be especially painful for large stackframes that contain many/large container types. @@ -75,37 +85,45 @@ delays of tens of seconds (or even minutes) before being able to interact with a frame. There is a tendancy to balk at the idea of optimizing Python code, but it really can have a -substantial impact. Remember, there is no compiler to help keep the code fast. Even simple -transformations are not done for you. It can be difficult to find Python performance tips through +substantial impact. +Remember, there is no compiler to help keep the code fast. +Even simple transformations are not done for you. +It can be difficult to find Python performance tips through all the noise of people suggesting you don't bother optimizing Python, so here are some things to keep in mind that are relevant to these scripts: * Everything allocates, even `int` -* Use tuples when possible. `list` is effectively `Vec>`, whereas tuples are equivalent -to `Box<[Any]>`. They have one less layer of indirection, don't carry extra capacity and can't -grow/shrink which can be advantageous in many cases. An additional benefit is that Python caches and -recycles the underlying allocations of all tuples up to size 20. +* Use tuples when possible. + `list` is effectively `Vec>`, whereas tuples are equivalent to `Box<[Any]>`. + They have one less layer of indirection, don't carry extra capacity and can't grow/shrink, + which can be advantageous in many cases. + An additional benefit is that Python caches and + recycles the underlying allocations of all tuples up to size 20. * Regexes are slow and should be avoided when simple string manipulation will do * Strings are immutable, thus many string operations implictly copy the contents. * When concatenating large lists of strings, `"".join(iterable_of_strings)` is typically the fastest -way to do it. + way to do it. * f-strings are generally the fastest way to do small, simple string transformations such as surrounding a string with parentheses. -* The act of calling a function is somewhat slow (even if the function is completely empty). If the -code section is very hot, consider inlining the function manually. +* The act of calling a function is somewhat slow (even if the function is completely empty). + If the code section is very hot, consider inlining the function manually. * Local variable access is significantly faster than global and built-in function access * Member/method access via the `.` operator is also slow, consider reassigning deeply nested values -to local variables to avoid this cost (e.g. `h = a.b.c.d.e.f.g.h`). + to local variables to avoid this cost (e.g. `h = a.b.c.d.e.f.g.h`). * Accessing inherited methods and fields is about 2x slower than base-class methods and fields. -Avoid inheritance whenever possible. -* Use [`__slots__`](https://wiki.python.org/moin/UsingSlots) wherever possible. `__slots__` is a way -to indicate to Python that your class's fields won't change and speeds up field access by a -noticable amount. This does require you to name your fields in advance and initialize them in -`__init__`, but it's a small price to pay for the benefits. -* Match statements/if..elif..else are not optimized in any way. The conditions are checked in order, -1 by 1. If possible, use an alternative such as dictionary dispatch or a table of values + Avoid inheritance whenever possible. +* Use [`__slots__`](https://wiki.python.org/moin/UsingSlots) wherever possible. + `__slots__` is a way + to indicate to Python that your class's fields won't change and speeds up field access by a + noticable amount. + This does require you to name your fields in advance and initialize them in + `__init__`, but it's a small price to pay for the benefits. +* Match statements/if..elif..else are not optimized in any way. + The conditions are checked in order, 1 by 1. + If possible, use an alternative such as dictionary dispatch or a table of values * Compute lazily when possible * List comprehensions are typically faster than loops, generator comprehensions are a bit slower -than list comprehensions, but use less memory. You can think of comprehensions as equivalent to -Rust's `iter.map()`. List comprehensions effectively call `collect::>` at the end, whereas -generator comprehensions do not. \ No newline at end of file + than list comprehensions, but use less memory. + You can think of comprehensions as equivalent to Rust's `iter.map()`. + List comprehensions effectively call `collect::>` at the end, whereas + generator comprehensions do not. diff --git a/src/doc/rustc-dev-guide/src/debuginfo/gdb-internals.md b/src/doc/rustc-dev-guide/src/debuginfo/gdb-internals.md index 95f543c8e94a0..2b80521f4519e 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/gdb-internals.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/gdb-internals.md @@ -1,4 +1,4 @@ -# (WIP) GDB Internals +# GDB internals -GDB's Rust support lives at `gdb/rust-lang.h` and `gdb/rust-lang.c`. The expression parsing support -can be found in `gdb/rust-exp.h` and `gdb/rust-parse.c` \ No newline at end of file +GDB's Rust support lives at `gdb/rust-lang.h` and `gdb/rust-lang.c`. +The expression parsing support can be found in `gdb/rust-exp.h` and `gdb/rust-parse.c` diff --git a/src/doc/rustc-dev-guide/src/debuginfo/gdb-visualizers.md b/src/doc/rustc-dev-guide/src/debuginfo/gdb-visualizers.md index 4027ef897f28a..3f8fb3bf439a5 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/gdb-visualizers.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/gdb-visualizers.md @@ -1,4 +1,4 @@ -# (WIP) GDB - Python Providers +# GDB - Python Providers Below are links to relevant parts of the GDB documentation diff --git a/src/doc/rustc-dev-guide/src/debuginfo/intro.md b/src/doc/rustc-dev-guide/src/debuginfo/intro.md index 0f1e59fa65e26..e8c4d11d12b0a 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/intro.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/intro.md @@ -1,13 +1,13 @@ -# Debug Info +# Debug info Debug info is a collection of information generated by the compiler that allows debuggers to -correctly interpret the state of a program while it is running. That includes things like mapping -instruction addresses to lines of code in the source file, and type layout information so that -bytes in memory can be read and displayed in a meaningful way. +correctly interpret the state of a program while it is running. +That includes things like mapping instruction addresses to lines of code in the source file, +and type layout information so that bytes in memory can be read and displayed in a meaningful way. Debug info can be a slightly overloaded term, covering all the layers between Rust MIR, and the -end-user seeing the output of their debugger onscreen. In brief, the stack from beginning to end is -as follows: +end-user seeing the output of their debugger onscreen. +In brief, the stack from beginning to end is as follows: 1. Rustc inspects the MIR and communicates the relevant source, symbol, and type information to LLVM 2. LLVM translates this information into a target-specific debug info format during compilation @@ -33,65 +33,84 @@ API layers (e.g. VSCode extension by way of the # DWARF -The is the primary debug info format for `*-gnu` targets. It is typically bundled in with the -binary, but it [can be generated as a separate file](https://gcc.gnu.org/wiki/DebugFission). The -DWARF standard is available [here](https://dwarfstd.org/). +The is the primary debug info format for `*-gnu` targets. +It is typically bundled in with the +binary, but it [can be generated as a separate file](https://gcc.gnu.org/wiki/DebugFission). +The DWARF standard is available [here](https://dwarfstd.org/). > NOTE: To inspect DWARF debug info, [gimli](https://crates.io/crates/gimli) can be used > programatically. If you prefer a GUI, the author recommends [DWEX](https://github.com/sevaa/dwex) # PDB/CodeView -The primary debug info format for `*-msvc` targets. PDB is a proprietary container format created by -Microsoft that, unfortunately, +The primary debug info format for `*-msvc` targets. +PDB is a proprietary container format created by Microsoft that, unfortunately, [has multiple meanings](https://docs.rs/ms-pdb/0.1.10/ms_pdb/taster/enum.Flavor.html). -We are concerned with ordinary PDB files, as Portable PDB is used mainly for .Net applications. PDB -files are separate from the compiled binary and use the `.pdb` extension. - -PDB files contain CodeView objects, equivalent to DWARF's tags. CodeView, the debugger that -consumed CodeView objects, was originally released in 1985. Its original intent was for C debugging, -and was later extended to support Visual C++. There are still minor alterations to the format to -support modern architectures and languages, but many of these changes are undocumented and/or -sparsely used. - -It is important to keep this context in mind when working with CodeView objects. Due to its origins, -the "feature-set" of these objects is very limited, and focused around the core features of C. It -does not have many of the convenience or features of modern DWARF standards. A fair number of -workarounds exist within the debug info stack to compensate for CodeView's shortcomings. - -Due to its proprietary nature, it is very difficult to find information about PDB and CodeView. Many -of the sources were made at vastly different times and contain incomplete or somewhat contradictory -information. As such this page will aim to collect as many sources as possible. - -* [CodeView 1.0 specification](./CodeView.pdf) +We are concerned with ordinary PDB files, as Portable PDB is used mainly for .Net applications. +PDB files are separate from the compiled binary and use the `.pdb` extension. + +PDB files contain CodeView objects, equivalent to DWARF's tags. +CodeView, the debugger that consumed CodeView objects, was originally released in 1985. +Its original intent was for C debugging, +and was later extended to support Visual C++. +There are still minor alterations to the format to support modern architectures and languages, +but many of these changes are undocumented and/or sparsely used. + +It is important to keep this context in mind when working with CodeView objects. +Due to its origins, +the "feature-set" of these objects is very limited, and focused around the core features of C. +It does not have many of the convenience or features of modern DWARF standards. +A fair number of workarounds exist within the debug info stack +to compensate for CodeView's shortcomings. + +Due to its proprietary nature, it is very difficult to find information about PDB and CodeView. +Many of the sources were made at vastly different times +and contain incomplete or somewhat contradictory information. +As such, this page will aim to collect as many sources as possible. + +* [TIS PE specification](https://web.archive.org/web/20260315080740/http://x-ways.net/winhex/kb/ff/PE_EXE.pdf) +which includes a lengthy section titled "Microsoft Symbol and Type Information", detailing much of +the CodeView format. +The document was created in 1993, but the information is still detailed, +accurate, and has useful diagrams. * LLVM * [CodeView Overview](https://llvm.org/docs/SourceLevelDebugging.html#codeview-debug-info-format) * [PDB Overview and technical details](https://llvm.org/docs/PDB/index.html) * Microsoft * [microsoft-pdb](https://github.com/microsoft/microsoft-pdb) - A C/C++ implementation of a PDB - reader. The implementation does not contain the full PDB or CodeView specification, but does - contain enough information for other PDB consumers to be written. At time of writing (Nov 2025), + reader. + The implementation does not contain the full PDB or CodeView specification, but does + contain enough information for other PDB consumers to be written. + At time of writing (Nov 2025), this repo has been archived for several years. * [pdb-rs](https://github.com/microsoft/pdb-rs/) - A Rust-based PDB reader and writer based on - other publicly-available information. Does not guarantee stability or spec compliance. Also - contains `pdbtool`, which can dump PDB files (`cargo install pdbtool`) + other publicly-available information. + Does not guarantee stability or spec compliance. + Also contains `pdbtool`, which can dump PDB files (`cargo install pdbtool`) * [Debug Interface Access SDK](https://learn.microsoft.com/en-us/visualstudio/debugger/debug-interface-access/getting-started-debug-interface-access-sdk). While it does not document the PDB format directly, details can be gleaned from the interface itself. + * [PDB-Documentation](https://github.com/PascalBeyer/PDB-Documentation) - a resource compiling + much of the publicly available information about PDB and CodeView. # Debuggers -Rust supports 3 major debuggers: GDB, LLDB, and CDB. Each has its own set of requirements, -limitations, and quirks. This unfortunately creates a large surface area to account for. +Rust supports 3 major debuggers: GDB, LLDB, and CDB. +Each has its own set of requirements, +limitations, and quirks. +This unfortunately creates a large surface area to account for. -> NOTE: CDB is a proprietary debugger created by Microsoft. The underlying engine also powers ->WinDbg, KD, the Microsoft C/C++ extension for VSCode, and part of the Visual Studio Debugger. In ->these docs, it will be referred to as CDB for consistency +> NOTE: CDB is a proprietary debugger created by Microsoft. +> The underlying engine also powers WinDbg, KD, the Microsoft C/C++ extension for VSCode, +> and part of the Visual Studio Debugger. +> In these docs, it will be referred to as CDB for consistency While GDB and LLDB do offer facilities to natively support Rust's value layout, this isn't -completely necessary. Rust currently outputs debug info very similar to that of C++, allowing -debuggers without Rust support to work with a slightly degraded experience. More detail will be -included in later sections, but here is a quick reference for the capabilities of each debugger: +completely necessary. +Rust currently outputs debug info very similar to that of C++, allowing +debuggers without Rust support to work with a slightly degraded experience. +More detail will be included in later sections, +but here is a quick reference for the capabilities of each debugger: | Debugger | Debug Info Format | Native Rust support | Expression Style | Visualizer Scripts | | --- | --- | --- | --- | --- | @@ -108,7 +127,9 @@ Below, are several unsupported debuggers that are of particular note due to thei in the future. * [Bugstalker](https://github.com/godzie44/BugStalker) is an x86-64 Linux debugger written in Rust, -specifically to debug Rust programs. While promising, it is still in early development. -* [RAD Debugger](https://github.com/EpicGamesExt/raddebugger) is a Windows-only GUI debugger. It has -a custom debug info format that PDB is translated into. The project also includes a linker that can -generate their new debug info format during the linking phase. \ No newline at end of file + specifically to debug Rust programs. + While promising, it is still in early development. +* [RAD Debugger](https://github.com/EpicGamesExt/raddebugger) is a Windows-only GUI debugger. + It has a custom debug info format that PDB is translated into. + The project also includes a linker that can generate their new debug info format + during the linking phase. diff --git a/src/doc/rustc-dev-guide/src/debuginfo/lldb-internals.md b/src/doc/rustc-dev-guide/src/debuginfo/lldb-internals.md index e104f1d245327..98eb1da6e77eb 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/lldb-internals.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/lldb-internals.md @@ -1,7 +1,8 @@ -# LLDB Internals +# LLDB internals LLDB's debug info processing relies on a set of extensible interfaces largely defined in -[lldb/src/Plugins][lldb_plugins]. These are meant to allow third-party compiler developers to add +[lldb/src/Plugins][lldb_plugins]. +These are meant to allow third-party compiler developers to add language support that is loaded at run-time by LLDB, but at time of writing (Nov 2025) the public API has not been settled on, so plugins exist either in LLDB itself or in standalone forks of LLDB. @@ -16,27 +17,32 @@ Here are some existing implementations of LLDB's plugin API: * [CodeLLDB's former fork with support for Rust](https://archive.softwareheritage.org/browse/origin/directory/?branch=refs/heads/codelldb/16.x&origin_url=https://github.com/vadimcn/llvm-project&path=lldb/source/Plugins/TypeSystem/Rust×tamp=2023-09-11T04:55:10Z) * [A work in progress reimplementation of Rust support](https://github.com/Walnut356/llvm-project/tree/lldbrust/19.x) * [A Rust expression parser plugin](https://github.com/tromey/lldb/tree/a0fc10ce0dacb3038b7302fff9f6cb8cb34b37c6/source/Plugins/ExpressionParser/Rust). -This was written before the `TypeSystem` API was created. Due to the freeform nature of expression parsing, the +This was written before the `TypeSystem` API was created. +Due to the freeform nature of expression parsing, the underlyng lexing, parsing, function calling, etc. should still offer valuable insights. -## Rust Support and TypeSystemClang - -As mentioned in the debug info overview, LLDB has partial Rust support. To further clarify, Rust -uses the plugin-pipeline that was built for C/C++ (though it contains some helpers for Rust enum -types), which relies directly on the `clang` compiler's representation of types. This imposes heavy -restrictions on how much we can change when LLDB's output doesn't match what we want. Some -workarounds can help, but at the end of the day Rust's needs are secondary compared to making sure +## Rust support and TypeSystemClang + +As mentioned in the debug info overview, LLDB has partial Rust support. +To further clarify, +Rust uses the plugin-pipeline that was built for C/C++ +(though it contains some helpers for Rust enum types), +which relies directly on the `clang` compiler's representation of types. +This imposes heavy restrictions on how much we can change +when LLDB's output doesn't match what we want. +Some workarounds can help, but at the end of the day, +Rust's needs are secondary compared to making sure C and C++ compilation and debugging work correctly. -LLDB is receptive to adding a `TypeSystemRust`, but it is a massive undertaking. This section serves -to not only document how we currently interact with [`TypeSystemClang`][ts_clang], but also as light -guidance on implementing a `TypeSystemRust` in the future. +LLDB is receptive to adding a `TypeSystemRust`, but it is a massive undertaking. +This section serves to not only document how we currently interact with [`TypeSystemClang`], +but also as light guidance on implementing a `TypeSystemRust` in the future. -[ts_clang]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/TypeSystem/Clang +[`TypeSystemClang`]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/TypeSystem/Clang It is worth noting that a `TypeSystem` directly interacting with the target language's compiler is -the intention, but it is not a requirement. One can create all the necessary supporting types within -their plugin implementation. +the intention, but it is not a requirement. +One can create all the necessary supporting types within their plugin implementation. > Note: LLDB's documentation, including comments in the source code, is pretty sparse. Trying to > understand how language support works by reading `TypeSystemClang`'s implementation is somewhat @@ -47,8 +53,9 @@ their plugin implementation. ## DWARF vs PDB -LLDB is unique in being able to handle both DWARF and PDB debug information. This does come with -some added complexity. To complicate matters further, PDB support is split between `dia`, which +LLDB is unique in being able to handle both DWARF and PDB debug information. +This does come with some added complexity. +To complicate matters further, PDB support is split between `dia`, which relies on the `msdia140.dll` library distributed with Visual Studio, and `native`, which is written from scratch using publicly available information about the PDB format. @@ -63,50 +70,59 @@ from scratch using publicly available information about the PDB format. [dia_discourse]: https://discourse.llvm.org/t/rfc-removing-the-dia-pdb-plugin-from-lldb/87827 [dia_tracking]: https://github.com/llvm/llvm-project/issues/114906 -## Debug Node Parsing +## Debug node parsing -The first step is to process the raw debug nodes into something usable. This primarily occurs in -the [`DWARFASTParser`][dwarf_ast] and [`PdbAstBuilder`][pdb_ast] classes. These classes are fed a -deserialized form of the debug info generated from [`SymbolFileDWARF`][sf_dwarf] and -[`SymbolFileNativePDB`][sf_pdb] respectively. The `SymbolFile` implementers make almost no -transformations to the underlying debug info before passing it to the parsers. For both PDB and -DWARF, the debug info is read using LLVM's debug info handlers. +The first step is to process the raw debug nodes into something usable. +This primarily occurs in the [`DWARFASTParser`][dwarf_ast] and [`PdbAstBuilder`][pdb_ast] classes. +These classes are fed a deserialized form of the debug info +generated from [`SymbolFileDWARF`][sf_dwarf] and [`SymbolFileNativePDB`][sf_pdb] respectively. +The `SymbolFile` implementers make almost no +transformations to the underlying debug info before passing it to the parsers. +For both PDB and DWARF, the debug info is read using LLVM's debug info handlers. [dwarf_ast]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/SymbolFile/DWARF [pdb_ast]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/SymbolFile/NativePDB [sf_dwarf]: https://github.com/llvm/llvm-project/blob/main/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h [sf_pdb]: https://github.com/llvm/llvm-project/blob/main/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h -The parsers translate the nodes into more convenient formats for LLDB's purposes. For `clang`, these -formats are `clang::QualType`, `clang::Decl`, and `clang::DeclContext`, which are the types `clang` -uses internally when compiling C and C++. Again, using the compiler's representation of types is not a +The parsers translate the nodes into more convenient formats for LLDB's purposes. +For `clang`, these formats are `clang::QualType`, `clang::Decl`, and `clang::DeclContext`, +which are the types `clang` uses internally when compiling C and C++. +Again, using the compiler's representation of types is not a requirement, but the plugin system was built with it as a possibility. > Note: The above types will be referred to language-agnostically as `LangType`, `Decl`, and `DeclContext` when the specific implementation details of `TypeSystemClang` are not relevant. -`LangType` represents a type. This includes information such as the name of the type, the size and +`LangType` represents a type. +This includes information such as the name of the type, the size and alignment, its classification (e.g. struct, primitive, pointer), its qualifiers (e.g. `const`, `volatile`), template arguments, function argument and return types, etc. [Here][rust_type] is an example of what a `RustType` might look like. [rust_type]: https://github.com/Walnut356/llvm-project/blob/13bcfd502452606d69faeea76aec3a06db554af9/lldb/source/Plugins/TypeSystem/Rust/TypeSystemRust.h#L618 -`Decl` represents any kind of declaration. It could be a type, a variable, a static field of a +`Decl` represents any kind of declaration. +It could be a type, a variable, a static field of a struct, the value that a static or const is initialized with, etc. -`DeclContext` more or less represents a scope. `DeclContext`s typically contain `Decl`s and other -`DeclContexts`, though the relationship isn't that straight forward. For example, a function can be -both a `Decl` (because function signatures are types), **and** a `DeclContext` (because functions -contain variable declarations, nested functions declarations, etc.). +`DeclContext` more or less represents a scope. +`DeclContext`s typically contain `Decl`s and other +`DeclContexts`, though the relationship isn't that straight forward. +For example, a function can be both a `Decl` (because function signatures are types), +**and** a `DeclContext` +(because functions contain variable declarations, nested functions declarations, etc.). -The translation process can be quite verbose, but is usually straightforward. Much of the work here -is dependant on the exact information needed to fill out `LangType`, `Decl`, and `DeclContext`. +The translation process can be quite verbose, but is usually straightforward. +Much of the work here is dependant on the exact information needed to fill out `LangType`, +`Decl`, and `DeclContext`. Once a node is translated, a pointer to it is type-erased (`void*`) and wrapped in `CompilerType`, -`CompilerDecl`, or `CompilerDeclContext`. These wrappers associate the them with the `TypeSystem` -that owns them. Methods on these objects delegates to the `TypeSystem`, which casts the `void*` back -to the appropriate `LangType*`/`Decl*`/`DeclContext*` and operates on the internals. In Rust terms, +`CompilerDecl`, or `CompilerDeclContext`. +These wrappers associate the them with the `TypeSystem` that owns them. +Methods on these objects delegates to the `TypeSystem`, which casts the `void*` back +to the appropriate `LangType*`/`Decl*`/`DeclContext*` and operates on the internals. +In Rust terms, the relationship looks something like this: ```Rust @@ -135,59 +151,67 @@ impl TypeSystem for TypeSystemLang { } ``` -## Type Systems +## Type systems The [`TypeSystem` interface][ts_interface] has 3 major purposes: [ts_interface]: https://github.com/llvm/llvm-project/blob/main/lldb/include/lldb/Symbol/TypeSystem.h#L69 -1. Act as the "sole authority" of a language's types. This allows the type system to be added to -LLDB's "pool" of type systems. When an executable is loaded, the target language is determined, and -the pool is queried to find a `TypeSystem` that claims it can handle the language. One can also use -the `TypeSystem` to retrieve the backing `SymbolFile`, search for types, and synthesize basic types -that might not exist in the debug info (e.g. primitives, arrays-of-`T`, pointers-to-`T`). +1. Act as the "sole authority" of a language's types. + This allows the type system to be added to LLDB's "pool" of type systems. + When an executable is loaded, the target language is determined, and + the pool is queried to find a `TypeSystem` that claims it can handle the language. + One can also use the `TypeSystem` to retrieve the backing `SymbolFile`, + search for types, and synthesize basic types + that might not exist in the debug info (e.g. primitives, arrays-of-`T`, pointers-to-`T`). 2. Manage the lifetimes of the `LangType`, `Decl`, and `DeclContext` objects 3. Customize the "defaults" of how those types appear and how they can be interacted with. The first two functions are pretty straightforward so we will focus on the third. Many of the functions in the `TypeSystem` interface will look familiar if you have worked with the -visualizer scripts. These functions underpin `SBType` the `SBValue` functions with matching names. +visualizer scripts. +These functions underpin `SBType` the `SBValue` functions with matching names. For example, `TypeSystem::GetFormat` returns the default format for the type if no custom formatter has been applied to it. -Of particular note are `GetIndexOfChildWithName` and `GetNumChildren`. The `TypeSystem` versions of -these functions operate on a *type*, not a value like the `SBValue` versions. The values returned -from the `TypeSystem` functions dictate what parts of the struct can be interacted with *at all* by -the rest of LLDB. If a field is ommitted, that field effectively no longer exists to LLDB. +Of particular note are `GetIndexOfChildWithName` and `GetNumChildren`. +The `TypeSystem` versions of these functions operate on a *type*, +not a value like the `SBValue` versions. +The values returned from the `TypeSystem` functions +dictate what parts of the struct can be interacted with *at all* by the rest of LLDB. +If a field is ommitted, that field effectively no longer exists to LLDB. Additionally, since they do not work with objects, there is no underlying memory to inspect or -interpret. Essentially, this means these functions do not have the same purpose as their equivalent -`SyntheticProvider` functions. There is no way to determine how many elements a `Vec` has or what -address those elements live at. It is also not possible to determine the value of the discriminant -of a sum-type. +interpret. +Essentially, this means these functions do not have the same purpose as their equivalent +`SyntheticProvider` functions. +There is no way to determine how many elements a `Vec` has or what address those elements live at. +It is also not possible to determine the value of the discriminant of a sum-type. Ideally, the `TypeSystem` should expose types as they appear in the debug info with as few -alterations as possible. LLDB's synthetics and frontend can handle making the type pretty. If some -piece of information is useless, the Rust compiler should be altered to not output that debug info -in the first place. +alterations as possible. +LLDB's synthetics and frontend can handle making the type pretty. +If some piece of information is useless, +the Rust compiler should be altered to not output that debug info in the first place. -## Expression Parsing +## Expression parsing -The `TypeSystem` is typically written to have a counterpart that can handle expression parsing. It -requires implementing a few extra functions in the `TypeSystem` interface. The bulk of the -expression parsing code should live in [lldb/source/Plugins/ExpressionParser][expr]. +The `TypeSystem` is typically written to have a counterpart that can handle expression parsing. +It requires implementing a few extra functions in the `TypeSystem` interface. +The bulk of the expression parsing code should live in [lldb/source/Plugins/ExpressionParser][expr]. [expr]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/ExpressionParser -There isn't too much of note about the parser. It requires implementing a simple interpreter that -can handle (possibly simplified) Rust syntax. They operate on `lldb::ValueObject`s, which are the -objects that underpin `SBValue`. +There isn't too much of note about the parser. +It requires implementing a simple interpreter that can handle (possibly simplified) Rust syntax. +They operate on `lldb::ValueObject`s, which are the objects that underpin `SBValue`. ## Language -The [`Language` plugins][lang_plugin] are the C++ equivalent to the Python visualizer scripts. They -operate on `SBValue` objects for the same purpose: creating synthetic children and pretty-printing. +The [`Language` plugins][lang_plugin] are the C++ equivalent to the Python visualizer scripts. +They operate on `SBValue` objects for the same purpose: +creating synthetic children and pretty-printing. The [CPlusPlusLanguage's implementations][cpp_lang] for the LibCxx types are great resources to learn how visualizers should be written. @@ -200,9 +224,10 @@ their python equivalent. While debug node parsing, type systems, and expression parsing are all closely tied to eachother, the `Language` plugin is encapsulated more and thus can be written "standalone" for any language -that an existing type system supports. Due to the lower barrier of entry, a `RustLanguage` plugin +that an existing type system supports. +Due to the lower barrier of entry, a `RustLanguage` plugin may be a good stepping stone towards full language support in LLDB. ## Visualizers -WIP \ No newline at end of file +WIP diff --git a/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md b/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md index 83e2b0d5794e8..58d4bd068f5bb 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md @@ -330,7 +330,7 @@ of the synthetic. By implementing an instance summary, we can retrieve the variant name via `self.variant.GetTypeName()` and some string manipulation. -# Writing Visualizer Scripts +# Writing visualizer scripts > IMPORTANT: Unlike GDB and CDB, LLDB can debug executables with either DWARF or PDB debug info. >Visualizers must be written to account for both formats whenever possible. See: @@ -373,7 +373,7 @@ use depending on what version of LLDB the script detects. This is vital for backwards compatibility once we begin using recognizer functions, as recognizers were added in lldb 19.0. -## Visualizer Resolution +## Visualizer resolution The order that visualizers resolve in is listed [here][formatters_101]. In short: diff --git a/src/doc/rustc-dev-guide/src/debuginfo/llvm-codegen.md b/src/doc/rustc-dev-guide/src/debuginfo/llvm-codegen.md index cc052b6b965f1..4f5bdaa1e6795 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/llvm-codegen.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/llvm-codegen.md @@ -1,7 +1,8 @@ -# (WIP) LLVM Codegen +# LLVM codegen When Rust calls an LLVM `DIBuilder` function, LLVM translates the given information to a -["debug record"][dbg_record] that is format-agnostic. These records can be inspected in the LLVM-IR. +["debug record"][dbg_record] that is format-agnostic. +These records can be inspected in the LLVM-IR. [dbg_record]: https://llvm.org/docs/SourceLevelDebugging.html#debug-records @@ -9,4 +10,4 @@ It is important to note that tags within the debug records are **always stored a the target calls for PDB debug info, during codegen the debug records will then be passed through [a module that translates the DWARF tags to their CodeView counterparts][cv]. -[cv]:https://github.com/llvm/llvm-project/blob/main/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp \ No newline at end of file +[cv]:https://github.com/llvm/llvm-project/blob/main/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp diff --git a/src/doc/rustc-dev-guide/src/debuginfo/natvis-visualizers.md b/src/doc/rustc-dev-guide/src/debuginfo/natvis-visualizers.md index 653e7d5d65555..1b156465f4f85 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/natvis-visualizers.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/natvis-visualizers.md @@ -1,3 +1,3 @@ -# (WIP) CDB - Natvis +# CDB - Natvis Official documentation for Natvis can be found [here](https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects) and [here](https://code.visualstudio.com/docs/cpp/natvis) diff --git a/src/doc/rustc-dev-guide/src/debuginfo/rust-codegen.md b/src/doc/rustc-dev-guide/src/debuginfo/rust-codegen.md index 73fa68d36076a..f879579de9907 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/rust-codegen.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/rust-codegen.md @@ -1,43 +1,46 @@ -# Rust Codegen +# Rust codegen The first phase in debug info generation requires Rust to inspect the MIR of the program and -communicate it to LLVM. This is primarily done in [`rustc_codegen_llvm/debuginfo`][llvm_di], though -some type-name processing exists in [`rustc_codegen_ssa/debuginfo`][ssa_di]. Rust communicates to -LLVM via the `DIBuilder` API - a thin wrapper around LLVM's internals that exists in -[rustc_llvm][rustc_llvm]. +communicate it to LLVM. +This is primarily done in [`rustc_codegen_llvm/debuginfo`][llvm_di], though +some type-name processing exists in [`rustc_codegen_ssa/debuginfo`][ssa_di]. +Rust communicates to LLVM via the `DIBuilder` API, +a thin wrapper around LLVM's internals that exists in [rustc_llvm]. [llvm_di]: https://github.com/rust-lang/rust/tree/main/compiler/rustc_codegen_llvm/src/debuginfo [ssa_di]: https://github.com/rust-lang/rust/tree/main/compiler/rustc_codegen_ssa/src/debuginfo [rustc_llvm]: https://github.com/rust-lang/rust/tree/main/compiler/rustc_llvm -# Type Information +# Type information Type information typically consists of the type name, size, alignment, as well as things like -fields, generic parameters, and storage modifiers if they are relevant. Much of this work happens in -[rustc_codegen_llvm/src/debuginfo/metadata][di_metadata]. +fields, generic parameters, and storage modifiers if they are relevant. +Much of this work happens in [rustc_codegen_llvm/src/debuginfo/metadata][di_metadata]. [di_metadata]: https://github.com/rust-lang/rust/blob/main/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs It is important to keep in mind that the goal is not necessarily "represent types exactly how they appear in Rust", rather it is to represent them in a way that allows debuggers to most accurately -reconstruct the data during debugging. This distinction is vital to understanding the core work that +reconstruct the data during debugging. +This distinction is vital to understanding the core work that occurs on this layer; many changes made here will be for the purpose of working around debugger limitations when no other option will work. ## Quirks -Rust's generated DI nodes "pretend" to be C/C++ for both CDB and LLDB's sake. This can result in -some unintuitive and non-idiomatic debug info. +Rust's generated DI nodes "pretend" to be C/C++ for both CDB and LLDB's sake. +This can result in some unintuitive and non-idiomatic debug info. -### Pointers and Reference +### Pointers and references Wide pointers/references/`Box` are treated as a struct with 2 fields: `data_ptr` and `length`. All non-wide pointers, references, and `Box` pointers are output as pointer nodes, and no -distinction is made between `mut` and non-`mut`. Several attempts have been made to rectify this, -but unfortunately there is not a straightforward solution. Using the `reference` DI nodes of the -respective formats has pitfalls. There is a semantic difference between C++ references and Rust -references that is unreconcilable. +distinction is made between `mut` and non-`mut`. +Several attempts have been made to rectify this, +but unfortunately there is not a straightforward solution. +Using the `reference` DI nodes of the respective formats has pitfalls. +There is a semantic difference between C++ references and Rust references that is unreconcilable. >From [cppreference](https://en.cppreference.com/w/cpp/language/reference.html): > @@ -48,24 +51,27 @@ references that is unreconcilable. > >Because references are not objects, **there are no arrays of references, no pointers to references, and no references to references** -The current proposed solution is to simply [typedef the pointer nodes][issue_144394]. +The current proposed solution is to simply [typedef the pointer nodes]. -[issue_144394]: https://github.com/rust-lang/rust/pull/144394 +[typedef the pointer nodes]: https://github.com/rust-lang/rust/pull/144394 Using the `const` qualifier to denote non-`mut` poses potential issues due to LLDB's internal optimizations. In short, LLDB attempts to cache the child-values of variables (e.g. struct fields, -array elements) when stepping through code. A heuristic is used to determine which values are safely -cache-able, and `const` is part of that heuristic. Research has not been done into how this would +array elements) when stepping through code. +A heuristic is used to determine which values are safely +cache-able, and `const` is part of that heuristic. +Research has not been done into how this would interact with things like Rust's interior mutability constructs. ### DWARF vs PDB While most of the type information is fairly straight forward, one notable issue is the debug info -format of the target. Each format has different semantics and limitations, as such they require -slightly different debug info in some cases. This is gated by calls to -[`cpp_like_debuginfo`][cpp_like]. +format of the target. +Each format has different semantics and limitations, as such they require +slightly different debug info in some cases. +This is gated by calls to [`cpp_like_debuginfo`]. -[cpp_like]: https://github.com/rust-lang/rust/blob/main/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs#L813 +[`cpp_like_debuginfo`]: https://github.com/rust-lang/rust/blob/main/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs#L813 ### Naming @@ -97,7 +103,8 @@ consecutive `>`'s with a space (`> >`) in type names. [^2]: While these type names are generated as part of the debug info node (which is then wrapped in a typedef node with the Rust name), once the LLVM-IR node is converted to a CodeView node, the type -name information is lost. This is because CodeView has special shorthand nodes for primitive types, +name information is lost. +This is because CodeView has special shorthand nodes for primitive types, and those shorthand nodes to not have a "name" field. ### Generics @@ -106,10 +113,11 @@ Rust outputs generic *type* information (`T` in `ArrayVec`), but no information (`N` in `ArrayVec`). CodeView does not have a leaf node for generics/C++ templates, so all generic information is lost -when generating PDB debug info. There are workarounds that allow the debugger to retrieve the -generic arguments via the type name, but it is fragile solution at best. Efforts are being made to -contact Microsoft to correct this deficiency, and/or to use one of the unused CodeView node types as -a suitable equivalent. +when generating PDB debug info. +There are workarounds that allow the debugger to retrieve the +generic arguments via the type name, but it is fragile solution at best. +Efforts are being made to contact Microsoft to correct this deficiency, +and/or to use one of the unused CodeView node types as a suitable equivalent. ### Type aliases @@ -126,9 +134,10 @@ Enum DI nodes are generated in [rustc_codegen_llvm/src/debuginfo/metadata/enums] #### DWARF -DWARF has a dedicated node for discriminated unions: `DW_TAG_variant`. It is a container that -references `DW_TAG_variant_part` nodes that may or may not contain a discriminant value. The -hierarchy looks as follows: +DWARF has a dedicated node for discriminated unions: `DW_TAG_variant`. +It is a container that references `DW_TAG_variant_part` nodes +that may or may not contain a discriminant value. +The hierarchy looks as follows: ```txt DW_TAG_structure_type (top-level type for the coroutine) @@ -175,9 +184,10 @@ union enum2$ { ``` An important note is that due to limitations in LLDB, the `DISCR_*` value generated is always a -`u64` even if the value is not `#[repr(u64)]`. This is largely a non-issue for LLDB because the +`u64` even if the value is not `#[repr(u64)]`. +This is largely a non-issue for LLDB because the `DISCR_*` value and the `tag` are read into `uint64_t` values regardless of their type. -# Source Information +# Source information -TODO \ No newline at end of file +TODO diff --git a/src/doc/rustc-dev-guide/src/debuginfo/testing.md b/src/doc/rustc-dev-guide/src/debuginfo/testing.md index f58050875e1a0..7aec83aa97ac6 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/testing.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/testing.md @@ -1,8 +1,8 @@ -# (WIP) Testing +# Testing The debug info test suite is undergoing a substantial rewrite. This section will be filled out as the rewrite makes progress. Please see [this tracking issue][148483] for more information. -[148483]: https://github.com/rust-lang/rust/issues/148483 \ No newline at end of file +[148483]: https://github.com/rust-lang/rust/issues/148483 diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index 7ae1c881be8f7..1aca1b2eab1c5 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -34,6 +34,9 @@ to these tools should be filed against the tools directly in their respective up The exception is that when rustc changes are required to implement a new tool feature or test, that should happen in one collective rustc PR. +Similarly, if your rustc changes break the build of some subtree dependency, +you should include fixes for those in the rustc PR as well. + `subtree` dependencies are currently managed by two distinct approaches: * Using `git subtree` diff --git a/src/doc/rustc-dev-guide/src/hir-typeck/summary.md b/src/doc/rustc-dev-guide/src/hir-typeck/summary.md index 23df97a9cf833..5fd10663694c5 100644 --- a/src/doc/rustc-dev-guide/src/hir-typeck/summary.md +++ b/src/doc/rustc-dev-guide/src/hir-typeck/summary.md @@ -3,7 +3,7 @@ The [`hir_analysis`] crate contains the source for "type collection" as well as a bunch of related functionality. Checking the bodies of functions is implemented in the [`hir_typeck`] crate. -These crates draw heavily on the [type inference] and [trait solving]. +These crates draw heavily on [type inference] and [trait solving]. [`hir_analysis`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/index.html [`hir_typeck`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/index.html @@ -14,8 +14,8 @@ These crates draw heavily on the [type inference] and [trait solving]. Type "collection" is the process of converting the types found in the HIR (`hir::Ty`), which represent the syntactic things that the user wrote, into the -**internal representation** used by the compiler (`Ty<'tcx>`) – we also do -similar conversions for where-clauses and other bits of the function signature. +**internal representation** used by the compiler (`Ty<'tcx>`). +Note that we also do similar conversions for where-clauses and other bits of the function signature. To try and get a sense of the difference, consider this function: @@ -25,9 +25,10 @@ fn foo(x: Foo, y: self::Foo) { ... } // ^^^ ^^^^^^^^^ ``` -Those two parameters `x` and `y` each have the same type: but they will have -distinct `hir::Ty` nodes. Those nodes will have different spans, and of course -they encode the path somewhat differently. But once they are "collected" into +Those two parameters `x` and `y` each have the same type, +but they will have distinct `hir::Ty` nodes. +Those nodes will have different spans, and of course they encode the path somewhat differently. +But once they are "collected" into `Ty<'tcx>` nodes, they will be represented by the exact same internal type. Collection is defined as a bundle of [queries] for computing information about @@ -35,8 +36,7 @@ the various functions, traits, and other items in the crate being compiled. Note that each of these queries is concerned with *interprocedural* things – for example, for a function definition, collection will figure out the type and signature of the function, but it will not visit the *body* of the function in -any way, nor examine type annotations on local variables (that's the job of -type *checking*). +any way, nor examine type annotations on local variables (that's the job of type *checking*). For more details, see the [`collect`][collect] module. diff --git a/src/doc/rustc-dev-guide/src/normalization.md b/src/doc/rustc-dev-guide/src/normalization.md index 0a79d98c5db04..a8bc9a87d3b4c 100644 --- a/src/doc/rustc-dev-guide/src/normalization.md +++ b/src/doc/rustc-dev-guide/src/normalization.md @@ -1,4 +1,4 @@ -# Aliases and Normalization +# Aliases and normalization ## Aliases @@ -17,7 +17,7 @@ so this chapter mostly discusses things in the context of types (even though the [`TyKind::Alias`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/enum.TyKind.html#variant.Alias [`AliasTyKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/enum.AliasTyKind.html -### Rigid, Ambiguous and Unnormalized Aliases +### Rigid, ambiguous and unnormalized aliases Aliases can either be "rigid", "ambiguous", or simply unnormalized. @@ -68,7 +68,7 @@ only it just hasn't been done yet. It is worth noting that Free and Inherent aliases cannot be rigid or ambiguous as naming them also implies having resolved the definition of the alias, which specifies the underlying type of the alias. -### Diverging Aliases +### Diverging aliases An alias is considered to "diverge" if its definition does not specify an underlying non-alias type to normalize to. A concrete example of diverging aliases: @@ -126,11 +126,11 @@ fn main() { In this example, we only encounter an error from the diverging alias during codegen of `foo::<()>`. If the call to `foo` is removed, then no compilation error will be emitted. -### Opaque Types +### Opaque types Opaque types are a relatively special kind of alias, and are covered in [their own chapter](opaque-types-type-alias-impl-trait.md). -### Const Aliases +### Const aliases Unlike type aliases, const aliases are not represented directly in the type system. Instead, const aliases are always an anonymous body containing a path expression to a const item. @@ -157,7 +157,7 @@ fn bar() { This is likely to change as const generics functionality is improved. For example, `feature(associated_const_equality)` and `feature(min_generic_const_args)` both require handling const aliases similarly to types (without an anonymous constant wrapping all const args). -## What is Normalization +## What is normalization ### Structural vs deep normalization diff --git a/src/doc/rustc-dev-guide/src/tests/autodiff-ci-job.md b/src/doc/rustc-dev-guide/src/tests/autodiff-ci-job.md new file mode 100644 index 0000000000000..3e5d55fbc81a9 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/tests/autodiff-ci-job.md @@ -0,0 +1,45 @@ +# Autodiff CI job + +The [`optional-x86_64-gnu-autodiff`] job provides continuous test coverage for +the experimental `autodiff` feature and its integration with LLVM Enzyme. It is +an optional [auto job](./ci.md#auto-builds), so a failure does not prevent a +pull request from being merged. + +For more context about the feature, see the [autodiff tracking issue] and the +[autodiff internals](../autodiff/internals.md) chapter. + +## What is tested + +The job checks: + +- forward- and reverse-mode macro expansion and diagnostics; +- LLVM IR generation for autodiff; +- enforcement of the `autodiff` feature gate. + +## Running the job + +To run the job in a try build, comment on a pull request: + +```text +@bors try jobs=optional-x86_64-gnu-autodiff +``` + +To run the job locally, run this command from a Rust checkout: + +```console +$ cargo run --manifest-path src/ci/citool/Cargo.toml run-local optional-x86_64-gnu-autodiff +``` + +See [Testing with Docker](./docker.md) for more information about running CI +jobs locally. + +## Point of contact + +If you have questions or need help with a failure in this job, open a new topic +in the [autodiff Zulip channel]. For suspected Enzyme backend failures, see the +[autodiff debugging guide]. + +[autodiff Zulip channel]: https://rust-lang.zulipchat.com/#narrow/channel/390790-wg-autodiff +[autodiff debugging guide]: ../autodiff/debugging.md +[autodiff tracking issue]: https://github.com/rust-lang/rust/issues/124509 +[`optional-x86_64-gnu-autodiff`]: https://github.com/rust-lang/rust/blob/HEAD/src/ci/github-actions/jobs.yml diff --git a/src/doc/rustc-dev-guide/src/thir.md b/src/doc/rustc-dev-guide/src/thir.md index 802df2286095d..1ba30d86a6913 100644 --- a/src/doc/rustc-dev-guide/src/thir.md +++ b/src/doc/rustc-dev-guide/src/thir.md @@ -48,7 +48,7 @@ which is useful to keep peak memory in check. Having a THIR representation of all bodies of a crate in memory at the same time would be very heavy. -You can get a debug representation of the THIR by passing the `-Zunpretty=thir-tree` flag +You can get a debug representation of the THIR by passing the `-Zunpretty=thir-flat` flag to `rustc`. To demonstrate, let's use the following example: @@ -59,213 +59,168 @@ fn main() { } ``` -Here is how that gets represented in THIR (as of Aug 2022): +Here is how that gets represented in THIR (as of Jul 2026): ```rust,no_run +DefId(0:3 ~ main[26fd]::main): Thir { + body_type: Fn( + fn(), + ), + attributes: {}, // no match arms arms: [], + blocks: [ + Block { + targeted_by_break: false, + region_scope: Node(1), + span: main.rs:1:11: 3:2 (#0), + stmts: [ + s0, + ], + expr: None, + safety_mode: Safe, + }, + ], exprs: [ // expression 0, a literal with a value of 1 Expr { - ty: i32, - temp_lifetime: Some( - Node(1), - ), - span: oneplustwo.rs:2:13: 2:14 (#0), kind: Literal { lit: Spanned { node: Int( - 1, + Pu128( + 1, + ), Unsuffixed, ), - span: oneplustwo.rs:2:13: 2:14 (#0), + span: main.rs:2:13: 2:14 (#0), }, neg: false, }, + ty: i32, + temp_scope_id: 4, + span: main.rs:2:13: 2:14 (#0), }, // expression 1, scope surrounding literal 1 Expr { - ty: i32, - temp_lifetime: Some( - Node(1), - ), - span: oneplustwo.rs:2:13: 2:14 (#0), kind: Scope { + region_scope: Node(4), + hir_id: HirId(DefId(0:3 ~ main[26fd]::main).4), // reference to expression 0 above - region_scope: Node(3), - lint_level: Explicit( - HirId { - owner: DefId(0:3 ~ oneplustwo[6932]::main), - local_id: 3, - }, - ), value: e0, }, + ty: i32, + temp_scope_id: 4, + span: main.rs:2:13: 2:14 (#0), }, // expression 2, literal 2 Expr { - ty: i32, - temp_lifetime: Some( - Node(1), - ), - span: oneplustwo.rs:2:17: 2:18 (#0), kind: Literal { lit: Spanned { node: Int( - 2, + Pu128( + 2, + ), Unsuffixed, ), - span: oneplustwo.rs:2:17: 2:18 (#0), + span: main.rs:2:17: 2:18 (#0), }, neg: false, }, + ty: i32, + temp_scope_id: 5, + span: main.rs:2:17: 2:18 (#0), }, // expression 3, scope surrounding literal 2 Expr { - ty: i32, - temp_lifetime: Some( - Node(1), - ), - span: oneplustwo.rs:2:17: 2:18 (#0), kind: Scope { - region_scope: Node(4), - lint_level: Explicit( - HirId { - owner: DefId(0:3 ~ oneplustwo[6932]::main), - local_id: 4, - }, - ), - // reference to expression 2 above + region_scope: Node(5), + hir_id: HirId(DefId(0:3 ~ main[26fd]::main).5), + // reference to expression 0 above value: e2, }, + ty: i32, + temp_scope_id: 5, + span: main.rs:2:17: 2:18 (#0), }, // expression 4, represents 1 + 2 Expr { - ty: i32, - temp_lifetime: Some( - Node(1), - ), - span: oneplustwo.rs:2:13: 2:18 (#0), kind: Binary { op: Add, // references to scopes surrounding literals above lhs: e1, rhs: e3, }, + ty: i32, + temp_scope_id: 3, + span: main.rs:2:13: 2:18 (#0), }, // expression 5, scope surrounding expression 4 Expr { - ty: i32, - temp_lifetime: Some( - Node(1), - ), - span: oneplustwo.rs:2:13: 2:18 (#0), kind: Scope { - region_scope: Node(5), - lint_level: Explicit( - HirId { - owner: DefId(0:3 ~ oneplustwo[6932]::main), - local_id: 5, - }, - ), + region_scope: Node(3), + hir_id: HirId(DefId(0:3 ~ main[26fd]::main).3), value: e4, }, + ty: i32, + temp_scope_id: 3, + span: main.rs:2:13: 2:18 (#0), }, // expression 6, block around statement Expr { - ty: (), - temp_lifetime: Some( - Node(9), - ), - span: oneplustwo.rs:1:11: 3:2 (#0), kind: Block { - body: Block { - targeted_by_break: false, - region_scope: Node(8), - opt_destruction_scope: None, - span: oneplustwo.rs:1:11: 3:2 (#0), - // reference to statement 0 below - stmts: [ - s0, - ], - expr: None, - safety_mode: Safe, - }, + block: b0, }, + ty: (), + temp_scope_id: 8, + span: main.rs:1:11: 3:2 (#0), }, // expression 7, scope around block in expression 6 Expr { - ty: (), - temp_lifetime: Some( - Node(9), - ), - span: oneplustwo.rs:1:11: 3:2 (#0), kind: Scope { - region_scope: Node(9), - lint_level: Explicit( - HirId { - owner: DefId(0:3 ~ oneplustwo[6932]::main), - local_id: 9, - }, - ), + region_scope: Node(8), + hir_id: HirId(DefId(0:3 ~ main[26fd]::main).8), value: e6, }, - }, - // destruction scope around expression 7 - Expr { ty: (), - temp_lifetime: Some( - Node(9), - ), - span: oneplustwo.rs:1:11: 3:2 (#0), - kind: Scope { - region_scope: Destruction(9), - lint_level: Inherited, - value: e7, - }, + temp_scope_id: 8, + span: main.rs:1:11: 3:2 (#0), }, ], stmts: [ - // let statement Stmt { kind: Let { - remainder_scope: Remainder { block: 8, first_statement_index: 0}, - init_scope: Node(1), + remainder_scope: Remainder { block: 1, first_statement_index: 0}, + init_scope: Node(2), pattern: Pat { ty: i32, - span: oneplustwo.rs:2:9: 2:10 (#0), + span: main.rs:2:9: 2:10 (#0), + extra: None, kind: Binding { - mutability: Not, name: "x", - mode: ByValue, + mode: BindingMode( + No, + Not, + ), var: LocalVarId( - HirId { - owner: DefId(0:3 ~ oneplustwo[6932]::main), - local_id: 7, - }, + HirId(DefId(0:3 ~ main[26fd]::main).7), ), ty: i32, subpattern: None, is_primary: true, + is_shorthand: false, }, }, initializer: Some( e5, ), else_block: None, - lint_level: Explicit( - HirId { - owner: DefId(0:3 ~ oneplustwo[6932]::main), - local_id: 6, - }, - ), + hir_id: HirId(DefId(0:3 ~ main[26fd]::main).6), + span: main.rs:2:5: 2:18 (#0), }, - opt_destruction_scope: Some( - Destruction(1), - ), }, ], + params: [], } ``` diff --git a/src/doc/rustc-dev-guide/src/typing-parameter-envs.md b/src/doc/rustc-dev-guide/src/typing-parameter-envs.md index 45635ebfa15d6..db9f369d2659e 100644 --- a/src/doc/rustc-dev-guide/src/typing-parameter-envs.md +++ b/src/doc/rustc-dev-guide/src/typing-parameter-envs.md @@ -1,6 +1,6 @@ -# Typing/Parameter Environments +# Typing/Parameter environments -## Typing Environments +## Typing environments When interacting with the type system there are a few variables to consider that can affect the results of trait solving. The set of in-scope where clauses, and what phase of the compiler type system operations are being performed in (the [`ParamEnv`][penv] and [`TypingMode`][tmode] structs respectively). @@ -14,7 +14,7 @@ whereas different `ParamEnv`s can be used on a per-goal basis. [ocx]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/struct.ObligationCtxt.html [fnctxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html -## Parameter Environments +## Parameter environments ### What is a `ParamEnv` @@ -197,7 +197,7 @@ impl Other for T { // `foo`'s unnormalized `ParamEnv` would be: // `[T: Sized, U: Sized, U: Trait]` -fn foo(a: U) +fn foo(a: U) where U: Trait<::Bar>, { @@ -222,7 +222,7 @@ In the next-gen trait solver the requirement for all where clauses in the `Param [pe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html [normalize_env_or_error]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/fn.normalize_param_env_or_error.html -## Typing Modes +## Typing modes Depending on what context we are performing type system operations in, different behaviour may be required. diff --git a/src/doc/rustc/src/symbol-mangling/index.md b/src/doc/rustc/src/symbol-mangling/index.md index 3f7d55063ca6a..67df4f0a9b07f 100644 --- a/src/doc/rustc/src/symbol-mangling/index.md +++ b/src/doc/rustc/src/symbol-mangling/index.md @@ -17,13 +17,16 @@ The [`#[export_name]`attribute][reference-export_name] can be used to specify th Items listed in an [`extern` block][reference-extern-block] use the identifier of the item without mangling to refer to the item. The [`#[link_name]` attribute][reference-link_name] can be used to change that name. - +### WebAssembly import modules + +On WebAssembly targets, foreign items in `extern` blocks can use the same import name without +conflicting when they use different [`#[link(wasm_import_module = "...")]`][reference-link-attribute] +values. [reference-no_mangle]: ../../reference/abi.html#the-no_mangle-attribute [reference-export_name]: ../../reference/abi.html#the-export_name-attribute [reference-link_name]: ../../reference/items/external-blocks.html#the-link_name-attribute +[reference-link-attribute]: ../../reference/items/external-blocks.html#the-link-attribute [reference-extern-block]: ../../reference/items/external-blocks.html ## Decoding diff --git a/src/doc/unstable-book/src/library-features/try-as-dyn.md b/src/doc/unstable-book/src/library-features/try-as-dyn.md new file mode 100644 index 0000000000000..81abde709c6dc --- /dev/null +++ b/src/doc/unstable-book/src/library-features/try-as-dyn.md @@ -0,0 +1,90 @@ +# `try_as_dyn` + +The tracking issue for this feature is: [#144361] + +[#144361]: https://github.com/rust-lang/rust/issues/144361 + +------------------------ + +The `try_as_dyn` feature allows going from a generic `T` with no bounds +to a `dyn Trait`, if `T: Trait` and various conditions are upheld. It is +very related to specialization, as it allows you to specialize within +function bodies, but in a more general manner than `Any::downcast`. + +```rust +#![feature(try_as_dyn)] + +fn downcast_debug_format(t: &T) -> String { + match std::any::try_as_dyn::<_, dyn std::fmt::Debug>(t) { + Some(d) => format!("{d:?}"), + None => "default".to_string() + } +} +``` + + +## Rules and reasons for them + +> [!IMPORTANT] +> The main problem of **`try_as_dyn` and specialization is the compiler's inability, while trait-checking, to distinguish/_discriminate_ between any two given lifetimes**[^1]. + +[^1]: the compiler cannot _branch_ on whether "`'a : 'b` holds": for soundness, it can either choose not to know the answer, or _assume_ that it holds and produce an obligation for the borrow-checker which shall "assert this" (making compilation fail in a fatal manner if not). Most usages of Rust lie in the latter category (typical `where` clauses anywhere), whilst specialization/`try_as_dyn()` wants to support fallibility of the operation (_i.e._, being queried on a type not fulfilling the predicate without causing a compilation error). This rules out the latter, resulting in the need for the former, _i.e._, for the `try_as_dyn()` attempt to unconditionally "fail" with `None`. + +### `'static` is not mentioned anywhere in the `impl` block header. + +The most obvious one: if you have `impl IsStatic for &'static str`, then determining whether `&'? str : IsStatic` does hold amounts to discriminating `'? : 'static`. + +### Each outlives `where` bound (`Type: 'a` and `'a: 'b`) does not mention lifetime-infected parameters. + +Parameters are considered lifetime-infected if they are defined in an `impl` block's generic parameter list. +Const generics are excempt, as they can't affect lifetimes. +`for<'a>` lifetimes (and in the future types) are not lifetime-infected. + +We can create lifetime discrimination this way. For instance, given `impl<'a, 'b> Outlives<'a> for &'b str where 'b : 'a {}`, `Outlives<'static>` amounts to `IsStatic` from previous bullet. + +### Each lifetime-infected parameter is mentioned at most once in the `Self` type and the implemented trait's generic parameters, combined. + +Repetition of a parameter entails equality of those two use-sites; in lifetime-terms, this would be a double `'a : 'b` / `'b : 'a` clause, for instance. +Follow-up from the previous example: `impl<'a> Uses<'a> for &'a str {}`, and check whether `&'? str : Uses<'static>`. + +### Each individual trait where bound (`Type: Trait`) mentions each lifetime-infected parameter at most once. + +Mentioning a lifetime-infected parameter in multiple `where` bounds is allowed. + +Looking at the previous rules, which focuses on `Self : …`, this is just observing that shifting the requirements to other parameters within `where` clauses \[ought to\] boil down to the same set of issues. + +This is _unnecessarily restrictive_: we should be able to loosen it up somehow. Repetition only in `where` clauses seems fine. + + +### The `impl` block is a handwritten impl + +as opposed to a type implementing a trait automatically by the compiler (such as auto-traits, `dyn Bounds… : Bounds…`, and closures) + + +The reason for this is that some such auto-generated impls _come with hidden bounds or whatnot_, which run afoul of the previous rules, whilst also being _extremely challenging for the current compiler logic to know of such bounds_. +IIUC, this restriction could be lifted in the future should the compiler logic be better at spotting these hidden bounds, when present. + +One contrived such example being the case of `dyn 'u + for<'a> Outlives<'a>`, where the compiler-generated `impl` for it of `Outlives` is: `impl<'b, 'u> Outlives<'b> for dyn 'u + for<'a> Outlives<'a> where 'b : 'u {}` which violates the "`'a: 'b` not to mention lt-infected params" rule, whilst also being hard to detect in current compiler logic. + +### Associated type projections (`::Assoc`) are not mentioned anywhere in the `impl` block header. + +Associated-type equality bounds can very much amount to lifetime-infected parameter equality constraints, +which are problematic as per the "at most one mention of each lifetime-infected parameter in header" rule. +To illustrate, with the following definitions, `&'? str: Trait<'static>` discriminates `'?` against `'static`: +```rust +trait Trait<'x> {} +impl<'a, 'b> Trait<'b> for &'a str +where + // &'a str = &'b str, + Option<&'a str>: IntoIterator, +{} +``` + +```rust +trait Trait<'x> {} +impl<'a> Trait<'a> for &'a str {} +``` + +### Each trait `where` bound with an associated type equality (`Type: Trait`) does not mention lifetime-infected parameters. + +Checking whether `Option<&'? str>: IntoIterator` holds discriminates `'?` against `'static`. diff --git a/tests/assembly-llvm/asm/aarch64-types.rs b/tests/assembly-llvm/asm/aarch64-types.rs index 21f9294dc647b..c171ba3b11e13 100644 --- a/tests/assembly-llvm/asm/aarch64-types.rs +++ b/tests/assembly-llvm/asm/aarch64-types.rs @@ -1,8 +1,10 @@ //@ add-minicore -//@ revisions: aarch64 arm64ec +//@ revisions: aarch64 aarch64_be arm64ec //@ assembly-output: emit-asm //@ [aarch64] compile-flags: --target aarch64-unknown-linux-gnu //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64_be] compile-flags: --target aarch64_be-unknown-linux-gnu +//@ [aarch64_be] needs-llvm-components: aarch64 //@ [arm64ec] compile-flags: --target arm64ec-pc-windows-msvc //@ [arm64ec] needs-llvm-components: aarch64 //@ compile-flags: -Zmerge-functions=disabled diff --git a/tests/assembly-llvm/asm/global_asm.rs b/tests/assembly-llvm/asm/global_asm.rs index 8a4bf98c7450b..f7bf13281dbde 100644 --- a/tests/assembly-llvm/asm/global_asm.rs +++ b/tests/assembly-llvm/asm/global_asm.rs @@ -5,6 +5,7 @@ //@ compile-flags: -C symbol-mangling-version=v0 #![crate_type = "rlib"] +#![feature(asm_const_ptr)] use std::arch::global_asm; @@ -26,6 +27,10 @@ global_asm!("call {}", sym my_func); global_asm!("lea rax, [rip + {}]", sym MY_STATIC); // CHECK: call _RNvC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_10global_asm6foobar global_asm!("call {}", sym foobar); +// CHECK: lea rax, [rip + _RNKNaC[[CRATE_IDENT]]_10global_asms4_00B3_] +global_asm!("lea rax, [rip + {}]", const &1); +// CHECK: lea rax, [rip + _RNKNaC[[CRATE_IDENT]]_10global_asms5_00B3_+4] +global_asm!("lea rax, [rip + {}]", const &[1; 2][1]); // CHECK: _RNvC[[CRATE_IDENT]]_10global_asm6foobar: fn foobar() { loop {} diff --git a/tests/assembly-llvm/asm/x86-types.rs b/tests/assembly-llvm/asm/x86-types.rs index 6e52ab260569d..ac778f6320c28 100644 --- a/tests/assembly-llvm/asm/x86-types.rs +++ b/tests/assembly-llvm/asm/x86-types.rs @@ -9,7 +9,7 @@ //@ compile-flags: -C target-feature=+avx512bw //@ compile-flags: -Zmerge-functions=disabled -#![feature(no_core, f16, f128)] +#![feature(no_core, f16, f128, asm_const_ptr)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] @@ -43,6 +43,15 @@ pub unsafe fn sym_static() { asm!("mov al, byte ptr [{}]", sym extern_static); } +// CHECK-LABEL: const_ptr: +// CHECK: #APP +// CHECK: mov al, byte ptr [{{.*}}anon{{.*}}] +// CHECK: #NO_APP +#[no_mangle] +pub unsafe fn const_ptr() { + asm!("mov al, byte ptr [{}]", const &1u8); +} + macro_rules! check { ($func:ident $ty:ident $class:ident $mov:literal) => { #[no_mangle] diff --git a/tests/assembly-llvm/cmse-clear-padding.rs b/tests/assembly-llvm/cmse-clear-padding.rs index b092bf304dcca..93b10845ea511 100644 --- a/tests/assembly-llvm/cmse-clear-padding.rs +++ b/tests/assembly-llvm/cmse-clear-padding.rs @@ -123,6 +123,8 @@ struct WideU8 { a: u8, } +// `extern "C"` does not clear the padding. +// // CHECK-LABEL: c_ret_with_wide_u8: // CHECK: mov r7, sp // CHECK-NEXT: orr.w r0, r0, r1, lsl #16 @@ -131,6 +133,8 @@ extern "C" fn c_ret_with_wide_u8(a: u8, b: u8) -> [WideU8; 2] { [WideU8 { a }, WideU8 { a: b }] } +// Upper bits are cleared by uxtb. +// // CHECK-LABEL: cmse_ret_with_wide_u8: // CHECK: mov r7, sp // CHECK-NEXT: uxtb r1, r1 @@ -141,6 +145,36 @@ extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8(a: u8, b: u8) -> [WideU8; [WideU8 { a }, WideU8 { a: b }] } +// Same idea, the padding is recognized even through the MaybeUninit. +// +// CHECK-LABEL: cmse_ret_with_wide_u8_uninit: +// CHECK: mov r7, sp +// CHECK-NEXT: uxtb r0, r0 +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +// CHECK-NEXT: bic r0, r0, #-16711936 +#[no_mangle] +extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8_uninit( + a: u16, + b: u16, +) -> [MaybeUninit; 2] { + unsafe { [mem::transmute(a), mem::transmute(b)] } +} + +// Same idea, the padding is recognized even through the MaybeUninit. +// +// CHECK-LABEL: cmse_ret_with_wide_u8_uninit_tuple: +// CHECK: mov r7, sp +// CHECK-NEXT: uxtb r0, r0 +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +// CHECK-NEXT: bic r0, r0, #-16711936 +#[no_mangle] +extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8_uninit_tuple( + a: u16, + b: u16, +) -> (MaybeUninit, MaybeUninit) { + unsafe { (mem::transmute(a), mem::transmute(b)) } +} + // CHECK-LABEL: c_call_with_inner_wide_u8: // CHECK: push {r7, lr} // CHECK-NEXT: .setfp r7, sp @@ -177,28 +211,230 @@ extern "C" fn cmse_call_with_inner_wide_u8( unsafe { f(x) } } -// CHECK-LABEL: cmse_ret_with_wide_u8_uninit: +/// No variant-dependent padding. +#[repr(C)] +enum VariantsSameSize { + A(u16), + B(u16), +} +impl Copy for VariantsSameSize {} + +// CHECK-LABEL: variants_same_size: // CHECK: mov r7, sp -// CHECK-NEXT: uxtb r0, r0 +// CHECK-NEXT: ldrh r1, [r0, #2] +// CHECK-NEXT: ldrb r0, [r0] // CHECK-NEXT: orr.w r0, r0, r1, lsl #16 -// CHECK-NEXT: bic r0, r0, #-16711936 #[no_mangle] -extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8_uninit( - a: u16, - b: u16, -) -> [MaybeUninit; 2] { - unsafe { [mem::transmute(a), mem::transmute(b)] } +extern "cmse-nonsecure-entry" fn variants_same_size(v: &VariantsSameSize) -> VariantsSameSize { + *v } -// CHECK-LABEL: cmse_ret_with_wide_u8_uninit_tuple: +/// One byte of variant-dependent padding. +#[repr(C)] +enum VariantsDifferentSize { + A(u8), + B(u16), +} +impl Copy for VariantsDifferentSize {} + +// A uxtbeq conditionally clears the padding only for variant A. +// +// CHECK-LABEL: variants_different_size: // CHECK: mov r7, sp -// CHECK-NEXT: uxtb r0, r0 +// CHECK-NEXT: ldrh r1, [r0, #2] +// CHECK-NEXT: ldrb r0, [r0] +// CHECK-NEXT: lsls r2, r0, #31 +// CHECK-NEXT: it eq +// CHECK-NEXT: uxtbeq r1, r1 // CHECK-NEXT: orr.w r0, r0, r1, lsl #16 -// CHECK-NEXT: bic r0, r0, #-16711936 #[no_mangle] -extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8_uninit_tuple( - a: u16, - b: u16, -) -> (MaybeUninit, MaybeUninit) { - unsafe { (mem::transmute(a), mem::transmute(b)) } +extern "cmse-nonsecure-entry" fn variants_different_size( + v: &VariantsDifferentSize, +) -> VariantsDifferentSize { + *v +} + +enum Void {} +impl Copy for Void {} + +#[repr(C)] +enum UninhabitedVariant { + A(Void), + B(u16), +} +impl Copy for UninhabitedVariant {} + +// Only `B` is inhabited, so reading the tag is not needed. +// +// CHECK-LABEL: uninhabited_variant: +// CHECK: mov r7, sp +// CHECK-NEXT: ldrh r1, [r0, #2] +// CHECK-NEXT: ldrb r0, [r0] +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +extern "cmse-nonsecure-entry" fn uninhabited_variant(v: &UninhabitedVariant) -> UninhabitedVariant { + *v +} + +// The single guaranteed-padding byte is cleared with a `bic` mask over the whole loaded word. +// CHECK-LABEL: variants_same_size_array: +// CHECK: mov r7, sp +// CHECK-NEXT: ldr r0, [r0] +// CHECK-NEXT: bic r0, r0, #65280 +#[no_mangle] +#[expect(improper_ctypes_definitions)] +extern "cmse-nonsecure-entry" fn variants_same_size_array( + v: &[VariantsSameSize; 1], +) -> [VariantsSameSize; 1] { + *v +} + +// CHECK-LABEL: variants_different_size_array: +// CHECK: mov r7, sp +// CHECK-NEXT: ldrh r1, [r0, #2] +// CHECK-NEXT: ldrb r0, [r0] +// CHECK-NEXT: lsls r2, r0, #31 +// CHECK-NEXT: it eq +// CHECK-NEXT: uxtbeq r1, r1 +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +#[expect(improper_ctypes_definitions)] +extern "cmse-nonsecure-entry" fn variants_different_size_array( + v: &[VariantsDifferentSize; 1], +) -> [VariantsDifferentSize; 1] { + *v +} + +// CHECK-LABEL: variants_same_size_tuple: +// CHECK: mov r7, sp +// CHECK-NEXT: ldrh r1, [r0, #2] +// CHECK-NEXT: ldrb r0, [r0] +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +#[expect(improper_ctypes_definitions)] +extern "cmse-nonsecure-entry" fn variants_same_size_tuple( + v: &(VariantsSameSize,), +) -> (VariantsSameSize,) { + *v +} + +// CHECK-LABEL: variants_different_size_tuple: +// CHECK: mov r7, sp +// CHECK-NEXT: ldrh r1, [r0, #2] +// CHECK-NEXT: ldrb r0, [r0] +// CHECK-NEXT: lsls r2, r0, #31 +// CHECK-NEXT: it eq +// CHECK-NEXT: uxtbeq r1, r1 +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +#[expect(improper_ctypes_definitions)] +extern "cmse-nonsecure-entry" fn variants_different_size_tuple( + v: &(VariantsDifferentSize,), +) -> (VariantsDifferentSize,) { + *v +} + +/// Three variants of different sizes. +#[repr(C)] +enum ThreeVariants { + A(u8), + B(u16), + C(u32), +} + +// CHECK-LABEL: cmse_call_three_variants: +// CHECK: mov r3, r0 +// +// Clears padding in the extend the discriminant. +// CHECK-NEXT: uxtb r0, r1 +// +// Uses uxtb for the A variant, uxtheq for the B variant, +// and nothing for the C variant. +// +// CHECK-NEXT: cbz r0, .LBB{{[0-9_]+}} +// CHECK-NEXT: cmp r0, #1 +// CHECK-NEXT: it eq +// CHECK-NEXT: uxtheq r2, r2 +// CHECK-NEXT: b .LBB{{[0-9_]+}} +// CHECK-NEXT: .LBB{{[0-9_]+}}: +// CHECK-NEXT: uxtb r2, r2 +#[no_mangle] +#[expect(improper_ctypes_definitions)] +extern "C" fn cmse_call_three_variants( + f: unsafe extern "cmse-nonsecure-call" fn(ThreeVariants), + x: ThreeVariants, +) { + unsafe { f(x) } +} + +/// Three variants of different sizes, with a nested enum. +#[repr(C)] +enum ThreeVariantsNested { + A(u8), + B(Option), + C(u32), +} + +// CHECK-LABEL: cmse_call_three_variants_nested: +// CHECK: mov r3, r0 +// +// Clears padding in the extend the discriminant. +// CHECK-NEXT: uxtb r0, r1 +// +// Match on the discriminant, jump to A block +// +// CHECK-NEXT: cbz r0, .LBB{{[0-9_]+}} +// +// Match on the discriminant, do nothing for C. +// +// CHECK-NEXT: cmp r0, #1 +// CHECK-NEXT: bne .LBB{{[0-9_]+}} +// +// The B variant conditionally clears the Option payload. +// +// CHECK-NEXT: lsls r1, r2, #31 +// CHECK-NEXT: movw r1, #65535 +// CHECK-NEXT: it eq +// CHECK-NEXT: moveq r1, #254 +// CHECK-NEXT: ands r2, r1 +// CHECK-NEXT: b .LBB{{[0-9_]+}} +// +// The A variant uses uxtb. +// +// CHECK-NEXT: .LBB{{[0-9_]+}}: +// CHECK-NEXT: uxtb r2, r2 +#[no_mangle] +#[expect(improper_ctypes_definitions)] +extern "C" fn cmse_call_three_variants_nested( + f: unsafe extern "cmse-nonsecure-call" fn(ThreeVariantsNested), + x: ThreeVariantsNested, +) { + unsafe { f(x) } +} + +/// The tag is stored in the niche of the `bool`. +#[repr(C)] +struct BoolU32 { + flag: bool, + val: u32, +} + +// Bit 0b0010 is used to encode the tag, so `r0 - 2 == 0` checks the tag value. +// Zero-extension clears padding in the tag 32-bit word, the `val` is either 0 +// (stored in r1) when None or the actual value r2 when Some. +// +// CHECK-LABEL: cmse_call_niche: +// CHECK: mov r7, sp +// CHECK-NEXT: mov r3, r0 +// CHECK-NEXT: uxtb r0, r1 +// CHECK-NEXT: subs r1, r0, #2 +// CHECK-NEXT: it ne +// CHECK-NEXT: movne r1, r2 +#[no_mangle] +#[expect(improper_ctypes_definitions)] +extern "C" fn cmse_call_niche( + f: unsafe extern "cmse-nonsecure-call" fn(Option), + x: Option, +) { + unsafe { f(x) } } diff --git a/tests/mir-opt/early_otherwise_branch.dont_hoist_deref.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch.dont_hoist_deref.EarlyOtherwiseBranch.diff new file mode 100644 index 0000000000000..66891f818a6cf --- /dev/null +++ b/tests/mir-opt/early_otherwise_branch.dont_hoist_deref.EarlyOtherwiseBranch.diff @@ -0,0 +1,38 @@ +- // MIR for `dont_hoist_deref` before EarlyOtherwiseBranch ++ // MIR for `dont_hoist_deref` after EarlyOtherwiseBranch + + fn dont_hoist_deref(_1: u64, _2: *const u64) -> u64 { + let mut _0: u64; + + bb0: { + switchInt(copy _1) -> [1: bb1, 2: bb2, otherwise: bb5]; + } + + bb1: { + switchInt(copy (*_2)) -> [1: bb3, otherwise: bb5]; + } + + bb2: { + switchInt(copy (*_2)) -> [2: bb4, otherwise: bb5]; + } + + bb3: { + _0 = const 100_u64; + goto -> bb6; + } + + bb4: { + _0 = const 200_u64; + goto -> bb6; + } + + bb5: { + _0 = const 999_u64; + goto -> bb6; + } + + bb6: { + return; + } + } + diff --git a/tests/mir-opt/early_otherwise_branch.rs b/tests/mir-opt/early_otherwise_branch.rs index 19a5d25de2dfb..54406f41f8fc1 100644 --- a/tests/mir-opt/early_otherwise_branch.rs +++ b/tests/mir-opt/early_otherwise_branch.rs @@ -156,6 +156,56 @@ fn target_self(val: i32) { } } +// EMIT_MIR early_otherwise_branch.dont_hoist_deref.EarlyOtherwiseBranch.diff +#[custom_mir(dialect = "runtime")] +fn dont_hoist_deref(q: u64, p: *const u64) -> u64 { + // The dereference of `p` cannot be hoisted because the `otherwise` branch + // can be taken without dereferencing `p`. + // Hoisting the dereference could therefore cause UB when `p` is null. + // Hoisting a dereference also requires proving that the dereference is safe to reorder. + // CHECK-LABEL: fn dont_hoist_deref( + // CHECK: bb0: { + // CHECK-NEXT: switchInt(copy _1) + // CHECK: switchInt(copy (*_2)) + // CHECK: switchInt(copy (*_2)) + mir! { + { + match q { + 1 => bb1, + 2 => bb2, + _ => bb5, + } + } + bb1 = { + match *p { + 1 => bb3, + _ => bb5, + } + } + bb2 = { + match *p { + 2 => bb4, + _ => bb5, + } + } + bb3 = { + RET = 100; + Goto(bb6) + } + bb4 = { + RET = 200; + Goto(bb6) + } + bb5 = { + RET = 999; + Goto(bb6) + } + bb6 = { + Return() + } + } +} + fn main() { opt1(None, Some(0)); opt2(None, Some(0)); @@ -164,4 +214,5 @@ fn main() { opt5(0, 0); opt5_failed(0, 0); target_self(1); + dont_hoist_deref(3, std::ptr::null()); } diff --git a/tests/run-make/rmeta-unrelated-search-path-files/client.rs b/tests/run-make/rmeta-unrelated-search-path-files/client.rs new file mode 100644 index 0000000000000..a0fb04732fef1 --- /dev/null +++ b/tests/run-make/rmeta-unrelated-search-path-files/client.rs @@ -0,0 +1,5 @@ +//! [crate::Client] + +extern crate foo; + +pub struct Client; diff --git a/tests/run-make/rmeta-unrelated-search-path-files/foo.rs b/tests/run-make/rmeta-unrelated-search-path-files/foo.rs new file mode 100644 index 0000000000000..4a835673a596b --- /dev/null +++ b/tests/run-make/rmeta-unrelated-search-path-files/foo.rs @@ -0,0 +1 @@ +pub struct Foo; diff --git a/tests/run-make/rmeta-unrelated-search-path-files/foo_bar.rs b/tests/run-make/rmeta-unrelated-search-path-files/foo_bar.rs new file mode 100644 index 0000000000000..1c6a3b4c6cce8 --- /dev/null +++ b/tests/run-make/rmeta-unrelated-search-path-files/foo_bar.rs @@ -0,0 +1 @@ +pub struct FooBar; diff --git a/tests/run-make/rmeta-unrelated-search-path-files/rmake.rs b/tests/run-make/rmeta-unrelated-search-path-files/rmake.rs new file mode 100644 index 0000000000000..3bd402c5c6b79 --- /dev/null +++ b/tests/run-make/rmeta-unrelated-search-path-files/rmake.rs @@ -0,0 +1,30 @@ +//@ needs-target-std +// +// Regression test for . +// +// Ensures two builds of `client.rs` produce identical metadata +// even if there is an unrelated crate on the search path. + +use run_make_support::{rfs, rustc}; + +fn main() { + rustc().input("foo.rs").crate_type("rlib").run(); + rustc() + .input("client.rs") + .crate_type("rlib") + .emit("metadata") + .library_search_path(".") + .output("client1.rmeta") + .run(); + + rustc().input("foo_bar.rs").crate_type("rlib").run(); + rustc() + .input("client.rs") + .crate_type("rlib") + .emit("metadata") + .library_search_path(".") + .output("client2.rmeta") + .run(); + + assert_eq!(rfs::read("client1.rmeta"), rfs::read("client2.rmeta")); +} diff --git a/tests/ui/any/any_static.next.stderr b/tests/ui/any/any_static.next.stderr new file mode 100644 index 0000000000000..e57d544140980 --- /dev/null +++ b/tests/ui/any/any_static.next.stderr @@ -0,0 +1,16 @@ +error[E0521]: borrowed data escapes outside of function + --> $DIR/any_static.rs:18:35 + | +LL | fn extend(a: &Payload) -> &'static Payload { + | - - let's call the lifetime of this reference `'1` + | | + | `a` is a reference that is only valid in the function body +LL | let b: &(dyn Any + 'static) = try_as_dyn::<&Payload, dyn Any + 'static>(&a).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `a` escapes the function body here + | argument requires that `'1` must outlive `'static` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/any/any_static.old.stderr b/tests/ui/any/any_static.old.stderr new file mode 100644 index 0000000000000..e57d544140980 --- /dev/null +++ b/tests/ui/any/any_static.old.stderr @@ -0,0 +1,16 @@ +error[E0521]: borrowed data escapes outside of function + --> $DIR/any_static.rs:18:35 + | +LL | fn extend(a: &Payload) -> &'static Payload { + | - - let's call the lifetime of this reference `'1` + | | + | `a` is a reference that is only valid in the function body +LL | let b: &(dyn Any + 'static) = try_as_dyn::<&Payload, dyn Any + 'static>(&a).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `a` escapes the function body here + | argument requires that `'1` must outlive `'static` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/any/any_static.rs b/tests/ui/any/any_static.rs new file mode 100644 index 0000000000000..7305cabfa8c16 --- /dev/null +++ b/tests/ui/any/any_static.rs @@ -0,0 +1,22 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +#![feature(try_as_dyn)] + +use std::any::{Any, try_as_dyn}; + +type Payload = Box; + +fn main() { + let storage: Box = Box::new(Box::new(1i32)); + let wrong: &'static Payload = extend(&*storage); + drop(storage); + println!("{wrong}"); +} + +fn extend(a: &Payload) -> &'static Payload { + let b: &(dyn Any + 'static) = try_as_dyn::<&Payload, dyn Any + 'static>(&a).unwrap(); + //~^ ERROR: borrowed data escapes outside of function + let c: &&'static Payload = b.downcast_ref::<&'static Payload>().unwrap(); + *c +} diff --git a/tests/ui/any/builtin_bound.rs b/tests/ui/any/builtin_bound.rs new file mode 100644 index 0000000000000..8c4bcf7ad2e3f --- /dev/null +++ b/tests/ui/any/builtin_bound.rs @@ -0,0 +1,23 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +#![feature(try_as_dyn)] + +trait Trait {} + +// In contrast to `T: Sized`, `Struct: Sized` does not go +// through a fast path, and is thus rejected by the builtin impl +// check that rejects all builtin impls in reflection mode. +// FIXME(try_as_dyn): should probably allow builtin impls that +// are never lifetime dependent (like Sized). +impl Trait for Struct where Struct: Sized {} + +struct Struct(T); + +const _: () = { + let x = Struct(42); + assert!(std::any::try_as_dyn::<_, dyn Trait>(&x).is_none()); +}; + +fn main() {} diff --git a/tests/ui/any/hrtb.next.stderr b/tests/ui/any/hrtb.next.stderr new file mode 100644 index 0000000000000..385b3c3cf2212 --- /dev/null +++ b/tests/ui/any/hrtb.next.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: called `Option::unwrap()` on a `None` value + --> $DIR/hrtb.rs:15:25 + | +LL | let _dy: &dyn Bar = try_as_dyn(&x).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/any/hrtb.old.stderr b/tests/ui/any/hrtb.old.stderr new file mode 100644 index 0000000000000..385b3c3cf2212 --- /dev/null +++ b/tests/ui/any/hrtb.old.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: called `Option::unwrap()` on a `None` value + --> $DIR/hrtb.rs:15:25 + | +LL | let _dy: &dyn Bar = try_as_dyn(&x).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/any/hrtb.rs b/tests/ui/any/hrtb.rs new file mode 100644 index 0000000000000..ab31529a0d207 --- /dev/null +++ b/tests/ui/any/hrtb.rs @@ -0,0 +1,19 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +#![feature(try_as_dyn)] + +use std::any::try_as_dyn; + +trait Foo<'a, 'b> {} + +trait Bar {} +impl Foo<'a, 'b> + ?Sized> Bar for Option<*const T> {} + +const _: () = { + let x: Option<*const dyn for<'a> Foo<'a, 'a>> = None; + let _dy: &dyn Bar = try_as_dyn(&x).unwrap(); + //~^ ERROR: `Option::unwrap()` on a `None` value +}; + +fn main() {} diff --git a/tests/ui/any/hrtb2.next.stderr b/tests/ui/any/hrtb2.next.stderr new file mode 100644 index 0000000000000..dfe000dc503a8 --- /dev/null +++ b/tests/ui/any/hrtb2.next.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: called `Option::unwrap()` on a `None` value + --> $DIR/hrtb2.rs:15:25 + | +LL | let _dy: &dyn Bar = try_as_dyn(&x).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/any/hrtb2.old.stderr b/tests/ui/any/hrtb2.old.stderr new file mode 100644 index 0000000000000..dfe000dc503a8 --- /dev/null +++ b/tests/ui/any/hrtb2.old.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: called `Option::unwrap()` on a `None` value + --> $DIR/hrtb2.rs:15:25 + | +LL | let _dy: &dyn Bar = try_as_dyn(&x).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/any/hrtb2.rs b/tests/ui/any/hrtb2.rs new file mode 100644 index 0000000000000..ee1bd7cde8982 --- /dev/null +++ b/tests/ui/any/hrtb2.rs @@ -0,0 +1,19 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +#![feature(try_as_dyn)] + +use std::any::try_as_dyn; + +trait Foo<'a> {} + +trait Bar {} +impl Bar for Option<*const T> where T: for<'a> Foo<'a> {} + +const _: () = { + let x: Option<*const dyn Foo<'_>> = None; + let _dy: &dyn Bar = try_as_dyn(&x).unwrap(); + //~^ ERROR: `Option::unwrap()` on a `None` value +}; + +fn main() {} diff --git a/tests/ui/any/non_static.rs b/tests/ui/any/non_static.rs new file mode 100644 index 0000000000000..40d7bb7bf5469 --- /dev/null +++ b/tests/ui/any/non_static.rs @@ -0,0 +1,89 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@check-pass +#![feature(try_as_dyn)] + +trait Trait {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait>(&&42_i32).is_none()); +}; + +impl<'a> Trait for &'a [(); 1] {} +const _: () = { + let x = (); + assert!(std::any::try_as_dyn::<_, dyn Trait>(&&[x]).is_some()); +}; + +type Foo = &'static [(); 2]; + +// Ensure type aliases don't skip these checks +impl Trait for Foo {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait>(&&[(), ()]).is_none()); +}; + +impl Trait for &() {} +const _: () = { + let x = (); + assert!(std::any::try_as_dyn::<_, dyn Trait>(&&x).is_some()); +}; + +impl Trait for () {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait>(&()).is_some()); +}; + +// Not fully generic impl -> returns None even tho +// implemented for *some* lifetimes +impl<'a> Trait for (&'a (), &'a ()) {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait>(&(&(), &())).is_none()); +}; + +// Not fully generic impl -> returns None even tho +// implemented for *some* lifetimes +impl<'a, 'b: 'a> Trait for (&'a (), &'b (), ()) {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait>(&(&(), &(), ())).is_none()); +}; + +// Only valid for 'static lifetimes -> returns None +// even though we are actually using a `'static` lifetime. +// We can't know what lifetimes are there during codegen, so +// we pessimistically assume it could be a shorter one +impl Trait for &'static u32 {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait>(&&42_u32).is_none()); +}; + +trait Trait2 {} + +struct Struct(T); + +// While this is the impl for `Trait`, in `Reflection` solver mode +// we reject the impl for `Trait2` below, and thus this impl also +// doesn't match. +impl Trait for Struct {} + +impl Trait2 for &'static u32 {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait>(&Struct(&42_u32)).is_none()); +}; + +const _: () = { + trait Homo {} + impl Homo for (T, T) {} + + // Let's pick `T = &'_ i32`. + assert!(std::any::try_as_dyn::<_, dyn Homo>(&(&42_i32, &27_i32)).is_none()); +}; + +trait Trait3<'a> {} + +impl Trait3<'static> for () {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait3<'_>>(&()).is_none()); +}; + +fn main() {} diff --git a/tests/ui/any/reject_manual_impl.rs b/tests/ui/any/reject_manual_impl.rs new file mode 100644 index 0000000000000..590cebd48950f --- /dev/null +++ b/tests/ui/any/reject_manual_impl.rs @@ -0,0 +1,29 @@ +#![feature(try_as_dyn)] + +use std::any::TryAsDynCompatible; + +struct Foo(dyn Iterator); + +impl TryAsDynCompatible<'static> for Foo {} +//~^ ERROR: explicit impls for the `TryAsDynCompatible` trait are not permitted + +struct Bar(dyn Iterator); + +impl<'a> TryAsDynCompatible<'a> for Bar {} +//~^ ERROR: explicit impls for the `TryAsDynCompatible` trait are not permitted + +struct Baz; + +impl<'a> TryAsDynCompatible<'a> for Baz {} +//~^ ERROR: explicit impls for the `TryAsDynCompatible` trait are not permitted + +trait Trait {} + +impl<'a> TryAsDynCompatible<'a> for dyn Trait {} +//~^ ERROR: explicit impls for the `TryAsDynCompatible` trait are not permitted + +impl TryAsDynCompatible<'static> for dyn Iterator {} +//~^ ERROR: explicit impls for the `TryAsDynCompatible` trait are not permitted +//~| ERROR: only traits defined in the current crate can be implemented for arbitrary types + +fn main() {} diff --git a/tests/ui/any/reject_manual_impl.stderr b/tests/ui/any/reject_manual_impl.stderr new file mode 100644 index 0000000000000..37be37c47ea79 --- /dev/null +++ b/tests/ui/any/reject_manual_impl.stderr @@ -0,0 +1,46 @@ +error[E0322]: explicit impls for the `TryAsDynCompatible` trait are not permitted + --> $DIR/reject_manual_impl.rs:7:1 + | +LL | impl TryAsDynCompatible<'static> for Foo {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `TryAsDynCompatible` not allowed + +error[E0322]: explicit impls for the `TryAsDynCompatible` trait are not permitted + --> $DIR/reject_manual_impl.rs:12:1 + | +LL | impl<'a> TryAsDynCompatible<'a> for Bar {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `TryAsDynCompatible` not allowed + +error[E0322]: explicit impls for the `TryAsDynCompatible` trait are not permitted + --> $DIR/reject_manual_impl.rs:17:1 + | +LL | impl<'a> TryAsDynCompatible<'a> for Baz {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `TryAsDynCompatible` not allowed + +error[E0322]: explicit impls for the `TryAsDynCompatible` trait are not permitted + --> $DIR/reject_manual_impl.rs:22:1 + | +LL | impl<'a> TryAsDynCompatible<'a> for dyn Trait {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `TryAsDynCompatible` not allowed + +error[E0322]: explicit impls for the `TryAsDynCompatible` trait are not permitted + --> $DIR/reject_manual_impl.rs:25:1 + | +LL | impl TryAsDynCompatible<'static> for dyn Iterator {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `TryAsDynCompatible` not allowed + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/reject_manual_impl.rs:25:1 + | +LL | impl TryAsDynCompatible<'static> for dyn Iterator {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------------------ + | | + | `dyn Iterator` is not defined in the current crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0117, E0322. +For more information about an error, try `rustc --explain E0117`. diff --git a/tests/ui/any/static_method_bound.rs b/tests/ui/any/static_method_bound.rs new file mode 100644 index 0000000000000..b2cc77e4d9cfe --- /dev/null +++ b/tests/ui/any/static_method_bound.rs @@ -0,0 +1,33 @@ +#![feature(try_as_dyn)] + +use std::any::try_as_dyn; + +type Payload = Box; + +trait Trait { + fn as_static(&self) -> &'static Payload + where + Self: 'static; +} + +impl<'a> Trait for &'a Payload { + fn as_static(&self) -> &'static Payload + where + Self: 'static, + { + *self + } +} + +fn main() { + let storage: Box = Box::new(Box::new(1i32)); + let wrong: &'static Payload = extend(&*storage); + drop(storage); + println!("{wrong}"); +} + +fn extend(a: &Payload) -> &'static Payload { + let b: &(dyn Trait + 'static) = try_as_dyn::<&Payload, dyn Trait + 'static>(&a).unwrap(); + //~^ ERROR: borrowed data escapes outside of function + b.as_static() +} diff --git a/tests/ui/any/static_method_bound.stderr b/tests/ui/any/static_method_bound.stderr new file mode 100644 index 0000000000000..152672d024ab1 --- /dev/null +++ b/tests/ui/any/static_method_bound.stderr @@ -0,0 +1,16 @@ +error[E0521]: borrowed data escapes outside of function + --> $DIR/static_method_bound.rs:30:37 + | +LL | fn extend(a: &Payload) -> &'static Payload { + | - - let's call the lifetime of this reference `'1` + | | + | `a` is a reference that is only valid in the function body +LL | let b: &(dyn Trait + 'static) = try_as_dyn::<&Payload, dyn Trait + 'static>(&a).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `a` escapes the function body here + | argument requires that `'1` must outlive `'static` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/any/trait_info_of.next.stderr b/tests/ui/any/trait_info_of.next.stderr new file mode 100644 index 0000000000000..45bbbe4892937 --- /dev/null +++ b/tests/ui/any/trait_info_of.next.stderr @@ -0,0 +1,14 @@ +error[E0277]: the trait bound `dyn Trait>: TryAsDynCompatible<'_>` is not satisfied + --> $DIR/trait_info_of.rs:30:30 + | +LL | .trait_info_of::>() + | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `TryAsDynCompatible<'_>` is not implemented for `dyn Trait>` + | | + | required by a bound introduced by this call + | +note: required by a bound in `TypeId::trait_info_of` + --> $SRC_DIR/core/src/any.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/any/trait_info_of.old.stderr b/tests/ui/any/trait_info_of.old.stderr new file mode 100644 index 0000000000000..45bbbe4892937 --- /dev/null +++ b/tests/ui/any/trait_info_of.old.stderr @@ -0,0 +1,14 @@ +error[E0277]: the trait bound `dyn Trait>: TryAsDynCompatible<'_>` is not satisfied + --> $DIR/trait_info_of.rs:30:30 + | +LL | .trait_info_of::>() + | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `TryAsDynCompatible<'_>` is not implemented for `dyn Trait>` + | | + | required by a bound introduced by this call + | +note: required by a bound in `TypeId::trait_info_of` + --> $SRC_DIR/core/src/any.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/any/trait_info_of.rs b/tests/ui/any/trait_info_of.rs new file mode 100644 index 0000000000000..5eb6742be506e --- /dev/null +++ b/tests/ui/any/trait_info_of.rs @@ -0,0 +1,45 @@ +//! Check that we can't use `TypeId::trait_info_of` to unsoundly skip +//! the try_as_dyn checks. + +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + +#![feature(type_info, ptr_metadata, arbitrary_self_types_pointers)] + +use std::any::TypeId; +use std::ptr::{self, DynMetadata}; + +type Payload = Box; + +trait Trait { + type Assoc; + fn method(self: *const Self, value: Self::Assoc) -> &'static Payload; +} +struct Thing; +impl Trait for Thing { + type Assoc = &'static Payload; + fn method(self: *const Self, value: Self::Assoc) -> &'static Payload { + value + } +} + +fn extend<'a>(payload: &'a Payload) -> &'static Payload { + let metadata: DynMetadata> = const { + TypeId::of::() + .trait_info_of::>() + //~^ ERROR `dyn Trait>: TryAsDynCompatible<'_>` is not satisfied + .unwrap() + .get_vtable() + }; + let ptr: *const dyn Trait = + ptr::from_raw_parts(std::ptr::null::<()>(), metadata); + ptr.method(payload) +} + +fn main() { + let payload: Box = Box::new(Box::new(1i32)); + let wrong: &'static Payload = extend(&*payload); + drop(payload); + println!("{wrong}"); +} diff --git a/tests/ui/any/try_as_dyn.rs b/tests/ui/any/try_as_dyn.rs index ee220f797ced9..cf9ca7bd885da 100644 --- a/tests/ui/any/try_as_dyn.rs +++ b/tests/ui/any/try_as_dyn.rs @@ -1,3 +1,6 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) //@ run-pass #![feature(try_as_dyn)] @@ -7,7 +10,7 @@ use std::fmt::Debug; fn debug_format_with_try_as_dyn(t: &T) -> String { match std::any::try_as_dyn::<_, dyn Debug>(t) { Some(d) => format!("{d:?}"), - None => "default".to_string() + None => "default".to_string(), } } @@ -16,7 +19,7 @@ fn main() { #[allow(dead_code)] #[derive(Debug)] struct A { - index: usize + index: usize, } let a = A { index: 42 }; let result = debug_format_with_try_as_dyn(&a); diff --git a/tests/ui/any/try_as_dyn_assoc_ty_lifetime.rs b/tests/ui/any/try_as_dyn_assoc_ty_lifetime.rs new file mode 100644 index 0000000000000..aa2056a7fc42c --- /dev/null +++ b/tests/ui/any/try_as_dyn_assoc_ty_lifetime.rs @@ -0,0 +1,27 @@ +//@check-pass +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + +#![feature(try_as_dyn)] + +use std::any::try_as_dyn; + +trait HasAssoc<'a> { + type Assoc; +} +struct Dummy; +impl<'a> HasAssoc<'a> for Dummy { + // Changing this to &'a i64 makes try_as_dyn succeed + type Assoc = &'static i64; +} + +trait Trait {} +impl Trait for i32 where for<'a> Dummy: HasAssoc<'a, Assoc = &'a i64> {} + +const _: () = { + let x = 1i32; + assert!(try_as_dyn::<_, dyn Trait>(&x).is_none()); +}; + +fn main() {} diff --git a/tests/ui/any/try_as_dyn_builtin_impl.rs b/tests/ui/any/try_as_dyn_builtin_impl.rs new file mode 100644 index 0000000000000..4ed06a78c2e36 --- /dev/null +++ b/tests/ui/any/try_as_dyn_builtin_impl.rs @@ -0,0 +1,48 @@ +#![feature(try_as_dyn)] +//@ run-fail +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + +use std::any::{Any, try_as_dyn}; + +type Payload = Box; + +trait Outlives<'b>: 'b {} + +trait WithLt { + type Ref; +} +impl<'a> WithLt for dyn for<'b> Outlives<'b> + 'a { + type Ref = &'a Payload; +} + +struct Thing(T::Ref); + +trait Trait { + fn get(&self) -> &'static Payload; +} +impl Trait for Thing +where + T: WithLt + for<'b> Outlives<'b> + ?Sized, +{ + fn get(&self) -> &'static Payload { + let x: &::Ref = &self.0; + let y: &(dyn Any + 'static) = x; + let z: &&'static Payload = y.downcast_ref().unwrap(); + *z + } +} + +fn extend<'a>(payload: &'a Payload) -> &'static Payload { + let thing: Thing Outlives<'b> + 'a> = Thing(payload); + let dy: &dyn Trait = try_as_dyn(&thing).unwrap(); + dy.get() +} + +fn main() { + let payload: Box = Box::new(Box::new(1)); + let wrong: &'static Payload = extend(&*payload); + drop(payload); + println!("{wrong}"); +} diff --git a/tests/ui/any/try_as_dyn_elaborated_bounds.rs b/tests/ui/any/try_as_dyn_elaborated_bounds.rs new file mode 100644 index 0000000000000..b0357e4fcca2c --- /dev/null +++ b/tests/ui/any/try_as_dyn_elaborated_bounds.rs @@ -0,0 +1,29 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@check-pass + +#![feature(try_as_dyn)] + +use std::any::try_as_dyn; + +trait Trait: 'static {} +trait Other {} +struct Foo(T); + +impl Trait for () {} +impl Trait for &'static () {} + +// This impl has an implied `T: 'static` bound, but that's +// not an issue, as we just ignore all `Trait` impls where +// that would be a relevant distinguisher. +impl Other for Foo {} + +const _: () = { + let foo = Foo(()); + assert!(try_as_dyn::, dyn Other>(&foo).is_some()); + let foo = Foo(&()); + assert!(try_as_dyn::, dyn Other>(&foo).is_none()); +}; + +fn main() {} diff --git a/tests/ui/any/try_as_dyn_generic_impl.rs b/tests/ui/any/try_as_dyn_generic_impl.rs new file mode 100644 index 0000000000000..21033312fe277 --- /dev/null +++ b/tests/ui/any/try_as_dyn_generic_impl.rs @@ -0,0 +1,40 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +#![feature(try_as_dyn)] +//@ check-pass + +use std::any::try_as_dyn; + +struct Thing(T); +trait Trait {} +impl Trait for Thing {} + +const _: () = { + let thing = Thing(1); + assert!(try_as_dyn::<_, dyn Trait>(&thing).is_some()); +}; + +struct Thing2(T); +impl Trait for Thing2 {} +struct NoDebug; + +const _: () = { + let thing = Thing2(1); + assert!(try_as_dyn::<_, dyn Trait>(&thing).is_some()); + let thing = Thing2(NoDebug); + assert!(try_as_dyn::<_, dyn Trait>(&thing).is_none()); +}; + +trait Trait2 {} +impl<'a, 'b> Trait2 for &'a &'b () {} + +struct Thing3(T); +impl Trait for Thing3 {} + +const _: () = { + let thing = Thing3(&&()); + assert!(try_as_dyn::<_, dyn Trait2>(&thing).is_none()); +}; + +fn main() {} diff --git a/tests/ui/any/try_as_dyn_generic_trait.next.stderr b/tests/ui/any/try_as_dyn_generic_trait.next.stderr new file mode 100644 index 0000000000000..99382f9a4032c --- /dev/null +++ b/tests/ui/any/try_as_dyn_generic_trait.next.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: called `Option::unwrap()` on a `None` value + --> $DIR/try_as_dyn_generic_trait.rs:22:51 + | +LL | let convert: &dyn Convert<&'static Payload> = try_as_dyn(&payload).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/any/try_as_dyn_generic_trait.old.stderr b/tests/ui/any/try_as_dyn_generic_trait.old.stderr new file mode 100644 index 0000000000000..99382f9a4032c --- /dev/null +++ b/tests/ui/any/try_as_dyn_generic_trait.old.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: called `Option::unwrap()` on a `None` value + --> $DIR/try_as_dyn_generic_trait.rs:22:51 + | +LL | let convert: &dyn Convert<&'static Payload> = try_as_dyn(&payload).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/any/try_as_dyn_generic_trait.rs b/tests/ui/any/try_as_dyn_generic_trait.rs new file mode 100644 index 0000000000000..630c071218fc8 --- /dev/null +++ b/tests/ui/any/try_as_dyn_generic_trait.rs @@ -0,0 +1,26 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +#![feature(try_as_dyn)] + +use std::any::try_as_dyn; + +type Payload = *const i32; + +trait Convert { + fn convert(&self) -> &T; +} + +impl Convert for T { + fn convert(&self) -> &T { + self + } +} + +const _: () = { + let payload: Payload = std::ptr::null(); + let convert: &dyn Convert<&'static Payload> = try_as_dyn(&payload).unwrap(); + //~^ ERROR: `Option::unwrap()` on a `None` value +}; + +fn main() {} diff --git a/tests/ui/any/try_as_dyn_mut.rs b/tests/ui/any/try_as_dyn_mut.rs index ff7baa32ea874..a4fadb72d2823 100644 --- a/tests/ui/any/try_as_dyn_mut.rs +++ b/tests/ui/any/try_as_dyn_mut.rs @@ -1,3 +1,6 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) //@ run-pass #![feature(try_as_dyn)] @@ -7,7 +10,7 @@ use std::fmt::{Error, Write}; fn try_as_dyn_mut_write(t: &mut T, s: &str) -> Result<(), Error> { match std::any::try_as_dyn_mut::<_, dyn Write>(t) { Some(w) => w.write_str(s), - None => Ok(()) + None => Ok(()), } } diff --git a/tests/ui/any/try_as_dyn_soundness_test1.rs b/tests/ui/any/try_as_dyn_soundness_test1.rs index 0772e9a2d77ee..4dfb5492e3a59 100644 --- a/tests/ui/any/try_as_dyn_soundness_test1.rs +++ b/tests/ui/any/try_as_dyn_soundness_test1.rs @@ -1,19 +1,16 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) //@ run-pass #![feature(try_as_dyn)] use std::any::try_as_dyn; -trait Trait { +trait Trait {} -} - -impl Trait for for<'a> fn(&'a Box) { - -} - -fn store(_: &'static Box) { +impl Trait for for<'a> fn(&'a Box) {} -} +fn store(_: &'static Box) {} fn main() { let fn_ptr: fn(&'static Box) = store; diff --git a/tests/ui/any/try_as_dyn_soundness_test2.rs b/tests/ui/any/try_as_dyn_soundness_test2.rs index c16b50d0261ed..a9985384cd1e6 100644 --- a/tests/ui/any/try_as_dyn_soundness_test2.rs +++ b/tests/ui/any/try_as_dyn_soundness_test2.rs @@ -1,14 +1,13 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) //@ run-pass #![feature(try_as_dyn)] use std::any::try_as_dyn; -trait Trait { +trait Trait {} -} - -impl Trait fn(&'a Box)> for () { - -} +impl Trait fn(&'a Box)> for () {} fn main() { let dt = try_as_dyn::<_, dyn Trait)>>(&()); diff --git a/tests/ui/asm/asm-const-ptr-mir-inline.rs b/tests/ui/asm/asm-const-ptr-mir-inline.rs new file mode 100644 index 0000000000000..428825ac34412 --- /dev/null +++ b/tests/ui/asm/asm-const-ptr-mir-inline.rs @@ -0,0 +1,17 @@ +//@ build-pass +//@ needs-asm-support + +#![feature(asm_const_ptr)] + +// Force inline to exercise the codegen when the same asm const ptr is code-generated multiple +// times. +#[inline(always)] +fn foo() { + unsafe{core::arch::asm!("/* {} */", const &N)}; +} + +fn main(){ + foo::<0>(); + foo::<0>(); + foo::<1>(); +} diff --git a/tests/ui/asm/const-refs-to-static.rs b/tests/ui/asm/const-refs-to-static.rs index ce2c5b3246ec8..8058d70550aba 100644 --- a/tests/ui/asm/const-refs-to-static.rs +++ b/tests/ui/asm/const-refs-to-static.rs @@ -1,19 +1,20 @@ //@ needs-asm-support //@ ignore-nvptx64 //@ ignore-spirv +//@ build-pass + +#![feature(asm_const_ptr)] use std::arch::{asm, global_asm}; use std::ptr::addr_of; static FOO: u8 = 42; -global_asm!("{}", const addr_of!(FOO)); -//~^ ERROR invalid type for `const` operand +global_asm!("/* {} */", const addr_of!(FOO)); #[no_mangle] fn inline() { - unsafe { asm!("{}", const addr_of!(FOO)) }; - //~^ ERROR invalid type for `const` operand + unsafe { asm!("/* {} */", const addr_of!(FOO)) }; } fn main() {} diff --git a/tests/ui/asm/const-refs-to-static.stderr b/tests/ui/asm/const-refs-to-static.stderr deleted file mode 100644 index 10e1ca5bd6068..0000000000000 --- a/tests/ui/asm/const-refs-to-static.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: invalid type for `const` operand - --> $DIR/const-refs-to-static.rs:10:19 - | -LL | global_asm!("{}", const addr_of!(FOO)); - | ^^^^^^------------- - | | - | is a `*const u8` - | - = help: `const` operands must be of an integer type - -error: invalid type for `const` operand - --> $DIR/const-refs-to-static.rs:15:25 - | -LL | unsafe { asm!("{}", const addr_of!(FOO)) }; - | ^^^^^^------------- - | | - | is a `*const u8` - | - = help: `const` operands must be of an integer type - -error: aborting due to 2 previous errors - diff --git a/tests/ui/asm/invalid-const-operand.rs b/tests/ui/asm/invalid-const-operand.rs index 5c7b1a6b9654f..083d4f4e18480 100644 --- a/tests/ui/asm/invalid-const-operand.rs +++ b/tests/ui/asm/invalid-const-operand.rs @@ -1,8 +1,11 @@ +//@ edition:2021 //@ needs-asm-support //@ ignore-nvptx64 //@ ignore-spirv //@ reference: asm.operand-type.supported-operands.const +#![feature(asm_const_ptr)] + use std::arch::{asm, global_asm}; // Const operands must be integers and must be constants. @@ -13,11 +16,10 @@ global_asm!("{}", const 0i128); global_asm!("{}", const 0f32); //~^ ERROR invalid type for `const` operand global_asm!("{}", const 0 as *mut u8); -//~^ ERROR invalid type for `const` operand fn test1() { unsafe { - // Const operands must be integers and must be constants. + // Const operands must be integers or thin pointers asm!("{}", const 0); asm!("{}", const 0i32); @@ -25,9 +27,18 @@ fn test1() { asm!("{}", const 0f32); //~^ ERROR invalid type for `const` operand asm!("{}", const 0 as *mut u8); - //~^ ERROR invalid type for `const` operand asm!("{}", const &0); + asm!("{}", const b"Foo".as_slice()); //~^ ERROR invalid type for `const` operand + asm!("{}", const "Foo"); + //~^ ERROR invalid type for `const` operand + asm!("{}", const c"Foo"); + //~^ ERROR invalid type for `const` operand + + asm!("{}", const test1 as fn()); + asm!("{}", const test1); + asm!("{}", const (|| {}) as fn()); + asm!("{}", const || {}); } } diff --git a/tests/ui/asm/invalid-const-operand.stderr b/tests/ui/asm/invalid-const-operand.stderr index 3a3129ff3f6be..c4fa69a095e53 100644 --- a/tests/ui/asm/invalid-const-operand.stderr +++ b/tests/ui/asm/invalid-const-operand.stderr @@ -1,5 +1,5 @@ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/invalid-const-operand.rs:45:26 + --> $DIR/invalid-const-operand.rs:56:26 | LL | asm!("{}", const x); | ^ non-constant value @@ -11,7 +11,7 @@ LL + const x: /* Type */ = 0; | error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/invalid-const-operand.rs:48:36 + --> $DIR/invalid-const-operand.rs:59:36 | LL | asm!("{}", const const_foo(x)); | ^ non-constant value @@ -23,7 +23,7 @@ LL + const x: /* Type */ = 0; | error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/invalid-const-operand.rs:51:36 + --> $DIR/invalid-const-operand.rs:62:36 | LL | asm!("{}", const const_bar(x)); | ^ non-constant value @@ -35,54 +35,54 @@ LL + const x: /* Type */ = 0; | error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:13:19 + --> $DIR/invalid-const-operand.rs:16:19 | LL | global_asm!("{}", const 0f32); | ^^^^^^---- | | | is an `f32` | - = help: `const` operands must be of an integer type + = help: `const` operands must be of an integer or thin pointer type error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:15:19 + --> $DIR/invalid-const-operand.rs:27:20 | -LL | global_asm!("{}", const 0 as *mut u8); - | ^^^^^^------------ - | | - | is a `*mut u8` +LL | asm!("{}", const 0f32); + | ^^^^^^---- + | | + | is an `f32` | - = help: `const` operands must be of an integer type + = help: `const` operands must be of an integer or thin pointer type error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:25:20 + --> $DIR/invalid-const-operand.rs:31:20 | -LL | asm!("{}", const 0f32); - | ^^^^^^---- +LL | asm!("{}", const b"Foo".as_slice()); + | ^^^^^^----------------- | | - | is an `f32` + | is a `&[u8]` | - = help: `const` operands must be of an integer type + = help: `const` operands must be of an integer or thin pointer type error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:27:20 + --> $DIR/invalid-const-operand.rs:33:20 | -LL | asm!("{}", const 0 as *mut u8); - | ^^^^^^------------ +LL | asm!("{}", const "Foo"); + | ^^^^^^----- | | - | is a `*mut u8` + | is a `&str` | - = help: `const` operands must be of an integer type + = help: `const` operands must be of an integer or thin pointer type error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:29:20 + --> $DIR/invalid-const-operand.rs:35:20 | -LL | asm!("{}", const &0); - | ^^^^^^-- +LL | asm!("{}", const c"Foo"); + | ^^^^^^------ | | - | is a `&i32` + | is a `&CStr` | - = help: `const` operands must be of an integer type + = help: `const` operands must be of an integer or thin pointer type error: aborting due to 8 previous errors diff --git a/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.rs b/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.rs new file mode 100644 index 0000000000000..74f0618e4291f --- /dev/null +++ b/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.rs @@ -0,0 +1,17 @@ +//@ compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +trait Trait {} + +trait Proj<'a> { + type Assoc; +} + +fn foo<'a, T>() +where + T: Proj<'a, Assoc = fn(::Assoc)>, + (): Trait<>::Assoc>, + //~^ ERROR the trait bound `(): Trait fn(>::Assoc))>` is not satisfied +{ +} + +fn main() {} diff --git a/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr b/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr new file mode 100644 index 0000000000000..5e8e131addd28 --- /dev/null +++ b/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr @@ -0,0 +1,14 @@ +error[E0277]: the trait bound `(): Trait fn(>::Assoc))>` is not satisfied + --> $DIR/placeholder-assumptions-issue-157840.rs:12:9 + | +LL | (): Trait<>::Assoc>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait fn(>::Assoc))>` is not implemented for `()` + | +help: consider extending the `where` clause, but there might be an alternative better way to express this requirement + | +LL | (): Trait<>::Assoc>, (): Trait fn(>::Assoc))> + | +++++++++++++++++++++++++++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/issues/issue-3991.rs b/tests/ui/borrowck/push-on-nested-vec.rs similarity index 58% rename from tests/ui/issues/issue-3991.rs rename to tests/ui/borrowck/push-on-nested-vec.rs index e69c693ed49ea..bbab952e85355 100644 --- a/tests/ui/issues/issue-3991.rs +++ b/tests/ui/borrowck/push-on-nested-vec.rs @@ -1,6 +1,8 @@ +//! Regression test for . +//! Test borrowck doesn't complain on nested vector mutable reference. //@ check-pass -#![allow(dead_code)] +#![allow(dead_code)] struct HasNested { nest: Vec > , diff --git a/tests/ui/issues/issue-48728.rs b/tests/ui/coherence/clone-impl-unsized-slice.rs similarity index 60% rename from tests/ui/issues/issue-48728.rs rename to tests/ui/coherence/clone-impl-unsized-slice.rs index 8ad9289c65cf2..6a36df0166354 100644 --- a/tests/ui/issues/issue-48728.rs +++ b/tests/ui/coherence/clone-impl-unsized-slice.rs @@ -1,5 +1,5 @@ -// Regression test for #48728, an ICE that occurred computing -// coherence "help" information. +//! Regression test for . +//! ICE occurred computing coherence "help" information. //@ check-pass #[derive(Clone)] diff --git a/tests/ui/issues/issue-51655.rs b/tests/ui/consts/const_in_pattern/const-bytes-slice-pattern.rs similarity index 67% rename from tests/ui/issues/issue-51655.rs rename to tests/ui/consts/const_in_pattern/const-bytes-slice-pattern.rs index 05f71623d1419..028c7a74bd640 100644 --- a/tests/ui/issues/issue-51655.rs +++ b/tests/ui/consts/const_in_pattern/const-bytes-slice-pattern.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! This used to ICE. //@ check-pass + #![allow(dead_code)] const PATH_DOT: &[u8] = &[b'.']; diff --git a/tests/ui/consts/min_const_fn/bad_const_fn_body_ice.rs b/tests/ui/consts/min_const_fn/bad_const_fn_body_ice.rs deleted file mode 100644 index 74e0a36560b08..0000000000000 --- a/tests/ui/consts/min_const_fn/bad_const_fn_body_ice.rs +++ /dev/null @@ -1,7 +0,0 @@ -const fn foo(a: i32) -> Vec { - vec![1, 2, 3] - //~^ ERROR cannot call non-const - //~| ERROR cannot call non-const -} - -fn main() {} diff --git a/tests/ui/consts/min_const_fn/bad_const_fn_body_ice.stderr b/tests/ui/consts/min_const_fn/bad_const_fn_body_ice.stderr deleted file mode 100644 index a7e8fdf37ae29..0000000000000 --- a/tests/ui/consts/min_const_fn/bad_const_fn_body_ice.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0015]: cannot call non-const associated function `Box::<[i32; 3]>::new_uninit` in constant functions - --> $DIR/bad_const_fn_body_ice.rs:2:5 - | -LL | vec![1, 2, 3] - | ^^^^^^^^^^^^^ - | - = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - -error[E0015]: cannot call non-const function `std::boxed::box_assume_init_into_vec_unsafe::` in constant functions - --> $DIR/bad_const_fn_body_ice.rs:2:5 - | -LL | vec![1, 2, 3] - | ^^^^^^^^^^^^^ - | -note: function `box_assume_init_into_vec_unsafe` is not const - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL - = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/crate-loading/missing-target-error.rs b/tests/ui/crate-loading/missing-target-error.rs new file mode 100644 index 0000000000000..3a75d568d2c8e --- /dev/null +++ b/tests/ui/crate-loading/missing-target-error.rs @@ -0,0 +1,13 @@ +//! Regression test for . +//! Tests that compiling for a target which is not installed will result in a helpful +//! error message. +//~^^^ ERROR can't find crate for `std` +//~| NOTE target may not be installed +//~| NOTE can't find crate + +//@ compile-flags: --target=thumbv6m-none-eabi +//@ ignore-arm +//@ needs-llvm-components: arm +//@ ignore-backends: gcc + +fn main() { } diff --git a/tests/ui/issues/issue-37131.stderr b/tests/ui/crate-loading/missing-target-error.stderr similarity index 100% rename from tests/ui/issues/issue-37131.stderr rename to tests/ui/crate-loading/missing-target-error.stderr diff --git a/tests/ui/issues/issue-24687-embed-debuginfo/auxiliary/issue-24687-mbcs-in-comments.rs b/tests/ui/debuginfo/auxiliary/cross-crate-multibyte-debuginfo-comments.rs similarity index 85% rename from tests/ui/issues/issue-24687-embed-debuginfo/auxiliary/issue-24687-mbcs-in-comments.rs rename to tests/ui/debuginfo/auxiliary/cross-crate-multibyte-debuginfo-comments.rs index 215145a64b177..eec8c8e1d0d4e 100644 --- a/tests/ui/issues/issue-24687-embed-debuginfo/auxiliary/issue-24687-mbcs-in-comments.rs +++ b/tests/ui/debuginfo/auxiliary/cross-crate-multibyte-debuginfo-comments.rs @@ -1,9 +1,10 @@ -use std::fmt; +//! Auxiliary file for . +//! This is a file with many multi-byte characters, to try to encourage +//! the compiler to trip on them. The Drop implementation below will +//! need to have its source location embedded into the debug info for +//! the output file. -// This ia file with many multi-byte characters, to try to encourage -// the compiler to trip on them. The Drop implementation below will -// need to have its source location embedded into the debug info for -// the output file. +use std::fmt; // αααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααα // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ diff --git a/tests/ui/debuginfo/auxiliary/cross-crate-multibyte-debuginfo.rs b/tests/ui/debuginfo/auxiliary/cross-crate-multibyte-debuginfo.rs new file mode 100644 index 0000000000000..695fccc4519a8 --- /dev/null +++ b/tests/ui/debuginfo/auxiliary/cross-crate-multibyte-debuginfo.rs @@ -0,0 +1,10 @@ +//! Auxiliary file for . +//! This is a file that pulls in a separate file as a submodule, where +//! that separate file has many multi-byte characters, to try to +//! encourage the compiler to trip on them. +#![crate_type="lib"] + +#[path = "cross-crate-multibyte-debuginfo-comments.rs"] +mod issue_24687_mbcs_in_comments; + +pub use issue_24687_mbcs_in_comments::D; diff --git a/tests/ui/issues/issue-24687-embed-debuginfo/main.rs b/tests/ui/debuginfo/cross-crate-multibyte-debuginfo.rs similarity index 63% rename from tests/ui/issues/issue-24687-embed-debuginfo/main.rs rename to tests/ui/debuginfo/cross-crate-multibyte-debuginfo.rs index d1ab717264b4c..026aecaa5f277 100644 --- a/tests/ui/issues/issue-24687-embed-debuginfo/main.rs +++ b/tests/ui/debuginfo/cross-crate-multibyte-debuginfo.rs @@ -1,8 +1,9 @@ +//! Regression test for . //@ run-pass -//@ aux-build:issue-24687-lib.rs +//@ aux-build:cross-crate-multibyte-debuginfo.rs //@ compile-flags:-g -extern crate issue_24687_lib as d; +extern crate cross_crate_multibyte_debuginfo as d; fn main() { // Create a `D`, which has a destructor whose body will be codegen'ed diff --git a/tests/ui/eii/implementation-attribute-allowlist-issue-159015.rs b/tests/ui/eii/implementation-attribute-allowlist-issue-159015.rs new file mode 100644 index 0000000000000..d27ea2a833e0f --- /dev/null +++ b/tests/ui/eii/implementation-attribute-allowlist-issue-159015.rs @@ -0,0 +1,102 @@ +// EII implementations only accept attributes from a conservative allowlist. +// Regression test for #159015 + +//@ edition: 2024 +//@ needs-asm-support + +#![feature(coverage_attribute)] +#![feature(extern_item_impls)] +#![feature(optimize_attribute)] +#![feature(sanitize)] + +#[eii] +fn allowed(); + +/// Sugared and explicit documentation attributes are both allowed. +#[allowed] +#[allow(dead_code)] +#[warn(unreachable_code)] +#[deny(unused_mut)] +#[forbid(unsafe_code)] +#[expect(unused_variables)] +#[cfg(all())] +#[doc = "An allowed EII implementation."] +#[cold] +#[optimize(none)] +#[coverage(off)] +#[sanitize(address = "off")] +#[must_use] +#[deprecated] +fn allowed_impl() { + let unused = (); +} + +#[eii] +fn allowed_inline(); + +#[allowed_inline] +#[allow(unused_attributes)] +#[cfg_attr(all(), inline)] +fn allowed_inline_impl() {} + +#[eii] +fn foo(); + +#[foo] +#[unsafe(no_mangle)] +//~^ ERROR `#[foo]` is not allowed to have `#[no_mangle]` +fn bar() {} + +#[eii] +fn baz(); + +#[baz] +#[unsafe(export_name = "qux")] +//~^ ERROR `#[baz]` is not allowed to have `#[export_name]` +fn qux() {} + +#[eii] +fn quux(); + +#[quux] +#[unsafe(link_section = "__TEXT,__text")] +//~^ ERROR `#[quux]` is not allowed to have `#[link_section]` +fn corge() {} + +#[eii] +fn grault(); + +#[grault] +#[track_caller] +//~^ ERROR `#[grault]` is not allowed to have `#[track_caller]` +fn garply() {} + +#[eii] +extern "C" fn naked_attr(); + +#[naked_attr] +#[unsafe(naked)] +//~^ ERROR `#[naked_attr]` is not allowed to have `#[naked]` +extern "C" fn naked_attr_impl() { + core::arch::naked_asm!("") +} + +#[eii] +fn multiple_invalid_attrs(); + +#[multiple_invalid_attrs] +#[unsafe(no_mangle)] +//~^ ERROR `#[multiple_invalid_attrs]` is not allowed to have `#[no_mangle]` +#[track_caller] +//~^ ERROR `#[multiple_invalid_attrs]` is not allowed to have `#[track_caller]` +fn multiple_invalid_attrs_impl() {} + +#[eii(static_eii)] +static STATIC_EII: u8; + +#[static_eii] +#[used] +//~^ ERROR `#[static_eii]` is not allowed to have `#[used]` +static STATIC_EII_IMPL: u8 = 0; + +fn main() {} diff --git a/tests/ui/eii/implementation-attribute-allowlist-issue-159015.stderr b/tests/ui/eii/implementation-attribute-allowlist-issue-159015.stderr new file mode 100644 index 0000000000000..af9673099f20f --- /dev/null +++ b/tests/ui/eii/implementation-attribute-allowlist-issue-159015.stderr @@ -0,0 +1,67 @@ +error: `#[foo]` is not allowed to have `#[no_mangle]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:46:1 + | +LL | #[foo] + | ------ `#[foo]` is not allowed to have `#[no_mangle]` +LL | #[unsafe(no_mangle)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: `#[baz]` is not allowed to have `#[export_name]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:54:1 + | +LL | #[baz] + | ------ `#[baz]` is not allowed to have `#[export_name]` +LL | #[unsafe(export_name = "qux")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[quux]` is not allowed to have `#[link_section]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:62:1 + | +LL | #[quux] + | ------- `#[quux]` is not allowed to have `#[link_section]` +LL | #[unsafe(link_section = "__TEXT,__text")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[grault]` is not allowed to have `#[track_caller]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:70:1 + | +LL | #[grault] + | --------- `#[grault]` is not allowed to have `#[track_caller]` +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ + +error: `#[naked_attr]` is not allowed to have `#[naked]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:78:1 + | +LL | #[naked_attr] + | ------------- `#[naked_attr]` is not allowed to have `#[naked]` +LL | #[unsafe(naked)] + | ^^^^^^^^^^^^^^^^ + +error: `#[multiple_invalid_attrs]` is not allowed to have `#[no_mangle]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:88:1 + | +LL | #[multiple_invalid_attrs] + | ------------------------- `#[multiple_invalid_attrs]` is not allowed to have `#[no_mangle]` +LL | #[unsafe(no_mangle)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: `#[multiple_invalid_attrs]` is not allowed to have `#[track_caller]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:90:1 + | +LL | #[multiple_invalid_attrs] + | ------------------------- `#[multiple_invalid_attrs]` is not allowed to have `#[track_caller]` +... +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ + +error: `#[static_eii]` is not allowed to have `#[used]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:98:1 + | +LL | #[static_eii] + | ------------- `#[static_eii]` is not allowed to have `#[used]` +LL | #[used] + | ^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/tests/ui/eii/track_caller_errors.stderr b/tests/ui/eii/track_caller_errors.stderr index e096146b67830..356f86093d638 100644 --- a/tests/ui/eii/track_caller_errors.stderr +++ b/tests/ui/eii/track_caller_errors.stderr @@ -4,8 +4,7 @@ error: `#[decl1]` is not allowed to have `#[track_caller]` LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | #[decl1] -LL | fn impl1(x: u64) { - | ---------------- `#[decl1]` is not allowed to have `#[track_caller]` + | -------- `#[decl1]` is not allowed to have `#[track_caller]` error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-38942.rs b/tests/ui/enum-discriminant/repr-u64-enum-discriminant-cast.rs similarity index 62% rename from tests/ui/issues/issue-38942.rs rename to tests/ui/enum-discriminant/repr-u64-enum-discriminant-cast.rs index 3f80beb53f357..c9f6d6a65ea35 100644 --- a/tests/ui/issues/issue-38942.rs +++ b/tests/ui/enum-discriminant/repr-u64-enum-discriminant-cast.rs @@ -1,5 +1,6 @@ +//! Regression test for . +//! This used to ICE when compiling for `i686-apple-darwin`. //@ run-pass -// See https://github.com/rust-lang/rust/issues/38942 #[repr(u64)] pub enum NSEventType { diff --git a/tests/ui/issues/issue-37510.rs b/tests/ui/expr/if/mut-borrow-in-else-if-after-if-let.rs similarity index 55% rename from tests/ui/issues/issue-37510.rs rename to tests/ui/expr/if/mut-borrow-in-else-if-after-if-let.rs index 62a90c5604bd9..7c95752f760c1 100644 --- a/tests/ui/issues/issue-37510.rs +++ b/tests/ui/expr/if/mut-borrow-in-else-if-after-if-let.rs @@ -1,3 +1,5 @@ +//! Regression test for . +//! Test that else-if after if-let is not considered a pattern guard. //@ check-pass fn foo(_: &mut i32) -> bool { true } diff --git a/tests/ui/feature-gates/feature-gate-asm_const_ptr.rs b/tests/ui/feature-gates/feature-gate-asm_const_ptr.rs new file mode 100644 index 0000000000000..cdcb5995a0f08 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-asm_const_ptr.rs @@ -0,0 +1,22 @@ +//@ only-x86_64 + +use std::arch::{asm, global_asm, naked_asm}; + +global_asm!("/* {} */", const &0); +//~^ ERROR using pointers in asm `const` operand is experimental + +#[unsafe(naked)] +extern "C" fn naked() { + unsafe { + naked_asm!("ret /* {} */", const &0); + //~^ ERROR using pointers in asm `const` operand is experimental + } +} + +fn main() { + naked(); + unsafe { + asm!("/* {} */", const &0); + //~^ ERROR using pointers in asm `const` operand is experimental + } +} diff --git a/tests/ui/feature-gates/feature-gate-asm_const_ptr.stderr b/tests/ui/feature-gates/feature-gate-asm_const_ptr.stderr new file mode 100644 index 0000000000000..a804d8fe44be5 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-asm_const_ptr.stderr @@ -0,0 +1,33 @@ +error[E0658]: using pointers in asm `const` operand is experimental + --> $DIR/feature-gate-asm_const_ptr.rs:5:25 + | +LL | global_asm!("/* {} */", const &0); + | ^^^^^^^^ + | + = note: see issue #128464 for more information + = help: add `#![feature(asm_const_ptr)]` 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]: using pointers in asm `const` operand is experimental + --> $DIR/feature-gate-asm_const_ptr.rs:11:36 + | +LL | naked_asm!("ret /* {} */", const &0); + | ^^^^^^^^ + | + = note: see issue #128464 for more information + = help: add `#![feature(asm_const_ptr)]` 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]: using pointers in asm `const` operand is experimental + --> $DIR/feature-gate-asm_const_ptr.rs:19:26 + | +LL | asm!("/* {} */", const &0); + | ^^^^^^^^ + | + = note: see issue #128464 for more information + = help: add `#![feature(asm_const_ptr)]` 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/issues/issue-50618.rs b/tests/ui/functional-struct-update/fru-unknown-field-in-closure.rs similarity index 75% rename from tests/ui/issues/issue-50618.rs rename to tests/ui/functional-struct-update/fru-unknown-field-in-closure.rs index 5f762bc431e12..c9fb4e2774938 100644 --- a/tests/ui/issues/issue-50618.rs +++ b/tests/ui/functional-struct-update/fru-unknown-field-in-closure.rs @@ -1,3 +1,6 @@ +//! Regression test for . +//! This used to ICE. + struct Point { pub x: u64, pub y: u64, diff --git a/tests/ui/issues/issue-50618.stderr b/tests/ui/functional-struct-update/fru-unknown-field-in-closure.stderr similarity index 86% rename from tests/ui/issues/issue-50618.stderr rename to tests/ui/functional-struct-update/fru-unknown-field-in-closure.stderr index 1a3514fb715d1..990e65c87aaae 100644 --- a/tests/ui/issues/issue-50618.stderr +++ b/tests/ui/functional-struct-update/fru-unknown-field-in-closure.stderr @@ -1,5 +1,5 @@ error[E0560]: struct `Point` has no field named `nonexistent` - --> $DIR/issue-50618.rs:14:13 + --> $DIR/fru-unknown-field-in-closure.rs:17:13 | LL | nonexistent: 0, | ^^^^^^^^^^^ `Point` does not have this field diff --git a/tests/ui/issues/issue-38556.rs b/tests/ui/imports/macro-use-as.rs similarity index 58% rename from tests/ui/issues/issue-38556.rs rename to tests/ui/imports/macro-use-as.rs index b04558707e148..e26a831e74b57 100644 --- a/tests/ui/issues/issue-38556.rs +++ b/tests/ui/imports/macro-use-as.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! Reexport in macro caused ICE. //@ run-pass + #![allow(dead_code)] pub struct Foo; diff --git a/tests/ui/issues/issue-51116.rs b/tests/ui/inference/need_type_info/for-loop-nested-array-inference.rs similarity index 66% rename from tests/ui/issues/issue-51116.rs rename to tests/ui/inference/need_type_info/for-loop-nested-array-inference.rs index 4c21cbfc61d43..a6f35620fcc9b 100644 --- a/tests/ui/issues/issue-51116.rs +++ b/tests/ui/inference/need_type_info/for-loop-nested-array-inference.rs @@ -1,3 +1,6 @@ +//! Regression test for . +//! This used to leak internal `__next` ident into suggestion. + fn main() { let tiles = Default::default(); for row in &mut tiles { diff --git a/tests/ui/issues/issue-51116.stderr b/tests/ui/inference/need_type_info/for-loop-nested-array-inference.stderr similarity index 81% rename from tests/ui/issues/issue-51116.stderr rename to tests/ui/inference/need_type_info/for-loop-nested-array-inference.stderr index 4839a0d46095f..75f84797cecd9 100644 --- a/tests/ui/issues/issue-51116.stderr +++ b/tests/ui/inference/need_type_info/for-loop-nested-array-inference.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/issue-51116.rs:5:13 + --> $DIR/for-loop-nested-array-inference.rs:8:13 | LL | *tile = 0; | ^^^^^ cannot infer type diff --git a/tests/ui/issues/issue-39827.rs b/tests/ui/intrinsics/volatile-zst-intrinsics.rs similarity index 80% rename from tests/ui/issues/issue-39827.rs rename to tests/ui/intrinsics/volatile-zst-intrinsics.rs index e3248c9e9ac72..0927442268d1f 100644 --- a/tests/ui/issues/issue-39827.rs +++ b/tests/ui/intrinsics/volatile-zst-intrinsics.rs @@ -1,3 +1,8 @@ +//! Regression test for . +//! This test ensures that volatile intrinsics can be specialised with +//! zero-sized types and, in case of copy/set functions, can accept +//! number of elements equal to zero. + //@ run-pass #![feature(core_intrinsics)] @@ -5,11 +10,6 @@ use std::intrinsics::{ volatile_copy_memory, volatile_store, volatile_load, volatile_copy_nonoverlapping_memory, volatile_set_memory }; -// -// This test ensures that volatile intrinsics can be specialised with -// zero-sized types and, in case of copy/set functions, can accept -// number of elements equal to zero. -// fn main () { let mut dst_pair = (1, 2); let src_pair = (3, 4); diff --git a/tests/ui/issues/issue-24687-embed-debuginfo/auxiliary/issue-24687-lib.rs b/tests/ui/issues/issue-24687-embed-debuginfo/auxiliary/issue-24687-lib.rs deleted file mode 100644 index 5b1b1389cebb3..0000000000000 --- a/tests/ui/issues/issue-24687-embed-debuginfo/auxiliary/issue-24687-lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![crate_type="lib"] - -// This is a file that pulls in a separate file as a submodule, where -// that separate file has many multi-byte characters, to try to -// encourage the compiler to trip on them. - -#[path = "issue-24687-mbcs-in-comments.rs"] -mod issue_24687_mbcs_in_comments; - -pub use issue_24687_mbcs_in_comments::D; diff --git a/tests/ui/issues/issue-37131.rs b/tests/ui/issues/issue-37131.rs deleted file mode 100644 index 875495d08ed77..0000000000000 --- a/tests/ui/issues/issue-37131.rs +++ /dev/null @@ -1,12 +0,0 @@ -//~ ERROR can't find crate for `std` -//~| NOTE target may not be installed -//~| NOTE can't find crate -// Tests that compiling for a target which is not installed will result in a helpful -// error message. - -//@ compile-flags: --target=thumbv6m-none-eabi -//@ ignore-arm -//@ needs-llvm-components: arm -//@ ignore-backends: gcc - -fn main() { } diff --git a/tests/ui/issues/issue-3779.rs b/tests/ui/issues/issue-3779.rs deleted file mode 100644 index 901c1be80ca06..0000000000000 --- a/tests/ui/issues/issue-3779.rs +++ /dev/null @@ -1,8 +0,0 @@ -struct S { - //~^ ERROR E0072 - element: Option -} - -fn main() { - let x = S { element: None }; -} diff --git a/tests/ui/issues/issue-3779.stderr b/tests/ui/issues/issue-3779.stderr deleted file mode 100644 index d4f4b79102d5e..0000000000000 --- a/tests/ui/issues/issue-3779.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0072]: recursive type `S` has infinite size - --> $DIR/issue-3779.rs:1:1 - | -LL | struct S { - | ^^^^^^^^ -LL | -LL | element: Option - | - recursive without indirection - | -help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle - | -LL | element: Option> - | ++++ + - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0072`. diff --git a/tests/ui/issues/issue-38954.rs b/tests/ui/issues/issue-38954.rs deleted file mode 100644 index 61df411b1f92b..0000000000000 --- a/tests/ui/issues/issue-38954.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn _test(ref _p: str) {} -//~^ ERROR the size for values of type - -fn main() { } diff --git a/tests/ui/issues/issue-39709.rs b/tests/ui/issues/issue-39709.rs deleted file mode 100644 index df3286721f51f..0000000000000 --- a/tests/ui/issues/issue-39709.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ run-pass -#![allow(unused_macros)] -fn main() { - println!("{}", { macro_rules! x { ($(t:tt)*) => {} } 33 }); -} diff --git a/tests/ui/issues/issue-39808.rs b/tests/ui/issues/issue-39808.rs deleted file mode 100644 index 99e3d9b4bcdfd..0000000000000 --- a/tests/ui/issues/issue-39808.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ run-pass -#![allow(unreachable_code)] - -// Regression test for #39808. The type parameter of `Owned` was -// considered to be "unconstrained" because the type resulting from -// `format!` (`String`) was not being propagated upward, owing to the -// fact that the expression diverges. - -use std::borrow::Cow; - -fn main() { - let _ = if false { - Cow::Owned(format!("{:?}", panic!())) - } else { - Cow::Borrowed("") - }; -} diff --git a/tests/ui/issues/issue-46771.rs b/tests/ui/issues/issue-46771.rs deleted file mode 100644 index 22be8d6af8a7f..0000000000000 --- a/tests/ui/issues/issue-46771.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - struct Foo; - (1 .. 2).find(|_| Foo(0) == 0); //~ ERROR expected function, found `Foo` -} diff --git a/tests/ui/issues/issue-4759-1.rs b/tests/ui/issues/issue-4759-1.rs deleted file mode 100644 index 7368a7b2f845b..0000000000000 --- a/tests/ui/issues/issue-4759-1.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ run-pass -trait U { fn f(self); } -impl U for isize { fn f(self) {} } -pub fn main() { 4.f(); } diff --git a/tests/ui/issues/issue-5315.rs b/tests/ui/issues/issue-5315.rs deleted file mode 100644 index 29a6f8f2934a1..0000000000000 --- a/tests/ui/issues/issue-5315.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ run-pass - -struct A(#[allow(dead_code)] bool); - -pub fn main() { - let f = A; - f(true); -} diff --git a/tests/ui/issues/issue-67039-unsound-pin-partialeq.rs b/tests/ui/issues/issue-67039-unsound-pin-partialeq.rs deleted file mode 100644 index a496e58a79bdd..0000000000000 --- a/tests/ui/issues/issue-67039-unsound-pin-partialeq.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Pin's PartialEq implementation allowed to access the pointer allowing for -// unsoundness by using Rc::get_mut to move value within Rc. -// See https://internals.rust-lang.org/t/unsoundness-in-pin/11311/73 for more details. - -use std::ops::Deref; -use std::pin::Pin; -use std::rc::Rc; - -struct Apple; - -impl Deref for Apple { - type Target = Apple; - fn deref(&self) -> &Apple { - &Apple - } -} - -impl PartialEq> for Apple { - fn eq(&self, _rc: &Rc) -> bool { - unreachable!() - } -} - -fn main() { - let _ = Pin::new(Apple) == Rc::pin(Apple); - //~^ ERROR type mismatch resolving -} diff --git a/tests/ui/issues/issue-67039-unsound-pin-partialeq.stderr b/tests/ui/issues/issue-67039-unsound-pin-partialeq.stderr deleted file mode 100644 index 9164e4696eb34..0000000000000 --- a/tests/ui/issues/issue-67039-unsound-pin-partialeq.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0271]: type mismatch resolving ` as Deref>::Target == Rc` - --> $DIR/issue-67039-unsound-pin-partialeq.rs:25:29 - | -LL | let _ = Pin::new(Apple) == Rc::pin(Apple); - | ^^ expected `Rc`, found `Apple` - | - = note: expected struct `Rc` - found struct `Apple` - = note: required for `Pin` to implement `PartialEq>>` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/layout/randomize.rs b/tests/ui/layout/randomize.rs index 27e99327a3196..eaeb527f191cb 100644 --- a/tests/ui/layout/randomize.rs +++ b/tests/ui/layout/randomize.rs @@ -49,6 +49,52 @@ const _: () = { assert!(std::mem::offset_of!(Result::<&usize, ()>, Ok.0) == 0); }; +// these types only have their size checked, they're never constructed. +// these repr(Rust) types must remain zero-sized. +#[allow(dead_code)] +pub struct UnitStruct; +#[allow(dead_code)] +pub struct EmptyTupleStruct(); +#[allow(dead_code)] +pub struct EmptyStruct {} +#[allow(dead_code)] +pub struct ZstFieldsTupleStruct((), [u64; 0], [u8; 0], [(); 42]); +#[allow(dead_code)] +pub struct ZstFieldsStruct { + a: (), + b: [u64; 0], + c: [u8; 0], + d: [(); 42], +} +#[allow(dead_code)] +pub enum EmptyEnum {} +#[allow(dead_code)] +pub enum SingleUnitVariantEnum { A } +#[allow(dead_code)] +pub enum SingleZstFieldTupleVariantEnum { A((), [u64; 0], [u8; 0], [(); 42]) } +#[allow(dead_code)] +pub enum SingleZstFieldVariantEnum { + A { + a: (), + b: [u64; 0], + c: [u8; 0], + d: [(); 42], + } +} + +// all these types must remain zero-sized. +const _: () = { + assert!(size_of::() == 0); + assert!(size_of::() == 0); + assert!(size_of::() == 0); + assert!(size_of::() == 0); + assert!(size_of::() == 0); + assert!(size_of::() == 0); + assert!(size_of::() == 0); + assert!(size_of::() == 0); + assert!(size_of::() == 0); +}; + #[allow(dead_code)] struct Unsizable(usize, T); diff --git a/tests/ui/issues/issue-37884.rs b/tests/ui/lifetimes/lifetime-errors/iterator-next-extra-named-lifetime.rs similarity index 70% rename from tests/ui/issues/issue-37884.rs rename to tests/ui/lifetimes/lifetime-errors/iterator-next-extra-named-lifetime.rs index 3480942d9d282..11a1397c8ef09 100644 --- a/tests/ui/issues/issue-37884.rs +++ b/tests/ui/lifetimes/lifetime-errors/iterator-next-extra-named-lifetime.rs @@ -1,3 +1,5 @@ +//! Regression test for . +//! This used to leak compiler data structures in error message. //@ dont-require-annotations: NOTE struct RepeatMut<'a, T>(T, &'a ()); diff --git a/tests/ui/issues/issue-37884.stderr b/tests/ui/lifetimes/lifetime-errors/iterator-next-extra-named-lifetime.stderr similarity index 86% rename from tests/ui/issues/issue-37884.stderr rename to tests/ui/lifetimes/lifetime-errors/iterator-next-extra-named-lifetime.stderr index a7a19e3c6dfdc..c3e1ed6e2641e 100644 --- a/tests/ui/issues/issue-37884.stderr +++ b/tests/ui/lifetimes/lifetime-errors/iterator-next-extra-named-lifetime.stderr @@ -1,5 +1,5 @@ error[E0308]: method not compatible with trait - --> $DIR/issue-37884.rs:8:5 + --> $DIR/iterator-next-extra-named-lifetime.rs:10:5 | LL | fn next(&'a mut self) -> Option | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch @@ -9,7 +9,7 @@ LL | fn next(&'a mut self) -> Option note: the anonymous lifetime as defined here... --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL note: ...does not necessarily outlive the lifetime `'a` as defined here - --> $DIR/issue-37884.rs:5:6 + --> $DIR/iterator-next-extra-named-lifetime.rs:7:6 | LL | impl<'a, T: 'a> Iterator for RepeatMut<'a, T> { | ^^ diff --git a/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr b/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr index c9c15e2d62c9f..c6a53d4face4f 100644 --- a/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr +++ b/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr @@ -1,15 +1,15 @@ error[E0080]: values of the type `[u8; usize::MAX]` are too big for the target architecture --> $SRC_DIR/core/src/any.rs:LL:COL | - = note: evaluation of `std::any::try_as_dyn::<[u8; usize::MAX], dyn Trait>::{constant#0}` failed inside this call -note: inside `TypeId::trait_info_of::` + = note: evaluation of `std::any::try_as_dyn::<'_, [u8; usize::MAX], dyn Trait>::{constant#0}` failed inside this call +note: inside `TypeId::trait_info_of::<'_, dyn Trait>` --> $SRC_DIR/core/src/any.rs:LL:COL note: inside `TypeId::trait_info_of_trait_type_id` --> $SRC_DIR/core/src/any.rs:LL:COL note: inside `type_info::::size` --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL -note: the above error was encountered while instantiating `fn try_as_dyn::<[u8; usize::MAX], dyn Trait>` +note: the above error was encountered while instantiating `fn try_as_dyn::<'_, [u8; usize::MAX], dyn Trait>` --> $DIR/vtable-try-as-dyn.rs:14:13 | LL | let _ = std::any::try_as_dyn::<[u8; usize::MAX], dyn Trait>(x); diff --git a/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr b/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr index c9c15e2d62c9f..c6a53d4face4f 100644 --- a/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr +++ b/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr @@ -1,15 +1,15 @@ error[E0080]: values of the type `[u8; usize::MAX]` are too big for the target architecture --> $SRC_DIR/core/src/any.rs:LL:COL | - = note: evaluation of `std::any::try_as_dyn::<[u8; usize::MAX], dyn Trait>::{constant#0}` failed inside this call -note: inside `TypeId::trait_info_of::` + = note: evaluation of `std::any::try_as_dyn::<'_, [u8; usize::MAX], dyn Trait>::{constant#0}` failed inside this call +note: inside `TypeId::trait_info_of::<'_, dyn Trait>` --> $SRC_DIR/core/src/any.rs:LL:COL note: inside `TypeId::trait_info_of_trait_type_id` --> $SRC_DIR/core/src/any.rs:LL:COL note: inside `type_info::::size` --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL -note: the above error was encountered while instantiating `fn try_as_dyn::<[u8; usize::MAX], dyn Trait>` +note: the above error was encountered while instantiating `fn try_as_dyn::<'_, [u8; usize::MAX], dyn Trait>` --> $DIR/vtable-try-as-dyn.rs:14:13 | LL | let _ = std::any::try_as_dyn::<[u8; usize::MAX], dyn Trait>(x); diff --git a/tests/ui/issues/issue-39848.rs b/tests/ui/macros/macro-adjacent-ident-on-token.rs similarity index 61% rename from tests/ui/issues/issue-39848.rs rename to tests/ui/macros/macro-adjacent-ident-on-token.rs index 2a059120e8175..5e57f427724aa 100644 --- a/tests/ui/issues/issue-39848.rs +++ b/tests/ui/macros/macro-adjacent-ident-on-token.rs @@ -1,3 +1,6 @@ +//! Regression test for . +//! This used to ice on `IllFormedSpan`. + macro_rules! get_opt { ($tgt:expr, $field:ident) => { if $tgt.has_$field() {} //~ ERROR expected `{`, found identifier `foo` diff --git a/tests/ui/issues/issue-39848.stderr b/tests/ui/macros/macro-adjacent-ident-on-token.stderr similarity index 88% rename from tests/ui/issues/issue-39848.stderr rename to tests/ui/macros/macro-adjacent-ident-on-token.stderr index 1ffed2d4a1da1..b13a3d45828d3 100644 --- a/tests/ui/issues/issue-39848.stderr +++ b/tests/ui/macros/macro-adjacent-ident-on-token.stderr @@ -1,5 +1,5 @@ error: expected `{`, found identifier `foo` - --> $DIR/issue-39848.rs:3:21 + --> $DIR/macro-adjacent-ident-on-token.rs:6:21 | LL | if $tgt.has_$field() {} | ^^^^^^ expected `{` @@ -8,7 +8,7 @@ LL | get_opt!(bar, foo); | ------------------ in this macro invocation | note: the `if` expression is missing a block after this condition - --> $DIR/issue-39848.rs:3:12 + --> $DIR/macro-adjacent-ident-on-token.rs:6:12 | LL | if $tgt.has_$field() {} | ^^^^^^^^^ diff --git a/tests/ui/macros/macro-rules-def-in-block-expr.rs b/tests/ui/macros/macro-rules-def-in-block-expr.rs new file mode 100644 index 0000000000000..4074c6c9cac1e --- /dev/null +++ b/tests/ui/macros/macro-rules-def-in-block-expr.rs @@ -0,0 +1,8 @@ +//! Regression test for . +//! Macro definition inside block expression caused ICE. +//@ run-pass + +#![allow(unused_macros)] +fn main() { + println!("{}", { macro_rules! x { ($(t:tt)*) => {} } 33 }); +} diff --git a/tests/ui/issues/issue-5067.rs b/tests/ui/macros/repetition-matches-empty-token-tree.rs similarity index 92% rename from tests/ui/issues/issue-5067.rs rename to tests/ui/macros/repetition-matches-empty-token-tree.rs index 47d07f0df014c..eb722764fc63a 100644 --- a/tests/ui/issues/issue-5067.rs +++ b/tests/ui/macros/repetition-matches-empty-token-tree.rs @@ -1,10 +1,11 @@ -#![allow(unused_macros)] - -// Tests that repetition matchers cannot match the empty token tree (since that would be -// ambiguous). +//! Regression test for . +//! Tests that repetition matchers cannot match the empty token tree (since that would be +//! ambiguous). //@ edition:2018 +#![allow(unused_macros)] + macro_rules! foo { ( $()* ) => {}; //~^ ERROR repetition matches empty token tree diff --git a/tests/ui/issues/issue-5067.stderr b/tests/ui/macros/repetition-matches-empty-token-tree.stderr similarity index 67% rename from tests/ui/issues/issue-5067.stderr rename to tests/ui/macros/repetition-matches-empty-token-tree.stderr index 7ffc6071407c5..eba436c233ca7 100644 --- a/tests/ui/issues/issue-5067.stderr +++ b/tests/ui/macros/repetition-matches-empty-token-tree.stderr @@ -1,107 +1,107 @@ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:9:8 + --> $DIR/repetition-matches-empty-token-tree.rs:10:8 | LL | ( $()* ) => {}; | ^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:11:8 + --> $DIR/repetition-matches-empty-token-tree.rs:12:8 | LL | ( $()+ ) => {}; | ^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:13:8 + --> $DIR/repetition-matches-empty-token-tree.rs:14:8 | LL | ( $()? ) => {}; | ^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:18:9 + --> $DIR/repetition-matches-empty-token-tree.rs:19:9 | LL | ( [$()*] ) => {}; | ^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:20:9 + --> $DIR/repetition-matches-empty-token-tree.rs:21:9 | LL | ( [$()+] ) => {}; | ^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:22:9 + --> $DIR/repetition-matches-empty-token-tree.rs:23:9 | LL | ( [$()?] ) => {}; | ^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:27:8 + --> $DIR/repetition-matches-empty-token-tree.rs:28:8 | LL | ( $($()* $(),* $(a)* $(a),* )* ) => {}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:29:8 + --> $DIR/repetition-matches-empty-token-tree.rs:30:8 | LL | ( $($()* $(),* $(a)* $(a),* )+ ) => {}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:31:8 + --> $DIR/repetition-matches-empty-token-tree.rs:32:8 | LL | ( $($()* $(),* $(a)* $(a),* )? ) => {}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:33:8 + --> $DIR/repetition-matches-empty-token-tree.rs:34:8 | LL | ( $($()? $(),* $(a)? $(a),* )* ) => {}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:35:8 + --> $DIR/repetition-matches-empty-token-tree.rs:36:8 | LL | ( $($()? $(),* $(a)? $(a),* )+ ) => {}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:37:8 + --> $DIR/repetition-matches-empty-token-tree.rs:38:8 | LL | ( $($()? $(),* $(a)? $(a),* )? ) => {}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:47:12 + --> $DIR/repetition-matches-empty-token-tree.rs:48:12 | LL | ( $(a $()+)* ) => {}; | ^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:49:12 + --> $DIR/repetition-matches-empty-token-tree.rs:50:12 | LL | ( $(a $()*)+ ) => {}; | ^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:51:12 + --> $DIR/repetition-matches-empty-token-tree.rs:52:12 | LL | ( $(a $()+)? ) => {}; | ^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:53:12 + --> $DIR/repetition-matches-empty-token-tree.rs:54:12 | LL | ( $(a $()?)+ ) => {}; | ^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:60:18 + --> $DIR/repetition-matches-empty-token-tree.rs:61:18 | LL | (a $e1:expr $($(, a $e2:expr)*)*) => ([$e1 $($(, $e2)*)*]); | ^^^^^^^^^^^^^^^^^^ error: repetition matches empty token tree - --> $DIR/issue-5067.rs:71:8 + --> $DIR/repetition-matches-empty-token-tree.rs:72:8 | LL | ( $()* ) => {}; | ^^ diff --git a/tests/ui/issues/issue-3895.rs b/tests/ui/match/guard-arm-and-or-arm.rs similarity index 63% rename from tests/ui/issues/issue-3895.rs rename to tests/ui/match/guard-arm-and-or-arm.rs index 6bd173d48785b..0296ee0d3cc3c 100644 --- a/tests/ui/issues/issue-3895.rs +++ b/tests/ui/match/guard-arm-and-or-arm.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! Match with guard arm and or pattern used to ICE. //@ run-pass + #![allow(dead_code)] pub fn main() { diff --git a/tests/ui/issues/issue-3702-2.rs b/tests/ui/methods/ambig-method-call-in-trait-impl.rs similarity index 79% rename from tests/ui/issues/issue-3702-2.rs rename to tests/ui/methods/ambig-method-call-in-trait-impl.rs index d47f6d248f708..260ca0b546a3f 100644 --- a/tests/ui/issues/issue-3702-2.rs +++ b/tests/ui/methods/ambig-method-call-in-trait-impl.rs @@ -1,3 +1,6 @@ +//! Regression test for . +//! This used to trigger LLVM assertion. + pub trait ToPrimitive { fn to_int(&self) -> isize { 0 } } diff --git a/tests/ui/issues/issue-3702-2.stderr b/tests/ui/methods/ambig-method-call-in-trait-impl.stderr similarity index 85% rename from tests/ui/issues/issue-3702-2.stderr rename to tests/ui/methods/ambig-method-call-in-trait-impl.stderr index 448263aafd44e..0482346d9a482 100644 --- a/tests/ui/issues/issue-3702-2.stderr +++ b/tests/ui/methods/ambig-method-call-in-trait-impl.stderr @@ -1,16 +1,16 @@ error[E0034]: multiple applicable items in scope - --> $DIR/issue-3702-2.rs:16:14 + --> $DIR/ambig-method-call-in-trait-impl.rs:19:14 | LL | self.to_int() + other.to_int() | ^^^^^^ multiple `to_int` found | note: candidate #1 is defined in an impl of the trait `Add` for the type `isize` - --> $DIR/issue-3702-2.rs:14:5 + --> $DIR/ambig-method-call-in-trait-impl.rs:17:5 | LL | fn to_int(&self) -> isize { *self } | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: candidate #2 is defined in an impl of the trait `ToPrimitive` for the type `isize` - --> $DIR/issue-3702-2.rs:2:5 + --> $DIR/ambig-method-call-in-trait-impl.rs:5:5 | LL | fn to_int(&self) -> isize { 0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/methods/by-value-self-method-on-int.rs b/tests/ui/methods/by-value-self-method-on-int.rs new file mode 100644 index 0000000000000..c27898b156d86 --- /dev/null +++ b/tests/ui/methods/by-value-self-method-on-int.rs @@ -0,0 +1,7 @@ +//! Regression test for . +//! This used to trigger LLVM assertion. +//@ run-pass + +trait U { fn f(self); } +impl U for isize { fn f(self) {} } +pub fn main() { 4.f(); } diff --git a/tests/ui/issues/issue-4759.rs b/tests/ui/methods/destructure-and-call-self-method.rs similarity index 60% rename from tests/ui/issues/issue-4759.rs rename to tests/ui/methods/destructure-and-call-self-method.rs index 4b49442b4010f..a88276ec774f1 100644 --- a/tests/ui/issues/issue-4759.rs +++ b/tests/ui/methods/destructure-and-call-self-method.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! Destructuring a struct and calling consuming method used to segfault. //@ run-pass + #![allow(non_shorthand_field_patterns)] struct T { a: Box } diff --git a/tests/ui/mir/hoist_deref_early_else.rs b/tests/ui/mir/hoist_deref_early_else.rs new file mode 100644 index 0000000000000..fb749e96d7c08 --- /dev/null +++ b/tests/ui/mir/hoist_deref_early_else.rs @@ -0,0 +1,36 @@ +//! Regression test for issue . +//! The null pointer `p` should never be dereferenced. +//@ run-pass +//@ revisions: noopt opt +//@ check-run-results +//@[noopt] compile-flags: -C opt-level=0 +//@[opt] compile-flags: -C opt-level=3 + +use std::hint::black_box; + +#[inline(never)] +fn foo(q: u64, p: *const u64) -> u64 { + unsafe { + 'a: { + match q { + 1 => match *p { + 1 => break 'a 100, + _ => {} + }, + 2 => match *p { + 2 => break 'a 200, + _ => {} + }, + _ => {} + } + 999 + } + } +} + +fn main() { + let q: u64 = black_box(3); + let p: *const u64 = black_box(std::ptr::null()); + let r = foo(q, p); + assert_eq!(999, black_box(r)); +} diff --git a/tests/ui/issues/issue-52262.rs b/tests/ui/moves/move-out-of-shared-ref-deref.rs similarity index 89% rename from tests/ui/issues/issue-52262.rs rename to tests/ui/moves/move-out-of-shared-ref-deref.rs index 547643f0d6e20..6adb7811c5d4d 100644 --- a/tests/ui/issues/issue-52262.rs +++ b/tests/ui/moves/move-out-of-shared-ref-deref.rs @@ -1,3 +1,5 @@ +//! Regression test for . + #[derive(Debug)] enum MyError { NotFound { key: Vec }, diff --git a/tests/ui/issues/issue-52262.stderr b/tests/ui/moves/move-out-of-shared-ref-deref.stderr similarity index 92% rename from tests/ui/issues/issue-52262.stderr rename to tests/ui/moves/move-out-of-shared-ref-deref.stderr index 51959f22b97a4..7a7158dfbdbef 100644 --- a/tests/ui/issues/issue-52262.stderr +++ b/tests/ui/moves/move-out-of-shared-ref-deref.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of `*key` which is behind a shared reference - --> $DIR/issue-52262.rs:15:35 + --> $DIR/move-out-of-shared-ref-deref.rs:17:35 | LL | String::from_utf8(*key).unwrap() | ^^^^ move occurs because `*key` has type `Vec`, which does not implement the `Copy` trait diff --git a/tests/ui/issues/issue-3847.rs b/tests/ui/parser/other-module-struct-match.rs similarity index 63% rename from tests/ui/issues/issue-3847.rs rename to tests/ui/parser/other-module-struct-match.rs index 73aaab4183694..c9c7a9d52dbc1 100644 --- a/tests/ui/issues/issue-3847.rs +++ b/tests/ui/parser/other-module-struct-match.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! Pattern matching struct from different module caused parser to fail. //@ run-pass + mod buildings { pub struct Tower { pub height: usize } } diff --git a/tests/ui/issues/issue-3753.rs b/tests/ui/parser/pub-method-after-attribute.rs similarity index 79% rename from tests/ui/issues/issue-3753.rs rename to tests/ui/parser/pub-method-after-attribute.rs index a243ceab83ebe..828fc52189b32 100644 --- a/tests/ui/issues/issue-3753.rs +++ b/tests/ui/parser/pub-method-after-attribute.rs @@ -1,7 +1,6 @@ +//! Regression test for . +//! Pub keyword after attribute caused parser to fail. //@ run-pass -// Issue #3656 -// Issue Name: pub method preceded by attribute can't be parsed -// Abstract: Visibility parsing failed when compiler parsing use std::f64; diff --git a/tests/ui/issues/issue-51102.rs b/tests/ui/pattern/struct-pattern-nonexistent-field.rs similarity index 84% rename from tests/ui/issues/issue-51102.rs rename to tests/ui/pattern/struct-pattern-nonexistent-field.rs index b5ddc7221d06a..ed74bdf937145 100644 --- a/tests/ui/issues/issue-51102.rs +++ b/tests/ui/pattern/struct-pattern-nonexistent-field.rs @@ -1,3 +1,6 @@ +//! Regression test for . +//! Test matching non-existing fields via struct pattern syntax in closures doesn't ICE. + enum SimpleEnum { NoState, } diff --git a/tests/ui/issues/issue-51102.stderr b/tests/ui/pattern/struct-pattern-nonexistent-field.stderr similarity index 85% rename from tests/ui/issues/issue-51102.stderr rename to tests/ui/pattern/struct-pattern-nonexistent-field.stderr index 09c52292dccaf..4eddc55bb5490 100644 --- a/tests/ui/issues/issue-51102.stderr +++ b/tests/ui/pattern/struct-pattern-nonexistent-field.stderr @@ -1,5 +1,5 @@ error[E0026]: struct `SimpleStruct` does not have a field named `state` - --> $DIR/issue-51102.rs:13:17 + --> $DIR/struct-pattern-nonexistent-field.rs:16:17 | LL | state: 0, | ^^^^^ @@ -8,7 +8,7 @@ LL | state: 0, | help: `SimpleStruct` has a field named `no_state_here` error[E0025]: field `no_state_here` bound multiple times in the pattern - --> $DIR/issue-51102.rs:24:17 + --> $DIR/struct-pattern-nonexistent-field.rs:27:17 | LL | no_state_here: 0, | ---------------- first use of `no_state_here` @@ -16,7 +16,7 @@ LL | no_state_here: 1 | ^^^^^^^^^^^^^^^^ multiple uses of `no_state_here` in pattern error[E0026]: variant `SimpleEnum::NoState` does not have a field named `state` - --> $DIR/issue-51102.rs:33:17 + --> $DIR/struct-pattern-nonexistent-field.rs:36:17 | LL | state: 0 | ^^^^^ variant `SimpleEnum::NoState` does not have this field diff --git a/tests/ui/issues/issue-38857.rs b/tests/ui/privacy/locally-pub-private-module-access.rs similarity index 57% rename from tests/ui/issues/issue-38857.rs rename to tests/ui/privacy/locally-pub-private-module-access.rs index 63a0af759a3de..7939219b6a999 100644 --- a/tests/ui/issues/issue-38857.rs +++ b/tests/ui/privacy/locally-pub-private-module-access.rs @@ -1,3 +1,6 @@ +//! Regression test for . +//! Trying to access locally pub but inaccessible items caused ICE. + fn main() { let a = std::sys::imp::process::process_common::StdioPipes { ..panic!() }; //~^ ERROR: cannot find `imp` in `sys` [E0433] diff --git a/tests/ui/issues/issue-38857.stderr b/tests/ui/privacy/locally-pub-private-module-access.stderr similarity index 85% rename from tests/ui/issues/issue-38857.stderr rename to tests/ui/privacy/locally-pub-private-module-access.stderr index 85a0c266ac657..5f69f1a660923 100644 --- a/tests/ui/issues/issue-38857.stderr +++ b/tests/ui/privacy/locally-pub-private-module-access.stderr @@ -1,11 +1,11 @@ error[E0433]: cannot find `imp` in `sys` - --> $DIR/issue-38857.rs:2:23 + --> $DIR/locally-pub-private-module-access.rs:5:23 | LL | let a = std::sys::imp::process::process_common::StdioPipes { ..panic!() }; | ^^^ could not find `imp` in `sys` error[E0603]: module `sys` is private - --> $DIR/issue-38857.rs:2:18 + --> $DIR/locally-pub-private-module-access.rs:5:18 | LL | let a = std::sys::imp::process::process_common::StdioPipes { ..panic!() }; | ^^^ private module diff --git a/tests/ui/issues/issue-50415.rs b/tests/ui/range/inclusive-range-in-closure.rs similarity index 71% rename from tests/ui/issues/issue-50415.rs rename to tests/ui/range/inclusive-range-in-closure.rs index 5f6211eb149dd..b842fed586608 100644 --- a/tests/ui/issues/issue-50415.rs +++ b/tests/ui/range/inclusive-range-in-closure.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! Test inclusive ranges in closures don't ICE. //@ run-pass + fn main() { // Simplified test case let _ = || 0..=1; diff --git a/tests/ui/reflection/trait_info_of_too_big.stderr b/tests/ui/reflection/trait_info_of_too_big.stderr index b5dc1d3cdb44a..8d8557670e816 100644 --- a/tests/ui/reflection/trait_info_of_too_big.stderr +++ b/tests/ui/reflection/trait_info_of_too_big.stderr @@ -15,7 +15,7 @@ error[E0080]: values of the type `[u8; usize::MAX]` are too big for the target a LL | TypeId::of::<[u8; usize::MAX]>().trait_info_of::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_::{constant#0}` failed inside this call | -note: inside `TypeId::trait_info_of::` +note: inside `TypeId::trait_info_of::<'_, dyn Trait>` --> $SRC_DIR/core/src/any.rs:LL:COL note: inside `TypeId::trait_info_of_trait_type_id` --> $SRC_DIR/core/src/any.rs:LL:COL diff --git a/tests/ui/issues/issue-47094.rs b/tests/ui/repr/conflicting-repr-enum-attrs.rs similarity index 58% rename from tests/ui/issues/issue-47094.rs rename to tests/ui/repr/conflicting-repr-enum-attrs.rs index c5d37feb1447f..d7e629d230c02 100644 --- a/tests/ui/issues/issue-47094.rs +++ b/tests/ui/repr/conflicting-repr-enum-attrs.rs @@ -1,3 +1,7 @@ +//! Regression test for . +//! Test `conflicting representation hints` warning is being triggered +//! when there are multiple repr attributes. + #[repr(C, u8)] //~ ERROR conflicting representation hints //~^ WARN this was previously accepted enum Foo { diff --git a/tests/ui/issues/issue-47094.stderr b/tests/ui/repr/conflicting-repr-enum-attrs.stderr similarity index 90% rename from tests/ui/issues/issue-47094.stderr rename to tests/ui/repr/conflicting-repr-enum-attrs.stderr index da414d68214a8..6810a2a2a1171 100644 --- a/tests/ui/issues/issue-47094.stderr +++ b/tests/ui/repr/conflicting-repr-enum-attrs.stderr @@ -1,5 +1,5 @@ error[E0566]: conflicting representation hints - --> $DIR/issue-47094.rs:1:8 + --> $DIR/conflicting-repr-enum-attrs.rs:5:8 | LL | #[repr(C, u8)] | ^ ^^ @@ -9,7 +9,7 @@ LL | #[repr(C, u8)] = note: `#[deny(conflicting_repr_hints)]` (part of `#[deny(future_incompatible)]`) on by default error[E0566]: conflicting representation hints - --> $DIR/issue-47094.rs:8:8 + --> $DIR/conflicting-repr-enum-attrs.rs:12:8 | LL | #[repr(C)] | ^ @@ -25,7 +25,7 @@ error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0566`. Future incompatibility report: Future breakage diagnostic: error[E0566]: conflicting representation hints - --> $DIR/issue-47094.rs:1:8 + --> $DIR/conflicting-repr-enum-attrs.rs:5:8 | LL | #[repr(C, u8)] | ^ ^^ @@ -36,7 +36,7 @@ LL | #[repr(C, u8)] Future breakage diagnostic: error[E0566]: conflicting representation hints - --> $DIR/issue-47094.rs:8:8 + --> $DIR/conflicting-repr-enum-attrs.rs:12:8 | LL | #[repr(C)] | ^ diff --git a/tests/ui/statics/check-values-constraints.rs b/tests/ui/statics/check-values-constraints.rs index c62abd75a304b..6f1635b761b07 100644 --- a/tests/ui/statics/check-values-constraints.rs +++ b/tests/ui/statics/check-values-constraints.rs @@ -1,5 +1,6 @@ // Verifies all possible restrictions for statics values. +#![feature(const_heap)] #![allow(warnings)] use std::marker; @@ -79,8 +80,6 @@ static STATIC10: UnsafeStruct = UnsafeStruct; struct MyOwned; static STATIC11: Vec = vec![MyOwned]; -//~^ ERROR cannot call non-const function -//~| ERROR cannot call non-const static mut STATIC12: UnsafeStruct = UnsafeStruct; @@ -93,29 +92,25 @@ static mut STATIC14: SafeStruct = SafeStruct { }; static STATIC15: &'static [Vec] = &[ - vec![MyOwned], //~ ERROR cannot call non-const function - //~| ERROR cannot call non-const - vec![MyOwned], //~ ERROR cannot call non-const function - //~| ERROR cannot call non-const + vec![MyOwned], + vec![MyOwned], ]; static STATIC16: (&'static Vec, &'static Vec) = ( - &vec![MyOwned], //~ ERROR cannot call non-const function - //~| ERROR cannot call non-const - &vec![MyOwned], //~ ERROR cannot call non-const function - //~| ERROR cannot call non-const + &vec![MyOwned], + &vec![MyOwned], ); static mut STATIC17: SafeEnum = SafeEnum::Variant1; static STATIC19: Vec = vec![3]; -//~^ ERROR cannot call non-const function -//~| ERROR cannot call non-const +//~^ ERROR encountered `const_allocate` pointer in final value that was not made global + pub fn main() { let y = { - static x: Vec = vec![3]; //~ ERROR cannot call non-const function - //~| ERROR cannot call non-const + static x: Vec = vec![3]; + //~^ ERROR encountered `const_allocate` pointer in final value that was not made global x //~^ ERROR cannot move out of static }; diff --git a/tests/ui/statics/check-values-constraints.stderr b/tests/ui/statics/check-values-constraints.stderr index 94d2b8ce80641..a3d49400703be 100644 --- a/tests/ui/statics/check-values-constraints.stderr +++ b/tests/ui/statics/check-values-constraints.stderr @@ -1,5 +1,5 @@ error[E0493]: destructor of `SafeStruct` cannot be evaluated at compile-time - --> $DIR/check-values-constraints.rs:64:7 + --> $DIR/check-values-constraints.rs:65:7 | LL | ..SafeStruct { | _______^ @@ -11,28 +11,8 @@ LL | | } LL | }; | - value is dropped here -error[E0015]: cannot call non-const associated function `Box::<[MyOwned; 1]>::new_uninit` in statics - --> $DIR/check-values-constraints.rs:81:33 - | -LL | static STATIC11: Vec = vec![MyOwned]; - | ^^^^^^^^^^^^^ - | - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - -error[E0015]: cannot call non-const function `std::boxed::box_assume_init_into_vec_unsafe::` in statics - --> $DIR/check-values-constraints.rs:81:33 - | -LL | static STATIC11: Vec = vec![MyOwned]; - | ^^^^^^^^^^^^^ - | -note: function `box_assume_init_into_vec_unsafe` is not const - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - error[E0015]: cannot call non-const method `::to_string` in statics - --> $DIR/check-values-constraints.rs:92:38 + --> $DIR/check-values-constraints.rs:91:38 | LL | field2: SafeEnum::Variant4("str".to_string()), | ^^^^^^^^^^^ @@ -47,128 +27,24 @@ note: method `to_string` is not const because trait `ToString` is not const = note: calls in statics are limited to constant functions, tuple structs and tuple variants = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` -error[E0015]: cannot call non-const associated function `Box::<[MyOwned; 1]>::new_uninit` in statics - --> $DIR/check-values-constraints.rs:96:5 - | -LL | vec![MyOwned], - | ^^^^^^^^^^^^^ - | - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - -error[E0015]: cannot call non-const function `std::boxed::box_assume_init_into_vec_unsafe::` in statics - --> $DIR/check-values-constraints.rs:96:5 - | -LL | vec![MyOwned], - | ^^^^^^^^^^^^^ - | -note: function `box_assume_init_into_vec_unsafe` is not const - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - -error[E0015]: cannot call non-const associated function `Box::<[MyOwned; 1]>::new_uninit` in statics - --> $DIR/check-values-constraints.rs:98:5 - | -LL | vec![MyOwned], - | ^^^^^^^^^^^^^ - | - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - -error[E0015]: cannot call non-const function `std::boxed::box_assume_init_into_vec_unsafe::` in statics - --> $DIR/check-values-constraints.rs:98:5 - | -LL | vec![MyOwned], - | ^^^^^^^^^^^^^ - | -note: function `box_assume_init_into_vec_unsafe` is not const - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - -error[E0015]: cannot call non-const associated function `Box::<[MyOwned; 1]>::new_uninit` in statics - --> $DIR/check-values-constraints.rs:103:6 - | -LL | &vec![MyOwned], - | ^^^^^^^^^^^^^ - | - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - -error[E0015]: cannot call non-const function `std::boxed::box_assume_init_into_vec_unsafe::` in statics - --> $DIR/check-values-constraints.rs:103:6 - | -LL | &vec![MyOwned], - | ^^^^^^^^^^^^^ - | -note: function `box_assume_init_into_vec_unsafe` is not const - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - -error[E0015]: cannot call non-const associated function `Box::<[MyOwned; 1]>::new_uninit` in statics - --> $DIR/check-values-constraints.rs:105:6 - | -LL | &vec![MyOwned], - | ^^^^^^^^^^^^^ - | - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - -error[E0015]: cannot call non-const function `std::boxed::box_assume_init_into_vec_unsafe::` in statics - --> $DIR/check-values-constraints.rs:105:6 - | -LL | &vec![MyOwned], - | ^^^^^^^^^^^^^ - | -note: function `box_assume_init_into_vec_unsafe` is not const - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - -error[E0015]: cannot call non-const associated function `Box::<[isize; 1]>::new_uninit` in statics - --> $DIR/check-values-constraints.rs:111:31 +error: encountered `const_allocate` pointer in final value that was not made global + --> $DIR/check-values-constraints.rs:106:1 | LL | static STATIC19: Vec = vec![3]; - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - -error[E0015]: cannot call non-const function `std::boxed::box_assume_init_into_vec_unsafe::` in statics - --> $DIR/check-values-constraints.rs:111:31 - | -LL | static STATIC19: Vec = vec![3]; - | ^^^^^^^ - | -note: function `box_assume_init_into_vec_unsafe` is not const - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` + = note: use `const_make_global` to turn allocated pointers into immutable globals before returning -error[E0015]: cannot call non-const associated function `Box::<[isize; 1]>::new_uninit` in statics - --> $DIR/check-values-constraints.rs:117:32 +error: encountered `const_allocate` pointer in final value that was not made global + --> $DIR/check-values-constraints.rs:112:9 | LL | static x: Vec = vec![3]; - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ | - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - -error[E0015]: cannot call non-const function `std::boxed::box_assume_init_into_vec_unsafe::` in statics - --> $DIR/check-values-constraints.rs:117:32 - | -LL | static x: Vec = vec![3]; - | ^^^^^^^ - | -note: function `box_assume_init_into_vec_unsafe` is not const - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` + = note: use `const_make_global` to turn allocated pointers into immutable globals before returning error[E0507]: cannot move out of static item `x` - --> $DIR/check-values-constraints.rs:119:9 + --> $DIR/check-values-constraints.rs:114:9 | LL | x | ^ move occurs because `x` has type `Vec`, which does not implement the `Copy` trait @@ -182,7 +58,7 @@ help: consider cloning the value if the performance cost is acceptable LL | x.clone() | ++++++++ -error: aborting due to 17 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0015, E0493, E0507. For more information about an error, try `rustc --explain E0015`. diff --git a/tests/ui/structs-enums/call-tuple-struct-ctor-as-fn.rs b/tests/ui/structs-enums/call-tuple-struct-ctor-as-fn.rs new file mode 100644 index 0000000000000..b715c9d3eaab2 --- /dev/null +++ b/tests/ui/structs-enums/call-tuple-struct-ctor-as-fn.rs @@ -0,0 +1,10 @@ +//! Regression test for . +//! Test calling tuple struct constructor doesn't cause segfault. +//@ run-pass + +struct A(#[allow(dead_code)] bool); + +pub fn main() { + let f = A; + f(true); +} diff --git a/tests/ui/issues/issue-4736.rs b/tests/ui/structs/tuple-struct-init-with-named-field.rs similarity index 56% rename from tests/ui/issues/issue-4736.rs rename to tests/ui/structs/tuple-struct-init-with-named-field.rs index 799d2d4809860..586d96284e170 100644 --- a/tests/ui/issues/issue-4736.rs +++ b/tests/ui/structs/tuple-struct-init-with-named-field.rs @@ -1,3 +1,6 @@ +//! Regression test for . +//! This used to ICE. + struct NonCopyable(()); fn main() { diff --git a/tests/ui/issues/issue-4736.stderr b/tests/ui/structs/tuple-struct-init-with-named-field.stderr similarity index 90% rename from tests/ui/issues/issue-4736.stderr rename to tests/ui/structs/tuple-struct-init-with-named-field.stderr index b099e528ca8a8..1fb3b0be0b02e 100644 --- a/tests/ui/issues/issue-4736.stderr +++ b/tests/ui/structs/tuple-struct-init-with-named-field.stderr @@ -1,5 +1,5 @@ error[E0560]: struct `NonCopyable` has no field named `p` - --> $DIR/issue-4736.rs:4:26 + --> $DIR/tuple-struct-init-with-named-field.rs:7:26 | LL | struct NonCopyable(()); | ----------- `NonCopyable` defined here diff --git a/tests/ui/issues/issue-46756-consider-borrowing-cast-or-binexpr.fixed b/tests/ui/suggestions/borrow-cast-or-binexpr-with-parens.fixed similarity index 73% rename from tests/ui/issues/issue-46756-consider-borrowing-cast-or-binexpr.fixed rename to tests/ui/suggestions/borrow-cast-or-binexpr-with-parens.fixed index d8402cdf07e38..157ed7e609d07 100644 --- a/tests/ui/issues/issue-46756-consider-borrowing-cast-or-binexpr.fixed +++ b/tests/ui/suggestions/borrow-cast-or-binexpr-with-parens.fixed @@ -1,3 +1,5 @@ +//! Regression test for . +//! Test reference suggestion correctly puts cast in parentheses. //@ run-rustfix #![allow(unused)] diff --git a/tests/ui/issues/issue-46756-consider-borrowing-cast-or-binexpr.rs b/tests/ui/suggestions/borrow-cast-or-binexpr-with-parens.rs similarity index 73% rename from tests/ui/issues/issue-46756-consider-borrowing-cast-or-binexpr.rs rename to tests/ui/suggestions/borrow-cast-or-binexpr-with-parens.rs index 0e04680911c80..3657cc67f7588 100644 --- a/tests/ui/issues/issue-46756-consider-borrowing-cast-or-binexpr.rs +++ b/tests/ui/suggestions/borrow-cast-or-binexpr-with-parens.rs @@ -1,3 +1,5 @@ +//! Regression test for . +//! Test reference suggestion correctly puts cast in parentheses. //@ run-rustfix #![allow(unused)] diff --git a/tests/ui/issues/issue-46756-consider-borrowing-cast-or-binexpr.stderr b/tests/ui/suggestions/borrow-cast-or-binexpr-with-parens.stderr similarity index 83% rename from tests/ui/issues/issue-46756-consider-borrowing-cast-or-binexpr.stderr rename to tests/ui/suggestions/borrow-cast-or-binexpr-with-parens.stderr index 211dd51289595..61b309291438c 100644 --- a/tests/ui/issues/issue-46756-consider-borrowing-cast-or-binexpr.stderr +++ b/tests/ui/suggestions/borrow-cast-or-binexpr-with-parens.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-46756-consider-borrowing-cast-or-binexpr.rs:12:42 + --> $DIR/borrow-cast-or-binexpr-with-parens.rs:14:42 | LL | light_flows_our_war_of_mocking_words(behold as usize); | ------------------------------------ ^^^^^^^^^^^^^^^ expected `&usize`, found `usize` @@ -7,7 +7,7 @@ LL | light_flows_our_war_of_mocking_words(behold as usize); | arguments to this function are incorrect | note: function defined here - --> $DIR/issue-46756-consider-borrowing-cast-or-binexpr.rs:5:4 + --> $DIR/borrow-cast-or-binexpr-with-parens.rs:7:4 | LL | fn light_flows_our_war_of_mocking_words(and_yet: &usize) -> usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --------------- @@ -17,7 +17,7 @@ LL | light_flows_our_war_of_mocking_words(&(behold as usize)); | ++ + error[E0308]: mismatched types - --> $DIR/issue-46756-consider-borrowing-cast-or-binexpr.rs:14:42 + --> $DIR/borrow-cast-or-binexpr-with-parens.rs:16:42 | LL | light_flows_our_war_of_mocking_words(with_tears + 4); | ------------------------------------ ^^^^^^^^^^^^^^ expected `&usize`, found `usize` @@ -25,7 +25,7 @@ LL | light_flows_our_war_of_mocking_words(with_tears + 4); | arguments to this function are incorrect | note: function defined here - --> $DIR/issue-46756-consider-borrowing-cast-or-binexpr.rs:5:4 + --> $DIR/borrow-cast-or-binexpr-with-parens.rs:7:4 | LL | fn light_flows_our_war_of_mocking_words(and_yet: &usize) -> usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --------------- diff --git a/tests/ui/issues/issue-47309.rs b/tests/ui/traits/default-method/mono-item-collector-on-impl-with-lifetimes.rs similarity index 54% rename from tests/ui/issues/issue-47309.rs rename to tests/ui/traits/default-method/mono-item-collector-on-impl-with-lifetimes.rs index 99f1ccbc5758c..44c1e171835c8 100644 --- a/tests/ui/issues/issue-47309.rs +++ b/tests/ui/traits/default-method/mono-item-collector-on-impl-with-lifetimes.rs @@ -1,6 +1,6 @@ -// Make sure that the mono-item collector does not crash when trying to -// instantiate a default impl of a method with lifetime parameters. -// See https://github.com/rust-lang/rust/issues/47309 +//! Regression test for . +//! Make sure that the mono-item collector does not crash when trying to +//! instantiate a default impl of a method with lifetime parameters. //@ compile-flags:-Clink-dead-code //@ build-pass diff --git a/tests/ui/issues/issue-3979-2.rs b/tests/ui/traits/default-method/nested-supertrait-method-in-default-body.rs similarity index 55% rename from tests/ui/issues/issue-3979-2.rs rename to tests/ui/traits/default-method/nested-supertrait-method-in-default-body.rs index 98b6e85225db5..5516b58e57624 100644 --- a/tests/ui/issues/issue-3979-2.rs +++ b/tests/ui/traits/default-method/nested-supertrait-method-in-default-body.rs @@ -1,3 +1,5 @@ +//! Regression test for . +//! Test calling nested supertrait's method in default method body works. //@ check-pass trait A { diff --git a/tests/ui/issues/issue-3979.rs b/tests/ui/traits/default-method/supertrait-method-in-default-body.rs similarity index 79% rename from tests/ui/issues/issue-3979.rs rename to tests/ui/traits/default-method/supertrait-method-in-default-body.rs index 238df225f0f34..597ff6375c7a2 100644 --- a/tests/ui/issues/issue-3979.rs +++ b/tests/ui/traits/default-method/supertrait-method-in-default-body.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! Test calling supertrait's method in default method body works. //@ run-pass + #![allow(dead_code)] #![allow(non_snake_case)] diff --git a/tests/ui/issues/issue-51907.rs b/tests/ui/traits/object/extern-c-trait-object-methods.rs similarity index 75% rename from tests/ui/issues/issue-51907.rs rename to tests/ui/traits/object/extern-c-trait-object-methods.rs index bf3f629df4970..f1ee19fc67cf2 100644 --- a/tests/ui/issues/issue-51907.rs +++ b/tests/ui/traits/object/extern-c-trait-object-methods.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! Test extern `C` trait object methods don't ICE. //@ run-pass + trait Foo { extern "C" fn borrow(&self); extern "C" fn take(self: Box); diff --git a/tests/ui/issues/issue-3702.rs b/tests/ui/traits/object/nested-trait-method-call.rs similarity index 54% rename from tests/ui/issues/issue-3702.rs rename to tests/ui/traits/object/nested-trait-method-call.rs index bb79e3a7f9384..c63671ba48a43 100644 --- a/tests/ui/issues/issue-3702.rs +++ b/tests/ui/traits/object/nested-trait-method-call.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! Calling method of trait defined in function used to trigger LLVM assertion. //@ run-pass + #![allow(dead_code)] pub fn main() { diff --git a/tests/ui/type-inference/diverging-expr-unconstrained-type-param.rs b/tests/ui/type-inference/diverging-expr-unconstrained-type-param.rs new file mode 100644 index 0000000000000..1e523dcc66ea0 --- /dev/null +++ b/tests/ui/type-inference/diverging-expr-unconstrained-type-param.rs @@ -0,0 +1,17 @@ +//! Regression test for . +//! The type parameter of `Owned` was considered to be "unconstrained" +//! because the type resulting from `format!` (`String`) was not being +//! propagated upward, owing to the fact that the expression diverges. +//@ run-pass + +#![allow(unreachable_code)] + +use std::borrow::Cow; + +fn main() { + let _ = if false { + Cow::Owned(format!("{:?}", panic!())) + } else { + Cow::Borrowed("") + }; +} diff --git a/tests/ui/typeck/call-unit-struct-as-fn.rs b/tests/ui/typeck/call-unit-struct-as-fn.rs new file mode 100644 index 0000000000000..2fe2085dd72a9 --- /dev/null +++ b/tests/ui/typeck/call-unit-struct-as-fn.rs @@ -0,0 +1,7 @@ +//! Regression test for . +//! Test calling unit struct as fn doesn't ICE. + +fn main() { + struct Foo; + (1 .. 2).find(|_| Foo(0) == 0); //~ ERROR expected function, found `Foo` +} diff --git a/tests/ui/issues/issue-46771.stderr b/tests/ui/typeck/call-unit-struct-as-fn.stderr similarity index 90% rename from tests/ui/issues/issue-46771.stderr rename to tests/ui/typeck/call-unit-struct-as-fn.stderr index fab55fbfd704a..8056d0df57f99 100644 --- a/tests/ui/issues/issue-46771.stderr +++ b/tests/ui/typeck/call-unit-struct-as-fn.stderr @@ -1,5 +1,5 @@ error[E0618]: expected function, found `Foo` - --> $DIR/issue-46771.rs:3:23 + --> $DIR/call-unit-struct-as-fn.rs:6:23 | LL | struct Foo; | ---------- `Foo` defined here diff --git a/tests/ui/issues/issue-37665.rs b/tests/ui/unpretty/mir-unpretty-type-error.rs similarity index 62% rename from tests/ui/issues/issue-37665.rs rename to tests/ui/unpretty/mir-unpretty-type-error.rs index db74b1f025987..4b48a0126ac51 100644 --- a/tests/ui/issues/issue-37665.rs +++ b/tests/ui/unpretty/mir-unpretty-type-error.rs @@ -1,3 +1,5 @@ +//! Regression test for . +//! Test type error when compiling with `unpretty=mir` doesn't ICE. //@ compile-flags: -Z unpretty=mir use std::path::MAIN_SEPARATOR; diff --git a/tests/ui/issues/issue-37665.stderr b/tests/ui/unpretty/mir-unpretty-type-error.stderr similarity index 86% rename from tests/ui/issues/issue-37665.stderr rename to tests/ui/unpretty/mir-unpretty-type-error.stderr index 2c404b4e74473..f0c19a23ed0e4 100644 --- a/tests/ui/issues/issue-37665.stderr +++ b/tests/ui/unpretty/mir-unpretty-type-error.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-37665.rs:9:17 + --> $DIR/mir-unpretty-type-error.rs:11:17 | LL | let x: () = 0; | -- ^ expected `()`, found integer diff --git a/tests/ui/issues/issue-48131.rs b/tests/ui/unsafe/unused-unsafe-in-nested-fn-lint.rs similarity index 89% rename from tests/ui/issues/issue-48131.rs rename to tests/ui/unsafe/unused-unsafe-in-nested-fn-lint.rs index a1084ea243b26..92b9bbfefc88e 100644 --- a/tests/ui/issues/issue-48131.rs +++ b/tests/ui/unsafe/unused-unsafe-in-nested-fn-lint.rs @@ -1,3 +1,5 @@ +//! Regression test for . + // This note is annotated because the purpose of the test // is to ensure that certain other notes are not generated. #![deny(unused_unsafe)] diff --git a/tests/ui/issues/issue-48131.stderr b/tests/ui/unsafe/unused-unsafe-in-nested-fn-lint.stderr similarity index 72% rename from tests/ui/issues/issue-48131.stderr rename to tests/ui/unsafe/unused-unsafe-in-nested-fn-lint.stderr index fe0cd4efae891..cc5fab4fcdf94 100644 --- a/tests/ui/issues/issue-48131.stderr +++ b/tests/ui/unsafe/unused-unsafe-in-nested-fn-lint.stderr @@ -1,17 +1,17 @@ error: unnecessary `unsafe` block - --> $DIR/issue-48131.rs:9:9 + --> $DIR/unused-unsafe-in-nested-fn-lint.rs:11:9 | LL | unsafe { /* unnecessary */ } | ^^^^^^ unnecessary `unsafe` block | note: the lint level is defined here - --> $DIR/issue-48131.rs:3:9 + --> $DIR/unused-unsafe-in-nested-fn-lint.rs:5:9 | LL | #![deny(unused_unsafe)] | ^^^^^^^^^^^^^ error: unnecessary `unsafe` block - --> $DIR/issue-48131.rs:19:13 + --> $DIR/unused-unsafe-in-nested-fn-lint.rs:21:13 | LL | unsafe { /* unnecessary */ } | ^^^^^^ unnecessary `unsafe` block diff --git a/tests/ui/unsized/unsized-ref-fn-param.rs b/tests/ui/unsized/unsized-ref-fn-param.rs new file mode 100644 index 0000000000000..9a36e82b049fe --- /dev/null +++ b/tests/ui/unsized/unsized-ref-fn-param.rs @@ -0,0 +1,7 @@ +//! Regression test for . +//! Test we emit helpful error message instead of silently failing. + +fn _test(ref _p: str) {} +//~^ ERROR the size for values of type + +fn main() { } diff --git a/tests/ui/issues/issue-38954.stderr b/tests/ui/unsized/unsized-ref-fn-param.stderr similarity index 93% rename from tests/ui/issues/issue-38954.stderr rename to tests/ui/unsized/unsized-ref-fn-param.stderr index bd9c5e4197bc7..3fa5758d2083a 100644 --- a/tests/ui/issues/issue-38954.stderr +++ b/tests/ui/unsized/unsized-ref-fn-param.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/issue-38954.rs:1:18 + --> $DIR/unsized-ref-fn-param.rs:4:18 | LL | fn _test(ref _p: str) {} | ^^^ doesn't have a size known at compile-time