diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index d4407dbf7be77..fb2ccefe12a18 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2117,10 +2117,9 @@ pub struct MacroDef { #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic, Walkable)] pub struct EiiExternTarget { - /// path to the extern item we're targetting + /// path to the extern item we're targeting pub extern_item_path: Path, pub impl_unsafe: bool, - pub span: Span, } #[derive(Clone, Encodable, Decodable, Debug, Copy, Hash, Eq, PartialEq)] @@ -3813,6 +3812,19 @@ pub struct Fn { pub struct EiiImpl { pub node_id: NodeId, pub eii_macro_path: Path, + /// This field is an implementation detail that prevents a lot of bugs. + /// See for an example. + /// + /// The problem is, that if we generate a declaration *together* with its default, + /// we generate both a declaration and an implementation. The generated implementation + /// uses the same mechanism to register itself as a user-defined implementation would, + /// despite being invisible to users. What does happen is a name resolution step. + /// The invisible default implementation has to find the declaration. + /// Both are generated at the same time, so we can skip that name resolution step. + /// + /// This field is that shortcut: we prefill the extern target to skip a name resolution step, + /// making sure it never fails. It'd be awful UX if we fail name resolution in code invisible to the user. + pub known_eii_macro_resolution: Option, pub impl_safety: Safety, pub span: Span, pub inner_span: Span, diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index f8b98a5ece40e..3a7308ef7c97a 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -2,7 +2,7 @@ use rustc_abi::ExternAbi; use rustc_ast::visit::AssocCtxt; use rustc_ast::*; use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err}; -use rustc_hir::attrs::{AttributeKind, EiiDecl}; +use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImplResolution}; use rustc_hir::def::{DefKind, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_hir::{ @@ -134,6 +134,56 @@ impl<'hir> LoweringContext<'_, 'hir> { } } + fn lower_eii_extern_target( + &mut self, + id: NodeId, + eii_name: Ident, + EiiExternTarget { extern_item_path, impl_unsafe }: &EiiExternTarget, + ) -> Option { + self.lower_path_simple_eii(id, extern_item_path).map(|did| EiiDecl { + eii_extern_target: did, + impl_unsafe: *impl_unsafe, + name: eii_name, + }) + } + + fn lower_eii_impl( + &mut self, + EiiImpl { + node_id, + eii_macro_path, + impl_safety, + span, + inner_span, + is_default, + known_eii_macro_resolution, + }: &EiiImpl, + ) -> hir::attrs::EiiImpl { + let resolution = if let Some(target) = known_eii_macro_resolution + && let Some(decl) = self.lower_eii_extern_target( + *node_id, + // the expect is ok here since we always generate this path in the eii macro. + eii_macro_path.segments.last().expect("at least one segment").ident, + target, + ) { + EiiImplResolution::Known(decl) + } else if let Some(macro_did) = self.lower_path_simple_eii(*node_id, eii_macro_path) { + EiiImplResolution::Macro(macro_did) + } else { + EiiImplResolution::Error( + self.dcx().span_delayed_bug(*span, "eii never resolved without errors given"), + ) + }; + + hir::attrs::EiiImpl { + span: self.lower_span(*span), + inner_span: self.lower_span(*inner_span), + impl_marked_unsafe: self.lower_safety(*impl_safety, hir::Safety::Safe).is_unsafe(), + is_default: *is_default, + resolution, + } + } + fn generate_extra_attrs_for_item_kind( &mut self, id: NodeId, @@ -143,49 +193,14 @@ impl<'hir> LoweringContext<'_, 'hir> { ItemKind::Fn(box Fn { eii_impls, .. }) if eii_impls.is_empty() => Vec::new(), ItemKind::Fn(box Fn { eii_impls, .. }) => { vec![hir::Attribute::Parsed(AttributeKind::EiiImpls( - eii_impls - .iter() - .flat_map( - |EiiImpl { - node_id, - eii_macro_path, - impl_safety, - span, - inner_span, - is_default, - }| { - self.lower_path_simple_eii(*node_id, eii_macro_path).map(|did| { - hir::attrs::EiiImpl { - eii_macro: did, - span: self.lower_span(*span), - inner_span: self.lower_span(*inner_span), - impl_marked_unsafe: self - .lower_safety(*impl_safety, hir::Safety::Safe) - .is_unsafe(), - is_default: *is_default, - } - }) - }, - ) - .collect(), + eii_impls.iter().map(|i| self.lower_eii_impl(i)).collect(), ))] } - ItemKind::MacroDef( - _, - MacroDef { - eii_extern_target: Some(EiiExternTarget { extern_item_path, impl_unsafe, span }), - .. - }, - ) => self - .lower_path_simple_eii(id, extern_item_path) - .map(|did| { - vec![hir::Attribute::Parsed(AttributeKind::EiiExternTarget(EiiDecl { - eii_extern_target: did, - impl_unsafe: *impl_unsafe, - span: self.lower_span(*span), - }))] - }) + ItemKind::MacroDef(name, MacroDef { eii_extern_target: Some(target), .. }) => self + .lower_eii_extern_target(id, *name, target) + .map(|decl| vec![hir::Attribute::Parsed(AttributeKind::EiiExternTarget(decl))]) .unwrap_or_default(), + ItemKind::ExternCrate(..) | ItemKind::Use(..) | ItemKind::Static(..) diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 9049639925ddb..2d67608609e39 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -103,19 +103,19 @@ fn eii_( // span of the declaring item without attributes let item_span = func.sig.span; - // span of the eii attribute and the item below it, i.e. the full declaration - let decl_span = eii_attr_span.to(item_span); let foreign_item_name = func.ident; let mut return_items = Vec::new(); if func.body.is_some() { return_items.push(Box::new(generate_default_impl( + ecx, &func, impl_unsafe, macro_name, eii_attr_span, item_span, + foreign_item_name, ))) } @@ -133,7 +133,6 @@ fn eii_( macro_name, foreign_item_name, impl_unsafe, - decl_span, ))); return_items.into_iter().map(wrap_item).collect() @@ -187,11 +186,13 @@ fn filter_attrs_for_multiple_eii_attr( } fn generate_default_impl( + ecx: &mut ExtCtxt<'_>, func: &ast::Fn, impl_unsafe: bool, macro_name: Ident, eii_attr_span: Span, item_span: Span, + foreign_item_name: Ident, ) -> ast::Item { // FIXME: re-add some original attrs let attrs = ThinVec::new(); @@ -208,6 +209,21 @@ fn generate_default_impl( }, span: eii_attr_span, is_default: true, + known_eii_macro_resolution: Some(ast::EiiExternTarget { + extern_item_path: ast::Path { + span: foreign_item_name.span, + segments: thin_vec![ + ast::PathSegment { + ident: Ident::from_str_and_span("super", foreign_item_name.span,), + id: DUMMY_NODE_ID, + args: None + }, + ast::PathSegment { ident: foreign_item_name, id: DUMMY_NODE_ID, args: None }, + ], + tokens: None, + }, + impl_unsafe, + }), }); ast::Item { @@ -236,18 +252,66 @@ fn generate_default_impl( stmts: thin_vec![ast::Stmt { id: DUMMY_NODE_ID, kind: ast::StmtKind::Item(Box::new(ast::Item { - attrs, + attrs: ThinVec::new(), id: DUMMY_NODE_ID, span: item_span, vis: ast::Visibility { - span: eii_attr_span, + span: item_span, kind: ast::VisibilityKind::Inherited, tokens: None }, - kind: ItemKind::Fn(Box::new(default_func)), + kind: ItemKind::Mod( + ast::Safety::Default, + Ident::from_str_and_span("dflt", item_span), + ast::ModKind::Loaded( + thin_vec![ + Box::new(ast::Item { + attrs: thin_vec![ecx.attr_nested_word( + sym::allow, + sym::unused_imports, + item_span + ),], + id: DUMMY_NODE_ID, + span: item_span, + vis: ast::Visibility { + span: eii_attr_span, + kind: ast::VisibilityKind::Inherited, + tokens: None + }, + kind: ItemKind::Use(ast::UseTree { + prefix: ast::Path::from_ident( + Ident::from_str_and_span( + "super", item_span, + ) + ), + kind: ast::UseTreeKind::Glob, + span: item_span, + }), + tokens: None, + }), + Box::new(ast::Item { + attrs, + id: DUMMY_NODE_ID, + span: item_span, + vis: ast::Visibility { + span: eii_attr_span, + kind: ast::VisibilityKind::Inherited, + tokens: None + }, + kind: ItemKind::Fn(Box::new(default_func)), + tokens: None, + }), + ], + ast::Inline::Yes, + ast::ModSpans { + inner_span: item_span, + inject_use_span: item_span, + } + ) + ), tokens: None, })), - span: eii_attr_span + span: eii_attr_span, }], id: DUMMY_NODE_ID, rules: ast::BlockCheckMode::Default, @@ -352,7 +416,6 @@ fn generate_attribute_macro_to_implement( macro_name: Ident, foreign_item_name: Ident, impl_unsafe: bool, - decl_span: Span, ) -> ast::Item { let mut macro_attrs = ThinVec::new(); @@ -394,7 +457,6 @@ fn generate_attribute_macro_to_implement( eii_extern_target: Some(ast::EiiExternTarget { extern_item_path: ast::Path::from_ident(foreign_item_name), impl_unsafe, - span: decl_span, }), }, ), @@ -451,7 +513,7 @@ pub(crate) fn eii_extern_target( false }; - d.eii_extern_target = Some(EiiExternTarget { extern_item_path, impl_unsafe, span }); + d.eii_extern_target = Some(EiiExternTarget { extern_item_path, impl_unsafe }); // Return the original item and the new methods. vec![item] @@ -508,6 +570,7 @@ pub(crate) fn eii_shared_macro( impl_safety: meta_item.unsafety, span, is_default, + known_eii_macro_resolution: None, }); vec![item] diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 3ce28612ddfcb..1878f4043ee84 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -111,7 +111,7 @@ impl CodegenCx<'_, '_> { } } - /// Whether a definition or declaration can be assumed to be local to a group of + /// A definition or declaration can be assumed to be local to a group of /// libraries that form a single DSO or executable. /// Marks the local as DSO if so. pub(crate) fn assume_dso_local(&self, llval: &llvm::Value, is_declaration: bool) -> bool { @@ -153,7 +153,7 @@ impl CodegenCx<'_, '_> { return false; } - // With pie relocation model calls of functions defined in the translation + // With pie relocation model, calls of functions defined in the translation // unit can use copy relocations. if self.tcx.sess.relocation_model() == RelocModel::Pie && !is_declaration { return true; diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 74f4662118b6d..7dda824e2b18a 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -3,7 +3,9 @@ use std::str::FromStr; use rustc_abi::{Align, ExternAbi}; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr}; -use rustc_hir::attrs::{AttributeKind, InlineAttr, Linkage, RtsanSetting, UsedBy}; +use rustc_hir::attrs::{ + AttributeKind, EiiImplResolution, InlineAttr, Linkage, RtsanSetting, UsedBy, +}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{self as hir, Attribute, LangItem, find_attr, lang_items}; @@ -285,11 +287,23 @@ fn process_builtin_attrs( } AttributeKind::EiiImpls(impls) => { for i in impls { - let extern_item = find_attr!( - tcx.get_all_attrs(i.eii_macro), - AttributeKind::EiiExternTarget(target) => target.eii_extern_target - ) - .expect("eii should have declaration macro with extern target attribute"); + let extern_item = match i.resolution { + EiiImplResolution::Macro(def_id) => { + let Some(extern_item) = find_attr!( + tcx.get_all_attrs(def_id), + AttributeKind::EiiExternTarget(target) => target.eii_extern_target + ) else { + tcx.dcx().span_delayed_bug( + i.span, + "resolved to something that's not an EII", + ); + continue; + }; + extern_item + } + EiiImplResolution::Known(decl) => decl.eii_extern_target, + EiiImplResolution::Error(_eg) => continue, + }; // this is to prevent a bug where a single crate defines both the default and explicit implementation // for an EII. In that case, both of them may be part of the same final object file. I'm not 100% sure @@ -302,7 +316,7 @@ fn process_builtin_attrs( // iterate over all implementations *in the current crate* // (this is ok since we generate codegen fn attrs in the local crate) // if any of them is *not default* then don't emit the alias. - && tcx.externally_implementable_items(LOCAL_CRATE).get(&i.eii_macro).expect("at least one").1.iter().any(|(_, imp)| !imp.is_default) + && tcx.externally_implementable_items(LOCAL_CRATE).get(&extern_item).expect("at least one").1.iter().any(|(_, imp)| !imp.is_default) { continue; } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index fa8998f0546d1..23201eff455fe 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -11,7 +11,7 @@ use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; use rustc_span::def_id::DefId; use rustc_span::hygiene::Transparency; -use rustc_span::{Ident, Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; pub use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; @@ -19,9 +19,22 @@ use crate::attrs::pretty_printing::PrintAttribute; use crate::limit::Limit; use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability}; +#[derive(Copy, Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum EiiImplResolution { + /// Usually, finding the extern item that an EII implementation implements means finding + /// the defid of the associated attribute macro, and looking at *its* attributes to find + /// what foreign item its associated with. + Macro(DefId), + /// Sometimes though, we already know statically and can skip some name resolution. + /// Stored together with the eii's name for diagnostics. + Known(EiiDecl), + /// For when resolution failed, but we want to continue compilation + Error(ErrorGuaranteed), +} + #[derive(Copy, Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] pub struct EiiImpl { - pub eii_macro: DefId, + pub resolution: EiiImplResolution, pub impl_marked_unsafe: bool, pub span: Span, pub inner_span: Span, @@ -33,7 +46,7 @@ pub struct EiiDecl { pub eii_extern_target: DefId, /// whether or not it is unsafe to implement this EII pub impl_unsafe: bool, - pub span: Span, + pub name: Ident, } #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)] diff --git a/compiler/rustc_hir_analysis/src/check/compare_eii.rs b/compiler/rustc_hir_analysis/src/check/compare_eii.rs index 4e7f47a92108f..d8afee9aafc98 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_eii.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_eii.rs @@ -32,21 +32,21 @@ use crate::errors::{EiiWithGenerics, LifetimesOrBoundsMismatchOnEii}; pub(crate) fn compare_eii_function_types<'tcx>( tcx: TyCtxt<'tcx>, external_impl: LocalDefId, - declaration: DefId, + foreign_item: DefId, eii_name: Symbol, eii_attr_span: Span, ) -> Result<(), ErrorGuaranteed> { - check_is_structurally_compatible(tcx, external_impl, declaration, eii_name, eii_attr_span)?; + check_is_structurally_compatible(tcx, external_impl, foreign_item, eii_name, eii_attr_span)?; let external_impl_span = tcx.def_span(external_impl); let cause = ObligationCause::new( external_impl_span, external_impl, - ObligationCauseCode::CompareEii { external_impl, declaration }, + ObligationCauseCode::CompareEii { external_impl, declaration: foreign_item }, ); // FIXME(eii): even if we don't support generic functions, we should support explicit outlive bounds here - let param_env = tcx.param_env(declaration); + let param_env = tcx.param_env(foreign_item); let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let ocx = ObligationCtxt::new_with_diagnostics(infcx); @@ -62,7 +62,7 @@ pub(crate) fn compare_eii_function_types<'tcx>( let mut wf_tys = FxIndexSet::default(); let norm_cause = ObligationCause::misc(external_impl_span, external_impl); - let declaration_sig = tcx.fn_sig(declaration).instantiate_identity(); + let declaration_sig = tcx.fn_sig(foreign_item).instantiate_identity(); let declaration_sig = tcx.liberate_late_bound_regions(external_impl.into(), declaration_sig); debug!(?declaration_sig); @@ -103,7 +103,7 @@ pub(crate) fn compare_eii_function_types<'tcx>( cause, param_env, terr, - (declaration, declaration_sig), + (foreign_item, declaration_sig), (external_impl, external_impl_sig), eii_attr_span, eii_name, diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 725294dfd3771..7ec4110f80b90 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -6,7 +6,7 @@ use rustc_abi::{ExternAbi, ScalableElt}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; -use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl}; +use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl, EiiImplResolution}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; @@ -1196,27 +1196,28 @@ fn check_item_fn( fn check_eiis(tcx: TyCtxt<'_>, def_id: LocalDefId) { // does the function have an EiiImpl attribute? that contains the defid of a *macro* // that was used to mark the implementation. This is a two step process. - for EiiImpl { eii_macro, span, .. } in + for EiiImpl { resolution, span, .. } in find_attr!(tcx.get_all_attrs(def_id), AttributeKind::EiiImpls(impls) => impls) .into_iter() .flatten() { - // we expect this macro to have the `EiiMacroFor` attribute, that points to a function - // signature that we'd like to compare the function we're currently checking with - if let Some(eii_extern_target) = find_attr!(tcx.get_all_attrs(*eii_macro), AttributeKind::EiiExternTarget(EiiDecl {eii_extern_target, ..}) => *eii_extern_target) - { - let _ = compare_eii_function_types( - tcx, - def_id, - eii_extern_target, - tcx.item_name(*eii_macro), - *span, - ); - } else { - panic!( - "EII impl macro {eii_macro:?} did not have an eii extern target attribute pointing to a foreign function" - ) - } + let (foreign_item, name) = match resolution { + EiiImplResolution::Macro(def_id) => { + // we expect this macro to have the `EiiMacroFor` attribute, that points to a function + // signature that we'd like to compare the function we're currently checking with + if let Some(foreign_item) = find_attr!(tcx.get_all_attrs(*def_id), AttributeKind::EiiExternTarget(EiiDecl {eii_extern_target: t, ..}) => *t) + { + (foreign_item, tcx.item_name(*def_id)) + } else { + tcx.dcx().span_delayed_bug(*span, "resolved to something that's not an EII"); + continue; + } + } + EiiImplResolution::Known(decl) => (decl.eii_extern_target, decl.name.name), + EiiImplResolution::Error(_eg) => continue, + }; + + let _ = compare_eii_function_types(tcx, def_id, foreign_item, name, *span); } } diff --git a/compiler/rustc_metadata/src/eii.rs b/compiler/rustc_metadata/src/eii.rs index 5558ff9308512..b06ac84813975 100644 --- a/compiler/rustc_metadata/src/eii.rs +++ b/compiler/rustc_metadata/src/eii.rs @@ -1,5 +1,5 @@ use rustc_data_structures::fx::FxIndexMap; -use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl}; +use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl, EiiImplResolution}; use rustc_hir::def_id::DefId; use rustc_hir::find_attr; use rustc_middle::query::LocalCrate; @@ -9,7 +9,7 @@ use rustc_middle::ty::TyCtxt; pub(crate) type EiiMapEncodedKeyValue = (DefId, (EiiDecl, Vec<(DefId, EiiImpl)>)); pub(crate) type EiiMap = FxIndexMap< - DefId, // the defid of the macro that declared the eii + DefId, // the defid of the foreign item associated with the eii ( // the corresponding declaration EiiDecl, @@ -28,18 +28,34 @@ pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap for i in find_attr!(tcx.get_all_attrs(id), AttributeKind::EiiImpls(e) => e).into_iter().flatten() { - eiis.entry(i.eii_macro) - .or_insert_with(|| { + let decl = match i.resolution { + EiiImplResolution::Macro(macro_defid) => { // find the decl for this one if it wasn't in yet (maybe it's from the local crate? not very useful but not illegal) - (find_attr!(tcx.get_all_attrs(i.eii_macro), AttributeKind::EiiExternTarget(d) => *d).unwrap(), Default::default()) - }).1.insert(id.into(), *i); + let Some(decl) = find_attr!(tcx.get_all_attrs(macro_defid), AttributeKind::EiiExternTarget(d) => *d) + else { + // skip if it doesn't have eii_extern_target (if we resolved to another macro that's not an EII) + tcx.dcx() + .span_delayed_bug(i.span, "resolved to something that's not an EII"); + continue; + }; + decl + } + EiiImplResolution::Known(decl) => decl, + EiiImplResolution::Error(_eg) => continue, + }; + + // FIXME(eii) remove extern target from encoded decl + eiis.entry(decl.eii_extern_target) + .or_insert_with(|| (decl, Default::default())) + .1 + .insert(id.into(), *i); } // if we find a new declaration, add it to the list without a known implementation if let Some(decl) = find_attr!(tcx.get_all_attrs(id), AttributeKind::EiiExternTarget(d) => *d) { - eiis.entry(id.into()).or_insert((decl, Default::default())); + eiis.entry(decl.eii_extern_target).or_insert((decl, Default::default())); } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index c85327dc3db19..eedb88783ec0e 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1657,9 +1657,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { empty_proc_macro!(self); let externally_implementable_items = self.tcx.externally_implementable_items(LOCAL_CRATE); - self.lazy_array(externally_implementable_items.iter().map(|(decl_did, (decl, impls))| { - (*decl_did, (decl.clone(), impls.iter().map(|(impl_did, i)| (*impl_did, *i)).collect())) - })) + self.lazy_array(externally_implementable_items.iter().map( + |(foreign_item, (decl, impls))| { + ( + *foreign_item, + (decl.clone(), impls.iter().map(|(impl_did, i)| (*impl_did, *i)).collect()), + ) + }, + )) } #[instrument(level = "trace", skip(self))] diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index ee21d4fbceadc..21a6c4d653bc5 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -19,14 +19,14 @@ pub(crate) fn emit_inline_always_target_feature_diagnostic<'a, 'tcx>( caller_def_id: DefId, callee_only: &[&'a str], ) { - let callee = tcx.def_path_str(callee_def_id); - let caller = tcx.def_path_str(caller_def_id); - tcx.node_span_lint( lint::builtin::INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES, tcx.local_def_id_to_hir_id(caller_def_id.as_local().unwrap()), call_span, |lint| { + let callee = tcx.def_path_str(callee_def_id); + let caller = tcx.def_path_str(caller_def_id); + lint.primary_message(format!( "call to `#[inline(always)]`-annotated `{callee}` \ requires the same target features to be inlined" diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f1cbb72554d2f..2d3c5c7e48a0b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -21,8 +21,8 @@ use rustc_feature::{ BuiltinAttribute, }; use rustc_hir::attrs::{ - AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, InlineAttr, MirDialect, MirPhase, - ReprAttr, SanitizerSet, + AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr, + MirDialect, MirPhase, ReprAttr, SanitizerSet, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalModDefId; @@ -506,7 +506,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } fn check_eii_impl(&self, impls: &[EiiImpl], target: Target) { - for EiiImpl { span, inner_span, eii_macro, impl_marked_unsafe, is_default: _ } in impls { + for EiiImpl { span, inner_span, resolution, impl_marked_unsafe, is_default: _ } in impls { match target { Target::Fn => {} _ => { @@ -514,7 +514,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - if find_attr!(self.tcx.get_all_attrs(*eii_macro), AttributeKind::EiiExternTarget(EiiDecl { impl_unsafe, .. }) if *impl_unsafe) + if let EiiImplResolution::Macro(eii_macro) = resolution + && find_attr!(self.tcx.get_all_attrs(*eii_macro), AttributeKind::EiiExternTarget(EiiDecl { impl_unsafe, .. }) if *impl_unsafe) && !impl_marked_unsafe { self.dcx().emit_err(errors::EiiImplRequiresUnsafe { @@ -758,9 +759,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if let Some(impls) = find_attr!(attrs, AttributeKind::EiiImpls(impls) => impls) { let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); for i in impls { + let name = match i.resolution { + EiiImplResolution::Macro(def_id) => self.tcx.item_name(def_id), + EiiImplResolution::Known(decl) => decl.name.name, + EiiImplResolution::Error(_eg) => continue, + }; self.dcx().emit_err(errors::EiiWithTrackCaller { attr_span, - name: self.tcx.item_name(i.eii_macro), + name, sig_span: sig.span, }); } diff --git a/compiler/rustc_passes/src/eii.rs b/compiler/rustc_passes/src/eii.rs index ab3f9f0d21825..f3e84665f21dc 100644 --- a/compiler/rustc_passes/src/eii.rs +++ b/compiler/rustc_passes/src/eii.rs @@ -81,7 +81,7 @@ pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, (): } // now we have all eiis! For each of them, choose one we want to actually generate. - for (decl_did, FoundEii { decl, decl_crate, impls }) in eiis { + for (foreign_item, FoundEii { decl, decl_crate, impls }) in eiis { let mut default_impls = Vec::new(); let mut explicit_impls = Vec::new(); @@ -97,7 +97,7 @@ pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, (): // is instantly an error. if explicit_impls.len() > 1 { tcx.dcx().emit_err(DuplicateEiiImpls { - name: tcx.item_name(decl_did), + name: decl.name.name, first_span: tcx.def_span(explicit_impls[0].0), first_crate: tcx.crate_name(explicit_impls[0].1), second_span: tcx.def_span(explicit_impls[1].0), @@ -116,7 +116,7 @@ pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, (): } if default_impls.len() > 1 { - let decl_span = tcx.def_ident_span(decl_did).unwrap(); + let decl_span = tcx.def_ident_span(foreign_item).unwrap(); tcx.dcx().span_delayed_bug(decl_span, "multiple not supported right now"); } @@ -139,8 +139,9 @@ pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, (): tcx.dcx().emit_err(EiiWithoutImpl { current_crate_name: tcx.crate_name(LOCAL_CRATE), decl_crate_name: tcx.crate_name(decl_crate), - name: tcx.item_name(decl_did), - span: decl.span, + // FIXME: shouldn't call `item_name` + name: decl.name.name, + span: decl.name.span, help: (), }); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index b4941a6f5b999..32cf57af2e7f5 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1069,8 +1069,20 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc debug!("(resolving function) entering function"); if let FnKind::Fn(_, _, f) = fn_kind { - for EiiImpl { node_id, eii_macro_path, .. } in &f.eii_impls { - self.smart_resolve_path(*node_id, &None, &eii_macro_path, PathSource::Macro); + for EiiImpl { node_id, eii_macro_path, known_eii_macro_resolution, .. } in &f.eii_impls + { + // See docs on the `known_eii_macro_resolution` field: + // if we already know the resolution statically, don't bother resolving it. + if let Some(target) = known_eii_macro_resolution { + self.smart_resolve_path( + *node_id, + &None, + &target.extern_item_path, + PathSource::Expr(None), + ); + } else { + self.smart_resolve_path(*node_id, &None, &eii_macro_path, PathSource::Macro); + } } } @@ -2917,7 +2929,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.parent_scope.macro_rules = self.r.macro_rules_scopes[&def_id]; } - if let Some(EiiExternTarget { extern_item_path, impl_unsafe: _, span: _ }) = + if let Some(EiiExternTarget { extern_item_path, impl_unsafe: _ }) = ¯o_def.eii_extern_target { self.smart_resolve_path( diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index 484e626d4638a..a0ccf8d7e7986 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -81,7 +81,7 @@ impl fmt::Display for CrateNum { /// because it depends on the set of crates in the entire crate graph of a /// compilation session. Again, using the same crate with a different version /// number would fix the issue with a high probability -- but that might be -/// easier said then done if the crates in questions are dependencies of +/// easier said than done if the crates in questions are dependencies of /// third-party crates. /// /// That being said, given a high quality hash function, the collision diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index db01cebe5b27a..c7fdc2054f712 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -21,6 +21,8 @@ #![feature(binary_heap_into_iter_sorted)] #![feature(binary_heap_drain_sorted)] #![feature(slice_ptr_get)] +#![feature(slice_range)] +#![feature(slice_partial_sort_unstable)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] diff --git a/library/alloctests/tests/sort/mod.rs b/library/alloctests/tests/sort/mod.rs index 0e2494ca9d34e..e2e141a02b597 100644 --- a/library/alloctests/tests/sort/mod.rs +++ b/library/alloctests/tests/sort/mod.rs @@ -12,6 +12,7 @@ pub trait Sort { mod ffi_types; mod known_good_stable_sort; +mod partial; mod patterns; mod tests; mod zipf; diff --git a/library/alloctests/tests/sort/partial.rs b/library/alloctests/tests/sort/partial.rs new file mode 100644 index 0000000000000..c7248990c70cd --- /dev/null +++ b/library/alloctests/tests/sort/partial.rs @@ -0,0 +1,84 @@ +use std::fmt::Debug; +use std::ops::{Range, RangeBounds}; +use std::slice; + +use super::patterns; + +fn check_is_partial_sorted>(v: &mut [T], range: R) { + let Range { start, end } = slice::range(range, ..v.len()); + v.partial_sort_unstable(start..end); + + let max_before = v[..start].iter().max().into_iter(); + let sorted_range = v[start..end].into_iter(); + let min_after = v[end..].iter().min().into_iter(); + let seq = max_before.chain(sorted_range).chain(min_after); + assert!(seq.is_sorted()); +} + +fn check_is_partial_sorted_ranges(v: &[T]) { + let len = v.len(); + + check_is_partial_sorted::(&mut v.to_vec(), ..); + check_is_partial_sorted::(&mut v.to_vec(), 0..0); + check_is_partial_sorted::(&mut v.to_vec(), len..len); + + if len > 0 { + check_is_partial_sorted::(&mut v.to_vec(), len - 1..len - 1); + check_is_partial_sorted::(&mut v.to_vec(), 0..1); + check_is_partial_sorted::(&mut v.to_vec(), len - 1..len); + + for mid in 1..len { + check_is_partial_sorted::(&mut v.to_vec(), 0..mid); + check_is_partial_sorted::(&mut v.to_vec(), mid..len); + check_is_partial_sorted::(&mut v.to_vec(), mid..mid); + check_is_partial_sorted::(&mut v.to_vec(), mid - 1..mid + 1); + check_is_partial_sorted::(&mut v.to_vec(), mid - 1..mid); + check_is_partial_sorted::(&mut v.to_vec(), mid..mid + 1); + } + + let quarters = [0, len / 4, len / 2, (3 * len) / 4, len]; + for &start in &quarters { + for &end in &quarters { + if start < end { + check_is_partial_sorted::(&mut v.to_vec(), start..end); + } + } + } + } +} + +#[test] +fn basic_impl() { + check_is_partial_sorted::(&mut [], ..); + check_is_partial_sorted::<(), _>(&mut [], ..); + check_is_partial_sorted::<(), _>(&mut [()], ..); + check_is_partial_sorted::<(), _>(&mut [(), ()], ..); + check_is_partial_sorted::<(), _>(&mut [(), (), ()], ..); + check_is_partial_sorted::(&mut [], ..); + + check_is_partial_sorted::(&mut [77], ..); + check_is_partial_sorted::(&mut [2, 3], ..); + check_is_partial_sorted::(&mut [2, 3, 6], ..); + check_is_partial_sorted::(&mut [2, 3, 99, 6], ..); + check_is_partial_sorted::(&mut [2, 7709, 400, 90932], ..); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], ..); + + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 0..0); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 0..1); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 0..5); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 0..7); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 7..7); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 6..7); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 5..7); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 5..5); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 4..5); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 4..6); +} + +#[test] +fn random_patterns() { + check_is_partial_sorted_ranges(&patterns::random(10)); + check_is_partial_sorted_ranges(&patterns::random(50)); + check_is_partial_sorted_ranges(&patterns::random(100)); + check_is_partial_sorted_ranges(&patterns::random(1000)); +} diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 1bca13b14ed4c..8113e1824f27d 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3244,6 +3244,219 @@ impl [T] { sort::unstable::sort(self, &mut |a, b| f(a).lt(&f(b))); } + /// Partially sorts the slice in ascending order **without** preserving the initial order of equal elements. + /// + /// Upon completion, for the specified range `start..end`, it's guaranteed that: + /// + /// 1. Every element in `self[..start]` is smaller than or equal to + /// 2. Every element in `self[start..end]`, which is sorted, and smaller than or equal to + /// 3. Every element in `self[end..]`. + /// + /// This partial sort is unstable, meaning it may reorder equal elements in the specified range. + /// It may reorder elements outside the specified range as well, but the guarantees above still hold. + /// + /// This partial sort is in-place (i.e., does not allocate), and *O*(*n* + *k* \* log(*k*)) worst-case, + /// where *n* is the length of the slice and *k* is the length of the specified range. + /// + /// See the documentation of [`sort_unstable`] for implementation notes. + /// + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `T` does not implement a total order, or if + /// the [`Ord`] implementation panics, or if the specified range is out of bounds. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partial_sort_unstable)] + /// + /// let mut v = [4, -5, 1, -3, 2]; + /// + /// // empty range at the beginning, nothing changed + /// v.partial_sort_unstable(0..0); + /// assert_eq!(v, [4, -5, 1, -3, 2]); + /// + /// // empty range in the middle, partitioning the slice + /// v.partial_sort_unstable(2..2); + /// for i in 0..2 { + /// assert!(v[i] <= v[2]); + /// } + /// for i in 3..v.len() { + /// assert!(v[2] <= v[i]); + /// } + /// + /// // single element range, same as select_nth_unstable + /// v.partial_sort_unstable(2..3); + /// for i in 0..2 { + /// assert!(v[i] <= v[2]); + /// } + /// for i in 3..v.len() { + /// assert!(v[2] <= v[i]); + /// } + /// + /// // partial sort a subrange + /// v.partial_sort_unstable(1..4); + /// assert_eq!(&v[1..4], [-3, 1, 2]); + /// + /// // partial sort the whole range, same as sort_unstable + /// v.partial_sort_unstable(..); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); + /// ``` + /// + /// [`sort_unstable`]: slice::sort_unstable + #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] + #[inline] + pub fn partial_sort_unstable(&mut self, range: R) + where + T: Ord, + R: RangeBounds, + { + sort::unstable::partial_sort(self, range, T::lt); + } + + /// Partially sorts the slice in ascending order with a comparison function, **without** + /// preserving the initial order of equal elements. + /// + /// Upon completion, for the specified range `start..end`, it's guaranteed that: + /// + /// 1. Every element in `self[..start]` is smaller than or equal to + /// 2. Every element in `self[start..end]`, which is sorted, and smaller than or equal to + /// 3. Every element in `self[end..]`. + /// + /// This partial sort is unstable, meaning it may reorder equal elements in the specified range. + /// It may reorder elements outside the specified range as well, but the guarantees above still hold. + /// + /// This partial sort is in-place (i.e., does not allocate), and *O*(*n* + *k* \* log(*k*)) worst-case, + /// where *n* is the length of the slice and *k* is the length of the specified range. + /// + /// See the documentation of [`sort_unstable_by`] for implementation notes. + /// + /// # Panics + /// + /// May panic if the `compare` does not implement a total order, or if + /// the `compare` itself panics, or if the specified range is out of bounds. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partial_sort_unstable)] + /// + /// let mut v = [4, -5, 1, -3, 2]; + /// + /// // empty range at the beginning, nothing changed + /// v.partial_sort_unstable_by(0..0, |a, b| b.cmp(a)); + /// assert_eq!(v, [4, -5, 1, -3, 2]); + /// + /// // empty range in the middle, partitioning the slice + /// v.partial_sort_unstable_by(2..2, |a, b| b.cmp(a)); + /// for i in 0..2 { + /// assert!(v[i] >= v[2]); + /// } + /// for i in 3..v.len() { + /// assert!(v[2] >= v[i]); + /// } + /// + /// // single element range, same as select_nth_unstable + /// v.partial_sort_unstable_by(2..3, |a, b| b.cmp(a)); + /// for i in 0..2 { + /// assert!(v[i] >= v[2]); + /// } + /// for i in 3..v.len() { + /// assert!(v[2] >= v[i]); + /// } + /// + /// // partial sort a subrange + /// v.partial_sort_unstable_by(1..4, |a, b| b.cmp(a)); + /// assert_eq!(&v[1..4], [2, 1, -3]); + /// + /// // partial sort the whole range, same as sort_unstable + /// v.partial_sort_unstable_by(.., |a, b| b.cmp(a)); + /// assert_eq!(v, [4, 2, 1, -3, -5]); + /// ``` + /// + /// [`sort_unstable_by`]: slice::sort_unstable_by + #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] + #[inline] + pub fn partial_sort_unstable_by(&mut self, range: R, mut compare: F) + where + F: FnMut(&T, &T) -> Ordering, + R: RangeBounds, + { + sort::unstable::partial_sort(self, range, |a, b| compare(a, b) == Less); + } + + /// Partially sorts the slice in ascending order with a key extraction function, **without** + /// preserving the initial order of equal elements. + /// + /// Upon completion, for the specified range `start..end`, it's guaranteed that: + /// + /// 1. Every element in `self[..start]` is smaller than or equal to + /// 2. Every element in `self[start..end]`, which is sorted, and smaller than or equal to + /// 3. Every element in `self[end..]`. + /// + /// This partial sort is unstable, meaning it may reorder equal elements in the specified range. + /// It may reorder elements outside the specified range as well, but the guarantees above still hold. + /// + /// This partial sort is in-place (i.e., does not allocate), and *O*(*n* + *k* \* log(*k*)) worst-case, + /// where *n* is the length of the slice and *k* is the length of the specified range. + /// + /// See the documentation of [`sort_unstable_by_key`] for implementation notes. + /// + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `K` does not implement a total order, or if + /// the [`Ord`] implementation panics, or if the specified range is out of bounds. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partial_sort_unstable)] + /// + /// let mut v = [4i32, -5, 1, -3, 2]; + /// + /// // empty range at the beginning, nothing changed + /// v.partial_sort_unstable_by_key(0..0, |k| k.abs()); + /// assert_eq!(v, [4, -5, 1, -3, 2]); + /// + /// // empty range in the middle, partitioning the slice + /// v.partial_sort_unstable_by_key(2..2, |k| k.abs()); + /// for i in 0..2 { + /// assert!(v[i].abs() <= v[2].abs()); + /// } + /// for i in 3..v.len() { + /// assert!(v[2].abs() <= v[i].abs()); + /// } + /// + /// // single element range, same as select_nth_unstable + /// v.partial_sort_unstable_by_key(2..3, |k| k.abs()); + /// for i in 0..2 { + /// assert!(v[i].abs() <= v[2].abs()); + /// } + /// for i in 3..v.len() { + /// assert!(v[2].abs() <= v[i].abs()); + /// } + /// + /// // partial sort a subrange + /// v.partial_sort_unstable_by_key(1..4, |k| k.abs()); + /// assert_eq!(&v[1..4], [2, -3, 4]); + /// + /// // partial sort the whole range, same as sort_unstable + /// v.partial_sort_unstable_by_key(.., |k| k.abs()); + /// assert_eq!(v, [1, 2, -3, 4, -5]); + /// ``` + /// + /// [`sort_unstable_by_key`]: slice::sort_unstable_by_key + #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] + #[inline] + pub fn partial_sort_unstable_by_key(&mut self, range: R, mut f: F) + where + F: FnMut(&T) -> K, + K: Ord, + R: RangeBounds, + { + sort::unstable::partial_sort(self, range, |a, b| f(a).lt(&f(b))); + } + /// Reorders the slice such that the element at `index` is at a sort-order position. All /// elements before `index` will be `<=` to this value, and all elements after will be `>=` to /// it. diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index d4df8d3a264db..7ca95a3b1b198 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -1,11 +1,13 @@ //! This module contains the entry points for `slice::sort_unstable`. use crate::mem::SizedTypeProperties; +use crate::ops::{Range, RangeBounds}; +use crate::slice::sort::select::partition_at_index; #[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::find_existing_run; #[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; -use crate::{cfg_select, intrinsics}; +use crate::{cfg_select, intrinsics, slice}; pub(crate) mod heapsort; pub(crate) mod quicksort; @@ -17,7 +19,10 @@ pub(crate) mod quicksort; /// Upholds all safety properties outlined here: /// #[inline(always)] -pub fn sort bool>(v: &mut [T], is_less: &mut F) { +pub fn sort(v: &mut [T], is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ // Arrays of zero-sized types are always all-equal, and thus sorted. if T::IS_ZST { return; @@ -52,6 +57,54 @@ pub fn sort bool>(v: &mut [T], is_less: &mut F) { } } +/// Unstable partial sort the range `start..end`, after which it's guaranteed that: +/// +/// 1. Every element in `v[..start]` is smaller than or equal to +/// 2. Every element in `v[start..end]`, which is sorted, and smaller than or equal to +/// 3. Every element in `v[end..]`. +#[inline] +pub fn partial_sort(v: &mut [T], range: R, mut is_less: F) +where + F: FnMut(&T, &T) -> bool, + R: RangeBounds, +{ + // Arrays of zero-sized types are always all-equal, and thus sorted. + if T::IS_ZST { + return; + } + + let len = v.len(); + let Range { start, end } = slice::range(range, ..len); + + if end - start <= 1 { + // Empty range or single element. This case can be resolved in at most + // single partition_at_index call, without further sorting. + + if end == 0 || start == len { + // Do nothing if it is an empty range at start or end: all guarantees + // are already upheld. + return; + } + + partition_at_index(v, start, &mut is_less); + return; + } + + // A heuristic factor to decide whether to partition the slice or not. + // If the range bound is close to the edges of the slice, it's not worth + // partitioning first. + const PARTITION_THRESHOLD: usize = 8; + let mut v = v; + if end + PARTITION_THRESHOLD <= len { + v = partition_at_index(v, end - 1, &mut is_less).0; + } + if start >= PARTITION_THRESHOLD { + v = partition_at_index(v, start, &mut is_less).2; + } + + sort(v, &mut is_less); +} + /// See [`sort`] /// /// Deliberately don't inline the main sorting routine entrypoint to ensure the diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index b1e5f33b1b22e..fcf21d9e97318 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -320,8 +320,8 @@ impl File { self.0.set_file_info(file_info) } - pub fn read(&self, _buf: &mut [u8]) -> io::Result { - unsupported() + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { @@ -364,7 +364,7 @@ impl File { } pub fn tell(&self) -> io::Result { - unsupported() + self.0.position() } pub fn duplicate(&self) -> io::Result { @@ -666,6 +666,19 @@ mod uefi_fs { Ok(p) } + pub(crate) fn read(&self, buf: &mut [u8]) -> io::Result { + let file_ptr = self.protocol.as_ptr(); + let mut buf_size = buf.len(); + + let r = unsafe { ((*file_ptr).read)(file_ptr, &mut buf_size, buf.as_mut_ptr().cast()) }; + + if buf_size == 0 && r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + Ok(buf_size) + } + } + pub(crate) fn read_dir_entry(&self) -> io::Result>> { let file_ptr = self.protocol.as_ptr(); let mut buf_size = 0; @@ -734,6 +747,14 @@ mod uefi_fs { if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } + pub(crate) fn position(&self) -> io::Result { + let file_ptr = self.protocol.as_ptr(); + let mut pos = 0; + + let r = unsafe { ((*file_ptr).get_position)(file_ptr, &mut pos) }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(pos) } + } + pub(crate) fn delete(self) -> io::Result<()> { let file_ptr = self.protocol.as_ptr(); let r = unsafe { ((*file_ptr).delete)(file_ptr) }; diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 327b0eb7468a1..bb2ee6ae10ed4 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -2296,7 +2296,7 @@ fn open_to_and_set_permissions( _reader_metadata: &crate::fs::Metadata, ) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { use crate::fs::OpenOptions; - let writer = OpenOptions::new().open(to)?; + let writer = OpenOptions::new().write(true).create(true).truncate(true).open(to)?; let writer_metadata = writer.metadata()?; Ok((writer, writer_metadata)) } diff --git a/src/tools/tidy/src/triagebot.rs b/src/tools/tidy/src/triagebot.rs index 59cd9d80b07ac..a74d980cfcb7f 100644 --- a/src/tools/tidy/src/triagebot.rs +++ b/src/tools/tidy/src/triagebot.rs @@ -2,11 +2,22 @@ use std::collections::HashSet; use std::path::Path; +use std::sync::LazyLock; use toml::Value; use crate::diagnostics::TidyCtx; +static SUBMODULES: LazyLock> = LazyLock::new(|| { + // WORKSPACES doesn't list all submodules but it's contains the main at least + crate::deps::WORKSPACES + .iter() + .map(|ws| ws.submodules.iter()) + .flatten() + .map(|p| Path::new(p)) + .collect() +}); + pub fn check(path: &Path, tidy_ctx: TidyCtx) { let mut check = tidy_ctx.start_check("triagebot"); let triagebot_path = path.join("triagebot.toml"); @@ -39,8 +50,13 @@ pub fn check(path: &Path, tidy_ctx: TidyCtx) { if !full_path.exists() { // The full-path doesn't exists, maybe it's a glob, let's add it to the glob set builder // to be checked against all the file and directories in the repository. - builder.add(globset::Glob::new(&format!("{clean_path}*")).unwrap()); + let trimmed_path = clean_path.trim_end_matches('/'); + builder.add(globset::Glob::new(&format!("{trimmed_path}{{,/*}}")).unwrap()); glob_entries.push(clean_path.to_string()); + } else if is_in_submodule(Path::new(clean_path)) { + check.error(format!( + "triagebot.toml [mentions.*] '{clean_path}' cannot match inside a submodule" + )); } } @@ -49,8 +65,18 @@ pub fn check(path: &Path, tidy_ctx: TidyCtx) { let mut found = HashSet::new(); let mut matches = Vec::new(); + let cloned_path = path.to_path_buf(); + // Walk the entire repository and match any entry against the remaining paths - for entry in ignore::WalkBuilder::new(path).build().flatten() { + for entry in ignore::WalkBuilder::new(&path) + .filter_entry(move |entry| { + // Ignore entries inside submodules as triagebot cannot detect them + let entry_path = entry.path().strip_prefix(&cloned_path).unwrap(); + is_not_in_submodule(entry_path) + }) + .build() + .flatten() + { // Strip the prefix as mentions entries are always relative to the repo let entry_path = entry.path().strip_prefix(path).unwrap(); @@ -126,3 +152,11 @@ pub fn check(path: &Path, tidy_ctx: TidyCtx) { } } } + +fn is_not_in_submodule(path: &Path) -> bool { + SUBMODULES.contains(&path) || !SUBMODULES.iter().any(|p| path.starts_with(*p)) +} + +fn is_in_submodule(path: &Path) -> bool { + !SUBMODULES.contains(&path) && SUBMODULES.iter().any(|p| path.starts_with(*p)) +} diff --git a/tests/ui/eii/duplicate/eii_conflict_with_macro.rs b/tests/ui/eii/duplicate/eii_conflict_with_macro.rs new file mode 100644 index 0000000000000..d8118f8417a09 --- /dev/null +++ b/tests/ui/eii/duplicate/eii_conflict_with_macro.rs @@ -0,0 +1,7 @@ +//@ compile-flags: --crate-type rlib +//@ build-pass +#![feature(extern_item_impls)] + +macro_rules! foo_impl { () => {} } +#[eii] +fn foo_impl() {} diff --git a/tests/ui/eii/privacy2.rs b/tests/ui/eii/privacy2.rs index a4ae188bd0ec7..a0bc26ef629b7 100644 --- a/tests/ui/eii/privacy2.rs +++ b/tests/ui/eii/privacy2.rs @@ -1,5 +1,5 @@ //@ aux-build:codegen3.rs -// Tests whether name resulution respects privacy properly. +// Tests whether name resolution respects privacy properly. #![feature(extern_item_impls)] extern crate codegen3 as codegen; diff --git a/tests/ui/eii/privacy2.stderr b/tests/ui/eii/privacy2.stderr index 0d44604567e44..903285d4d1e37 100644 --- a/tests/ui/eii/privacy2.stderr +++ b/tests/ui/eii/privacy2.stderr @@ -17,18 +17,18 @@ LL | fn decl1(x: u64); | ^^^^^^^^^^^^^^^^^ error: `#[eii2]` required, but not found - --> $DIR/auxiliary/codegen3.rs:9:1 + --> $DIR/auxiliary/codegen3.rs:9:7 | LL | #[eii(eii2)] - | ^^^^^^^^^^^^ expected because `#[eii2]` was declared here in crate `codegen3` + | ^^^^ expected because `#[eii2]` was declared here in crate `codegen3` | = help: expected at least one implementation in crate `privacy2` or any of its dependencies error: `#[eii3]` required, but not found - --> $DIR/auxiliary/codegen3.rs:13:5 + --> $DIR/auxiliary/codegen3.rs:13:11 | LL | #[eii(eii3)] - | ^^^^^^^^^^^^ expected because `#[eii3]` was declared here in crate `codegen3` + | ^^^^ expected because `#[eii3]` was declared here in crate `codegen3` | = help: expected at least one implementation in crate `privacy2` or any of its dependencies