diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index f30b923eeed17..5a44323a0b755 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -343,10 +343,16 @@ impl ExternAbi { // This ABI does not support calls at all (except via assembly). false } + Self::RustCall => { + // Argument untupling requires additional support for tail calls to be possible, + // see . There is no real uses of + // tail calling `extern "rust-call"` functions + false + } + Self::C { .. } | Self::System { .. } | Self::Rust - | Self::RustCall | Self::RustCold | Self::RustInvalid | Self::Unadjusted diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 33f0f6fc2f809..8ae4dedff8f28 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1459,6 +1459,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { ); } + fn vscale(&mut self, _: Self::Type) -> Self::Value { + unimplemented!("`rustc_codegen_gcc` doesn't support scalable vectors yet") + } + fn select( &mut self, cond: RValue<'gcc>, diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index acfec99438059..afb6985d21a95 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1226,6 +1226,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } + fn vscale(&mut self, ty: &'ll Type) -> &'ll Value { + unsafe { llvm::LLVMRustBuildVScale(self.llbuilder, ty) } + } + fn select( &mut self, cond: &'ll Value, diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 1cefdaae5ebde..ee17468ec0c03 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -3,6 +3,9 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{DiffActivity, DiffMode}; use rustc_ast::expand::typetree::FncTree; use rustc_codegen_ssa::common::TypeKind; +use rustc_codegen_ssa::mir::IntrinsicResult; +use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; +use rustc_codegen_ssa::mir::place::PlaceValue; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir::attrs::RustcAutodiff; @@ -11,7 +14,7 @@ use rustc_middle::{bug, ty}; use rustc_target::callconv::PassMode; use tracing::debug; -use crate::builder::{Builder, PlaceRef, UNNAMED}; +use crate::builder::{Builder, UNNAMED}; use crate::context::SimpleCx; use crate::declare::declare_simple_fn; use crate::llvm::{self, TRUE, Type, Value}; @@ -296,9 +299,10 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>( ret_ty: &'ll Type, fn_args: &[&'ll Value], attrs: &RustcAutodiff, - dest: PlaceRef<'tcx, &'ll Value>, + dest_layout: ty::layout::TyAndLayout<'tcx>, + dest_place: Option>, fnc_tree: FncTree, -) { +) -> IntrinsicResult<'tcx, &'ll Value> { // We have to pick the name depending on whether we want forward or reverse mode autodiff. let mut ad_name: String = match attrs.mode { DiffMode::Forward => "__enzyme_fwddiff", @@ -381,11 +385,18 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>( let call = builder.call(enzyme_ty, None, None, ad_fn, &args, None, None); let fn_ret_ty = builder.cx.val_ty(call); - if fn_ret_ty != builder.cx.type_void() && fn_ret_ty != builder.cx.type_struct(&[], false) { + if fn_ret_ty == builder.cx.type_void() || fn_ret_ty == builder.cx.type_struct(&[], false) { // If we return void or an empty struct, then our caller (due to how we generated it) // does not expect a return value. As such, we have no pointer (or place) into which // we could store our value, and would store into an undef, which would cause UB. // As such, we just ignore the return value in those cases. - builder.store_to_place(call, dest.val); + IntrinsicResult::Operand(OperandValue::ZeroSized) + } else if let Some(dest_place) = dest_place { + builder.store_to_place(call, dest_place); + IntrinsicResult::WroteIntoPlace + } else { + IntrinsicResult::Operand( + OperandRef::from_immediate_or_packed_pair(builder, call, dest_layout).val, + ) } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 64e1ae03bde76..84661f5160b14 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -978,10 +978,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn intrinsic_call_expects_place_always(&self, name: Symbol) -> bool { - matches!( - name, - sym::autodiff | sym::volatile_load | sym::unaligned_volatile_load | sym::black_box - ) + matches!(name, sym::volatile_load | sym::unaligned_volatile_load | sym::black_box) } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 0b03d6862ca84..a886a96b281dc 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -43,6 +43,7 @@ use crate::errors::{ AutoDiffWithoutEnable, AutoDiffWithoutLto, IntrinsicSignatureMismatch, IntrinsicWrongArch, OffloadWithoutEnable, OffloadWithoutFatLTO, UnknownIntrinsic, }; +use crate::intrinsic::ty::typetree::fnc_typetrees; use crate::llvm::{self, Type, Value}; use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; @@ -223,12 +224,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ) } sym::autodiff => { - let result = PlaceRef { - val: result_place.unwrap(), - layout: result_layout, - }; - codegen_autodiff(self, tcx, instance, args, result); - return IntrinsicResult::WroteIntoPlace; + return codegen_autodiff(self, tcx, instance, args, result_layout, result_place); } sym::offload => { if tcx.sess.opts.unstable_opts.offload.is_empty() { @@ -1728,8 +1724,9 @@ fn codegen_autodiff<'ll, 'tcx>( tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>, args: &[OperandRef<'tcx, &'ll Value>], - result: PlaceRef<'tcx, &'ll Value>, -) { + result_layout: ty::layout::TyAndLayout<'tcx>, + result_place: Option>, +) -> IntrinsicResult<'tcx, &'ll Value> { if !tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::Enable) { let _ = tcx.dcx().emit_almost_fatal(AutoDiffWithoutEnable); } @@ -1769,9 +1766,9 @@ fn codegen_autodiff<'ll, 'tcx>( diff_id, diff_args ), - Err(_) => { + Err(err) => { // An error has already been emitted - return; + return IntrinsicResult::Err(err); } }; @@ -1791,7 +1788,7 @@ fn codegen_autodiff<'ll, 'tcx>( &mut diff_attrs.input_activity, ); - let fnc_tree = rustc_middle::ty::fnc_typetrees(tcx, source_fn_ptr_ty); + let fnc_tree = fnc_typetrees(tcx, source_fn_ptr_ty); // Build body generate_enzyme_call( @@ -1802,9 +1799,10 @@ fn codegen_autodiff<'ll, 'tcx>( llret_ty, &val_arr, &diff_attrs, - result, + result_layout, + result_place, fnc_tree, - ); + ) } // Generates the LLVM code to offload a Rust function to a target device (e.g., GPU). diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index b14a6206ae682..dd527eafe8362 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2139,6 +2139,8 @@ unsafe extern "C" { IsVolatile: bool, ) -> &'a Value; + pub(crate) fn LLVMRustBuildVScale<'a>(B: &Builder<'a>, Ty: &'a Type) -> &'a Value; + pub(crate) fn LLVMRustTimeTraceProfilerInitialize(); pub(crate) fn LLVMRustTimeTraceProfilerFinishThread(); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 2c3ee1bae09f8..102f0a33df721 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -47,7 +47,7 @@ use rustc_session::{Session, filesearch}; use rustc_span::Symbol; use rustc_target::spec::crt_objects::CrtObjects; use rustc_target::spec::{ - BinaryFormat, Cc, CfgAbi, Env, LinkOutputKind, LinkSelfContainedComponents, + Arch, BinaryFormat, Cc, CfgAbi, Env, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, Os, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, }; @@ -2413,6 +2413,69 @@ fn add_rpath_args( } } +fn strip_numeric_suffix<'a>(base: &'a str, suffix: impl AsRef, fallback: &'a str) -> &'a str { + if suffix.as_ref().parse::().is_ok() { base } else { fallback } +} + +fn undecorate_c_symbol<'a>( + name: &'a str, + sess: &Session, + kind: SymbolExportKind, +) -> Option<&'a str> { + match sess.target.binary_format { + BinaryFormat::MachO => { + // Mach-O: strip the leading underscore that all external symbols have. + // The Darwin linker's export_symbols will add it back. + name.strip_prefix('_') + } + BinaryFormat::Coff => { + // MSVC C++ mangled names start with '?' and use a completely different + // decorating scheme that includes '@@' as structural delimiters. + // They must not be subjected to C calling-convention undecoration. + if name.starts_with('?') { + return Some(name); + } + Some(match sess.target.arch { + Arch::X86 => { + // COFF 32-bit: strip calling-convention decorations. + if let Some(rest) = name.strip_prefix('@') { + // fastcall: @foo@N -> foo + rest.rsplit_once('@') + .map(|(base, suffix)| strip_numeric_suffix(base, suffix, name)) + .unwrap_or(name) + } else if let Some(stripped) = name.strip_prefix('_') { + if let Some((base, suffix)) = stripped.rsplit_once('@') { + // stdcall: _foo@N -> foo + strip_numeric_suffix(base, suffix, stripped) + } else { + // cdecl: _foo -> foo + stripped + } + } else { + // vectorcall: foo@@N -> foo + name.rsplit_once("@@") + .map(|(base, suffix)| strip_numeric_suffix(base, suffix, name)) + .unwrap_or(name) + } + } + Arch::X86_64 => { + // COFF 64-bit: vectorcall mangling (foo@@N -> foo) also applies on x86_64. + name.rsplit_once("@@") + .map(|(base, suffix)| strip_numeric_suffix(base, suffix, name)) + .unwrap_or(name) + } + Arch::Arm64EC if kind == SymbolExportKind::Text => { + // Arm64EC: `#` prefix distinguishes ARM64EC text symbols from x64 thunks. + name.strip_prefix('#').unwrap_or(name) + } + _ => name, + }) + } + // ELF: no decoration + _ => Some(name), + } +} + fn add_c_staticlib_symbols( sess: &Session, lib: &NativeLib, @@ -2454,7 +2517,14 @@ fn add_c_staticlib_symbols( } for symbol in object.symbols() { - if symbol.scope() != object::SymbolScope::Dynamic { + // The `object` crate returns `Dynamic` for ELF/Mach-O global symbols, + // but always returns `Linkage` for COFF external symbols. + // Accept both for COFF (Windows and UEFI). + let scope = symbol.scope(); + if scope != object::SymbolScope::Dynamic + && !(sess.target.binary_format == BinaryFormat::Coff + && scope == object::SymbolScope::Linkage) + { continue; } @@ -2469,9 +2539,10 @@ fn add_c_staticlib_symbols( _ => continue, }; - // FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple. - // Need to be resolved. - out.push((name.to_string(), export_kind)); + let Some(undecorated) = undecorate_c_symbol(name, sess, export_kind) else { + continue; + }; + out.push((undecorated.to_string(), export_kind)); } } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 363e47aa60fa1..d68549c6871f4 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -476,6 +476,11 @@ pub trait BuilderMethods<'a, 'tcx>: flags: MemFlags, ); + // Produce a value from calling the `vscale` intrinsic (containing the `vscale` multiplier that + // a scalable vector's element size and count can be multiplied by to get the real size of the + // vector) + fn vscale(&mut self, ty: Self::Type) -> Self::Value; + /// *Typed* copy for non-overlapping places. /// /// Has a default implementation in terms of `memcpy`, but specific backends @@ -513,6 +518,12 @@ pub trait BuilderMethods<'a, 'tcx>: temp.val.store_with_flags(self, dst.with_type(layout), flags); } else if !layout.is_zst() { let bytes = self.const_usize(layout.size.bytes()); + let bytes = if layout.peel_transparent_wrappers(self).ty.is_scalable_vector() { + let vscale = self.vscale(self.type_i64()); + self.mul(vscale, bytes) + } else { + bytes + }; self.memcpy(dst.llval, dst.align, src.llval, src.align, bytes, flags, None); } } diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index a50b06e5120b2..8b063af187a58 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1497,6 +1497,10 @@ LLVMRustBuildMemMove(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, unwrap(Size), IsVolatile)); } +extern "C" LLVMValueRef LLVMRustBuildVScale(LLVMBuilderRef B, LLVMTypeRef Ty) { + return wrap(unwrap(B)->CreateVScale(unwrap(Ty))); +} + extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Val, LLVMValueRef Size, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index fcb3654bbe435..3ea798ee45fb2 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -27,7 +27,6 @@ pub use intrinsic::IntrinsicDef; use rustc_abi::{ Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, ScalableElt, VariantIdx, }; -use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree}; use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast}; pub use rustc_ast_ir::{Movability, Mutability, try_visit}; @@ -67,7 +66,7 @@ pub use rustc_type_ir::solve::{CandidatePreferenceMode, SizedTraitKind, VisibleF pub use rustc_type_ir::*; #[allow(hidden_glob_reexports, unused_imports)] use rustc_type_ir::{InferCtxtLike, Interner}; -use tracing::{debug, instrument, trace}; +use tracing::{debug, instrument}; pub use vtable::*; pub use self::closure::{ @@ -144,6 +143,7 @@ pub mod print; pub mod relate; pub mod significant_drop_order; pub mod trait_def; +pub mod typetree; pub mod util; pub mod vtable; @@ -2390,228 +2390,3 @@ pub struct DestructuredAdtConst<'tcx> { pub variant: VariantIdx, pub fields: &'tcx [ty::Const<'tcx>], } - -/// Generate TypeTree information for autodiff. -/// This function creates TypeTree metadata that describes the memory layout -/// of function parameters and return types for Enzyme autodiff. -pub fn fnc_typetrees<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>) -> FncTree { - // Check if TypeTrees are disabled via NoTT flag - if tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::NoTT) { - return FncTree { args: vec![], ret: TypeTree::new() }; - } - - // Check if this is actually a function type - if !fn_ty.is_fn() { - return FncTree { args: vec![], ret: TypeTree::new() }; - } - - // Get the function signature - let fn_sig = fn_ty.fn_sig(tcx); - let sig = tcx.instantiate_bound_regions_with_erased(fn_sig); - - // Create TypeTrees for each input parameter - let mut args = vec![]; - for ty in sig.inputs().iter() { - let type_tree = typetree_from_ty(tcx, *ty); - args.push(type_tree); - } - - // Create TypeTree for return type - let ret = typetree_from_ty(tcx, sig.output()); - - FncTree { args, ret } -} - -/// Generate TypeTree for a specific type. -/// This function analyzes a Rust type and creates appropriate TypeTree metadata. -pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { - let mut visited = Vec::new(); - typetree_from_ty_inner(tcx, ty, 0, &mut visited) -} - -/// Maximum recursion depth for TypeTree generation to prevent stack overflow -/// from pathological deeply nested types. Combined with cycle detection. -const MAX_TYPETREE_DEPTH: usize = 6; - -/// Internal recursive function for TypeTree generation with cycle detection and depth limiting. -fn typetree_from_ty_inner<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - depth: usize, - visited: &mut Vec>, -) -> TypeTree { - if depth >= MAX_TYPETREE_DEPTH { - trace!("typetree depth limit {} reached for type: {}", MAX_TYPETREE_DEPTH, ty); - return TypeTree::new(); - } - - if visited.contains(&ty) { - return TypeTree::new(); - } - - visited.push(ty); - let result = typetree_from_ty_impl(tcx, ty, depth, visited); - visited.pop(); - result -} - -/// Implementation of TypeTree generation logic. -fn typetree_from_ty_impl<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - depth: usize, - visited: &mut Vec>, -) -> TypeTree { - typetree_from_ty_impl_inner(tcx, ty, depth, visited, false) -} - -/// Internal implementation with context about whether this is for a reference target. -fn typetree_from_ty_impl_inner<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - depth: usize, - visited: &mut Vec>, - is_reference_target: bool, -) -> TypeTree { - if ty.is_scalar() { - let (kind, size) = if ty.is_integral() || ty.is_char() || ty.is_bool() { - (Kind::Integer, ty.primitive_size(tcx).bytes_usize()) - } else if ty.is_floating_point() { - match ty { - x if x == tcx.types.f16 => (Kind::Half, 2), - x if x == tcx.types.f32 => (Kind::Float, 4), - x if x == tcx.types.f64 => (Kind::Double, 8), - x if x == tcx.types.f128 => (Kind::F128, 16), - _ => (Kind::Integer, 0), - } - } else { - (Kind::Integer, 0) - }; - - // Use offset 0 for scalars that are direct targets of references (like &f64) - // Use offset -1 for scalars used directly (like function return types) - let offset = if is_reference_target && !ty.is_array() { 0 } else { -1 }; - return TypeTree(vec![Type { offset, size, kind, child: TypeTree::new() }]); - } - - if ty.is_ref() || ty.is_raw_ptr() || ty.is_box() { - let Some(inner_ty) = ty.builtin_deref(true) else { - return TypeTree::new(); - }; - - let child = typetree_from_ty_impl_inner(tcx, inner_ty, depth + 1, visited, true); - return TypeTree(vec![Type { - offset: -1, - size: tcx.data_layout.pointer_size().bytes_usize(), - kind: Kind::Pointer, - child, - }]); - } - - if ty.is_array() { - if let ty::Array(element_ty, len_const) = ty.kind() { - let len = len_const.try_to_target_usize(tcx).unwrap_or(0); - if len == 0 { - return TypeTree::new(); - } - let element_tree = - typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false); - let mut types = Vec::new(); - for elem_type in &element_tree.0 { - types.push(Type { - offset: -1, - size: elem_type.size, - kind: elem_type.kind, - child: elem_type.child.clone(), - }); - } - - return TypeTree(types); - } - } - - if ty.is_slice() { - if let ty::Slice(element_ty) = ty.kind() { - let element_tree = - typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false); - return element_tree; - } - } - - if let ty::Tuple(tuple_types) = ty.kind() { - if tuple_types.is_empty() { - return TypeTree::new(); - } - - let mut types = Vec::new(); - let mut current_offset = 0; - - for tuple_ty in tuple_types.iter() { - let element_tree = - typetree_from_ty_impl_inner(tcx, tuple_ty, depth + 1, visited, false); - - let element_layout = tcx - .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tuple_ty)) - .ok() - .map(|layout| layout.size.bytes_usize()) - .unwrap_or(0); - - for elem_type in &element_tree.0 { - types.push(Type { - offset: if elem_type.offset == -1 { - current_offset as isize - } else { - current_offset as isize + elem_type.offset - }, - size: elem_type.size, - kind: elem_type.kind, - child: elem_type.child.clone(), - }); - } - - current_offset += element_layout; - } - - return TypeTree(types); - } - - if let ty::Adt(adt_def, args) = ty.kind() { - if adt_def.is_struct() { - let struct_layout = - tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)); - if let Ok(layout) = struct_layout { - let mut types = Vec::new(); - - for (field_idx, field_def) in adt_def.all_fields().enumerate() { - let field_ty = field_def.ty(tcx, args); - let field_tree = typetree_from_ty_impl_inner( - tcx, - field_ty.skip_norm_wip(), - depth + 1, - visited, - false, - ); - - let field_offset = layout.fields.offset(field_idx).bytes_usize(); - - for elem_type in &field_tree.0 { - types.push(Type { - offset: if elem_type.offset == -1 { - field_offset as isize - } else { - field_offset as isize + elem_type.offset - }, - size: elem_type.size, - kind: elem_type.kind, - child: elem_type.child.clone(), - }); - } - } - - return TypeTree(types); - } - } - } - - TypeTree::new() -} diff --git a/compiler/rustc_middle/src/ty/typetree.rs b/compiler/rustc_middle/src/ty/typetree.rs new file mode 100644 index 0000000000000..9e941bdb849ec --- /dev/null +++ b/compiler/rustc_middle/src/ty/typetree.rs @@ -0,0 +1,208 @@ +use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree}; +use tracing::trace; + +use crate::ty::context::TyCtxt; +use crate::ty::{self, Ty}; + +/// Generate TypeTree information for autodiff. +/// This function creates TypeTree metadata that describes the memory layout +/// of function parameters and return types for Enzyme autodiff. +pub fn fnc_typetrees<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>) -> FncTree { + // Check if TypeTrees are disabled via NoTT flag + if tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::NoTT) { + return FncTree { args: vec![], ret: TypeTree::new() }; + } + + // Check if this is actually a function type + if !fn_ty.is_fn() { + return FncTree { args: vec![], ret: TypeTree::new() }; + } + + // Get the function signature + let fn_sig = fn_ty.fn_sig(tcx); + let sig = tcx.instantiate_bound_regions_with_erased(fn_sig); + + // Create TypeTrees for each input parameter + let mut args = vec![]; + for ty in sig.inputs().iter() { + let type_tree = typetree_from_ty(tcx, *ty); + args.push(type_tree); + } + + // Create TypeTree for return type + let ret = typetree_from_ty(tcx, sig.output()); + + FncTree { args, ret } +} + +/// Generate a TypeTree for a specific type. +/// Mainly a convenience wrapper around the actual implementation. +pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { + let mut visited = Vec::new(); + typetree_from_ty_impl_inner(tcx, ty, 0, &mut visited, false) +} + +/// Maximum recursion depth for TypeTree generation to prevent stack overflow +/// from pathological deeply nested types. Combined with cycle detection. +const MAX_TYPETREE_DEPTH: usize = 6; + +/// Internal implementation with context about whether this is for a reference target. +fn typetree_from_ty_impl_inner<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + depth: usize, + visited: &mut Vec>, + is_reference_target: bool, +) -> TypeTree { + if depth >= MAX_TYPETREE_DEPTH { + trace!("typetree depth limit {} reached for type: {}", MAX_TYPETREE_DEPTH, ty); + return TypeTree::new(); + } + + if visited.contains(&ty) { + return TypeTree::new(); + } + visited.push(ty); + + if ty.is_scalar() { + let (kind, size) = if ty.is_integral() || ty.is_char() || ty.is_bool() { + (Kind::Integer, ty.primitive_size(tcx).bytes_usize()) + } else if ty.is_floating_point() { + match ty { + x if x == tcx.types.f16 => (Kind::Half, 2), + x if x == tcx.types.f32 => (Kind::Float, 4), + x if x == tcx.types.f64 => (Kind::Double, 8), + x if x == tcx.types.f128 => (Kind::F128, 16), + _ => (Kind::Integer, 0), + } + } else { + (Kind::Integer, 0) + }; + + // Use offset 0 for scalars that are direct targets of references (like &f64) + // Use offset -1 for scalars used directly (like function return types) + let offset = if is_reference_target && !ty.is_array() { 0 } else { -1 }; + return TypeTree(vec![Type { offset, size, kind, child: TypeTree::new() }]); + } + + if ty.is_ref() || ty.is_raw_ptr() || ty.is_box() { + let Some(inner_ty) = ty.builtin_deref(true) else { + return TypeTree::new(); + }; + + let child = typetree_from_ty_impl_inner(tcx, inner_ty, depth + 1, visited, true); + return TypeTree(vec![Type { + offset: -1, + size: tcx.data_layout.pointer_size().bytes_usize(), + kind: Kind::Pointer, + child, + }]); + } + + if ty.is_array() { + if let ty::Array(element_ty, len_const) = ty.kind() { + let len = len_const.try_to_target_usize(tcx).unwrap_or(0); + if len == 0 { + return TypeTree::new(); + } + let element_tree = + typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false); + let mut types = Vec::new(); + for elem_type in &element_tree.0 { + types.push(Type { + offset: -1, + size: elem_type.size, + kind: elem_type.kind, + child: elem_type.child.clone(), + }); + } + + return TypeTree(types); + } + } + + if ty.is_slice() { + if let ty::Slice(element_ty) = ty.kind() { + let element_tree = + typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false); + return element_tree; + } + } + + if let ty::Tuple(tuple_types) = ty.kind() { + if tuple_types.is_empty() { + return TypeTree::new(); + } + + let mut types = Vec::new(); + let mut current_offset = 0; + + for tuple_ty in tuple_types.iter() { + let element_tree = + typetree_from_ty_impl_inner(tcx, tuple_ty, depth + 1, visited, false); + + let element_layout = tcx + .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tuple_ty)) + .ok() + .map(|layout| layout.size.bytes_usize()) + .unwrap_or(0); + + for elem_type in &element_tree.0 { + types.push(Type { + offset: if elem_type.offset == -1 { + current_offset as isize + } else { + current_offset as isize + elem_type.offset + }, + size: elem_type.size, + kind: elem_type.kind, + child: elem_type.child.clone(), + }); + } + + current_offset += element_layout; + } + + return TypeTree(types); + } + + if let ty::Adt(adt_def, args) = ty.kind() { + if adt_def.is_struct() { + let struct_layout = + tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)); + if let Ok(layout) = struct_layout { + let mut types = Vec::new(); + + for (field_idx, field_def) in adt_def.all_fields().enumerate() { + let field_ty = field_def.ty(tcx, args); + let field_tree = typetree_from_ty_impl_inner( + tcx, + field_ty.skip_norm_wip(), + depth + 1, + visited, + false, + ); + + let field_offset = layout.fields.offset(field_idx).bytes_usize(); + + for elem_type in &field_tree.0 { + types.push(Type { + offset: if elem_type.offset == -1 { + field_offset as isize + } else { + field_offset as isize + elem_type.offset + }, + size: elem_type.size, + kind: elem_type.kind, + child: elem_type.child.clone(), + }); + } + } + + return TypeTree(types); + } + } + } + + TypeTree::new() +} diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 778890ace6362..6d9c11789f2b7 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -607,6 +607,12 @@ impl<'tcx> AutoTraitFinder<'tcx> { // if possible. predicates.push_back(bound_predicate.rebind(p)); } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(p)) => { + let p = bound_predicate.rebind(p); + if self.is_param_no_infer(p.skip_binder().trait_ref.args) && is_new_pred { + self.add_user_pred(computed_preds, predicate); + } + } ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => { let p = bound_predicate.rebind(p); debug!( @@ -811,8 +817,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { | ty::PredicateKind::DynCompatible(..) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) - | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => {} + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) => {} ty::PredicateKind::Ambiguous => return false, // FIXME(generic_const_exprs): you can absolutely add this as a where clauses diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 9889f32c502ad..9bc8b0d128d2b 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1,7 +1,7 @@ use crate::any::type_name; use crate::clone::TrivialClone; use crate::marker::Destruct; -use crate::mem::ManuallyDrop; +use crate::mem::{ManuallyDrop, transmute_neo}; use crate::{fmt, intrinsics, ptr, slice}; /// A wrapper type to construct uninitialized instances of `T`. @@ -724,9 +724,9 @@ impl MaybeUninit { // This also means that `self` must be a `value` variant. unsafe { intrinsics::assert_inhabited::(); - // We do this via a raw ptr read instead of `ManuallyDrop::into_inner` so that there's + // We do this via a transmute instead of `ManuallyDrop::into_inner` so that there's // no trace of `ManuallyDrop` in Miri's error messages here. - (&raw const self.value).cast::().read() + transmute_neo(self) } } diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 828572df6968b..a00a6c4783ba6 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1196,6 +1196,9 @@ pub const unsafe fn transmute_prefix(src: Src) -> Dst { /// New version of `transmute`, exposed under this name so it can be iterated upon /// without risking breakage to uses of "real" transmute. /// +/// Uses a `const`-`assert` to check the sizes instead of typeck hacks, +/// but is semantially identical to `transmute` otherwise. +/// /// It will not be stabilized under this name. /// /// # Examples @@ -1214,6 +1217,8 @@ pub const unsafe fn transmute_prefix(src: Src) -> Dst { /// unsafe { transmute_neo::(123) }; /// ``` #[unstable(feature = "transmute_neo", issue = "155079")] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[inline] pub const unsafe fn transmute_neo(src: Src) -> Dst { const { assert!(Src::SIZE == Dst::SIZE) }; diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 6bd3d9a290e44..18cc4ca74f682 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -415,6 +415,7 @@ pub mod panic_count { // // This also updates thread-local state to keep track of whether a panic // hook is currently executing. + #[must_use = "MustAbort may not be ignored"] pub fn increase(run_panic_hook: bool) -> Option { let global_count = GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed); if global_count & ALWAYS_ABORT_FLAG != 0 { @@ -843,7 +844,18 @@ fn panic_with_hook( /// It just forwards the payload to the panic runtime. #[cfg_attr(panic = "immediate-abort", inline)] pub fn resume_unwind(payload: Box) -> ! { - panic_count::increase(false); + if let Some(must_abort) = panic_count::increase(false) { + match must_abort { + panic_count::MustAbort::PanicInHook => { + rtprintpanic!("thread panicked while processing panic. aborting.\n"); + } + panic_count::MustAbort::AlwaysAbort => { + rtprintpanic!("aborting due to panic\n"); + } + } + + crate::process::abort(); + } struct RewrapBox(Box); diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 9891f54907c7c..087a395a067f0 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -169,7 +169,7 @@ pub fn prebuilt_llvm_config( generate_smart_stamp_hash( builder, &builder.config.src.join("src/llvm-project"), - &builder.llvm_cache_key(), + builder.in_tree_llvm_info.sha().unwrap_or_default(), ) }); @@ -999,7 +999,7 @@ impl Step for OmpOffload { generate_smart_stamp_hash( builder, &builder.config.src.join("src/llvm-project/offload"), - &builder.llvm_cache_key(), + builder.in_tree_llvm_info.sha().unwrap_or_default(), ) }); let stamp = BuildStamp::new(&out_dir).with_prefix("offload").add_stamp(smart_stamp_hash); @@ -1166,8 +1166,8 @@ impl Step for Enzyme { // Enzyme links against LLVM. If we update the LLVM submodule libLLVM might get a new // version number, in which case Enzyme will now fail to find LLVM. By including the LLVM // hash into the Enzyme hash we force a rebuild of Enzyme when updating LLVM. - let enzyme_hash_input = - builder.llvm_cache_key() + builder.enzyme_info.sha().unwrap_or_default(); + let enzyme_hash_input = builder.in_tree_llvm_info.sha().unwrap_or_default().to_owned() + + builder.enzyme_info.sha().unwrap_or_default(); static STAMP_HASH_MEMO: OnceLock = OnceLock::new(); let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| { @@ -1430,7 +1430,7 @@ impl Step for Sanitizers { generate_smart_stamp_hash( builder, &builder.config.src.join("src/llvm-project/compiler-rt"), - &builder.llvm_cache_key(), + builder.in_tree_llvm_info.sha().unwrap_or_default(), ) }); diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 6b2f526a8226d..048fcc5c80145 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1687,16 +1687,6 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler pub fn exec_ctx(&self) -> &ExecutionContext { &self.config.exec_ctx } - - /// When to rebuild LLVM. Currently includes the LLVM commit hash and the configuration from - /// bootstrap.toml. - pub(crate) fn llvm_cache_key(&self) -> String { - format!( - "sha={sha}\nkey={key}", - sha = self.in_tree_llvm_info.sha().unwrap_or_default(), - key = self.config.llvm_cache_key, - ) - } } /// Return qualified step name, e.g. `compile::Rustc`. diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 0297907057ef9..5a9c7264c006f 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -156,9 +156,6 @@ pub struct Config { pub backtrace_on_ice: bool, // llvm codegen options - /// Key used to decide when to rebuild LLVM. - pub llvm_cache_key: String, - pub llvm_assertions: bool, pub llvm_tests: bool, pub llvm_enzyme: bool, @@ -598,8 +595,6 @@ impl Config { rustflags: rust_rustflags, } = toml.rust.unwrap_or_default(); - let llvm = toml.llvm.unwrap_or_default(); - let llvm_cache_key = llvm.cache_key(); let Llvm { optimize: llvm_optimize, thin_lto: llvm_thin_lto, @@ -630,7 +625,7 @@ impl Config { enable_warnings: llvm_enable_warnings, download_ci_llvm: llvm_download_ci_llvm, build_config: llvm_build_config, - } = llvm; + } = toml.llvm.unwrap_or_default(); let Dist { sign_folder: dist_sign_folder, @@ -1406,7 +1401,6 @@ impl Config { llvm_assertions, llvm_bitcode_linker_enabled: rust_llvm_bitcode_linker.unwrap_or(false), llvm_build_config: llvm_build_config.clone().unwrap_or(Default::default()), - llvm_cache_key, llvm_cflags, llvm_clang: llvm_clang.unwrap_or(false), llvm_clang_cl, diff --git a/src/bootstrap/src/core/config/toml/llvm.rs b/src/bootstrap/src/core/config/toml/llvm.rs index e54c17c63aaae..5f08884e4ef71 100644 --- a/src/bootstrap/src/core/config/toml/llvm.rs +++ b/src/bootstrap/src/core/config/toml/llvm.rs @@ -43,93 +43,6 @@ define_config! { } } -impl Llvm { - /// A key that is used to determine whether LLVM should be rebuilt. - pub(crate) fn cache_key(&self) -> String { - let helper = || { - let mut key = String::with_capacity(512); - - let Self { - optimize, - thin_lto, - release_debuginfo, - assertions, - tests, - enzyme, - plugins, - static_libstdcpp, - libzstd, - ninja, - targets, - experimental_targets, - link_jobs: _, - link_shared, - version_suffix, - clang_cl, - cflags, - cxxflags, - ldflags, - use_libcxx, - use_linker, - allow_old_toolchain, - offload, - polly, - offload_clang_dir, - clang, - enable_warnings: _, - build_config, - download_ci_llvm: _, - } = self; - - use std::fmt::Write; - write!(key, "{:?}", optimize)?; - write!(key, "{:?}", thin_lto)?; - write!(key, "{:?}", release_debuginfo)?; - write!(key, "{:?}", assertions)?; - write!(key, "{:?}", tests)?; - write!(key, "{:?}", enzyme)?; - write!(key, "{:?}", plugins)?; - write!(key, "{:?}", static_libstdcpp)?; - write!(key, "{:?}", libzstd)?; - write!(key, "{:?}", ninja)?; - write!(key, "{:?}", targets)?; - write!(key, "{:?}", experimental_targets)?; - write!(key, "{:?}", link_shared)?; - write!(key, "{:?}", version_suffix)?; - write!(key, "{:?}", clang_cl)?; - write!(key, "{:?}", cflags)?; - write!(key, "{:?}", cxxflags)?; - write!(key, "{:?}", ldflags)?; - write!(key, "{:?}", use_libcxx)?; - write!(key, "{:?}", use_linker)?; - write!(key, "{:?}", allow_old_toolchain)?; - write!(key, "{:?}", offload)?; - write!(key, "{:?}", polly)?; - write!(key, "{:?}", offload_clang_dir)?; - write!(key, "{:?}", clang)?; - - match build_config { - None => { - write!(key, "None")?; - } - Some(c) => { - let mut build_config = c.iter().collect::>(); - build_config.sort(); - - for (k, v) in build_config { - write!(key, "{}: {}", k, v)?; - } - } - } - - Ok::<_, std::fmt::Error>(key) - }; - - // write! to a String always succeeds unless OOM. - helper().unwrap() - } -} - /// Compares the current `Llvm` options against those in the CI LLVM builder and detects any incompatible options. /// It does this by destructuring the `Llvm` instance to make sure every `Llvm` field is covered and not missing. #[cfg(not(test))] diff --git a/src/doc/unstable-book/src/compiler-flags/default-visibility.md b/src/doc/unstable-book/src/compiler-flags/default-visibility.md index ad9e5d84bba80..c401fd1d2d4dd 100644 --- a/src/doc/unstable-book/src/compiler-flags/default-visibility.md +++ b/src/doc/unstable-book/src/compiler-flags/default-visibility.md @@ -41,4 +41,4 @@ building for Linux. Other linkers such as LLD are not affected. Using `-Zdefault-visibility=interposable` will cause symbols to be emitted with "default" visibility. On platforms that support it, this makes it so that symbols can be interposed, which means that they can be overridden by symbols with the same name from the executable or by other -shared objects earier in the load order. +shared objects earlier in the load order. diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 117437f9b20bf..26cd2cd0b9698 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -5185,9 +5185,9 @@ function makeTab(tabNb, text, results, query, isTypeSearch, goToFirst) { errorReport.className = "error"; errorReport.innerHTML = `Query parser error: "${error.join("")}".`; search.insertBefore(errorReport, search.firstElementChild); - } else if (goToFirst || + } else if (tabNb === 0 && (goToFirst || (count === 1 && getSettingValue("go-to-only-result") === "true") - ) { + )) { // Needed to force re-execution of JS when coming back to a page. Let's take this // scenario as example: // diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index bfd2b240e4dac..4de9c46900a76 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -8,9 +8,9 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; use rustc_hir::attrs::{self, DeprecatedSince, DocAttribute, DocInline, HideOrShow}; -use rustc_hir::def::CtorKind; +use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::DefId; -use rustc_hir::{HeaderSafety, Safety}; +use rustc_hir::{HeaderSafety, Safety, find_attr}; use rustc_metadata::rendered_const; use rustc_middle::ty::TyCtxt; use rustc_middle::{bug, ty}; @@ -92,6 +92,9 @@ impl JsonRenderer<'_> { item.item_id.as_def_id() }; let stability = stability_def_id.and_then(|def_id| self.tcx.lookup_stability(def_id)); + let const_stability = item.item_id.as_def_id().and_then(|def_id| { + const_stability_for_def_id(self.tcx, def_id).map(|s| Box::new(s.into_json(self))) + }); Some(Item { id, @@ -100,6 +103,7 @@ impl JsonRenderer<'_> { span: span.and_then(|span| span.into_json(self)), visibility: visibility.into_json(self), stability: stability.map(|s| Box::new(s.into_json(self))), + const_stability, docs, attrs, deprecation: deprecation.into_json(self), @@ -246,6 +250,24 @@ impl FromClean for Stability { } } +impl FromClean for Stability { + fn from_clean(stab: &hir::ConstStability, _renderer: &JsonRenderer<'_>) -> Self { + let feature = stab.feature.to_string(); + let level = match stab.level { + hir::StabilityLevel::Stable { since, .. } => StabilityLevel::Stable { + since: match since { + hir::StableSince::Version(since) => Some(since.to_string()), + hir::StableSince::Current => Some(hir::RustcVersion::CURRENT.to_string()), + // Match rustdoc HTML: malformed stable-since values are omitted. + hir::StableSince::Err(_) => None, + }, + }, + hir::StabilityLevel::Unstable { .. } => StabilityLevel::Unstable, + }; + Stability { feature, level } + } +} + impl FromClean for Option> { fn from_clean(generic_args: &clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self { use clean::GenericArgs::*; @@ -948,6 +970,55 @@ impl FromClean for ItemKind { } } +fn const_stability_for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + if !tcx.is_conditionally_const(def_id) { + // The item cannot be conditionally-const. No const stability here. + // + // This includes associated consts, which are an interesting exception + // to the general rule that items inside `const impl` and `const trait` carry + // the const-stability of that block. Associated consts are already const, always. + return None; + } + + let const_stability = tcx.lookup_const_stability(def_id)?; + if find_attr!(tcx, def_id, RustcConstStability { .. }) { + // Direct const-stability attribute on the item itself. Return it directly. + return Some(const_stability); + } + + if const_stability.is_const_stable() { + // Items that are const-stable without an explicit attribute on their own item + // must be associated items inside `const trait` or `const impl`. + // We don't want to duplicate their parent item's const-stability attribute. + return None; + } + + // We're dealing with an item that is const-unstable, + // but doesn't have an explicit const-stability attribute on it. + // + // Today, this means one of two cases: + // - The item is enclosed within a `#[rustc_const_unstable]` block, + // like a `const trait` or `const impl`, in which case our query propagated the parent's + // const-instability info. This const-instability is desirable to place into JSON + // because *only some* associated items inside such a block are const-unstable. + // Associated consts are the exception, and were handled earlier. + // - The item is `#[unstable]` which implies it's const-unstable under the same feature, + // in which case we don't want to duplicate the existing stability attribute + // which would already appear in an adjacent field in the JSON anyway. + if let Some(parent_def_id) = tcx.opt_parent(def_id) + && matches!(tcx.def_kind(parent_def_id), DefKind::Trait | DefKind::Impl { .. }) + && tcx.lookup_const_stability(parent_def_id) == Some(const_stability) + { + Some(const_stability) + } else { + std::debug_assert_matches!( + tcx.lookup_stability(def_id).map(|s| s.level), + Some(hir::StabilityLevel::Unstable { .. }) + ); + None + } +} + /// Maybe convert a attribute from hir to json. /// /// Returns `None` if the attribute shouldn't be in the output. @@ -966,6 +1037,7 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) vec![match kind { AK::Deprecated { .. } => return Vec::new(), // Handled separately into Item::deprecation. AK::Stability { .. } => return Vec::new(), // Handled separately into Item::stability + AK::RustcConstStability { .. } => return Vec::new(), // Handled separately into Item::const_stability. AK::DocComment { .. } => unreachable!("doc comments stripped out earlier"), diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 5a40b03dbd5e8..433e4487eb9d5 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -361,6 +361,6 @@ mod size_asserts { // tidy-alphabetical-end // These contains a `PathBuf`, which is different sizes on different OSes. - static_assert_size!(Item, 536 + size_of::()); + static_assert_size!(Item, 544 + size_of::()); static_assert_size!(ExternalCrate, 48 + size_of::()); } diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index daaf30e25d15d..e6af2fa04bb10 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -114,8 +114,8 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc // will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line // are deliberately not in a doc comment, because they need not be in public docs.) // -// Latest feature: Add `Item::stability`. -pub const FORMAT_VERSION: u32 = 58; +// Latest feature: Add `Item::const_stability`. +pub const FORMAT_VERSION: u32 = 59; /// The root of the emitted JSON blob. /// @@ -286,6 +286,8 @@ pub struct Item { /// - `#[doc = "Doc Comment"]` or `/// Doc comment`: see [`Self::docs`] instead. /// - `#[deprecated]` attributes: see the [`Self::deprecation`] field instead. /// - `#[stable]` and `#[unstable]` attributes: see the [`Self::stability`] field instead. + /// - `#[rustc_const_stable]` and `#[rustc_const_unstable]` attributes: + /// see the [`Self::const_stability`] field instead. /// /// Attributes appear in pretty-printed Rust form, regardless of their formatting /// in the original source code. For example: @@ -319,20 +321,31 @@ pub struct Item { /// most ordinary third-party crates usually have no data here. pub stability: Option>, + /// Stability information for using this item in const contexts, if any. + /// + /// This is separate from [`Self::stability`]. An item can be stable as regular API while its + /// const use is unstable. An unstable item may have no separate const-stability value here. + /// + /// This field is only populated for item kinds whose const behavior can have separate + /// stability information, such as const functions, const traits, const trait impls, + /// and associated items whose const behavior is controlled by a const trait or const impl. + pub const_stability: Option>, + /// The type-specific fields describing this item. pub inner: ItemEnum, } /// Stability information for an item. /// -/// This only refers to regular item stability: whether the item is stable or unstable -/// as represented by the `#[stable]` or `#[unstable]` attributes. -/// Const stability and default-body stability are different things and not captured here. +/// In [`Item::stability`], this refers to regular item stability: whether the item is +/// stable or unstable as represented by the `#[stable]` or `#[unstable]` attributes. +/// In [`Item::const_stability`], this refers to using the item in const contexts, +/// as represented by `#[rustc_const_stable]` or `#[rustc_const_unstable]`. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "rkyv_0_8", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] #[cfg_attr(feature = "rkyv_0_8", rkyv(derive(Debug)))] pub struct Stability { - /// The stability feature associated with this item. + /// The feature associated with this stability record. /// /// For unstable items, this is the feature gate associated with the item. /// For stable items, this is the historical label recorded when the item was stabilized. @@ -342,7 +355,6 @@ pub struct Stability { pub level: StabilityLevel, } -/// Whether an item is stable or unstable as regular public API. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "rkyv_0_8", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] #[cfg_attr(feature = "rkyv_0_8", rkyv(derive(Debug)))] @@ -365,6 +377,8 @@ pub enum StabilityLevel { /// - `#[doc = "Doc Comment"]` or `/// Doc comment`. These are in [`Item::docs`] instead. /// - `#[deprecated]`. These are in [`Item::deprecation`] instead. /// - `#[stable]` and `#[unstable]`. These are in [`Item::stability`] instead. +/// - `#[rustc_const_stable]` and `#[rustc_const_unstable]`. These are in +/// [`Item::const_stability`] instead. pub enum Attribute { /// `#[non_exhaustive]` NonExhaustive, diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs index cd64bdb94ffbe..ff2fae157f04c 100644 --- a/src/tools/jsondoclint/src/validator/tests.rs +++ b/src/tools/jsondoclint/src/validator/tests.rs @@ -34,6 +34,7 @@ fn errors_on_missing_links() { attrs: vec![], deprecation: None, stability: None, + const_stability: None, inner: ItemEnum::Module(Module { is_crate: true, items: vec![], @@ -83,6 +84,7 @@ fn errors_on_local_in_paths_and_not_index() { attrs: Vec::new(), deprecation: None, stability: None, + const_stability: None, inner: ItemEnum::Module(Module { is_crate: true, items: vec![Id(1)], @@ -103,6 +105,7 @@ fn errors_on_local_in_paths_and_not_index() { attrs: Vec::new(), deprecation: None, stability: None, + const_stability: None, inner: ItemEnum::Primitive(Primitive { name: "i32".to_owned(), impls: vec![] }), }, ), @@ -157,6 +160,7 @@ fn errors_on_missing_path() { attrs: Vec::new(), deprecation: None, stability: None, + const_stability: None, inner: ItemEnum::Module(Module { is_crate: true, items: vec![Id(1), Id(2)], @@ -177,6 +181,7 @@ fn errors_on_missing_path() { attrs: Vec::new(), deprecation: None, stability: None, + const_stability: None, inner: ItemEnum::Struct(Struct { kind: StructKind::Unit, generics: generics.clone(), @@ -197,6 +202,7 @@ fn errors_on_missing_path() { attrs: Vec::new(), deprecation: None, stability: None, + const_stability: None, inner: ItemEnum::Function(Function { sig: FunctionSignature { inputs: vec![], @@ -260,6 +266,7 @@ fn checks_local_crate_id_is_correct() { attrs: Vec::new(), deprecation: None, stability: None, + const_stability: None, inner: ItemEnum::Module(Module { is_crate: true, items: vec![], diff --git a/tests/codegen-llvm/scalable-vectors/memcpy.rs b/tests/codegen-llvm/scalable-vectors/memcpy.rs new file mode 100644 index 0000000000000..859f50b5b1178 --- /dev/null +++ b/tests/codegen-llvm/scalable-vectors/memcpy.rs @@ -0,0 +1,88 @@ +//@ compile-flags: -Copt-level=0 -Ctarget-feature=+sve,+sve2 +//@ edition: 2021 +//@ only-aarch64 + +#![crate_type = "lib"] +#![feature(simd_ffi)] +#![feature(stdarch_aarch64_sve)] + +// Test that `vscale * size` is generated for `memcpy` of scalable vector types + +use std::arch::aarch64::*; + +#[allow(improper_ctypes)] +unsafe extern "C" { + fn svcreate2_s16_wrapper(__dst: *mut svint16x2_t, x0: *const svint16_t, x1: *const svint16_t); + fn svcreate3_s16_wrapper( + __dst: *mut svint16x3_t, + x0: *const svint16_t, + x1: *const svint16_t, + x2: *const svint16_t, + ); + fn svcreate4_s16_wrapper( + __dst: *mut svint16x4_t, + x0: *const svint16_t, + x1: *const svint16_t, + x2: *const svint16_t, + x3: *const svint16_t, + ); +} + +pub fn foo(x0_val: svint16_t) { + unsafe { + let __pred = svptrue_b16(); + + let mut __c_return_value = std::mem::MaybeUninit::uninit(); + svcreate2_s16_wrapper(__c_return_value.as_mut_ptr(), &raw const x0_val, &raw const x0_val); + let __c_return_value = __c_return_value.assume_init(); + // CHECK: call void @svcreate2_s16_wrapper( + // CHECK-NEXT: [[VSCALE:%.*]] = call i64 @llvm.vscale.i64() + // CHECK-NEXT: [[VSCALE_SIZE:%.*]] = mul i64 [[VSCALE]], 32 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}, {{.*}}, i64 [[VSCALE_SIZE]] + + let eq = svcmpeq_s16( + __pred, + svget2_s16::<1>(__c_return_value), + svget2_s16::<1>(__c_return_value), + ); + + let mut __c_return_value = std::mem::MaybeUninit::uninit(); + svcreate3_s16_wrapper( + __c_return_value.as_mut_ptr(), + &raw const x0_val, + &raw const x0_val, + &raw const x0_val, + ); + let __c_return_value = __c_return_value.assume_init(); + // CHECK: call void @svcreate3_s16_wrapper( + // CHECK-NEXT: [[VSCALE:%.*]] = call i64 @llvm.vscale.i64() + // CHECK-NEXT: [[VSCALE_SIZE:%.*]] = mul i64 [[VSCALE]], 48 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}, {{.*}}, i64 [[VSCALE_SIZE]] + + let eq = svcmpeq_s16( + __pred, + svget3_s16::<2>(__c_return_value), + svget3_s16::<2>(__c_return_value), + ); + + let mut __c_return_value = std::mem::MaybeUninit::uninit(); + svcreate4_s16_wrapper( + __c_return_value.as_mut_ptr(), + &raw const x0_val, + &raw const x0_val, + &raw const x0_val, + &raw const x0_val, + ); + let __c_return_value = __c_return_value.assume_init(); + // CHECK: call void @svcreate4_s16_wrapper( + // CHECK-NEXT: [[VSCALE:%.*]] = call i64 @llvm.vscale.i64() + // CHECK-NEXT: [[VSCALE_SIZE:%.*]] = mul i64 [[VSCALE]], 64 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}, {{.*}}, i64 [[VSCALE_SIZE]] + + let eq = svcmpeq_s16( + __pred, + svget4_s16::<3>(__c_return_value), + svget4_s16::<3>(__c_return_value), + ); + } +} diff --git a/tests/run-make/cdylib-export-c-library-symbols/rmake.rs b/tests/run-make/cdylib-export-c-library-symbols/rmake.rs index b71ff7bd6d8d7..286eb95c41561 100644 --- a/tests/run-make/cdylib-export-c-library-symbols/rmake.rs +++ b/tests/run-make/cdylib-export-c-library-symbols/rmake.rs @@ -1,41 +1,54 @@ //@ ignore-nvptx64 //@ ignore-wasm //@ ignore-cross-compile -// FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple. -// Need to be resolved. -//@ ignore-windows -//@ ignore-apple // Reason: the compiled binary is executed use run_make_support::{ - build_native_static_lib, cc, dynamic_lib_name, is_aix, is_darwin, llvm_nm, rustc, + build_native_static_lib, cc, dynamic_lib_name, is_aix, is_darwin, is_windows, is_windows_msvc, + llvm_ar, llvm_nm, llvm_readobj, rfs, rustc, static_lib_name, }; fn main() { - cc().input("foo.c").arg("-c").out_exe("foo.o").run(); - build_native_static_lib("foo"); + let obj_file = if is_windows_msvc() { "foo.obj" } else { "foo.o" }; + cc().input("foo.c").arg("-c").arg("-fno-lto").out_exe(obj_file).run(); + llvm_ar().obj_to_ar().output_input(&static_lib_name("foo"), obj_file).run(); rustc().input("foo.rs").arg("-lstatic=foo").crate_type("cdylib").run(); - let out = llvm_nm() - .input(dynamic_lib_name("foo")) - .run() - .assert_stdout_not_contains_regex("T *my_function"); + if is_darwin() { + llvm_nm().input(dynamic_lib_name("foo")).run().assert_stdout_not_contains("T _my_function"); + } else if is_windows() { + llvm_readobj() + .arg("--coff-exports") + .input(dynamic_lib_name("foo")) + .run() + .assert_stdout_not_contains("my_function"); + } else { + llvm_nm().input(dynamic_lib_name("foo")).run().assert_stdout_not_contains("T my_function"); + } + + rfs::remove_file(dynamic_lib_name("foo")); rustc().input("foo_export.rs").arg("-lstatic:+export-symbols=foo").crate_type("cdylib").run(); if is_darwin() { - let out = llvm_nm() + llvm_nm() .input(dynamic_lib_name("foo_export")) .run() .assert_stdout_contains("T _my_function"); + } else if is_windows() { + llvm_readobj() + .arg("--coff-exports") + .input(dynamic_lib_name("foo_export")) + .run() + .assert_stdout_contains("my_function"); } else if is_aix() { - let out = llvm_nm() + llvm_nm() .input(dynamic_lib_name("foo_export")) .run() .assert_stdout_contains("T .my_function"); } else { - let out = llvm_nm() + llvm_nm() .input(dynamic_lib_name("foo_export")) .run() .assert_stdout_contains("T my_function"); diff --git a/tests/rustdoc-gui/setting-go-to-only-result.goml b/tests/rustdoc-gui/setting-go-to-only-result.goml index 72c1e2bf59ca4..f94f45fe102ef 100644 --- a/tests/rustdoc-gui/setting-go-to-only-result.goml +++ b/tests/rustdoc-gui/setting-go-to-only-result.goml @@ -33,16 +33,18 @@ assert-local-storage: {"rustdoc-go-to-only-result": "true"} go-to: "file://" + |DOC_PATH| + "/lib2/index.html" // We enter it into the search. +click: "#search-button" +wait-for: "#alternative-display .search-input" write-into: (".search-input", "HasALongTraitWithParams") wait-for-document-property: {"title": "HasALongTraitWithParams in lib2 - Rust"} -assert-window-property: ({"location": "/lib2/struct.HasALongTraitWithParams.html"}, ENDS_WITH) +assert-window-property: ({"location"."pathname": "/lib2/struct.HasALongTraitWithParams.html"}, ENDS_WITH) // Regression test for . // If "go-to-only-result" is enabled and you go back to history, it should not lead you back to the // page result again automatically. history-go-back: wait-for-document-property: {"title": "lib2 - Rust"} -assert-window-property: ({"location": "/lib2/index.html"}, ENDS_WITH) +assert-window-property: ({"location"."pathname": "/lib2/index.html"}, ENDS_WITH) // We try again to see if it goes to the only result go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=HasALongTraitWithParams" @@ -69,3 +71,43 @@ call-function: ("check-setting", { "storage_value": "false", "setting_attribute_value": "false", }) + +define-function: ( + "search-multiple-results", + [location], + block { + // We wait for the search to be done. + wait-for: ".search-form" + // We ensure the page didn't change. + assert-window-property: {"location"."pathname": |location|} + // We ensure that there is more than 0 results. + wait-for-text: ("#search-tabs > button:nth-child(1) .count", "(2)", CONTAINS) + wait-for-text: ("#search-tabs > button:nth-child(2) .count", "(1)", CONTAINS) + wait-for-text: ("#search-tabs > button:nth-child(3) .count", "(0)", CONTAINS) + }, +) + +// This is a regression test to ensure that it works when the setting is switched in-between. It +// could be reproduced by: +// 1. Doing a search (with "in parameters" or "in return types" having exactly one result) +// 2. Enabling the setting to true. +// 3. Reloading the page. + +// First we go to a different page. +go-to: "file://" + |DOC_PATH| + "/lib2/index.html" + +// The setting should be disabled. +assert-local-storage-false: {"rustdoc-go-to-only-result": "true"} +store-window-property: {"location"."pathname": location} +click: "#search-button" +wait-for: "#alternative-display .search-input" +write-into: (".search-input", "Whitespace") +call-function: ("search-multiple-results", {"location": |location|}) + +// We now toggle the setting and reload the page. +call-function: ("open-settings-menu", {}) +click: "#go-to-only-result" +assert-local-storage: {"rustdoc-go-to-only-result": "true"} + +reload: +call-function: ("search-multiple-results", {"location": |location|}) diff --git a/tests/rustdoc-json/attrs/stability/const.rs b/tests/rustdoc-json/attrs/stability/const.rs new file mode 100644 index 0000000000000..eccbc89d54b1c --- /dev/null +++ b/tests/rustdoc-json/attrs/stability/const.rs @@ -0,0 +1,55 @@ +#![feature(staged_api)] + +//@ is "$.index[?(@.name=='non_const_function')].const_stability" null +#[stable(feature = "non_const_function_feature", since = "0.9.0")] +pub fn non_const_function() {} + +//@ set stable_const_fn = "$.index[?(@.name=='stable_const_fn')].id" +//@ is "$.index[?(@.name=='stable_const_fn')].stability.level" '"stable"' +//@ is "$.index[?(@.name=='stable_const_fn')].stability.feature" '"stable_const_fn_feature"' +//@ is "$.index[?(@.name=='stable_const_fn')].stability.since" '"1.0.0"' +//@ is "$.index[?(@.name=='stable_const_fn')].const_stability.level" '"stable"' +//@ is "$.index[?(@.name=='stable_const_fn')].const_stability.feature" '"stable_const_fn_const_feature"' +//@ is "$.index[?(@.name=='stable_const_fn')].const_stability.since" '"1.1.0"' +//@ is "$.index[?(@.name=='stable_const_fn')].attrs" [] +#[stable(feature = "stable_const_fn_feature", since = "1.0.0")] +#[rustc_const_stable(feature = "stable_const_fn_const_feature", since = "1.1.0")] +pub const fn stable_const_fn() {} + +//@ is "$.index[?(@.name=='const_unstable_fn')].stability.level" '"stable"' +//@ is "$.index[?(@.name=='const_unstable_fn')].stability.feature" '"const_unstable_fn_feature"' +//@ is "$.index[?(@.name=='const_unstable_fn')].stability.since" '"2.0.0"' +//@ is "$.index[?(@.name=='const_unstable_fn')].const_stability.level" '"unstable"' +//@ is "$.index[?(@.name=='const_unstable_fn')].const_stability.feature" '"const_unstable_fn_const_feature"' +//@ !has "$.index[?(@.name=='const_unstable_fn')].const_stability.since" +//@ is "$.index[?(@.name=='const_unstable_fn')].attrs" [] +#[stable(feature = "const_unstable_fn_feature", since = "2.0.0")] +#[rustc_const_unstable(feature = "const_unstable_fn_const_feature", issue = "none")] +pub const fn const_unstable_fn() {} + +// Even when the item itself is unstable, if a separate const-stability attribute is present, +// that's a distinct fact possibly associated with a different feature gate. +// It should therefore be exposed on its own, instead of being collapsed into regular stability. +//@ is "$.index[?(@.name=='unstable_fn_with_explicit_const_gate')].stability.level" '"unstable"' +//@ is "$.index[?(@.name=='unstable_fn_with_explicit_const_gate')].stability.feature" '"unstable_fn_with_explicit_const_gate_feature"' +//@ !has "$.index[?(@.name=='unstable_fn_with_explicit_const_gate')].stability.since" +//@ is "$.index[?(@.name=='unstable_fn_with_explicit_const_gate')].const_stability.level" '"unstable"' +//@ is "$.index[?(@.name=='unstable_fn_with_explicit_const_gate')].const_stability.feature" '"explicit_const_gate_on_unstable_fn"' +//@ !has "$.index[?(@.name=='unstable_fn_with_explicit_const_gate')].const_stability.since" +//@ is "$.index[?(@.name=='unstable_fn_with_explicit_const_gate')].attrs" [] +#[unstable(feature = "unstable_fn_with_explicit_const_gate_feature", issue = "none")] +#[rustc_const_unstable(feature = "explicit_const_gate_on_unstable_fn", issue = "none")] +pub const fn unstable_fn_with_explicit_const_gate() {} + +// `lookup_const_stability` synthesizes a const-unstable record for this item from its regular +// instability. Rustdoc JSON filters that out because there is no separate const feature gate. +//@ is "$.index[?(@.name=='unstable_const_fn_without_const_gate')].stability.level" '"unstable"' +//@ is "$.index[?(@.name=='unstable_const_fn_without_const_gate')].stability.feature" '"unstable_const_fn_without_const_gate_feature"' +//@ is "$.index[?(@.name=='unstable_const_fn_without_const_gate')].const_stability" null +#[unstable(feature = "unstable_const_fn_without_const_gate_feature", issue = "none")] +pub const fn unstable_const_fn_without_const_gate() {} + +// The `Use` item describes the re-export. It doesn't have `const_stability` of its own. +//@ is "$.index[?(@.inner.use.name=='stable_const_fn_reexport')].const_stability" null +//@ is "$.index[?(@.inner.use.name=='stable_const_fn_reexport')].inner.use.id" $stable_const_fn +pub use stable_const_fn as stable_const_fn_reexport; diff --git a/tests/rustdoc-json/attrs/stability/const_traits.rs b/tests/rustdoc-json/attrs/stability/const_traits.rs new file mode 100644 index 0000000000000..3308a2bdbd05b --- /dev/null +++ b/tests/rustdoc-json/attrs/stability/const_traits.rs @@ -0,0 +1,159 @@ +#![feature(staged_api, const_trait_impl)] + +#[stable(feature = "const_trait_target_feature", since = "1.0.0")] +pub struct ConstTraitTarget; + +#[stable(feature = "const_stable_trait_target_feature", since = "1.0.0")] +pub struct ConstStableTraitTarget; + +#[stable(feature = "inherent_const_method_target_feature", since = "1.1.0")] +pub struct InherentConstMethodTarget; + +impl InherentConstMethodTarget { + // This item is plain unstable, not const-unstable specifically. + // Rustdoc JSON should populate `stability` only, not both it and `const_stability`. + // Duplicating the info would just add noise to the JSON file. + //@ is "$.index[?(@.name=='unstable_inherent_const_method_without_const_gate')].stability.level" '"unstable"' + //@ is "$.index[?(@.name=='unstable_inherent_const_method_without_const_gate')].stability.feature" '"unstable_inherent_const_method_without_const_gate"' + //@ is "$.index[?(@.name=='unstable_inherent_const_method_without_const_gate')].const_stability" null + #[unstable(feature = "unstable_inherent_const_method_without_const_gate", issue = "none")] + pub const fn unstable_inherent_const_method_without_const_gate(&self) {} +} + +//@ is "$.index[?(@.name=='ConstTrait')].stability.level" '"stable"' +//@ is "$.index[?(@.name=='ConstTrait')].stability.feature" '"const_trait_feature"' +//@ is "$.index[?(@.name=='ConstTrait')].stability.since" '"2.0.0"' +//@ is "$.index[?(@.name=='ConstTrait')].const_stability.level" '"unstable"' +//@ is "$.index[?(@.name=='ConstTrait')].const_stability.feature" '"const_trait_const_feature"' +//@ !has "$.index[?(@.name=='ConstTrait')].const_stability.since" +//@ is "$.index[?(@.name=='ConstTrait')].attrs" [] +#[stable(feature = "const_trait_feature", since = "2.0.0")] +#[rustc_const_unstable(feature = "const_trait_const_feature", issue = "none")] +pub const trait ConstTrait { + // This item is *not* usable in a const context, because its parent is const-unstable. + //@ is "$.index[?(@.docs=='assoc type inside const trait')].const_stability.level" '"unstable"' + //@ is "$.index[?(@.docs=='assoc type inside const trait')].const_stability.feature" '"const_trait_const_feature"' + //@ !has "$.index[?(@.docs=='assoc type inside const trait')].const_stability.since" + /// assoc type inside const trait + #[stable(feature = "trait_assoc_type_feature", since = "2.1.0")] + type TraitAssocType; + + // This item is also *not* usable in a const context, because its parent is const-unstable. + //@ is "$.index[?(@.docs=='method inside const trait')].const_stability.level" '"unstable"' + //@ is "$.index[?(@.docs=='method inside const trait')].const_stability.feature" '"const_trait_const_feature"' + //@ !has "$.index[?(@.docs=='method inside const trait')].const_stability.since" + /// method inside const trait + #[stable(feature = "trait_method_feature", since = "2.2.0")] + fn trait_method(&self); + + // An associated const can't be const-unstable. + // It is usable in a const context *regardless* of the fact its parent is const-unstable. + // See the UI test in `tests/ui/traits/const-traits/associated-const-stability.rs` for proof. + //@ is "$.index[?(@.docs=='assoc const inside const trait')].const_stability" null + /// assoc const inside const trait + #[stable(feature = "trait_assoc_const_feature", since = "2.3.0")] + const TRAIT_ASSOC_CONST: usize; +} + +//@ is "$.index[?(@.name=='ConstTraitWithUnstableMethod')].stability.level" '"stable"' +//@ is "$.index[?(@.name=='ConstTraitWithUnstableMethod')].const_stability.level" '"unstable"' +//@ is "$.index[?(@.name=='ConstTraitWithUnstableMethod')].const_stability.feature" '"const_trait_with_unstable_method_const_feature"' +#[stable(feature = "const_trait_with_unstable_method_feature", since = "2.4.0")] +#[rustc_const_unstable(feature = "const_trait_with_unstable_method_const_feature", issue = "none")] +pub const trait ConstTraitWithUnstableMethod { + // The method has its own regular instability, but no separate const-stability attribute. + // Even if it became stable, the parent's const-instability would still dominate and + // keep it const-unstable. Rustdoc JSON should expose the inherited const-instability. + //@ is "$.index[?(@.name=='unstable_method_without_const_gate')].stability.level" '"unstable"' + //@ is "$.index[?(@.name=='unstable_method_without_const_gate')].stability.feature" '"unstable_method_without_const_gate"' + //@ is "$.index[?(@.name=='unstable_method_without_const_gate')].const_stability.level" '"unstable"' + //@ is "$.index[?(@.name=='unstable_method_without_const_gate')].const_stability.feature" '"const_trait_with_unstable_method_const_feature"' + #[unstable(feature = "unstable_method_without_const_gate", issue = "none")] + fn unstable_method_without_const_gate(&self); +} + +//@ is "$.index[?(@.docs=='const trait impl')].stability.level" '"stable"' +//@ is "$.index[?(@.docs=='const trait impl')].stability.feature" '"const_trait_impl_feature"' +//@ is "$.index[?(@.docs=='const trait impl')].stability.since" '"3.0.0"' +//@ is "$.index[?(@.docs=='const trait impl')].const_stability.level" '"unstable"' +//@ is "$.index[?(@.docs=='const trait impl')].const_stability.feature" '"const_trait_impl_const_feature"' +//@ !has "$.index[?(@.docs=='const trait impl')].const_stability.since" +/// const trait impl +#[stable(feature = "const_trait_impl_feature", since = "3.0.0")] +#[rustc_const_unstable(feature = "const_trait_impl_const_feature", issue = "none")] +const impl ConstTrait for ConstTraitTarget { + // This item is *not* usable in a const context, because its parent is const-unstable. + //@ is "$.index[?(@.docs=='assoc type inside const trait impl')].const_stability.level" '"unstable"' + //@ is "$.index[?(@.docs=='assoc type inside const trait impl')].const_stability.feature" '"const_trait_impl_const_feature"' + //@ !has "$.index[?(@.docs=='assoc type inside const trait impl')].const_stability.since" + /// assoc type inside const trait impl + type TraitAssocType = usize; + + // This item is also *not* usable in a const context, because its parent is const-unstable. + //@ is "$.index[?(@.docs=='method inside const trait impl')].const_stability.level" '"unstable"' + //@ is "$.index[?(@.docs=='method inside const trait impl')].const_stability.feature" '"const_trait_impl_const_feature"' + //@ !has "$.index[?(@.docs=='method inside const trait impl')].const_stability.since" + /// method inside const trait impl + fn trait_method(&self) {} + + // An associated const can't be const-unstable. + // It is usable in a const context *regardless* of the fact its parent is const-unstable. + // See the UI test in `tests/ui/traits/const-traits/associated-const-stability.rs` for proof. + //@ is "$.index[?(@.docs=='assoc const inside const trait impl')].const_stability" null + /// assoc const inside const trait impl + const TRAIT_ASSOC_CONST: usize = 0; +} + +// The `const trait` is const-stable. We indicate that *once* on the trait, but since +// there are no special cases here (unlike in the const-unstable case with associated consts) +// we do not duplicate the const-stability to all the associated items so as not to bloat the JSON. +//@ is "$.index[?(@.name=='ConstStableTrait')].stability.level" '"stable"' +//@ is "$.index[?(@.name=='ConstStableTrait')].stability.feature" '"const_stable_trait_feature"' +//@ is "$.index[?(@.name=='ConstStableTrait')].stability.since" '"4.0.0"' +//@ is "$.index[?(@.name=='ConstStableTrait')].const_stability.level" '"stable"' +//@ is "$.index[?(@.name=='ConstStableTrait')].const_stability.since" '"4.0.0"' +//@ is "$.index[?(@.name=='ConstStableTrait')].const_stability.feature" '"const_stable_trait_const_feature"' +#[stable(feature = "const_stable_trait_feature", since = "4.0.0")] +#[rustc_const_stable(feature = "const_stable_trait_const_feature", since = "4.0.0")] +pub const trait ConstStableTrait { + //@ is "$.index[?(@.docs=='assoc type inside const-stable trait')].const_stability" null + /// assoc type inside const-stable trait + #[stable(feature = "stable_trait_assoc_type_feature", since = "4.1.0")] + type StableTraitAssocType; + + //@ is "$.index[?(@.docs=='method inside const-stable trait')].const_stability" null + /// method inside const-stable trait + #[stable(feature = "stable_trait_method_feature", since = "4.2.0")] + fn stable_trait_method(&self); + + //@ is "$.index[?(@.docs=='assoc const inside const-stable trait')].const_stability" null + /// assoc const inside const-stable trait + #[stable(feature = "stable_trait_assoc_const_feature", since = "4.3.0")] + const STABLE_TRAIT_ASSOC_CONST: usize; +} + +// The `const impl` is const-stable. We indicate that *once* on the impl itself, but since +// there are no special cases here (unlike in the const-unstable case with associated consts) +// we do not duplicate the const-stability to all the associated items so as not to bloat the JSON. +//@ is "$.index[?(@.docs=='const-stable trait impl')].stability.level" '"stable"' +//@ is "$.index[?(@.docs=='const-stable trait impl')].stability.feature" '"const_stable_trait_impl_feature"' +//@ is "$.index[?(@.docs=='const-stable trait impl')].stability.since" '"5.0.0"' +//@ is "$.index[?(@.docs=='const-stable trait impl')].const_stability.level" '"stable"' +//@ is "$.index[?(@.docs=='const-stable trait impl')].const_stability.since" '"5.0.0"' +//@ is "$.index[?(@.docs=='const-stable trait impl')].const_stability.feature" '"const_stable_trait_impl_const_feature"' +/// const-stable trait impl +#[stable(feature = "const_stable_trait_impl_feature", since = "5.0.0")] +#[rustc_const_stable(feature = "const_stable_trait_impl_const_feature", since = "5.0.0")] +const impl ConstStableTrait for ConstStableTraitTarget { + //@ is "$.index[?(@.docs=='assoc type inside const-stable trait impl')].const_stability" null + /// assoc type inside const-stable trait impl + type StableTraitAssocType = usize; + + //@ is "$.index[?(@.docs=='method inside const-stable trait impl')].const_stability" null + /// method inside const-stable trait impl + fn stable_trait_method(&self) {} + + //@ is "$.index[?(@.docs=='assoc const inside const-stable trait impl')].const_stability" null + /// assoc const inside const-stable trait impl + const STABLE_TRAIT_ASSOC_CONST: usize = 1; +} diff --git a/tests/rustdoc-ui/ice-bug-report-url.rs b/tests/rustdoc-ui/ice-bug-report-url.rs index 2e384fa1be623..5aa82c438fa36 100644 --- a/tests/rustdoc-ui/ice-bug-report-url.rs +++ b/tests/rustdoc-ui/ice-bug-report-url.rs @@ -4,7 +4,7 @@ //@ normalize-stderr: "note: compiler flags.*\n\n" -> "" //@ normalize-stderr: "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}" -//@ normalize-stderr: "thread.*panicked at compiler.*" -> "" +//@ normalize-stderr: "thread 'rustc'.*panicked.*:\n.*\n" -> "" //@ normalize-stderr: " +\d{1,}: .*\n" -> "" //@ normalize-stderr: " + at .*\n" -> "" //@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" diff --git a/tests/rustdoc-ui/ice-bug-report-url.stderr b/tests/rustdoc-ui/ice-bug-report-url.stderr index bc46b226a703e..01dac0433e630 100644 --- a/tests/rustdoc-ui/ice-bug-report-url.stderr +++ b/tests/rustdoc-ui/ice-bug-report-url.stderr @@ -5,8 +5,6 @@ LL | fn wrong() | ^ expected one of `->`, `where`, or `{` - -aborting due to `-Z treat-err-as-bug=1` stack backtrace: error: the compiler unexpectedly panicked. This is a bug diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/const-trait-bound.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/const-trait-bound.rs new file mode 100644 index 0000000000000..858e38b08fcd1 --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/const-trait-bound.rs @@ -0,0 +1,14 @@ +// We used to ICE here while trying to synthesize auto trait impls. +//@ check-pass + +#![feature(const_default)] +#![feature(const_trait_impl)] + +pub struct Inner(T); + +pub struct Outer(Inner); + +impl Unpin for Inner +where + T: const std::default::Default, +{} diff --git a/tests/ui/issues/issue-26997.rs b/tests/ui/abi/extern/extern-c-method-return-struct.rs similarity index 75% rename from tests/ui/issues/issue-26997.rs rename to tests/ui/abi/extern/extern-c-method-return-struct.rs index 5441dc68bae6c..679f0b37758ab 100644 --- a/tests/ui/issues/issue-26997.rs +++ b/tests/ui/abi/extern/extern-c-method-return-struct.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ build-pass + #![allow(dead_code)] pub struct Foo { x: isize, diff --git a/tests/ui/async-await/higher-ranked-auto-trait-18.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-18.no_assumptions.stderr new file mode 100644 index 0000000000000..5a4c99acd0d43 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-18.no_assumptions.stderr @@ -0,0 +1,13 @@ +error: higher-ranked lifetime error + --> $DIR/higher-ranked-auto-trait-18.rs:69:5 + | +LL | / require_send(async { +LL | | let r = Receiver::> { _value: None, _not_sync: PhantomData }; +LL | | let _ = r.await; +LL | | }); + | |______^ + | + = note: could not prove `{async block@$DIR/higher-ranked-auto-trait-18.rs:69:18: 69:23}: Send` + +error: aborting due to 1 previous error + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-18.rs b/tests/ui/async-await/higher-ranked-auto-trait-18.rs new file mode 100644 index 0000000000000..3839fd86cfeba --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-18.rs @@ -0,0 +1,79 @@ +// Repro for a bug where `PhantomData<*mut ()>` + `unsafe impl Send` fails to prove +// `Send` for an async block when the type parameter is a trait object (`dyn Trait`). +// +// The static assertion `assert_send::>>()` succeeds, +// but the same type captured across an `.await` in an async block does not. +// This is because MIR erases the `'static` lifetime from `dyn MyTrait + 'static`, +// and the auto-trait analysis cannot recover the `T: 'static` bound without +// higher-ranked assumptions. +// +// Using `PhantomData>` instead of `PhantomData<*mut ()>` avoids the +// issue because `Cell<()>` is natively `Send`, so no `unsafe impl` (with its +// `T: 'static` bound) is needed. +// +// See . +//@ edition: 2021 +//@ revisions: assumptions no_assumptions +//@[assumptions] compile-flags: -Zhigher-ranked-assumptions +//@[assumptions] check-pass +//@[no_assumptions] known-bug: #110338 + +use std::cell::Cell; +use std::future::Future; +use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; + +// --- PhantomData<*mut ()> version: needs `unsafe impl Send` --- + +struct Receiver { + _value: Option, + _not_sync: PhantomData<*mut ()>, +} + +unsafe impl Send for Receiver {} + +impl Future for Receiver { + type Output = T; + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Pending + } +} + +// --- PhantomData> version: auto-derived Send works --- + +struct ReceiverCell { + _value: Option, + _not_sync: PhantomData>, +} + +impl Future for ReceiverCell { + type Output = T; + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Pending + } +} + +trait MyTrait: Send {} + +fn require_send(_f: F) {} + +fn main() { + // PhantomData<*mut ()> + concrete type: always works. + require_send(async { + let r = Receiver:: { _value: None, _not_sync: PhantomData }; + let _ = r.await; + }); + + // PhantomData<*mut ()> + dyn Trait: fails without higher-ranked assumptions. + require_send(async { + let r = Receiver::> { _value: None, _not_sync: PhantomData }; + let _ = r.await; + }); + + // PhantomData> + dyn Trait: always works (auto-derived Send). + require_send(async { + let r = ReceiverCell::> { _value: None, _not_sync: PhantomData }; + let _ = r.await; + }); +} diff --git a/tests/ui/issues/issue-27054-primitive-binary-ops.rs b/tests/ui/binop/binop-mutate-lhs-in-rhs.rs similarity index 52% rename from tests/ui/issues/issue-27054-primitive-binary-ops.rs rename to tests/ui/binop/binop-mutate-lhs-in-rhs.rs index 96b0edf838ebf..d3e8166050513 100644 --- a/tests/ui/issues/issue-27054-primitive-binary-ops.rs +++ b/tests/ui/binop/binop-mutate-lhs-in-rhs.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ run-pass + fn main() { let x = &mut 1; assert_eq!(*x + { *x=2; 1 }, 2); diff --git a/tests/ui/issues/issue-27105.rs b/tests/ui/coercion/infer-coerce-unsized-trait-object.rs similarity index 80% rename from tests/ui/issues/issue-27105.rs rename to tests/ui/coercion/infer-coerce-unsized-trait-object.rs index 3308f3e8e010d..dfb9ce361f481 100644 --- a/tests/ui/issues/issue-27105.rs +++ b/tests/ui/coercion/infer-coerce-unsized-trait-object.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ check-pass + use std::cell::RefCell; use std::rc::Rc; diff --git a/tests/ui/issues/issue-26709.rs b/tests/ui/drop/dst-coerce-unsized-drop.rs similarity index 58% rename from tests/ui/issues/issue-26709.rs rename to tests/ui/drop/dst-coerce-unsized-drop.rs index 5c31bd6562887..88dbd77c3d33d 100644 --- a/tests/ui/issues/issue-26709.rs +++ b/tests/ui/drop/dst-coerce-unsized-drop.rs @@ -1,4 +1,10 @@ +//! Regression test for . +//! Test value is still getting dropped after unsized coerce. +//! +//! Fat pointer was passed in two immediate args, but the drop invocation +//! used to accept one, which led to ICE. //@ run-pass + struct Wrapper<'a, T: ?Sized>(&'a mut i32, #[allow(dead_code)] T); impl<'a, T: ?Sized> Drop for Wrapper<'a, T> { diff --git a/tests/ui/issues/issue-26655.rs b/tests/ui/drop/enum-destructor-on-unwind.rs similarity index 78% rename from tests/ui/issues/issue-26655.rs rename to tests/ui/drop/enum-destructor-on-unwind.rs index 32c4b33a8c967..9d2655564ca8c 100644 --- a/tests/ui/issues/issue-26655.rs +++ b/tests/ui/drop/enum-destructor-on-unwind.rs @@ -1,10 +1,10 @@ +//! Regression test for . +//! Check that the destructors of simple enums are run on unwinding. //@ run-pass //@ needs-unwind //@ needs-threads //@ ignore-backends: gcc -// Check that the destructors of simple enums are run on unwinding - use std::sync::atomic::{Ordering, AtomicUsize}; use std::thread; diff --git a/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.rs b/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.rs new file mode 100644 index 0000000000000..6f3ec783a4bfe --- /dev/null +++ b/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.rs @@ -0,0 +1,16 @@ +// extern "rust-call" untuples the first (and only, except self) argument, this requires additional +// support for tail calls to work correctly. Since there doesn't seem to be a usecase for tail +// calling extern "rust-call" functions (extern "rust-call" only shows up in closures, which we +// disallow tail-calling anyway), we just disallow that. +// +// @ ignore-backends: gcc +#![expect(incomplete_features)] +#![feature(explicit_tail_calls)] +#![feature(unboxed_closures)] + +extern "rust-call" fn a(_: ()) { + become a(()); + //~^ error: ABI does not support guaranteed tail calls +} + +fn main() {} diff --git a/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.stderr b/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.stderr new file mode 100644 index 0000000000000..6662836672097 --- /dev/null +++ b/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.stderr @@ -0,0 +1,10 @@ +error: ABI does not support guaranteed tail calls + --> $DIR/rust-call.rs:12:5 + | +LL | become a(()); + | ^^^^^^^^^^^^ + | + = note: `become` is not supported for `extern "rust-call"` functions + +error: aborting due to 1 previous error + diff --git a/tests/ui/issues/issue-26948.rs b/tests/ui/functional-struct-update/fru-on-enum-variant.rs similarity index 69% rename from tests/ui/issues/issue-26948.rs rename to tests/ui/functional-struct-update/fru-on-enum-variant.rs index 832c9641daa0d..db87ad43b38db 100644 --- a/tests/ui/issues/issue-26948.rs +++ b/tests/ui/functional-struct-update/fru-on-enum-variant.rs @@ -1,3 +1,5 @@ +//! Regression test for . + fn main() { enum Foo { A { x: u32 } } let orig = Foo::A { x: 5 }; diff --git a/tests/ui/issues/issue-26948.stderr b/tests/ui/functional-struct-update/fru-on-enum-variant.stderr similarity index 86% rename from tests/ui/issues/issue-26948.stderr rename to tests/ui/functional-struct-update/fru-on-enum-variant.stderr index 982c9758a3bc1..88a9a1c3d8ffd 100644 --- a/tests/ui/issues/issue-26948.stderr +++ b/tests/ui/functional-struct-update/fru-on-enum-variant.stderr @@ -1,5 +1,5 @@ error[E0436]: functional record update syntax requires a struct - --> $DIR/issue-26948.rs:4:22 + --> $DIR/fru-on-enum-variant.rs:6:22 | LL | Foo::A { x: 6, ..orig }; | ^^^^ diff --git a/tests/ui/issues/issue-27042.rs b/tests/ui/label/loop-label-included-in-span.rs similarity index 80% rename from tests/ui/issues/issue-27042.rs rename to tests/ui/label/loop-label-included-in-span.rs index 517c1f2e6c7cc..d6eee79cfa1b9 100644 --- a/tests/ui/issues/issue-27042.rs +++ b/tests/ui/label/loop-label-included-in-span.rs @@ -1,4 +1,5 @@ -// Regression test for #27042. Test that a loop's label is included in its span. +//! Regression test for . +//! Test that a loop's label is included in its span. fn main() { let _: i32 = diff --git a/tests/ui/issues/issue-27042.stderr b/tests/ui/label/loop-label-included-in-span.stderr similarity index 87% rename from tests/ui/issues/issue-27042.stderr rename to tests/ui/label/loop-label-included-in-span.stderr index 6586e61f2f644..803d5a7747eaa 100644 --- a/tests/ui/issues/issue-27042.stderr +++ b/tests/ui/label/loop-label-included-in-span.stderr @@ -1,5 +1,5 @@ warning: denote infinite loops with `loop { ... }` - --> $DIR/issue-27042.rs:8:9 + --> $DIR/loop-label-included-in-span.rs:9:9 | LL | / 'b: LL | | @@ -9,7 +9,7 @@ LL | | while true { break }; // but here we cite the whole loop = note: `#[warn(while_true)]` on by default error[E0308]: mismatched types - --> $DIR/issue-27042.rs:6:16 + --> $DIR/loop-label-included-in-span.rs:7:16 | LL | let _: i32 = | - expected because of this assignment @@ -25,7 +25,7 @@ LL | loop { break 42 }; | ++ error[E0308]: mismatched types - --> $DIR/issue-27042.rs:8:9 + --> $DIR/loop-label-included-in-span.rs:9:9 | LL | let _: i32 = | --- expected due to this @@ -35,7 +35,7 @@ LL | | while true { break }; // but here we cite the whole loop | |____________________________^ expected `i32`, found `()` error[E0308]: mismatched types - --> $DIR/issue-27042.rs:12:9 + --> $DIR/loop-label-included-in-span.rs:13:9 | LL | / 'c: LL | | for _ in None { break }; // but here we cite the whole loop @@ -44,7 +44,7 @@ LL | | for _ in None { break }; // but here we cite the whole loop = note: `for` loops evaluate to unit type `()` error[E0308]: mismatched types - --> $DIR/issue-27042.rs:15:9 + --> $DIR/loop-label-included-in-span.rs:16:9 | LL | let _: i32 = | --- expected due to this diff --git a/tests/ui/panics/abort-on-panic.rs b/tests/ui/panics/abort-on-panic.rs index d3bf087bd3ef8..e8f491d5926f2 100644 --- a/tests/ui/panics/abort-on-panic.rs +++ b/tests/ui/panics/abort-on-panic.rs @@ -61,11 +61,20 @@ fn test_always_abort_thread() { bomb_out_but_not_abort("joined - but we were supposed to panic!"); } +fn test_always_abort_resume_unwind() { + panic::always_abort(); + let _ = panic::catch_unwind(|| { + panic::resume_unwind(Box::new(())); + }); + should_have_aborted(); +} + fn main() { let tests: &[(_, fn())] = &[ ("test", test), ("test_always_abort", test_always_abort), ("test_always_abort_thread", test_always_abort_thread), + ("test_always_abort_resume_unwind", test_always_abort_resume_unwind), ]; let args: Vec = env::args().collect(); diff --git a/tests/ui/panics/panic-in-hook.rs b/tests/ui/panics/panic-in-hook.rs new file mode 100644 index 0000000000000..db4d8a4dd1e0f --- /dev/null +++ b/tests/ui/panics/panic-in-hook.rs @@ -0,0 +1,14 @@ +// Checks what happens when panicking inside the panic hook. + +//@ run-crash +//@ exec-env:RUST_BACKTRACE=0 +//@ check-run-results +//@ error-pattern: panicked while processing panic +//@ ignore-emscripten "RuntimeError" junk in output + +use std::panic; + +fn main() { + panic::set_hook(Box::new(|_| panic!("panic in hook"))); + panic!(); +} diff --git a/tests/ui/panics/panic-in-hook.run.stderr b/tests/ui/panics/panic-in-hook.run.stderr new file mode 100644 index 0000000000000..9ce7e2f019105 --- /dev/null +++ b/tests/ui/panics/panic-in-hook.run.stderr @@ -0,0 +1,3 @@ +panicked at $DIR/panic-in-hook.rs:12:34: + +thread panicked while processing panic. aborting. diff --git a/tests/ui/panics/panic-resume_unwind-in-hook.rs b/tests/ui/panics/panic-resume_unwind-in-hook.rs new file mode 100644 index 0000000000000..a4c735312ebba --- /dev/null +++ b/tests/ui/panics/panic-resume_unwind-in-hook.rs @@ -0,0 +1,14 @@ +// Checks what happens when panicking inside the panic hook. + +//@ run-crash +//@ exec-env:RUST_BACKTRACE=0 +//@ check-run-results +//@ error-pattern: panicked while processing panic +//@ ignore-emscripten "RuntimeError" junk in output + +use std::panic; + +fn main() { + panic::set_hook(Box::new(|_| panic::resume_unwind(Box::new(())))); + panic!(); +} diff --git a/tests/ui/panics/panic-resume_unwind-in-hook.run.stderr b/tests/ui/panics/panic-resume_unwind-in-hook.run.stderr new file mode 100644 index 0000000000000..22e94e755a26a --- /dev/null +++ b/tests/ui/panics/panic-resume_unwind-in-hook.run.stderr @@ -0,0 +1 @@ +thread panicked while processing panic. aborting. diff --git a/tests/ui/traits/const-traits/associated-const-stability.rs b/tests/ui/traits/const-traits/associated-const-stability.rs new file mode 100644 index 0000000000000..837629d608a36 --- /dev/null +++ b/tests/ui/traits/const-traits/associated-const-stability.rs @@ -0,0 +1,24 @@ +//@ check-pass +//@ aux-build: associated-const-stability.rs + +extern crate associated_const_stability; + +use associated_const_stability::{Probe, TraitWithConstGate}; + +// Demonstrate that it's possible to use a `const` associated item in a stable `const` context +// even when the item is defined inside a const-unstable enclosing parent item. +// +// Here `TraitWithConstGate` is const-unstable, as is its `const impl` for `Probe`. +// This crate *does not* enable the `const_trait_gate` feature for those const impls. +// By showing that this crate compiles without that feature, while having const contexts +// that use both `::ASSOC` specifically +// and generic `::ASSOC`, we prove that the associated consts respect +// the item-level stability like `#[stable]`, not const-stability like `#[rustc_const_unstable]`. + +const VALUE: usize = ::ASSOC; + +const fn example(_: &T) -> usize { + ::ASSOC +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/auxiliary/associated-const-stability.rs b/tests/ui/traits/const-traits/auxiliary/associated-const-stability.rs new file mode 100644 index 0000000000000..77d94cd98ed43 --- /dev/null +++ b/tests/ui/traits/const-traits/auxiliary/associated-const-stability.rs @@ -0,0 +1,19 @@ +#![feature(const_trait_impl)] +#![feature(staged_api)] +#![stable(feature = "rust1", since = "1.0.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Probe; + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_trait_gate", issue = "none")] +pub const trait TraitWithConstGate { + #[stable(feature = "rust1", since = "1.0.0")] + const ASSOC: usize; +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_trait_gate", issue = "none")] +const impl TraitWithConstGate for Probe { + const ASSOC: usize = 1; +} diff --git a/tests/ui/issues/issue-26802.rs b/tests/ui/traits/default-method/trait-object-lifetime-bound.rs similarity index 78% rename from tests/ui/issues/issue-26802.rs rename to tests/ui/traits/default-method/trait-object-lifetime-bound.rs index 62fcc617b46a4..d6bea7f956a9f 100644 --- a/tests/ui/issues/issue-26802.rs +++ b/tests/ui/traits/default-method/trait-object-lifetime-bound.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ run-pass + trait Foo<'a> { fn bar<'b>(&self, x: &'b u8) -> u8 where 'a: 'b { *x+7 } } diff --git a/tests/ui/issues/issue-26805.rs b/tests/ui/traits/object/dyn-iterator-with-non-ord-item.rs similarity index 61% rename from tests/ui/issues/issue-26805.rs rename to tests/ui/traits/object/dyn-iterator-with-non-ord-item.rs index cf75908570acf..2e818884d0511 100644 --- a/tests/ui/issues/issue-26805.rs +++ b/tests/ui/traits/object/dyn-iterator-with-non-ord-item.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ run-pass + struct NonOrd; fn main() { diff --git a/tests/ui/issues/issue-27078.rs b/tests/ui/unsized/trait-method-unsized-self-param.rs similarity index 67% rename from tests/ui/issues/issue-27078.rs rename to tests/ui/unsized/trait-method-unsized-self-param.rs index 5f09b9587b15a..f3df9706503ca 100644 --- a/tests/ui/issues/issue-27078.rs +++ b/tests/ui/unsized/trait-method-unsized-self-param.rs @@ -1,3 +1,5 @@ +//! Regression test for . + trait Foo { const BAR: i32; fn foo(self) -> &'static i32 { diff --git a/tests/ui/issues/issue-27078.stderr b/tests/ui/unsized/trait-method-unsized-self-param.stderr similarity index 93% rename from tests/ui/issues/issue-27078.stderr rename to tests/ui/unsized/trait-method-unsized-self-param.stderr index d0ba9f6ae2b18..0f5409190d2f2 100644 --- a/tests/ui/issues/issue-27078.stderr +++ b/tests/ui/unsized/trait-method-unsized-self-param.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `Self` cannot be known at compilation time - --> $DIR/issue-27078.rs:3:12 + --> $DIR/trait-method-unsized-self-param.rs:5:12 | LL | fn foo(self) -> &'static i32 { | ^^^^ doesn't have a size known at compile-time