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:
rust-bors[bot] 2026-01-10 01:19:39 +00:00 committed by GitHub
commit 2b82e05da4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 702 additions and 129 deletions

View file

@ -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,

View file

@ -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(..)

View file

@ -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]

View file

@ -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;

View file

@ -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;
}

View file

@ -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)]

View file

@ -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,

View file

@ -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);
}
}

View file

@ -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()));
}
}

View file

@ -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))]

View file

@ -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"

View file

@ -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,
});
}

View file

@ -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: (),
});

View file

@ -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: _ }) =
&macro_def.eii_extern_target
{
self.smart_resolve_path(

View file

@ -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

View file

@ -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)]

View file

@ -12,6 +12,7 @@ pub trait Sort {
mod ffi_types;
mod known_good_stable_sort;
mod partial;
mod patterns;
mod tests;
mod zipf;

View 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));
}

View file

@ -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.

View file

@ -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

View file

@ -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) };

View file

@ -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))
}

View file

@ -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))
}

View file

@ -0,0 +1,7 @@
//@ compile-flags: --crate-type rlib
//@ build-pass
#![feature(extern_item_impls)]
macro_rules! foo_impl { () => {} }
#[eii]
fn foo_impl() {}

View file

@ -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;

View file

@ -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