diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index 79cee81d0fa2d..91c0bc0b59bca 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -369,6 +369,43 @@ impl ExternAbi { | Self::Swift => true, } } + + /// Returns whether safe functions with this ABI implement the `Fn` traits. + /// `false` only for uncallable or internal ABIs. + pub fn is_fn_trait_compatible(self) -> bool { + match self { + ExternAbi::C { .. } + | ExternAbi::System { .. } + | ExternAbi::Rust + | ExternAbi::RustCold + | ExternAbi::RustPreserveNone + | ExternAbi::RustTail + | ExternAbi::EfiApi + | ExternAbi::Swift + | ExternAbi::Aapcs { .. } + | ExternAbi::CmseNonSecureCall + | ExternAbi::CmseNonSecureEntry + | ExternAbi::Cdecl { .. } + | ExternAbi::Stdcall { .. } + | ExternAbi::Fastcall { .. } + | ExternAbi::Thiscall { .. } + | ExternAbi::Vectorcall { .. } + | ExternAbi::SysV64 { .. } + | ExternAbi::Win64 { .. } => true, + ExternAbi::RustCall + | ExternAbi::RustInvalid + | ExternAbi::LlvmIntrinsic + | ExternAbi::Custom + | ExternAbi::GpuKernel + | ExternAbi::PtxKernel + | ExternAbi::AvrInterrupt + | ExternAbi::AvrNonBlockingInterrupt + | ExternAbi::Msp430Interrupt + | ExternAbi::RiscvInterruptM + | ExternAbi::RiscvInterruptS + | ExternAbi::X86Interrupt => false, + } + } } pub fn all_names() -> Vec<&'static str> { diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index ebc5c20da7f04..ce33e5cba69cc 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -503,7 +503,7 @@ pub struct DocAttribute { pub keyword: Option<(Symbol, Span)>, pub attribute: Option<(Symbol, Span)>, pub masked: Option, - pub notable_trait: Option, + pub notable_trait: Option<(Option<(NotableTraitColor, Span)>, Span)>, pub search_unbox: Option, // valid on crate @@ -520,6 +520,41 @@ pub struct DocAttribute { pub no_crate_inject: Option, } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(StableHash, Encodable, Decodable, PrintAttribute)] +pub enum NotableTraitColor { + Grey, + Red, + Green, + Yellow, + Blue, + Magenta, + Cyan, + Transparent, +} + +impl From for &'static str { + fn from(color: NotableTraitColor) -> &'static str { + use NotableTraitColor::*; + match color { + Grey => "grey", + Red => "red", + Green => "green", + Yellow => "yellow", + Blue => "blue", + Magenta => "magenta", + Cyan => "cyan", + Transparent => "transparent", + } + } +} + +impl std::fmt::Display for NotableTraitColor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str((*self).into()) + } +} + impl rustc_serialize::Encodable for DocAttribute { fn encode(&self, encoder: &mut E) { let DocAttribute { diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index e1181e5eb056d..bcc4b71b0ca23 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -2,7 +2,7 @@ use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit}; use rustc_attr_ir::target::{AstTarget, ConstTypeAstTarget, Target}; use rustc_attr_ir::{ AttributeKind, CfgEntry, CfgHideShow, DocAttribute, DocCfgHideShow, DocCfgHideShowValue, - DocInline, HideOrShow, + DocInline, HideOrShow, NotableTraitColor, }; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry}; use rustc_errors::{Applicability, MultiSpan, msg}; @@ -15,13 +15,14 @@ use super::{AcceptMapping, AttributeParser, template}; use crate::context::{AcceptContext, FinalizeContext}; use crate::diagnostics::{ DocAliasBadChar, DocAliasDuplicated, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, - DocAttrNotCrateLevel, DocAttributeNotAttribute, DocAutoCfgExpectsHideOrShow, - DocAutoCfgHideShowExpectsList, DocAutoCfgHideShowNoIdentBeforeValues, - DocAutoCfgHideShowUnexpectedItem, DocAutoCfgHideShowUnexpectedItemAfterValues, - DocAutoCfgHideShowValuesMix, DocAutoCfgWrongLiteral, DocInlineConflict, DocInlineOnlyUse, - DocKeywordAttributeNotAnonConst, DocKeywordNotKeyword, DocTestLiteral, DocTestTakesList, - DocTestUnknown, DocUnknownAny, DocUnknownInclude, DocUnknownPasses, DocUnknownPlugins, - DocUnknownSpotlight, ExpectedNameValue, ExpectedNoArgs, IllFormedAttributeInput, MalformedDoc, + DocAttrNotCrateLevel, DocAttrNotTraitLevel, DocAttributeNotAttribute, + DocAutoCfgExpectsHideOrShow, DocAutoCfgHideShowExpectsList, + DocAutoCfgHideShowNoIdentBeforeValues, DocAutoCfgHideShowUnexpectedItem, + DocAutoCfgHideShowUnexpectedItemAfterValues, DocAutoCfgHideShowValuesMix, + DocAutoCfgWrongLiteral, DocInlineConflict, DocInlineOnlyUse, DocKeywordAttributeNotAnonConst, + DocKeywordNotKeyword, DocTestLiteral, DocTestTakesList, DocTestUnknown, DocUnknownAny, + DocUnknownInclude, DocUnknownPasses, DocUnknownPlugins, DocUnknownSpotlight, ExpectedNameValue, + ExpectedNoArgs, IllFormedAttributeInput, InvalidNotableTraitAttr, MalformedDoc, UnusedDuplicate, }; use crate::parser::{ @@ -85,6 +86,82 @@ fn expected_string_literal( cx.emit_lint(INVALID_DOC_ATTRIBUTES, MalformedDoc, span); } +fn parse_notable_trait( + cx: &mut AcceptContext<'_, '_>, + path: &OwnedPathParser, + args: &ArgParser, + attr_value: &mut Option<(Option<(NotableTraitColor, Span)>, Span)>, + attr_name: Symbol, +) { + let span = path.span(); + + let notable_trait_color_attr = match args { + ArgParser::NoArgs => None, + ArgParser::List(meta_item_list_parser) => { + if meta_item_list_parser.is_empty() { + None + } else if let Some(meta_item) = meta_item_list_parser.as_single() { + Some(meta_item) + } else { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, InvalidNotableTraitAttr, span); + return; + } + } + ArgParser::NameValue(_) => { + // FIXME(164728): every invalid notable trait attribute lint produces exactly + // the same warning and we should switch to more specific diagnostics + cx.emit_lint(INVALID_DOC_ATTRIBUTES, InvalidNotableTraitAttr, span); + return; + } + }; + + let notable_trait_color_and_span = + if let Some(notable_trait_color_attr) = notable_trait_color_attr { + let Some(notable_trait_color_attr) = notable_trait_color_attr.meta_item() else { + // FIXME(164728): every invalid notable trait attribute lint produces exactly + // the same warning and we should switch to more specific diagnostics + cx.emit_lint(INVALID_DOC_ATTRIBUTES, InvalidNotableTraitAttr, span); + return; + }; + if !notable_trait_color_attr.path().word_is(sym::color) { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, InvalidNotableTraitAttr, span); + return; + } + let Some(notable_trait_color) = notable_trait_color_attr.args().as_name_value() else { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, InvalidNotableTraitAttr, span); + return; + }; + let Some(notable_trait_color) = notable_trait_color.value_as_str() else { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, InvalidNotableTraitAttr, span); + return; + }; + let notable_trait_color = match notable_trait_color.as_str() { + "grey" | "gray" => NotableTraitColor::Grey, + "red" => NotableTraitColor::Red, + "green" => NotableTraitColor::Green, + "yellow" => NotableTraitColor::Yellow, + "blue" => NotableTraitColor::Blue, + "magenta" => NotableTraitColor::Magenta, + "cyan" => NotableTraitColor::Cyan, + "transparent" => NotableTraitColor::Transparent, + _ => { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, InvalidNotableTraitAttr, span); + return; + } + }; + Some((notable_trait_color, notable_trait_color_attr.span())) + } else { + None + }; + + if cx.shared.target != Target::Trait { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, DocAttrNotTraitLevel { span, attr_name }, span); + return; + } + + *attr_value = Some((notable_trait_color_and_span, span)); +} + fn parse_keyword_and_attribute( cx: &mut AcceptContext<'_, '_>, path: &OwnedPathParser, @@ -174,14 +251,14 @@ impl DocParser { } Some(sym::attr) => { let Some(list) = args.as_list() else { - // FIXME: remove this method once merged and uncomment the line below instead. - // cx.expected_list(cx.attr_span, args); + // FIXME(164728): remove this method once merged and uncomment the line + // below instead. cx.expected_list(cx.attr_span, args); let span = cx.attr_span; cx.emit_lint(INVALID_DOC_ATTRIBUTES, MalformedDoc, span); return; }; - - // FIXME: convert list into a Vec of `AttributeKind` because current code is awful. + // FIXME(164728): remove this method once merged and uncomment the line + // below instead. cx.expected_list(cx.attr_span, args); for attr in list.mixed() { // Arguments of `attr` are checked via the span, so can be safely ignored attr.ignore_args(); @@ -621,7 +698,13 @@ impl DocParser { } Some(sym::notable_trait) => { gated!(doc_notable_trait); - no_args!(notable_trait) + parse_notable_trait( + cx, + path, + args, + &mut self.attribute.notable_trait, + sym::notable_trait, + ) } Some(sym::keyword) => { gated!(rustdoc_internals); diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index fb7b04a6437b6..4059114456431 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -400,6 +400,13 @@ pub(crate) struct ExpectedNoArgs; )] pub(crate) struct ExpectedNameValue; +#[derive(Diagnostic)] +#[diag("expected either `doc(notable_trait)` or `doc(notable_trait(color=\"...\"))`")] +#[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" +)] +pub(crate) struct InvalidNotableTraitAttr; + #[derive(Diagnostic)] #[diag("malformed `{$attribute}` attribute")] #[help("{$options}")] @@ -1052,6 +1059,17 @@ pub(crate) struct DocAttributeNotAttribute { pub attribute: Symbol, } +#[derive(Diagnostic)] +#[diag("`#![doc({$attr_name})]` must be a trait attribute")] +#[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" +)] +pub(crate) struct DocAttrNotTraitLevel { + #[primary_span] + pub span: Span, + pub attr_name: Symbol, +} + #[derive(Diagnostic)] #[diag( "`#[target_feature]` cannot be applied to a {$kind -> diff --git a/compiler/rustc_codegen_cranelift/patches/0028-sysroot_tests-Disable-long-running-tests.patch b/compiler/rustc_codegen_cranelift/patches/0028-sysroot_tests-Disable-long-running-tests.patch index e503acf8fee8c..edbb26945ceb0 100644 --- a/compiler/rustc_codegen_cranelift/patches/0028-sysroot_tests-Disable-long-running-tests.patch +++ b/compiler/rustc_codegen_cranelift/patches/0028-sysroot_tests-Disable-long-running-tests.patch @@ -46,16 +46,6 @@ index 8402833..84592e0 100644 split_off_tests! { slice: &mut [(); usize::MAX], method: split_off_mut, (split_off_mut_in_bounds_max_range_to, (..usize::MAX), Some(empty_max_mut!()), &mut [(); 0]), -diff --git a/alloctests/tests/sort/tests.rs b/alloctests/tests/sort/tests.rs -index d321f8d..8b2040a 100644 ---- a/alloctests/tests/sort/tests.rs -+++ b/alloctests/tests/sort/tests.rs -@@ -1,3 +1,5 @@ -+#![cfg(any())] -+ - use std::cell::Cell; - use std::cmp::Ordering; - use std::fmt::Debug; diff --git a/alloctests/tests/str.rs b/alloctests/tests/str.rs index 906fa2d..b82fa99 100644 --- a/alloctests/tests/str.rs diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs index 16465436e239e..69d63c52e4b88 100644 --- a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -443,7 +443,7 @@ impl CodegenBackend for LlvmCodegenBackend { pub struct ModuleLlvm { llcx_raw: ptr::NonNull, - llmod_raw: *const llvm::Module, + llmod_raw: ptr::NonNull, // This field is `ManuallyDrop` because it is important that the `TargetMachine` // is disposed prior to the `Context` being disposed otherwise UAFs can occur. @@ -461,22 +461,15 @@ pub struct ModuleLlvm { // `ModuleLlvm` owns the context and only allows borrowing LLVM objects tied to the context. unsafe impl Send for ModuleLlvm {} -/// Converts an unbranded pointer-to-context to a lifetime-branded [`llvm::Context`]. -/// -/// # Safety -/// - The pointer must be a valid LLVM context. -/// - If the context is passed to safe code, its caller-requested lifetime must be distinct -/// from that of any other context, and must not outlive the underlying context. -unsafe fn wrap_llcx<'ll>(llcx_raw: ptr::NonNull) -> llvm::Context<'ll> { - llvm::Context { llcx_raw, _invariant_ll: PhantomData } -} - /// Shared helper to call [`llvm::LLVMContextCreate`] and then configure /// [`llvm::LLVMContextSetDiscardValueNames`]. fn create_context_and_set_fewer_names(fewer_names: bool) -> ptr::NonNull { unsafe { let llcx_raw = llvm::LLVMContextCreate(); - llvm::LLVMContextSetDiscardValueNames(wrap_llcx(llcx_raw), fewer_names.to_llvm_bool()); + llvm::LLVMContextSetDiscardValueNames( + llvm::wrap_llcx(llcx_raw), + fewer_names.to_llvm_bool(), + ); llcx_raw } } @@ -485,7 +478,8 @@ impl ModuleLlvm { pub(crate) fn new(tcx: TyCtxt<'_>, mod_name: &str) -> Self { unsafe { let llcx_raw = create_context_and_set_fewer_names(tcx.sess.fewer_names()); - let llmod_raw = context::create_module(tcx, wrap_llcx(llcx_raw), mod_name); + let llmod_raw = + context::create_module(tcx, llvm::wrap_llcx(llcx_raw), mod_name).llmod_raw(); ModuleLlvm { llmod_raw, llcx_raw, @@ -498,7 +492,8 @@ impl ModuleLlvm { pub(crate) fn new_metadata(tcx: TyCtxt<'_>, mod_name: &str) -> Self { unsafe { let llcx_raw = create_context_and_set_fewer_names(tcx.sess.fewer_names()); - let llmod_raw = context::create_module(tcx, wrap_llcx(llcx_raw), mod_name); + let llmod_raw = + context::create_module(tcx, llvm::wrap_llcx(llcx_raw), mod_name).llmod_raw(); ModuleLlvm { llmod_raw, llcx_raw, @@ -517,7 +512,8 @@ impl ModuleLlvm { ) -> Self { unsafe { let llcx_raw = create_context_and_set_fewer_names(cgcx.fewer_names); - let llmod_raw = back::lto::parse_module(wrap_llcx(llcx_raw), name, buffer, dcx); + let llmod_raw = + back::lto::parse_module(llvm::wrap_llcx(llcx_raw), name, buffer, dcx).llmod_raw(); let tm = tm_factory(dcx, TargetMachineFactoryConfig::new(cgcx, name.to_str().unwrap())); ModuleLlvm { @@ -538,12 +534,9 @@ impl ModuleLlvm { &self, inner_fn: impl for<'ll> FnOnce(ModuleRefs<'ll>) -> R, ) -> R { - inner_fn(ModuleRefs { - llcx: unsafe { wrap_llcx(self.llcx_raw) }, - llmod: unsafe { &*self.llmod_raw }, - tm: self.tm.raw(), - _ph: PhantomData, - }) + let llcx = unsafe { llvm::wrap_llcx(self.llcx_raw) }; + let llmod = unsafe { llvm::wrap_llmod(llcx, self.llmod_raw) }; + inner_fn(ModuleRefs { llcx, llmod, tm: self.tm.raw(), _ph: PhantomData }) } } @@ -560,7 +553,7 @@ impl Drop for ModuleLlvm { #[derive(Clone, Copy)] pub(crate) struct ModuleRefs<'ll> { pub(crate) llcx: llvm::Context<'ll>, - pub(crate) llmod: &'ll llvm::Module, + pub(crate) llmod: llvm::Module<'ll>, pub(crate) tm: &'ll llvm::TargetMachine, /// Make sure `'ll` is invariant, even if no fields happen to require that. /// diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 4ba4ca6e03b89..8258c1f7c8d20 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -695,7 +695,7 @@ pub struct ModuleBuffer { } impl ModuleBuffer { - pub(crate) fn new(m: &llvm::Module, is_thin: bool) -> ModuleBuffer { + pub(crate) fn new<'ll>(m: llvm::Module<'ll>, is_thin: bool) -> ModuleBuffer { unsafe { let buffer = llvm::LLVMRustModuleSerialize(m, is_thin); ModuleBuffer { data: Buffer(buffer) } @@ -859,7 +859,7 @@ pub(crate) fn parse_module<'ll>( name: &CStr, data: &[u8], dcx: DiagCtxtHandle<'_>, -) -> &'ll llvm::Module { +) -> llvm::Module<'ll> { unsafe { llvm::LLVMRustParseBitcodeForLTO(llcx, data.as_ptr(), data.len(), name.as_ptr()) .unwrap_or_else(|| write::llvm_err(dcx, LlvmError::ParseBitcode)) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 56f855b340440..2a130a9358c4f 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -54,7 +54,7 @@ fn write_output_file<'ll>( dcx: DiagCtxtHandle<'_>, target: &'ll llvm::TargetMachine, no_builtins: bool, - m: &'ll llvm::Module, + m: llvm::Module<'ll>, output: &Path, dwo_output: Option<&Path>, file_type: llvm::FileType, @@ -1207,7 +1207,7 @@ pub(crate) fn bitcode_section_name(cgcx: &CodegenContext) -> &'static CStr { fn embed_bitcode<'ll>( cgcx: &CodegenContext, llcx: llvm::Context<'ll>, - llmod: &'ll llvm::Module, + llmod: llvm::Module<'ll>, bitcode: &[u8], ) { // We're adding custom sections to the output object file, but we definitely @@ -1293,7 +1293,7 @@ fn embed_bitcode<'ll>( fn create_msvc_imps<'ll>( cgcx: &CodegenContext, llcx: llvm::Context<'ll>, - llmod: &'ll llvm::Module, + llmod: llvm::Module<'ll>, ) { if !cgcx.msvc_imps_needed { return; @@ -1359,7 +1359,11 @@ fn record_artifact_size( } } -fn record_llvm_cgu_instructions_stats(prof: &SelfProfilerRef, name: &str, llmod: &llvm::Module) { +fn record_llvm_cgu_instructions_stats<'ll>( + prof: &SelfProfilerRef, + name: &str, + llmod: llvm::Module<'ll>, +) { if !prof.enabled() { return; } diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 87eb157a8892f..7909072db1d8d 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -50,11 +50,11 @@ impl<'ll> Iterator for ValueIter<'ll> { } } -pub(crate) fn iter_globals(llmod: &llvm::Module) -> ValueIter<'_> { +pub(crate) fn iter_globals<'ll>(llmod: llvm::Module<'ll>) -> ValueIter<'ll> { unsafe { ValueIter { cur: llvm::LLVMGetFirstGlobal(llmod), step: llvm::LLVMGetNextGlobal } } } -pub(crate) fn iter_global_aliases(llmod: &llvm::Module) -> ValueIter<'_> { +pub(crate) fn iter_global_aliases<'ll>(llmod: llvm::Module<'ll>) -> ValueIter<'ll> { unsafe { ValueIter { cur: llvm::LLVMGetFirstGlobalAlias(llmod), step: llvm::LLVMGetNextGlobalAlias } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index ead1c8670713c..4291408212358 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -47,7 +47,7 @@ use crate::{attributes, common, coverageinfo, debuginfo, llvm_util}; /// other compiler pieces. Here we define a small subset which has enough information and can be /// moved around more freely. pub(crate) struct SCx<'ll> { - pub llmod: &'ll llvm::Module, + pub llmod: llvm::Module<'ll>, pub llcx: llvm::Context<'ll>, pub isize_ty: &'ll Type, } @@ -186,15 +186,14 @@ pub(crate) unsafe fn create_module<'ll>( tcx: TyCtxt<'_>, llcx: llvm::Context<'ll>, mod_name: &str, -) -> *const llvm::Module { +) -> llvm::Module<'ll> { let mod_name_cstr = SmallCStr::new(mod_name); - let llmod_raw = - unsafe { llvm::LLVMModuleCreateWithNameInContext(mod_name_cstr.as_ptr(), llcx) }; - init_module(tcx, llcx, unsafe { &*llmod_raw }); - llmod_raw + let llmod = unsafe { llvm::LLVMModuleCreateWithNameInContext(mod_name_cstr.as_ptr(), llcx) }; + init_module(tcx, llcx, llmod); + llmod } -fn init_module<'ll, 'tcx>(tcx: TyCtxt<'tcx>, llcx: llvm::Context<'ll>, llmod: &'ll llvm::Module) { +fn init_module<'ll, 'tcx>(tcx: TyCtxt<'tcx>, llcx: llvm::Context<'ll>, llmod: llvm::Module<'ll>) { let sess = tcx.sess; let cx = SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size()); @@ -880,7 +879,7 @@ impl<'ll> SimpleCx<'ll> { } impl<'ll> SimpleCx<'ll> { pub(crate) fn new( - llmod: &'ll llvm::Module, + llmod: llvm::Module<'ll>, llcx: llvm::Context<'ll>, pointer_size: Size, ) -> Self { @@ -1226,7 +1225,7 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { /// - `LLVMAddNamedMetadataOperand` (to set that node as metadata of `kind_name` for `module`) pub(crate) fn module_add_named_metadata_node( &self, - module: &'ll Module, + module: Module<'ll>, kind_name: &CStr, md_list: &[&'ll Metadata], ) { diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs index 93d0b09578315..92abe16eb4109 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs @@ -12,14 +12,14 @@ pub(crate) fn covmap_var_name() -> CString { .expect("covmap variable name should not contain NUL") } -pub(crate) fn covmap_section_name(llmod: &llvm::Module) -> CString { +pub(crate) fn covmap_section_name<'ll>(llmod: llvm::Module<'ll>) -> CString { CString::new(llvm::build_byte_buffer(|s| { llvm::LLVMRustCoverageWriteCovmapSectionNameToString(llmod, s); })) .expect("covmap section name should not contain NUL") } -pub(crate) fn covfun_section_name(llmod: &llvm::Module) -> CString { +pub(crate) fn covfun_section_name<'ll>(llmod: llvm::Module<'ll>) -> CString { CString::new(llvm::build_byte_buffer(|s| { llvm::LLVMRustCoverageWriteCovfunSectionNameToString(llmod, s); })) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/di_builder.rs b/compiler/rustc_codegen_llvm/src/debuginfo/di_builder.rs index 0b5ef6c687404..9237c8be87473 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/di_builder.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/di_builder.rs @@ -14,7 +14,7 @@ pub(crate) struct DIBuilderBox<'ll> { } impl<'ll> DIBuilderBox<'ll> { - pub(crate) fn new(llmod: &'ll Module) -> Self { + pub(crate) fn new(llmod: Module<'ll>) -> Self { let raw = unsafe { llvm::LLVMCreateDIBuilder(llmod) }; let raw = ptr::NonNull::new(raw).unwrap(); Self { raw } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 337d477bc74ea..5fe5ee3e92fea 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -61,7 +61,7 @@ pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { } impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { - pub(crate) fn new(llmod: &'ll llvm::Module, sess: &Session) -> Self { + pub(crate) fn new(llmod: llvm::Module<'ll>, sess: &Session) -> Self { debug!("CodegenUnitDebugContext::new"); match sess.target.debuginfo_kind { diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index b0641b70d1189..9ec5b667f7c6c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -55,7 +55,7 @@ unsafe extern "C" { Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: libc::size_t, ); - pub(crate) fn LLVMGetFirstFunction(M: &Module) -> Option<&Value>; + pub(crate) fn LLVMGetFirstFunction<'ll>(M: Module<'ll>) -> Option<&'ll Value>; pub(crate) fn LLVMGetNextFunction(Fn: &Value) -> Option<&Value>; pub(crate) fn LLVMRustRemoveEnumAttributeAtIndex( Fn: &Value, @@ -75,11 +75,14 @@ unsafe extern "C" { unsafe extern "C" { // Enzyme - pub(crate) fn LLVMDumpModule(M: &Module); + pub(crate) fn LLVMDumpModule<'ll>(M: Module<'ll>); pub(crate) fn LLVMDumpValue(V: &Value); pub(crate) fn LLVMGetFunctionCallConv(F: &Value) -> c_uint; pub(crate) fn LLVMGetParams(Fnc: &Value, params: *mut &Value); - pub(crate) fn LLVMGetNamedFunction(M: &Module, Name: *const c_char) -> Option<&Value>; + pub(crate) fn LLVMGetNamedFunction<'ll>( + M: Module<'ll>, + Name: *const c_char, + ) -> Option<&'ll Value>; } #[repr(C)] diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7e6c4869f0b26..5c49f5830444b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -20,6 +20,7 @@ use std::ptr; use bitflags::bitflags; use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_void, size_t}; +pub(crate) use self::branded::{Context, ContextRaw, Module, ModuleRaw, wrap_llcx, wrap_llmod}; use super::RustString; use super::debuginfo::{ DIArray, DIBuilder, DIDerivedType, DIDescriptor, DIFile, DIFlags, DILocation, DISPFlags, @@ -29,6 +30,8 @@ use crate::llvm; use crate::llvm::MetadataKindId; use crate::macros::TryFromU32; +mod branded; + /// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`, /// which has a different ABI from Rust or C++ `bool`. /// @@ -701,18 +704,7 @@ struct InvariantOpaque<'a> { // Opaque pointer types -/// Corresponds to `LLVMContextRef`. -#[derive(Clone, Copy)] -#[repr(transparent)] -pub(crate) struct Context<'ll> { - pub(crate) llcx_raw: ptr::NonNull, - pub(crate) _invariant_ll: PhantomData &'ll ()>, -} - unsafe extern "C" { - /// Pointee of the raw pointer that is wrapped by a lifetime-branded [`Context`]. - pub(crate) type ContextRaw; - pub(crate) type Module; pub(crate) type Type; pub(crate) type Value; pub(crate) type ConstantInt; @@ -918,12 +910,12 @@ unsafe extern "C" { pub(crate) fn LLVMModuleCreateWithNameInContext<'ll>( ModuleID: *const c_char, C: Context<'ll>, - ) -> *const Module; - pub(crate) safe fn LLVMCloneModule(M: &Module) -> &Module; + ) -> Module<'ll>; + pub(crate) safe fn LLVMCloneModule<'ll>(M: Module<'ll>) -> Module<'ll>; /// Data layout. See Module::getDataLayout. - pub(crate) fn LLVMGetDataLayoutStr(M: &Module) -> *const c_char; - pub(crate) fn LLVMSetDataLayout(M: &Module, Triple: *const c_char); + pub(crate) fn LLVMGetDataLayoutStr<'ll>(M: Module<'ll>) -> *const c_char; + pub(crate) fn LLVMSetDataLayout<'ll>(M: Module<'ll>, Triple: *const c_char); /// Create the specified uniqued inline asm string. See `InlineAsm::get()`. pub(crate) fn LLVMGetInlineAsm<'ll>( @@ -1036,10 +1028,10 @@ unsafe extern "C" { Vals: *const &'ll Metadata, Count: size_t, ) -> &'ll Metadata; - pub(crate) fn LLVMAddNamedMetadataOperand<'a>( - M: &'a Module, + pub(crate) fn LLVMAddNamedMetadataOperand<'ll>( + M: Module<'ll>, Name: *const c_char, - Val: &'a Value, + Val: &'ll Value, ); pub(crate) fn LLVMReplaceMDNodeOperandWith(Val: &Value, index: u32, replacement: &Metadata); @@ -1107,9 +1099,16 @@ unsafe extern "C" { // Operations on global variables pub(crate) safe fn LLVMIsAGlobalVariable(GlobalVar: &Value) -> Option<&Value>; - pub(crate) fn LLVMAddGlobal<'a>(M: &'a Module, Ty: &'a Type, Name: *const c_char) -> &'a Value; - pub(crate) fn LLVMGetNamedGlobal(M: &Module, Name: *const c_char) -> Option<&Value>; - pub(crate) fn LLVMGetFirstGlobal(M: &Module) -> Option<&Value>; + pub(crate) fn LLVMAddGlobal<'ll>( + M: Module<'ll>, + Ty: &'ll Type, + Name: *const c_char, + ) -> &'ll Value; + pub(crate) fn LLVMGetNamedGlobal<'ll>( + M: Module<'ll>, + Name: *const c_char, + ) -> Option<&'ll Value>; + pub(crate) fn LLVMGetFirstGlobal<'ll>(M: Module<'ll>) -> Option<&'ll Value>; pub(crate) fn LLVMGetNextGlobal(GlobalVar: &Value) -> Option<&Value>; pub(crate) fn LLVMDeleteGlobal(GlobalVar: &Value); pub(crate) safe fn LLVMGetInitializer(GlobalVar: &Value) -> Option<&Value>; @@ -1124,13 +1123,13 @@ unsafe extern "C" { // Operations on global aliases pub(crate) fn LLVMAddAlias2<'ll>( - M: &'ll Module, + M: Module<'ll>, ValueTy: &Type, AddressSpace: c_uint, Aliasee: &Value, Name: *const c_char, ) -> &'ll Value; - pub(crate) fn LLVMGetFirstGlobalAlias(M: &Module) -> Option<&Value>; + pub(crate) fn LLVMGetFirstGlobalAlias<'ll>(M: Module<'ll>) -> Option<&'ll Value>; pub(crate) fn LLVMGetNextGlobalAlias(GlobalAlias: &Value) -> Option<&Value>; // Operations on attributes @@ -1144,18 +1143,18 @@ unsafe extern "C" { // Operations on functions pub(crate) fn LLVMSetFunctionCallConv(Fn: &Value, CC: c_uint); - pub(crate) fn LLVMAddFunction<'a>( - Mod: &'a Module, + pub(crate) fn LLVMAddFunction<'ll>( + Mod: Module<'ll>, Name: *const c_char, - FunctionTy: &'a Type, - ) -> &'a Value; + FunctionTy: &'ll Type, + ) -> &'ll Value; pub(crate) fn LLVMDeleteFunction(Fn: &Value); - pub(crate) fn LLVMGetOrInsertFunction<'a>( - M: &'a Module, + pub(crate) fn LLVMGetOrInsertFunction<'ll>( + M: Module<'ll>, Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: size_t, - FunctionTy: &'a Type, - ) -> &'a Value; + FunctionTy: &'ll Type, + ) -> &'ll Value; // Operations about llvm intrinsics pub(crate) fn LLVMLookupIntrinsicID( @@ -1163,12 +1162,12 @@ unsafe extern "C" { NameLen: size_t, ) -> c_uint; pub(crate) fn LLVMIntrinsicIsOverloaded(ID: NonZero) -> Bool; - pub(crate) fn LLVMGetIntrinsicDeclaration<'a>( - Mod: &'a Module, + pub(crate) fn LLVMGetIntrinsicDeclaration<'ll>( + Mod: Module<'ll>, ID: NonZero, - ParamTypes: *const &'a Type, + ParamTypes: *const &'ll Type, ParamCount: size_t, - ) -> &'a Value; + ) -> &'ll Value; pub(crate) fn LLVMRustUpgradeIntrinsicFunction<'a>( Fn: &'a Value, NewFn: &mut Option<&'a Value>, @@ -1209,7 +1208,7 @@ unsafe extern "C" { ); // Instruction builders - pub(crate) fn LLVMCreateBuilderForModule<'ll>(M: &'ll Module) -> ptr::NonNull>; + pub(crate) fn LLVMCreateBuilderForModule<'ll>(M: Module<'ll>) -> ptr::NonNull>; pub(crate) fn LLVMPositionBuilderAtEnd<'a>(Builder: &Builder<'a>, Block: &'a BasicBlock); pub(crate) fn LLVMGetInsertBlock<'a>(Builder: &Builder<'a>) -> &'a BasicBlock; pub(crate) fn LLVMDisposeBuilder<'ll>(Builder: ptr::NonNull>); @@ -1667,7 +1666,7 @@ unsafe extern "C" { ) -> &'a Value; /// Writes a module to the specified path. Returns 0 on success. - pub(crate) fn LLVMWriteBitcodeToFile(M: &Module, Path: *const c_char) -> c_int; + pub(crate) fn LLVMWriteBitcodeToFile<'ll>(M: Module<'ll>, Path: *const c_char) -> c_int; pub(crate) fn LLVMGetHostCPUFeatures() -> *mut c_char; @@ -1693,7 +1692,7 @@ unsafe extern "C" { pub(crate) fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&ConstantInt>; - pub(crate) fn LLVMGetOrInsertComdat(M: &Module, Name: *const c_char) -> &Comdat; + pub(crate) fn LLVMGetOrInsertComdat<'ll>(M: Module<'ll>, Name: *const c_char) -> &'ll Comdat; pub(crate) fn LLVMSetComdat(V: &Value, C: &Comdat); pub(crate) fn LLVMCreateOperandBundle( @@ -1748,7 +1747,7 @@ unsafe extern "C" { // that they really are nullable on the C/C++ side. LLVM doesn't appear to // actually document which ones are nullable. unsafe extern "C" { - pub(crate) fn LLVMCreateDIBuilder<'ll>(M: &'ll Module) -> *mut DIBuilder<'ll>; + pub(crate) fn LLVMCreateDIBuilder<'ll>(M: Module<'ll>) -> *mut DIBuilder<'ll>; pub(crate) fn LLVMDisposeDIBuilder<'ll>(Builder: ptr::NonNull>); pub(crate) fn LLVMDIBuilderFinalize<'ll>(Builder: &DIBuilder<'ll>); @@ -2012,24 +2011,24 @@ unsafe extern "C" { pub(crate) fn LLVMRustSetDSOLocal(Global: &Value, is_dso_local: bool); // Operations on global variables - pub(crate) fn LLVMRustGetOrInsertGlobal<'a>( - M: &'a Module, + pub(crate) fn LLVMRustGetOrInsertGlobal<'ll>( + M: Module<'ll>, Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: size_t, - T: &'a Type, - ) -> &'a Value; - pub(crate) fn LLVMRustGetOrInsertGlobalInAddrspace<'a>( - M: &'a Module, + T: &'ll Type, + ) -> &'ll Value; + pub(crate) fn LLVMRustGetOrInsertGlobalInAddrspace<'ll>( + M: Module<'ll>, Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: size_t, - T: &'a Type, + T: &'ll Type, AddressSpace: c_uint, - ) -> &'a Value; - pub(crate) fn LLVMRustGetNamedValue( - M: &Module, + ) -> &'ll Value; + pub(crate) fn LLVMRustGetNamedValue<'ll>( + M: Module<'ll>, Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: size_t, - ) -> Option<&Value>; + ) -> Option<&'ll Value>; // Operations on attributes pub(crate) fn LLVMRustCreateAttrNoValue<'ll>( @@ -2157,8 +2156,8 @@ unsafe extern "C" { ) -> bool; /// Append inline assembly to a module. See `Module::appendModuleInlineAsm`. - pub(crate) fn LLVMRustAppendModuleInlineAsm( - M: &Module, + pub(crate) fn LLVMRustAppendModuleInlineAsm<'ll>( + M: Module<'ll>, Asm: *const c_uchar, // See "PTR_LEN_STR". AsmLen: size_t, TargetFeatures: *const c_uchar, // See "PTR_LEN_STR". @@ -2202,12 +2201,12 @@ unsafe extern "C" { NumBytes: size_t, ) -> u64; - pub(crate) safe fn LLVMRustCoverageWriteCovmapSectionNameToString( - M: &Module, + pub(crate) safe fn LLVMRustCoverageWriteCovmapSectionNameToString<'ll>( + M: Module<'ll>, OutStr: &RustString, ); - pub(crate) safe fn LLVMRustCoverageWriteCovfunSectionNameToString( - M: &Module, + pub(crate) safe fn LLVMRustCoverageWriteCovfunSectionNameToString<'ll>( + M: Module<'ll>, OutStr: &RustString, ); pub(crate) safe fn LLVMRustCoverageWriteCovmapVarNameToString(OutStr: &RustString); @@ -2226,16 +2225,16 @@ unsafe extern "C" { /// /// In order for Rust-C LTO to work, module flags must be compatible with Clang. What /// "compatible" means depends on the merge behaviors involved. - pub(crate) fn LLVMRustAddModuleFlagU32( - M: &Module, + pub(crate) fn LLVMRustAddModuleFlagU32<'ll>( + M: Module<'ll>, MergeBehavior: ModuleFlagMergeBehavior, Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: size_t, Value: u32, ); - pub(crate) fn LLVMRustAddModuleFlagString( - M: &Module, + pub(crate) fn LLVMRustAddModuleFlagString<'ll>( + M: Module<'ll>, MergeBehavior: ModuleFlagMergeBehavior, Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: size_t, @@ -2465,18 +2464,18 @@ unsafe extern "C" { pub(crate) fn LLVMRustDisposeMCSubtargetInfo(MCInfo: ptr::NonNull); - pub(crate) fn LLVMRustWriteOutputFile<'a>( - T: &'a TargetMachine, - M: &'a Module, + pub(crate) fn LLVMRustWriteOutputFile<'ll>( + T: &'ll TargetMachine, + M: Module<'ll>, Output: *const c_char, DwoOutput: *const c_char, FileType: FileType, VerifyIR: bool, DisableSimplifyLibCalls: bool, ) -> LLVMRustResult; - pub(crate) fn LLVMRustOptimize<'a>( - M: &'a Module, - TM: &'a TargetMachine, + pub(crate) fn LLVMRustOptimize<'ll>( + M: Module<'ll>, + TM: &'ll TargetMachine, OptLevel: PassBuilderOptLevel, OptStage: OptStage, IsLinkerPluginLTO: bool, @@ -2512,15 +2511,19 @@ unsafe extern "C" { LLVMPlugins: *const c_uchar, // See "PTR_LEN_STR". LLVMPluginsLen: size_t, ) -> LLVMRustResult; - pub(crate) fn LLVMRustPrintModule( - M: &Module, + pub(crate) fn LLVMRustPrintModule<'ll>( + M: Module<'ll>, Output: *const c_char, Demangle: extern "C" fn(*const c_char, size_t, *mut c_char, size_t) -> size_t, ) -> LLVMRustResult; pub(crate) fn LLVMRustSetLLVMOptions(Argc: c_int, Argv: *const *const c_char); pub(crate) fn LLVMRustPrintPasses(); - pub(crate) fn LLVMRustSetNormalizedTarget(M: &Module, triple: *const c_char); - pub(crate) fn LLVMRustRunRestrictionPass(M: &Module, syms: *const *const c_char, len: size_t); + pub(crate) fn LLVMRustSetNormalizedTarget<'ll>(M: Module<'ll>, triple: *const c_char); + pub(crate) fn LLVMRustRunRestrictionPass<'ll>( + M: Module<'ll>, + syms: *const *const c_char, + len: size_t, + ); pub(crate) fn LLVMRustWriteTwineToString(T: &Twine, s: &RustString); @@ -2559,38 +2562,47 @@ unsafe extern "C" { num_ranges: &mut usize, ) -> bool; - pub(crate) fn LLVMRustSetDataLayoutFromTargetMachine<'a>(M: &'a Module, TM: &'a TargetMachine); + pub(crate) fn LLVMRustSetDataLayoutFromTargetMachine<'ll>(M: Module<'ll>, TM: &TargetMachine); pub(crate) fn LLVMRustPositionBuilderPastAllocas<'a>(B: &Builder<'a>, Fn: &'a Value); pub(crate) fn LLVMRustPositionBuilderAtStart<'a>(B: &Builder<'a>, BB: &'a BasicBlock); - pub(crate) fn LLVMRustSetModulePICLevel(M: &Module); - pub(crate) fn LLVMRustSetModulePIELevel(M: &Module); - pub(crate) fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); - pub(crate) fn LLVMRustSetModuleLargeDataThreshold(M: &Module, Threshold: u64); + pub(crate) fn LLVMRustSetModulePICLevel<'ll>(M: Module<'ll>); + pub(crate) fn LLVMRustSetModulePIELevel<'ll>(M: Module<'ll>); + pub(crate) fn LLVMRustSetModuleCodeModel<'ll>(M: Module<'ll>, Model: CodeModel); + pub(crate) fn LLVMRustSetModuleLargeDataThreshold<'ll>(M: Module<'ll>, Threshold: u64); pub(crate) fn LLVMRustBufferPtr(p: &Buffer) -> *const u8; pub(crate) fn LLVMRustBufferLen(p: &Buffer) -> usize; pub(crate) fn LLVMRustBufferFree(p: &'static mut Buffer); - pub(crate) fn LLVMRustModuleCost(M: &Module) -> u64; - pub(crate) fn LLVMRustModuleInstructionStats(M: &Module) -> u64; + pub(crate) fn LLVMRustModuleCost<'ll>(M: Module<'ll>) -> u64; + pub(crate) fn LLVMRustModuleInstructionStats<'ll>(M: Module<'ll>) -> u64; - pub(crate) fn LLVMRustModuleSerialize(M: &Module, is_thin: bool) -> &'static mut Buffer; + pub(crate) fn LLVMRustModuleSerialize<'ll>( + M: Module<'ll>, + is_thin: bool, + ) -> &'static mut Buffer; pub(crate) fn LLVMRustCreateThinLTOData( Modules: *const ThinLTOModule, NumModules: size_t, PreservedSymbols: *const *const c_char, PreservedSymbolsLen: size_t, ) -> Option<&'static mut ThinLTOData>; - pub(crate) fn LLVMRustPrepareThinLTORename( + pub(crate) fn LLVMRustPrepareThinLTORename<'ll>( Data: &ThinLTOData, - Module: &Module, + Module: Module<'ll>, Target: &TargetMachine, ); - pub(crate) fn LLVMRustPrepareThinLTOResolveWeak(Data: &ThinLTOData, Module: &Module) -> bool; - pub(crate) fn LLVMRustPrepareThinLTOInternalize(Data: &ThinLTOData, Module: &Module) -> bool; - pub(crate) fn LLVMRustPrepareThinLTOImport( + pub(crate) fn LLVMRustPrepareThinLTOResolveWeak<'ll>( + Data: &ThinLTOData, + Module: Module<'ll>, + ) -> bool; + pub(crate) fn LLVMRustPrepareThinLTOInternalize<'ll>( + Data: &ThinLTOData, + Module: Module<'ll>, + ) -> bool; + pub(crate) fn LLVMRustPrepareThinLTOImport<'ll>( Data: &ThinLTOData, - Module: &Module, + Module: Module<'ll>, Target: &TargetMachine, ) -> bool; pub(crate) fn LLVMRustFreeThinLTOData(Data: &'static mut ThinLTOData); @@ -2599,9 +2611,9 @@ unsafe extern "C" { Data: *const u8, len: usize, Identifier: *const c_char, - ) -> Option<&'ll Module>; + ) -> Option>; - pub(crate) fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>; + pub(crate) fn LLVMRustLinkerNew<'ll>(M: Module<'ll>) -> &'ll mut Linker<'ll>; pub(crate) fn LLVMRustLinkerAdd( linker: &Linker<'_>, bytecode: *const c_uchar, // See "PTR_LEN_STR". diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi/branded.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi/branded.rs new file mode 100644 index 0000000000000..c57ffef589737 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi/branded.rs @@ -0,0 +1,55 @@ +//! Lifetime-branded references to things manipulated by the LLVM bindings. + +use std::marker::PhantomData; +use std::ptr; + +/// Corresponds to `LLVMContextRef`. +#[derive(Clone, Copy)] +#[repr(transparent)] +pub(crate) struct Context<'ll> { + llcx_raw: ptr::NonNull, + _invariant_ll: PhantomData &'ll ()>, +} + +/// Corresponds to `LLVMModuleRef`. +#[derive(Clone, Copy)] +#[repr(transparent)] +pub(crate) struct Module<'ll> { + llmod_raw: ptr::NonNull, + _invariant_ll: PhantomData &'ll ()>, +} + +impl<'ll> Module<'ll> { + pub(crate) fn llmod_raw(&self) -> ptr::NonNull { + self.llmod_raw + } +} + +unsafe extern "C" { + /// Pointee of the raw pointer that is wrapped by a lifetime-branded [`Context`]. + pub(crate) type ContextRaw; + /// Pointee of the raw pointer that is wrapped by a lifetime-branded [`Module`]. + pub(crate) type ModuleRaw; +} + +/// Converts an unbranded pointer-to-context to a lifetime-branded [`Context`]. +/// +/// # Safety +/// - The pointer must be a valid LLVM context. +/// - If the context is passed to safe code, its caller-requested lifetime must be distinct +/// from that of any other context, and must not outlive the underlying context. +pub(crate) unsafe fn wrap_llcx<'ll>(llcx_raw: ptr::NonNull) -> Context<'ll> { + Context { llcx_raw, _invariant_ll: PhantomData } +} + +/// Converts an unbranded pointer-to-module to a lifetime-branded [`Module`]. +/// +/// # Safety +/// - The pointer must be a valid LLVM module within the specified context. +/// - The module must not be disposed during the lifetime of its context. +pub(crate) unsafe fn wrap_llmod<'ll>( + _llcx: Context<'ll>, // The context constrains `'ll` but isn't actually used. + llmod_raw: ptr::NonNull, +) -> Module<'ll> { + Module { llmod_raw, _invariant_ll: PhantomData } +} diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 54b31bc450cfb..0cd26ca835971 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -220,7 +220,7 @@ pub(crate) fn SetFunctionCallConv(fn_: &Value, cc: CallConv) { // value's name as the comdat value to make sure that it is in a 1-to-1 relationship to the // function. // For more details on COMDAT sections see e.g., https://www.airs.com/blog/archives/52 -pub(crate) fn SetUniqueComdat(llmod: &Module, val: &Value) { +pub(crate) fn SetUniqueComdat<'ll>(llmod: Module<'ll>, val: &'ll Value) { let name_buf = get_value_name(val); let name = CString::from_vec_with_nul(name_buf).or_else(|buf| CString::new(buf.into_bytes())).unwrap(); @@ -257,7 +257,7 @@ pub(crate) fn set_section(llglobal: &Value, section_name: &CStr) { } } -pub(crate) fn add_global<'a>(llmod: &'a Module, ty: &'a Type, name_cstr: &CStr) -> &'a Value { +pub(crate) fn add_global<'ll>(llmod: Module<'ll>, ty: &'ll Type, name_cstr: &CStr) -> &'ll Value { unsafe { LLVMAddGlobal(llmod, ty, name_cstr.as_ptr()) } } @@ -309,7 +309,7 @@ pub(crate) fn set_externally_initialized(llglobal: &Value, is_ext_init: bool) { /// /// Inserts the comdat into `llmod` if it does not exist. /// It is an error to call this if the target does not support comdat. -pub(crate) fn set_comdat(llmod: &Module, llglobal: &Value, name: &CStr) { +pub(crate) fn set_comdat<'ll>(llmod: Module<'ll>, llglobal: &'ll Value, name: &CStr) { unsafe { let comdat = LLVMGetOrInsertComdat(llmod, name.as_ptr()); LLVMSetComdat(llglobal, comdat); @@ -365,7 +365,7 @@ impl Intrinsic { pub(crate) fn get_declaration<'ll>( self, - llmod: &'ll Module, + llmod: Module<'ll>, type_params: &[&'ll Type], ) -> &'ll Value { unsafe { @@ -440,8 +440,8 @@ impl Drop for OperandBundleBox<'_> { } } -pub(crate) fn add_module_flag_u32( - module: &Module, +pub(crate) fn add_module_flag_u32<'ll>( + module: Module<'ll>, merge_behavior: ModuleFlagMergeBehavior, key: &str, value: u32, @@ -451,8 +451,8 @@ pub(crate) fn add_module_flag_u32( } } -pub(crate) fn add_module_flag_str( - module: &Module, +pub(crate) fn add_module_flag_str<'ll>( + module: Module<'ll>, merge_behavior: ModuleFlagMergeBehavior, key: &str, value: &str, @@ -484,7 +484,7 @@ pub(crate) fn set_dso_local<'ll>(v: &'ll Value) { /// Safe wrapper for `LLVMRustAppendModuleInlineAsm`, which delegates to /// `Module::appendModuleInlineAsm`. pub(crate) fn append_module_inline_asm<'ll>( - llmod: &'ll Module, + llmod: Module<'ll>, asm: &[u8], target_features: &str, target_cpu: &str, @@ -504,7 +504,7 @@ pub(crate) fn append_module_inline_asm<'ll>( /// Safe wrapper for `LLVMAddAlias2` pub(crate) fn add_alias<'ll>( - module: &'ll Module, + module: Module<'ll>, ty: &Type, address_space: AddressSpace, aliasee: &Value, diff --git a/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs index 840d07a1ed2ae..60e0836a35d88 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs @@ -6,7 +6,7 @@ use super::ffi::{Module, Value}; type LLVMRustOffloadMapperFn = unsafe extern "C" fn(&Value, &Value, *const &Value); type LLVMRustOffloadWrapImagesFn = - unsafe extern "C" fn(&Module, *const c_char, *const c_char) -> bool; + for<'ll> unsafe extern "C" fn(Module<'ll>, *const c_char, *const c_char) -> bool; use rustc_fs_util::path_to_c_string; use rustc_session::config::host_tuple; @@ -56,9 +56,9 @@ impl RustOffloadWrapper { unsafe { (self.LLVMRustOffloadMapper)(v1, v2, vs.as_ptr()) } } - pub(crate) unsafe fn llvm_rust_offload_wrap_images( + pub(crate) unsafe fn llvm_rust_offload_wrap_images<'ll>( &self, - host_m: &Module, + host_m: Module<'ll>, device_bitcode_path: &CStr, ) -> bool { let lld_c = self.lld_path.as_deref().map(path_to_c_string).unwrap_or_default(); diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 0a0c82650984f..7c022b2c4f6a8 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -152,7 +152,7 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { (**self).borrow().llcx } - pub(crate) fn llmod(&self) -> &'ll llvm::Module { + pub(crate) fn llmod(&self) -> llvm::Module<'ll> { (**self).borrow().llmod } diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs index e781ffea99479..efda11f399ba4 100644 --- a/compiler/rustc_codegen_llvm/src/typetree.rs +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -92,7 +92,7 @@ pub(crate) fn add_tt<'tcx, 'll>(cx: &FullCx<'ll, 'tcx>, fn_def: &'ll Value, tt: let inputs = tt.args; let ret_tt: RustTypeTree = tt.ret; - let llvm_data_layout: *const c_char = unsafe { llvm::LLVMGetDataLayoutStr(&*llmod) }; + let llvm_data_layout: *const c_char = unsafe { llvm::LLVMGetDataLayoutStr(llmod) }; let llvm_data_layout = std::str::from_utf8(unsafe { std::ffi::CStr::from_ptr(llvm_data_layout) }.to_bytes()) .expect("got a non-UTF8 data-layout from LLVM"); diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 33b019445686a..f1919cab3957e 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -518,9 +518,11 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { } // The pager failed. Try to print pretty output to stdout. - if let Some((bufwtr, mdbuf)) = &mut pretty_data + if matches!(color, ColorConfig::Auto | ColorConfig::Always) + && let Some((bufwtr, mdbuf)) = &mut pretty_data && bufwtr.write_all(&mdbuf).is_ok() { + // Colored output was successfully printed to stdout. return; } diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 87dddb7b74c35..d0e35ad80bfb0 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -330,7 +330,7 @@ impl AllocError { } /// The information that makes up a memory access: offset and size. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct AllocRange { pub start: Size, pub size: Size, diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 7aae6f8ca9cc4..7442daa296f03 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -55,7 +55,7 @@ use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::tokenstream::TokenStream; use rustc_attr_ir::diagnostic_items::DiagnosticItems; use rustc_attr_ir::lang_items::{LangItem, LanguageItems}; -use rustc_attr_ir::{CanonicalSymbols, EiiDecl, EiiImpl, StrippedCfgItem}; +use rustc_attr_ir::{CanonicalSymbols, EiiDecl, EiiImpl, NotableTraitColor, StrippedCfgItem}; use rustc_crate_store::{ CrateDepKind, CrateSource, ExternCrate, ForeignModule, LinkagePreference, NativeLib, }; @@ -1529,8 +1529,10 @@ rustc_queries! { separate_provide_extern } - /// Determines whether an item is annotated with `#[doc(notable_trait)]`. - query is_doc_notable_trait(def_id: DefId) -> bool { + /// If an item is annotated with `#[doc(notable_trait)]`, + /// returns the color used to render its badge. If the crate specifies + /// no color, `Transparent` is used. + query doc_notable_trait(def_id: DefId) -> Option<&'tcx NotableTraitColor> { desc { "checking whether `{}` is `doc(notable_trait)`", tcx.def_path_str(def_id) } } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 12228ae721734..8e6850ca0f07a 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -4,7 +4,7 @@ use std::{fmt, iter}; use rustc_abi::{Float, Integer, IntegerType, Size}; use rustc_apfloat::Float as _; -use rustc_attr_ir::find_attr; +use rustc_attr_ir::{NotableTraitColor, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hash::{StableHash, StableHasher}; use rustc_errors::ErrorGuaranteed; @@ -1701,8 +1701,15 @@ fn is_doc_hidden(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { } /// Determines whether an item is annotated with `doc(notable_trait)`. -pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - find_attr!(tcx, def_id, Doc(doc) if doc.notable_trait.is_some()) +pub fn doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Option<&'_ NotableTraitColor> { + find_attr!(tcx, def_id, Doc(doc) if doc.notable_trait.is_some() => { + let (color, _span) = doc.notable_trait.as_ref()?; + if let Some((color, _span)) = color { + color + } else { + &NotableTraitColor::Transparent + } + }) } /// Determines whether an item is an intrinsic (which may be via Abi or via the `rustc_intrinsic` attribute). @@ -1732,7 +1739,7 @@ pub fn provide(providers: &mut Providers) { *providers = Providers { reveal_opaque_types_in_bounds, is_doc_hidden, - is_doc_notable_trait, + doc_notable_trait, intrinsic_raw, ..*providers } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9cc95cde4ad12..de141dcc8b823 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -669,6 +669,7 @@ symbols! { cold, cold_path, collapse_debuginfo, + color, column, compare_bytes, compare_exchange, diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 992ef8da29b55..0d1ca5f85416a 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -5,7 +5,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_span::Symbol; -use crate::spec::{Arch, RelocModel, Target}; +use crate::spec::{Arch, LlvmAbi, RelocModel, Target}; pub struct ModifierInfo { pub modifier: char, @@ -1024,6 +1024,7 @@ pub enum InlineAsmClobberAbi { Arm64EC, Avr, RiscV, + RiscVD, RiscVE, LoongArch, PowerPC, @@ -1077,11 +1078,15 @@ impl InlineAsmClobberAbi { _ => Err(&["C", "system"]), }, InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name { - "C" | "system" | "efiapi" => Ok(if riscv::is_e(target_features) { - InlineAsmClobberAbi::RiscVE - } else { - InlineAsmClobberAbi::RiscV - }), + "C" | "system" | "efiapi" => { + Ok(match target.llvm_abiname { + LlvmAbi::Ilp32e | LlvmAbi::Lp64e => InlineAsmClobberAbi::RiscVE, + // FIXME: Once LLVM adds support for the `q` extension, `d` will no longer + // be the largest supported FLEN, so change this to Lp64q => RiscVQ. + LlvmAbi::Ilp32d | LlvmAbi::Lp64d => InlineAsmClobberAbi::RiscVD, + _ => InlineAsmClobberAbi::RiscV, + }) + } _ => Err(&["C", "system", "efiapi"]), }, InlineAsmArch::Avr => match name { @@ -1298,6 +1303,36 @@ impl InlineAsmClobberAbi { v24, v25, v26, v27, v28, v29, v30, v31, } }, + InlineAsmClobberAbi::RiscVD => clobbered_regs! { + RiscV RiscVInlineAsmReg { + // ra + x1, + // t0-t2 + x5, x6, x7, + // a0-a7 + x10, x11, x12, x13, x14, x15, x16, x17, + // t3-t6 + x28, x29, x30, x31, + + // Only ABI_FLEN bits of the callee-saved floating point registers are + // preserved. As `d` (FLEN == 64) is the largest floating point register LLVM + // currently supports (`q` is currently assembly only), and ABI_FLEN == 64, + // ABI_FLEN must equal FLEN and therefore there is no need to clobber the + // callee-saved registers. + // ft0-ft7 + f0, f1, f2, f3, f4, f5, f6, f7, + // fa0-fa7 + f10, f11, f12, f13, f14, f15, f16, f17, + // ft8-ft11 + f28, f29, f30, f31, + + // v0-v31 + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, + } + }, InlineAsmClobberAbi::RiscVE => clobbered_regs! { RiscV RiscVInlineAsmReg { // Refs: @@ -1310,6 +1345,9 @@ impl InlineAsmClobberAbi { x5, x6, x7, // a0-a5 x10, x11, x12, x13, x14, x15, + // x16-x31 + x16, x17, x18, x19, x20, x21, x22, x23, + x24, x25, x26, x27, x28, x29, x30, x31, // f0-f31 f0, f1, f2, f3, f4, f5, f6, f7, @@ -1318,7 +1356,6 @@ impl InlineAsmClobberAbi { f24, f25, f26, f27, f28, f29, f30, f31, // v0-v31 - v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs index 3976d9890256f..d67d1373a1048 100644 --- a/compiler/rustc_target/src/asm/riscv.rs +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -54,7 +54,7 @@ impl RiscVInlineAsmRegClass { } } -pub(crate) fn is_e(target_features: &FxIndexSet) -> bool { +fn is_e(target_features: &FxIndexSet) -> bool { target_features.contains(&sym::character('e')) } @@ -63,9 +63,9 @@ fn not_e( _reloc_model: RelocModel, target_features: &FxIndexSet, _target: &Target, - _is_clobber: bool, + is_clobber: bool, ) -> Result<(), &'static str> { - if is_e(target_features) { + if is_e(target_features) && !is_clobber { Err("register can't be used with the `e` target feature") } else { Ok(()) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 6c87becc29c84..1724db1966038 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -5,7 +5,6 @@ use std::path::PathBuf; use std::{debug_assert_matches, iter}; use itertools::{EitherOrBoth, Itertools}; -use rustc_abi::ExternAbi; use rustc_attr_ir::lang_items::{self, LangItem}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::codes::*; @@ -5530,9 +5529,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .. } = fn_ty.fn_sig(tcx).skip_binder() // FIXME(splat): this might need to change if the Fn* traits start using/supporting splat - && fn_sig.abi() == ExternAbi::Rust - && !fn_sig.c_variadic() - && fn_sig.safety() == hir::Safety::Safe + && fn_sig.is_fn_trait_compatible() // Extract first param of fn sig with peeled refs, e.g. `fn(&T)` -> `T` && let Some(&ty::Ref(_, target_ty, needs_mut)) = fn_sig.inputs().first().map(|t| t.kind()) diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index d067fc7f5677f..70efba8361b24 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -1172,7 +1172,7 @@ impl FnSig { } pub fn is_fn_trait_compatible(self) -> bool { - !self.c_variadic() && self.safety().is_safe() && self.abi() == ExternAbi::Rust + !self.c_variadic() && self.safety().is_safe() && self.abi().is_fn_trait_compatible() } /// Set the safety flag. diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index a05dade2bdcf6..145cee958aeb3 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -81,7 +81,7 @@ use crate::vec::Vec; /// [`&str`]: prim@str /// [`std::io`]: crate::io #[stable(feature = "rust1", since = "1.0.0")] -#[doc(notable_trait)] +#[doc(notable_trait(color = "transparent"))] #[cfg_attr(not(test), rustc_diagnostic_item = "IoRead")] #[rustc_must_implement_one_of(read_buf, read)] // Keep this order, it's important for rust-analyzer (the preferred-to-implement method should come first). pub trait Read { diff --git a/library/alloctests/tests/sort/tests.rs b/library/alloctests/tests/sort/tests.rs index ec4c4fa619ddf..2802a13e1faa3 100644 --- a/library/alloctests/tests/sort/tests.rs +++ b/library/alloctests/tests/sort/tests.rs @@ -927,24 +927,33 @@ gen_sort_test_fns_with_default_patterns_3_ty!( ); macro_rules! instantiate_sort_test_inner { - ($sort_impl:ty, miri_yes, $test_fn_name:ident) => { + ($test_fn_name:ident, $sort_impl:ty, (), [$($meta:meta),*]) => { #[test] + $(#[$meta])* fn $test_fn_name() { $crate::sort::tests::$test_fn_name::<$sort_impl>(); } }; - ($sort_impl:ty, miri_no, $test_fn_name:ident) => { - #[test] - #[cfg_attr(miri, ignore)] - fn $test_fn_name() { - $crate::sort::tests::$test_fn_name::<$sort_impl>(); - } + ($test_fn_name:ident, $sort_impl:ty, (miri_no $(, $attr:ident)*), [$($meta:meta),*]) => { + instantiate_sort_test_inner! ( + $test_fn_name, $sort_impl, ($($attr)*), [$($meta,)* cfg_attr(miri, ignore)] + ); + }; + ($test_fn_name:ident, $sort_impl:ty, (miri_yes $(, $attr:ident)*), [$($meta:meta),*]) => { + instantiate_sort_test_inner! ( + $test_fn_name, $sort_impl, ($($attr)*), [$($meta),*] + ); + }; + ($test_fn_name:ident, $sort_impl:ty, (panic $(, $attr:ident)*), [$($meta:meta),*]) => { + instantiate_sort_test_inner! ( + $test_fn_name, $sort_impl, ($($attr)*), [$($meta,)* cfg_attr(not(panic = "unwind"), ignore)] + ); }; } // Using this construct allows us to get warnings for unused test functions. macro_rules! define_instantiate_sort_tests { - ($([$miri_use:ident, $test_fn_name:ident]),*,) => { + ($([$miri_use:ident, $test_fn_name:ident $(, $attr:ident)*]),*,) => { $(pub fn $test_fn_name() { ${concat($test_fn_name, _impl)}::(); })* @@ -954,9 +963,10 @@ macro_rules! define_instantiate_sort_tests { ($sort_impl:ty) => { $( instantiate_sort_test_inner!( + $test_fn_name, $sort_impl, - $miri_use, - $test_fn_name + ($miri_use $(, $attr)*), + [] ); )* } @@ -1083,38 +1093,38 @@ define_instantiate_sort_tests!( [miri_no, observable_is_less_ascending], [miri_no, observable_is_less_descending], [miri_no, observable_is_less_saw_mixed], - [miri_no, panic_retain_orig_set_i32_random], - [miri_yes, panic_retain_orig_set_i32_random_z1], - [miri_no, panic_retain_orig_set_i32_random_d2], - [miri_no, panic_retain_orig_set_i32_random_d20], - [miri_no, panic_retain_orig_set_i32_random_s95], - [miri_no, panic_retain_orig_set_i32_ascending], - [miri_no, panic_retain_orig_set_i32_descending], - [miri_no, panic_retain_orig_set_i32_saw_mixed], - [miri_no, panic_retain_orig_set_cell_i32_random], - [miri_yes, panic_retain_orig_set_cell_i32_random_z1], - [miri_no, panic_retain_orig_set_cell_i32_random_d2], - [miri_no, panic_retain_orig_set_cell_i32_random_d20], - [miri_no, panic_retain_orig_set_cell_i32_random_s95], - [miri_no, panic_retain_orig_set_cell_i32_ascending], - [miri_no, panic_retain_orig_set_cell_i32_descending], - [miri_no, panic_retain_orig_set_cell_i32_saw_mixed], - [miri_no, panic_retain_orig_set_string_random], - [miri_yes, panic_retain_orig_set_string_random_z1], - [miri_no, panic_retain_orig_set_string_random_d2], - [miri_no, panic_retain_orig_set_string_random_d20], - [miri_no, panic_retain_orig_set_string_random_s95], - [miri_no, panic_retain_orig_set_string_ascending], - [miri_no, panic_retain_orig_set_string_descending], - [miri_no, panic_retain_orig_set_string_saw_mixed], - [miri_no, panic_observable_is_less_random], - [miri_yes, panic_observable_is_less_random_z1], - [miri_no, panic_observable_is_less_random_d2], - [miri_no, panic_observable_is_less_random_d20], - [miri_no, panic_observable_is_less_random_s95], - [miri_no, panic_observable_is_less_ascending], - [miri_no, panic_observable_is_less_descending], - [miri_no, panic_observable_is_less_saw_mixed], + [miri_no, panic_retain_orig_set_i32_random, panic], + [miri_yes, panic_retain_orig_set_i32_random_z1, panic], + [miri_no, panic_retain_orig_set_i32_random_d2, panic], + [miri_no, panic_retain_orig_set_i32_random_d20, panic], + [miri_no, panic_retain_orig_set_i32_random_s95, panic], + [miri_no, panic_retain_orig_set_i32_ascending, panic], + [miri_no, panic_retain_orig_set_i32_descending, panic], + [miri_no, panic_retain_orig_set_i32_saw_mixed, panic], + [miri_no, panic_retain_orig_set_cell_i32_random, panic], + [miri_yes, panic_retain_orig_set_cell_i32_random_z1, panic], + [miri_no, panic_retain_orig_set_cell_i32_random_d2, panic], + [miri_no, panic_retain_orig_set_cell_i32_random_d20, panic], + [miri_no, panic_retain_orig_set_cell_i32_random_s95, panic], + [miri_no, panic_retain_orig_set_cell_i32_ascending, panic], + [miri_no, panic_retain_orig_set_cell_i32_descending, panic], + [miri_no, panic_retain_orig_set_cell_i32_saw_mixed, panic], + [miri_no, panic_retain_orig_set_string_random, panic], + [miri_yes, panic_retain_orig_set_string_random_z1, panic], + [miri_no, panic_retain_orig_set_string_random_d2, panic], + [miri_no, panic_retain_orig_set_string_random_d20, panic], + [miri_no, panic_retain_orig_set_string_random_s95, panic], + [miri_no, panic_retain_orig_set_string_ascending, panic], + [miri_no, panic_retain_orig_set_string_descending, panic], + [miri_no, panic_retain_orig_set_string_saw_mixed, panic], + [miri_no, panic_observable_is_less_random, panic], + [miri_yes, panic_observable_is_less_random_z1, panic], + [miri_no, panic_observable_is_less_random_d2, panic], + [miri_no, panic_observable_is_less_random_d20, panic], + [miri_no, panic_observable_is_less_random_s95, panic], + [miri_no, panic_observable_is_less_ascending, panic], + [miri_no, panic_observable_is_less_descending, panic], + [miri_no, panic_observable_is_less_saw_mixed, panic], [miri_no, deterministic_i32_random], [miri_yes, deterministic_i32_random_z1], [miri_no, deterministic_i32_random_d2], @@ -1163,30 +1173,30 @@ define_instantiate_sort_tests!( [miri_no, self_cmp_string_ascending], [miri_no, self_cmp_string_descending], [miri_no, self_cmp_string_saw_mixed], - [miri_no, violate_ord_retain_orig_set_i32_random], - [miri_yes, violate_ord_retain_orig_set_i32_random_z1], - [miri_no, violate_ord_retain_orig_set_i32_random_d2], - [miri_no, violate_ord_retain_orig_set_i32_random_d20], - [miri_no, violate_ord_retain_orig_set_i32_random_s95], - [miri_no, violate_ord_retain_orig_set_i32_ascending], - [miri_no, violate_ord_retain_orig_set_i32_descending], - [miri_no, violate_ord_retain_orig_set_i32_saw_mixed], - [miri_no, violate_ord_retain_orig_set_cell_i32_random], - [miri_yes, violate_ord_retain_orig_set_cell_i32_random_z1], - [miri_no, violate_ord_retain_orig_set_cell_i32_random_d2], - [miri_no, violate_ord_retain_orig_set_cell_i32_random_d20], - [miri_no, violate_ord_retain_orig_set_cell_i32_random_s95], - [miri_no, violate_ord_retain_orig_set_cell_i32_ascending], - [miri_no, violate_ord_retain_orig_set_cell_i32_descending], - [miri_no, violate_ord_retain_orig_set_cell_i32_saw_mixed], - [miri_no, violate_ord_retain_orig_set_string_random], - [miri_yes, violate_ord_retain_orig_set_string_random_z1], - [miri_no, violate_ord_retain_orig_set_string_random_d2], - [miri_no, violate_ord_retain_orig_set_string_random_d20], - [miri_no, violate_ord_retain_orig_set_string_random_s95], - [miri_no, violate_ord_retain_orig_set_string_ascending], - [miri_no, violate_ord_retain_orig_set_string_descending], - [miri_no, violate_ord_retain_orig_set_string_saw_mixed], + [miri_no, violate_ord_retain_orig_set_i32_random, panic], + [miri_yes, violate_ord_retain_orig_set_i32_random_z1, panic], + [miri_no, violate_ord_retain_orig_set_i32_random_d2, panic], + [miri_no, violate_ord_retain_orig_set_i32_random_d20, panic], + [miri_no, violate_ord_retain_orig_set_i32_random_s95, panic], + [miri_no, violate_ord_retain_orig_set_i32_ascending, panic], + [miri_no, violate_ord_retain_orig_set_i32_descending, panic], + [miri_no, violate_ord_retain_orig_set_i32_saw_mixed, panic], + [miri_no, violate_ord_retain_orig_set_cell_i32_random, panic], + [miri_yes, violate_ord_retain_orig_set_cell_i32_random_z1, panic], + [miri_no, violate_ord_retain_orig_set_cell_i32_random_d2, panic], + [miri_no, violate_ord_retain_orig_set_cell_i32_random_d20, panic], + [miri_no, violate_ord_retain_orig_set_cell_i32_random_s95, panic], + [miri_no, violate_ord_retain_orig_set_cell_i32_ascending, panic], + [miri_no, violate_ord_retain_orig_set_cell_i32_descending, panic], + [miri_no, violate_ord_retain_orig_set_cell_i32_saw_mixed, panic], + [miri_no, violate_ord_retain_orig_set_string_random, panic], + [miri_yes, violate_ord_retain_orig_set_string_random_z1, panic], + [miri_no, violate_ord_retain_orig_set_string_random_d2, panic], + [miri_no, violate_ord_retain_orig_set_string_random_d20, panic], + [miri_no, violate_ord_retain_orig_set_string_random_s95, panic], + [miri_no, violate_ord_retain_orig_set_string_ascending, panic], + [miri_no, violate_ord_retain_orig_set_string_descending, panic], + [miri_no, violate_ord_retain_orig_set_string_saw_mixed, panic], ); macro_rules! instantiate_sort_tests { diff --git a/library/core/src/future/future.rs b/library/core/src/future/future.rs index fab13bb7c8535..b84b00510c41e 100644 --- a/library/core/src/future/future.rs +++ b/library/core/src/future/future.rs @@ -25,7 +25,7 @@ use crate::task::{Context, Poll}; /// /// [`async`]: ../../std/keyword.async.html /// [`Waker`]: crate::task::Waker -#[doc(notable_trait)] +#[doc(notable_trait(color = "transparent"))] #[doc(search_unbox)] #[must_use = "futures do nothing unless you `.await` or poll them"] #[stable(feature = "futures_api", since = "1.36.0")] diff --git a/library/core/src/io/write.rs b/library/core/src/io/write.rs index cdddd380885f4..1318d4bc0ee8a 100644 --- a/library/core/src/io/write.rs +++ b/library/core/src/io/write.rs @@ -47,7 +47,7 @@ use crate::io::{Error, IoSlice, Result}; /// /// [`write_all`]: Write::write_all #[stable(feature = "rust1", since = "1.0.0")] -#[doc(notable_trait)] +#[doc(notable_trait(color = "transparent"))] #[cfg_attr(not(test), rustc_diagnostic_item = "IoWrite")] pub trait Write { /// Writes a buffer into this writer, returning how many bytes were written. diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index cc4077d0ee26f..a5165217aca43 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -34,7 +34,7 @@ fn _assert_is_dyn_compatible(_: &dyn Iterator) {} label = "`{Self}` is not an iterator", message = "`{Self}` is not an iterator" )] -#[doc(notable_trait)] +#[doc(notable_trait(color = "transparent"))] #[lang = "iterator"] #[rustc_diagnostic_item = "Iterator"] #[must_use = "iterators are lazy and do nothing unless consumed"] diff --git a/src/doc/rustc-dev-guide/.github/workflows/ci.yml b/src/doc/rustc-dev-guide/.github/workflows/ci.yml index 23b590e4aebaf..afdf7ca185e80 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/ci.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/ci.yml @@ -9,10 +9,14 @@ on: # Run multiple times a day as the successful cached links are not checked every time. - cron: "0 */8 * * *" +permissions: {} + jobs: ci: if: github.repository == 'rust-lang/rustc-dev-guide' runs-on: ubuntu-latest + permissions: + contents: write # Required by `git push` env: MDBOOK_VERSION: 0.5.2 MDBOOK_LINKCHECK2_VERSION: 0.13.0 @@ -26,13 +30,14 @@ jobs: with: # linkcheck needs the base commit. fetch-depth: 0 + persist-credentials: false - name: Test CI scripts run: ci/tests/report-scheduled-linkcheck-failure.sh - name: Cache binaries id: mdbook-cache - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | ~/.cargo/bin @@ -41,7 +46,7 @@ jobs: - name: Restore cached Linkcheck if: github.event_name == 'schedule' id: cache-linkcheck-restore - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: book/linkcheck/cache.json key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}--${{ github.run_id }} @@ -57,9 +62,9 @@ jobs: - name: Install Dependencies if: steps.mdbook-cache.outputs.cache-hit != 'true' run: | - cargo install --locked mdbook --version ${{ env.MDBOOK_VERSION }} - cargo install --locked mdbook-linkcheck2 --version ${{ env.MDBOOK_LINKCHECK2_VERSION }} - cargo install --locked mdbook-mermaid --version ${{ env.MDBOOK_MERMAID_VERSION }} + cargo install --locked mdbook --version ${MDBOOK_VERSION} + cargo install --locked mdbook-linkcheck2 --version ${MDBOOK_LINKCHECK2_VERSION} + cargo install --locked mdbook-mermaid --version ${MDBOOK_MERMAID_VERSION} - name: Check build run: ENABLE_LINKCHECK=1 mdbook build @@ -73,7 +78,7 @@ jobs: - name: Save cached Linkcheck id: cache-linkcheck-save if: ${{ !cancelled() && github.event_name == 'schedule' }} - uses: actions/cache/save@v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: book/linkcheck/cache.json key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}--${{ github.run_id }} @@ -81,16 +86,16 @@ jobs: - name: Deploy to gh-pages if: github.event_name == 'push' run: | - touch "${{ env.DEPLOY_DIR }}/.nojekyll" - cp CNAME "${{ env.DEPLOY_DIR }}" - cd "${{ env.DEPLOY_DIR }}" + touch "${DEPLOY_DIR}/.nojekyll" + cp CNAME "${DEPLOY_DIR}" + cd "${DEPLOY_DIR}" rm -rf .git git init git config user.name "Deploy from CI" git config user.email "" git add . git commit -m "Deploy ${GITHUB_SHA} to gh-pages" - git push --quiet -f "https://x-token:${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}" HEAD:gh-pages + git push --quiet -f "https://x-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}" HEAD:gh-pages - name: Check if files comply with semantic line breaks continue-on-error: true @@ -104,11 +109,11 @@ jobs: if: failure() && github.event_name == 'schedule' runs-on: ubuntu-latest permissions: - actions: read - contents: read - issues: write + issues: write # Required to open issues with `gh issue create` steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Open an issue or comment on an existing one run: ci/report-scheduled-linkcheck-failure.sh env: @@ -120,10 +125,11 @@ jobs: if: success() && github.event_name == 'schedule' runs-on: ubuntu-latest permissions: - contents: read - issues: write + issues: write # Required to close issues with `gh issue close` steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Close issue run: ci/close-scheduled-linkcheck-issues.sh env: diff --git a/src/doc/rustc-dev-guide/.github/workflows/date-check.yml b/src/doc/rustc-dev-guide/.github/workflows/date-check.yml index fb7ff3bc04b11..868891c786c24 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/date-check.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/date-check.yml @@ -8,14 +8,19 @@ on: # Allow manually starting the workflow workflow_dispatch: +permissions: {} + jobs: date-check: if: github.repository == 'rust-lang/rustc-dev-guide' runs-on: ubuntu-latest - + permissions: + issues: write # Required by `actions/github-script` to open issues steps: - name: Checkout repo - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Ensure Rust is up-to-date run: | @@ -25,7 +30,7 @@ jobs: run: cargo run --manifest-path ci/date-check/Cargo.toml . > date-check-output.txt - name: Open issue - uses: actions/github-script@v7 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 with: script: | const fs = require('fs'); diff --git a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml index 0a2cfd1c85985..fa967b3ee5bb1 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml @@ -6,9 +6,15 @@ on: # Run at 04:00 UTC every Monday - cron: '0 4 * * 1' +permissions: {} + jobs: pull: if: github.repository == 'rust-lang/rustc-dev-guide' + permissions: + contents: write # Required by `josh-sync/rustc-pull` to push to a branch + pull-requests: write # Required by `josh-sync/rustc-pull` to open a PR + # zizmor: ignore[unpinned-uses] We explicitly want to track the main branch. uses: rust-lang/josh-sync/.github/workflows/rustc-pull.yml@main with: github-app-id: ${{ vars.APP_CLIENT_ID }} diff --git a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs index 56761cbb07487..6125f2261cc77 100644 --- a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs +++ b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs @@ -121,12 +121,10 @@ fn comply(content: &str) -> String { line.find(|ch: char| !ch.is_whitespace()).unwrap() }; let mut newly_split_lines = line.split_inclusive(&*REGEX_SPLIT); - let first = newly_split_lines.next().unwrap().trim_end().to_owned(); - let mut remaining: Vec<_> = newly_split_lines - .map(|portion| format!("{:indent$}{}", "", portion.trim_end())) - .collect(); let mut new_lines = Vec::new(); - new_lines.push(first); + new_lines.push(newly_split_lines.next().unwrap().trim_end().to_owned()); + let mut remaining: Vec<_> = + newly_split_lines.map(|portion| format!("{:indent$}{}", "", portion.trim())).collect(); new_lines.append(&mut remaining); new_content.splice(new_n..=new_n, new_lines.clone()); new_n += new_lines.len() - 1; @@ -244,13 +242,14 @@ some code. block ``` sentence with *italics* should not be ignored. truly. git log main.. compiler - foo. bar. baz o? whatever r? @reviewer r? @reviewer ~? diagnostic the queries that we do, as well as the **query DAG**. The + +trim double space. or many spaces. in this next sentence "; let expected = " # some. heading @@ -276,9 +275,6 @@ some code. block sentence with *italics* should not be ignored. truly. git log main.. compiler - foo. - bar. - baz o? whatever r? @reviewer @@ -287,6 +283,10 @@ r? @reviewer the queries that we do, as well as the **query DAG**. The + +trim double space. +or many spaces. +in this next sentence "; assert_eq!(expected, comply(original)); } diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index 0e23be130c403..257757c17fca9 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -42,11 +42,11 @@ you should include fixes for those in the rustc PR as well. * Using `git subtree` * `clippy` ([sync guide](https://doc.rust-lang.org/nightly/clippy/development/infrastructure/sync.html#performing-the-sync-from-rust-langrust-to-clippy)) * `portable-simd` ([sync script](https://github.com/rust-lang/portable-simd/blob/master/subtree-sync.sh)) - * `rustc_codegen_gcc` ([sync guide](https://github.com/rust-lang/rustc_codegen_gcc/blob/master/doc/subtree.md)) * Using the [josh](#synchronizing-a-josh-subtree) tool * `miri` * `rust-analyzer` * `rustc_codegen_cranelift` + * `rustc_codegen_gcc` * `rustc-dev-guide` * `rustfmt` * `compiler-builtins` diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-html-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-html-test-suite.md index a88e60c183b0d..13b618e7d4e44 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-html-test-suite.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-html-test-suite.md @@ -3,7 +3,7 @@ This page is about the test suite named `rustdoc-html` used to test the HTML output of `rustdoc`. For other rustdoc-specific test suites, see [Rustdoc test suites]. -Each test file in this test suite is simply a Rust source file `file.rs` sprinkled with +Each test file in this test suite is simply a Rust source file sprinkled with so-called *directives* located inside normal Rust code comments. These come in two flavors: *Compiletest* and *HtmlDocCk*. @@ -145,8 +145,8 @@ Checks that the directory given by `PATH` contains exactly `ENTRIES`. ## Compiletest Directives (Brief) As mentioned in the introduction, you also have access to [compiletest directives]. -Most importantly, they allow you to register auxiliary crates and -to pass flags to the `rustdoc` binary under test. +Most importantly, +they allow you to register auxiliary crates and to pass flags to the `rustdoc` binary under test. It's *strongly recommended* to read that chapter if you don't know anything about them yet. Here are some details that are relevant to this test suite specifically: @@ -177,9 +177,9 @@ the original negative check failing. **Note**: Please avoid the use of *negated* checks! -**Tip**: If you can't avoid it, please **always** pair it with an analogous positive check in the -immediate vicinity, so people changing "the shape" have a chance to notice and to update the -negated check! +**Tip**: If you can't avoid it, +please **always** pair it with an analogous positive check in the immediate vicinity, +so people changing "the shape" have a chance to notice and to update the negated check! ## Limitations diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index 7f3c38385ceca..68e35287dac4a 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -3,19 +3,19 @@ The primary goal of our CI system is to ensure that the `main` branch of `rust-lang/rust` is always in a valid state by passing our test suite. -From a high-level point of view, when you open a pull request at -`rust-lang/rust`, the following will happen: +From a high-level point of view, when you open a pull request at `rust-lang/rust`, +the following will happen: - A small [subset](#pull-request-builds) of tests and checks are run after each push to the PR. This should help catch common errors. - When the PR is approved, the [bors] bot enqueues the PR into a [merge queue]. -- Once the PR gets to the front of the queue, bors will create a merge commit - and run the [full test suite](#auto-builds) on it. +- Once the PR gets to the front of the queue, + bors will create a merge commit and run the [full test suite](#auto-builds) on it. The merge commit either contains only one specific PR or it can be a ["rollup"](#rollups) which combines multiple PRs together, to reduce CI costs and merge delays. - Once the whole test suite finishes, two things can happen. - Either CI fails with an error that needs to be addressed by the developer, or CI succeeds and - the merge commit is then pushed to the `main` branch. + Either CI fails with an error that needs to be addressed by the developer, + or CI succeeds and the merge commit is then pushed to the `main` branch. If you want to modify what gets executed on CI, see [Modifying CI jobs](#modifying-ci-jobs). @@ -23,13 +23,13 @@ If you want to modify what gets executed on CI, see [Modifying CI jobs](#modifyi -Our CI is primarily executed on [GitHub Actions], with a single workflow defined -in [`.github/workflows/ci.yml`], which contains a bunch of steps that are -unified for all CI jobs that we execute. +Our CI is primarily executed on [GitHub Actions], +with a single workflow defined in [`.github/workflows/ci.yml`], +which contains a bunch of steps that are unified for all CI jobs that we execute. When a commit is pushed to a corresponding branch or a PR, the workflow executes the [`src/ci/citool`] crate, which dynamically generates the specific CI jobs that should be executed. -This script uses the [`jobs.yml`] file as an -input, which contains a declarative configuration of all our CI jobs. +This script uses the [`jobs.yml`] file as an input, +which contains a declarative configuration of all our CI jobs. > Almost all build steps shell out to separate scripts. This keeps the CI fairly > platform independent (i.e., we are not overly reliant on GitHub Actions). @@ -48,14 +48,14 @@ There are two broad categories of jobs that are executed, `dist` and non-`dist` The same builds are also used for actual releases: our release process basically consists of copying those artifacts from `rust-lang-ci2` to the production endpoint and signing them. -- Non-dist jobs run our full test suite on the platform, and the test suite of - all the tools we ship through rustup; +- Non-dist jobs run our full test suite on the platform, + and the test suite of all the tools we ship through rustup; The amount of stuff we test depends on - the platform (for example some tests are run only on Tier 1 platforms), and - some quicker platforms are grouped together on the same builder to avoid wasting CI resources. + the platform (for example some tests are run only on Tier 1 platforms), + and some quicker platforms are grouped together on the same builder to avoid wasting CI resources. -Based on an input event (usually a push to a branch), we execute one of three -kinds of builds (sets of jobs). +Based on an input event (usually a push to a branch), +we execute one of three kinds of builds (sets of jobs). 1. PR builds 2. Auto builds @@ -66,29 +66,29 @@ kinds of builds (sets of jobs). ### Pull Request builds After each push to a pull request, a set of `pr` jobs are executed. -Currently, these execute the `test-x86_64-gnu-llvm-X`, `test-x86_64-gnu-tools`, `test-pr-check-1`, `test-pr-check-2` -and `test-tidy` jobs, all running on Linux. +Currently, these execute the `test-x86_64-gnu-llvm-X`, `test-x86_64-gnu-tools`, `test-pr-check-1`, +`test-pr-check-2` and `test-tidy` jobs, all running on Linux. These execute a relatively short (~40 minutes) and lightweight test suite that should catch common issues. -More specifically, they run a set of lints, they try to perform a cross-compile check -build to Windows mingw (without producing any artifacts), and they test the -compiler using a *system* version of LLVM. -Unfortunately, it would take too many -resources to run the full test suite for each commit on every PR. +More specifically, they run a set of lints, +they try to perform a cross-compile check build to Windows mingw (without producing any artifacts), +and they test the compiler using a *system* version of LLVM. +Unfortunately, +it would take too many resources to run the full test suite for each commit on every PR. > **Note on doc comments** > -> Note that PR CI as of Oct 2024 by default does not try to -> run `./x doc xxx`. This means that if you have any broken intradoc links that -> would lead to `./x doc xxx` failing, it will happen very late into the full -> merge queue CI pipeline. +> Note that PR CI as of Oct 2024 by default does not try to run `./x doc xxx`. +> This means that if you have any broken intradoc links that +> would lead to `./x doc xxx` failing, +> it will happen very late into the full merge queue CI pipeline. > > Thus, it is a good idea to run `./x doc xxx` locally for any doc comment > changes to help catch these early. PR jobs are defined in the `pr` section of [`jobs.yml`]. -Their results can be observed -directly on the PR, in the "CI checks" section at the bottom of the PR page. +Their results can be observed directly on the PR, +in the "CI checks" section at the bottom of the PR page. ### Auto builds @@ -110,12 +110,12 @@ At any given time, at most a single `auto` build is being executed. Find out more in [Merging PRs serially with bors](#merging-prs-serially-with-bors). Normally, when an auto job fails, the whole CI workflow immediately ends. -However, it can be useful to -create auto jobs that are "non-blocking", or optional, to test them on CI for some time before blocking -merges on them. +However, it can be useful to create auto jobs that are "non-blocking", +or optional, to test them on CI for some time before blocking merges on them. This can be useful if those jobs can be flaky. -To do that, prefix such a job with `optional-`, and set `continue_on_error: true` for it in [`jobs.yml`]. +To do that, prefix such a job with `optional-`, +and set `continue_on_error: true` for it in [`jobs.yml`]. [platform tiers]: https://forge.rust-lang.org/release/platform-support.html#rust-platform-support [auto]: https://github.com/rust-lang/rust/tree/automation/bors/auto @@ -135,20 +135,20 @@ There are several use-cases for try builds: try build that runs the [dist-x86_64-linux] CI job, which builds an optimized version of the compiler on Linux (this job is currently executed by default when you start a try build). - To create a try build and schedule it for a - performance benchmark, you can use the `@bors try @rust-timer queue` command combination. + To create a try build and schedule it for a performance benchmark, + you can use the `@bors try @rust-timer queue` command combination. - Check the impact of the PR across the Rust ecosystem, using a [Crater](crater.md) run. - Again, a working compiler build is needed for this, which can be produced by - the [dist-x86_64-linux] CI job. + Again, a working compiler build is needed for this, + which can be produced by the [dist-x86_64-linux] CI job. - Run a specific CI job (e.g. Windows tests) on a PR, to quickly test if it passes the test suite executed by that job. -By default, if you send a comment with `@bors try`, the jobs defined in the `try` section of -[`jobs.yml`] will be executed. +By default, if you send a comment with `@bors try`, +the jobs defined in the `try` section of [`jobs.yml`] will be executed. We call this mode a "fast try build". Such a try build will not execute any tests, and it will allow compilation warnings. -It is useful when you want to -get an optimized toolchain as fast as possible, for a Crater run or performance benchmarks, +It is useful when you want to get an optimized toolchain as fast as possible, +for a Crater run or performance benchmarks, even if it might not be working fully correctly. The CI job executed in fast try builds has a special suffix (`-quick`), @@ -157,13 +157,14 @@ If you want to do the full build instead, specify its job name in a job pattern (explained below). If you want to run custom CI jobs in a try build and make sure that they pass all tests and do -not produce any compilation warnings, you can select CI jobs to be executed by specifying a *job pattern*, +not produce any compilation warnings, +you can select CI jobs to be executed by specifying a *job pattern*, which can be used in one of two ways: - You can add a set of `try-job: ` directives to the PR description (described below) and then simply run `@bors try`. CI will read these directives and run the jobs that you have specified. - This is - useful if you want to rerun the same set of try jobs multiple times, after incrementally modifying a PR. + This is useful if you want to rerun the same set of try jobs multiple times, + after incrementally modifying a PR. - You can specify the job pattern using the `jobs` parameter of the try command: `@bors try jobs=`. This is useful for one-off try builds with specific jobs. Note that the `jobs` parameter has a higher priority than the PR description directives. @@ -172,10 +173,10 @@ which can be used in one of two ways: Each job pattern can either be an exact name of a job or a glob pattern that matches multiple jobs, for example `*msvc*` or `*-alt`. You can start at most 20 jobs in a single try build. -When using -glob patterns in the PR description, you can optionally wrap them in backticks (`` ` ``) to avoid GitHub rendering -the pattern as Markdown if it contains e.g. an asterisk. Note that this escaping will not work when using -the `@bors jobs=` parameter. +When using glob patterns in the PR description, +you can optionally wrap them in backticks (`` ` ``) to avoid GitHub rendering +the pattern as Markdown if it contains e.g. an asterisk. +Note that this escaping will not work when using the `@bors jobs=` parameter. The job pattern needs to match one or more jobs defined in the `auto` or `optional` sections of [`jobs.yml`]: @@ -221,34 +222,34 @@ their results can be seen on [the GitHub Actions workflows page], although usually you will be notified of the result by a comment made by bors on the corresponding PR. -Multiple try builds can execute concurrently across different PRs, but there can be at most -a single try build running on a single PR at any given time. +Multiple try builds can execute concurrently across different PRs, +but there can be at most a single try build running on a single PR at any given time. [rustc-perf]: https://github.com/rust-lang/rustc-perf [try]: https://github.com/rust-lang/rust/tree/automation/bors/try ### Modifying CI jobs -If you want to modify what gets executed on our CI, you can simply modify the -`pr`, `auto` or `try` sections of the [`jobs.yml`] file. +If you want to modify what gets executed on our CI, you can simply modify the `pr`, +`auto` or `try` sections of the [`jobs.yml`] file. -You can also modify what gets executed temporarily, for example to test a -particular platform or configuration that is challenging to test locally (for +You can also modify what gets executed temporarily, +for example to test a particular platform or configuration that is challenging to test locally (for example, if a Windows build fails, but you don't have access to a Windows machine). Don't hesitate to use CI resources in such situations. You can perform an arbitrary CI job in two ways: -- Use the [try build](#try-builds) functionality, and specify the CI jobs that - you want to be executed in try builds in your PR description. +- Use the [try build](#try-builds) functionality, + and specify the CI jobs that you want to be executed in try builds in your PR description. - Modify the [`pr`](#pull-request-builds) section of `jobs.yml` to specify which CI jobs should be executed after each push to your PR. This might be faster than repeatedly starting try builds. -To modify the jobs executed after each push to a PR, you can simply copy one of -the job definitions from the `auto` section to the `pr` section. +To modify the jobs executed after each push to a PR, +you can simply copy one of the job definitions from the `auto` section to the `pr` section. For example, the `x86_64-msvc` job is responsible for running the 64-bit MSVC tests. -You can copy it to the `pr` section to cause it to be executed after a commit is pushed -to your PR, like this: +You can copy it to the `pr` section to cause it to be executed after a commit is pushed to your PR, +like this: ```yaml pr: @@ -276,41 +277,40 @@ A good practice is to prefix `[WIP]` in PR title while still running try jobs and `[DO NOT MERGE]` in the commit that modifies the CI jobs for testing purposes. -Although you are welcome to use CI, just be conscious that this is a shared -resource with limited concurrency. +Although you are welcome to use CI, +just be conscious that this is a shared resource with limited concurrency. Try not to enable too many jobs at once; one or two should be sufficient in most cases. ## Merging PRs serially with bors -CI services usually test the last commit of a branch merged with the last commit -in `main`, and while that’s great to check if the feature works in isolation, +CI services usually test the last commit of a branch merged with the last commit in `main`, +and while that’s great to check if the feature works in isolation, it doesn’t provide any guarantee the code is going to work once it’s merged. -Breakages like these usually happen when another, incompatible PR is merged -after the build happened. +Breakages like these usually happen when another, +incompatible PR is merged after the build happened. To ensure a `main` branch that works all the time, we forbid manual merges. Instead, all PRs have to be approved through our bot, [bors]. All the approved PRs are put in a [merge queue] (sorted by priority and creation date) and are automatically tested one at the time. -If all the builders are green, the PR is merged, otherwise the failure is -recorded and the PR will have to be re-approved again. +If all the builders are green, the PR is merged, +otherwise the failure is recorded and the PR will have to be re-approved again. Bors doesn’t interact with CI services directly, but it works by pushing the merge commit it wants to test to specific branches (like `automation/bors/auto` or `automation/bors/try`), which are configured to execute CI checks. Bors then detects the outcome of the build by listening for either Commit Statuses or Check Runs. -Since the merge commit is -based on the latest `main` and only one can be tested at the same time, when -the results are green, `main` is fast-forwarded to that merge commit. +Since the merge commit is based on the latest `main` and only one can be tested at the same time, +when the results are green, `main` is fast-forwarded to that merge commit. -Unfortunately, testing a single PR at a time, combined with our long CI (~2 -hours for a full run), means we can’t merge a lot of PRs in a single day, and a -single failure greatly impacts our throughput. +Unfortunately, testing a single PR at a time, combined with our long CI (~2 hours for a full run), +means we can’t merge a lot of PRs in a single day, +and a single failure greatly impacts our throughput. The maximum number of PRs we can merge in a day is around ~10. -The long CI run times, and requirement for a large builder pool, is largely due -to the fact that full release artifacts are built in the `dist-` builders. +The long CI run times, and requirement for a large builder pool, +is largely due to the fact that full release artifacts are built in the `dist-` builders. This is worth it because these release artifacts: - Allow perf testing even at a later date. @@ -320,18 +320,18 @@ This is worth it because these release artifacts: ### Rollups Some PRs don’t need the full test suite to be executed: trivial changes like -typo fixes or README improvements *shouldn’t* break the build, and testing every -single one of them for 2+ hours would be wasteful. -To solve this, we regularly create a "rollup", a PR where we merge several pending trivial PRs so -they can be tested together. +typo fixes or README improvements *shouldn’t* break the build, +and testing every single one of them for 2+ hours would be wasteful. +To solve this, we regularly create a "rollup", +a PR where we merge several pending trivial PRs so they can be tested together. Rollups are created manually by a team member using the "create a rollup" button on the [merge queue]. The team member uses their judgment to decide if a PR is risky or not. ## Docker -All CI jobs, except those on macOS and Windows, are executed inside that -platform’s custom [Docker container]. +All CI jobs, except those on macOS and Windows, +are executed inside that platform’s custom [Docker container]. This has a lot of advantages for us: - The build environment is consistent regardless of the changes of the @@ -352,8 +352,8 @@ The Docker images prefixed with `dist-` are used for building artifacts while those without that prefix run tests and checks. We also run tests for less common architectures (mainly Tier 2 and Tier 3 platforms) in CI. -Since those platforms are not x86, we either run everything -inside QEMU, or we just cross-compile if we don’t want to run the tests for that platform. +Since those platforms are not x86, we either run everything inside QEMU, +or we just cross-compile if we don’t want to run the tests for that platform. These builders are running on a special pool of builders set up and maintained for us by GitHub. @@ -366,20 +366,20 @@ Our CI workflow uses various caching mechanisms, mainly for two things: ### Docker images caching The Docker images we use to run most of the Linux-based builders take a *long* time to fully build. -To speed up the build, we cache them using [Docker registry -caching], with the intermediate artifacts being stored on [ghcr.io]. +To speed up the build, we cache them using [Docker registry caching], +with the intermediate artifacts being stored on [ghcr.io]. We also push the built Docker images to ghcr, so that they can be reused by other tools (rustup) or by developers running the Docker build locally (to speed up their build). -Since we test multiple, diverged branches (`main`, `beta` and `stable`), we -can’t rely on a single cache for the images, otherwise builds on a branch would -override the cache for the others. -Instead, we store the images under different -tags, identifying them with a custom hash made from the contents of all the +Since we test multiple, diverged branches (`main`, `beta`, and `stable`), +we can’t rely on a single cache for the images, +otherwise builds on a branch would override the cache for the others. +Instead, we store the images under different tags, +identifying them with a custom hash made from the contents of all the Dockerfiles and related scripts. -The CI calculates a hash key, so that the cache of a Docker image is -invalidated if one of the following changes: +The CI calculates a hash key, +so that the cache of a Docker image is invalidated if one of the following changes: - Dockerfile - Files copied into the Docker image in the Dockerfile @@ -390,10 +390,10 @@ invalidated if one of the following changes: ### LLVM caching with Sccache -We build some C/C++ stuff in various CI jobs, and we rely on [Sccache] to cache -the intermediate LLVM artifacts. -Sccache is a distributed ccache developed by -Mozilla, which can use an object storage bucket as the storage backend. +We build some C/C++ stuff in various CI jobs, +and we rely on [Sccache] to cache the intermediate LLVM artifacts. +Sccache is a distributed ccache developed by Mozilla, +which can use an object storage bucket as the storage backend. With Sccache there's no need to calculate the hash key ourselves. Sccache invalidates the cache automatically when it detects changes to relevant inputs, @@ -402,8 +402,8 @@ So we just pass the Sccache wrapper on top of Cargo and Sccache does the rest. We store the persistent artifacts on the S3 bucket, `rust-lang-ci-sccache2`. So when the CI runs, if Sccache sees that LLVM is being compiled with the same C/C++ -compiler and the LLVM source code is the same, Sccache retrieves the individual -compiled translation units from S3. +compiler and the LLVM source code is the same, +Sccache retrieves the individual compiled translation units from S3. [sccache]: https://github.com/mozilla/sccache @@ -413,8 +413,8 @@ During the years, we developed some custom tooling to improve our CI experience. ### Rust Log Analyzer to show the error message in PRs -The build logs for `rust-lang/rust` are huge, and it’s not practical to find -what caused the build to fail by looking at the logs. +The build logs for `rust-lang/rust` are huge, +and it’s not practical to find what caused the build to fail by looking at the logs. We therefore developed a bot called [Rust Log Analyzer][rla] (RLA) that receives the build logs on failure, and extracts the error message automatically, posting it on the PR thread. @@ -428,26 +428,25 @@ good at identifying the relevant lines, even if it’s an error we've never seen ### Toolstate to support allowed failures -The `rust-lang/rust` repo doesn’t only test the compiler on its CI, but also a -variety of tools and documentation. +The `rust-lang/rust` repo doesn’t only test the compiler on its CI, +but also a variety of tools and documentation. Some documentation is pulled in via git submodules. If we blocked merging rustc PRs on the documentation being fixed, we would be stuck in a chicken-and-egg problem, because the documentation's CI would not pass since updating it would need the not-yet-merged version of rustc to test against (and we usually require CI to be passing). -To avoid the problem, submodules are allowed to fail, and their status is -recorded in [rust-toolstate]. -When a submodule breaks, a bot automatically pings -the maintainers so they know about the breakage, and it records the failure on -the toolstate repository. -The release process will then ignore broken tools on -nightly, removing them from the shipped nightlies. +To avoid the problem, submodules are allowed to fail, +and their status is recorded in [rust-toolstate]. +When a submodule breaks, a bot automatically pings the maintainers so they know about the breakage, +and it records the failure on the toolstate repository. +The release process will then ignore broken tools on nightly, +removing them from the shipped nightlies. While tool failures are allowed most of the time, they’re automatically forbidden a week before a release: we don’t care if tools are broken on nightly -but they must work on beta and stable, so they also need to work on nightly a -few days before we promote nightly to beta. +but they must work on beta and stable, +so they also need to work on nightly a few days before we promote nightly to beta. More information is available in the [toolstate documentation]. @@ -473,8 +472,8 @@ To learn more about the dashboard, see the [Datadog CI docs]. ## Determining the CI configuration -If you want to determine which `bootstrap.toml` settings are used in CI for a -particular job, it is probably easiest to just look at the build log. +If you want to determine which `bootstrap.toml` settings are used in CI for a particular job, +it is probably easiest to just look at the build log. To do this: 1. Go to [the Rust CI successful workflow runs page][workflow runs] diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 639386b49cf98..900c042a3f1fd 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -477,6 +477,10 @@ causing separate files to be generated for 32bit and 64bit systems. > (including transitively via `rust.debug` or `rust.debug-assertions`), > they will not be run! +Similarly, +to generate separate files for each panic strategy ("panic-abort" and "panic-unwind"), +add `// EMIT_MIR_FOR_EACH_PANIC_STRATEGY` to the test. + [`tests/mir-opt`]: https://github.com/rust-lang/rust/tree/HEAD/tests/mir-opt [`ignore-std-debug-assertions` directive]: ./directives.md#controlling-when-tests-are-run diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index ed6c8b9f93951..7d8fc182ba453 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -274,9 +274,11 @@ For example: - A test with the `//@ edition: 2015..2021` directive can be run under the 2015 and the 2018 edition, so the upper bound is exclusive just like in Rust (note that there's no equivalent to Rust's `..=` where the upper bound is inclusive). - However, CI will only run the test with the lowest edition in the range (which is 2015 in this example). + However, + CI will only run the test with the lowest edition in the range (which is 2015 in this example). - A test with the `//@ edition: 2018..` directive will run under 2018 edition or greater. - However, CI will only run the test with the lowest edition in the range (which is 2018 in this example). + However, + CI will only run the test with the lowest edition in the range (which is 2018 in this example). You can also force `./x test` to use a specific edition by passing the `-- --edition=` argument. However, tests with the `//@ edition` directive will clamp the value passed to the argument. @@ -300,10 +302,11 @@ Asked in #### Test-suite-specific directives -The test suites [`rustdoc-html`][rustdoc-html-tests], [`rustdoc-js`/`rustdoc-js-std`][rustdoc-js-tests] +The test suites [`rustdoc-html`][rustdoc-html-tests], +[`rustdoc-js`/`rustdoc-js-std`][rustdoc-js-tests], and [`rustdoc-json`][rustdoc-json-tests] each feature an additional set of directives whose basic -syntax resembles the one of compiletest directives but which are ultimately read and checked by -separate tools. +syntax resembles one of the compiletest directives, +but which are ultimately read and checked by separate tools. For more information, please read their respective chapters as linked above. [rustdoc-html-tests]: ../rustdoc-internals/rustdoc-html-test-suite.md diff --git a/src/doc/rustc-dev-guide/src/traits/canonicalization.md b/src/doc/rustc-dev-guide/src/traits/canonicalization.md index 4bd56a020f14f..d1cd1a2cefb49 100644 --- a/src/doc/rustc-dev-guide/src/traits/canonicalization.md +++ b/src/doc/rustc-dev-guide/src/traits/canonicalization.md @@ -4,52 +4,48 @@ > [Next-gen trait solving Canonicalization chapter](../solve/canonicalization.md). > It is suggested to reorganize these contents in the future. -Canonicalization is the process of **isolating** an inference value -from its context. It is a key part of implementing -[canonical queries][cq], and you may wish to read the parent chapter -to get more context. +Canonicalization is the process of **isolating** an inference value from its context. +It is a key part of implementing [canonical queries][cq], +and you may wish to read the parent chapter to get more context. Canonicalization is really based on a very simple concept: every [inference variable](../type-inference.md#vars) is always in one of -two states: either it is **unbound**, in which case we don't know yet -what type it is, or it is **bound**, in which case we do. So to -isolate some data-structure T that contains types/regions from its -environment, we just walk down and find the unbound variables that +two states: either it is **unbound**, in which case we don't know yet what type it is, +or it is **bound**, in which case we do. +So, to isolate some data-structure T that contains types/regions from its environment, +we just walk down and find the unbound variables that appear in T; those variables get replaced with "canonical variables", -starting from zero and numbered in a fixed order (left to right, for -the most part, but really it doesn't matter as long as it is -consistent). +starting from zero and numbered in a fixed order (left to right, for the most part, +but really it doesn't matter as long as it is consistent). [cq]: ./canonical-queries.md -So, for example, if we have the type `X = (?T, ?U)`, where `?T` and -`?U` are distinct, unbound inference variables, then the canonical -form of `X` would be `(?0, ?1)`, where `?0` and `?1` represent these -**canonical placeholders**. Note that the type `Y = (?U, ?T)` also -canonicalizes to `(?0, ?1)`. But the type `Z = (?T, ?T)` would -canonicalize to `(?0, ?0)` (as would `(?U, ?U)`). In other words, the -exact identity of the inference variables is not important – unless -they are repeated. - -We use this to improve caching as well as to detect cycles and other -things during trait resolution. Roughly speaking, the idea is that if -two trait queries have the same canonical form, then they will get -the same answer. That answer will be expressed in terms of the -canonical variables (`?0`, `?1`), which we can then map back to the -original variables (`?T`, `?U`). +So, for example, if we have the type `X = (?T, ?U)`, where `?T` and `?U` are distinct +unbound inference variables, then the canonical form of `X` would be `(?0, ?1)`, +where `?0` and `?1` represent these **canonical placeholders**. +Note that the type `Y = (?U, ?T)` also canonicalizes to `(?0, ?1)`. +But the type `Z = (?T, ?T)` would canonicalize to `(?0, ?0)` (as would `(?U, ?U)`). +In other words, the exact identity of the inference variables is not important, +unless they are repeated. + +We use this to improve caching as well as to detect cycles and other things during trait resolution. +Roughly speaking, the idea is that if two trait queries have the same canonical form, +then they will get the same answer. +That answer will be expressed in terms of the canonical variables (`?0`, +`?1`), which we can then map back to the original variables (`?T`, `?U`). ## Canonicalizing the query To see how it works, imagine that we are asking to solve the following trait query: `?A: Foo<'static, ?B>`, where `?A` and `?B` are unbound. -This query contains two unbound variables, but it also contains the -lifetime `'static`. The trait system generally ignores all lifetimes -and treats them equally, so when canonicalizing, we will *also* -replace any [free lifetime](../appendix/background.md#free-vs-bound) with a -canonical variable (Note that `'static` is actually a _free_ lifetime -variable here. We are not considering it in the typing context of the whole -program but only in the context of this trait reference. Mathematically, we -are not quantifying over the whole program, but only this obligation). +This query contains two unbound variables, but it also contains the lifetime `'static`. +The trait system generally ignores all lifetimes and treats them equally, +so when canonicalizing, +we will *also* replace any [free lifetime](../appendix/background.md#free-vs-bound) with a +canonical variable (Note that `'static` is actually a _free_ lifetime variable here. +We are not considering it in the typing context of the whole program, +but only in the context of this trait reference. +Mathematically, we are not quantifying over the whole program, but only this obligation). Therefore, we get the following result: ```text @@ -62,12 +58,11 @@ Sometimes we write this differently, like so: for { ?0: Foo<'?1, ?2> } ``` -This `for<>` gives some information about each of the canonical -variables within. In this case, each `T` indicates a type variable, -so `?0` and `?2` are types; the `L` indicates a lifetime variable, so -`?1` is a lifetime. The `canonicalize` method *also* gives back a -`CanonicalVarValues` array OV with the "original values" for each -canonicalized variable: +This `for<>` gives some information about each of the canonical variables within. +In this case, each `T` indicates a type variable, +so `?0` and `?2` are types; the `L` indicates a lifetime variable, so `?1` is a lifetime. +The `canonicalize` method *also* gives back a +`CanonicalVarValues` array OV with the "original values" for each canonicalized variable: ```text [?A, 'static, ?B] @@ -79,8 +74,8 @@ We'll need this vector OV later, when we process the query response. Once we've constructed the canonical query, we can try to solve it. To do so, we will wind up creating a fresh inference context and -**instantiating** the canonical query in that context. The idea is that -we create a substitution S from the canonical form containing a fresh +**instantiating** the canonical query in that context. +The idea is that we create a substitution S from the canonical form containing a fresh inference variable (of suitable kind) for each canonical variable. So, for our example query: @@ -94,22 +89,22 @@ the substitution S might be: S = [?A, '?B, ?C] ``` -We can then replace the bound canonical variables (`?0`, etc) with -these inference variables, yielding the following fully instantiated -query: +We can then replace the bound canonical variables (`?0`, etc) with these inference variables, +yielding the following fully instantiated query: ```text ?A: Foo<'?B, ?C> ``` -Remember that substitution S though! We're going to need it later. +Remember that substitution S though! +We're going to need it later. -OK, now that we have a fresh inference context and an instantiated -query, we can go ahead and try to solve it. The trait solver itself is -explained in more detail in [another section](../solve/the-solver.md), but -suffice to say that it will compute a [certainty value][cqqr] (`Proven` or -`Ambiguous`) and have side-effects on the inference variables we've -created. For example, if there were only one impl of `Foo`, like so: +OK, now that we have a fresh inference context and an instantiated query, +we can go ahead and try to solve it. +The trait solver itself is explained in more detail in [another section](../solve/the-solver.md), +but suffice to say that it will compute a [certainty value][cqqr] (`Proven` or +`Ambiguous`) and have side-effects on the inference variables we've created. +For example, if there were only one impl of `Foo`, like so: [cqqr]: ./canonical-queries.md#query-response @@ -119,35 +114,35 @@ where X: 'a { ... } ``` -then we might wind up with a certainty value of `Proven`, as well as -creating fresh inference variables `'?D` and `?E` (to represent the +then we might wind up with a certainty value of `Proven`, +as well as creating fresh inference variables `'?D` and `?E` (to represent the parameters on the impl) and unifying as follows: - `'?B = '?D` - `?A = Vec` - `?C = ?E` -We would also accumulate the region constraint `?E: '?D`, due to the -where clause. +We would also accumulate the region constraint `?E: '?D`, due to the where clause. -In order to create our final query result, we have to "lift" these -values out of the query's inference context and into something that -can be reapplied in our original inference context. We do that by -**re-applying canonicalization**, but to the **query result**. +In order to create our final query result, +we have to "lift" these values out of the query's inference context and into something that +can be reapplied in our original inference context. +We do that by **re-applying canonicalization**, but to the **query result**. ## Canonicalizing the query result As discussed in [the parent section][cqqr], most trait queries wind up with a result that brings together a "certainty value" `certainty`, a -result substitution `var_values`, and some region constraints. To -create this, we wind up re-using the substitution S that we created -when first instantiating our query. To refresh your memory, we had a query +result substitution `var_values`, and some region constraints. +To create this, +we wind up re-using the substitution S that we created when first instantiating our query. +To refresh your memory, we had a query: ```text for { ?0: Foo<'?1, ?2> } ``` -for which we made a substutition S: +We made a substutition S for it: ```text S = [?A, '?B, ?C] @@ -160,10 +155,10 @@ If we "refresh" S with the latest results, we get: S = [Vec, '?D, ?E] ``` -These are precisely the new values for the three input variables from -our original query. Note though that they include some new variables -(like `?E`). We can make those go away by canonicalizing again! We don't -just canonicalize S, though, we canonicalize the whole query response QR: +These are precisely the new values for the three input variables from our original query. +Note though that they include some new variables (like `?E`). +We can make those go away by canonicalizing again! +We don't just canonicalize S, though, we canonicalize the whole query response QR: ```text QR = { @@ -187,20 +182,20 @@ Canonical(QR) = for { } ``` -(One subtle point: when we canonicalize the query **result**, we do not -use any special treatment for free lifetimes. Note that both -references to `'?D`, for example, were converted into the same -canonical variable (`?1`). This is in contrast to the original query, -where we canonicalized every free lifetime into a fresh canonical -variable.) +(One subtle point: when we canonicalize the query **result**, +we do not use any special treatment for free lifetimes. +Note that both references to `'?D`, for example, +were converted into the same canonical variable (`?1`). +This is in contrast to the original query, +where we canonicalized every free lifetime into a fresh canonical variable.) Now, this result must be reapplied in each context where needed. ## Processing the canonicalized query result -In the previous section we produced a canonical query result. We now have -to apply that result in our original context. If you recall, way back in the -beginning, we were trying to prove this query: +In the previous section, we produced a canonical query result. +We now have to apply that result in our original context. +If you recall, way back in the beginning, we were trying to prove this query: ```text ?A: Foo<'static, ?B> @@ -223,11 +218,11 @@ for { } ``` -We now want to apply that response to our context. Conceptually, how -we do that is to (a) instantiate each of the canonical variables in +We now want to apply that response to our context. +Conceptually, how we do that is to (a) instantiate each of the canonical variables in the result with a fresh inference variable, (b) unify the values in -the result with the original values, and then (c) record the region -constraints for later. Doing step (a) would yield a result of +the result with the original values, and then (c) record the region constraints for later. +Doing step (a) would yield this result: ```text { @@ -247,13 +242,13 @@ Step (b) would then unify: ?B with ?C ``` -And finally the region constraint of `?C: 'static` would be recorded -for later verification. +And finally, the region constraint of `?C: 'static` would be recorded for later verification. (What we *actually* do is a mildly optimized variant of that: Rather -than eagerly instantiating all of the canonical values in the result -with variables, we instead walk the vector of values, looking for -cases where the value is just a canonical variable. In our example, -`values[2]` is `?C`, so that means we can deduce that `?C := ?B` and -`'?D := 'static`. This gives us a partial set of values. Anything for -which we do not find a value, we create an inference variable.) +than eagerly instantiating all of the canonical values in the result with variables, +we instead walk the vector of values, +looking for cases where the value is just a canonical variable. +In our example, +`values[2]` is `?C`, so that means we can deduce that `?C := ?B` and `'?D := 'static`. +This gives us a partial set of values. +Anything for which we do not find a value, we create an inference variable.) diff --git a/src/doc/rustc-dev-guide/src/traits/resolution.md b/src/doc/rustc-dev-guide/src/traits/resolution.md index 394d800b5f868..5d4c8a2b758fe 100644 --- a/src/doc/rustc-dev-guide/src/traits/resolution.md +++ b/src/doc/rustc-dev-guide/src/traits/resolution.md @@ -3,15 +3,15 @@ This chapter describes the general process of _trait resolution_ and points out some non-obvious things. -**Note:** This chapter (and its subchapters) describe how the trait -solver **currently** works. However, we are in the process of -designing a new trait solver. If you'd prefer to read about *that*, +**Note:** This chapter (and its subchapters) describe how the trait solver **currently** works. +However, we are in the process of designing a new trait solver. +If you'd prefer to read about *that*, see [*this* subchapter](./chalk.md). ## Major concepts -Trait resolution is the process of pairing up an impl with each -reference to a trait. So, for example, if there is a generic function like: +Trait resolution is the process of pairing up an impl with each reference to a trait. +So, for example, if there is a generic function like: ```rust,ignore fn clone_slice(x: &[T]) -> Vec { ... } @@ -26,9 +26,9 @@ let v: Vec = clone_slice(&[1, 2, 3]) it is the job of trait resolution to figure out whether there exists an impl of (in this case) `isize : Clone`. -Note that in some cases, like generic functions, we may not be able to -find a specific impl, but we can figure out that the caller must -provide an impl. For example, consider the body of `clone_slice`: +Note that in some cases, like generic functions, we may not be able to find a specific impl, +but we can figure out that the caller must provide an impl. +For example, consider the body of `clone_slice`: ```rust,ignore fn clone_slice(x: &[T]) -> Vec { @@ -39,57 +39,54 @@ fn clone_slice(x: &[T]) -> Vec { } ``` -The line marked `(*)` is only legal if `T` (the type of `*e`) -implements the `Clone` trait. Naturally, since we don't know what `T` -is, we can't find the specific impl; but based on the bound `T:Clone`, +The line marked `(*)` is only legal if `T` (the type of `*e`) implements the `Clone` trait. +Naturally, since we don't know what `T` is, +we can't find the specific impl; but based on the bound `T:Clone`, we can say that there exists an impl which the caller must provide. -We use the term *obligation* to refer to a trait reference in need of -an impl. Basically, the trait resolution system resolves an obligation -by proving that an appropriate impl does exist. +We use the term *obligation* to refer to a trait reference in need of an impl. +Basically, +the trait resolution system resolves an obligation by proving that an appropriate impl does exist. During type checking, we do not store the results of trait selection. -We simply wish to verify that trait selection will succeed. Then -later, at codegen time, when we have all concrete types available, we -can repeat the trait selection to choose an actual implementation, which -will then be generated in the output binary. +We simply wish to verify that trait selection will succeed. +Then later, at codegen time, when we have all concrete types available, +we can repeat the trait selection to choose an actual implementation, +which will then be generated in the output binary. ## Overview Trait resolution consists of three major parts: -- **Selection**: Deciding how to resolve a specific obligation. For - example, selection might decide that a specific obligation can be +- **Selection**: Deciding how to resolve a specific obligation. + For example, selection might decide that a specific obligation can be resolved by employing an impl which matches the `Self` type, or by using a parameter bound (e.g. `T: Trait`). In the case of an impl, selecting one - obligation can create *nested obligations* because of where clauses - on the impl itself. It may also require evaluating those nested - obligations to resolve ambiguities. - -- **Fulfillment**: The fulfillment code is what tracks that obligations - are completely fulfilled. Basically it is a worklist of obligations - to be selected: once selection is successful, the obligation is - removed from the worklist and any nested obligations are enqueued. + obligation can create *nested obligations* because of where clauses on the impl itself. + It may also require evaluating those nested obligations to resolve ambiguities. + +- **Fulfillment**: The fulfillment code is what tracks that obligations are completely fulfilled. + Basically, it is a worklist of obligations to be selected: once selection is successful, + the obligation is removed from the worklist, and any nested obligations are enqueued. Fulfillment constrains inference variables. -- **Evaluation**: Checks whether obligations holds without constraining - any inference variables. Used by selection. +- **Evaluation**: Checks whether obligations holds without constraining any inference variables. + Used by selection. ## Selection -Selection is the process of deciding whether an obligation can be -resolved and, if so, how it is to be resolved (via impl, where clause, etc). -The main interface is the `select()` function, which takes an obligation -and returns a `SelectionResult`. There are three possible outcomes: +Selection is the process of deciding whether an obligation can be resolved and, +if so, how it is to be resolved (via impl, where clause, etc). +The main interface is the `select()` function, +which takes an obligation and returns a `SelectionResult`. +There are three possible outcomes: -- `Ok(Some(selection))` – yes, the obligation can be resolved, and - `selection` indicates how. If the impl was resolved via an impl, - then `selection` may also indicate nested obligations that are required - by the impl. +- `Ok(Some(selection))` – yes, the obligation can be resolved, and `selection` indicates how. + If the impl was resolved via an impl, + then `selection` may also indicate nested obligations that are required by the impl. -- `Ok(None)` – we are not yet sure whether the obligation can be - resolved or not. This happens most commonly when the obligation - contains unbound type variables. +- `Ok(None)` – we are not yet sure whether the obligation can be resolved or not. + This happens most commonly when the obligation contains unbound type variables. - `Err(err)` – the obligation definitely cannot be resolved due to a type error or because there are no impls that could possibly apply. @@ -97,42 +94,42 @@ and returns a `SelectionResult`. There are three possible outcomes: The basic algorithm for selection is broken into two big phases: candidate assembly and confirmation. -Note that because of how lifetime inference works, it is not possible to -give back immediate feedback as to whether a unification or subtype -relationship between lifetimes holds or not. Therefore, lifetime -matching is *not* considered during selection. This is reflected in -the fact that subregion assignment is infallible. This may yield -lifetime constraints that will later be found to be in error (in -contrast, the non-lifetime-constraints have already been checked -during selection and can never cause an error, though naturally they -may lead to other errors downstream). +Note that because of how lifetime inference works, +it is not possible to give back immediate feedback as to whether a unification or subtype +relationship between lifetimes holds or not. +Therefore, lifetime matching is *not* considered during selection. +This is reflected in the fact that subregion assignment is infallible. +This may yield lifetime constraints that will later be found to be in error (in contrast, +the non-lifetime-constraints have already been checked +during selection and can never cause an error, +though naturally they may lead to other errors downstream). ### Candidate assembly **TODO**: Talk about _why_ we have different candidates, and why it needs to happen in a probe. -Searches for impls/where-clauses/etc that might -possibly be used to satisfy the obligation. Each of those is called -a candidate. To avoid ambiguity, we want to find exactly one -candidate that is definitively applicable. In some cases, we may not -know whether an impl/where-clause applies or not – this occurs when +Searches for impls/where-clauses/etc that might possibly be used to satisfy the obligation. +Each of those is called a candidate. +To avoid ambiguity, we want to find exactly one candidate that is definitively applicable. +In some cases, we may not know whether an impl/where-clause applies or not – this occurs when the obligation contains unbound inference variables. The subroutines that decide whether a particular impl/where-clause/etc applies to a particular obligation are collectively referred to as the process of _matching_. For `impl` candidates , this amounts to unifying the impl header (the `Self` type and the trait arguments) -while ignoring nested obligations. If matching succeeds then we add it -to a set of candidates. There are other rules when assembling candidates for -built-in traits such as `Copy`, `Sized`, and `CoerceUnsized`. +while ignoring nested obligations. +If matching succeeds then we add it to a set of candidates. +There are other rules when assembling candidates for built-in traits such as `Copy`, +`Sized`, and `CoerceUnsized`. -Once this first pass is done, we can examine the set of candidates. If -it is a singleton set, then we are done: this is the only impl in -scope that could possibly apply. Otherwise, we can **winnow** down the set -of candidates by using where clauses and other conditions. Winnowing uses -`evaluate_candidate` to check whether the nested obligations may apply. -If this still leaves more than 1 candidate, we use ` fn candidate_should_be_dropped_in_favor_of` -to prefer some candidates over others. +Once this first pass is done, we can examine the set of candidates. +If it is a singleton set, +then we are done: this is the only impl in scope that could possibly apply. +Otherwise, we can **winnow** down the set of candidates by using where clauses and other conditions. +Winnowing uses `evaluate_candidate` to check whether the nested obligations may apply. +If this still leaves more than 1 candidate, +we use ` fn candidate_should_be_dropped_in_favor_of` to prefer some candidates over others. If this reduced set yields a single, unambiguous entry, we're good to go, @@ -140,8 +137,8 @@ otherwise the result is considered ambiguous. #### Winnowing: Resolving ambiguities -But what happens if there are multiple impls where all the types -unify? Consider this example: +But what happens if there are multiple impls where all the types unify? +Consider this example: ```rust,ignore trait Get { @@ -161,24 +158,25 @@ impl Get for Box { } ``` -What happens when we invoke `get(&Box::new(1_u16))`, for example? In this -case, the `Self` type is `Box` – that unifies with both impls, -because the first applies to all types `T`, and the second to all -`Box`. In order for this to be unambiguous, the compiler does a *winnowing* -pass that considers `where` clauses -and attempts to remove candidates. In this case, the first impl only -applies if `Box : Copy`, which doesn't hold. After winnowing, +What happens when we invoke `get(&Box::new(1_u16))`, for example? +In this case, the `Self` type is `Box` – that unifies with both impls, +because the first applies to all types `T`, and the second to all `Box`. +In order for this to be unambiguous, +the compiler does a *winnowing* pass that considers `where` clauses +and attempts to remove candidates. +In this case, the first impl only applies if `Box : Copy`, which doesn't hold. +After winnowing, then, we are left with just one candidate, so we can proceed. #### `where` clauses -Besides an impl, the other major way to resolve an obligation is via a -where clause. The selection process is always given a [parameter -environment] which contains a list of where clauses, which are -basically obligations that we can assume are satisfiable. We will iterate -over that list and check whether our current obligation can be found -in that list. If so, it is considered satisfied. More precisely, we -want to check whether there is a where-clause obligation that is for +Besides an impl, the other major way to resolve an obligation is via a where clause. +The selection process is always given a [parameter +environment] which contains a list of where clauses, +which are basically obligations that we can assume are satisfiable. +We will iterate over that list and check whether our current obligation can be found in that list. +If so, it is considered satisfied. +More precisely, we want to check whether there is a where-clause obligation that is for the same trait (or some subtrait) and which can match against the obligation. [parameter environment]: ../typing-parameter-envs.md @@ -201,12 +199,13 @@ fn foo(x: X) { } ``` -In the body of `foo`, clearly we can use methods of `A1`, `A2`, or `B` -on variable `x`. The line marked `(*)` will incur an obligation `X: A1`, -while the line marked `(#)` will incur an obligation `X: B`. Meanwhile, +In the body of `foo`, clearly we can use methods of `A1`, `A2`, or `B` on variable `x`. +The line marked `(*)` will incur an obligation `X: A1`, +while the line marked `(#)` will incur an obligation `X: B`. +Meanwhile, the parameter environment will contain two where-clauses: `X : A2` and `X : B`. -For each obligation, then, we search this list of where-clauses. The -obligation `X: B` trivially matches against the where-clause `X: B`. +For each obligation, then, we search this list of where-clauses. +The obligation `X: B` trivially matches against the where-clause `X: B`. To resolve an obligation `X:A1`, we would note that `X:A2` implies that `X:A1`. ### Confirmation @@ -214,8 +213,7 @@ To resolve an obligation `X:A1`, we would note that `X:A2` implies that `X:A1`. _Confirmation_ unifies the output type parameters of the trait with the values found in the obligation, possibly yielding a type error. -Suppose we have the following variation of the `Convert` example in the -previous section: +Suppose we have the following variation of the `Convert` example in the previous section: ```rust,ignore trait Convert { @@ -230,24 +228,25 @@ let y: char = x.convert(); // NOTE: `y: char` now! ``` Confirmation is where an error would be reported because the impl specified -that `Target` would be `usize`, but the obligation reported `char`. Hence the -result of selection would be an error. +that `Target` would be `usize`, but the obligation reported `char`. +Hence the result of selection would be an error. -Note that the candidate impl is chosen based on the `Self` type, but -confirmation is done based on (in this case) the `Target` type parameter. +Note that the candidate impl is chosen based on the `Self` type, +but confirmation is done based on (in this case) the `Target` type parameter. ### Selection during codegen -As mentioned above, during type checking, we do not store the results of trait -selection. At codegen time, we repeat the trait selection to choose a particular -impl for each method call. This is done using `fn codegen_select_candidate`. +As mentioned above, during type checking, we do not store the results of trait selection. +At codegen time, we repeat the trait selection to choose a particular impl for each method call. +This is done using `fn codegen_select_candidate`. In this second selection, we do not consider any where-clauses to be in scope because we know that each resolution will resolve to a particular impl. -One interesting twist has to do with nested obligations. In general, in codegen, +One interesting twist has to do with nested obligations. +In general, in codegen, we only need to figure out which candidate applies, and we do not care about nested obligations, -as these are already assumed to be true. Nonetheless, we *do* currently fulfill all of them. +as these are already assumed to be true. +Nonetheless, we *do* currently fulfill all of them. That is because it can sometimes inform the results of type inference. That is, we do not have the full substitutions in terms of the type variables -of the impl available to us, so we must run trait selection to figure -everything out. +of the impl available to us, so we must run trait selection to figure everything out. diff --git a/src/doc/rustc-dev-guide/src/ty-module/early-binder.md b/src/doc/rustc-dev-guide/src/ty-module/early-binder.md index d83d73c955bfc..d1c311827cc1b 100644 --- a/src/doc/rustc-dev-guide/src/ty-module/early-binder.md +++ b/src/doc/rustc-dev-guide/src/ty-module/early-binder.md @@ -1,6 +1,9 @@ # `EarlyBinder` and instantiating parameters -Given an item that introduces a generic parameter `T`, whenever we refer to types inside of `foo` (i.e. the return type or argument types) from outside of `foo` we must take care to handle the generic parameters defined on `foo`. As an example: +Given an item that introduces a generic parameter `T`, +whenever we refer to types inside of `foo` (i.e. the return type or argument types) from outside of `foo`, +we must take care to handle the generic parameters defined on `foo`. +As an example: ```rust,ignore fn foo(a: T, _b: U) -> T { a } @@ -10,11 +13,24 @@ fn main() { } ``` -When type checking `main` we cannot just naively look at the return type of `foo` and assign the type `T` to the variable `c`, The function `main` does not define any generic parameters, `T` is completely meaningless in this context. More generally whenever an item introduces (binds) generic parameters, when accessing types inside the item from outside, the generic parameters must be instantiated with values from the outer item. - -In rustc we track this via the [`EarlyBinder`] type, the return type of `foo` is represented as an `EarlyBinder` with the only way to access `Ty` being to provide arguments for any generic parameters `Ty` might be using. This is implemented via the [`EarlyBinder::instantiate`] method which discharges the binder returning the inner value with all the generic parameters replaced by the provided arguments. - -To go back to our example, when type checking `main` the return type of `foo` would be represented as `EarlyBinder(T/#0)`. Then, because we called the function with `i32, u128` for the generic arguments, we would call `EarlyBinder::instantiate` on the return type with `[i32, u128]` for the args. This would result in an instantiated return type of `i32` that we can use as the type of the local `c`. +When type checking `main`, +we cannot just naively look at the return type of `foo` and assign the type `T` to the variable `c`. +The function `main` does not define any generic parameters, +and `T` is completely meaningless in this context. +More generally, whenever an item introduces (binds) generic parameters, +when accessing types inside the item from outside, +the generic parameters must be instantiated with values from the outer item. + +In rustc we track this via the [`EarlyBinder`] type, +the return type of `foo` is represented as an `EarlyBinder` with the only way to access `Ty` being to provide arguments for any generic parameters `Ty` might be using. +This is implemented via the [`EarlyBinder::instantiate`] method which discharges the binder returning the inner value with all the generic parameters replaced by the provided arguments. + +To go back to our example, +when type checking `main`, +the return type of `foo` would be represented as `EarlyBinder(T/#0)`. +Then, because we called the function with `i32, u128` for the generic arguments, +we would call `EarlyBinder::instantiate` on the return type with `[i32, u128]` for the args. +This would result in an instantiated return type of `i32` that we can use as the type of the local `c`. Here are some more examples: @@ -35,7 +51,7 @@ struct Foo { .. } -fn bar(foo: Foo) { +fn bar(foo: Foo) { // the type of `foo`'s `x` field before instantiating it would be: // `EarlyBinder(Vec)` // we then instantiate the binder with `[u32, f32]` as those are the @@ -45,12 +61,15 @@ fn bar(foo: Foo) { } ``` -In the compiler the `instantiate` call for this is done in [`FieldDef::ty`] ([src][field_def_ty_src]), at some point during type checking `bar` we will wind up calling `FieldDef::ty(x, &[u32, f32])` in order to obtain the type of `foo.x`. +In the compiler, the `instantiate` call for this is done in [`FieldDef::ty`] ([src][field_def_ty_src]), +and at some point during type checking `bar`, +we will wind up calling `FieldDef::ty(x, &[u32, f32])` in order to obtain the type of `foo.x`. -**Note on indices:** It is a bug if the index of a `Param` does not match what the `EarlyBinder` binds. For -example, if the index is out of bounds or the index of a lifetime corresponds to a type parameter. +**Note on indices:** It is a bug if the index of a `Param` does not match what the `EarlyBinder` binds. +For example, +if the index is out of bounds or the index of a lifetime corresponds to a type parameter. These sorts of errors are caught earlier in the compiler during name resolution where we disallow references -to generics parameters introduced by items that should not be nameable by the inner item. +to generics parameters introduced by items that should not be nameable by the inner item. [`FieldDef::ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.FieldDef.html#method.ty [field_def_ty_src]: https://github.com/rust-lang/rust/blob/44d679b9021f03a79133021b94e6d23e9b55b3ab/compiler/rustc_middle/src/ty/mod.rs#L1421-L1426 @@ -59,7 +78,9 @@ to generics parameters introduced by items that should not be nameable by the in --- -As mentioned previously when _outside_ of an item, it is important to instantiate the `EarlyBinder` with generic arguments before accessing the value inside, but the setup for when we are conceptually inside of the binder already is a bit different. +As mentioned previously when _outside_ of an item, +it is important to instantiate the `EarlyBinder` with generic arguments before accessing the value inside, +but the setup for when we are conceptually inside of the binder already is a bit different. For example: ```rust @@ -68,9 +89,17 @@ impl Trait for Vec { } ``` -When constructing a `Ty` to represent the `b` parameter's type we need to get the type of `Self` on the impl that we are inside. This can be acquired by calling the [`type_of`] query with the `impl`'s `DefId`, however, this will return a `EarlyBinder` as the impl block binds generic parameters that may have to be discharged if we are outside of the impl. - -The `EarlyBinder` type provides an [`instantiate_identity`] function for discharging the binder when you are "already inside of it". This is effectively a more performant version of writing `EarlyBinder::instantiate(GenericArgs::identity_for_item(..))`. Conceptually this discharges the binder by instantiating it with placeholders in the root universe (we will talk about what this means in the next few chapters). In practice though it simply returns the inner value with no modification taking place. +When constructing a `Ty` to represent the `b` parameter's type, +we need to get the type of `Self` on the impl that we are inside of. +This can be acquired by calling the [`type_of`] query with the `impl`'s `DefId`. +However, +this will return a `EarlyBinder` as the impl block binds generic parameters that may have to be discharged if we are outside of the impl. + +The `EarlyBinder` type provides an [`instantiate_identity`] function for discharging the binder when you are "already inside of it". +This is effectively a more performant version of writing `EarlyBinder::instantiate(GenericArgs::identity_for_item(..))`. +Conceptually, this discharges the binder by instantiating it with placeholders in the root universe +(and we will talk about what this means in the next few chapters). +In practice, though, it simply returns the inner value with no modification taking place. [`type_of`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.type_of [`instantiate_identity`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.EarlyBinder.html#method.instantiate_identity diff --git a/src/doc/rustc-dev-guide/src/type-inference.md b/src/doc/rustc-dev-guide/src/type-inference.md index 196fcdd9190c3..31f2f4b198e63 100644 --- a/src/doc/rustc-dev-guide/src/type-inference.md +++ b/src/doc/rustc-dev-guide/src/type-inference.md @@ -1,7 +1,6 @@ # Type inference -Type inference is the process of automatic detection of the type of an -expression. +Type inference is the process of automatic detection of the type of an expression. It is what allows Rust to work with fewer or no type annotations, making things easier for users: @@ -13,55 +12,50 @@ fn main() { } ``` -Here, the type of `things` is *inferred* to be `Vec<&str>` because of the value -we push into `things`. +Here, +the type of `things` is *inferred* to be `Vec<&str>` because of the value we push into `things`. -The type inference is based on the standard Hindley-Milner (HM) type inference -algorithm, but extended in various ways to accommodate subtyping, region -inference, and higher-ranked types. +The type inference is based on the standard Hindley-Milner (HM) type inference algorithm, +but extended in various ways to accommodate subtyping, region inference, and higher-ranked types. ## A note on terminology -We use the notation `?T` to refer to inference variables, also called -existential variables. +We use the notation `?T` to refer to inference variables, also called existential variables. -We use the terms "region" and "lifetime" interchangeably. Both refer to -the `'a` in `&'a T`. +We use the terms "region" and "lifetime" interchangeably. +Both refer to the `'a` in `&'a T`. -The term "bound region" refers to a region that is bound in a function -signature, such as the `'a` in `for<'a> fn(&'a u32)`. A region is -"free" if it is not bound. +The term "bound region" refers to a region that is bound in a function signature, +such as the `'a` in `for<'a> fn(&'a u32)`. +A region is "free" if it is not bound. ## Creating an inference context -You create an inference context by doing something like -the following: +You create an inference context by doing something like the following: ```rust,ignore let infcx = tcx.infer_ctxt().build(); // Use the inference context `infcx` here. ``` -`infcx` has the type `InferCtxt<'tcx>`, the same `'tcx` lifetime as on -the `tcx` it was built from. +`infcx` has the type `InferCtxt<'tcx>`, the same `'tcx` lifetime as on the `tcx` it was built from. -The `tcx.infer_ctxt` method actually returns a builder, which means -there are some kinds of configuration you can do before the `infcx` is -created. See `InferCtxtBuilder` for more information. +The `tcx.infer_ctxt` method actually returns a builder, +which means there are some kinds of configuration you can do before the `infcx` is created. +See `InferCtxtBuilder` for more information. ## Inference variables -The main purpose of the inference context is to house a bunch of -**inference variables** – these represent types or regions whose precise -value is not yet known, but will be uncovered as we perform type-checking. +The main purpose of the inference context is to house a bunch of **inference variables**. +These represent types or regions whose precise value is not yet known, +but will be uncovered as we perform type-checking. -If you're familiar with the basic ideas of unification from H-M type -systems, or logic languages like Prolog, this is the same concept. If -you're not, you might want to read a tutorial on how H-M type -inference works, or perhaps this blog post on -[unification in the Chalk project]. +If you're familiar with the basic ideas of unification from H-M type systems, +or logic languages like Prolog, this is the same concept. +If you're not, you might want to read a tutorial on how H-M type inference works, +or perhaps this blog post on [unification in the Chalk project]. [Unification in the Chalk project]: http://smallcultfollowing.com/babysteps/blog/2017/03/25/unification-in-chalk-part-1/ @@ -69,102 +63,97 @@ All told, the inference context stores five kinds of inference variables (as of March 2023): - Type variables, which come in three varieties: - - General type variables (the most common). These can be unified with any - type. + - General type variables (the most common). + These can be unified with any type. - Integral type variables, which can only be unified with an integral type, and arise from an integer literal expression like `22`. - - Float type variables, which can only be unified with a float type, and - arise from a float literal expression like `22.0`. + - Float type variables, which can only be unified with a float type, + and arise from a float literal expression like `22.0`. - Region variables, which represent lifetimes, and arise all over the place. - Const variables, which represent constants. -All the type variables work in much the same way: you can create a new -type variable, and what you get is `Ty<'tcx>` representing an -unresolved type `?T`. Then later you can apply the various operations -that the inferencer supports, such as equality or subtyping, and it -will possibly **instantiate** (or **bind**) that `?T` to a specific -value as a result. +All the type variables work in much the same way: you can create a new type variable, +and what you get is `Ty<'tcx>` representing an unresolved type `?T`. +You can then later apply the various operations that the inferencer supports, +such as equality or subtyping, +and it will possibly **instantiate** (or **bind**) that `?T` to a specific value as a result. -The region variables work somewhat differently, and are described -below in a separate section. +The region variables work somewhat differently, and are described below in a separate section. ## Enforcing equality / subtyping -The most basic operations you can perform in the type inferencer is -**equality**, which forces two types `T` and `U` to be the same. The -recommended way to add an equality constraint is to use the `at` -method, roughly like so: +The most basic operations you can perform in the type inferencer is **equality**, +which forces two types `T` and `U` to be the same. +The recommended way to add an equality constraint is to use the `at` method, roughly like so: ```rust,ignore infcx.at(...).eq(t, u); ``` The first `at()` call provides a bit of context, i.e. why you are -doing this unification, and in what environment, and the `eq` method -performs the actual equality constraint. - -When you equate things, you force them to be precisely equal. Equating -returns an `InferResult` – if it returns `Err(err)`, then equating -failed, and the enclosing `TypeError` will tell you what went wrong. - -The success case is perhaps more interesting. The "primary" return -type of `eq` is `()` – that is, when it succeeds, it doesn't return a -value of any particular interest. Rather, it is executed for its -side-effects of constraining type variables and so forth. However, the -actual return type is not `()`, but rather `InferOk<()>`. The -`InferOk` type is used to carry extra trait obligations – your job is -to ensure that these are fulfilled (typically by enrolling them in a -fulfillment context). See the [trait chapter] for more background on that. +doing this unification, and in what environment, +and the `eq` method performs the actual equality constraint. + +When you equate things, you force them to be precisely equal. +Equating returns an `InferResult` – if it returns `Err(err)`, then equating failed, +and the enclosing `TypeError` will tell you what went wrong. + +The success case is perhaps more interesting. +The "primary" return type of `eq` is `()` – that is, when it succeeds, +it doesn't return a value of any particular interest. +Rather, it is executed for its side-effects of constraining type variables and so forth. +However, the actual return type is not `()`, but rather `InferOk<()>`. +The `InferOk` type is used to carry extra trait obligations – your job is +to ensure that these are fulfilled (typically by enrolling them in a fulfillment context). +See the [trait chapter] for more background on that. [trait chapter]: traits/resolution.md -You can similarly enforce subtyping through `infcx.at(..).sub(..)`. The same -basic concepts as above apply. +You can similarly enforce subtyping through `infcx.at(..).sub(..)`. +The same basic concepts as above apply. ## "Trying" equality -Sometimes you would like to know if it is *possible* to equate two -types without error. You can test that with `infcx.can_eq` (or -`infcx.can_sub` for subtyping). If this returns `Ok`, then equality -is possible – but in all cases, any side-effects are reversed. +Sometimes you would like to know if it is *possible* to equate two types without error. +You can test that with `infcx.can_eq` (or `infcx.can_sub` for subtyping). +If this returns `Ok`, then equality is possible – but in all cases, any side-effects are reversed. -Be aware, though, that the success or failure of these methods is always -**modulo regions**. That is, two types `&'a u32` and `&'b u32` will -return `Ok` for `can_eq`, even if `'a != 'b`. This falls out from the -"two-phase" nature of how we solve region constraints. +Be aware, though, that the success or failure of these methods is always **modulo regions**. +That is, two types `&'a u32` and `&'b u32` will return `Ok` for `can_eq`, even if `'a != 'b`. +This falls out from the "two-phase" nature of how we solve region constraints. ## Snapshots -As described in the previous section on `can_eq`, often it is useful -to be able to do a series of operations and then roll back their -side-effects. This is done for various reasons: one of them is to be -able to backtrack, trying out multiple possibilities before settling -on which path to take. Another is in order to ensure that a series of -smaller changes take place atomically or not at all. +As described in the previous section on `can_eq`, +often it is useful to be able to do a series of operations and then roll back their side-effects. +This is done for various reasons: one of them is to be able to backtrack, +trying out multiple possibilities before settling on which path to take. +Another is in order to ensure that a series of smaller changes take place atomically or not at all. To allow for this, the inference context supports a `snapshot` method. -When you call it, it will start recording changes that occur from the -operations you perform. When you are done, you can either invoke -`rollback_to`, which will undo those changes, or else `confirm`, which -will make them permanent. Snapshots can be nested as long as you follow -a stack-like discipline. - -Rather than use snapshots directly, it is often helpful to use the -methods like `commit_if_ok` or `probe` that encapsulate higher-level +When you call it, it will start recording changes that occur from the operations you perform. +When you are done, you can either invoke `rollback_to`, +which will undo those changes, or else `confirm`, which will make them permanent. +Snapshots can be nested as long as you follow a stack-like discipline. + +Rather than use snapshots directly, +it is often helpful to use the methods like `commit_if_ok` or `probe` that encapsulate higher-level patterns. ## Subtyping obligations -One thing worth discussing is subtyping obligations. When you force -two types to be a subtype, like `?T <: i32`, we can often convert those -into equality constraints. This follows from Rust's rather limited notion -of subtyping: so, in the above case, `?T <: i32` is equivalent to `?T = i32`. +One thing worth discussing is subtyping obligations. +When you force two types to be a subtype, like `?T <: i32`, +we can often convert those into equality constraints. +This follows from Rust's rather limited notion of subtyping: so, +in the above case, `?T <: i32` is equivalent to `?T = i32`. -However, in some cases we have to be more careful. For example, when -regions are involved. So if you have `?T <: &'a i32`, what we would do -is to first "generalize" `&'a i32` into a type with a region variable: -`&'?b i32`, and then unify `?T` with that (`?T = &'?b i32`). We then -relate this new variable with the original bound: +However, in some cases we have to be more careful. +An example is when regions are involved. +So, if you have `?T <: &'a i32`, +what we would do is to first "generalize" `&'a i32` into a type with a region variable: `&'?b i32`, +and then unify `?T` with that (`?T = &'?b i32`). +We then relate this new variable with the original bound: ```text &'?b i32 <: &'a i32 @@ -173,73 +162,70 @@ relate this new variable with the original bound: This will result in a region constraint (see below) of `'?b: 'a`. One final interesting case is relating two unbound type variables, -like `?T <: ?U`. In that case, we can't make progress, so we enqueue -an obligation `Subtype(?T, ?U)` and return it via the `InferOk` -mechanism. You'll have to try again when more details about `?T` or -`?U` are known. +like `?T <: ?U`. +In that case, we can't make progress, so we enqueue an obligation `Subtype(?T, +?U)` and return it via the `InferOk` mechanism. +You'll have to try again when more details about `?T` or `?U` are known. ## Region constraints -Regions are inferenced somewhat differently from types. Rather than -eagerly unifying things, we simply collect constraints as we go, but -make (almost) no attempt to solve regions. These constraints have the -form of an "outlives" constraint: +Regions are inferenced somewhat differently from types. +Rather than eagerly unifying things, we simply collect constraints as we go, +but make (almost) no attempt to solve regions. +These constraints have the form of an "outlives" constraint: ```text 'a: 'b ``` -Actually the code tends to view them as a subregion relation, but it's the same -idea: +Actually the code tends to view them as a subregion relation, but it's the same idea: ```text 'b <= 'a ``` -(There are various other kinds of constraints, such as "verifys"; see -the [`region_constraints`] module for details.) +(There are various other kinds of constraints, +such as "verifys"; see the [`region_constraints`] module for details.) -There is one case where we do some amount of eager unification. If you have an -equality constraint between two regions +There is one case where we do some amount of eager unification. +If you have an equality constraint between two regions ```text 'a = 'b ``` -we will record that fact in a unification table. You can then use -[`opportunistic_resolve_var`] to convert `'b` to `'a` (or vice -versa). This is sometimes needed to ensure termination of fixed-point -algorithms. +we will record that fact in a unification table. +You can then use [`opportunistic_resolve_var`] to convert `'b` to `'a` (or vice versa). +This is sometimes needed to ensure termination of fixed-point algorithms. [`region_constraints`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/region_constraints/index.html [`opportunistic_resolve_var`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/region_constraints/struct.RegionConstraintCollector.html#method.opportunistic_resolve_var ## Solving region constraints -Region constraints are only solved at the very end of -typechecking, once all other constraints are known and -all other obligations have been proven. There are two -ways to solve region constraints right now: lexical and -non-lexical. Eventually there will only be one. +Region constraints are only solved at the very end of typechecking, +once all other constraints are known and all other obligations have been proven. +There are two ways to solve region constraints right now: lexical and non-lexical. +Eventually there will only be one. An exception here is the leak-check which is used during trait solving -and relies on region constraints containing higher-ranked regions. Region +and relies on region constraints containing higher-ranked regions. +Region constraints in the root universe (i.e. not arising from a `for<'a>`) must -not influence the trait system, as these regions are all erased during -codegen. - -To solve **lexical** region constraints, you invoke -[`resolve_regions_and_report_errors`]. This "closes" the region -constraint process and invokes the [`lexical_region_resolve`] code. Once -this is done, any further attempt to equate or create a subtyping -relationship will yield an ICE. - -The NLL solver (actually, the MIR type-checker) does things slightly -differently. It uses canonical queries for trait solving which use -[`take_and_reset_region_constraints`] at the end. This extracts all of the -outlives constraints added during the canonical query. This is required -as the NLL solver must not only know *what* regions outlive each other, -but also *where*. Finally, the NLL solver invokes [`get_region_var_infos`], +not influence the trait system, as these regions are all erased during codegen. + +To solve **lexical** region constraints, you invoke [`resolve_regions_and_report_errors`]. +This "closes" the region constraint process and invokes the [`lexical_region_resolve`] code. +Once this is done, +any further attempt to equate or create a subtyping relationship will yield an ICE. + +The NLL solver (actually, the MIR type-checker) does things slightly differently. +It uses canonical queries for trait solving which use +[`take_and_reset_region_constraints`] at the end. +This extracts all of the outlives constraints added during the canonical query. +This is required as the NLL solver must not only know *what* regions outlive each other, +but also *where*. +Finally, the NLL solver invokes [`get_region_var_infos`], providing all region variables to the solver. [`resolve_regions_and_report_errors`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/struct.ObligationCtxt.html#method.resolve_regions_and_report_errors @@ -249,8 +235,8 @@ providing all region variables to the solver. ## Lexical region resolution -Lexical region resolution is done by initially assigning each region -variable to an empty value. We then process each outlives constraint -repeatedly, growing region variables until a fixed-point is reached. +Lexical region resolution is done by initially assigning each region variable to an empty value. +We then process each outlives constraint repeatedly, +growing region variables until a fixed-point is reached. Region variables can be grown using a least-upper-bound relation on the region lattice in a fairly straightforward fashion. diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index ab317ba1048a4..9bd795b759ec4 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -75,8 +75,21 @@ on them: `#[doc(notable_trait)]`. This means that you can apply this attribute to your own trait to include it in the "Notable traits" dialog in documentation. In addition to the "Notable traits" dialog, every type that implements a -`#[doc(notable_trait)]` trait renders a colored badge for that trait at the top +`#[doc(notable_trait)]` trait renders a badge for that trait at the top of its page, making the relationship easy to spot when browsing the type. +To set a color for the badge, write `#[doc(notable_trait(color="red"))]` or +one of the other colors in the list (from the [ANSI 3 bit terminal palette][]): + +[ANSI 3 bit terminal palette]: https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit + +- grey +- red +- green +- yellow +- blue +- magenta +- cyan +- transparent The `#[doc(notable_trait)]` attribute currently requires the `#![feature(doc_notable_trait)]` feature gate. For more information, see [its chapter in the Unstable Book][unstable-notable_trait] diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c34b33d9c92f5..56d651aff2731 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1354,7 +1354,7 @@ impl Trait { tcx.trait_is_auto(self.def_id) } pub(crate) fn is_notable_trait(&self, tcx: TyCtxt<'_>) -> bool { - tcx.is_doc_notable_trait(self.def_id) + tcx.doc_notable_trait(self.def_id).is_some() } pub(crate) fn safety(&self, tcx: TyCtxt<'_>) -> hir::Safety { tcx.trait_def(self.def_id).safety diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index c2d8cc9ebae3e..bee0f61c3a69e 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -52,8 +52,8 @@ use indexmap::IndexMap; use itertools::Either; use rustc_ast::join_path_syms; use rustc_attr_ir::{ - AttributeKind, ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, - StableSince, find_attr, + AttributeKind, ConstStability, DeprecatedSince, Deprecation, NotableTraitColor, RustcVersion, + StabilityLevel, StableSince, find_attr, }; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::Mutability; @@ -1729,13 +1729,60 @@ fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option) -> (String, String) { +fn notable_traits_decl_write_html<'cx>( + notable_impls: impl Iterator, + f: &mut fmt::Formatter<'_>, + cx: &Context<'_>, +) -> Result<(), fmt::Error> { + let mut notable_impls = notable_impls.peekable(); + let code_tags_closing = if let Some((impl_, _)) = notable_impls.peek() { + write!( + f, + "

