Auto merge of #150900 - Urgau:rollup-og7uEW9, r=Urgau
Rollup of 9 pull requests Successful merges: - rust-lang/rust#149318 (Implement partial_sort_unstable for slice) - rust-lang/rust#150805 (Fix ICE in inline always warning emission.) - rust-lang/rust#150822 (Fix for ICE: eii: fn / macro rules None in find_attr()) - rust-lang/rust#150853 (std: sys: fs: uefi: Implement File::read) - rust-lang/rust#150855 (std: sys: fs: uefi: Implement File::tell) - rust-lang/rust#150881 (Fix std::fs::copy on WASI by setting proper OpenOptions flags) - rust-lang/rust#150891 (Fix a trivial typo in def_id.rs) - rust-lang/rust#150892 (Don't check `[mentions]` paths in submodules from tidy) - rust-lang/rust#150894 (cg_llvm: add a pause to make comment less confusing) r? @ghost
This commit is contained in:
commit
2b82e05da4
26 changed files with 702 additions and 129 deletions
|
|
@ -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 <https://github.com/rust-lang/rust/issues/149981> 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<EiiExternTarget>,
|
||||
pub impl_safety: Safety,
|
||||
pub span: Span,
|
||||
pub inner_span: Span,
|
||||
|
|
|
|||
|
|
@ -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<EiiDecl> {
|
||||
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(..)
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))]
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: (),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ pub trait Sort {
|
|||
|
||||
mod ffi_types;
|
||||
mod known_good_stable_sort;
|
||||
mod partial;
|
||||
mod patterns;
|
||||
mod tests;
|
||||
mod zipf;
|
||||
|
|
|
|||
84
library/alloctests/tests/sort/partial.rs
Normal file
84
library/alloctests/tests/sort/partial.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
use std::fmt::Debug;
|
||||
use std::ops::{Range, RangeBounds};
|
||||
use std::slice;
|
||||
|
||||
use super::patterns;
|
||||
|
||||
fn check_is_partial_sorted<T: Ord + Clone + Debug, R: RangeBounds<usize>>(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<T: Ord + Clone + Debug>(v: &[T]) {
|
||||
let len = v.len();
|
||||
|
||||
check_is_partial_sorted::<T, _>(&mut v.to_vec(), ..);
|
||||
check_is_partial_sorted::<T, _>(&mut v.to_vec(), 0..0);
|
||||
check_is_partial_sorted::<T, _>(&mut v.to_vec(), len..len);
|
||||
|
||||
if len > 0 {
|
||||
check_is_partial_sorted::<T, _>(&mut v.to_vec(), len - 1..len - 1);
|
||||
check_is_partial_sorted::<T, _>(&mut v.to_vec(), 0..1);
|
||||
check_is_partial_sorted::<T, _>(&mut v.to_vec(), len - 1..len);
|
||||
|
||||
for mid in 1..len {
|
||||
check_is_partial_sorted::<T, _>(&mut v.to_vec(), 0..mid);
|
||||
check_is_partial_sorted::<T, _>(&mut v.to_vec(), mid..len);
|
||||
check_is_partial_sorted::<T, _>(&mut v.to_vec(), mid..mid);
|
||||
check_is_partial_sorted::<T, _>(&mut v.to_vec(), mid - 1..mid + 1);
|
||||
check_is_partial_sorted::<T, _>(&mut v.to_vec(), mid - 1..mid);
|
||||
check_is_partial_sorted::<T, _>(&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::<T, _>(&mut v.to_vec(), start..end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_impl() {
|
||||
check_is_partial_sorted::<i32, _>(&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::<i32, _>(&mut [], ..);
|
||||
|
||||
check_is_partial_sorted::<i32, _>(&mut [77], ..);
|
||||
check_is_partial_sorted::<i32, _>(&mut [2, 3], ..);
|
||||
check_is_partial_sorted::<i32, _>(&mut [2, 3, 6], ..);
|
||||
check_is_partial_sorted::<i32, _>(&mut [2, 3, 99, 6], ..);
|
||||
check_is_partial_sorted::<i32, _>(&mut [2, 7709, 400, 90932], ..);
|
||||
check_is_partial_sorted::<i32, _>(&mut [15, -1, 3, -1, -3, -1, 7], ..);
|
||||
|
||||
check_is_partial_sorted::<i32, _>(&mut [15, -1, 3, -1, -3, -1, 7], 0..0);
|
||||
check_is_partial_sorted::<i32, _>(&mut [15, -1, 3, -1, -3, -1, 7], 0..1);
|
||||
check_is_partial_sorted::<i32, _>(&mut [15, -1, 3, -1, -3, -1, 7], 0..5);
|
||||
check_is_partial_sorted::<i32, _>(&mut [15, -1, 3, -1, -3, -1, 7], 0..7);
|
||||
check_is_partial_sorted::<i32, _>(&mut [15, -1, 3, -1, -3, -1, 7], 7..7);
|
||||
check_is_partial_sorted::<i32, _>(&mut [15, -1, 3, -1, -3, -1, 7], 6..7);
|
||||
check_is_partial_sorted::<i32, _>(&mut [15, -1, 3, -1, -3, -1, 7], 5..7);
|
||||
check_is_partial_sorted::<i32, _>(&mut [15, -1, 3, -1, -3, -1, 7], 5..5);
|
||||
check_is_partial_sorted::<i32, _>(&mut [15, -1, 3, -1, -3, -1, 7], 4..5);
|
||||
check_is_partial_sorted::<i32, _>(&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));
|
||||
}
|
||||
|
|
@ -3244,6 +3244,219 @@ impl<T> [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<R>(&mut self, range: R)
|
||||
where
|
||||
T: Ord,
|
||||
R: RangeBounds<usize>,
|
||||
{
|
||||
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<F, R>(&mut self, range: R, mut compare: F)
|
||||
where
|
||||
F: FnMut(&T, &T) -> Ordering,
|
||||
R: RangeBounds<usize>,
|
||||
{
|
||||
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<K, F, R>(&mut self, range: R, mut f: F)
|
||||
where
|
||||
F: FnMut(&T) -> K,
|
||||
K: Ord,
|
||||
R: RangeBounds<usize>,
|
||||
{
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
/// <https://github.com/Voultapher/sort-research-rs/blob/main/writeup/sort_safety/text.md>
|
||||
#[inline(always)]
|
||||
pub fn sort<T, F: FnMut(&T, &T) -> bool>(v: &mut [T], is_less: &mut F) {
|
||||
pub fn sort<T, F>(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<T, F: FnMut(&T, &T) -> 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<T, F, R>(v: &mut [T], range: R, mut is_less: F)
|
||||
where
|
||||
F: FnMut(&T, &T) -> bool,
|
||||
R: RangeBounds<usize>,
|
||||
{
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -320,8 +320,8 @@ impl File {
|
|||
self.0.set_file_info(file_info)
|
||||
}
|
||||
|
||||
pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||
unsupported()
|
||||
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
|
||||
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
|
|
@ -364,7 +364,7 @@ impl File {
|
|||
}
|
||||
|
||||
pub fn tell(&self) -> io::Result<u64> {
|
||||
unsupported()
|
||||
self.0.position()
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<File> {
|
||||
|
|
@ -666,6 +666,19 @@ mod uefi_fs {
|
|||
Ok(p)
|
||||
}
|
||||
|
||||
pub(crate) fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
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<Option<UefiBox<file::Info>>> {
|
||||
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<u64> {
|
||||
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) };
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Vec<&'static Path>> = 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))
|
||||
}
|
||||
|
|
|
|||
7
tests/ui/eii/duplicate/eii_conflict_with_macro.rs
Normal file
7
tests/ui/eii/duplicate/eii_conflict_with_macro.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
//@ compile-flags: --crate-type rlib
|
||||
//@ build-pass
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
macro_rules! foo_impl { () => {} }
|
||||
#[eii]
|
||||
fn foo_impl() {}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue