diff --git a/Cargo.lock b/Cargo.lock index f55daf9e01c5f..ebc5ce37252c8 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", @@ -6336,9 +6337,9 @@ dependencies = [ [[package]] name = "wasm-component-ld" -version = "0.5.26" +version = "0.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51a12709376d4ce64f472699500db3b0e5902cc2bef16fb6ca3098bfdac032fa" +checksum = "5fd1ba338b2c06654988a76b13f38cad9a1c738e3275bf71ecf70eeac5d98eb4" dependencies = [ "anyhow", "clap", @@ -6347,12 +6348,12 @@ dependencies = [ "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.253.0", + "wasmparser 0.254.0", "wat", "windows-sys 0.61.2", "winsplit", - "wit-component 0.253.0", - "wit-parser 0.253.0", + "wit-component 0.254.0", + "wit-parser 0.254.0", ] [[package]] @@ -6384,12 +6385,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.253.0" +version = "0.254.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59972d6cd272259de647b7c1f1912e45e289c75ffd4be04e10695507cd7e1b59" +checksum = "09480d646178e5fdd12bb06e812d0af9a3a191dbc9cd697fdc86687beade7393" dependencies = [ "leb128fmt", - "wasmparser 0.253.0", + "wasmparser 0.254.0", ] [[package]] @@ -6406,14 +6407,14 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.253.0" +version = "0.254.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3f45816ef616806f48498bcd831377de578c4fa51db0c83ab8ceb78cc13523b" +checksum = "b01df5f3b4ca7881e843f3bc0fb8a3905d79c68692250dcb8e33e698705ccdb6" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.253.0", - "wasmparser 0.253.0", + "wasm-encoder 0.254.0", + "wasmparser 0.254.0", ] [[package]] @@ -6450,9 +6451,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.253.0" +version = "0.254.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19db11f87d2486580e1e8b6f494c54df7e0566b87d0b599db843c24019667339" +checksum = "d5769a29f799fbab136aaf65b4fe5384cd7d93fe6fc9ba0dcb6c8382a1f16e27" dependencies = [ "bitflags", "hashbrown 0.17.0", @@ -6463,22 +6464,22 @@ dependencies = [ [[package]] name = "wast" -version = "253.0.0" +version = "254.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3264542f8965c5d84fb1085d924bfba9a6314bb228eff13a2de14d7627664d0" +checksum = "e7ed4dfc8f6b9fc38b231065e2cdfbf7359af5ab945990abf09658dcc63c3e32" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.2", - "wasm-encoder 0.253.0", + "wasm-encoder 0.254.0", ] [[package]] name = "wat" -version = "1.253.0" +version = "1.254.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bfc5ce906144200c972ec617470aa35bd847472e170b26dde3e80541c674055" +checksum = "7127f7f9b8f127c879991cecd35f494e4628bae1b0874c681414d8d8831e952c" dependencies = [ "wast", ] @@ -6926,9 +6927,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.253.0" +version = "0.254.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbbd2500ac3488489ee8c6e59b79d7e47e6da5bfb019efd35d5dca57b78af624" +checksum = "b0e65bb94c369b3c4741ce3d1d2704b1fec93db7c540df0e521a097e7ceeb5be" dependencies = [ "anyhow", "bitflags", @@ -6937,10 +6938,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.253.0", - "wasm-metadata 0.253.0", - "wasmparser 0.253.0", - "wit-parser 0.253.0", + "wasm-encoder 0.254.0", + "wasm-metadata 0.254.0", + "wasmparser 0.254.0", + "wit-parser 0.254.0", ] [[package]] @@ -6963,9 +6964,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.253.0" +version = "0.254.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d997b8e5920fcbeec742b58e583325d6419a6aca617ae8075c406a61c65ba8a" +checksum = "1655131e4f7d3f0cb141f6eca71315ca40eff0f3d4de7cff0a82bacedd8c89b4" dependencies = [ "anyhow", "hashbrown 0.17.0", @@ -6977,7 +6978,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-ident", - "wasmparser 0.253.0", + "wasmparser 0.254.0", ] [[package]] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index a36e7f3248dd0..abe3d40da49ba 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2339,27 +2339,7 @@ pub struct FnSig { impl FnSig { /// Return a span encompassing the header, or where to insert it if empty. pub fn header_span(&self) -> Span { - match self.header.ext { - Extern::Implicit(span) | Extern::Explicit(_, span) => { - return self.span.with_hi(span.hi()); - } - Extern::None => {} - } - - match self.header.safety { - Safety::Unsafe(span) | Safety::Safe(span) => return self.span.with_hi(span.hi()), - Safety::Default => {} - }; - - if let Some(coroutine_kind) = self.header.coroutine_kind { - return self.span.with_hi(coroutine_kind.span().hi()); - } - - if let Const::Yes(span) = self.header.constness { - return self.span.with_hi(span.hi()); - } - - self.span.shrink_to_lo() + self.header.span().unwrap_or(self.span.shrink_to_lo()) } /// The span of the header's safety, or where to insert it if empty. @@ -2382,6 +2362,19 @@ impl FnSig { pub fn extern_span(&self) -> Span { self.header.ext.span().unwrap_or(self.safety_span().shrink_to_hi()) } + + pub fn as_borrowed(&self) -> BorrowedFnSig<'_> { + BorrowedFnSig { header: self.header, decl: &self.decl, span: self.span } + } +} + +/// A borrowed version of `FnSig`, used to share logic between function declarations and function +/// pointer types. +#[derive(Clone, Debug)] +pub struct BorrowedFnSig<'a> { + pub header: FnHeader, + pub decl: &'a FnDecl, + pub span: Span, } /// A constraint on an associated item. @@ -2487,6 +2480,16 @@ pub struct FnPtrTy { pub decl_span: Span, } +impl FnPtrTy { + pub fn header(&self) -> FnHeader { + FnHeader { constness: Const::No, coroutine_kind: None, safety: self.safety, ext: self.ext } + } + + pub fn as_borrowed_fn_sig<'a>(&'a self) -> BorrowedFnSig<'a> { + BorrowedFnSig { header: self.header(), decl: &self.decl, span: self.decl_span } + } +} + #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct UnsafeBinderTy { pub generic_params: ThinVec, @@ -3402,6 +3405,15 @@ pub enum AttrStyle { Inner, } +impl AttrStyle { + pub fn line_doc_comment_prefix(self) -> &'static str { + match self { + AttrStyle::Outer => "///", + AttrStyle::Inner => "//!", + } + } +} + /// A list of attributes. pub type AttrVec = ThinVec; @@ -3836,6 +3848,30 @@ impl FnHeader { || matches!(constness, Const::Yes(_)) || !matches!(ext, Extern::None) } + + pub fn span(&self) -> Option { + let mut spans = smallvec::SmallVec::<[Span; 4]>::new(); + + match self.ext { + Extern::Implicit(span) | Extern::Explicit(_, span) => spans.push(span), + Extern::None => {} + } + + match self.safety { + Safety::Unsafe(span) | Safety::Safe(span) => spans.push(span), + Safety::Default => {} + }; + + if let Some(coroutine_kind) = self.coroutine_kind { + spans.push(coroutine_kind.span()); + } + + if let Const::Yes(span) = self.constness { + spans.push(span) + } + + spans.into_iter().reduce(Span::to) + } } impl Default for FnHeader { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 1276aa8f4fb96..0e47424ba1aa4 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -546,7 +546,13 @@ impl<'a> AstValidator<'a> { } /// Check that the signature of this function does not violate the constraints of its ABI. - fn check_extern_fn_signature(&self, abi: ExternAbi, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) { + fn check_extern_fn_signature( + &self, + abi: ExternAbi, + ctxt: FnCtxt, + opt_function_name: Option<&Ident>, // None for function pointers + sig: &BorrowedFnSig<'_>, + ) { match AbiMap::from_target(&self.sess.target).canonize_abi(abi, false) { AbiMapping::Direct(canon_abi) | AbiMapping::Deprecated(canon_abi) => { match canon_abi { @@ -569,13 +575,13 @@ impl<'a> AstValidator<'a> { CanonAbi::Custom => { // An `extern "custom"` function must be unsafe. - self.reject_safe_fn(abi, ctxt, sig); + self.reject_safe_fn(abi, ctxt, sig, opt_function_name.is_none()); // An `extern "custom"` function cannot be `async` and/or `gen`. self.reject_coroutine(abi, sig); // An `extern "custom"` function must have type `fn()`. - self.reject_params_or_return(abi, ident, sig); + self.reject_params_or_return(abi, opt_function_name, sig); } CanonAbi::Interrupt(interrupt_kind) => { @@ -600,7 +606,7 @@ impl<'a> AstValidator<'a> { self.reject_return(abi, sig); } else { // An `extern "interrupt"` function must have type `fn()`. - self.reject_params_or_return(abi, ident, sig); + self.reject_params_or_return(abi, opt_function_name, sig); } } } @@ -609,18 +615,27 @@ impl<'a> AstValidator<'a> { } } - fn reject_safe_fn(&self, abi: ExternAbi, ctxt: FnCtxt, sig: &FnSig) { + fn reject_safe_fn( + &self, + abi: ExternAbi, + ctxt: FnCtxt, + sig: &BorrowedFnSig<'_>, + is_fn_ptr: bool, + ) { let dcx = self.dcx(); match sig.header.safety { Safety::Unsafe(_) => { /* all good */ } Safety::Safe(safe_span) => { - let source_map = self.sess.psess.source_map(); - let safe_span = source_map.span_until_non_whitespace(safe_span.to(sig.span)); - dcx.emit_err(diagnostics::AbiCustomSafeForeignFunction { - span: sig.span, - safe_span, - }); + // Function pointers already error when `safe` is used. + if !is_fn_ptr { + let source_map = self.sess.psess.source_map(); + let safe_span = source_map.span_until_non_whitespace(safe_span.to(sig.span)); + dcx.emit_err(diagnostics::AbiCustomSafeForeignFunction { + span: sig.span, + safe_span, + }); + } } Safety::Default => match ctxt { FnCtxt::Foreign => { /* all good */ } @@ -635,7 +650,7 @@ impl<'a> AstValidator<'a> { } } - fn reject_coroutine(&self, abi: ExternAbi, sig: &FnSig) { + fn reject_coroutine(&self, abi: ExternAbi, sig: &BorrowedFnSig<'_>) { if let Some(coroutine_kind) = sig.header.coroutine_kind { let coroutine_kind_span = self .sess @@ -652,7 +667,7 @@ impl<'a> AstValidator<'a> { } } - fn reject_return(&self, abi: ExternAbi, sig: &FnSig) { + fn reject_return(&self, abi: ExternAbi, sig: &BorrowedFnSig<'_>) { if let FnRetTy::Ty(ref ret_ty) = sig.decl.output && match &ret_ty.kind { TyKind::Never => false, @@ -664,29 +679,41 @@ impl<'a> AstValidator<'a> { } } - fn reject_params_or_return(&self, abi: ExternAbi, ident: &Ident, sig: &FnSig) { + fn reject_params_or_return( + &self, + abi: ExternAbi, + opt_function_name: Option<&Ident>, // None for function pointers + sig: &BorrowedFnSig<'_>, + ) { let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect(); + + let allowed_return = |ret_ty: &Ty| match &ret_ty.kind { + TyKind::Never if abi != ExternAbi::Custom => true, + TyKind::Tup(tup) if tup.is_empty() => true, + _ => false, + }; + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output - && match &ret_ty.kind { - TyKind::Never => false, - TyKind::Tup(tup) if tup.is_empty() => false, - _ => true, - } + && !allowed_return(ret_ty) { spans.push(ret_ty.span); } if !spans.is_empty() { - let header_span = sig.header_span(); + let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo()); let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span()); let padding = if header_span.is_empty() { "" } else { " " }; self.dcx().emit_err(diagnostics::AbiMustNotHaveParametersOrReturnType { spans, - symbol: ident.name, + abi, + suggestion_span, padding, - abi, + symbol: match opt_function_name { + Some(ident) => format!(" {}", ident.name), + None => String::new(), + }, }); } } @@ -717,8 +744,12 @@ impl<'a> AstValidator<'a> { } fn check_fn_ptr_safety(&self, span: Span, safety: Safety) { - if matches!(safety, Safety::Safe(_)) { - self.dcx().emit_err(diagnostics::InvalidSafetyOnFnPtr { span }); + if let Safety::Safe(safe_span) = safety { + let remove_span = self.sess.source_map().span_until_non_whitespace(span); + self.dcx().emit_err(diagnostics::InvalidSafetyOnFnPtr { + span: safe_span, + safe_span: remove_span, + }); } } @@ -1138,6 +1169,24 @@ impl<'a> AstValidator<'a> { if let Extern::Implicit(extern_span) = bfty.ext { self.handle_missing_abi(extern_span, ty.id); } + + let ext = match bfty.ext { + Extern::None => None, + Extern::Implicit(_) => Some(ExternAbi::FALLBACK), + Extern::Explicit(str_lit, _) => { + ExternAbi::from_str(str_lit.symbol.as_str()).ok() + } + }; + + // Some ABIs impose special restrictions on the signature. + if let Some(extern_abi) = ext { + self.check_extern_fn_signature( + extern_abi, + FnCtxt::Free, + None, + &bfty.as_borrowed_fn_sig(), + ); + } } TyKind::TraitObject(bounds, ..) => { let mut any_lifetime_bounds = false; @@ -1664,8 +1713,8 @@ impl Visitor<'_> for AstValidator<'_> { self.check_extern_fn_signature( self.extern_mod_abi.unwrap_or(ExternAbi::FALLBACK), FnCtxt::Foreign, - ident, - sig, + Some(ident), + &sig.as_borrowed(), ); if let Some(attr) = attr::find_by_name(fi.attrs(), sym::track_caller) @@ -1870,7 +1919,12 @@ impl Visitor<'_> for AstValidator<'_> { if let Some((extern_abi, extern_abi_span)) = ext { // Some ABIs impose special restrictions on the signature. - self.check_extern_fn_signature(extern_abi, ctxt, &fun.ident, &fun.sig); + self.check_extern_fn_signature( + extern_abi, + ctxt, + Some(&fun.ident), + &fun.sig.as_borrowed(), + ); // #[track_caller] can only be used with the rust ABI. if let Some(attr) = attr::find_by_name(attrs, sym::track_caller) diff --git a/compiler/rustc_ast_passes/src/diagnostics.rs b/compiler/rustc_ast_passes/src/diagnostics.rs index 5bcffe96f6086..af22ead1332d1 100644 --- a/compiler/rustc_ast_passes/src/diagnostics.rs +++ b/compiler/rustc_ast_passes/src/diagnostics.rs @@ -382,6 +382,13 @@ pub(crate) struct InvalidSafetyOnItem { pub(crate) struct InvalidSafetyOnFnPtr { #[primary_span] pub span: Span, + #[suggestion( + "remove the `safe` qualifier", + code = "", + applicability = "machine-applicable", + style = "verbose" + )] + pub safe_span: Span, } #[derive(Diagnostic)] @@ -1115,11 +1122,11 @@ pub(crate) struct AbiMustNotHaveParametersOrReturnType { #[suggestion( "remove the parameters and return type", applicability = "maybe-incorrect", - code = "{padding}fn {symbol}()", + code = "{padding}fn{symbol}()", style = "verbose" )] pub suggestion_span: Span, - pub symbol: Symbol, + pub symbol: String, pub padding: &'static str, } diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index a2348eef975cf..69bdccb85c5cd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -53,7 +53,7 @@ pub(crate) struct RustcParenSugarParser; impl NoArgsAttributeParser for RustcParenSugarParser { const PATH: &[Symbol] = &[sym::rustc_paren_sugar]; const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); - const STABILITY: AttributeStability = unstable!(rustc_attrs); + const STABILITY: AttributeStability = unstable!(unboxed_closures); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcParenSugar; } 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/back/mod.rs b/compiler/rustc_codegen_ssa/src/back/mod.rs index 0f5050a9d7a9a..68db2d0cbf0bb 100644 --- a/compiler/rustc_codegen_ssa/src/back/mod.rs +++ b/compiler/rustc_codegen_ssa/src/back/mod.rs @@ -15,6 +15,8 @@ mod symbol_edit; pub mod symbol_export; pub mod write; +pub use symbol_export::{exported_non_generic_symbols_helper, reachable_non_generics_helper}; + /// The target triple depends on the deployment target, and is required to /// enable features such as cross-language LTO, and for picking the right /// Mach-O commands. diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 42e2b646e4793..51413d3a67a68 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -54,6 +54,11 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap) -> DefIdMap { let is_compiler_builtins = tcx.is_compiler_builtins(LOCAL_CRATE); let mut reachable_non_generics: DefIdMap<_> = tcx @@ -176,6 +181,13 @@ fn exported_non_generic_symbols_provider_local<'tcx>( return &[]; } + exported_non_generic_symbols_helper(tcx) +} + +/// Exposed separately *without* the "should codegen" check so Miri can access it. +pub fn exported_non_generic_symbols_helper<'tcx>( + tcx: TyCtxt<'tcx>, +) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { // FIXME: Sorting this is unnecessary since we are sorting later anyway. // Can we skip the later sorting? let sorted = tcx.with_stable_hashing_context(|mut hcx| { 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 5d9c1a876f0cb..5dbaae44a441e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -11,6 +11,7 @@ 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, TyAndLayout, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; @@ -1489,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_); @@ -1507,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]) } 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_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/hir.rs b/compiler/rustc_hir/src/hir.rs index 2e272fa3af9d0..28200bc3f4712 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -4769,7 +4769,7 @@ pub enum RestrictionKind<'hir> { /// explicitly to allow unsafe operations. #[derive(Copy, Clone, Debug, StableHash, PartialEq, Eq)] pub enum HeaderSafety { - /// A safe function annotated with `#[target_features]`. + /// A safe function annotated with `#[target_feature(..)]`. /// The type system treats this function as an unsafe function, /// but safety checking will check this enum to treat it as safe /// and allowing calling other safe target feature functions with diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 92f48cda10f9f..e6e0b3726552f 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -226,6 +226,7 @@ language_item_table! { IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1); UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None; + CovariantUnsafeCell, sym::covariant_unsafe_cell, covariant_unsafe_cell_type, Target::Struct, GenericRequirement::Exact(1); UnsafePinned, sym::unsafe_pinned, unsafe_pinned_type, Target::Struct, GenericRequirement::None; VaArgSafe, sym::va_arg_safe, va_arg_safe, Target::Trait, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 605b6e3751ef3..6c393ed3dfc6b 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -947,9 +947,6 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { let attrs = tcx.get_all_attrs(def_id); let paren_sugar = find_attr!(attrs, RustcParenSugar); - if paren_sugar && !tcx.features().unboxed_closures() { - tcx.dcx().emit_err(diagnostics::ParenSugarAttribute { span: item.span }); - } // Only regular traits can be marker. let is_marker = !is_alias && find_attr!(attrs, Marker); diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index a6e274ceb4bc6..0ea353cf14cee 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -862,16 +862,6 @@ pub(crate) struct EnumDiscriminantOverflowed { pub wrapped_discr: String, } -#[derive(Diagnostic)] -#[diag( - "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation" -)] -#[help("add `#![feature(unboxed_closures)]` to the crate attributes to use it")] -pub(crate) struct ParenSugarAttribute { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag("use of SIMD type{$snip} in FFI is highly experimental and may result in invalid code")] #[help("add `#![feature(simd_ffi)]` to the crate attributes to enable")] diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index f5b4cee8dedf2..497ff02c9b407 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2938,42 +2938,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .span_err(path.span, "static items cannot be used as const arguments"); return Const::new_error(tcx, guar); } - // FIXME(const_generics): create real const to allow fn items as const paths - Res::Def(DefKind::Fn | DefKind::AssocFn, did) => { - self.dcx().span_delayed_bug(span, "function items cannot be used as const args"); - let args = self.lower_generic_args_of_path_segment( - span, - did, - path.segments.last().unwrap(), - ); - - if self.tcx().generics_of(did).own_synthetic_params_count() == 0 { - // FIXME(156581): actually instantiate the binder correctly (turbofishing/fndef changes) - ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, ty::Binder::dummy(args))) - } else { - let tcx = self.tcx(); - let generics = tcx.generics_of(did); - - // Use infer tys for synthetic params; otherwise the impl header's trait ref may - // contain callee-owned synthetic params and fail when instantiated with impl args. - // See issue #155834 - let args = args.iter().enumerate().map(|(index, arg)| { - let param = generics.param_at(index, tcx); - if param.kind.is_synthetic() { - self.ty_infer(Some(param), span).into() - } else { - arg - } - }); - - // FIXME(156581): actually instantiate the binder correctly (turbofishing/fndef changes) - ty::Const::zero_sized( - tcx, - Ty::new_fn_def(tcx, did, ty::Binder::dummy(args.collect::>())), - ) - } + // FIXME(const_generics): create real consts to allow fn items as const paths. + // Lowering these to recovered `FnDef` consts currently interacts poorly with WF + // checking: WF of a `FnDef` walks the function signature, so a signature that mentions + // the same function item as a const arg can recurse until it overflows/segfaults. + Res::Def(DefKind::Fn | DefKind::AssocFn, _) => { + let guar = self + .dcx() + .struct_span_err(span, "function items cannot be used as const args") + .emit(); + Const::new_error(tcx, guar) } - // Exhaustive match to be clear about what exactly we're considering to be // an invalid Res for a const path. res @ (Res::Def( diff --git a/compiler/rustc_hir_analysis/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs index 6faeab4217e37..d22eb01467ebe 100644 --- a/compiler/rustc_hir_analysis/src/variance/terms.rs +++ b/compiler/rustc_hir_analysis/src/variance/terms.rs @@ -111,6 +111,7 @@ fn lang_items(tcx: TyCtxt<'_>) -> Vec<(LocalDefId, Vec)> { let all = [ (lang_items.phantom_data(), vec![ty::Covariant]), (lang_items.unsafe_cell_type(), vec![ty::Invariant]), + (lang_items.covariant_unsafe_cell_type(), vec![ty::Covariant]), ]; all.into_iter() // iterating over (Option, Variance) 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/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_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index d1d864246b9ff..aa0f1afcda6b0 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -364,7 +364,8 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { use rustc_data_structures::undo_log::UndoLogs; use crate::infer::UndoLog; - inner.undo_log.push(UndoLog::PushSolverRegionConstraint); + let previous_was_and = inner.solver_region_constraint_storage.is_and(); + inner.undo_log.push(UndoLog::PushSolverRegionConstraint { previous_was_and }); inner.solver_region_constraint_storage.push(c); } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 72820a31a95b9..ef235274daac3 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1841,12 +1841,21 @@ impl<'tcx> SolverRegionConstraintStorage<'tcx> { self.0.clone() } - fn pop(&mut self) -> Option> { + fn is_and(&self) -> bool { + self.0.is_and() + } + + fn pop(&mut self, previous_was_and: bool) -> Option> { match &mut self.0 { SolverRegionConstraint::And(and) => { let mut and = core::mem::take(and).into_iter().collect::>(); let popped = and.pop()?; - self.0 = SolverRegionConstraint::And(and.into_boxed_slice()); + if previous_was_and { + self.0 = SolverRegionConstraint::And(and.into_boxed_slice()); + } else { + assert_eq!(and.len(), 1); + self.0 = and.pop().unwrap(); + } Some(popped) } _ => unreachable!(), @@ -1855,25 +1864,20 @@ impl<'tcx> SolverRegionConstraintStorage<'tcx> { #[instrument(level = "debug")] fn push(&mut self, constraint: SolverRegionConstraint<'tcx>) { - match &mut self.0 { + match core::mem::replace(&mut self.0, SolverRegionConstraint::new_true()) { SolverRegionConstraint::And(and) => { - let and = core::mem::take(and) - .into_iter() - .chain([constraint]) - .collect::>() - .into_boxed_slice(); + let and = + and.into_iter().chain([constraint]).collect::>().into_boxed_slice(); self.0 = SolverRegionConstraint::And(and); } - _ => unreachable!(), + previous => { + self.0 = SolverRegionConstraint::And(Box::new([previous, constraint])); + } } } #[instrument(level = "debug", skip(self))] fn overwrite_solver_region_constraint(&mut self, constraint: SolverRegionConstraint<'tcx>) { - if !constraint.is_and() { - self.0 = SolverRegionConstraint::And(vec![constraint].into_boxed_slice()) - } else { - self.0 = constraint; - } + self.0 = constraint; } } diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index 09d8eb3bf9232..eb5b3fe7bfd41 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -28,7 +28,7 @@ pub(crate) enum UndoLog<'tcx> { RegionUnificationTable(sv::UndoLog>>), ProjectionCache(traits::UndoLog<'tcx>), PushTypeOutlivesConstraint, - PushSolverRegionConstraint, + PushSolverRegionConstraint { previous_was_and: bool }, OverwriteSolverRegionConstraint { old_constraint: SolverRegionConstraint<'tcx> }, PushRegionAssumption, PushHirTypeckPotentiallyRegionDependentGoal, @@ -79,8 +79,8 @@ impl<'tcx> Rollback> for InferCtxtInner<'tcx> { self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo) } UndoLog::ProjectionCache(undo) => self.projection_cache.reverse(undo), - UndoLog::PushSolverRegionConstraint => { - let popped = self.solver_region_constraint_storage.pop(); + UndoLog::PushSolverRegionConstraint { previous_was_and } => { + let popped = self.solver_region_constraint_storage.pop(previous_was_and); assert_matches!( popped, Some(_), 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/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 83670daf909fa..a16352e45564e 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -129,7 +129,8 @@ impl<'tcx> TypeError<'tcx> { } TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(), TypeError::TargetFeatureCast(_) => { - "cannot coerce functions with `#[target_feature]` to safe function pointers".into() + "cannot coerce functions with `#[target_feature(..)]` to safe function pointers" + .into() } } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 3c7e3b63ee9b5..3614a67b71045 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -789,7 +789,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let mut sig = self.tcx().fn_sig(def_id).instantiate(self.tcx(), args).skip_norm_wip(); if self.tcx().codegen_fn_attrs(def_id).safe_target_features { - write!(self, "#[target_features] ")?; + write!(self, "#[target_feature(..)] ")?; sig = sig.map_bound(|mut sig| { sig.fn_sig_kind = sig.fn_sig_kind.set_safety(hir::Safety::Safe); sig 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/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 16f8f3496d9e7..673b8b62e5853 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 @@ -5,7 +5,7 @@ use std::ops::ControlFlow; use rustc_macros::StableHash; use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; -use rustc_type_ir::region_constraint::RegionConstraint; +use rustc_type_ir::region_constraint::{RegionConstraint, evaluate_solver_constraint}; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::search_graph::{CandidateHeadUsages, LowerAvailableDepth, PathKind}; @@ -338,18 +338,12 @@ where // We currently only consider a cycle coinductive if it steps // into a where-clause of a coinductive trait. CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive, - // While normalizing via an impl does step into a where-clause of - // an impl, accessing the associated item immediately steps out of - // it again. This means cycles/recursive calls are not guarded - // by impls used for normalization. - // - // See tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs - // for how this can go wrong. - CurrentGoalKind::ProjectionComputeAssocTermCandidate => PathKind::Inductive, // We probably want to make all traits coinductive in the future, // so we treat cycles involving where-clauses of not-yet coinductive // traits as ambiguous for now. - CurrentGoalKind::Misc => PathKind::Unknown, + CurrentGoalKind::Misc | CurrentGoalKind::ProjectionComputeAssocTermCandidate => { + PathKind::Unknown + } }, // Relating types is always unproductive. If we were to map proof trees to // corecursive functions as explained in #136824, relating types never @@ -1546,7 +1540,12 @@ where // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`. let region_constraints = if self.cx().assumptions_on_binders() { ExternalRegionConstraints::NextGen(if let Certainty::Yes = certainty { - self.delegate.get_solver_region_constraint() + let constraint = self.delegate.get_solver_region_constraint(); + debug_assert_eq!( + constraint, + evaluate_solver_constraint(&constraint.clone().canonical_form()) + ); + constraint } else { RegionConstraint::new_true() }) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs index 129a7b0f0de78..9d2da722b2553 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs @@ -9,6 +9,7 @@ use rustc_type_ir::outlives::{Component, push_outlives_components}; use rustc_type_ir::region_constraint::TransitiveRelationBuilder; use rustc_type_ir::region_constraint::{ Assumptions, RegionConstraint, eagerly_handle_placeholders_in_universe, + evaluate_solver_constraint, }; use rustc_type_ir::{ AliasTy, Binder, ClauseKind, InferCtxtLike, Interner, OutlivesPredicate, Region, TypeVisitable, @@ -136,6 +137,7 @@ where .fold(constraint, |constraint, u| { eagerly_handle_placeholders_in_universe(&**self.delegate, constraint, u) }); + let constraint = evaluate_solver_constraint(&constraint.canonical_form()); self.delegate.overwrite_solver_region_constraint(constraint.clone()); 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 4918258350bf3..fb44782ffb039 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -107,6 +107,7 @@ where response_no_constraints(cx, input, Certainty::overflow(true)) } + const FIXPOINT_OVERFLOW_AMBIGUITY_KIND: Certainty = Certainty::overflow(false); fn fixpoint_overflow_result( cx: I, input: CanonicalInput, @@ -126,14 +127,6 @@ where }) } - fn propagate_ambiguity( - cx: I, - for_input: CanonicalInput, - certainty: Certainty, - ) -> (QueryResult, AccessedOpaques) { - response_no_constraints(cx, for_input, certainty) - } - fn compute_goal( search_graph: &mut SearchGraph, cx: I, diff --git a/compiler/rustc_passes/src/canonical_symbols.rs b/compiler/rustc_passes/src/canonical_symbols.rs index 746758d3e36f2..c823453ef17f4 100644 --- a/compiler/rustc_passes/src/canonical_symbols.rs +++ b/compiler/rustc_passes/src/canonical_symbols.rs @@ -1,8 +1,8 @@ use rustc_hir::{CanonicalSymbols, ForeignItemId, find_attr}; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::{Instance, List, TyCtxt}; -use rustc_span::Symbol; use rustc_span::def_id::{DefId, LOCAL_CRATE}; +use rustc_span::{Symbol, sym}; use crate::diagnostics::DuplicateCanonicalSymbolInCrate; @@ -57,12 +57,15 @@ fn canonical_symbols(tcx: TyCtxt<'_>, _: LocalCrate) -> CanonicalSymbols { // Initialize the collector. let mut canonical_symbols = CanonicalSymbols::new(); - // Collect canonical symbols in this crate. - let crate_items = tcx.hir_crate_items(()); - for id in crate_items.foreign_items() { - observe_item(tcx, &mut canonical_symbols, id); + // Optimization: can this crate even define canonical items? + // (But do not mark `rustc_attrs` as used while doing so) + if tcx.features().enabled_features().contains(&sym::rustc_attrs) { + // Collect canonical symbols in this crate. + let crate_items = tcx.hir_crate_items(()); + for id in crate_items.foreign_items() { + observe_item(tcx, &mut canonical_symbols, id); + } } - canonical_symbols } diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index 49e46a05e5ccd..e950a26af91a3 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -13,8 +13,8 @@ use rustc_hir::diagnostic_items::DiagnosticItems; use rustc_hir::{CRATE_OWNER_ID, OwnerId, find_attr}; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::TyCtxt; -use rustc_span::Symbol; use rustc_span::def_id::{DefId, LOCAL_CRATE}; +use rustc_span::{Symbol, sym}; use crate::diagnostics::DuplicateDiagnosticItemInCrate; @@ -53,15 +53,19 @@ fn report_duplicate_item( }); } -/// Traverse and collect the diagnostic items in the current +/// Traverse and collect the diagnostic items in the current crate fn diagnostic_items(tcx: TyCtxt<'_>, _: LocalCrate) -> DiagnosticItems { // Initialize the collector. let mut diagnostic_items = DiagnosticItems::default(); - // Collect diagnostic items in this crate. - let crate_items = tcx.hir_crate_items(()); - for id in crate_items.owners().chain(std::iter::once(CRATE_OWNER_ID)) { - observe_item(tcx, &mut diagnostic_items, id); + // Optimization: can this crate even define diagnostic items? + // (But do not mark `rustc_attrs` as used while doing so) + if tcx.features().enabled_features().contains(&sym::rustc_attrs) { + // Collect diagnostic items in this crate. + let crate_items = tcx.hir_crate_items(()); + for id in crate_items.owners().chain(std::iter::once(CRATE_OWNER_ID)) { + observe_item(tcx, &mut diagnostic_items, id); + } } diagnostic_items diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index fdae03ec49d9f..b8497aefb7767 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -444,6 +444,8 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by // `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their // `SymbolExportLevel::Rust` export level but may end up being exported in dylibs. + // Also note that Miri is relying on this to be able to find private `link_section` statics + // across all crates. || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) // Right now, the only way to get "foreign item symbol aliases" is by being an EII-implementation. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 95421ba8cfdab..c874c1af265f8 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, @@ -737,6 +738,7 @@ symbols! { cosf64, cosf128, count, + covariant_unsafe_cell, coverage, coverage_attribute, cr, 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_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index fc25849b2d602..3e07621a20d55 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -1219,7 +1219,7 @@ impl Target { /// These features are checked against the target features reported by LLVM based on /// `-Ctarget-cpu` and `-Ctarget-features`. Constraint violations result in a warning. /// - /// We also check features enabled via `#[target_features]` (and here, constraint violations + /// We also check features enabled via `#[target_feature(..)]` (and here, constraint violations /// emit a hard error), including features enabled indirectly via implications -- but if LLVM /// considers more features to be implied than we do, that could bypass this check! pub fn abi_required_features(&self) -> FeatureConstraints { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 69ef6c8d2a5c4..0af41423b846a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -773,17 +773,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let (lt1, sig1) = get_lifetimes(sig1); let (lt2, sig2) = get_lifetimes(sig2); - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T let mut values = (DiagStyledString::normal("".to_string()), DiagStyledString::normal("".to_string())); - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^^^^^^^^^^^^^^^^^^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^^^^^^^^^^^^^^^^ let fn_item_prefix_and_safety = |fn_def, sig: ty::FnSig<'_>| match fn_def { None => ("", sig.safety().prefix_str()), Some((did, _)) => { if self.tcx.codegen_fn_attrs(did).safe_target_features { - ("#[target_features] ", "") + ("#[target_feature(..)] ", "") } else { ("", sig.safety().prefix_str()) } @@ -794,19 +794,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { values.0.push(prefix1, prefix1 != prefix2); values.1.push(prefix2, prefix1 != prefix2); - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^^^^^^^^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^^^ let lifetime_diff = lt1 != lt2; values.0.push(lt1, lifetime_diff); values.1.push(lt2, lifetime_diff); - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^^^^^^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^ values.0.push(safety1, safety1 != safety2); values.1.push(safety2, safety1 != safety2); - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^^^^^^^^^^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^^^^^ if sig1.abi() != ExternAbi::Rust { values.0.push(format!("extern {} ", sig1.abi()), sig1.abi() != sig2.abi()); } @@ -814,13 +814,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { values.1.push(format!("extern {} ", sig2.abi()), sig1.abi() != sig2.abi()); } - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^^^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^ values.0.push_normal("fn("); values.1.push_normal("fn("); - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^^^^^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^ let len1 = sig1.inputs().len(); let len2 = sig2.inputs().len(); let splatted_arg_index1 = sig1.splatted().map(usize::from); @@ -868,13 +868,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { values.1.push("...", !sig1.c_variadic()); } - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^ values.0.push_normal(")"); values.1.push_normal(")"); - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^^^^^^^^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^^^ let output1 = sig1.output(); let output2 = sig2.output(); let (x1, x2) = self.cmp(output1, output2); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index f93d28c29cdfa..7fcc53c2b4348 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -621,9 +621,9 @@ impl Trait for X { TypeError::TargetFeatureCast(def_id) => { let target_spans = find_attr!(tcx, def_id, TargetFeature{attr_span: span, was_forced: false, ..} => *span); diag.note( - "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" + "functions with `#[target_feature(..)]` can only be coerced to `unsafe` function pointers" ); - diag.span_labels(target_spans, "`#[target_feature]` added here"); + diag.span_labels(target_spans, "`#[target_feature(..)]` added here"); } _ => {} } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index b98c3fab5bcb5..8af061168a865 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -555,7 +555,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; if is_fn_trait && is_target_feature_fn { err.note( - "`#[target_feature]` functions do not implement the `Fn` traits", + "`#[target_feature(..)]` functions do not implement the `Fn` traits", ); err.note( "try casting the function to a `fn` pointer or wrapping it in a closure", diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index bfa1c982bd0b7..fd02f017b2950 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -680,7 +680,7 @@ impl CollectAndApply for Result { impl search_graph::Cx for I { type Input = CanonicalInput; type Result = (QueryResult, AccessedOpaques); - type AmbiguityInfo = Certainty; + type AmbiguityKind = Certainty; type DepNodeIndex = I::DepNodeIndex; type Tracked = I::Tracked; diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index e42e96901b74d..e33f1b3dfa5f1 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -356,10 +356,11 @@ impl RegionConstraint { [or1, rest_ors @ ..] => { let mut choices = vec![]; for choice in or1 { - choices.extend(permutations(rest_ors).into_iter().map(|mut and| { - and.push(choice.clone()); - and - })); + choices.extend( + permutations(rest_ors) + .into_iter() + .map(|and| std::iter::once(choice.clone()).chain(and).collect()), + ); } choices } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 711e7ae1c8945..b1635e7e4097c 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -40,7 +40,7 @@ pub use global_cache::GlobalCache; pub trait Cx: Copy { type Input: Debug + Eq + Hash + Copy; type Result: Debug + Eq + Hash + Copy; - type AmbiguityInfo: Debug + Eq + Hash + Copy; + type AmbiguityKind: Debug + Eq + Hash + Copy; type DepNodeIndex; type Tracked: Debug; @@ -92,6 +92,8 @@ pub trait Delegate: Sized { cx: Self::Cx, input: ::Input, ) -> ::Result; + + const FIXPOINT_OVERFLOW_AMBIGUITY_KIND: ::AmbiguityKind; fn fixpoint_overflow_result( cx: Self::Cx, input: ::Input, @@ -99,12 +101,7 @@ pub trait Delegate: Sized { fn is_ambiguous_result( result: ::Result, - ) -> Option<::AmbiguityInfo>; - fn propagate_ambiguity( - cx: Self::Cx, - for_input: ::Input, - ambiguity_info: ::AmbiguityInfo, - ) -> ::Result; + ) -> Option<::AmbiguityKind>; fn compute_goal( search_graph: &mut SearchGraph, @@ -955,8 +952,7 @@ impl, X: Cx> SearchGraph { #[derive_where(Debug; X: Cx)] enum RebaseReason { NoCycleUsages, - Ambiguity(X::AmbiguityInfo), - Overflow, + Ambiguity(X::AmbiguityKind), /// We've actually reached a fixpoint. /// /// This either happens in the first evaluation step for the cycle head. @@ -987,10 +983,9 @@ impl, X: Cx> SearchGraph { /// cache entries to also be ambiguous. This causes some undesirable ambiguity for nested /// goals whose result doesn't actually depend on this cycle head, but that's acceptable /// to me. - #[instrument(level = "trace", skip(self, cx))] + #[instrument(level = "trace", skip(self))] fn rebase_provisional_cache_entries( &mut self, - cx: X, stack_entry: &StackEntry, rebase_reason: RebaseReason, ) { @@ -1065,18 +1060,22 @@ impl, X: Cx> SearchGraph { } // The provisional cache entry does depend on the provisional result - // of the popped cycle head. We need to mutate the result of our - // provisional cache entry in case we did not reach a fixpoint. + // of the popped cycle head. In case we didn't actually reach a fixpoint, + // we must not keep potentially incorrect provisional cache entries around. match rebase_reason { // If the cycle head does not actually depend on itself, then // the provisional result used by the provisional cache entry // is not actually equal to the final provisional result. We // need to discard the provisional cache entry in this case. RebaseReason::NoCycleUsages => return false, - RebaseReason::Ambiguity(info) => { - *result = D::propagate_ambiguity(cx, input, info); + // If we avoid rerunning a goal due to ambiguity, we only keep provisional + // results which depend on that cycle head if these are already ambiguous + // themselves. + RebaseReason::Ambiguity(kind) => { + if !D::is_ambiguous_result(*result).is_some_and(|k| k == kind) { + return false; + } } - RebaseReason::Overflow => *result = D::fixpoint_overflow_result(cx, input), RebaseReason::ReachedFixpoint(None) => {} RebaseReason::ReachedFixpoint(Some(path_kind)) => { if !popped_head.usages.is_single(path_kind) { @@ -1380,17 +1379,12 @@ impl, X: Cx> SearchGraph { // final result is equal to the initial response for that case. if let Ok(fixpoint) = self.reached_fixpoint(&stack_entry, usages, result) { self.rebase_provisional_cache_entries( - cx, &stack_entry, RebaseReason::ReachedFixpoint(fixpoint), ); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } else if usages.is_empty() { - self.rebase_provisional_cache_entries( - cx, - &stack_entry, - RebaseReason::NoCycleUsages, - ); + self.rebase_provisional_cache_entries(&stack_entry, RebaseReason::NoCycleUsages); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } @@ -1399,19 +1393,15 @@ impl, X: Cx> SearchGraph { // response in the next iteration in this case. These changes would // likely either be caused by incompleteness or can change the maybe // cause from ambiguity to overflow. Returning ambiguity always - // preserves soundness and completeness even if the goal is be known - // to succeed or fail. + // preserves soundness and completeness even if the goal could + // otherwise succeed or fail. // // This prevents exponential blowup affecting multiple major crates. // As we only get to this branch if we haven't yet reached a fixpoint, // we also taint all provisional cache entries which depend on the // current goal. - if let Some(info) = D::is_ambiguous_result(result) { - self.rebase_provisional_cache_entries( - cx, - &stack_entry, - RebaseReason::Ambiguity(info), - ); + if let Some(kind) = D::is_ambiguous_result(result) { + self.rebase_provisional_cache_entries(&stack_entry, RebaseReason::Ambiguity(kind)); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); }; @@ -1421,7 +1411,10 @@ impl, X: Cx> SearchGraph { if i >= D::FIXPOINT_STEP_LIMIT { debug!("canonical cycle overflow"); let result = D::fixpoint_overflow_result(cx, input); - self.rebase_provisional_cache_entries(cx, &stack_entry, RebaseReason::Overflow); + self.rebase_provisional_cache_entries( + &stack_entry, + RebaseReason::Ambiguity(D::FIXPOINT_OVERFLOW_AMBIGUITY_KIND), + ); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 8560a644f207b..49bf941984af5 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -64,6 +64,28 @@ pub struct Global; /// of the allocator registered with the `#[global_allocator]` attribute /// if there is one, or the `std` crate’s default. /// +/// Note, however, that invoking this function is *not* equivalent to invoking the underlying +/// [`GlobalAlloc::alloc`] method of the registered allocator directly. Users of this function +/// cannot assume anything about what the allocator does, other than the documented requirements. +/// This means: +/// +/// - This function may non-deterministically entirely skip the underlying allocator, e.g. if the +/// compiler can show that this allocation can be replaced by a stack variable. The compiler may +/// also merge multiple allocation operations into one, as long as it can also adjust all +/// corresponding deallocation operations accordingly. +/// - An allocation created by invoking this function has exactly the size and minimum alignment +/// defined by `layout`, even if the underlying allocator makes stronger promises. +/// - The allocation can only be freed by invoking [`dealloc`] or [`realloc`]. In particular, +/// passing a pointer to such an allocation directly to the underlying method on [`GlobalAlloc`] is +/// not permitted. Until one of those functions is called, it is undefined behavior to access the +/// memory that backs this allocation with any pointer not derived from the return value of this +/// function (e.g., with internal pointers the allocator might keep around). +/// - This function de-initializes the contents of the allocation before handing it to the user. So even +/// if you control the underlying allocator and know that it explicitly initialized this memory, +/// you cannot rely on it being initialized. +/// +/// Users of this function have to consider that in the future, allocators may be allowed to unwind. +/// /// This function is expected to be deprecated in favor of the `allocate` method /// of the [`Global`] type when it and the [`Allocator`] trait become stable. /// @@ -109,6 +131,24 @@ pub unsafe fn alloc(layout: Layout) -> *mut u8 { /// of the allocator registered with the `#[global_allocator]` attribute /// if there is one, or the `std` crate’s default. /// +/// Note, however, that invoking this function is *not* equivalent to invoking the underlying +/// [`GlobalAlloc::dealloc`] method of the registered allocator directly. Users of this function +/// cannot assume anything about what the allocator does, other than the documented requirements. +/// This means: +/// +/// - This function may non-deterministically entirely skip the underlying allocator, e.g. if the +/// compiler can show that this allocation can be replaced by a stack variable. The compiler may +/// also merge multiple allocation operations into one, as long as it can also adjust all +/// corresponding deallocation operations accordingly. +/// - The pointer passed to this function must have been obtained by invoking [`alloc`], +/// [`alloc_zeroed`], or [`realloc`]. In particular, passing a pointer returned by the underlying +/// methods on [`GlobalAlloc`] is not permitted. +/// - This function de-initializes the contents of the allocation before handing it to the allocator. +/// So even if you know that the program previously initialized that memory, the allocator cannot +/// rely on it being initialized. +/// +/// Users of this function have to consider that in the future, allocators may be allowed to unwind. +/// /// This function is expected to be deprecated in favor of the `deallocate` method /// of the [`Global`] type when it and the [`Allocator`] trait become stable. /// @@ -135,6 +175,32 @@ unsafe fn dealloc_nonnull(ptr: NonNull, layout: Layout) { /// of the allocator registered with the `#[global_allocator]` attribute /// if there is one, or the `std` crate’s default. /// +/// Note, however, that invoking this function is *not* equivalent to invoking the underlying +/// [`GlobalAlloc::realloc`] method of the registered allocator directly. Users of this function +/// cannot assume anything about what the allocator does, other than the documented requirements. +/// This means: +/// +/// - This function may non-deterministically entirely skip the underlying allocator, e.g. if the +/// compiler can show that this allocation can be replaced by a stack variable. The compiler may +/// also merge multiple allocation operations into one, as long as it can also adjust all +/// corresponding deallocation operations accordingly. +/// - The pointer passed to this function must have been obtained by invoking [`alloc`], +/// [`alloc_zeroed`], or [`realloc`]. In particular, passing a pointer returned by the underlying +/// methods on [`GlobalAlloc`] is not permitted. +/// - An allocation created by invoking this function has exactly the size and minimum alignment +/// defined by `layout`, even if the underlying allocator makes stronger promises. +/// - The allocation can only be freed by invoking [`dealloc`] or [`realloc`]. In particular, +/// passing a pointer to such an allocation directly to the underlying method on [`GlobalAlloc`] is +/// not permitted. Until one of those functions is called, it is undefined behavior to access the +/// memory that backs this allocation with any pointer not derived from the return value of this +/// function (e.g., with internal pointers the allocator might keep around). +/// - If this grows the allocation, the contents of the grown part of the new allocation allocation +/// are de-initialized by this function before returning. +/// - If this shrinks the allocation, the contents of the removed part of the old allocation are +/// de-initialized by this function before invoking the underlying allocator. +/// +/// Users of this function have to consider that in the future, allocators may be allowed to unwind. +/// /// This function is expected to be deprecated in favor of the `grow` and `shrink` methods /// of the [`Global`] type when it and the [`Allocator`] trait become stable. /// @@ -162,6 +228,25 @@ unsafe fn realloc_nonnull(ptr: NonNull, layout: Layout, new_size: usize) -> /// of the allocator registered with the `#[global_allocator]` attribute /// if there is one, or the `std` crate’s default. /// +/// Note, however, that invoking this function is *not* equivalent to invoking the underlying +/// [`GlobalAlloc::alloc_zeroed`] method of the registered allocator directly. Users of this +/// function cannot assume anything about what the allocator does, other than the documented +/// requirements. This means: +/// +/// - This function may non-deterministically entirely skip the underlying allocator, e.g. if the +/// compiler can show that this allocation can be replaced by a stack variable. The compiler may +/// also merge multiple allocation operations into one, as long as it can also adjust all +/// corresponding deallocation operations accordingly. +/// - The allocation can only be freed by invoking [`dealloc`] or [`realloc`]. In particular, +/// passing a pointer to such an allocation directly to the underlying method on [`GlobalAlloc`] is +/// not permitted. Until one of those functions is called, it is undefined behavior to access the +/// memory that backs this allocation with any pointer not derived from the return value of this +/// function (e.g., with internal pointers the allocator might keep around). +/// - An allocation created by invoking this function has exactly the size and minimum alignment +/// defined by `layout`, even if the underlying allocator makes stronger promises. +/// +/// Users of this function have to consider that in the future, allocators may be allowed to unwind. +/// /// This function is expected to be deprecated in favor of the `allocate_zeroed` method /// of the [`Global`] type when it and the [`Allocator`] trait become stable. /// diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index e6e6fcf5420f3..b340cf9566f2e 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -84,7 +84,7 @@ use crate::vec::Vec; /// /// // We are certain that our string doesn't have 0 bytes in the middle, /// // so we can .expect() -/// let c_to_print = CString::new("Hello, world!").expect("CString::new failed"); +/// let c_to_print = CString::new("Hello, world!").expect("we provided a string without NUL bytes, so CString::new should not fail"); /// unsafe { /// my_printer(c_to_print.as_ptr()); /// } @@ -242,7 +242,7 @@ impl CString { /// /// extern "C" { fn puts(s: *const c_char); } /// - /// let to_print = CString::new("Hello!").expect("CString::new failed"); + /// let to_print = CString::new("Hello!").expect("we provided a string without NUL bytes, so CString::new should not fail"); /// unsafe { /// puts(to_print.as_ptr()); /// } @@ -466,12 +466,12 @@ impl CString { /// use std::ffi::CString; /// /// let valid_utf8 = vec![b'f', b'o', b'o']; - /// let cstring = CString::new(valid_utf8).expect("CString::new failed"); - /// assert_eq!(cstring.into_string().expect("into_string() call failed"), "foo"); + /// let cstring = CString::new(valid_utf8).expect("we provided bytes that do not have a NUL byte, so CString::new should not fail"); + /// assert_eq!(cstring.into_string().expect("we provided bytes that are valid UTF-8, so `into_string` should not fail"), "foo"); /// /// let invalid_utf8 = vec![b'f', 0xff, b'o', b'o']; - /// let cstring = CString::new(invalid_utf8).expect("CString::new failed"); - /// let err = cstring.into_string().err().expect("into_string().err() failed"); + /// let cstring = CString::new(invalid_utf8).expect("we provided bytes that do not have a NUL byte, so CString::new should not fail"); + /// let err = cstring.into_string().expect_err("we provided bytes that are invalid UTF-8, so `into_string` should fail"); /// assert_eq!(err.utf8_error().valid_up_to(), 1); /// ``` #[stable(feature = "cstring_into", since = "1.7.0")] @@ -577,7 +577,7 @@ impl CString { /// let c_string = CString::from(c"foo"); /// let cstr = c_string.as_c_str(); /// assert_eq!(cstr, - /// CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed")); + /// CStr::from_bytes_with_nul(b"foo\0").expect("we provided bytes that has one NUL byte exactly at the end, so CStr::from_bytes_with_nul should not fail")); /// ``` #[inline] #[must_use] @@ -660,7 +660,7 @@ impl CString { /// use std::ffi::CString; /// assert_eq!( /// CString::from_vec_with_nul(b"abc\0".to_vec()) - /// .expect("CString::from_vec_with_nul failed"), + /// .expect("we provided bytes that has one NUL byte exactly at the end, so CString::from_vec_with_nul should not fail"), /// c"abc".to_owned() /// ); /// ``` diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index 44a8ab6e54196..0036b7a55c8ce 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -19,7 +19,6 @@ use crate::{cmp, ptr}; /// method such as `dealloc` or by being /// passed to a reallocation method that returns a non-null pointer. /// -/// /// # Example /// /// ```standalone_crate @@ -85,39 +84,83 @@ use crate::{cmp, ptr}; /// } /// ``` /// +/// # The `#[global_allocator]` attribute +/// +/// As the example above demonstrates, the `#[global_allocator]` attribute can be used to register a +/// concrete `static` of a type that implements this trait to become *the* global allocator +/// for the current program. That global allocator can be invoked via the functions [`alloc`], +/// [`alloc_zeroed`], [`dealloc`], [`realloc`]). Note, however, that invoking those functions is +/// *not* equivalent to directly invoking the underlying methods on the declared global allocator! +/// Users of the global allocator cannot assume anything about what the allocator does (even if they know which allocator is being used), +/// and implementors of the allocator cannot assume anything about what the program does (even if they know how the allocator is being used). +/// Both can only assume the documented requirements for the respective other party of this contract. +/// This means: +/// +/// - Allocation functions may non-deterministically entirely skip the underlying allocator, e.g. if the +/// compiler can show that this allocation can be replaced by a stack variable. The compiler may +/// also merge multiple allocation operations into one, as long as it can also adjust all +/// corresponding deallocation operations accordingly. +/// - An allocation created by invoking [`alloc`], [`alloc_zeroed`], or [`realloc`] has exactly the +/// size and minimum alignment defined by `layout`, even if the underlying allocator makes +/// stronger promises. +/// - An allocation created by invoking [`alloc`], [`alloc_zeroed`], or [`realloc`] can only be +/// freed by invoking [`dealloc`] or [`realloc`]. In particular, passing a pointer to such an +/// allocation directly to the underlying method on [`GlobalAlloc`] is not permitted. Until one of +/// those functions is called, it is undefined behavior to access the memory that backs this +/// allocation with any pointer not derived from the return value of this function (e.g., with +/// internal pointers the allocator might keep around). +/// - The pointer passed to [`dealloc`] or [`realloc`] must have been obtained by invoking [`alloc`], +/// [`alloc_zeroed`], or [`realloc`]. In particular, passing a pointer returned by the underlying +/// methods on [`GlobalAlloc`] is not permitted. +/// - [`alloc`] de-initializes the contents of the allocation before handing it to the user. So even +/// if you control the underlying allocator and know that it explicitly initialized this memory, +/// you cannot rely on it being initialized. For a [`realloc`] that grows an allocation, this +/// applies to the newly allocated part. +/// - [`dealloc`] de-initializes the contents of the allocation before handing it to the allocator. +/// So even if you know that the program previously initialized that memory, the allocator cannot +/// rely on it being initialized. For a [`realloc`] that shrinks an allocation, this applies to +/// the part being removed. +/// +/// [`alloc`]: ../../std/alloc/fn.alloc.html +/// [`alloc_zeroed`]: ../../std/alloc/fn.alloc_zeroed.html +/// [`dealloc`]: ../../std/alloc/fn.dealloc.html +/// [`realloc`]: ../../std/alloc/fn.realloc.html +/// +/// The first point means that you cannot rely on global allocations actually happening, even if +/// there are explicit global allocations in the source. The optimizer may detect unused global +/// allocations that it can either eliminate entirely or move to the stack and thus never invoke the +/// global allocator. The optimizer may further assume that allocation is infallible, so code that +/// used to fail due to allocator failures may now suddenly work because the optimizer worked around +/// the need for an allocation. More concretely, the following code example is unsound, irrespective +/// of whether your custom allocator allows counting how many allocations have happened. +/// +/// ```rust,ignore (unsound and has placeholders) +/// drop(Box::new(42)); +/// let number_of_heap_allocs = /* call private allocator API */; +/// unsafe { std::hint::assert_unchecked(number_of_heap_allocs > 0); } +/// ``` +/// +/// Note that the optimizations mentioned above are not the only +/// optimization that can be applied. You may generally not rely on global allocations +/// happening if they can be removed without changing program behavior. +/// Whether allocations happen or not is not part of the program behavior, even if it +/// could be detected via an allocator that tracks allocations by printing or otherwise +/// having side effects. +/// /// # Safety /// /// The `GlobalAlloc` trait is an `unsafe` trait for a number of reasons, and /// implementors must ensure that they adhere to these contracts: /// +/// * It is undefined behavior for the allocator to read, write, or deallocate any memory that +/// is *currently allocated*. This memory is owned by the user, the allocator must not touch it. +/// /// * It's undefined behavior if global allocators unwind. This restriction may /// be lifted in the future, but currently a panic from any of these /// functions may lead to memory unsafety. /// -/// * `Layout` queries and calculations in general must be correct. Callers of -/// this trait are allowed to rely on the contracts defined on each method, -/// and implementors must ensure such contracts remain true. -/// -/// * You must not rely on allocations actually happening, even if there are explicit -/// heap allocations in the source. The optimizer may detect unused allocations that it can either -/// eliminate entirely or move to the stack and thus never invoke the allocator. The -/// optimizer may further assume that allocation is infallible, so code that used to fail due -/// to allocator failures may now suddenly work because the optimizer worked around the -/// need for an allocation. More concretely, the following code example is unsound, irrespective -/// of whether your custom allocator allows counting how many allocations have happened. -/// -/// ```rust,ignore (unsound and has placeholders) -/// drop(Box::new(42)); -/// let number_of_heap_allocs = /* call private allocator API */; -/// unsafe { std::hint::assert_unchecked(number_of_heap_allocs > 0); } -/// ``` -/// -/// Note that the optimizations mentioned above are not the only -/// optimization that can be applied. You may generally not rely on heap allocations -/// happening if they can be removed without changing program behavior. -/// Whether allocations happen or not is not part of the program behavior, even if it -/// could be detected via an allocator that tracks allocations by printing or otherwise -/// having side effects. +/// * Callers of this trait are allowed to rely on the contracts defined on each method, and +/// implementors must ensure such contracts remain true. /// /// # Re-entrance /// @@ -133,7 +176,7 @@ use crate::{cmp, ptr}; /// - [`std::thread_local`], /// - [`std::thread::current`], /// - [`std::thread::park`] and [`std::thread::Thread`]'s [`unpark`] method and -/// [`Clone`] implementation. +/// [`Clone`] implementation. /// /// [`std`]: ../../std/index.html /// [`std::sync::Mutex`]: ../../std/sync/struct.Mutex.html @@ -174,7 +217,7 @@ pub unsafe trait GlobalAlloc { /// /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the [`handle_alloc_error`] function, - /// rather than directly invoking `panic!` or similar. + /// rather than directly invoking `panic!` or similar (but note that both may unwind). /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html #[stable(feature = "global_alloc", since = "1.28.0")] diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 1b8ec2a91478a..1145a085931f4 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -259,9 +259,12 @@ use crate::pin::PinCoerceUnsized; use crate::ptr::{self, NonNull}; use crate::range; +mod covariant_unsafe_cell; mod lazy; mod once; +#[unstable(feature = "covariant_unsafe_cell", issue = "159735")] +pub use covariant_unsafe_cell::CovariantUnsafeCell; #[stable(feature = "lazy_cell", since = "1.80.0")] pub use lazy::LazyCell; #[stable(feature = "once_cell", since = "1.70.0")] @@ -2150,8 +2153,9 @@ impl fmt::Display for RefMut<'_, T> { /// use `UnsafeCell` to wrap their data. /// /// Note that only the immutability guarantee for shared references is affected by `UnsafeCell`. The -/// uniqueness guarantee for mutable references is unaffected. There is *no* legal way to obtain -/// aliasing `&mut`, not even with `UnsafeCell`. +/// uniqueness guarantee for mutable references is unaffected. As explained below, for the duration +/// of the lifetime of an `&mut`, no other reference may exist and no pointer may be used to access +/// that memory; this applies even with `UnsafeCell`. /// /// `UnsafeCell` does nothing to avoid data races; they are still undefined behavior. If multiple /// threads have access to the same `UnsafeCell`, they must follow the usual rules of the @@ -2171,11 +2175,13 @@ impl fmt::Display for RefMut<'_, T> { /// /// - If you create a safe reference with lifetime `'a` (either a `&T` or `&mut T` reference), then /// you must not access the data in any way that contradicts that reference for the remainder of -/// `'a`. For example, this means that if you take the `*mut T` from an `UnsafeCell` and cast it -/// to a `&T`, then the data in `T` must remain immutable (modulo any `UnsafeCell` data found -/// within `T`, of course) until that reference's lifetime expires. Similarly, if you create a -/// `&mut T` reference, then you must not access the data within the -/// `UnsafeCell` until that reference expires. +/// `'a`, and you must not create any contradicting references. For example, this means that if +/// you take the `*mut T` from an `UnsafeCell` and cast it to a `&T`, then the data in `T` must +/// remain immutable (modulo any `UnsafeCell` data found within `T`, of course) until that +/// reference's lifetime expires, and no `&mut` reference to this data may be created. Similarly, +/// if you create a `&mut T` reference, then you must not access the data within the `UnsafeCell` +/// with any other pointer/reference until that reference expires, and no reference of any kind +/// may be created. /// /// - For both `&T` without `UnsafeCell<_>` and `&mut T`, you must also not deallocate the data /// until the reference expires. As a special exception, given a `&T`, any part of it that is @@ -2200,7 +2206,7 @@ impl fmt::Display for RefMut<'_, T> { /// Note that whilst mutating the contents of a `&UnsafeCell` (even while other /// `&UnsafeCell` references alias the cell) is /// ok (provided you enforce the above invariants some other way), it is still undefined behavior -/// to have multiple `&mut UnsafeCell` aliases. That is, `UnsafeCell` is a wrapper +/// to have aliasing `&mut UnsafeCell` (or aliasing `&mut` of *any* type). That is, `UnsafeCell` is a wrapper /// designed to have a special interaction with _shared_ accesses (_i.e._, through an /// `&UnsafeCell<_>` reference); there is no magic whatsoever when dealing with _exclusive_ /// accesses (_e.g._, through a `&mut UnsafeCell<_>`): neither the cell nor the wrapped value @@ -2223,36 +2229,20 @@ impl fmt::Display for RefMut<'_, T> { /// order to avoid its interior mutability property from spreading from `T` into the `Outer` type, /// thus this can cause distortions in the type size in these cases. /// -/// Note that the only valid way to obtain a `*mut T` pointer to the contents of a -/// _shared_ `UnsafeCell` is through [`.get()`] or [`.raw_get()`]. A `&T` or `&mut T` reference -/// can then be obtained from that pointer, as long as the aliasing rules outlined above are obeyed. -/// Even though `T` and `UnsafeCell` have the -/// same memory layout, the following is not allowed and undefined behavior: +/// The following examples make use of this guarantee: /// -/// ```rust,compile_fail +/// ```rust /// # use std::cell::UnsafeCell; -/// unsafe fn not_allowed(ptr: &UnsafeCell) -> &mut T { +/// /// # Safety +/// /// The caller must not call `get_mut_unchecked` again (on any alias of `ptr`) for the duration +/// /// of the lifetime of the returned reference. +/// # #[allow(invalid_reference_casting)] // FIXME should the lint really fire here? +/// unsafe fn get_mut_unchecked(ptr: &UnsafeCell) -> &mut T { /// let t = ptr as *const UnsafeCell as *mut T; -/// // This is undefined behavior, because the `*mut T` pointer -/// // was not obtained through `.get()` nor `.raw_get()`: /// unsafe { &mut *t } /// } /// ``` /// -/// Instead, do this: -/// -/// ```rust -/// # use std::cell::UnsafeCell; -/// // Safety: the caller must ensure that there are no references that -/// // point to the *contents* of the `UnsafeCell`. -/// unsafe fn get_mut(ptr: &UnsafeCell) -> &mut T { -/// unsafe { &mut *ptr.get() } -/// } -/// ``` -/// -/// Converting in the other direction from a `&mut T` -/// to an `&UnsafeCell` is allowed: -/// /// ```rust /// # use std::cell::UnsafeCell; /// fn get_shared(ptr: &mut T) -> &UnsafeCell { @@ -2263,7 +2253,6 @@ impl fmt::Display for RefMut<'_, T> { /// ``` /// /// [niche]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#niche -/// [`.raw_get()`]: `UnsafeCell::raw_get` /// /// # Examples /// @@ -2425,6 +2414,9 @@ impl UnsafeCell { /// must uphold the aliasing rules; see [the type-level docs][UnsafeCell#aliasing-rules] for /// more discussion and caveats. /// + /// This is equivalent to casting `self` to a raw pointer and then casting that raw + /// pointer to `*mut T`. + /// /// # Examples /// /// ``` @@ -2442,8 +2434,7 @@ impl UnsafeCell { #[rustc_should_not_be_called_on_const_items] pub const fn get(&self) -> *mut T { // We can just cast the pointer from `UnsafeCell` to `T` because of - // #[repr(transparent)]. This exploits std's special status, there is - // no guarantee for user code that this will work in future versions of the compiler! + // #[repr(transparent)]. self as *const UnsafeCell as *const T as *mut T } @@ -2477,6 +2468,8 @@ impl UnsafeCell { /// must uphold the aliasing rules; see [the type-level docs][UnsafeCell#aliasing-rules] for /// more discussion and caveats. /// + /// This is equivalent to casting `this` to `*mut T`. + /// /// [`get`]: UnsafeCell::get() /// /// # Examples diff --git a/library/core/src/cell/covariant_unsafe_cell.rs b/library/core/src/cell/covariant_unsafe_cell.rs new file mode 100644 index 0000000000000..3876cc404cd2e --- /dev/null +++ b/library/core/src/cell/covariant_unsafe_cell.rs @@ -0,0 +1,183 @@ +use crate::cell::UnsafeCell; +use crate::fmt; +use crate::ops::CoerceUnsized; +use crate::ptr::{self, NonNull}; + +/// **Co**variant version of [`UnsafeCell`]. +#[unstable(feature = "covariant_unsafe_cell", issue = "159735")] +#[repr(transparent)] +#[rustc_pub_transparent] +// Implementation note: +// +// We could make `CovariantUnsafeCell` be the canonical lang item and make `UnsafeCell` a wrapper +// over it, with `PhantomData<*mut T>`. That would however be a huge compiler change, without clear +// benefit. +// +// As such, `CovariantUnsafeCell` is wrapping `UnsafeCell` instead. It is a lang-item only to +// hardcode its variance to be **co**variant in `T`, even though it is wrapping `UnsafeCell` which +// is **in**variant in `T`. +#[lang = "covariant_unsafe_cell"] +pub struct CovariantUnsafeCell(UnsafeCell); + +#[unstable(feature = "covariant_unsafe_cell", issue = "159735")] +impl !Sync for CovariantUnsafeCell {} + +impl CovariantUnsafeCell { + /// Constructs a new instance of `CovariantUnsafeCell` which will wrap the specified value. + /// + /// All access to the inner value through `&CovariantUnsafeCell` requires `unsafe` code. + /// + /// # Examples + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// + /// let uc = CovariantUnsafeCell::new(5); + /// ``` + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[inline(always)] + pub const fn new(value: T) -> CovariantUnsafeCell { + CovariantUnsafeCell(UnsafeCell::new(value)) + } + + /// Unwraps the value, consuming the cell. + /// + /// # Examples + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// + /// let uc = CovariantUnsafeCell::new(5); + /// + /// let five = uc.into_inner(); + /// ``` + #[inline(always)] + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + pub const fn into_inner(self) -> T { + self.0.into_inner() + } +} + +impl CovariantUnsafeCell { + /// Gets a mutable non-null pointer to the wrapped value. + /// + /// This can be cast to a pointer of any kind. When creating (shared or mutable) references, you + /// must uphold the aliasing rules; see [the `UnsafeCell` type-level docs] for more discussion + /// and caveats. + /// + /// [the `UnsafeCell` type-level docs]: super::UnsafeCell#aliasing-rules + /// + /// # Examples + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// use std::ptr::NonNull; + /// + /// let uc = CovariantUnsafeCell::new(5); + /// + /// let ptr: NonNull = uc.get(); + /// ``` + #[inline(always)] + #[rustc_as_ptr] + #[rustc_should_not_be_called_on_const_items] + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + pub const fn get(&self) -> NonNull { + // We can just cast the pointer from `CovariantUnsafeCell` to `T` because of + // #[repr(transparent)]. + // + // Note that this is also known to be allowed for user code as per + // `#[rustc_pub_transparent]`. + // SAFETY: the pointer is not null, as it comes from a reference + unsafe { NonNull::new_unchecked(ptr::from_ref(self).cast_mut() as *mut T) } + } + + /// Returns a mutable reference to the underlying data. + /// + /// This call borrows the `CovariantUnsafeCell` mutably (at compile-time) which guarantees that + /// we possess the only reference. + /// + /// # Examples + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// + /// let mut c = CovariantUnsafeCell::new(5); + /// *c.get_mut() += 1; + /// + /// assert_eq!(*c.get_mut(), 6); + /// ``` + #[inline(always)] + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + pub const fn get_mut(&mut self) -> &mut T { + self.0.get_mut() + } + + /// Gets a mutable pointer to the wrapped value. + /// The difference from [`get`] is that this function accepts a raw pointer, + /// which is useful to avoid the creation of temporary references. + /// + /// This can be cast to a pointer of any kind. When creating (shared or mutable) references, you + /// must uphold the aliasing rules; see [the `UnsafeCell` type-level docs] for more discussion + /// and caveats. + /// + /// [`get`]: CovariantUnsafeCell::get() + /// + /// # Examples + /// + /// Gradual initialization of an `CovariantUnsafeCell` requires `raw_get`, as + /// calling `get` would require creating a reference to uninitialized data: + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// use std::mem::MaybeUninit; + /// + /// let m = MaybeUninit::>::uninit(); + /// unsafe { CovariantUnsafeCell::raw_get(m.as_ptr()).write(5); } + /// // avoid below which references to uninitialized data + /// // unsafe { CovariantUnsafeCell::get(&*m.as_ptr()).write(5); } + /// let uc = unsafe { m.assume_init() }; + /// + /// assert_eq!(uc.into_inner(), 5); + /// ``` + #[inline(always)] + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + pub const fn raw_get(this: *const Self) -> *mut T { + // We can just cast the pointer from `UnsafeCell` to `T` because of + // #[repr(transparent)]. + // + // Note that this is also known to be allowed for user code as per + // `#[rustc_pub_transparent]`. + this as *const T as *mut T + } +} + +#[unstable(feature = "coerce_unsized", issue = "18598")] +impl, U> CoerceUnsized> for CovariantUnsafeCell {} + +#[unstable(feature = "covariant_unsafe_cell", issue = "159735")] +impl fmt::Debug for CovariantUnsafeCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CovariantUnsafeCell").finish_non_exhaustive() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn _covarience<'short, 'long: 'short>( + x: CovariantUnsafeCell<&'long ()>, + ) -> CovariantUnsafeCell<&'short ()> { + x + } +} diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 6e670375fcc5e..c3f52d040d4c5 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -226,8 +226,6 @@ pub mod offload; #[unstable(feature = "contracts", issue = "128044")] pub mod contracts; -#[unstable(feature = "derive_macro_global_path", issue = "154645")] -pub use crate::macros::builtin::derive; #[stable(feature = "cfg_select", since = "1.95.0")] pub use crate::macros::cfg_select; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index b58d3b7f1f539..d69a5aad4c26b 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1771,7 +1771,7 @@ pub(crate) mod builtin { /// /// See [the reference] for more info. /// - /// [the reference]: ../reference/attributes/derive.html + /// [the reference]: ../../../reference/attributes/derive.html #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] pub macro derive($item:item) { diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index 6122ab12ec351..f2eb047d342bc 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -120,13 +120,9 @@ pub use crate::trace_macros; // (no public module for them to be re-exported from). #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] pub use crate::macros::builtin::{ - alloc_error_handler, bench, global_allocator, test, test_case, + alloc_error_handler, bench, derive, global_allocator, test, test_case, }; -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[doc(no_inline)] -pub use crate::macros::builtin::derive; - #[unstable(feature = "derive_const", issue = "118304")] pub use crate::macros::builtin::derive_const; diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 84447c06aaecf..9d39fbf770e91 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -53,6 +53,11 @@ //! The `#[global_allocator]` can only be used once in a crate //! or its recursive dependencies. //! +//! The global allocator is invoked via the functions in this module +//! ([`alloc`][crate::alloc::alloc], [`alloc_zeroed`], [`dealloc`], [`realloc`]). Note, however, +//! that invoking those functions is *not* equivalent to directly invoking the underlying methods on +//! the declared global allocator! See the documentation of those functions for details. +//! //! [^system-alloc]: Note that the Rust standard library internals may still //! directly call [`System`] when necessary (for example for the runtime //! support typically required to implement a global allocator, see [re-entrance] on [`GlobalAlloc`] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index e1061af1e7d6d..80ea577acd117 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -750,8 +750,6 @@ pub use core::cfg_select; reason = "`concat_bytes` is not stable enough for use and is subject to change" )] pub use core::concat_bytes; -#[unstable(feature = "derive_macro_global_path", issue = "154645")] -pub use core::derive; #[stable(feature = "matches_macro", since = "1.42.0")] #[allow(deprecated, deprecated_in_future)] pub use core::matches; diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index aeefec8b9e084..ee57e031c959c 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -115,13 +115,9 @@ pub use core::prelude::v1::trace_macros; // (no public module for them to be re-exported from). #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] pub use core::prelude::v1::{ - alloc_error_handler, bench, global_allocator, test, test_case, + alloc_error_handler, bench, derive, global_allocator, test, test_case, }; -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[doc(no_inline)] -pub use core::prelude::v1::derive; - #[unstable(feature = "derive_const", issue = "118304")] pub use core::prelude::v1::derive_const; diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index ee7e5f8586700..3c32ec34dee5b 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -263,6 +263,7 @@ impl PoisonError { /// or [`RwLock::read`](crate::sync::RwLock::read). /// /// This method may panic if std was built with `panic="abort"`. + #[doc(auto_cfg = false)] #[cfg(panic = "unwind")] #[stable(feature = "sync_poison", since = "1.2.0")] pub fn new(data: T) -> PoisonError { @@ -275,6 +276,7 @@ impl PoisonError { /// or [`RwLock::read`](crate::sync::RwLock::read). /// /// This method may panic if std was built with `panic="abort"`. + #[doc(auto_cfg = false)] #[cfg(not(panic = "unwind"))] #[stable(feature = "sync_poison", since = "1.2.0")] #[track_caller] diff --git a/library/std/src/sys/fs/windows/dir.rs b/library/std/src/sys/fs/windows/dir.rs index cd86f76bbcf83..5e69515b66599 100644 --- a/library/std/src/sys/fs/windows/dir.rs +++ b/library/std/src/sys/fs/windows/dir.rs @@ -6,7 +6,7 @@ use crate::os::windows::io::{ OwnedHandle, RawHandle, }; use crate::path::Path; -use crate::sys::api::{self, SetFileInformation, UnicodeStrRef, WinError}; +use crate::sys::api::{UnicodeStrRef, WinError}; use crate::sys::fs::windows::debug_path_handle; use crate::sys::fs::{File, FileAttr, OpenOptions}; use crate::sys::handle::Handle; @@ -127,16 +127,7 @@ impl Dir { let mut opts = OpenOptions::new(); opts.access_mode(c::DELETE); let handle = self.open_file_native(path, &opts, dir)?; - let info = c::FILE_DISPOSITION_INFO_EX { Flags: c::FILE_DISPOSITION_FLAG_DELETE }; - let result = unsafe { - c::SetFileInformationByHandle( - handle.as_raw_handle(), - c::FileDispositionInfoEx, - (&info).as_ptr(), - size_of::() as _, - ) - }; - if result == 0 { Err(api::get_last_error()).io_result() } else { Ok(()) } + File::from_inner(handle).delete().io_result() } fn rename_native(&self, from: &[u16], to_dir: &Self, to: &[u16], dir: bool) -> io::Result<()> { diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 44bedaf878e04..1d2ca88a2d0bd 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -839,6 +839,10 @@ impl Step for CargoMiri { SourceType::Submodule, &[], ); + // Run subcrate tests as well. + cargo.arg("--workspace"); + // Some tests need isolation disabled. + cargo.env("MIRIFLAGS", "-Zmiri-disable-isolation"); // If we are testing stage 2+ cargo miri, make sure that it works with the in-tree cargo. // We want to do this *somewhere* to ensure that Miri + nightly cargo actually works. diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs index 86b8e7b6f86aa..a2db26b782246 100644 --- a/src/librustdoc/passes/lint/html_tags.rs +++ b/src/librustdoc/passes/lint/html_tags.rs @@ -6,6 +6,8 @@ use std::ops::Range; use std::str::CharIndices; use itertools::Itertools as _; +use rustc_ast::attr::AttributeExt; +use rustc_ast::token::{CommentKind, DocFragmentKind}; use rustc_hir::HirId; use rustc_resolve::rustdoc::pulldown_cmark::{BrokenLink, Event, LinkType, Parser, Tag, TagEnd}; use rustc_resolve::rustdoc::source_span_for_markdown_range; @@ -16,7 +18,7 @@ use crate::html::markdown::main_body_opts; pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &str) { let tcx = cx.tcx; - let report_diag = |msg: String, range: &Range, is_open_tag: bool| { + let report_diag = |msg: String, range: &Range, mode: HtmlDiagMode| { let sp = match source_span_for_markdown_range(tcx, dox, range, &item.attrs.doc_strings) { Some((sp, _)) => sp, None => item.attr_span(tcx), @@ -34,7 +36,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & // We don't try to detect stuff `` because that's not valid HTML, // and we don't try to detect stuff `` because that's not valid Rust. let mut generics_end = range.end; - if is_open_tag + if mode == HtmlDiagMode::Unclosed && dox[..generics_end].ends_with('>') && let Some(mut generics_start) = extract_path_backwards(dox, range.start) { @@ -103,6 +105,78 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & ], Applicability::MaybeIncorrect, ); + } else if let HtmlDiagMode::Unopened { possible_pair: Some(possible_pair) } = mode { + let (reason_display_text, reason_range) = match possible_pair.reason { + HtmlOrMarkdownTag::Markdown(tag @ TagEnd::Paragraph, range) + if dox.as_bytes().get(range.end) == Some(&b'>') => + { + ( + format!( + "because the Markdown {} is interrupted by this block quote", + markdown_tag_name(tag) + ), + range.end..range.end, + ) + } + HtmlOrMarkdownTag::Markdown( + tag @ (TagEnd::Paragraph | TagEnd::TableCell), + range, + ) => ( + format!("because the Markdown {} ends here", markdown_tag_name(tag)), + range.end..range.end, + ), + HtmlOrMarkdownTag::Markdown(tag, range) => { + (format!("because of this Markdown {}", markdown_tag_name(tag)), range) + } + HtmlOrMarkdownTag::Html(name, range) => { + (format!("because of this HTML `{name}`"), range) + } + }; + if let HtmlOrMarkdownTag::Html(_, unclosed_tag_range) = + possible_pair.unclosed_tag + && let Some((unclosed_tag_span, _)) = source_span_for_markdown_range( + tcx, + dox, + &unclosed_tag_range, + &item.attrs.doc_strings, + ) + { + lint.span_label(sp, "this unopened tag"); + lint.span_label(unclosed_tag_span, "does not match this unclosed tag"); + } + if let Some((reason_span, _)) = source_span_for_markdown_range( + tcx, + dox, + &reason_range, + &item.attrs.doc_strings, + ) { + lint.span_label(reason_span, reason_display_text); + } + } else if let HtmlDiagMode::MarkdownNestedInRawText(html_tag_range, html_tag) = mode { + lint.span_label(sp, format!("Markdown translates this into HTML, but the browser parses it as {language}", language = html_tag.language())); + if + // get the span for this diagnostic, if possible + let Some((html_tag_span, _)) = source_span_for_markdown_range( + tcx, + dox, + &html_tag_range, + &item.attrs.doc_strings, + ) && + // this suggestion is only implemented for line doc comments + item.attrs.doc_strings.iter().all(|f| f.kind == DocFragmentKind::Sugared(CommentKind::Line)) && + // this suggestion is only implemented if every line doc comment has the same position (either outer or inner) + let Some(def_id) = item.def_id() && + let mut style_iter = inline::load_attrs(cx.tcx, def_id).iter().filter_map(|attr| attr.doc_resolution_scope()) && + let Some(doc_attr_style) = style_iter.next() && + style_iter.all(|style| style == doc_attr_style) + { + lint.span_suggestion( + html_tag_span, + "to turn off Markdown parsing, put the tag at the start of the line", + format!("\n{mark} {doc}", mark=doc_attr_style.line_doc_comment_prefix(), doc=&dox[html_tag_range.clone()]), + Applicability::MachineApplicable, + ); + } } }), ); @@ -110,7 +184,6 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & let mut tagp = TagParser::new(); let mut is_in_comment = None; - let mut in_code_block = false; let link_names = item.link_names(&cx.cache); @@ -148,37 +221,63 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & for (event, range) in p { match event { - Event::Start(Tag::CodeBlock(_)) => in_code_block = true, - Event::Html(text) | Event::InlineHtml(text) if !in_code_block => { + Event::Html(text) | Event::InlineHtml(text) => { tagp.extract_tags(&text, range, &mut is_in_comment, &report_diag) } - Event::End(TagEnd::CodeBlock) => in_code_block = false, + Event::Start(Tag::HtmlBlock) | Event::End(TagEnd::HtmlBlock) => {} + Event::Start(tag) => { + tagp.push_markdown_tag(tag.into(), range, &report_diag); + } + Event::End(tag) => { + tagp.pop_markdown_tag(tag, range, &report_diag); + } _ => {} } } if let Some(range) = is_in_comment { - report_diag("Unclosed HTML comment".to_string(), &range, false); + report_diag("Unclosed HTML comment".to_string(), &range, HtmlDiagMode::Incomplete); } else if let &Some(quote_pos) = &tagp.quote_pos { let qr = Range { start: quote_pos, end: quote_pos }; report_diag( format!("unclosed quoted HTML attribute on tag `{}`", &tagp.tag_name), &qr, - false, + HtmlDiagMode::Incomplete, ); } else { if !tagp.tag_name.is_empty() { report_diag( format!("incomplete HTML tag `{}`", &tagp.tag_name), &(tagp.tag_start_pos..dox.len()), - false, + HtmlDiagMode::Incomplete, ); } - for (tag, range) in tagp.tags.iter().filter(|(t, _)| { - let t = t.to_lowercase(); - !is_implicitly_self_closing(&t) - }) { - report_diag(format!("unclosed HTML tag `{tag}`"), range, true); + for tag in tagp.tags.iter().chain( + tagp.unclosed_tag_buf + .iter() + .map(|buffered_unclosed_tag| &buffered_unclosed_tag.unclosed_tag), + ) { + match tag { + HtmlOrMarkdownTag::Html(tag, range) => { + if !is_implicitly_self_closing(&tag.to_ascii_lowercase()) { + report_diag( + format!("unclosed HTML tag `{tag}`"), + range, + HtmlDiagMode::Unclosed, + ); + } + } + HtmlOrMarkdownTag::Markdown(tag, range) => { + report_diag( + format!( + "invalid tree with Markdown delimiter `{tag_name}`", + tag_name = markdown_tag_name(*tag) + ), + range, + HtmlDiagMode::Unclosed, + ); + } + } } } } @@ -254,10 +353,68 @@ fn is_valid_for_html_tag_name(c: char, is_empty: bool) -> bool { c.is_ascii_alphabetic() || !is_empty && (c == '-' || c.is_ascii_digit()) } +#[derive(Eq, PartialEq, Debug, Clone)] +enum HtmlOrMarkdownTag { + Html(String, Range), + Markdown(TagEnd, Range), +} + +impl HtmlOrMarkdownTag { + fn range(&self) -> Range { + match self { + HtmlOrMarkdownTag::Html(_, range) => range.clone(), + HtmlOrMarkdownTag::Markdown(_, range) => range.clone(), + } + } +} + +#[derive(Eq, PartialEq, Debug, Clone)] +struct BufferedUnclosedTag { + unclosed_tag: HtmlOrMarkdownTag, + reason: HtmlOrMarkdownTag, +} + +#[derive(Eq, PartialEq, Debug, Clone, Copy)] +enum HtmlRawTextTag { + Script, + Style, +} + +impl HtmlRawTextTag { + fn name(self) -> &'static str { + match self { + HtmlRawTextTag::Script => "script", + HtmlRawTextTag::Style => "style", + } + } + fn language(self) -> &'static str { + match self { + HtmlRawTextTag::Script => "JavaScript", + HtmlRawTextTag::Style => "CSS", + } + } + fn from_tag(tag: &str) -> Option { + match &tag.to_ascii_lowercase() { + "script" => Some(HtmlRawTextTag::Script), + "style" => Some(HtmlRawTextTag::Style), + _ => None, + } + } +} + +#[derive(Eq, PartialEq, Debug, Clone)] +enum HtmlDiagMode { + Unclosed, + Unopened { possible_pair: Option }, + Incomplete, + MarkdownNestedInRawText(Range, HtmlRawTextTag), +} + /// Parse html tags to ensure they are well-formed #[derive(Debug, Clone)] struct TagParser { - tags: Vec<(String, Range)>, + tags: Vec, + unclosed_tag_buf: Vec, /// Name of the tag that is being parsed, if we are within a tag. /// /// Since the `<` and name of a tag must appear on the same line with no whitespace, @@ -279,6 +436,7 @@ impl TagParser { fn new() -> Self { Self { tags: Vec::new(), + unclosed_tag_buf: Vec::new(), tag_name: String::with_capacity(8), tag_start_pos: 0, is_closing: false, @@ -289,34 +447,58 @@ impl TagParser { } } - fn drop_tag(&mut self, range: Range, f: &impl Fn(String, &Range, bool)) { - let tag_name_low = self.tag_name.to_lowercase(); - if let Some(pos) = self.tags.iter().rposition(|(t, _)| t.to_lowercase() == tag_name_low) { + fn drop_tag(&mut self, range: Range, f: &impl Fn(String, &Range, HtmlDiagMode)) { + let tag_name_low = self.tag_name.to_ascii_lowercase(); + let tag_name_is_match = |tag: &HtmlOrMarkdownTag| match tag { + HtmlOrMarkdownTag::Html(name, _span) => name.to_ascii_lowercase() == tag_name_low, + HtmlOrMarkdownTag::Markdown(..) => false, + }; + if let Some(pos) = self.tags.iter().rposition(tag_name_is_match) { // If the tag is nested inside a "` (the `h2` tag isn't required // but it helps for the visualization). - f(format!("unopened HTML tag `{}`", &self.tag_name), &range, false); + let mode = HtmlDiagMode::Unopened { + possible_pair: self + .unclosed_tag_buf + .iter() + .rposition(|buf| tag_name_is_match(&buf.unclosed_tag)) + .map(|pos| self.unclosed_tag_buf.remove(pos)), + }; + f(format!("unopened HTML tag `{}`", &self.tag_name), &range, mode); } } @@ -325,7 +507,7 @@ impl TagParser { &mut self, range: Range, lt_pos: usize, - f: &impl Fn(String, &Range, bool), + f: &impl Fn(String, &Range, HtmlDiagMode), ) { let global_pos = range.start + lt_pos; // is this check needed? @@ -337,7 +519,7 @@ impl TagParser { f( format!("incomplete HTML tag `{}`", &self.tag_name), &(self.tag_start_pos..global_pos), - false, + HtmlDiagMode::Incomplete, ); self.tag_parsed(); } @@ -348,7 +530,7 @@ impl TagParser { range: &Range, start_pos: usize, iter: &mut Peekable>, - f: &impl Fn(String, &Range, bool), + f: &impl Fn(String, &Range, HtmlDiagMode), ) { let mut prev_pos = start_pos; @@ -419,7 +601,7 @@ impl TagParser { pos: usize, c: char, iter: &mut Peekable>, - f: &impl Fn(String, &Range, bool), + f: &impl Fn(String, &Range, HtmlDiagMode), ) { // we can store this as a local, since html5 does require the `/` and `>` // to not be separated by whitespace. @@ -469,15 +651,22 @@ impl TagParser { if is_self_closing { // https://html.spec.whatwg.org/#parse-error-non-void-html-element-start-tag-with-trailing-solidus let valid = ALLOWED_UNCLOSED.contains(&&self.tag_name[..]) - || self.tags.iter().take(pos + 1).any(|(at, _)| { - let at = at.to_lowercase(); - at == "svg" || at == "math" + || self.tags.iter().take(pos + 1).any(|tag| match tag { + HtmlOrMarkdownTag::Html(at, _) => { + let at = at.to_ascii_lowercase(); + at == "svg" || at == "math" + } + HtmlOrMarkdownTag::Markdown(..) => false, }); if !valid { - f(format!("invalid self-closing HTML tag `{}`", self.tag_name), &r, false); + f( + format!("invalid self-closing HTML tag `{}`", self.tag_name), + &r, + HtmlDiagMode::Incomplete, + ); } } else if !self.tag_name.is_empty() { - self.tags.push((std::mem::take(&mut self.tag_name), r)); + self.tags.push(HtmlOrMarkdownTag::Html(std::mem::take(&mut self.tag_name), r)); } self.tag_parsed(); } @@ -493,7 +682,7 @@ impl TagParser { text: &str, range: Range, is_in_comment: &mut Option>, - f: &impl Fn(String, &Range, bool), + f: &impl Fn(String, &Range, HtmlDiagMode), ) { let mut iter = text.char_indices().peekable(); let mut prev_pos = 0; @@ -540,6 +729,123 @@ impl TagParser { } } } + + fn push_markdown_tag( + &mut self, + tag: TagEnd, + range: Range, + f: &impl Fn(String, &Range, HtmlDiagMode), + ) { + // If the tag is nested inside a " d* + self.tags.push(HtmlOrMarkdownTag::Html(tag.name().to_owned(), tag_range)); + } else { + // `tags` is used as a queue, meaning that everything after `pos` is included inside it. + // So `**` will look like `["*", "span"]`. So when closing `*`, we will still + // have `span`, meaning the tag wasn't closed as it should have. + self.unclosed_tag_buf.extend(self.tags.drain(pos + 1..).map(|unclosed_tag| { + BufferedUnclosedTag { + unclosed_tag, + reason: HtmlOrMarkdownTag::Markdown(tag_end, range.clone()), + } + })); + // Remove the tag that was originally closed + self.tags.pop(); + } + } else { + // It can happen for example in this case: `

` (the `h2` tag isn't required + // but it helps for the visualization). + let mode = HtmlDiagMode::Unopened { + possible_pair: self + .unclosed_tag_buf + .iter() + .rposition(|buf| tag_is_match(&buf.unclosed_tag)) + .map(|pos| self.unclosed_tag_buf.remove(pos)), + }; + f(format!("improperly nested Markdown {}", markdown_tag_name(tag_end)), &range, mode); + } + } +} + +fn markdown_tag_name(tag: TagEnd) -> &'static str { + match tag { + TagEnd::Paragraph => "paragraph", + TagEnd::Heading(..) => "heading", + TagEnd::BlockQuote => "block quote `>`", + TagEnd::CodeBlock => "code block", + TagEnd::HtmlBlock => "HTML", + TagEnd::List(true) => "numbered list", + TagEnd::List(false) => "bulleted list", + TagEnd::Item => "list item", + TagEnd::FootnoteDefinition => "footnote definition", + TagEnd::Table => "table", + TagEnd::TableHead => "table head", + TagEnd::TableRow => "table row", + TagEnd::TableCell => "table cell", + TagEnd::Emphasis => "emphasis", + TagEnd::Strong => "strong emphasis", + TagEnd::Strikethrough => "strikethrough", + TagEnd::Link => "link", + TagEnd::Image => "image", + TagEnd::MetadataBlock(..) => "front matter", + } } #[cfg(test)] diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 372829ea07a62..641d37e16f1b9 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -11,7 +11,6 @@ extern crate rustc_codegen_ssa; extern crate rustc_data_structures; extern crate rustc_driver; -extern crate rustc_hir; extern crate rustc_interface; extern crate rustc_log; extern crate rustc_middle; @@ -48,14 +47,9 @@ use miri::{ use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::sync::{self, DynSync}; use rustc_driver::Compilation; -use rustc_hir::{self as hir, Node}; use rustc_interface::interface::Config; use rustc_interface::util::DummyCodegenBackend; use rustc_log::tracing::debug; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::middle::exported_symbols::{ - ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel, -}; use rustc_middle::query::LocalCrate; use rustc_middle::ty::TyCtxt; use rustc_session::config::{CrateType, ErrorOutputType, OptLevel}; @@ -258,58 +252,14 @@ impl rustc_driver::Callbacks for MiriDepCompilerCalls { // Queries overridden here affect the data stored in `rmeta` files of dependencies, // which will be used later in non-`MIRI_BE_RUSTC` mode. config.override_queries = Some(|_, local_providers| { - // We need to add #[used] symbols to exported_symbols for `lookup_link_section`. - // FIXME handle this somehow in rustc itself to avoid this hack. - local_providers.queries.exported_non_generic_symbols = |tcx, LocalCrate| { - let reachable_set = tcx.with_stable_hashing_context(|mut hcx| { - tcx.reachable_set(()).to_sorted(&mut hcx, true) - }); - tcx.arena.alloc_from_iter( - // This is based on: - // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L62-L63 - // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L174 - reachable_set.into_iter().filter_map(|&local_def_id| { - // Do the same filtering that rustc does: - // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L84-L102 - // Otherwise it may cause unexpected behaviours and ICEs - // (https://github.com/rust-lang/rust/issues/86261). - let is_reachable_non_generic = matches!( - tcx.hir_node_by_def_id(local_def_id), - Node::Item(&hir::Item { - kind: hir::ItemKind::Static(..) | hir::ItemKind::Fn{ .. }, - .. - }) | Node::ImplItem(&hir::ImplItem { - kind: hir::ImplItemKind::Fn(..), - .. - }) - if !tcx.generics_of(local_def_id).requires_monomorphization(tcx) - ); - if !is_reachable_non_generic { - return None; - } - let codegen_fn_attrs = tcx.codegen_fn_attrs(local_def_id); - if codegen_fn_attrs.contains_extern_indicator() - || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) - || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) - { - Some(( - ExportedSymbol::NonGeneric(local_def_id.to_def_id()), - // Some dummy `SymbolExportInfo` here. We only use - // `exported_symbols` in shims/foreign_items.rs and the export info - // is ignored. - SymbolExportInfo { - level: SymbolExportLevel::C, - kind: SymbolExportKind::Text, - used: false, - rustc_std_internal_symbol: false, - }, - )) - } else { - None - } - }), - ) - } + // `exported_non_generic_symbols` is usually empty because we don't codegen anything. + // However, we need it for `lookup_link_section`. + // So overwrite the query with a version that dooes something even without codegen. + local_providers.queries.exported_non_generic_symbols = + |tcx, LocalCrate| rustc_codegen_ssa::back::exported_non_generic_symbols_helper(tcx); + // `exported_non_generic_symbols_helper` calls `reachable_non_generics`. + local_providers.queries.reachable_non_generics = + |tcx, LocalCrate| rustc_codegen_ssa::back::reachable_non_generics_helper(tcx); }); // Register our custom extra symbols. diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index c05c067d38b9d..8dc6b5f07b92e 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -116,34 +116,36 @@ pub fn path_ty_layout<'tcx>(cx: &impl LayoutOf<'tcx>, path: &[&str]) -> TyAndLay /// Call `f` for each exported symbol. pub fn iter_exported_symbols<'tcx>( tcx: TyCtxt<'tcx>, - mut f: impl FnMut(CrateNum, DefId) -> InterpResult<'tcx>, + mut f: impl FnMut(CrateNum, DefId, /* used */ bool) -> InterpResult<'tcx>, ) -> InterpResult<'tcx> { - // First, the symbols in the local crate. We can't use `exported_symbols` here as that - // skips `#[used]` statics (since `reachable_set` skips them in binary crates). - // So we walk all HIR items ourselves instead. + // First, the symbols in the local crate. We can't use `exported_symbols` here as that skips + // `#[used]` statics (since `reachable_set` does not specifically include them in binary crates, + // only in library crates). So we walk all HIR items ourselves instead. let crate_items = tcx.hir_crate_items(()); for def_id in crate_items.definitions() { - let exported = tcx.def_kind(def_id).has_codegen_attrs() && { - let codegen_attrs = tcx.codegen_fn_attrs(def_id); - codegen_attrs.contains_extern_indicator() - || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) - || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) - }; + if !tcx.def_kind(def_id).has_codegen_attrs() || tcx.is_foreign_item(def_id) { + continue; + } + let codegen_attrs = tcx.codegen_fn_attrs(def_id); + let used = codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) + || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER); + if !(used || codegen_attrs.contains_extern_indicator()) { + continue; + } // FIXME: `#[no_mangle]` makes no sense on a generic item, but still causes it to be // considered "extern". Remove this once `no_mangle_generic_items` is a hard error. - let exported_mono = exported && { + let mono = { let generics = tcx.generics_of(def_id); !generics.requires_monomorphization(tcx) }; - if exported_mono { - f(LOCAL_CRATE, def_id.into())?; + if mono { + f(LOCAL_CRATE, def_id.into(), used)?; } } // Next, all our dependencies. - // `dependency_formats` includes all the transitive information needed to link a crate, - // which is what we need here since we need to dig out `exported_symbols` from all transitive - // dependencies. + // `dependency_formats` includes all the transitive information needed to link a crate, which is + // what we need to dig out `exported_symbols` from all transitive dependencies. let dependency_formats = tcx.dependency_formats(()); // Find the dependencies of the executable we are running. let dependency_format = dependency_formats @@ -157,11 +159,12 @@ pub fn iter_exported_symbols<'tcx>( continue; // Already handled above } - // We can ignore `_export_info` here: we are a Rust crate, and everything is exported - // from a Rust crate. - for &(symbol, _export_info) in tcx.exported_non_generic_symbols(cnum) { - if let ExportedSymbol::NonGeneric(def_id) = symbol { - f(cnum, def_id)?; + for &(symbol, export_info) in tcx.exported_non_generic_symbols(cnum) { + if let ExportedSymbol::NonGeneric(def_id) = symbol + // Sometimes Rust has to re-export FFI imports; skip those. + && !tcx.is_foreign_item(def_id) + { + f(cnum, def_id, export_info.used)?; } } } @@ -961,8 +964,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut array = vec![]; - iter_exported_symbols(tcx, |_cnum, def_id| { + iter_exported_symbols(tcx, |_cnum, def_id, used| { let attrs = tcx.codegen_fn_attrs(def_id); + if !used { + // We don't know if the symbol is actually going to be in the final binary, + // so we conservatively skip it. + return interp_ok(()); + } let Some(link_section) = attrs.link_section else { return interp_ok(()); }; diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index efe597a5c1f7d..683e9095f9b0c 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -132,12 +132,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { is_weak: bool, } let mut symbol_target: Option> = None; - helpers::iter_exported_symbols(tcx, |cnum, def_id| { + helpers::iter_exported_symbols(tcx, |cnum, def_id, _used| { let attrs = tcx.codegen_fn_attrs(def_id); - // Skip over imports of items. - if tcx.is_foreign_item(def_id) { - return interp_ok(()); - } // Skip over items without an explicitly defined symbol name. if !(attrs.symbol_name.is_some() || attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) diff --git a/src/tools/miri/test-cargo-miri/exported-symbol-dep/src/lib.rs b/src/tools/miri/test-cargo-miri/exported-symbol-dep/src/lib.rs index 5b8a314ae7324..cd2a1e902fc2b 100644 --- a/src/tools/miri/test-cargo-miri/exported-symbol-dep/src/lib.rs +++ b/src/tools/miri/test-cargo-miri/exported-symbol-dep/src/lib.rs @@ -11,3 +11,49 @@ impl AssocFn { -123456 } } + +// Also check static constructors in dependencies are run. + +#[rustfmt::skip] +#[macro_export] +macro_rules! ctor { + ($ident:ident = $ctor:ident) => { + #[cfg_attr( + all(any( + target_os = "linux", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "haiku", + target_os = "illumos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "none", + target_family = "wasm", + )), + link_section = ".init_array" + )] + #[cfg_attr(windows, link_section = ".CRT$XCU")] + #[cfg_attr( + any(target_os = "macos", target_os = "ios"), + // We do not set the `mod_init_funcs` flag here since ctor/inventory also do not do + // that. See . + link_section = "__DATA,__mod_init_func" + )] + #[used] + static $ident: unsafe extern "C" fn() = $ctor; + }; +} + +static mut INITIALIZED: bool = false; + +unsafe extern "C" fn ctor() { + unsafe { INITIALIZED = true }; +} + +pub fn check_initialized() { + assert!(unsafe { INITIALIZED }); +} + +ctor! { CTOR = ctor } diff --git a/src/tools/miri/test-cargo-miri/exported-symbol/src/lib.rs b/src/tools/miri/test-cargo-miri/exported-symbol/src/lib.rs index de55eb2a1a5a0..ceb8a3633c5e1 100644 --- a/src/tools/miri/test-cargo-miri/exported-symbol/src/lib.rs +++ b/src/tools/miri/test-cargo-miri/exported-symbol/src/lib.rs @@ -1 +1,2 @@ extern crate exported_symbol_dep; +pub use exported_symbol_dep::check_initialized; diff --git a/src/tools/miri/test-cargo-miri/src/main.rs b/src/tools/miri/test-cargo-miri/src/main.rs index 00a239a9161a1..1568da43afe30 100644 --- a/src/tools/miri/test-cargo-miri/src/main.rs +++ b/src/tools/miri/test-cargo-miri/src/main.rs @@ -67,6 +67,10 @@ fn main() { mod test { use byteorder_2::{BigEndian, ByteOrder}; + extern crate cargo_miri_test; + extern crate exported_symbol; + extern crate issue_rust_86261; + // Make sure in-crate tests with dev-dependencies work #[test] fn dev_dependency() { @@ -75,9 +79,6 @@ mod test { #[test] fn exported_symbol() { - extern crate cargo_miri_test; - extern crate exported_symbol; - extern crate issue_rust_86261; // Test calling exported symbols in (transitive) dependencies. // Repeat calls to make sure the `Instance` cache is not broken. for _ in 0..3 { @@ -95,4 +96,9 @@ mod test { unsafe { no_mangle_generic() } } } + + #[test] + fn static_initializer_in_dep() { + exported_symbol::check_initialized(); + } } diff --git a/src/tools/miri/tests/pass/global_allocator.rs b/src/tools/miri/tests/pass/global_allocator.rs index 9a40c322b366e..ecb3a5e46e3a4 100644 --- a/src/tools/miri/tests/pass/global_allocator.rs +++ b/src/tools/miri/tests/pass/global_allocator.rs @@ -7,10 +7,12 @@ static ALLOCATOR: Allocator = Allocator; struct Allocator; +const SIZE: usize = 16 * 7; + unsafe impl GlobalAlloc for Allocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { // use specific size to avoid getting triggered by rt - if layout.size() == 123 { + if layout.size() == SIZE { println!("Allocated!") } @@ -18,7 +20,7 @@ unsafe impl GlobalAlloc for Allocator { } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - if layout.size() == 123 { + if layout.size() == SIZE { println!("Deallocated!") } @@ -27,13 +29,16 @@ unsafe impl GlobalAlloc for Allocator { } fn main() { - // Only okay because we explicitly set a global allocator that uses the system allocator! - let l = Layout::from_size_align(123, 1).unwrap(); + // Below we mix using `Global` and `System`. This is undefined behavior that Miri should + // detect, but currently it does not. + + let l = Layout::from_size_align(SIZE, 1).unwrap(); let ptr = Global.allocate(l).unwrap().as_non_null_ptr(); // allocating with Global... unsafe { System.deallocate(ptr, l); } // ... and deallocating with System. + let l = Layout::from_size_align(SIZE, 16).unwrap(); let ptr = System.allocate(l).unwrap().as_non_null_ptr(); // allocating with System... unsafe { Global.deallocate(ptr, l); diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index aaf40ceeadf71..9985fa1d7bcd5 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -539,6 +539,22 @@ async function loadSearchJS(doc_folder, resource_suffix) { }; } +/** + * Returns true if `fileName` looks like a proper rustdoc-js test file. + * + * Mirrors compiletest's `is_test` filtering so editor temp/autosave files + * (for example Emacs `.#foo.js`) are not treated as tests. + */ +function isTestFile(fileName) { + if (!fileName.endsWith(".js")) { + return false; + } + + // `.`, `#`, and `~` are common temp-file prefixes. + const invalidPrefixes = [".", "#", "~"]; + return !invalidPrefixes.some(prefix => fileName.startsWith(prefix)); +} + function showHelp() { console.log("rustdoc-js options:"); console.log(" --doc-folder [PATH] : location of the generated doc folder"); @@ -626,7 +642,7 @@ async function main(argv) { } } else if (opts["test_folder"].length !== 0) { for (const file of fs.readdirSync(opts["test_folder"])) { - if (!file.endsWith(".js")) { + if (!isTestFile(file)) { continue; } process.stdout.write(`Testing ${file} ... `); diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml index a07c2029f4b38..5a7e253b4fdb4 100644 --- a/src/tools/wasm-component-ld/Cargo.toml +++ b/src/tools/wasm-component-ld/Cargo.toml @@ -10,4 +10,4 @@ name = "wasm-component-ld" path = "src/main.rs" [dependencies] -wasm-component-ld = "0.5.26" +wasm-component-ld = "0.5.27" diff --git a/tests/assembly-llvm/asm/global_asm.rs b/tests/assembly-llvm/asm/global_asm.rs index 8a4bf98c7450b..3a7641cc7f76f 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 + _RNSC[[CRATE_IDENT]]_10global_asm3cgu.0] +global_asm!("lea rax, [rip + {}]", const &1); +// CHECK: lea rax, [rip + _RNSC[[CRATE_IDENT]]_10global_asm3cgu.1+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/crashes/138088.rs b/tests/crashes/138088.rs deleted file mode 100644 index bd0d0949d43d2..0000000000000 --- a/tests/crashes/138088.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ known-bug: #138088 -#![feature(min_generic_const_args)] -trait Bar { - fn x(&self) -> [i32; core::direct_const_arg!(Bar::x)]; -} diff --git a/tests/rustdoc-html/doc-cfg/mutually-exclusive-cfg-item.rs b/tests/rustdoc-html/doc-cfg/mutually-exclusive-cfg-item.rs new file mode 100644 index 0000000000000..d168bcdf0b41a --- /dev/null +++ b/tests/rustdoc-html/doc-cfg/mutually-exclusive-cfg-item.rs @@ -0,0 +1,24 @@ +// An item split into mutually-exclusive `#[cfg(..)]` variants that together cover +// every configuration must not get a portability note when `auto_cfg` is disabled. +// Regression test for . + +#![feature(doc_cfg)] +#![crate_name = "foo"] + +pub struct S; + +impl S { + //@ has 'foo/struct.S.html' + //@ count - '//*[@id="method.new"]/..//*[@class="stab portability"]' 0 + #[doc(auto_cfg = false)] + #[cfg(panic = "unwind")] + pub fn new() -> S { + S + } + + #[doc(auto_cfg = false)] + #[cfg(not(panic = "unwind"))] + pub fn new() -> S { + S + } +} diff --git a/tests/rustdoc-ui/lints/invalid-html-tags-correct-script-style.fixed b/tests/rustdoc-ui/lints/invalid-html-tags-correct-script-style.fixed new file mode 100644 index 0000000000000..a44a00106097f --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-html-tags-correct-script-style.fixed @@ -0,0 +1,30 @@ +//@ run-rustfix +#![deny(rustdoc::invalid_html_tags)] +//~^ NOTE the lint level is defined here + +//! Warn on Markdown text written in RAWTEXT HTML tags, +//! like *this: +//! +//~^ ERROR nested Markdown emphasis in HTML `script` tag +//~| NOTE Markdown translates this into HTML, but the browser parses it as JavaScript +//~| HELP to turn off Markdown parsing, put the tag at the start of the line +//! +//! Or +//! this* +//~^ ERROR nested Markdown emphasis in HTML `script` tag +//~| NOTE Markdown translates this into HTML, but the browser parses it as JavaScript +//~| HELP to turn off Markdown parsing, put the tag at the start of the line + +/// One +/// +//~^ ERROR nested Markdown emphasis in HTML `script` tag +//~| NOTE Markdown translates this into HTML, but the browser parses it as JavaScript +//~| HELP to turn off Markdown parsing, put the tag at the start of the line +pub struct ScriptMarkdown; + +/// One +/// +//~^ ERROR nested Markdown emphasis in HTML `style` tag +//~| NOTE Markdown translates this into HTML, but the browser parses it as CSS +//~| HELP to turn off Markdown parsing, put the tag at the start of the line +pub struct StyleMarkdown; diff --git a/tests/rustdoc-ui/lints/invalid-html-tags-correct-script-style.rs b/tests/rustdoc-ui/lints/invalid-html-tags-correct-script-style.rs new file mode 100644 index 0000000000000..372612cbb683b --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-html-tags-correct-script-style.rs @@ -0,0 +1,26 @@ +//@ run-rustfix +#![deny(rustdoc::invalid_html_tags)] +//~^ NOTE the lint level is defined here + +//! Warn on Markdown text written in RAWTEXT HTML tags, +//! like *this: +//~^ ERROR nested Markdown emphasis in HTML `script` tag +//~| NOTE Markdown translates this into HTML, but the browser parses it as JavaScript +//~| HELP to turn off Markdown parsing, put the tag at the start of the line +//! +//! Or this* +//~^ ERROR nested Markdown emphasis in HTML `script` tag +//~| NOTE Markdown translates this into HTML, but the browser parses it as JavaScript +//~| HELP to turn off Markdown parsing, put the tag at the start of the line + +/// One +//~^ ERROR nested Markdown emphasis in HTML `script` tag +//~| NOTE Markdown translates this into HTML, but the browser parses it as JavaScript +//~| HELP to turn off Markdown parsing, put the tag at the start of the line +pub struct ScriptMarkdown; + +/// One +//~^ ERROR nested Markdown emphasis in HTML `style` tag +//~| NOTE Markdown translates this into HTML, but the browser parses it as CSS +//~| HELP to turn off Markdown parsing, put the tag at the start of the line +pub struct StyleMarkdown; diff --git a/tests/rustdoc-ui/lints/invalid-html-tags-correct-script-style.stderr b/tests/rustdoc-ui/lints/invalid-html-tags-correct-script-style.stderr new file mode 100644 index 0000000000000..4bd19ee60ef1e --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-html-tags-correct-script-style.stderr @@ -0,0 +1,55 @@ +error: improperly nested Markdown emphasis in HTML `script` tag + --> $DIR/invalid-html-tags-correct-script-style.rs:6:10 + | +LL | //! like *this: + | ^^^^^^^^^^^^^^^^^ Markdown translates this into HTML, but the browser parses it as JavaScript + | +note: the lint level is defined here + --> $DIR/invalid-html-tags-correct-script-style.rs:2:9 + | +LL | #![deny(rustdoc::invalid_html_tags)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: to turn off Markdown parsing, put the tag at the start of the line + | +LL ~ //! like *this: +LL ~ //! + | + +error: nested Markdown emphasis in HTML `script` tag + --> $DIR/invalid-html-tags-correct-script-style.rs:11:17 + | +LL | //! Or this* + | ^^^^^^^^^^^^^^^^^ Markdown translates this into HTML, but the browser parses it as JavaScript + | +help: to turn off Markdown parsing, put the tag at the start of the line + | +LL ~ //! Or +LL ~ //! this* + | + +error: nested Markdown emphasis in HTML `script` tag + --> $DIR/invalid-html-tags-correct-script-style.rs:16:17 + | +LL | /// One + | ^^^^^^ Markdown translates this into HTML, but the browser parses it as JavaScript + | +help: to turn off Markdown parsing, put the tag at the start of the line + | +LL ~ /// One +LL ~ /// + | + +error: nested Markdown emphasis in HTML `style` tag + --> $DIR/invalid-html-tags-correct-script-style.rs:22:16 + | +LL | /// One + | ^^^^^^ Markdown translates this into HTML, but the browser parses it as CSS + | +help: to turn off Markdown parsing, put the tag at the start of the line + | +LL ~ /// One +LL ~ /// + | + +error: aborting due to 4 previous errors + diff --git a/tests/rustdoc-ui/lints/invalid-html-tags.rs b/tests/rustdoc-ui/lints/invalid-html-tags.rs index f5700e5846e51..d0aa97c9e4074 100644 --- a/tests/rustdoc-ui/lints/invalid-html-tags.rs +++ b/tests/rustdoc-ui/lints/invalid-html-tags.rs @@ -1,4 +1,5 @@ #![deny(rustdoc::invalid_html_tags)] +//~^ NOTE the lint level is defined here //!

💩

//~^ ERROR unclosed HTML tag `p` @@ -211,3 +212,98 @@ pub fn no_error_7() {} //~^ ERROR Unclosed HTML comment //~| ERROR incomplete HTML tag `p` pub fn v() {} + +/// *Improperly nested* HTML +//~^ ERROR unopened HTML tag `span` +//~| NOTE this unopened tag +//~| NOTE does not match this unclosed tag +//~| NOTE because of this Markdown emphasis +pub fn w() {} + +/// Improperly *nested HTML* +//~^ ERROR improperly nested Markdown emphasis +//~| NOTE because of this HTML `span` +pub fn x() {} + +/// Improperly nested HTML +//~^ ERROR unopened HTML tag `span` +//~| NOTE this unopened tag +//~| NOTE does not match this unclosed tag +//~| NOTE because of this HTML `em` +pub fn y() {} + +/// Improperly nested +/// +/// HTML emphasis +//~^ ERROR unopened HTML tag `em` +//~| NOTE this unopened tag +//~^^^^^ NOTE does not match this unclosed tag +//~^^^^^ NOTE because the Markdown paragraph ends here +pub fn z() {} + +/// +pub fn no_error_8() {} + +/// +pub fn no_error_9() {} + +/// +pub fn no_error_10() {} + +/// The Markdown parser takes care of its own syntax +/// +/// *testing __one two* three__ +pub fn no_error_11() {} + +///

Example +/// +/// ```console +/// error +/// ``` +/// +///
+pub fn no_error_12() {} + +/// Figure 1: an X +/// +/// +/// +//~^ ERROR unopened HTML tag `svg` +//~| NOTE this unopened tag +//~^^^^^^^^ NOTE does not match this unclosed tag +//~^^^^^ NOTE because the Markdown paragraph is interrupted by this block quote +pub struct Diagram; + +/// | left | right | +/// |------------------|--------------| +/// | `one|two` | `three|four` | +//~^ ERROR unopened HTML tag `i` +//~| NOTE this unopened tag +//~| NOTE does not match this unclosed tag +//~| NOTE because the Markdown table cell ends here +pub struct Table1; + +/// | one | two | +/// |-------|-----| +/// |
| foo | +///
+//~^ ERROR unopened HTML tag `div` +//~| NOTE this unopened tag +//~^^^^ NOTE does not match this unclosed tag +//~^^^^^ NOTE because the Markdown table cell ends here +pub struct Table2; diff --git a/tests/rustdoc-ui/lints/invalid-html-tags.stderr b/tests/rustdoc-ui/lints/invalid-html-tags.stderr index 3256004ddb6ab..15b88496b7557 100644 --- a/tests/rustdoc-ui/lints/invalid-html-tags.stderr +++ b/tests/rustdoc-ui/lints/invalid-html-tags.stderr @@ -1,5 +1,5 @@ error: unclosed HTML tag `p` - --> $DIR/invalid-html-tags.rs:3:5 + --> $DIR/invalid-html-tags.rs:4:5 | LL | //!

💩

| ^^^ @@ -11,115 +11,115 @@ LL | #![deny(rustdoc::invalid_html_tags)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unclosed HTML tag `p` - --> $DIR/invalid-html-tags.rs:3:9 + --> $DIR/invalid-html-tags.rs:4:9 | LL | //!

💩

| ^^^ error: unclosed HTML tag `unknown` - --> $DIR/invalid-html-tags.rs:11:5 + --> $DIR/invalid-html-tags.rs:12:5 | LL | /// | ^^^^^^^^^ error: unclosed HTML tag `script` - --> $DIR/invalid-html-tags.rs:14:5 + --> $DIR/invalid-html-tags.rs:15:5 | LL | ///