Notable traits for {}

\ +
",
+            print_type(&impl_.for_, cx),
+        )?;
+        "
" + } else { + "" + }; + + for (impl_, trait_did) in notable_impls { + write!(f, "
{}
", print_impl(impl_, false, cx))?; + for it in &impl_.items { + let clean::AssocTypeItem(tydef, ..) = &it.kind else { + continue; + }; + + let empty_set = FxIndexSet::default(); + let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set); + + write!( + f, + "
{};
", + assoc_type( + it, + &tydef.generics, + &[], // intentionally leaving out bounds + Some(&tydef.type_), + src_link, + 0, + cx, + ) + )?; + } + } + + f.write_str(code_tags_closing) +} + +fn notable_traits_decl_for_type(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this"); let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this"); let out = fmt::from_fn(|f| { - let mut notable_impls = impls + let notable_impls = impls .iter() .map(|impl_| impl_.inner_impl()) .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive) @@ -1753,60 +1800,57 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { } else { None } - }) - .peekable(); - - let has_notable_impl = if let Some((impl_, _)) = notable_impls.peek() { - write!( - f, - "

Notable traits for {}

\ -
",
-                print_type(&impl_.for_, cx),
-            )?;
-            true
-        } else {
-            false
-        };
-
-        for (impl_, trait_did) in notable_impls {
-            write!(f, "
{}
", print_impl(impl_, false, cx))?; - for it in &impl_.items { - let clean::AssocTypeItem(tydef, ..) = &it.kind else { - continue; - }; - - let empty_set = FxIndexSet::default(); - let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set); + }); - write!( - f, - "
{};
", - assoc_type( - it, - &tydef.generics, - &[], // intentionally leaving out bounds - Some(&tydef.type_), - src_link, - 0, - cx, - ) - )?; - } - } + notable_traits_decl_write_html(notable_impls, f, cx) + }) + .to_string(); - if !has_notable_impl { - f.write_str("
")?; - } + (format!("{:#}", print_type(ty, cx)), out) +} - Ok(()) +fn notable_traits_decl_for_item(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> { + let out = fmt::from_fn(|f| { + let notable_impls = if let Some(def_id) = item.def_id() + && !is_notable_trait_passthrough(def_id, cx) + && let Some(impls) = cx.cache().impls.get(&def_id) + { + impls + .iter() + .map(Impl::inner_impl) + .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive) + .filter_map(|impl_| { + if let Some(trait_) = &impl_.trait_ + && let trait_did = trait_.def_id() + && let Some(trait_) = cx.cache().traits.get(&trait_did) + && trait_.is_notable_trait(cx.tcx()) + { + Some((impl_, trait_did)) + } else { + None + } + }) + } else { + return Ok(()); + }; + notable_traits_decl_write_html(notable_impls, f, cx) }) .to_string(); - (format!("{:#}", print_type(ty, cx)), out) + Some((item.name?.to_string(), out)) } -fn notable_traits_json<'a>(tys: impl Iterator, cx: &Context<'_>) -> String { - let mut mp = tys.map(|ty| notable_traits_decl(ty, cx)).collect::>(); +fn notable_traits_json<'a>( + tys: impl Iterator, + item: &clean::Item, + cx: &Context<'_>, +) -> String { + let mut mp = tys.map(|ty| notable_traits_decl_for_type(ty, cx)).collect::>(); + if let Some((item_name, item_decl)) = notable_traits_decl_for_item(item, cx) + && !item_decl.is_empty() + { + mp.insert(item_name, item_decl); + } mp.sort_unstable_keys(); serde_json::to_string(&mp).expect("serialize (string, string) -> json object cannot fail") } @@ -1816,6 +1860,8 @@ pub(crate) struct NotableTraitBadge { pub full_path: String, /// Relative URL to the trait page, or `None` if it cannot be linked. pub href: Option, + /// One of the color names. + pub color: NotableTraitColor, } /// Returns all `#[doc(notable_trait)]` traits that `item` implements, to be @@ -1833,15 +1879,15 @@ pub(crate) fn notable_trait_badges(item: &clean::Item, cx: &Context<'_>) -> Vec< .filter_map(|impl_| { if let Some(trait_) = &impl_.trait_ && let trait_did = trait_.def_id() - && let Some(trait_) = cx.cache().traits.get(&trait_did) - && trait_.is_notable_trait(tcx) + && cx.cache().traits.contains_key(&trait_did) + && let Some(&color) = tcx.doc_notable_trait(trait_did) { let name = tcx.item_name(trait_did).to_string(); let (full_path, href) = match href(trait_did, cx) { Ok(info) => (join_path_syms(&info.rust_path), Some(info.url)), Err(_) => (tcx.def_path_str(trait_did), None), }; - Some((name.clone(), NotableTraitBadge { name, full_path, href })) + Some((name.clone(), NotableTraitBadge { name, full_path, href, color })) } else { None } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 544d57d92613c..586e7e26ecef3 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1,13 +1,12 @@ use std::borrow::Cow; use std::cmp::Ordering; -use std::collections::hash_map::DefaultHasher; use std::fmt::{self, Display, Write as _}; -use std::hash::{Hash, Hasher}; use std::iter; use askama::Template; use rustc_abi::VariantIdx; use rustc_ast::join_path_syms; +use rustc_attr_ir::NotableTraitColor; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, MacroKinds}; @@ -58,8 +57,8 @@ struct NotableTraitBadgeVars { full_path: String, /// Relative URL to the trait page, or `None` when not linkable. href: Option, - /// Index of the `.notable-trait-badge-{n}` color class. - color_index: u8, + /// Name of the `.notable-trait-badge-{color}` color class. + color: NotableTraitColor, } #[derive(Template)] @@ -126,22 +125,14 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item) -> impl fmt::Disp let notable_trait_badges: Vec = notable_trait_badges(item, cx) .into_iter() - .map(|info| { - // Stable per-trait color from a hash of the trait path so the - // same trait gets the same badge color across pages. - // This won't be stable between releases though. - let mut h = DefaultHasher::new(); - info.full_path.hash(&mut h); - const BADGE_COLORS: u8 = 6; - let color_index = (h.finish() as u8) % BADGE_COLORS; - NotableTraitBadgeVars { - name: info.name, - full_path: info.full_path, - href: info.href, - color_index, - } + .map(|info| NotableTraitBadgeVars { + name: info.name, + full_path: info.full_path, + href: info.href, + color: info.color, }) .collect(); + let has_notable_trait_badges = !notable_trait_badges.is_empty(); let path_components = if item.is_fake_item() { vec![] @@ -219,11 +210,11 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item) -> impl fmt::Disp // Render notable-traits.js used for all methods in this module. let mut types_with_notable_traits = cx.types_with_notable_traits.borrow_mut(); - if !types_with_notable_traits.is_empty() { + if !types_with_notable_traits.is_empty() || has_notable_trait_badges { write!( buf, r#""#, - notable_traits_json(types_with_notable_traits.iter(), cx), + notable_traits_json(types_with_notable_traits.iter(), item, cx), )?; types_with_notable_traits.clear(); } diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index 085880b215d14..564e785a19f93 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -141,12 +141,14 @@ nav.sub { --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0); --sidebar-resizer-hover: hsl(207, 90%, 66%); --sidebar-resizer-active: hsl(207, 90%, 54%); - --notable-badge-pink: oklch(0.88 0.21 0); - --notable-badge-red: oklch(0.88 0.21 40); - --notable-badge-orange: oklch(0.88 0.21 70); + --notable-badge-grey: oklch(0.88 0 0); + --notable-badge-red: oklch(0.88 0.21 20); --notable-badge-green: oklch(0.88 0.21 150); + --notable-badge-yellow: oklch(0.88 0.21 70); --notable-badge-blue: oklch(0.88 0.21 240); - --notable-badge-violet: oklch(0.88 0.21 300); + --notable-badge-magenta: oklch(0.88 0.21 320); + --notable-badge-cyan: oklch(0.88 0.21 180); + --notable-badge-transparent: transparent; } /* End theme: light */ @@ -258,12 +260,14 @@ nav.sub { --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0); --sidebar-resizer-hover: hsl(207, 30%, 54%); --sidebar-resizer-active: hsl(207, 90%, 54%); - --notable-badge-pink: oklch(0.55 0.21 0); - --notable-badge-red: oklch(0.55 0.21 40); - --notable-badge-orange: oklch(0.55 0.21 70); + --notable-badge-grey: oklch(0.55 0 0); + --notable-badge-red: oklch(0.55 0.21 20); --notable-badge-green: oklch(0.55 0.21 150); + --notable-badge-yellow: oklch(0.55 0.21 100); --notable-badge-blue: oklch(0.55 0.21 240); - --notable-badge-violet: oklch(0.55 0.21 300); + --notable-badge-magenta: oklch(0.55 0.21 320); + --notable-badge-cyan: oklch(0.55 0.21 180); + --notable-badge-transparent: transparent; } /* End theme: dark */ } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index acf60114bd47a..2634a9a3d8a95 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -499,6 +499,9 @@ code, pre, .code-header, .type-signature { border-radius: 3px; padding: 0 0.125em; } +.docblock code { + padding: 2px 0.125em; +} .docblock pre code, .item-table dd pre code { padding: 0; } @@ -1149,10 +1152,14 @@ pre, .rustdoc.src .example-wrap, .example-wrap .src-line-numbers { background: var(--table-alt-row-background-color); } -.docblock .stab, .item-table dd .stab, .docblock p code { +.docblock .stab, .item-table dd .stab { display: inline-block; } +.docblock p code { + display: inline; +} + .docblock li { margin-bottom: .4em; } @@ -1668,13 +1675,24 @@ so that we can apply CSS-filters to change the arrow color in themes */ } .notable-trait-badge-container { - padding: 0.5rem 0; + padding-top: 8px; display: flex; flex-wrap: wrap; - gap: 0.5rem; + gap: 8px; + margin-left: var(--docblock-indent); + position: relative; } -.notable-trait-badge-container > a { +.notable-trait-badge-container > a.tooltip { + position: absolute; + width: 16px; + line-height: 1.5rem; + vertical-align: center; + left: calc(-1 * var(--docblock-indent)); + text-decoration: none; +} + +.notable-trait-badge-container > a:not(.tooltip) { display: flex; align-items: center; width: fit-content; @@ -1686,16 +1704,47 @@ so that we can apply CSS-filters to change the arrow color in themes */ color: var(--main-color); } -.notable-trait-badge-container > a:hover { - text-decoration: none; +/* this list of colors is lifted from the ANSI escape codes list: + https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit + This way, CLI-based tooling can adapt them without having to do + complex platform support dances. */ +.notable-trait-badge-container > .notable-trait-badge-grey { + background: var(--notable-badge-grey); + border: solid 1px var(--notable-badge-grey); +} +.notable-trait-badge-container > .notable-trait-badge-red { + background: var(--notable-badge-red); + border: solid 1px var(--notable-badge-red); +} +.notable-trait-badge-container > .notable-trait-badge-green { + background: var(--notable-badge-green); + border: solid 1px var(--notable-badge-green); +} +.notable-trait-badge-container > .notable-trait-badge-yellow { + background: var(--notable-badge-yellow); + border: solid 1px var(--notable-badge-yellow); +} +.notable-trait-badge-container > .notable-trait-badge-blue { + background: var(--notable-badge-blue); + border: solid 1px var(--notable-badge-blue); +} +.notable-trait-badge-container > .notable-trait-badge-magenta { + background: var(--notable-badge-magenta); + border: solid 1px var(--notable-badge-magenta); +} +.notable-trait-badge-container > .notable-trait-badge-cyan { + background: var(--notable-badge-cyan); + border: solid 1px var(--notable-badge-cyan); +} +.notable-trait-badge-container > .notable-trait-badge-transparent { + background: var(--notable-badge-transparent); + border: solid 1px var(--border-color); } -.notable-trait-badge-container > .badge-0 { background: var(--notable-badge-pink); } -.notable-trait-badge-container > .badge-1 { background: var(--notable-badge-red); } -.notable-trait-badge-container > .badge-2 { background: var(--notable-badge-orange); } -.notable-trait-badge-container > .badge-3 { background: var(--notable-badge-green); } -.notable-trait-badge-container > .badge-4 { background: var(--notable-badge-blue); } -.notable-trait-badge-container > .badge-5 { background: var(--notable-badge-violet); } +.notable-trait-badge-container > a:hover:not(.tooltip) { + text-decoration: none; + border: solid 1px var(--settings-button-border-focus); +} .rightside { padding-left: 12px; @@ -2941,6 +2990,10 @@ in src-script.js and main.js left: -11px; } + .notable-trait-badge-container > a.tooltip { + left: calc(-1 * var(--docblock-indent) - 11px); + } + /* sidebar button becomes topbar button */ #sidebar-button > a::before { content: url('data:image/svg+xml, {# #} {# #} - {% if !notable_trait_badges.is_empty() %} -
- {% for badge in notable_trait_badges.iter() %} - {{badge.name}} - {% endfor %} -
- {% endif %} {% if !stability_since_raw.is_empty() %} {{ stability_since_raw|safe +}} {% endif %} @@ -38,5 +29,15 @@

Source {#+ #} {% else %} {% endmatch %} + {% if !notable_trait_badges.is_empty() %} +
{# #} + ⓘ + {% for badge in notable_trait_badges.iter() %} + {{badge.name}} + {% endfor %} +
+ {% endif %} {# #} {# #} diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index f4cecb08bb982..85af793308935 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -1225,7 +1225,13 @@ fn maybe_from_hir_attr( name_value_attr(&mut ret, "keyword", keyword); name_value_attr(&mut ret, "attribute", attribute); toggle_attr(&mut ret, "masked", masked); - toggle_attr(&mut ret, "notable_trait", notable_trait); + if let Some((notable_trait, _span)) = notable_trait { + if let Some((color, _span)) = notable_trait { + ret.push(Attribute::Other(format!("#[doc(notable_trait = \"{color}\")]"))); + } else { + ret.push(Attribute::Other(format!("#[doc(notable_trait)]"))); + } + } toggle_attr(&mut ret, "search_unbox", search_unbox); name_value_attr(&mut ret, "html_favicon_url", html_favicon_url); name_value_attr(&mut ret, "html_logo_url", html_logo_url); diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 8720e09bd85cd..3262255a3fcf9 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -48,7 +48,7 @@ pub(super) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> debug!("considering extern trait impl {trait_ref:?}"); if crate_items.contains(&ItemId::DefId(trait_ref.def_id())) || Some(trait_ref.def_id()) == tcx.lang_items().deref_trait() - || tcx.is_doc_notable_trait(trait_ref.def_id()) + || tcx.doc_notable_trait(trait_ref.def_id()).is_some() { debug!("-> inlining due to trait"); cx.with_param_env(impl_def_id, |cx| { diff --git a/src/tools/miri/tests/pass/fn-traits-abi.rs b/src/tools/miri/tests/pass/fn-traits-abi.rs new file mode 100644 index 0000000000000..ed2579f7ab529 --- /dev/null +++ b/src/tools/miri/tests/pass/fn-traits-abi.rs @@ -0,0 +1,39 @@ +//! Test implementation of `Fn` traits for functions with non-`Rust` ABI + +use std::ops::{Fn, FnMut, FnOnce}; + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct Foo(i32, i32); + +extern "C" fn square(foo: Foo) -> Foo { + Foo(foo.0 * foo.1, foo.0 - foo.1) +} + +fn call_it Foo>(f: &F, i: Foo) -> Foo { + f(i) +} + +fn call_it_mut Foo>(f: &mut F, i: Foo) -> Foo { + f(i) +} + +fn call_it_once Foo>(f: F, i: Foo) -> Foo { + f(i) +} + +fn main() { + assert_eq!(call_it(&square, Foo(20, 10)), Foo(200, 10)); + assert_eq!(call_it_mut(&mut square, Foo(21, 9)), Foo(189, 12)); + assert_eq!(call_it_once(square, Foo(18, -3)), Foo(-54, 21)); + + let mut square_ptr: extern "C" fn(Foo) -> Foo = square; + assert_eq!(call_it(&square_ptr, Foo(30, 11)), Foo(330, 19)); + assert_eq!(call_it_mut(&mut square_ptr, Foo(273, -1)), Foo(-273, 274)); + assert_eq!(call_it_once(square_ptr, Foo(27, 27)), Foo(729, 0)); + + let mut square_dyn: Box Foo> = Box::new(square); + assert_eq!(call_it(&*square_dyn, Foo(1, 3)), Foo(3, -2)); + assert_eq!(call_it_mut(&mut *square_dyn, Foo(5, 25)), Foo(125, -20)); + assert_eq!(call_it_once(square_dyn, Foo(9, 2)), Foo(18, 7)); +} diff --git a/tests/codegen-llvm/asm/riscv-clobbers.rs b/tests/codegen-llvm/asm/riscv-clobbers.rs index fb2bbebd8883a..bebf313d37068 100644 --- a/tests/codegen-llvm/asm/riscv-clobbers.rs +++ b/tests/codegen-llvm/asm/riscv-clobbers.rs @@ -1,12 +1,14 @@ //@ add-minicore //@ assembly-output: emit-asm -//@ revisions: rv32i rv64i rv32e -//@[rv32i] compile-flags: --target riscv32i-unknown-none-elf -//@[rv32i] needs-llvm-components: riscv -//@[rv64i] compile-flags: --target riscv64imac-unknown-none-elf -//@[rv64i] needs-llvm-components: riscv -//@[rv32e] compile-flags: --target riscv32e-unknown-none-elf -//@[rv32e] needs-llvm-components: riscv +//@ revisions: ilp32 ilp32f ilp32d ilp32e ilp32e_extra lp64 lp64d +//@[ilp32] compile-flags: --target riscv32i-unknown-none-elf +//@[ilp32f] compile-flags: --target riscv32imfc-unknown-none-elf +//@[ilp32d] compile-flags: --target riscv32gc-unknown-linux-gnu +//@[ilp32e] compile-flags: --target riscv32e-unknown-none-elf +//@[ilp32e_extra] compile-flags:--target riscv32e-unknown-none-elf -Ctarget-feature=-e,+f +//@[lp64] compile-flags: --target riscv64im-unknown-none-elf +//@[lp64d] compile-flags: --target riscv64gc-unknown-none-elf +//@ needs-llvm-components: riscv // ignore-tidy-linelength #![crate_type = "rlib"] @@ -31,9 +33,13 @@ pub unsafe fn no_clobber() { } // CHECK-LABEL: @clobber_abi -// rv32i: asm sideeffect "", "={x1},={x5},={x6},={x7},={x10},={x11},={x12},={x13},={x14},={x15},={x16},={x17},={x28},={x29},={x30},={x31},~{f0},~{f1},~{f2},~{f3},~{f4},~{f5},~{f6},~{f7},~{f8},~{f9},~{f10},~{f11},~{f12},~{f13},~{f14},~{f15},~{f16},~{f17},~{f18},~{f19},~{f20},~{f21},~{f22},~{f23},~{f24},~{f25},~{f26},~{f27},~{f28},~{f29},~{f30},~{f31},~{v0},~{v1},~{v2},~{v3},~{v4},~{v5},~{v6},~{v7},~{v8},~{v9},~{v10},~{v11},~{v12},~{v13},~{v14},~{v15},~{v16},~{v17},~{v18},~{v19},~{v20},~{v21},~{v22},~{v23},~{v24},~{v25},~{v26},~{v27},~{v28},~{v29},~{v30},~{v31}"() -// rv64i: asm sideeffect "", "={x1},={x5},={x6},={x7},={x10},={x11},={x12},={x13},={x14},={x15},={x16},={x17},={x28},={x29},={x30},={x31},~{f0},~{f1},~{f2},~{f3},~{f4},~{f5},~{f6},~{f7},~{f8},~{f9},~{f10},~{f11},~{f12},~{f13},~{f14},~{f15},~{f16},~{f17},~{f18},~{f19},~{f20},~{f21},~{f22},~{f23},~{f24},~{f25},~{f26},~{f27},~{f28},~{f29},~{f30},~{f31},~{v0},~{v1},~{v2},~{v3},~{v4},~{v5},~{v6},~{v7},~{v8},~{v9},~{v10},~{v11},~{v12},~{v13},~{v14},~{v15},~{v16},~{v17},~{v18},~{v19},~{v20},~{v21},~{v22},~{v23},~{v24},~{v25},~{v26},~{v27},~{v28},~{v29},~{v30},~{v31}"() -// rv32e: asm sideeffect "", "={x1},={x5},={x6},={x7},={x10},={x11},={x12},={x13},={x14},={x15},~{f0},~{f1},~{f2},~{f3},~{f4},~{f5},~{f6},~{f7},~{f8},~{f9},~{f10},~{f11},~{f12},~{f13},~{f14},~{f15},~{f16},~{f17},~{f18},~{f19},~{f20},~{f21},~{f22},~{f23},~{f24},~{f25},~{f26},~{f27},~{f28},~{f29},~{f30},~{f31},~{v0},~{v1},~{v2},~{v3},~{v4},~{v5},~{v6},~{v7},~{v8},~{v9},~{v10},~{v11},~{v12},~{v13},~{v14},~{v15},~{v16},~{v17},~{v18},~{v19},~{v20},~{v21},~{v22},~{v23},~{v24},~{v25},~{v26},~{v27},~{v28},~{v29},~{v30},~{v31}"() +// ilp32: asm sideeffect "", "={x1},={x5},={x6},={x7},={x10},={x11},={x12},={x13},={x14},={x15},={x16},={x17},={x28},={x29},={x30},={x31},~{f0},~{f1},~{f2},~{f3},~{f4},~{f5},~{f6},~{f7},~{f8},~{f9},~{f10},~{f11},~{f12},~{f13},~{f14},~{f15},~{f16},~{f17},~{f18},~{f19},~{f20},~{f21},~{f22},~{f23},~{f24},~{f25},~{f26},~{f27},~{f28},~{f29},~{f30},~{f31},~{v0},~{v1},~{v2},~{v3},~{v4},~{v5},~{v6},~{v7},~{v8},~{v9},~{v10},~{v11},~{v12},~{v13},~{v14},~{v15},~{v16},~{v17},~{v18},~{v19},~{v20},~{v21},~{v22},~{v23},~{v24},~{v25},~{v26},~{v27},~{v28},~{v29},~{v30},~{v31}"() +// ilp32f: asm sideeffect "", "={x1},={x5},={x6},={x7},={x10},={x11},={x12},={x13},={x14},={x15},={x16},={x17},={x28},={x29},={x30},={x31},={f0},={f1},={f2},={f3},={f4},={f5},={f6},={f7},={f8},={f9},={f10},={f11},={f12},={f13},={f14},={f15},={f16},={f17},={f18},={f19},={f20},={f21},={f22},={f23},={f24},={f25},={f26},={f27},={f28},={f29},={f30},={f31},~{v0},~{v1},~{v2},~{v3},~{v4},~{v5},~{v6},~{v7},~{v8},~{v9},~{v10},~{v11},~{v12},~{v13},~{v14},~{v15},~{v16},~{v17},~{v18},~{v19},~{v20},~{v21},~{v22},~{v23},~{v24},~{v25},~{v26},~{v27},~{v28},~{v29},~{v30},~{v31}"() +// ilp32d: asm sideeffect "", "={x1},={x5},={x6},={x7},={x10},={x11},={x12},={x13},={x14},={x15},={x16},={x17},={x28},={x29},={x30},={x31},={f0},={f1},={f2},={f3},={f4},={f5},={f6},={f7},={f10},={f11},={f12},={f13},={f14},={f15},={f16},={f17},={f28},={f29},={f30},={f31},~{v0},~{v1},~{v2},~{v3},~{v4},~{v5},~{v6},~{v7},~{v8},~{v9},~{v10},~{v11},~{v12},~{v13},~{v14},~{v15},~{v16},~{v17},~{v18},~{v19},~{v20},~{v21},~{v22},~{v23},~{v24},~{v25},~{v26},~{v27},~{v28},~{v29},~{v30},~{v31}"() +// ilp32e: asm sideeffect "", "={x1},={x5},={x6},={x7},={x10},={x11},={x12},={x13},={x14},={x15},={x16},={x17},={x18},={x19},={x20},={x21},={x22},={x23},={x24},={x25},={x26},={x27},={x28},={x29},={x30},={x31},~{f0},~{f1},~{f2},~{f3},~{f4},~{f5},~{f6},~{f7},~{f8},~{f9},~{f10},~{f11},~{f12},~{f13},~{f14},~{f15},~{f16},~{f17},~{f18},~{f19},~{f20},~{f21},~{f22},~{f23},~{f24},~{f25},~{f26},~{f27},~{f28},~{f29},~{f30},~{f31},~{v0},~{v1},~{v2},~{v3},~{v4},~{v5},~{v6},~{v7},~{v8},~{v9},~{v10},~{v11},~{v12},~{v13},~{v14},~{v15},~{v16},~{v17},~{v18},~{v19},~{v20},~{v21},~{v22},~{v23},~{v24},~{v25},~{v26},~{v27},~{v28},~{v29},~{v30},~{v31}"() +// ilp32e_extra: asm sideeffect "", "={x1},={x5},={x6},={x7},={x10},={x11},={x12},={x13},={x14},={x15},={x16},={x17},={x18},={x19},={x20},={x21},={x22},={x23},={x24},={x25},={x26},={x27},={x28},={x29},={x30},={x31},={f0},={f1},={f2},={f3},={f4},={f5},={f6},={f7},={f8},={f9},={f10},={f11},={f12},={f13},={f14},={f15},={f16},={f17},={f18},={f19},={f20},={f21},={f22},={f23},={f24},={f25},={f26},={f27},={f28},={f29},={f30},={f31},~{v0},~{v1},~{v2},~{v3},~{v4},~{v5},~{v6},~{v7},~{v8},~{v9},~{v10},~{v11},~{v12},~{v13},~{v14},~{v15},~{v16},~{v17},~{v18},~{v19},~{v20},~{v21},~{v22},~{v23},~{v24},~{v25},~{v26},~{v27},~{v28},~{v29},~{v30},~{v31}"() +// lp64: asm sideeffect "", "={x1},={x5},={x6},={x7},={x10},={x11},={x12},={x13},={x14},={x15},={x16},={x17},={x28},={x29},={x30},={x31},~{f0},~{f1},~{f2},~{f3},~{f4},~{f5},~{f6},~{f7},~{f8},~{f9},~{f10},~{f11},~{f12},~{f13},~{f14},~{f15},~{f16},~{f17},~{f18},~{f19},~{f20},~{f21},~{f22},~{f23},~{f24},~{f25},~{f26},~{f27},~{f28},~{f29},~{f30},~{f31},~{v0},~{v1},~{v2},~{v3},~{v4},~{v5},~{v6},~{v7},~{v8},~{v9},~{v10},~{v11},~{v12},~{v13},~{v14},~{v15},~{v16},~{v17},~{v18},~{v19},~{v20},~{v21},~{v22},~{v23},~{v24},~{v25},~{v26},~{v27},~{v28},~{v29},~{v30},~{v31}"() +// lp64d: asm sideeffect "", "={x1},={x5},={x6},={x7},={x10},={x11},={x12},={x13},={x14},={x15},={x16},={x17},={x28},={x29},={x30},={x31},={f0},={f1},={f2},={f3},={f4},={f5},={f6},={f7},={f10},={f11},={f12},={f13},={f14},={f15},={f16},={f17},={f28},={f29},={f30},={f31},~{v0},~{v1},~{v2},~{v3},~{v4},~{v5},~{v6},~{v7},~{v8},~{v9},~{v10},~{v11},~{v12},~{v13},~{v14},~{v15},~{v16},~{v17},~{v18},~{v19},~{v20},~{v21},~{v22},~{v23},~{v24},~{v25},~{v26},~{v27},~{v28},~{v29},~{v30},~{v31}"() #[no_mangle] pub unsafe fn clobber_abi() { asm!("", clobber_abi("C"), options(nostack, nomem, preserves_flags)); diff --git a/tests/rustdoc-gui/code-in-docblock-and-comma.goml b/tests/rustdoc-gui/code-in-docblock-and-comma.goml new file mode 100644 index 0000000000000..6bbbebb9bfe90 --- /dev/null +++ b/tests/rustdoc-gui/code-in-docblock-and-comma.goml @@ -0,0 +1,93 @@ +// This test ensures that `` in docblocks are `display: inline` so comma are +// "stuck" to them instead of going to next line on their own. +// Regression test for . + +go-to: "file://" + |DOC_PATH| + "/test_docs/inline_code/index.html" + +store-value: (height, 300) +// Very precise width! +store-value: (big_width, 588) +store-value: (small_width, 584) +set-window-size: (|big_width|, |height|) + +// Very annoying test as we can't query information about the comma itself, so instead +// we're gonna look for the position of the `` just before and after it. +store-position: ( + '//*[@class="docblock"]/p', + {"x": x_parent}, +) + +define-function: ( + "test_initial_positions", + [], + block { + store-position: ( + '//*[@class="docblock"]/p/code[text()="Color::srgba_u8"]', + {"x": x_before, "y": y_before}, + ) + store-position: ( + '//*[@class="docblock"]/p/code[text()="blob"]', + {"x": x_after, "y": y_after}, + ) + + // First we check that the "after" element is at the start of the line (so the same `x` position + // as its parent). + assert: |x_after| == |x_parent| + // Then we ensure that both `` aren't on the same line. + assert: |y_before| < |y_after| + }, +) + +call-function: ("test_initial_positions", {}) + +// We reduce the width of the screen a bit to force the first `` (and the comma) to go on the +// next line. +set-window-size: (|small_width|, |height|) + +store-position: ( + '//*[@class="docblock"]/p/code[text()="Color::srgba_u8"]', + {"x": x2_before, "y": y2_before}, +) +store-position: ( + '//*[@class="docblock"]/p/code[text()="blob"]', + {"x": x2_after, "y": y2_after}, +) + +// The "y" position of the second `` shouldn't have changed. +assert: |y_after| == |y2_after| +// However its "x" position should have changed. +assert: |x_after| < |x2_after| +// And the first `` should not be on the same line anymore. +assert: |y_before| < |y2_before| +// So the "x" position should now be the same as the parent. +assert: |x2_before| == |x_parent| + +// And now we switch to `display: inline-block` and confirm that the comma was not "stuck" to the +// `` (although it should). +set-window-size: (|big_width|, |height|) +set-css: ( + '//*[@class="docblock"]/p/code[text()="Color::srgba_u8"]', + {"display": "inline-block"}, +) + +call-function: ("test_initial_positions", {}) + +// Now we reduce the width, and that's where the interesting part starts. +set-window-size: (|small_width|, |height|) + +store-position: ( + '//*[@class="docblock"]/p/code[text()="Color::srgba_u8"]', + {"x": x2_before, "y": y2_before}, +) +store-position: ( + '//*[@class="docblock"]/p/code[text()="blob"]', + {"x": x2_after, "y": y2_after}, +) + +// The "y" position of the second `` shouldn't have changed. +assert: |y_after| == |y2_after| +// And its "x" position should also have changed (confirming that the comma is not on the same +// line anymore). +assert: |x_after| < |x2_after| +// However unlike previously, the first `` should still be on the same line. +assert: |y_before| == |y2_before| diff --git a/tests/rustdoc-gui/notable-trait-badges.goml b/tests/rustdoc-gui/notable-trait-badges.goml new file mode 100644 index 0000000000000..927c4e34cccd4 --- /dev/null +++ b/tests/rustdoc-gui/notable-trait-badges.goml @@ -0,0 +1,97 @@ +// Ensures that the theme change is working as expected. +include: "utils.goml" +go-to: "file://" + |DOC_PATH| + "/lib2/notable_trait_colors/struct.NotableTraitColors.html" + +define-function: ( + "test-badges", + [ + grey, red, green, yellow, blue, magenta, cyan, transparent, transparent_border, + ], + block { + assert-css: (".notable-trait-badge-grey", {"background-color": |grey|, "border-color": |grey|}) + assert-css: (".notable-trait-badge-red", {"background-color": |red|, "border-color": |red|}) + assert-css: (".notable-trait-badge-green", {"background-color": |green|, "border-color": |green|}) + assert-css: (".notable-trait-badge-yellow", {"background-color": |yellow|, "border-color": |yellow|}) + assert-css: (".notable-trait-badge-blue", {"background-color": |blue|, "border-color": |blue|}) + assert-css: (".notable-trait-badge-magenta", {"background-color": |magenta|, "border-color": |magenta|}) + assert-css: (".notable-trait-badge-cyan", {"background-color": |cyan|, "border-color": |cyan|}) + assert-css: (".notable-trait-badge-transparent", { + "background-color": |transparent|, + "border-color": |transparent_border|, + }) + store-css: (".top-doc > .docblock", {"margin-left": top_docblock_margin_left}) + assert-css: (".notable-trait-badge-container", {"margin-left": |top_docblock_margin_left|}) + + set-window-size: (1280, 1024) + store-position: (".top-doc > .docblock", {"x": top_docblock_x}) + assert-position: (".notable-trait-badge-container", {"x": |top_docblock_x|}) + store-position: (".top-doc > summary::before", {"x": top_docblock_toggle_x}) + assert-position: (".notable-trait-badge-container > a.tooltip", {"x": |top_docblock_toggle_x|}) + store-position: (".notable-trait-badge-container > a.tooltip + a", {"x": notable_trait_badge_first_x}) + store-position: (".notable-trait-badge-container > a.tooltip", {"x": notable_trait_tooltip_x}) + store-size: (".notable-trait-badge-container > a.tooltip", {"width": notable_trait_tooltip_width}) + assert: |notable_trait_tooltip_x| + |notable_trait_tooltip_width| < |notable_trait_badge_first_x| + + set-window-size: (320, 480) + store-position: (".top-doc > .docblock", {"x": top_docblock_x}) + assert-position: (".notable-trait-badge-container", {"x": |top_docblock_x|}) + store-position: (".top-doc > summary::before", {"x": top_docblock_toggle_x}) + assert-position: (".notable-trait-badge-container > a.tooltip", {"x": |top_docblock_toggle_x|}) + store-position: (".notable-trait-badge-container > a.tooltip + a", {"x": notable_trait_badge_first_x}) + store-position: (".notable-trait-badge-container > a.tooltip", {"x": notable_trait_tooltip_x}) + store-size: (".notable-trait-badge-container > a.tooltip", {"width": notable_trait_tooltip_width}) + assert: |notable_trait_tooltip_x| + |notable_trait_tooltip_width| < |notable_trait_badge_first_x| + } +) +call-function: ("switch-theme", {"theme": "dark"}) +call-function: ("test-badges", { + "grey": "oklch(0.55 0 0)", + "red": "oklch(0.55 0.21 20)", + "green": "oklch(0.55 0.21 150)", + "yellow": "oklch(0.55 0.21 100)", + "blue": "oklch(0.55 0.21 240)", + "magenta": "oklch(0.55 0.21 320)", + "cyan": "oklch(0.55 0.21 180)", + "transparent": "transparent", + "transparent_border": "#e0e0e0", +}) + +call-function: ("switch-theme", {"theme": "light"}) +call-function: ("test-badges", { + "grey": "oklch(0.88 0 0)", + "red": "oklch(0.88 0.21 20)", + "green": "oklch(0.88 0.21 150)", + "yellow": "oklch(0.88 0.21 70)", + "blue": "oklch(0.88 0.21 240)", + "magenta": "oklch(0.88 0.21 320)", + "cyan": "oklch(0.88 0.21 180)", + "transparent": "transparent", + "transparent_border": "#e0e0e0", +}) + +call-function: ("switch-theme", {"theme": "ayu"}) +call-function: ("test-badges", { + "grey": "oklch(0.49 0 0)", + "red": "oklch(0.49 0.21 20)", + "green": "oklch(0.49 0.21 150)", + "yellow": "oklch(0.49 0.21 100)", + "blue": "oklch(0.49 0.21 240)", + "magenta": "oklch(0.49 0.21 320)", + "cyan": "oklch(0.49 0.21 180)", + "transparent": "transparent", + "transparent_border": "#5c6773", +}) + +assert-false: ".tooltip.popover" +click: ".notable-trait-badge-container > a.tooltip" +wait-for: ".tooltip.popover" +assert-text: (".tooltip.popover h3", "Notable traits for NotableTraitColors") +assert-count: (".tooltip.popover .where", 8) +assert-text: (".tooltip.popover .where:nth-child(1)", "impl Grey for NotableTraitColors") +assert-text: (".tooltip.popover .where:nth-child(2)", "impl Red for NotableTraitColors") +assert-text: (".tooltip.popover .where:nth-child(3)", "impl Green for NotableTraitColors") +assert-text: (".tooltip.popover .where:nth-child(4)", "impl Yellow for NotableTraitColors") +assert-text: (".tooltip.popover .where:nth-child(5)", "impl Blue for NotableTraitColors") +assert-text: (".tooltip.popover .where:nth-child(6)", "impl Magenta for NotableTraitColors") +assert-text: (".tooltip.popover .where:nth-child(7)", "impl Cyan for NotableTraitColors") +assert-text: (".tooltip.popover .where:nth-child(8)", "impl Transparent for NotableTraitColors") diff --git a/tests/rustdoc-gui/notable-traits.goml b/tests/rustdoc-gui/notable-traits.goml index f31b83e12edf0..60a66b219b2f8 100644 --- a/tests/rustdoc-gui/notable-traits.goml +++ b/tests/rustdoc-gui/notable-traits.goml @@ -2,11 +2,12 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/notable/struct.Wrapper.html" show-text: true store-css: (".notable-trait-badge-container", {"background-color": background_color}) -// The background color should be different. -assert-css-false: (".notable-trait-badge-container > a", {"background-color": |background_color|}) +// The border color should be different. +assert-css-false: (".notable-trait-badge-container > a:not(.tooltip)", {"border-color": |background_color|}) +assert-css-false: (".notable-trait-badge-container > a:not(.tooltip)", {"border-color": "transparent"}) store-css: ("#trait-implementations", {"color": text_color}) -assert-css: (".notable-trait-badge-container > a", { +assert-css: (".notable-trait-badge-container > a:not(.tooltip)", { // The color should be the same as the "main color". "color": |text_color|, // Checking some additional CSS values. diff --git a/tests/rustdoc-gui/src/lib2/lib.rs b/tests/rustdoc-gui/src/lib2/lib.rs index 28dfa56ea1ad4..3104b88b183e3 100644 --- a/tests/rustdoc-gui/src/lib2/lib.rs +++ b/tests/rustdoc-gui/src/lib2/lib.rs @@ -1,7 +1,6 @@ // ignore-tidy-file-linelength -#![feature(doc_cfg)] -#![feature(negative_impls)] +#![feature(doc_cfg, negative_impls, doc_notable_trait)] pub mod another_folder; pub mod another_mod; @@ -411,3 +410,32 @@ pub mod deprecated { fn depr_deprecated_fn() {} } } + +pub mod notable_trait_colors { + /// Top doc + pub struct NotableTraitColors; + #[doc(notable_trait(color="grey"))] + pub trait Grey {} + impl Grey for NotableTraitColors {} + #[doc(notable_trait(color="red"))] + pub trait Red {} + impl Red for NotableTraitColors {} + #[doc(notable_trait(color="green"))] + pub trait Green {} + impl Green for NotableTraitColors {} + #[doc(notable_trait(color="yellow"))] + pub trait Yellow {} + impl Yellow for NotableTraitColors {} + #[doc(notable_trait(color="blue"))] + pub trait Blue {} + impl Blue for NotableTraitColors {} + #[doc(notable_trait(color="magenta"))] + pub trait Magenta {} + impl Magenta for NotableTraitColors {} + #[doc(notable_trait(color="cyan"))] + pub trait Cyan {} + impl Cyan for NotableTraitColors {} + #[doc(notable_trait(color="transparent"))] + pub trait Transparent {} + impl Transparent for NotableTraitColors {} +} diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index 54db340e7ba64..1ffcd80581c24 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -827,3 +827,6 @@ pub mod notable { pub macro decl_macro { () => { "bar" } } + +/// Some text before the code so the width isn't too small `Color::srgba_u8`, `blob`. +pub mod inline_code {} diff --git a/tests/rustdoc-html/README.md b/tests/rustdoc-html/README.md index 31657403f2858..009b0d09ea8e4 100644 --- a/tests/rustdoc-html/README.md +++ b/tests/rustdoc-html/README.md @@ -20,6 +20,10 @@ docs. Contains tests checking the rendering of `async` items. +## `tests/rustdoc-html/attributes` + +Contains tests checking the rendering of attributes in the docs. + ## `tests/rustdoc-html/auto` Contains tests related to auto traits implementations. diff --git a/tests/rustdoc-html/attributes-2021-edition.rs b/tests/rustdoc-html/attributes/attributes-2021-edition.rs similarity index 100% rename from tests/rustdoc-html/attributes-2021-edition.rs rename to tests/rustdoc-html/attributes/attributes-2021-edition.rs diff --git a/tests/rustdoc-html/attributes.rs b/tests/rustdoc-html/attributes/attributes.rs similarity index 100% rename from tests/rustdoc-html/attributes.rs rename to tests/rustdoc-html/attributes/attributes.rs diff --git a/tests/rustdoc-html/auxiliary/masked.rs b/tests/rustdoc-html/attributes/auxiliary/masked.rs similarity index 100% rename from tests/rustdoc-html/auxiliary/masked.rs rename to tests/rustdoc-html/attributes/auxiliary/masked.rs diff --git a/tests/rustdoc-html/masked.rs b/tests/rustdoc-html/attributes/masked.rs similarity index 100% rename from tests/rustdoc-html/masked.rs rename to tests/rustdoc-html/attributes/masked.rs diff --git a/tests/rustdoc-html/stability.rs b/tests/rustdoc-html/attributes/stability.rs similarity index 100% rename from tests/rustdoc-html/stability.rs rename to tests/rustdoc-html/attributes/stability.rs diff --git a/tests/rustdoc-html/splat-variadic-ellipsis.rs b/tests/rustdoc-html/fn/splat-variadic-ellipsis.rs similarity index 100% rename from tests/rustdoc-html/splat-variadic-ellipsis.rs rename to tests/rustdoc-html/fn/splat-variadic-ellipsis.rs diff --git a/tests/rustdoc-html/sort-53812.rs b/tests/rustdoc-html/impl/sort-53812.rs similarity index 100% rename from tests/rustdoc-html/sort-53812.rs rename to tests/rustdoc-html/impl/sort-53812.rs diff --git a/tests/rustdoc-html/mut-params.rs b/tests/rustdoc-html/mut-params.rs index b3d4fe8c0599c..f2ebd3a21d0ff 100644 --- a/tests/rustdoc-html/mut-params.rs +++ b/tests/rustdoc-html/mut-params.rs @@ -6,7 +6,8 @@ pub struct Foo; //@ count foo/struct.Foo.html '//*[@class="impl-items"]//*[@class="method"]' 2 -//@ !has - '//*[@class="impl-items"]//*[@class="method"]' 'mut' +//@ has - '//*[@class="impl-items"]//*[@class="method"]' 'pub fn foo(self)' +//@ has - '//*[@class="impl-items"]//*[@class="method"]' 'pub fn bar(bar: ())' impl Foo { pub fn foo(mut self) {} @@ -14,5 +15,5 @@ impl Foo { } //@ count foo/fn.baz.html '//pre[@class="rust item-decl"]' 1 -//@ !has - '//pre[@class="rust item-decl"]' 'mut' +//@ has - '//pre[@class="rust item-decl"]' 'fn baz(foo: Foo)' pub fn baz(mut foo: Foo) {} diff --git a/tests/rustdoc-html/notable-trait/doc-notable_trait-negative.positive.html b/tests/rustdoc-html/notable-trait/doc-notable_trait-negative.positive.html index a3d0fedaaf47e..250d90567aa08 100644 --- a/tests/rustdoc-html/notable-trait/doc-notable_trait-negative.positive.html +++ b/tests/rustdoc-html/notable-trait/doc-notable_trait-negative.positive.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/tests/rustdoc-html/notable-trait/doc-notable_trait-slice.bare_fn_matches.html b/tests/rustdoc-html/notable-trait/doc-notable_trait-slice.bare_fn_matches.html index 7d2a2589133be..8b5a27551ed0b 100644 --- a/tests/rustdoc-html/notable-trait/doc-notable_trait-slice.bare_fn_matches.html +++ b/tests/rustdoc-html/notable-trait/doc-notable_trait-slice.bare_fn_matches.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/tests/rustdoc-html/notable-trait/doc-notable_trait.bare-fn.html b/tests/rustdoc-html/notable-trait/doc-notable_trait.bare-fn.html index 9f71677cf4ffc..cfc3573c6d8f7 100644 --- a/tests/rustdoc-html/notable-trait/doc-notable_trait.bare-fn.html +++ b/tests/rustdoc-html/notable-trait/doc-notable_trait.bare-fn.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/tests/rustdoc-html/notable-trait/doc-notable_trait.some-struct-new.html b/tests/rustdoc-html/notable-trait/doc-notable_trait.some-struct-new.html index 38689c79f1ace..7577ffa4c7091 100644 --- a/tests/rustdoc-html/notable-trait/doc-notable_trait.some-struct-new.html +++ b/tests/rustdoc-html/notable-trait/doc-notable_trait.some-struct-new.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/tests/rustdoc-html/notable-trait/doc-notable_trait.wrap-me.html b/tests/rustdoc-html/notable-trait/doc-notable_trait.wrap-me.html index c137be9e3a5d8..761a2c220935b 100644 --- a/tests/rustdoc-html/notable-trait/doc-notable_trait.wrap-me.html +++ b/tests/rustdoc-html/notable-trait/doc-notable_trait.wrap-me.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs index 225572513c40f..dc7b349ede2f3 100644 --- a/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs @@ -8,7 +8,8 @@ pub trait Pos {} // A negative impl must not produce a badge. //@ has 'foo/struct.T.html' -//@ count - '//div[@class="notable-trait-badge-container"]/a' 1 +//@ count - '//div[@class="notable-trait-badge-container"]/a' 2 +//@ has - '//div[@class="notable-trait-badge-container"]/a[@href="#"]' 'ⓘ' //@ has - '//div[@class="notable-trait-badge-container"]/a[@href="trait.Pos.html"]' 'Pos' pub struct T; impl !Neg for T {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs index 9bb3226099a51..2d29fbeedf80e 100644 --- a/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs @@ -9,7 +9,8 @@ pub trait Derived: Base {} //@ has 'foo/struct.S.html' // Implementing `Derived` requires implementing the notable supertrait `Base`, // so its badge shows up. -//@ count - '//div[@class="notable-trait-badge-container"]/a' 1 +//@ count - '//div[@class="notable-trait-badge-container"]/a' 2 +//@ has - '//div[@class="notable-trait-badge-container"]/a[@href="#"]' 'ⓘ' //@ has - '//div[@class="notable-trait-badge-container"]/a[@href="trait.Base.html"]' 'Base' pub struct S; impl Base for S {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs index de7008cf5ab0c..a249a100e6541 100644 --- a/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs @@ -12,6 +12,7 @@ use notable_dep::Spaceship; // The badge is present... //@ has - '//div[@class="notable-trait-badge-container"]/a' 'Spaceship' // ...but unlinked: no badge carries an `href`. -//@ count - '//div[@class="notable-trait-badge-container"]/a[@href]' 0 +//@ count - '//div[@class="notable-trait-badge-container"]/a[@href]' 1 +//@ has - '//div[@class="notable-trait-badge-container"]/a[@href="#"]' 'ⓘ' pub struct Rocket; impl Spaceship for Rocket {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge.rs index b53d989d0f819..7692201bc6140 100644 --- a/tests/rustdoc-html/notable-trait/notable-trait-badge.rs +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge.rs @@ -12,8 +12,9 @@ pub trait Plain {} //@ has 'foo/struct.Tagged.html' //@ has - '//div[@class="notable-trait-badge-container"]/a[@href="trait.Labeled.html"][@title="foo::Labeled"]' 'Labeled' // Badges are sorted by trait name, so `AlsoLabeled` precedes `Labeled`. -//@ has - '//div[@class="notable-trait-badge-container"]/a[1]' 'AlsoLabeled' -//@ has - '//div[@class="notable-trait-badge-container"]/a[2]' 'Labeled' +//@ has - '//div[@class="notable-trait-badge-container"]/a[1]' 'ⓘ' +//@ has - '//div[@class="notable-trait-badge-container"]/a[2]' 'AlsoLabeled' +//@ has - '//div[@class="notable-trait-badge-container"]/a[3]' 'Labeled' pub struct Tagged; impl Labeled for Tagged {} impl AlsoLabeled for Tagged {} @@ -23,3 +24,20 @@ impl Plain for Tagged {} //@ count - '//div[@class="notable-trait-badge-container"]' 0 pub struct Untagged; impl Plain for Untagged {} + +#[doc(notable_trait(color="grey"))] +pub trait Grey {} + +#[doc(notable_trait(color="gray"))] +pub trait Gray {} + +#[doc(notable_trait(color="blue"))] +pub trait Blue {} + +//@ has 'foo/struct.Colors.html' +//@ count - '//a[@class="notable-trait-badge-grey"]' 2 +//@ count - '//a[@class="notable-trait-badge-blue"]' 1 +pub struct Colors; +impl Grey for Colors {} +impl Gray for Colors {} +impl Blue for Colors {} diff --git a/tests/rustdoc-html/notable-trait/spotlight-from-dependency.odd.html b/tests/rustdoc-html/notable-trait/spotlight-from-dependency.odd.html index 910698a5854e9..d7b6686e9d139 100644 --- a/tests/rustdoc-html/notable-trait/spotlight-from-dependency.odd.html +++ b/tests/rustdoc-html/notable-trait/spotlight-from-dependency.odd.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/tests/rustdoc-html/smoke.rs b/tests/rustdoc-html/smoke.rs deleted file mode 100644 index 415fca313fc55..0000000000000 --- a/tests/rustdoc-html/smoke.rs +++ /dev/null @@ -1,25 +0,0 @@ -//@ has smoke/index.html - -//! Very docs - -//@ has smoke/bar/index.html -pub mod bar { - - /// So correct - //@ has smoke/bar/baz/index.html - pub mod baz { - /// Much detail - //@ has smoke/bar/baz/fn.baz.html - pub fn baz() { } - } - - /// *wow* - //@ has smoke/bar/trait.Doge.html - pub trait Doge { fn dummy(&self) { } } - - //@ has smoke/bar/struct.Foo.html - pub struct Foo { x: isize, y: usize } - - //@ has smoke/bar/fn.prawns.html - pub fn prawns((a, b): (isize, usize), Foo { x, y }: Foo) { } -} diff --git a/tests/rustdoc-ui/lints/doc-notable_trait-check.rs b/tests/rustdoc-ui/lints/doc-notable_trait-check.rs new file mode 100644 index 0000000000000..e56ecd573ede1 --- /dev/null +++ b/tests/rustdoc-ui/lints/doc-notable_trait-check.rs @@ -0,0 +1,44 @@ +#![feature(doc_notable_trait)] +#![deny(invalid_doc_attributes)] + +#[doc(notable_trait)] +trait NoColor {} + +#[doc(notable_trait())] +trait NoColor2 {} + +#[doc(notable_trait(color="transparent"))] +trait Transparent {} + +#[doc(notable_trait(color="red"))] +trait Red {} + +#[doc(notable_trait="red")] +//~^ ERROR +//~| WARN previously accepted by the compiler +trait InvalidKV {} + +#[doc(notable_trait)] +//~^ ERROR +//~| WARN previously accepted by the compiler +struct InvalidNotTrait; + +#[doc(notable_trait(check="red"))] +//~^ ERROR +//~| WARN previously accepted by the compiler +trait InvalidKVArg {} + +#[doc(notable_trait(color))] +//~^ ERROR +//~| WARN previously accepted by the compiler +trait InvalidAtomArg {} + +#[doc(notable_trait(color="invalid_color"))] +//~^ ERROR +//~| WARN previously accepted by the compiler +trait InvalidColor {} + +#[doc(notable_trait(color="transparent", color="red"))] +//~^ ERROR +//~| WARN previously accepted by the compiler +trait InvalidMultiple {} diff --git a/tests/rustdoc-ui/lints/doc-notable_trait-check.stderr b/tests/rustdoc-ui/lints/doc-notable_trait-check.stderr new file mode 100644 index 0000000000000..4d0f99d34b4d3 --- /dev/null +++ b/tests/rustdoc-ui/lints/doc-notable_trait-check.stderr @@ -0,0 +1,55 @@ +error: expected either `doc(notable_trait)` or `doc(notable_trait(color="..."))` + --> $DIR/doc-notable_trait-check.rs:16:7 + | +LL | #[doc(notable_trait="red")] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/doc-notable_trait-check.rs:2:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `#![doc(notable_trait)]` must be a trait attribute + --> $DIR/doc-notable_trait-check.rs:21:7 + | +LL | #[doc(notable_trait)] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: expected either `doc(notable_trait)` or `doc(notable_trait(color="..."))` + --> $DIR/doc-notable_trait-check.rs:26:7 + | +LL | #[doc(notable_trait(check="red"))] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: expected either `doc(notable_trait)` or `doc(notable_trait(color="..."))` + --> $DIR/doc-notable_trait-check.rs:31:7 + | +LL | #[doc(notable_trait(color))] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: expected either `doc(notable_trait)` or `doc(notable_trait(color="..."))` + --> $DIR/doc-notable_trait-check.rs:36:7 + | +LL | #[doc(notable_trait(color="invalid_color"))] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: expected either `doc(notable_trait)` or `doc(notable_trait(color="..."))` + --> $DIR/doc-notable_trait-check.rs:41:7 + | +LL | #[doc(notable_trait(color="transparent", color="red"))] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: aborting due to 6 previous errors + diff --git a/tests/ui/asm/riscv/bad-reg.riscv32e.stderr b/tests/ui/asm/riscv/bad-reg.riscv32e.stderr index 27c8e958e536a..ceffe0fbd6304 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32e.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32e.stderr @@ -1,185 +1,185 @@ error: invalid register `s1`: s1 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("s1") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("gp") _); | ^^^^^^^^^^^ error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:94:18 + --> $DIR/bad-reg.rs:90:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:97:18 + --> $DIR/bad-reg.rs:93:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:100:26 + --> $DIR/bad-reg.rs:96:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:103:26 + --> $DIR/bad-reg.rs:99:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: cannot use register `x16`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:46:18 + --> $DIR/bad-reg.rs:41:18 | -LL | asm!("", out("x16") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x16") x); + | ^^^^^^^^^^^ error: cannot use register `x17`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:48:18 + --> $DIR/bad-reg.rs:43:18 | -LL | asm!("", out("x17") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x17") x); + | ^^^^^^^^^^^ error: cannot use register `x18`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:50:18 + --> $DIR/bad-reg.rs:45:18 | -LL | asm!("", out("x18") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x18") x); + | ^^^^^^^^^^^ error: cannot use register `x19`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:52:18 + --> $DIR/bad-reg.rs:47:18 | -LL | asm!("", out("x19") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x19") x); + | ^^^^^^^^^^^ error: cannot use register `x20`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:54:18 + --> $DIR/bad-reg.rs:49:18 | -LL | asm!("", out("x20") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x20") x); + | ^^^^^^^^^^^ error: cannot use register `x21`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:56:18 + --> $DIR/bad-reg.rs:51:18 | -LL | asm!("", out("x21") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x21") x); + | ^^^^^^^^^^^ error: cannot use register `x22`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:58:18 + --> $DIR/bad-reg.rs:53:18 | -LL | asm!("", out("x22") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x22") x); + | ^^^^^^^^^^^ error: cannot use register `x23`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:60:18 + --> $DIR/bad-reg.rs:55:18 | -LL | asm!("", out("x23") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x23") x); + | ^^^^^^^^^^^ error: cannot use register `x24`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:62:18 + --> $DIR/bad-reg.rs:57:18 | -LL | asm!("", out("x24") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x24") x); + | ^^^^^^^^^^^ error: cannot use register `x25`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:64:18 + --> $DIR/bad-reg.rs:59:18 | -LL | asm!("", out("x25") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x25") x); + | ^^^^^^^^^^^ error: cannot use register `x26`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:66:18 + --> $DIR/bad-reg.rs:61:18 | -LL | asm!("", out("x26") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x26") x); + | ^^^^^^^^^^^ error: cannot use register `x27`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:68:18 + --> $DIR/bad-reg.rs:63:18 | -LL | asm!("", out("x27") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x27") x); + | ^^^^^^^^^^^ error: cannot use register `x28`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:70:18 + --> $DIR/bad-reg.rs:65:18 | -LL | asm!("", out("x28") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x28") x); + | ^^^^^^^^^^^ error: cannot use register `x29`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:72:18 + --> $DIR/bad-reg.rs:67:18 | -LL | asm!("", out("x29") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x29") x); + | ^^^^^^^^^^^ error: cannot use register `x30`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:74:18 + --> $DIR/bad-reg.rs:69:18 | -LL | asm!("", out("x30") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x30") x); + | ^^^^^^^^^^^ error: cannot use register `x31`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:76:18 + --> $DIR/bad-reg.rs:71:18 | -LL | asm!("", out("x31") _); - | ^^^^^^^^^^^^ +LL | asm!("", in("x31") x); + | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:80:26 + --> $DIR/bad-reg.rs:75:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:77:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:79:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:87:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:94:27 + --> $DIR/bad-reg.rs:90:27 | LL | asm!("", in("v0") x); | ^ @@ -187,7 +187,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:97:28 + --> $DIR/bad-reg.rs:93:28 | LL | asm!("", out("v0") x); | ^ @@ -195,7 +195,7 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:100:35 + --> $DIR/bad-reg.rs:96:35 | LL | asm!("/* {} */", in(vreg) x); | ^ diff --git a/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr b/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr index 4ff03d819e60f..5acc779b2308c 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr @@ -1,65 +1,65 @@ error: invalid register `s1`: s1 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("s1") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("gp") _); | ^^^^^^^^^^^ error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:94:18 + --> $DIR/bad-reg.rs:90:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:97:18 + --> $DIR/bad-reg.rs:93:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:100:26 + --> $DIR/bad-reg.rs:96:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:103:26 + --> $DIR/bad-reg.rs:99:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:94:27 + --> $DIR/bad-reg.rs:90:27 | LL | asm!("", in("v0") x); | ^ @@ -67,7 +67,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:97:28 + --> $DIR/bad-reg.rs:93:28 | LL | asm!("", out("v0") x); | ^ @@ -75,7 +75,7 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:100:35 + --> $DIR/bad-reg.rs:96:35 | LL | asm!("/* {} */", in(vreg) x); | ^ diff --git a/tests/ui/asm/riscv/bad-reg.riscv32i.stderr b/tests/ui/asm/riscv/bad-reg.riscv32i.stderr index fbe63eb0563c3..324a8ba50df8e 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32i.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32i.stderr @@ -1,89 +1,89 @@ error: invalid register `s1`: s1 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("s1") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("gp") _); | ^^^^^^^^^^^ error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:94:18 + --> $DIR/bad-reg.rs:90:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:97:18 + --> $DIR/bad-reg.rs:93:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:100:26 + --> $DIR/bad-reg.rs:96:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:103:26 + --> $DIR/bad-reg.rs:99:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:80:26 + --> $DIR/bad-reg.rs:75:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:77:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:79:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:87:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:94:27 + --> $DIR/bad-reg.rs:90:27 | LL | asm!("", in("v0") x); | ^ @@ -91,7 +91,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:97:28 + --> $DIR/bad-reg.rs:93:28 | LL | asm!("", out("v0") x); | ^ @@ -99,7 +99,7 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:100:35 + --> $DIR/bad-reg.rs:96:35 | LL | asm!("/* {} */", in(vreg) x); | ^ diff --git a/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr b/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr index 57664cfe893b7..e8b10dbb0d74d 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr @@ -1,65 +1,65 @@ error: invalid register `s1`: s1 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("s1") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("gp") _); | ^^^^^^^^^^^ error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:94:18 + --> $DIR/bad-reg.rs:90:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:97:18 + --> $DIR/bad-reg.rs:93:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:100:26 + --> $DIR/bad-reg.rs:96:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:103:26 + --> $DIR/bad-reg.rs:99:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: `d` target feature is not enabled - --> $DIR/bad-reg.rs:84:35 + --> $DIR/bad-reg.rs:79:35 | LL | asm!("/* {} */", in(freg) d); | ^ @@ -67,7 +67,7 @@ LL | asm!("/* {} */", in(freg) d); = note: this is required to use type `f64` with register class `freg` error: `d` target feature is not enabled - --> $DIR/bad-reg.rs:87:36 + --> $DIR/bad-reg.rs:82:36 | LL | asm!("/* {} */", out(freg) d); | ^ @@ -75,7 +75,7 @@ LL | asm!("/* {} */", out(freg) d); = note: this is required to use type `f64` with register class `freg` error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:94:27 + --> $DIR/bad-reg.rs:90:27 | LL | asm!("", in("v0") x); | ^ @@ -83,7 +83,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:97:28 + --> $DIR/bad-reg.rs:93:28 | LL | asm!("", out("v0") x); | ^ @@ -91,7 +91,7 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:100:35 + --> $DIR/bad-reg.rs:96:35 | LL | asm!("/* {} */", in(vreg) x); | ^ diff --git a/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr b/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr index 4ff03d819e60f..5acc779b2308c 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr @@ -1,65 +1,65 @@ error: invalid register `s1`: s1 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("s1") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("gp") _); | ^^^^^^^^^^^ error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:94:18 + --> $DIR/bad-reg.rs:90:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:97:18 + --> $DIR/bad-reg.rs:93:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:100:26 + --> $DIR/bad-reg.rs:96:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:103:26 + --> $DIR/bad-reg.rs:99:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:94:27 + --> $DIR/bad-reg.rs:90:27 | LL | asm!("", in("v0") x); | ^ @@ -67,7 +67,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:97:28 + --> $DIR/bad-reg.rs:93:28 | LL | asm!("", out("v0") x); | ^ @@ -75,7 +75,7 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:100:35 + --> $DIR/bad-reg.rs:96:35 | LL | asm!("/* {} */", in(vreg) x); | ^ diff --git a/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr b/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr index fbe63eb0563c3..324a8ba50df8e 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr @@ -1,89 +1,89 @@ error: invalid register `s1`: s1 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("s1") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("gp") _); | ^^^^^^^^^^^ error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:94:18 + --> $DIR/bad-reg.rs:90:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:97:18 + --> $DIR/bad-reg.rs:93:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:100:26 + --> $DIR/bad-reg.rs:96:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:103:26 + --> $DIR/bad-reg.rs:99:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:80:26 + --> $DIR/bad-reg.rs:75:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:77:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:79:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:87:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:94:27 + --> $DIR/bad-reg.rs:90:27 | LL | asm!("", in("v0") x); | ^ @@ -91,7 +91,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:97:28 + --> $DIR/bad-reg.rs:93:28 | LL | asm!("", out("v0") x); | ^ @@ -99,7 +99,7 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:100:35 + --> $DIR/bad-reg.rs:96:35 | LL | asm!("/* {} */", in(vreg) x); | ^ diff --git a/tests/ui/asm/riscv/bad-reg.rs b/tests/ui/asm/riscv/bad-reg.rs index 9332df0cc5830..1a3784f084a08 100644 --- a/tests/ui/asm/riscv/bad-reg.rs +++ b/tests/ui/asm/riscv/bad-reg.rs @@ -1,18 +1,13 @@ //@ add-minicore //@ revisions: riscv32i riscv32imafc riscv32gc riscv32e riscv64imac riscv64gc //@[riscv32i] compile-flags: --target riscv32i-unknown-none-elf -//@[riscv32i] needs-llvm-components: riscv //@[riscv32imafc] compile-flags: --target riscv32imafc-unknown-none-elf -//@[riscv32imafc] needs-llvm-components: riscv //@[riscv32gc] compile-flags: --target riscv32gc-unknown-linux-gnu -//@[riscv32gc] needs-llvm-components: riscv //@[riscv32e] compile-flags: --target riscv32e-unknown-none-elf -//@[riscv32e] needs-llvm-components: riscv //@[riscv64imac] compile-flags: --target riscv64imac-unknown-none-elf -//@[riscv64imac] needs-llvm-components: riscv //@[riscv64gc] compile-flags: --target riscv64gc-unknown-linux-gnu -//@[riscv64gc] needs-llvm-components: riscv //@ ignore-backends: gcc +//@ needs-llvm-components: riscv // Unlike riscv32e-registers.rs, this tests if the rustc can reject invalid registers // usage in the asm! API (in, out, inout, etc.). @@ -43,37 +38,37 @@ fn f() { asm!("", out("zero") _); //~^ ERROR invalid register `zero`: the zero register cannot be used as an operand for inline asm - asm!("", out("x16") _); + asm!("", in("x16") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x17") _); + asm!("", in("x17") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x18") _); + asm!("", in("x18") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x19") _); + asm!("", in("x19") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x20") _); + asm!("", in("x20") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x21") _); + asm!("", in("x21") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x22") _); + asm!("", in("x22") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x23") _); + asm!("", in("x23") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x24") _); + asm!("", in("x24") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x25") _); + asm!("", in("x25") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x26") _); + asm!("", in("x26") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x27") _); + asm!("", in("x27") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x28") _); + asm!("", in("x28") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x29") _); + asm!("", in("x29") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x30") _); + asm!("", in("x30") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature - asm!("", out("x31") _); + asm!("", in("x31") x); //[riscv32e]~^ ERROR register can't be used with the `e` target feature asm!("", out("f0") _); // ok @@ -91,6 +86,7 @@ fn f() { // Clobber-only registers // vreg asm!("", out("v0") _); // ok + asm!("", out("x31") _); // ok asm!("", in("v0") x); //~^ ERROR can only be used as a clobber //~| ERROR type `i32` cannot be used with this register class diff --git a/tests/ui/async-await/async-closures/c-fn-ptr-asyncfn.rs b/tests/ui/async-await/async-closures/c-fn-ptr-asyncfn.rs new file mode 100644 index 0000000000000..45d5302b1584d --- /dev/null +++ b/tests/ui/async-await/async-closures/c-fn-ptr-asyncfn.rs @@ -0,0 +1,21 @@ +//@ run-pass +//@ edition: 2021 + +use std::future::Future; +use std::pin::Pin; + +#[expect(improper_ctypes_definitions)] +pub extern "C" fn abi() -> Pin + 'static>> { + Box::pin(async {}) +} + +fn test(f: impl AsyncFn()) { + let _ = async { + f().await; + f().await; + }; +} + +fn main() { + test(abi); +} diff --git a/tests/ui/async-await/async-closures/fn-exception.rs b/tests/ui/async-await/async-closures/fn-exception.rs index 8ff63983e7932..71df2178ff0c4 100644 --- a/tests/ui/async-await/async-closures/fn-exception.rs +++ b/tests/ui/async-await/async-closures/fn-exception.rs @@ -15,5 +15,5 @@ fn test(f: impl AsyncFn()) {} fn main() { test(unsafety); //~ ERROR the trait bound - test(abi); //~ ERROR the trait bound + test(abi); } diff --git a/tests/ui/async-await/async-closures/fn-exception.stderr b/tests/ui/async-await/async-closures/fn-exception.stderr index aa74ed234df00..a3366dda5ba73 100644 --- a/tests/ui/async-await/async-closures/fn-exception.stderr +++ b/tests/ui/async-await/async-closures/fn-exception.stderr @@ -12,20 +12,6 @@ note: required by a bound in `test` LL | fn test(f: impl AsyncFn()) {} | ^^^^^^^^^ required by this bound in `test` -error[E0277]: the trait bound `extern "C" fn() -> Pin + 'static)>> {abi}: AsyncFn()` is not satisfied - --> $DIR/fn-exception.rs:18:10 - | -LL | test(abi); - | ---- ^^^ the trait `AsyncFn()` is not implemented for fn item `extern "C" fn() -> Pin + 'static)>> {abi}` - | | - | required by a bound introduced by this call - | -note: required by a bound in `test` - --> $DIR/fn-exception.rs:14:17 - | -LL | fn test(f: impl AsyncFn()) {} - | ^^^^^^^^^ required by this bound in `test` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/extern/extern-wrong-value-type.rs b/tests/ui/extern/extern-wrong-value-type.rs index 38a77a697fe51..3aa3539777cad 100644 --- a/tests/ui/extern/extern-wrong-value-type.rs +++ b/tests/ui/extern/extern-wrong-value-type.rs @@ -1,11 +1,13 @@ +//@ run-pass + extern "C" fn f() { } -fn is_fn(_: F) where F: Fn() {} +fn is_fn(f: F) where F: Fn() { + f(); +} fn main() { - // extern functions are extern "C" fn let _x: extern "C" fn() = f; // OK is_fn(f); - //~^ ERROR expected `Fn()` signature, found `extern "C" fn() {f}` } diff --git a/tests/ui/extern/extern-wrong-value-type.stderr b/tests/ui/extern/extern-wrong-value-type.stderr deleted file mode 100644 index 0330992e62f06..0000000000000 --- a/tests/ui/extern/extern-wrong-value-type.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: expected `Fn()` signature, found `extern "C" fn() {f}` - --> $DIR/extern-wrong-value-type.rs:9:11 - | -LL | is_fn(f); - | ----- ^ expected `Fn()` signature, found `extern "C" fn() {f}` - | | - | required by a bound introduced by this call - | - = help: the trait `Fn()` is not implemented for fn item `extern "C" fn() {f}` - = note: wrap the `extern "C" fn() {f}` in a closure with no arguments: `|| { /* code */ }` -note: required by a bound in `is_fn` - --> $DIR/extern-wrong-value-type.rs:4:28 - | -LL | fn is_fn(_: F) where F: Fn() {} - | ^^^^ required by this bound in `is_fn` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/feature-gates/feature-gate-doc_notable_trait.rs b/tests/ui/feature-gates/feature-gate-doc_notable_trait.rs index 1bc1028b9e0ef..b06b17650da30 100644 --- a/tests/ui/feature-gates/feature-gate-doc_notable_trait.rs +++ b/tests/ui/feature-gates/feature-gate-doc_notable_trait.rs @@ -4,5 +4,7 @@ trait SomeTrait {} fn main() { #[doc(notable_trait)] //~^ ERROR the `doc(notable_trait)` attribute is experimental [E0658] + //~| WARN `#![doc(notable_trait)]` must be a trait attribute + //~| WARN this was previously accepted println!(); } diff --git a/tests/ui/feature-gates/feature-gate-doc_notable_trait.stderr b/tests/ui/feature-gates/feature-gate-doc_notable_trait.stderr index 632c91b929185..a7be03385fc3f 100644 --- a/tests/ui/feature-gates/feature-gate-doc_notable_trait.stderr +++ b/tests/ui/feature-gates/feature-gate-doc_notable_trait.stderr @@ -8,6 +8,15 @@ LL | #[doc(notable_trait)] = help: add `#![feature(doc_notable_trait)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +warning: `#![doc(notable_trait)]` must be a trait attribute + --> $DIR/feature-gate-doc_notable_trait.rs:5:11 + | +LL | #[doc(notable_trait)] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: `#[warn(invalid_doc_attributes)]` on by default + error[E0658]: the `doc(notable_trait)` attribute is experimental --> $DIR/feature-gate-doc_notable_trait.rs:1:7 | @@ -18,6 +27,6 @@ LL | #[doc(notable_trait)] = help: add `#![feature(doc_notable_trait)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs index 6dae9384ecba3..d212486d995f9 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs @@ -1,5 +1,5 @@ -fn produces_string() -> Option { - Some("my cool string".to_owned()) +fn produces_string() -> Option<&'static str> { + Some("my cool string") } fn takes_str_but_too_many_refs(_: &&str) -> Option<()> { @@ -24,9 +24,8 @@ fn main() { let _ = produces_string().and_then(takes_str_but_too_many_refs); //~^ ERROR type mismatch in function arguments let _ = produces_string().and_then(takes_str_but_wrong_abi); - //~^ ERROR expected `FnOnce(String)` signature, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` let _ = produces_string().and_then(takes_str_but_unsafe); - //~^ ERROR expected `FnOnce(String)` signature, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` + //~^ ERROR expected `FnOnce(&str)` signature, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` let _ = produces_string().and_then(no_args); //~^ ERROR function is expected to take 1 argument, but it takes 0 arguments let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr index 56f6437f77b54..1a3f1ed9a9ff4 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr @@ -9,42 +9,35 @@ LL | let _ = produces_string().and_then(takes_str_but_too_many_refs); | | | required by a bound introduced by this call | - = note: expected function signature `fn(String) -> _` - found function signature `fn(&&str) -> _` + = note: expected function signature `fn(&_) -> _` + found function signature `fn(&&_) -> _` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure | -LL | let _ = produces_string().and_then(|arg0: String| takes_str_but_too_many_refs(/* &&str */)); - | ++++++++++++++ +++++++++++++ - -error[E0277]: expected `FnOnce(String)` signature, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` - --> $DIR/suggest-option-asderef-unfixable.rs:26:40 +LL | let _ = produces_string().and_then(|arg0: &str| takes_str_but_too_many_refs(&arg0)); + | ++++++++++++ +++++++ +help: consider adjusting the signature so it does not borrow its argument | -LL | let _ = produces_string().and_then(takes_str_but_wrong_abi); - | -------- ^^^^^^^^^^^^^^^^^^^^^^^ expected `FnOnce(String)` signature, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` - | | - | required by a bound introduced by this call +LL - fn takes_str_but_too_many_refs(_: &&str) -> Option<()> { +LL + fn takes_str_but_too_many_refs(_: &str) -> Option<()> { | - = help: the trait `FnOnce(String)` is not implemented for fn item `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` -note: required by a bound in `Option::::and_then` - --> $SRC_DIR/core/src/option.rs:LL:COL -error[E0277]: expected `FnOnce(String)` signature, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` - --> $DIR/suggest-option-asderef-unfixable.rs:28:40 +error[E0277]: expected `FnOnce(&str)` signature, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` + --> $DIR/suggest-option-asderef-unfixable.rs:27:40 | LL | let _ = produces_string().and_then(takes_str_but_unsafe); | -------- ^^^^^^^^^^^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }` | | | required by a bound introduced by this call | - = help: the trait `FnOnce(String)` is not implemented for fn item `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` + = help: the trait `FnOnce(&str)` is not implemented for fn item `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` = note: unsafe function cannot be called generically without an unsafe block note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL error[E0593]: function is expected to take 1 argument, but it takes 0 arguments - --> $DIR/suggest-option-asderef-unfixable.rs:30:40 + --> $DIR/suggest-option-asderef-unfixable.rs:29:40 | LL | fn no_args() -> Option<()> { | -------------------------- takes 0 arguments @@ -58,7 +51,7 @@ note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef-unfixable.rs:32:45 + --> $DIR/suggest-option-asderef-unfixable.rs:31:45 | LL | fn takes_str_but_too_many_refs(_: &&str) -> Option<()> { | ------------------------------------------------------ found signature defined here @@ -77,7 +70,7 @@ help: consider wrapping the function in a closure LL | let _ = Some(TypeWithoutDeref).and_then(|arg0: TypeWithoutDeref| takes_str_but_too_many_refs(/* &&str */)); | ++++++++++++++++++++++++ +++++++++++++ -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0277, E0593, E0631. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/fn-trait.rs b/tests/ui/traits/next-solver/fn-trait.rs index 9d3d5ce904c87..113396845b81d 100644 --- a/tests/ui/traits/next-solver/fn-trait.rs +++ b/tests/ui/traits/next-solver/fn-trait.rs @@ -20,9 +20,7 @@ fn main() { require_fn(f as unsafe fn() -> i32); //~^ ERROR: expected `Fn()` signature, found `unsafe fn() -> i32` require_fn(g); - //~^ ERROR: expected `Fn()` signature, found `extern "C" fn() -> i32 {g}` require_fn(g as extern "C" fn() -> i32); - //~^ ERROR: expected `Fn()` signature, found `extern "C" fn() -> i32` require_fn(h); //~^ ERROR: expected `Fn()` signature, found `unsafe fn() -> i32 {h}` } diff --git a/tests/ui/traits/next-solver/fn-trait.stderr b/tests/ui/traits/next-solver/fn-trait.stderr index ec3a4dc54cca8..73bf3a77b6dfd 100644 --- a/tests/ui/traits/next-solver/fn-trait.stderr +++ b/tests/ui/traits/next-solver/fn-trait.stderr @@ -15,40 +15,8 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error[E0277]: expected `Fn()` signature, found `extern "C" fn() -> i32 {g}` - --> $DIR/fn-trait.rs:22:16 - | -LL | require_fn(g); - | ---------- ^ expected `Fn()` signature, found `extern "C" fn() -> i32 {g}` - | | - | required by a bound introduced by this call - | - = help: the trait `Fn()` is not implemented for fn item `extern "C" fn() -> i32 {g}` - = note: wrap the `extern "C" fn() -> i32 {g}` in a closure with no arguments: `|| { /* code */ }` -note: required by a bound in `require_fn` - --> $DIR/fn-trait.rs:3:23 - | -LL | fn require_fn(_: impl Fn() -> i32) {} - | ^^^^^^^^^^^ required by this bound in `require_fn` - -error[E0277]: expected `Fn()` signature, found `extern "C" fn() -> i32` - --> $DIR/fn-trait.rs:24:16 - | -LL | require_fn(g as extern "C" fn() -> i32); - | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Fn()` signature, found `extern "C" fn() -> i32` - | | - | required by a bound introduced by this call - | - = help: the trait `Fn()` is not implemented for `extern "C" fn() -> i32` - = note: wrap the `extern "C" fn() -> i32` in a closure with no arguments: `|| { /* code */ }` -note: required by a bound in `require_fn` - --> $DIR/fn-trait.rs:3:23 - | -LL | fn require_fn(_: impl Fn() -> i32) {} - | ^^^^^^^^^^^ required by this bound in `require_fn` - error[E0277]: expected `Fn()` signature, found `unsafe fn() -> i32 {h}` - --> $DIR/fn-trait.rs:26:16 + --> $DIR/fn-trait.rs:24:16 | LL | require_fn(h); | ---------- ^ call the function in a closure: `|| unsafe { /* code */ }` @@ -64,6 +32,6 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/unboxed-closures/unboxed-closures-abi.rs b/tests/ui/unboxed-closures/unboxed-closures-abi.rs new file mode 100644 index 0000000000000..5aabc12971e63 --- /dev/null +++ b/tests/ui/unboxed-closures/unboxed-closures-abi.rs @@ -0,0 +1,40 @@ +//@ run-pass +//! Test implementation of `Fn` traits for functions with non-`Rust` ABI + +use std::ops::{Fn, FnMut, FnOnce}; + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct Foo(i32, i32); + +extern "C" fn square(foo: Foo) -> Foo { + Foo(foo.0 * foo.1, foo.0 - foo.1) +} + +fn call_it Foo>(f: &F, i: Foo) -> Foo { + f(i) +} + +fn call_it_mut Foo>(f: &mut F, i: Foo) -> Foo { + f(i) +} + +fn call_it_once Foo>(f: F, i: Foo) -> Foo { + f(i) +} + +fn main() { + assert_eq!(call_it(&square, Foo(20, 10)), Foo(200, 10)); + assert_eq!(call_it_mut(&mut square, Foo(21, 9)), Foo(189, 12)); + assert_eq!(call_it_once(square, Foo(18, -3)), Foo(-54, 21)); + + let mut square_ptr: extern "C" fn(Foo) -> Foo = square; + assert_eq!(call_it(&square_ptr, Foo(30, 11)), Foo(330, 19)); + assert_eq!(call_it_mut(&mut square_ptr, Foo(273, -1)), Foo(-273, 274)); + assert_eq!(call_it_once(square_ptr, Foo(27, 27)), Foo(729, 0)); + + let mut square_dyn: Box Foo> = Box::new(square); + assert_eq!(call_it(&*square_dyn, Foo(1, 3)), Foo(3, -2)); + assert_eq!(call_it_mut(&mut *square_dyn, Foo(5, 25)), Foo(125, -20)); + assert_eq!(call_it_once(square_dyn, Foo(9, 2)), Foo(18, 7)); +} diff --git a/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.rs b/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.rs deleted file mode 100644 index dd76c597d28ad..0000000000000 --- a/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Tests that unsafe extern fn pointers do not implement any Fn traits. - -use std::ops::{Fn, FnMut, FnOnce}; - -extern "C" fn square(x: &isize) -> isize { - (*x) * (*x) -} - -fn call_it isize>(_: &F, _: isize) -> isize { - 0 -} -fn call_it_mut isize>(_: &mut F, _: isize) -> isize { - 0 -} -fn call_it_once isize>(_: F, _: isize) -> isize { - 0 -} - -fn a() { - let x = call_it(&square, 22); - //~^ ERROR E0277 -} - -fn b() { - let y = call_it_mut(&mut square, 22); - //~^ ERROR E0277 -} - -fn c() { - let z = call_it_once(square, 22); - //~^ ERROR E0277 -} - -fn main() {} diff --git a/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr b/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr deleted file mode 100644 index 80c368fa46b5f..0000000000000 --- a/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr +++ /dev/null @@ -1,48 +0,0 @@ -error[E0277]: expected `Fn(&isize)` signature, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` - --> $DIR/unboxed-closures-wrong-abi.rs:20:21 - | -LL | let x = call_it(&square, 22); - | ------- ^^^^^^^ expected `Fn(&isize)` signature, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` - | | - | required by a bound introduced by this call - | - = help: the trait `for<'a> Fn(&'a isize)` is not implemented for fn item `for<'a> extern "C" fn(&'a isize) -> isize {square}` -note: required by a bound in `call_it` - --> $DIR/unboxed-closures-wrong-abi.rs:9:15 - | -LL | fn call_it isize>(_: &F, _: isize) -> isize { - | ^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it` - -error[E0277]: expected `FnMut(&isize)` signature, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` - --> $DIR/unboxed-closures-wrong-abi.rs:25:25 - | -LL | let y = call_it_mut(&mut square, 22); - | ----------- ^^^^^^^^^^^ expected `FnMut(&isize)` signature, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` - | | - | required by a bound introduced by this call - | - = help: the trait `for<'a> FnMut(&'a isize)` is not implemented for fn item `for<'a> extern "C" fn(&'a isize) -> isize {square}` -note: required by a bound in `call_it_mut` - --> $DIR/unboxed-closures-wrong-abi.rs:12:19 - | -LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { - | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_mut` - -error[E0277]: expected `FnOnce(&isize)` signature, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` - --> $DIR/unboxed-closures-wrong-abi.rs:30:26 - | -LL | let z = call_it_once(square, 22); - | ------------ ^^^^^^ expected `FnOnce(&isize)` signature, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` - | | - | required by a bound introduced by this call - | - = help: the trait `for<'a> FnOnce(&'a isize)` is not implemented for fn item `for<'a> extern "C" fn(&'a isize) -> isize {square}` -note: required by a bound in `call_it_once` - --> $DIR/unboxed-closures-wrong-abi.rs:15:20 - | -LL | fn call_it_once isize>(_: F, _: isize) -> isize { - | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_once` - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0277`.