Merge pull request #2699 from rust-lang/rustc-pull

Rustc pull update
This commit is contained in:
Tshepang Mbambo 2025-12-16 06:00:15 +02:00 committed by GitHub
commit c2aeeaf19d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
625 changed files with 10776 additions and 6209 deletions

View file

@ -4630,7 +4630,6 @@ dependencies = [
name = "rustc_session"
version = "0.0.0"
dependencies = [
"bitflags",
"getopts",
"libc",
"rand 0.9.2",
@ -4657,6 +4656,7 @@ dependencies = [
name = "rustc_span"
version = "0.0.0"
dependencies = [
"bitflags",
"blake3",
"derive-where",
"indexmap",

View file

@ -191,6 +191,31 @@
# Currently, this is only supported for the `x86_64-unknown-linux-gnu` target.
#gcc.download-ci-gcc = false
# Provide a directory of prebuilt libgccjit.so dylibs for given (host, target) compilation pairs.
# This is useful when you want to cross-compile `rustc` to another target since GCC is not a
# multi-target compiler.
# You have to use a directory structure that looks like this:
# `<libgccjit-libs-dir>/<host>/<target>/libgccjit.so`.
# For example:
#
# ```
# <libgccjit-libs-dir>
# ├── m68k-unknown-linux-gnu
# │ └── m68k-unknown-linux-gnu
# │ └── libgccjit.so
# └── x86_64-unknown-linux-gnu
# ├── m68k-unknown-linux-gnu
# │ └── libgccjit.so
# └── x86_64-unknown-linux-gnu
# └── libgccjit.so
# ```
# The directory above would allow you to cross-compile rustc from x64 to m68k
#
# Note that this option has priority over `gcc.download-ci-gcc`.
# If you set both, bootstrap will first try to load libgccjit.so from this directory.
# Only if it isn't found, it will try to download it from CI or build it locally.
#gcc.libgccjit-libs-dir = "/path/to/libgccjit-libs-dir"
# =============================================================================
# General build configuration options
# =============================================================================

View file

@ -2107,6 +2107,19 @@ pub struct MacroDef {
pub body: Box<DelimArgs>,
/// `true` if macro was defined with `macro_rules`.
pub macro_rules: bool,
/// If this is a macro used for externally implementable items,
/// it refers to an extern item which is its "target". This requires
/// name resolution so can't just be an attribute, so we store it in this field.
pub eii_extern_target: Option<EiiExternTarget>,
}
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
pub struct EiiExternTarget {
/// path to the extern item we're targetting
pub extern_item_path: Path,
pub impl_unsafe: bool,
pub span: Span,
}
#[derive(Clone, Encodable, Decodable, Debug, Copy, Hash, Eq, PartialEq)]
@ -3746,6 +3759,21 @@ pub struct Fn {
pub contract: Option<Box<FnContract>>,
pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
pub body: Option<Box<Block>>,
/// This function is an implementation of an externally implementable item (EII).
/// This means, there was an EII declared somewhere and this function is the
/// implementation that should be run when the declaration is called.
pub eii_impls: ThinVec<EiiImpl>,
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct EiiImpl {
pub node_id: NodeId,
pub eii_macro_path: Path,
pub impl_safety: Safety,
pub span: Span,
pub inner_span: Span,
pub is_default: bool,
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
@ -4112,7 +4140,7 @@ mod size_asserts {
static_assert_size!(Block, 32);
static_assert_size!(Expr, 72);
static_assert_size!(ExprKind, 40);
static_assert_size!(Fn, 184);
static_assert_size!(Fn, 192);
static_assert_size!(ForeignItem, 80);
static_assert_size!(ForeignItemKind, 16);
static_assert_size!(GenericArg, 24);

View file

@ -393,6 +393,7 @@ macro_rules! common_visitor_and_walkers {
ThinVec<Pat>,
ThinVec<Box<Ty>>,
ThinVec<TyPat>,
ThinVec<EiiImpl>,
);
// This macro generates `impl Visitable` and `impl MutVisitable` that forward to `Walkable`
@ -486,6 +487,8 @@ macro_rules! common_visitor_and_walkers {
WhereEqPredicate,
WhereRegionPredicate,
YieldKind,
EiiExternTarget,
EiiImpl,
);
/// Each method of this trait is a hook to be potentially
@ -920,13 +923,13 @@ macro_rules! common_visitor_and_walkers {
_ctxt,
// Visibility is visited as a part of the item.
_vis,
Fn { defaultness, ident, sig, generics, contract, body, define_opaque },
Fn { defaultness, ident, sig, generics, contract, body, define_opaque, eii_impls },
) => {
let FnSig { header, decl, span } = sig;
visit_visitable!($($mut)? vis,
defaultness, ident, header, generics, decl,
contract, body, span, define_opaque
)
contract, body, span, define_opaque, eii_impls
);
}
FnKind::Closure(binder, coroutine_kind, decl, body) =>
visit_visitable!($($mut)? vis, binder, coroutine_kind, decl, body),

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;
use rustc_hir::attrs::{AttributeKind, EiiDecl};
use rustc_hir::def::{DefKind, PerNS, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
use rustc_hir::{
@ -11,6 +11,7 @@ use rustc_hir::{
use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::span_bug;
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
use rustc_span::def_id::DefId;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
use smallvec::{SmallVec, smallvec};
@ -133,10 +134,92 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
fn generate_extra_attrs_for_item_kind(
&mut self,
id: NodeId,
i: &ItemKind,
) -> Vec<hir::Attribute> {
match i {
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(),
))]
}
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),
}))]
})
.unwrap_or_default(),
ItemKind::ExternCrate(..)
| ItemKind::Use(..)
| ItemKind::Static(..)
| ItemKind::Const(..)
| ItemKind::Mod(..)
| ItemKind::ForeignMod(..)
| ItemKind::GlobalAsm(..)
| ItemKind::TyAlias(..)
| ItemKind::Enum(..)
| ItemKind::Struct(..)
| ItemKind::Union(..)
| ItemKind::Trait(..)
| ItemKind::TraitAlias(..)
| ItemKind::Impl(..)
| ItemKind::MacCall(..)
| ItemKind::MacroDef(..)
| ItemKind::Delegation(..)
| ItemKind::DelegationMac(..) => Vec::new(),
}
}
fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> {
let vis_span = self.lower_span(i.vis.span);
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span, Target::from_ast_item(i));
let extra_hir_attributes = self.generate_extra_attrs_for_item_kind(i.id, &i.kind);
let attrs = self.lower_attrs_with_extra(
hir_id,
&i.attrs,
i.span,
Target::from_ast_item(i),
&extra_hir_attributes,
);
let kind = self.lower_item_kind(i.span, i.id, hir_id, attrs, vis_span, &i.kind);
let item = hir::Item {
owner_id: hir_id.expect_owner(),
@ -144,6 +227,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
vis_span,
span: self.lower_span(i.span),
has_delayed_lints: !self.delayed_lints.is_empty(),
eii: find_attr!(
attrs,
AttributeKind::EiiImpls(..) | AttributeKind::EiiExternTarget(..)
),
};
self.arena.alloc(item)
}
@ -435,7 +522,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
hir::ItemKind::TraitAlias(constness, ident, generics, bounds)
}
ItemKind::MacroDef(ident, MacroDef { body, macro_rules }) => {
ItemKind::MacroDef(ident, MacroDef { body, macro_rules, eii_extern_target: _ }) => {
let ident = self.lower_ident(*ident);
let body = Box::new(self.lower_delim_args(body));
let def_id = self.local_def_id(id);
@ -446,7 +533,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
def_kind.descr(def_id.to_def_id())
);
};
let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules });
let macro_def = self.arena.alloc(ast::MacroDef {
body,
macro_rules: *macro_rules,
eii_extern_target: None,
});
hir::ItemKind::Macro(ident, macro_def, macro_kinds)
}
ItemKind::Delegation(box delegation) => {
@ -465,6 +556,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
fn lower_path_simple_eii(&mut self, id: NodeId, path: &Path) -> Option<DefId> {
let res = self.resolver.get_partial_res(id)?;
let Some(did) = res.expect_full_res().opt_def_id() else {
self.dcx().span_delayed_bug(path.span, "should have errored in resolve");
return None;
};
Some(did)
}
#[instrument(level = "debug", skip(self))]
fn lower_use_tree(
&mut self,
@ -573,6 +674,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
vis_span,
span: this.lower_span(use_tree.span),
has_delayed_lints: !this.delayed_lints.is_empty(),
eii: find_attr!(
attrs,
AttributeKind::EiiImpls(..) | AttributeKind::EiiExternTarget(..)
),
};
hir::OwnerNode::Item(this.arena.alloc(item))
});

View file

@ -971,11 +971,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
target_span: Span,
target: Target,
) -> &'hir [hir::Attribute] {
if attrs.is_empty() {
self.lower_attrs_with_extra(id, attrs, target_span, target, &[])
}
fn lower_attrs_with_extra(
&mut self,
id: HirId,
attrs: &[Attribute],
target_span: Span,
target: Target,
extra_hir_attributes: &[hir::Attribute],
) -> &'hir [hir::Attribute] {
if attrs.is_empty() && extra_hir_attributes.is_empty() {
&[]
} else {
let lowered_attrs =
let mut lowered_attrs =
self.lower_attrs_vec(attrs, self.lower_span(target_span), id, target);
lowered_attrs.extend(extra_hir_attributes.iter().cloned());
assert_eq!(id.owner, self.current_hir_id_owner);
let ret = self.arena.alloc_from_iter(lowered_attrs);

View file

@ -1179,11 +1179,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
contract: _,
body,
define_opaque: _,
eii_impls,
},
) => {
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
self.check_defaultness(item.span, *defaultness);
for EiiImpl { eii_macro_path, .. } in eii_impls {
self.visit_path(eii_macro_path);
}
let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic));
if body.is_none() && !is_intrinsic && !self.is_sdylib_interface {
self.dcx().emit_err(errors::FnWithoutBody {

View file

@ -865,6 +865,17 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
sp: Span,
print_visibility: impl FnOnce(&mut Self),
) {
if let Some(eii_extern_target) = &macro_def.eii_extern_target {
self.word("#[eii_extern_target(");
self.print_path(&eii_extern_target.extern_item_path, false, 0);
if eii_extern_target.impl_unsafe {
self.word(",");
self.space();
self.word("unsafe");
}
self.word(")]");
self.hardbreak();
}
let (kw, has_bang) = if macro_def.macro_rules {
("macro_rules", true)
} else {
@ -2162,6 +2173,15 @@ impl<'a> State<'a> {
fn print_meta_item(&mut self, item: &ast::MetaItem) {
let ib = self.ibox(INDENT_UNIT);
match item.unsafety {
ast::Safety::Unsafe(_) => {
self.word("unsafe");
self.popen();
}
ast::Safety::Default | ast::Safety::Safe(_) => {}
}
match &item.kind {
ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
ast::MetaItemKind::NameValue(value) => {
@ -2177,6 +2197,12 @@ impl<'a> State<'a> {
self.pclose();
}
}
match item.unsafety {
ast::Safety::Unsafe(_) => self.pclose(),
ast::Safety::Default | ast::Safety::Safe(_) => {}
}
self.end(ib);
}

View file

@ -1,6 +1,6 @@
use ast::StaticItem;
use itertools::{Itertools, Position};
use rustc_ast::{self as ast, ModKind, TraitAlias};
use rustc_ast::{self as ast, EiiImpl, ModKind, Safety, TraitAlias};
use rustc_span::Ident;
use crate::pp::BoxMarker;
@ -671,10 +671,25 @@ impl<'a> State<'a> {
}
fn print_fn_full(&mut self, vis: &ast::Visibility, attrs: &[ast::Attribute], func: &ast::Fn) {
let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque } = func;
let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque, eii_impls } =
func;
self.print_define_opaques(define_opaque.as_deref());
for EiiImpl { eii_macro_path, impl_safety, .. } in eii_impls {
self.word("#[");
if let Safety::Unsafe(..) = impl_safety {
self.word("unsafe");
self.popen();
}
self.print_path(eii_macro_path, false, 0);
if let Safety::Unsafe(..) = impl_safety {
self.pclose();
}
self.word("]");
self.hardbreak();
}
let body_cb_ib = body.as_ref().map(|body| (body, self.head("")));
self.print_visibility(vis);

View file

@ -50,11 +50,6 @@ attr_parsing_expects_feature_list =
attr_parsing_expects_features =
`{$name}` expects feature names
attr_parsing_ill_formed_attribute_input = {$num_suggestions ->
[1] attribute must be of the form {$suggestions}
*[other] valid forms for the attribute are {$suggestions}
}
attr_parsing_import_name_type_raw =
import name type can only be used with link kind `raw-dylib`
@ -213,10 +208,6 @@ attr_parsing_stability_outside_std = stability attributes may not be used outsid
attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes
.help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
attr_parsing_unknown_meta_item =
unknown meta item '{$item}'
.label = expected one of {$expected}
attr_parsing_unknown_version_literal =
unknown version literal format, assuming it refers to a future version

View file

@ -42,7 +42,7 @@ pub fn parse_cfg<S: Stage>(
args: &ArgParser,
) -> Option<CfgEntry> {
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
let Some(single) = list.single() else {

View file

@ -25,7 +25,7 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
@ -478,7 +478,7 @@ fn parse_tf_attribute<S: Stage>(
) -> impl IntoIterator<Item = (Symbol, Span)> {
let mut features = Vec::new();
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return features;
};
if list.is_empty() {
@ -601,7 +601,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
@ -699,3 +699,12 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcPassIndirectlyInNonRusticAbisPa
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
}
pub(crate) struct EiiExternItemParser;
impl<S: Stage> NoArgsAttributeParser<S> for EiiExternItemParser {
const PATH: &[Symbol] = &[sym::rustc_eii_extern_item];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::EiiExternItem;
}

View file

@ -13,7 +13,7 @@ impl<S: Stage> AttributeParser<S> for ConfusablesParser {
template!(List: &[r#""name1", "name2", ..."#]),
|this, cx, args| {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return;
};

View file

@ -21,7 +21,7 @@ impl<S: Stage> CombineAttributeParser<S> for DebuggerViualizerParser {
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let Some(l) = args.list() else {
cx.expected_list(args.span().unwrap_or(cx.attr_span));
cx.expected_list(cx.attr_span, args);
return None;
};
let Some(single) = l.single() else {

View file

@ -110,13 +110,12 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
Some(get(cx, name, param.span(), param.args(), &suggestion)?);
}
_ => {
cx.unknown_key(
cx.expected_specific_argument(
param.span(),
param.path().to_string(),
if features.deprecated_suggestion() {
&["since", "note", "suggestion"]
&[sym::since, sym::note, sym::suggestion]
} else {
&["since", "note"]
&[sym::since, sym::note]
},
);
return None;

View file

@ -106,7 +106,7 @@ impl DocParser {
}
Some(sym::attr) => {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return;
};

View file

@ -76,7 +76,7 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
return None;
}
_ => {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
}
};
@ -379,7 +379,7 @@ impl LinkParser {
return true;
}
let Some(link_cfg) = item.args().list() else {
cx.expected_list(item.span());
cx.expected_list(item.span(), item.args());
return true;
};
let Some(link_cfg) = link_cfg.single() else {

View file

@ -1,9 +1,7 @@
use rustc_errors::DiagArgValue;
use rustc_hir::attrs::MacroUseArgs;
use rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS;
use super::prelude::*;
use crate::session_diagnostics::IllFormedAttributeInputLint;
pub(crate) struct MacroEscapeParser;
impl<S: Stage> NoArgsAttributeParser<S> for MacroEscapeParser {
@ -101,15 +99,8 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser {
}
}
}
ArgParser::NameValue(_) => {
let suggestions = cx.suggestions();
cx.emit_err(IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
),
span,
});
ArgParser::NameValue(nv) => {
cx.expected_list_or_no_args(nv.args_span());
}
}
},
@ -164,16 +155,8 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
}
}
}
ArgParser::NameValue(_) => {
let span = cx.attr_span;
let suggestions = cx.suggestions();
cx.emit_err(IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
),
span,
});
ArgParser::NameValue(nv) => {
cx.expected_list_or_no_args(nv.args_span());
return None;
}
};

View file

@ -1,7 +1,4 @@
use rustc_errors::DiagArgValue;
use super::prelude::*;
use crate::session_diagnostics::IllFormedAttributeInputLint;
pub(crate) struct MustUseParser;
@ -44,15 +41,8 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
};
Some(value_str)
}
ArgParser::List(_) => {
let suggestions = cx.suggestions();
cx.emit_err(IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
),
span: cx.attr_span,
});
ArgParser::List(list) => {
cx.expected_nv_or_no_args(list.span);
return None;
}
},

View file

@ -65,7 +65,7 @@ fn parse_derive_like<S: Stage>(
if args.no_args().is_ok() && !trait_name_mandatory {
return Some((None, ThinVec::new()));
}
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
let mut items = list.mixed();
@ -96,7 +96,7 @@ fn parse_derive_like<S: Stage>(
let mut attributes = ThinVec::new();
if let Some(attrs) = items.next() {
let Some(attr_list) = attrs.meta_item() else {
cx.expected_list(attrs.span());
cx.unexpected_literal(attrs.span());
return None;
};
if !attr_list.path().word_is(sym::attributes) {
@ -104,7 +104,7 @@ fn parse_derive_like<S: Stage>(
return None;
}
let Some(attr_list) = attr_list.args().list() else {
cx.expected_list(attrs.span());
cx.expected_list(attrs.span(), attr_list.args());
return None;
};

View file

@ -27,7 +27,7 @@ impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
@ -46,9 +46,8 @@ impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed);
} else if let Some(arg) = meta_item.word_is(sym::phase) {
extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed);
} else if let Some(word) = meta_item.path().word() {
let word = word.to_string();
cx.unknown_key(meta_item.span(), word, &["dialect", "phase"]);
} else if let Some(..) = meta_item.path().word() {
cx.expected_specific_argument(meta_item.span(), &[sym::dialect, sym::phase]);
failed = true;
} else {
cx.expected_name_value(meta_item.span(), None);

View file

@ -33,7 +33,7 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
let mut reprs = Vec::new();
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return reprs;
};
@ -278,7 +278,7 @@ impl AlignParser {
fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
match args {
ArgParser::NoArgs | ArgParser::NameValue(_) => {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
}
ArgParser::List(list) => {
let Some(align) = list.single() else {

View file

@ -295,7 +295,7 @@ pub(crate) fn parse_stability<S: Stage>(
let mut since = None;
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
@ -315,11 +315,7 @@ pub(crate) fn parse_stability<S: Stage>(
insert_value_into_option_or_error(cx, &param, &mut since, word.unwrap())?
}
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param_span,
item: param.path().to_string(),
expected: &["feature", "since"],
});
cx.expected_specific_argument(param_span, &[sym::feature, sym::since]);
return None;
}
}
@ -371,7 +367,7 @@ pub(crate) fn parse_unstability<S: Stage>(
let mut old_name = None;
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
@ -426,11 +422,17 @@ pub(crate) fn parse_unstability<S: Stage>(
insert_value_into_option_or_error(cx, &param, &mut old_name, word.unwrap())?
}
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param.span(),
item: param.path().to_string(),
expected: &["feature", "reason", "issue", "soft", "implied_by", "old_name"],
});
cx.expected_specific_argument(
param.span(),
&[
sym::feature,
sym::reason,
sym::issue,
sym::soft,
sym::implied_by,
sym::old_name,
],
);
return None;
}
}

View file

@ -22,7 +22,7 @@ impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
let mut array = false;
let mut boxed_slice = false;
let Some(args) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
if args.is_empty() {

View file

@ -43,7 +43,7 @@ pub(crate) fn parse_single_integer<S: Stage>(
args: &ArgParser,
) -> Option<u128> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
let Some(single) = list.single() else {

View file

@ -20,8 +20,8 @@ use crate::attributes::allow_unstable::{
};
use crate::attributes::body::CoroutineParser;
use crate::attributes::codegen_attrs::{
ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
ColdParser, CoverageParser, EiiExternItemParser, ExportNameParser, ForceTargetFeatureParser,
NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser,
TrackCallerParser, UsedParser,
};
@ -77,7 +77,7 @@ use crate::attributes::transparency::TransparencyParser;
use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs};
use crate::parser::{ArgParser, RefPathParser};
use crate::session_diagnostics::{
AttributeParseError, AttributeParseErrorReason, ParsedDescription, UnknownMetaItem,
AttributeParseError, AttributeParseErrorReason, ParsedDescription,
};
use crate::target_checking::AllowedTargets;
@ -227,6 +227,7 @@ attribute_parsers!(
Single<WithoutArgs<CoroutineParser>>,
Single<WithoutArgs<DenyExplicitImplParser>>,
Single<WithoutArgs<DoNotImplementViaObjectParser>>,
Single<WithoutArgs<EiiExternItemParser>>,
Single<WithoutArgs<ExportStableParser>>,
Single<WithoutArgs<FfiConstParser>>,
Single<WithoutArgs<FfiPureParser>>,
@ -425,13 +426,20 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
}
impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
pub(crate) fn unknown_key(
fn emit_parse_error(
&self,
span: Span,
found: String,
options: &[&'static str],
reason: AttributeParseErrorReason<'_>,
) -> ErrorGuaranteed {
self.emit_err(UnknownMetaItem { span, item: found, expected: options })
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason,
suggestions: self.suggestions(),
})
}
/// error that a string literal was expected.
@ -443,133 +451,69 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
span: Span,
actual_literal: Option<&MetaItemLit>,
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
self.emit_parse_error(
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedStringLiteral {
AttributeParseErrorReason::ExpectedStringLiteral {
byte_string: actual_literal.and_then(|i| {
i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
}),
},
suggestions: self.suggestions(),
})
)
}
pub(crate) fn expected_integer_literal(&self, span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedIntegerLiteral,
suggestions: self.suggestions(),
})
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral)
}
pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedList,
suggestions: self.suggestions(),
})
pub(crate) fn expected_list(&self, span: Span, args: &ArgParser) -> ErrorGuaranteed {
let span = match args {
ArgParser::NoArgs => span,
ArgParser::List(list) => list.span,
ArgParser::NameValue(nv) => nv.args_span(),
};
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedList)
}
pub(crate) fn expected_no_args(&self, args_span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span: args_span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedNoArgs,
suggestions: self.suggestions(),
})
pub(crate) fn expected_list_or_no_args(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedListOrNoArgs)
}
pub(crate) fn expected_nv_or_no_args(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValueOrNoArgs)
}
pub(crate) fn expected_no_args(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNoArgs)
}
/// emit an error that a `name` was expected here
pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedIdentifier,
suggestions: self.suggestions(),
})
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIdentifier)
}
/// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
/// a nicer error message talking about the specific name that was found lacking a value.
pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedNameValue(name),
suggestions: self.suggestions(),
})
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValue(name))
}
/// emit an error that a `name = value` pair was found where that name was already seen.
pub(crate) fn duplicate_key(&self, span: Span, key: Symbol) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::DuplicateKey(key),
suggestions: self.suggestions(),
})
self.emit_parse_error(span, AttributeParseErrorReason::DuplicateKey(key))
}
/// an error that should be emitted when a [`MetaItemOrLitParser`](crate::parser::MetaItemOrLitParser)
/// was expected *not* to be a literal, but instead a meta item.
pub(crate) fn unexpected_literal(&self, span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::UnexpectedLiteral,
suggestions: self.suggestions(),
})
self.emit_parse_error(span, AttributeParseErrorReason::UnexpectedLiteral)
}
pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedSingleArgument,
suggestions: self.suggestions(),
})
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedSingleArgument)
}
pub(crate) fn expected_at_least_one_argument(&self, span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument,
suggestions: self.suggestions(),
})
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedAtLeastOneArgument)
}
/// produces an error along the lines of `expected one of [foo, meow]`
@ -578,19 +522,14 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
span: Span,
possibilities: &[Symbol],
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
self.emit_parse_error(
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: false,
list: false,
},
suggestions: self.suggestions(),
})
)
}
/// produces an error along the lines of `expected one of [foo, meow] as an argument`.
@ -600,19 +539,14 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
span: Span,
possibilities: &[Symbol],
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
self.emit_parse_error(
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: false,
list: true,
},
suggestions: self.suggestions(),
})
)
}
/// produces an error along the lines of `expected one of ["foo", "meow"]`
@ -621,19 +555,14 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
span: Span,
possibilities: &[Symbol],
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
self.emit_parse_error(
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: true,
list: false,
},
suggestions: self.suggestions(),
})
)
}
pub(crate) fn warn_empty_attribute(&mut self, span: Span) {

View file

@ -177,7 +177,7 @@ impl ArgParser {
match self {
Self::NoArgs => Ok(()),
Self::List(args) => Err(args.span),
Self::NameValue(args) => Err(args.eq_span.to(args.value_span)),
Self::NameValue(args) => Err(args.args_span()),
}
}
}
@ -314,6 +314,10 @@ impl NameValueParser {
pub fn value_as_str(&self) -> Option<Symbol> {
self.value_as_lit().kind.str()
}
pub fn args_span(&self) -> Span {
self.eq_span.to(self.value_span)
}
}
fn expr_to_lit(

View file

@ -64,26 +64,6 @@ pub(crate) struct DocAttributeNotAttribute {
pub attribute: Symbol,
}
/// Error code: E0541
pub(crate) struct UnknownMetaItem<'a> {
pub span: Span,
pub item: String,
pub expected: &'a [&'a str],
}
// Manual implementation to be able to format `expected` items correctly.
impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnknownMetaItem<'_> {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
Diag::new(dcx, level, fluent::attr_parsing_unknown_meta_item)
.with_span(self.span)
.with_code(E0541)
.with_arg("item", self.item)
.with_arg("expected", expected.join(", "))
.with_span_label(self.span, fluent::attr_parsing_label)
}
}
#[derive(Diagnostic)]
#[diag(attr_parsing_missing_since, code = E0542)]
pub(crate) struct MissingSince {
@ -400,15 +380,6 @@ pub(crate) struct UnusedMultiple {
pub name: Symbol,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_ill_formed_attribute_input)]
pub(crate) struct IllFormedAttributeInputLint {
#[primary_span]
pub span: Span,
pub num_suggestions: usize,
pub suggestions: DiagArgValue,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_null_on_export, code = E0648)]
pub(crate) struct NullOnExport {
@ -539,6 +510,8 @@ pub(crate) enum AttributeParseErrorReason<'a> {
ExpectedAtLeastOneArgument,
ExpectedSingleArgument,
ExpectedList,
ExpectedListOrNoArgs,
ExpectedNameValueOrNoArgs,
UnexpectedLiteral,
ExpectedNameValue(Option<Symbol>),
DuplicateKey(Symbol),
@ -611,6 +584,12 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
AttributeParseErrorReason::ExpectedList => {
diag.span_label(self.span, "expected this to be a list");
}
AttributeParseErrorReason::ExpectedListOrNoArgs => {
diag.span_label(self.span, "expected a list or no arguments here");
}
AttributeParseErrorReason::ExpectedNameValueOrNoArgs => {
diag.span_label(self.span, "didn't expect a list here");
}
AttributeParseErrorReason::DuplicateKey(key) => {
diag.span_label(self.span, format!("found `{key}` used as a key more than once"));
diag.code(E0538);

View file

@ -80,7 +80,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let OutlivesConstraint { sup, sub, locations, category, span, .. } = constraint;
let (name, arg) = match locations {
Locations::All(span) => {
("All", tcx.sess.source_map().span_to_embeddable_string(*span))
("All", tcx.sess.source_map().span_to_diagnostic_string(*span))
}
Locations::Single(loc) => ("Single", format!("{loc:?}")),
};

View file

@ -152,6 +152,17 @@ builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept
builtin_macros_duplicate_macro_attribute = duplicated attribute
builtin_macros_eii_extern_target_expected_list = `#[eii_extern_target(...)]` expects a list of one or two elements
builtin_macros_eii_extern_target_expected_macro = `#[eii_extern_target(...)]` is only valid on macros
builtin_macros_eii_extern_target_expected_unsafe = expected this argument to be "unsafe"
.note = the second argument is optional
builtin_macros_eii_only_once = `#[{$name}]` can only be specified once
.note = specified again here
builtin_macros_eii_shared_macro_expected_function = `#[{$name}]` is only valid on functions
builtin_macros_eii_shared_macro_expected_max_one_argument = `#[{$name}]` expected no arguments or a single argument: `#[{$name}(default)]`
builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time
.cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead
.cargo_typo = there is a similar Cargo environment variable: `{$suggested_var}`

View file

@ -90,6 +90,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span
contract: None,
body,
define_opaque: None,
eii_impls: ThinVec::new(),
}));
let attrs = thin_vec![cx.attr_word(sym::rustc_std_internal_symbol, span)];

View file

@ -346,6 +346,7 @@ mod llvm_enzyme {
contract: None,
body: Some(d_body),
define_opaque: None,
eii_impls: ThinVec::new(),
});
let mut rustc_ad_attr =
Box::new(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_autodiff)));

View file

@ -1092,6 +1092,7 @@ impl<'a> MethodDef<'a> {
contract: None,
body: Some(body_block),
define_opaque: None,
eii_impls: ThinVec::new(),
})),
tokens: None,
})

View file

@ -0,0 +1,447 @@
use rustc_ast::token::{Delimiter, TokenKind};
use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
use rustc_ast::{
DUMMY_NODE_ID, EiiExternTarget, EiiImpl, ItemKind, Stmt, StmtKind, ast, token, tokenstream,
};
use rustc_ast_pretty::pprust::path_to_string;
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::{Ident, Span, kw, sym};
use thin_vec::{ThinVec, thin_vec};
use crate::errors::{
EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe,
EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroExpectedFunction,
};
/// ```rust
/// #[eii]
/// fn panic_handler();
///
/// // or:
///
/// #[eii(panic_handler)]
/// fn panic_handler();
///
/// // expansion:
///
/// extern "Rust" {
/// fn panic_handler();
/// }
///
/// #[rustc_builtin_macro(eii_shared_macro)]
/// #[eii_extern_target(panic_handler)]
/// macro panic_handler() {}
/// ```
pub(crate) fn eii(
ecx: &mut ExtCtxt<'_>,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable,
) -> Vec<Annotatable> {
eii_(ecx, span, meta_item, item, false)
}
pub(crate) fn unsafe_eii(
ecx: &mut ExtCtxt<'_>,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable,
) -> Vec<Annotatable> {
eii_(ecx, span, meta_item, item, true)
}
fn eii_(
ecx: &mut ExtCtxt<'_>,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable,
impl_unsafe: bool,
) -> Vec<Annotatable> {
let span = ecx.with_def_site_ctxt(span);
let (item, stmt) = if let Annotatable::Item(item) = item {
(item, false)
} else if let Annotatable::Stmt(ref stmt) = item
&& let StmtKind::Item(ref item) = stmt.kind
{
(item.clone(), true)
} else {
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
span,
name: path_to_string(&meta_item.path),
});
return vec![item];
};
let orig_item = item.clone();
let item = *item;
let ast::Item { attrs, id: _, span: item_span, vis, kind: ItemKind::Fn(mut func), tokens: _ } =
item
else {
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
span,
name: path_to_string(&meta_item.path),
});
return vec![Annotatable::Item(Box::new(item))];
};
// Detect when this is the *second* eii attribute on an item.
let mut new_attrs = ThinVec::new();
for i in attrs {
if i.has_name(sym::eii) {
ecx.dcx().emit_err(EiiOnlyOnce {
span: i.span,
first_span: span,
name: path_to_string(&meta_item.path),
});
} else {
new_attrs.push(i);
}
}
let attrs = new_attrs;
let macro_name = if meta_item.is_word() {
func.ident
} else if let Some([first]) = meta_item.meta_item_list()
&& let Some(m) = first.meta_item()
&& m.path.segments.len() == 1
{
m.path.segments[0].ident
} else {
ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
span: meta_item.span,
name: path_to_string(&meta_item.path),
});
return vec![Annotatable::Item(orig_item)];
};
let mut return_items = Vec::new();
if func.body.is_some() {
let mut default_func = func.clone();
func.body = None;
default_func.eii_impls.push(ast::EiiImpl {
node_id: DUMMY_NODE_ID,
eii_macro_path: ast::Path::from_ident(macro_name),
impl_safety: if impl_unsafe { ast::Safety::Unsafe(span) } else { ast::Safety::Default },
span,
inner_span: macro_name.span,
is_default: true, // important!
});
return_items.push(Box::new(ast::Item {
attrs: ThinVec::new(),
id: ast::DUMMY_NODE_ID,
span,
vis: ast::Visibility { span, kind: ast::VisibilityKind::Inherited, tokens: None },
kind: ast::ItemKind::Const(Box::new(ast::ConstItem {
ident: Ident { name: kw::Underscore, span },
defaultness: ast::Defaultness::Final,
generics: ast::Generics::default(),
ty: Box::new(ast::Ty {
id: DUMMY_NODE_ID,
kind: ast::TyKind::Tup(ThinVec::new()),
span,
tokens: None,
}),
rhs: Some(ast::ConstItemRhs::Body(Box::new(ast::Expr {
id: DUMMY_NODE_ID,
kind: ast::ExprKind::Block(
Box::new(ast::Block {
stmts: thin_vec![ast::Stmt {
id: DUMMY_NODE_ID,
kind: ast::StmtKind::Item(Box::new(ast::Item {
attrs: thin_vec![], // FIXME: re-add some original attrs
id: DUMMY_NODE_ID,
span: item_span,
vis: ast::Visibility {
span,
kind: ast::VisibilityKind::Inherited,
tokens: None
},
kind: ItemKind::Fn(default_func),
tokens: None,
})),
span
}],
id: DUMMY_NODE_ID,
rules: ast::BlockCheckMode::Default,
span,
tokens: None,
}),
None,
),
span,
attrs: ThinVec::new(),
tokens: None,
}))),
define_opaque: None,
})),
tokens: None,
}))
}
let decl_span = span.to(func.sig.span);
let abi = match func.sig.header.ext {
// extern "X" fn => extern "X" {}
ast::Extern::Explicit(lit, _) => Some(lit),
// extern fn => extern {}
ast::Extern::Implicit(_) => None,
// fn => extern "Rust" {}
ast::Extern::None => Some(ast::StrLit {
symbol: sym::Rust,
suffix: None,
symbol_unescaped: sym::Rust,
style: ast::StrStyle::Cooked,
span,
}),
};
// ABI has been moved to the extern {} block, so we remove it from the fn item.
func.sig.header.ext = ast::Extern::None;
// And mark safe functions explicitly as `safe fn`.
if func.sig.header.safety == ast::Safety::Default {
func.sig.header.safety = ast::Safety::Safe(func.sig.span);
}
// extern "…" { safe fn item(); }
let mut extern_item_attrs = attrs.clone();
extern_item_attrs.push(ast::Attribute {
kind: ast::AttrKind::Normal(Box::new(ast::NormalAttr {
item: ast::AttrItem {
unsafety: ast::Safety::Default,
// Add the rustc_eii_extern_item on the foreign item. Usually, foreign items are mangled.
// This attribute makes sure that we later know that this foreign item's symbol should not be.
path: ast::Path::from_ident(Ident::new(sym::rustc_eii_extern_item, span)),
args: ast::AttrArgs::Empty,
tokens: None,
},
tokens: None,
})),
id: ecx.sess.psess.attr_id_generator.mk_attr_id(),
style: ast::AttrStyle::Outer,
span,
});
let extern_block = Box::new(ast::Item {
attrs: ast::AttrVec::default(),
id: ast::DUMMY_NODE_ID,
span,
vis: ast::Visibility { span, kind: ast::VisibilityKind::Inherited, tokens: None },
kind: ast::ItemKind::ForeignMod(ast::ForeignMod {
extern_span: span,
safety: ast::Safety::Unsafe(span),
abi,
items: From::from([Box::new(ast::ForeignItem {
attrs: extern_item_attrs,
id: ast::DUMMY_NODE_ID,
span: item_span,
vis,
kind: ast::ForeignItemKind::Fn(func.clone()),
tokens: None,
})]),
}),
tokens: None,
});
let mut macro_attrs = attrs.clone();
macro_attrs.push(
// #[builtin_macro(eii_shared_macro)]
ast::Attribute {
kind: ast::AttrKind::Normal(Box::new(ast::NormalAttr {
item: ast::AttrItem {
unsafety: ast::Safety::Default,
path: ast::Path::from_ident(Ident::new(sym::rustc_builtin_macro, span)),
args: ast::AttrArgs::Delimited(ast::DelimArgs {
dspan: DelimSpan::from_single(span),
delim: Delimiter::Parenthesis,
tokens: TokenStream::new(vec![tokenstream::TokenTree::token_alone(
token::TokenKind::Ident(sym::eii_shared_macro, token::IdentIsRaw::No),
span,
)]),
}),
tokens: None,
},
tokens: None,
})),
id: ecx.sess.psess.attr_id_generator.mk_attr_id(),
style: ast::AttrStyle::Outer,
span,
},
);
let macro_def = Box::new(ast::Item {
attrs: macro_attrs,
id: ast::DUMMY_NODE_ID,
span,
// pub
vis: ast::Visibility { span, kind: ast::VisibilityKind::Public, tokens: None },
kind: ast::ItemKind::MacroDef(
// macro macro_name
macro_name,
ast::MacroDef {
// { () => {} }
body: Box::new(ast::DelimArgs {
dspan: DelimSpan::from_single(span),
delim: Delimiter::Brace,
tokens: TokenStream::from_iter([
TokenTree::Delimited(
DelimSpan::from_single(span),
DelimSpacing::new(Spacing::Alone, Spacing::Alone),
Delimiter::Parenthesis,
TokenStream::default(),
),
TokenTree::token_alone(TokenKind::FatArrow, span),
TokenTree::Delimited(
DelimSpan::from_single(span),
DelimSpacing::new(Spacing::Alone, Spacing::Alone),
Delimiter::Brace,
TokenStream::default(),
),
]),
}),
macro_rules: false,
// #[eii_extern_target(func.ident)]
eii_extern_target: Some(ast::EiiExternTarget {
extern_item_path: ast::Path::from_ident(func.ident),
impl_unsafe,
span: decl_span,
}),
},
),
tokens: None,
});
return_items.push(extern_block);
return_items.push(macro_def);
if stmt {
return_items
.into_iter()
.map(|i| {
Annotatable::Stmt(Box::new(Stmt {
id: DUMMY_NODE_ID,
kind: StmtKind::Item(i),
span,
}))
})
.collect()
} else {
return_items.into_iter().map(|i| Annotatable::Item(i)).collect()
}
}
pub(crate) fn eii_extern_target(
ecx: &mut ExtCtxt<'_>,
span: Span,
meta_item: &ast::MetaItem,
mut item: Annotatable,
) -> Vec<Annotatable> {
let i = if let Annotatable::Item(ref mut item) = item {
item
} else if let Annotatable::Stmt(ref mut stmt) = item
&& let StmtKind::Item(ref mut item) = stmt.kind
{
item
} else {
ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
return vec![item];
};
let ItemKind::MacroDef(_, d) = &mut i.kind else {
ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
return vec![item];
};
let Some(list) = meta_item.meta_item_list() else {
ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
return vec![item];
};
if list.len() > 2 {
ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
return vec![item];
}
let Some(extern_item_path) = list.get(0).and_then(|i| i.meta_item()).map(|i| i.path.clone())
else {
ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
return vec![item];
};
let impl_unsafe = if let Some(i) = list.get(1) {
if i.lit().and_then(|i| i.kind.str()).is_some_and(|i| i == kw::Unsafe) {
true
} else {
ecx.dcx().emit_err(EiiExternTargetExpectedUnsafe { span: i.span() });
return vec![item];
}
} else {
false
};
d.eii_extern_target = Some(EiiExternTarget { extern_item_path, impl_unsafe, span });
// Return the original item and the new methods.
vec![item]
}
/// all Eiis share this function as the implementation for their attribute.
pub(crate) fn eii_shared_macro(
ecx: &mut ExtCtxt<'_>,
span: Span,
meta_item: &ast::MetaItem,
mut item: Annotatable,
) -> Vec<Annotatable> {
let i = if let Annotatable::Item(ref mut item) = item {
item
} else if let Annotatable::Stmt(ref mut stmt) = item
&& let StmtKind::Item(ref mut item) = stmt.kind
{
item
} else {
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
span,
name: path_to_string(&meta_item.path),
});
return vec![item];
};
let ItemKind::Fn(f) = &mut i.kind else {
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
span,
name: path_to_string(&meta_item.path),
});
return vec![item];
};
let is_default = if meta_item.is_word() {
false
} else if let Some([first]) = meta_item.meta_item_list()
&& let Some(m) = first.meta_item()
&& m.path.segments.len() == 1
{
m.path.segments[0].ident.name == kw::Default
} else {
ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
span: meta_item.span,
name: path_to_string(&meta_item.path),
});
return vec![item];
};
f.eii_impls.push(EiiImpl {
node_id: DUMMY_NODE_ID,
eii_macro_path: meta_item.path.clone(),
impl_safety: meta_item.unsafety,
span,
inner_span: meta_item.path.span,
is_default,
});
vec![item]
}

View file

@ -1010,3 +1010,51 @@ pub(crate) struct CfgSelectUnreachable {
#[label]
pub wildcard_span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_eii_extern_target_expected_macro)]
pub(crate) struct EiiExternTargetExpectedMacro {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_eii_extern_target_expected_list)]
pub(crate) struct EiiExternTargetExpectedList {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_eii_extern_target_expected_unsafe)]
pub(crate) struct EiiExternTargetExpectedUnsafe {
#[primary_span]
#[note]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_eii_shared_macro_expected_function)]
pub(crate) struct EiiSharedMacroExpectedFunction {
#[primary_span]
pub span: Span,
pub name: String,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_eii_only_once)]
pub(crate) struct EiiOnlyOnce {
#[primary_span]
pub span: Span,
#[note]
pub first_span: Span,
pub name: String,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_eii_shared_macro_expected_max_one_argument)]
pub(crate) struct EiiMacroExpectedMaxOneArgument {
#[primary_span]
pub span: Span,
pub name: String,
}

View file

@ -84,6 +84,7 @@ impl AllocFnFactory<'_, '_> {
contract: None,
body,
define_opaque: None,
eii_impls: ThinVec::new(),
}));
let item = self.cx.item(self.span, self.attrs(method), kind);
self.cx.stmt_item(self.ty_span, item)

View file

@ -38,6 +38,7 @@ mod define_opaque;
mod derive;
mod deriving;
mod edition_panic;
mod eii;
mod env;
mod errors;
mod format;
@ -117,9 +118,13 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
define_opaque: define_opaque::expand,
derive: derive::Expander { is_const: false },
derive_const: derive::Expander { is_const: true },
eii: eii::eii,
eii_extern_target: eii::eii_extern_target,
eii_shared_macro: eii::eii_shared_macro,
global_allocator: global_allocator::expand,
test: test::expand_test,
test_case: test::expand_test_case,
unsafe_eii: eii::unsafe_eii,
// tidy-alphabetical-end
}

View file

@ -68,13 +68,10 @@ pub(crate) fn expand_file(
let topmost = cx.expansion_cause().unwrap_or(sp);
let loc = cx.source_map().lookup_char_pos(topmost.lo());
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
use rustc_span::RemapPathScopeComponents;
ExpandResult::Ready(MacEager::expr(cx.expr_str(
topmost,
Symbol::intern(
&loc.file.name.for_scope(cx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(),
),
Symbol::intern(&loc.file.name.display(RemapPathScopeComponents::MACRO).to_string_lossy()),
)))
}

View file

@ -11,7 +11,7 @@ use rustc_errors::{Applicability, Diag, Level};
use rustc_expand::base::*;
use rustc_hir::Attribute;
use rustc_hir::attrs::AttributeKind;
use rustc_span::{ErrorGuaranteed, FileNameDisplayPreference, Ident, Span, Symbol, sym};
use rustc_span::{ErrorGuaranteed, Ident, RemapPathScopeComponents, Span, Symbol, sym};
use thin_vec::{ThinVec, thin_vec};
use tracing::debug;
@ -445,7 +445,7 @@ fn get_location_info(cx: &ExtCtxt<'_>, fn_: &ast::Fn) -> (Symbol, usize, usize,
cx.sess.source_map().span_to_location_info(span);
let file_name = match source_file {
Some(sf) => sf.name.display(FileNameDisplayPreference::Remapped).to_string(),
Some(sf) => sf.name.display(RemapPathScopeComponents::MACRO).to_string(),
None => "no-location".to_string(),
};

View file

@ -345,6 +345,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box<ast::Item> {
contract: None,
body: Some(main_body),
define_opaque: None,
eii_impls: ThinVec::new(),
}));
let main = Box::new(ast::Item {

View file

@ -6,7 +6,10 @@ use std::path::{Component, Path};
use cranelift_codegen::MachSrcLoc;
use cranelift_codegen::binemit::CodeOffset;
use gimli::write::{FileId, FileInfo, LineProgram, LineString, LineStringTable};
use rustc_span::{FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHashAlgorithm, hygiene};
use rustc_span::{
FileName, Pos, RemapPathScopeComponents, SourceFile, SourceFileAndLine,
SourceFileHashAlgorithm, hygiene,
};
use crate::debuginfo::FunctionDebugContext;
use crate::debuginfo::emit::address_for_func;
@ -95,7 +98,7 @@ impl DebugContext {
match &source_file.name {
FileName::Real(path) => {
let (dir_path, file_name) =
split_path_dir_and_file(path.to_path(self.filename_display_preference));
split_path_dir_and_file(path.path(RemapPathScopeComponents::DEBUGINFO));
let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str());
let file_name = osstr_as_utf8_bytes(file_name);

View file

@ -21,7 +21,7 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefIdMap;
use rustc_session::Session;
use rustc_session::config::DebugInfo;
use rustc_span::{FileNameDisplayPreference, SourceFileHash, StableSourceFileId};
use rustc_span::{RemapPathScopeComponents, SourceFileHash, StableSourceFileId};
use rustc_target::callconv::FnAbi;
pub(crate) use self::emit::{DebugReloc, DebugRelocName};
@ -44,7 +44,6 @@ pub(crate) struct DebugContext {
namespace_map: DefIdMap<UnitEntryId>,
array_size_type: Option<UnitEntryId>,
filename_display_preference: FileNameDisplayPreference,
embed_source: bool,
}
@ -102,18 +101,18 @@ impl DebugContext {
let mut dwarf = DwarfUnit::new(encoding);
use rustc_session::config::RemapPathScopeComponents;
let filename_display_preference =
tcx.sess.filename_display_preference(RemapPathScopeComponents::DEBUGINFO);
let producer = producer(tcx.sess);
let comp_dir =
tcx.sess.opts.working_dir.to_string_lossy(filename_display_preference).to_string();
let comp_dir = tcx
.sess
.source_map()
.working_dir()
.path(RemapPathScopeComponents::DEBUGINFO)
.to_string_lossy();
let (name, file_info) = match tcx.sess.local_crate_source_file() {
Some(path) => {
let name = path.to_string_lossy(filename_display_preference).to_string();
let name =
path.path(RemapPathScopeComponents::DEBUGINFO).to_string_lossy().into_owned();
(name, None)
}
None => (tcx.crate_name(LOCAL_CRATE).to_string(), None),
@ -137,7 +136,7 @@ impl DebugContext {
{
let name = dwarf.strings.add(format!("{name}/@/{cgu_name}"));
let comp_dir = dwarf.strings.add(comp_dir);
let comp_dir = dwarf.strings.add(&*comp_dir);
let root = dwarf.unit.root();
let root = dwarf.unit.get_mut(root);
@ -180,7 +179,6 @@ impl DebugContext {
stack_pointer_register,
namespace_map: DefIdMap::default(),
array_size_type,
filename_display_preference,
embed_source,
})
}

View file

@ -297,29 +297,11 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
let pos = span.lo();
let DebugLoc { file, line, col } = self.lookup_debug_loc(pos);
match file.name {
rustc_span::FileName::Real(ref name) => match *name {
rustc_span::RealFileName::LocalPath(ref name) => {
if let Some(name) = name.to_str() {
self.context.new_location(name, line as i32, col as i32)
} else {
Location::null()
}
}
rustc_span::RealFileName::Remapped {
ref local_path,
virtual_name: ref _unused,
} => {
if let Some(name) = local_path.as_ref() {
if let Some(name) = name.to_str() {
self.context.new_location(name, line as i32, col as i32)
} else {
Location::null()
}
} else {
Location::null()
}
}
},
rustc_span::FileName::Real(ref name) => self.context.new_location(
name.path(rustc_span::RemapPathScopeComponents::DEBUGINFO).to_string_lossy(),
line as i32,
col as i32,
),
_ => Location::null(),
}
}

View file

@ -98,7 +98,6 @@ use rustc_middle::ty::TyCtxt;
use rustc_middle::util::Providers;
use rustc_session::Session;
use rustc_session::config::{OptLevel, OutputFilenames};
use rustc_session::filesearch::make_target_lib_path;
use rustc_span::Symbol;
use rustc_target::spec::{Arch, RelocModel};
use tempfile::TempDir;
@ -207,18 +206,38 @@ impl CodegenBackend for GccCodegenBackend {
}
fn init(&self, sess: &Session) {
fn file_path(sysroot_path: &Path, sess: &Session) -> PathBuf {
let rustlib_path =
rustc_target::relative_target_rustlib_path(sysroot_path, &sess.host.llvm_target);
sysroot_path
.join(rustlib_path)
.join("codegen-backends")
.join("lib")
.join(sess.target.llvm_target.as_ref())
.join("libgccjit.so")
}
// We use all_paths() instead of only path() in case the path specified by --sysroot is
// invalid.
// This is the case for instance in Rust for Linux where they specify --sysroot=/dev/null.
for path in sess.opts.sysroot.all_paths() {
let libgccjit_target_lib_file =
make_target_lib_path(path, &sess.target.llvm_target).join("libgccjit.so");
let libgccjit_target_lib_file = file_path(path, sess);
if let Ok(true) = fs::exists(&libgccjit_target_lib_file) {
load_libgccjit_if_needed(&libgccjit_target_lib_file);
break;
}
}
if !gccjit::is_loaded() {
let mut paths = vec![];
for path in sess.opts.sysroot.all_paths() {
let libgccjit_target_lib_file = file_path(path, sess);
paths.push(libgccjit_target_lib_file);
}
panic!("Could not load libgccjit.so. Attempted paths: {:#?}", paths);
}
#[cfg(feature = "master")]
{
let target_cpu = target_cpu(sess);

View file

@ -21,10 +21,8 @@ use rustc_errors::{DiagCtxtHandle, Level};
use rustc_fs_util::{link_or_copy, path_to_c_string};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_session::config::{
self, Lto, OutputType, Passes, RemapPathScopeComponents, SplitDwarfKind, SwitchWithOptPath,
};
use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext, sym};
use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, SwitchWithOptPath};
use rustc_span::{BytePos, InnerSpan, Pos, RemapPathScopeComponents, SpanData, SyntaxContext, sym};
use rustc_target::spec::{
Arch, CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel,
};
@ -248,6 +246,7 @@ pub(crate) fn target_machine_factory(
!sess.opts.unstable_opts.use_ctors_section.unwrap_or(sess.target.use_ctors_section);
let path_mapping = sess.source_map().path_mapping().clone();
let working_dir = sess.source_map().working_dir().clone();
let use_emulated_tls = matches!(sess.tls_model(), TlsModel::Emulated);
@ -271,9 +270,6 @@ pub(crate) fn target_machine_factory(
}
};
let file_name_display_preference =
sess.filename_display_preference(RemapPathScopeComponents::DEBUGINFO);
let use_wasm_eh = wants_wasm_eh(sess);
let prof = SelfProfilerRef::clone(&sess.prof);
@ -284,8 +280,9 @@ pub(crate) fn target_machine_factory(
let path_to_cstring_helper = |path: Option<PathBuf>| -> CString {
let path = path.unwrap_or_default();
let path = path_mapping
.to_real_filename(path)
.to_string_lossy(file_name_display_preference)
.to_real_filename(&working_dir, path)
.path(RemapPathScopeComponents::DEBUGINFO)
.to_string_lossy()
.into_owned();
CString::new(path).unwrap()
};

View file

@ -7,9 +7,7 @@ use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods};
use rustc_data_structures::fx::FxIndexMap;
use rustc_index::IndexVec;
use rustc_middle::ty::TyCtxt;
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
use rustc_span::{SourceFile, StableSourceFileId};
use rustc_span::{RemapPathScopeComponents, SourceFile, StableSourceFileId};
use tracing::debug;
use crate::common::CodegenCx;
@ -127,10 +125,7 @@ impl GlobalFileTable {
for file in all_files {
raw_file_table.entry(file.stable_id).or_insert_with(|| {
file.name
.for_scope(tcx.sess, RemapPathScopeComponents::COVERAGE)
.to_string_lossy()
.into_owned()
file.name.display(RemapPathScopeComponents::COVERAGE).to_string_lossy().into_owned()
});
}
@ -145,9 +140,10 @@ impl GlobalFileTable {
// resolve any other entries that are stored as relative paths.
let base_dir = tcx
.sess
.opts
.working_dir
.for_scope(tcx.sess, RemapPathScopeComponents::COVERAGE)
.psess
.source_map()
.working_dir()
.path(RemapPathScopeComponents::COVERAGE)
.to_string_lossy();
table.push(base_dir.as_ref());

View file

@ -1,7 +1,7 @@
use std::borrow::Cow;
use std::fmt::{self, Write};
use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::sync::Arc;
use std::{iter, ptr};
@ -19,9 +19,7 @@ use rustc_middle::ty::{
self, AdtKind, CoroutineArgsExt, ExistentialTraitRef, Instance, Ty, TyCtxt, Visibility,
};
use rustc_session::config::{self, DebugInfo, Lto};
use rustc_span::{
DUMMY_SP, FileName, FileNameDisplayPreference, SourceFile, Span, Symbol, hygiene,
};
use rustc_span::{DUMMY_SP, FileName, RemapPathScopeComponents, SourceFile, Span, Symbol, hygiene};
use rustc_symbol_mangling::typeid_for_trait_ref;
use rustc_target::spec::DebuginfoKind;
use smallvec::smallvec;
@ -555,79 +553,38 @@ pub(crate) fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFi
) -> &'ll DIFile {
debug!(?source_file.name);
let filename_display_preference =
cx.sess().filename_display_preference(RemapPathScopeComponents::DEBUGINFO);
use rustc_session::config::RemapPathScopeComponents;
let (directory, file_name) = match &source_file.name {
FileName::Real(filename) => {
let working_directory = &cx.sess().opts.working_dir;
debug!(?working_directory);
let (working_directory, embeddable_name) =
filename.embeddable_name(RemapPathScopeComponents::DEBUGINFO);
if filename_display_preference == FileNameDisplayPreference::Remapped {
let filename = cx
.sess()
.source_map()
.path_mapping()
.to_embeddable_absolute_path(filename.clone(), working_directory);
debug!(?working_directory, ?embeddable_name);
// Construct the absolute path of the file
let abs_path = filename.remapped_path_if_available();
debug!(?abs_path);
if let Ok(rel_path) =
abs_path.strip_prefix(working_directory.remapped_path_if_available())
{
// If the compiler's working directory (which also is the DW_AT_comp_dir of
// the compilation unit) is a prefix of the path we are about to emit, then
// only emit the part relative to the working directory. Because of path
// remapping we sometimes see strange things here: `abs_path` might
// actually look like a relative path (e.g.
// `<crate-name-and-version>/src/lib.rs`), so if we emit it without taking
// the working directory into account, downstream tooling will interpret it
// as `<working-directory>/<crate-name-and-version>/src/lib.rs`, which
// makes no sense. Usually in such cases the working directory will also be
// remapped to `<crate-name-and-version>` or some other prefix of the path
// we are remapping, so we end up with
// `<crate-name-and-version>/<crate-name-and-version>/src/lib.rs`.
// By moving the working directory portion into the `directory` part of the
// DIFile, we allow LLVM to emit just the relative path for DWARF, while
// still emitting the correct absolute path for CodeView.
(
working_directory.to_string_lossy(FileNameDisplayPreference::Remapped),
rel_path.to_string_lossy().into_owned(),
)
} else {
("".into(), abs_path.to_string_lossy().into_owned())
}
if let Ok(rel_path) = embeddable_name.strip_prefix(working_directory) {
// If the compiler's working directory (which also is the DW_AT_comp_dir of
// the compilation unit) is a prefix of the path we are about to emit, then
// only emit the part relative to the working directory. Because of path
// remapping we sometimes see strange things here: `abs_path` might
// actually look like a relative path (e.g.
// `<crate-name-and-version>/src/lib.rs`), so if we emit it without taking
// the working directory into account, downstream tooling will interpret it
// as `<working-directory>/<crate-name-and-version>/src/lib.rs`, which
// makes no sense. Usually in such cases the working directory will also be
// remapped to `<crate-name-and-version>` or some other prefix of the path
// we are remapping, so we end up with
// `<crate-name-and-version>/<crate-name-and-version>/src/lib.rs`.
//
// By moving the working directory portion into the `directory` part of the
// DIFile, we allow LLVM to emit just the relative path for DWARF, while
// still emitting the correct absolute path for CodeView.
(working_directory.to_string_lossy(), rel_path.to_string_lossy().into_owned())
} else {
let working_directory = working_directory.local_path_if_available();
let filename = filename.local_path_if_available();
debug!(?working_directory, ?filename);
let abs_path: Cow<'_, Path> = if filename.is_absolute() {
filename.into()
} else {
let mut p = PathBuf::new();
p.push(working_directory);
p.push(filename);
p.into()
};
if let Ok(rel_path) = abs_path.strip_prefix(working_directory) {
(
working_directory.to_string_lossy(),
rel_path.to_string_lossy().into_owned(),
)
} else {
("".into(), abs_path.to_string_lossy().into_owned())
}
("".into(), embeddable_name.to_string_lossy().into_owned())
}
}
other => {
debug!(?other);
("".into(), other.display(filename_display_preference).to_string())
("".into(), other.display(RemapPathScopeComponents::DEBUGINFO).to_string())
}
};
@ -889,12 +846,10 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>(
codegen_unit_name: &str,
debug_context: &CodegenUnitDebugContext<'ll, 'tcx>,
) -> &'ll DIDescriptor {
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
let mut name_in_debuginfo = tcx
.sess
.local_crate_source_file()
.map(|src| src.for_scope(&tcx.sess, RemapPathScopeComponents::DEBUGINFO).to_path_buf())
.map(|src| src.path(RemapPathScopeComponents::DEBUGINFO).to_path_buf())
.unwrap_or_else(|| PathBuf::from(tcx.crate_name(LOCAL_CRATE).as_str()));
// To avoid breaking split DWARF, we need to ensure that each codegen unit
@ -923,12 +878,7 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>(
let producer = format!("clang LLVM ({rustc_producer})");
let name_in_debuginfo = name_in_debuginfo.to_string_lossy();
let work_dir = tcx
.sess
.opts
.working_dir
.for_scope(tcx.sess, RemapPathScopeComponents::DEBUGINFO)
.to_string_lossy();
let work_dir = tcx.sess.psess.source_map().working_dir();
let output_filenames = tcx.output_filenames(());
let split_name = if tcx.sess.target_can_use_split_dwarf()
&& let Some(f) = output_filenames.split_dwarf_path(
@ -938,14 +888,15 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>(
tcx.sess.invocation_temp.as_deref(),
) {
// We get a path relative to the working directory from split_dwarf_path
Some(tcx.sess.source_map().path_mapping().to_real_filename(f))
Some(tcx.sess.source_map().path_mapping().to_real_filename(work_dir, f))
} else {
None
};
let split_name = split_name
.as_ref()
.map(|f| f.for_scope(tcx.sess, RemapPathScopeComponents::DEBUGINFO).to_string_lossy())
.map(|f| f.path(RemapPathScopeComponents::DEBUGINFO).to_string_lossy())
.unwrap_or_default();
let work_dir = work_dir.path(RemapPathScopeComponents::DEBUGINFO).to_string_lossy();
let kind = DebugEmissionKind::from_generic(tcx.sess.opts.debuginfo);
let dwarf_version = tcx.sess.dwarf_version();

View file

@ -2583,4 +2583,12 @@ unsafe extern "C" {
pub(crate) fn LLVMRustSetNoSanitizeAddress(Global: &Value);
pub(crate) fn LLVMRustSetNoSanitizeHWAddress(Global: &Value);
pub(crate) fn LLVMAddAlias2<'ll>(
M: &'ll Module,
ValueTy: &Type,
AddressSpace: c_uint,
Aliasee: &Value,
Name: *const c_char,
) -> &'ll Value;
}

View file

@ -6,7 +6,7 @@ use std::ptr;
use std::string::FromUtf8Error;
use libc::c_uint;
use rustc_abi::{Align, Size, WrappingRange};
use rustc_abi::{AddressSpace, Align, Size, WrappingRange};
use rustc_llvm::RustString;
pub(crate) use self::CallConv::*;
@ -452,3 +452,14 @@ pub(crate) fn append_module_inline_asm<'ll>(llmod: &'ll Module, asm: &[u8]) {
LLVMAppendModuleInlineAsm(llmod, asm.as_ptr(), asm.len());
}
}
/// Safe wrapper for `LLVMAddAlias2`
pub(crate) fn add_alias<'ll>(
module: &'ll Module,
ty: &Type,
address_space: AddressSpace,
aliasee: &Value,
name: &CStr,
) -> &'ll Value {
unsafe { LLVMAddAlias2(module, ty, address_space.0, aliasee, name.as_ptr()) }
}

View file

@ -1,3 +1,6 @@
use std::ffi::CString;
use rustc_abi::AddressSpace;
use rustc_codegen_ssa::traits::*;
use rustc_hir::attrs::Linkage;
use rustc_hir::def::DefKind;
@ -7,6 +10,7 @@ use rustc_middle::mir::mono::Visibility;
use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf};
use rustc_middle::ty::{self, Instance, TypeVisitableExt};
use rustc_session::config::CrateType;
use rustc_span::Symbol;
use rustc_target::spec::{Arch, RelocModel};
use tracing::debug;
@ -41,6 +45,9 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
llvm::set_visibility(g, base::visibility_to_llvm(visibility));
self.assume_dso_local(g, false);
let attrs = self.tcx.codegen_instance_attrs(instance.def);
self.add_aliases(g, &attrs.foreign_item_symbol_aliases);
self.instances.borrow_mut().insert(instance, g);
}
@ -78,11 +85,31 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
self.assume_dso_local(lldecl, false);
self.add_aliases(lldecl, &attrs.foreign_item_symbol_aliases);
self.instances.borrow_mut().insert(instance, lldecl);
}
}
impl CodegenCx<'_, '_> {
fn add_aliases(&self, aliasee: &llvm::Value, aliases: &[(Symbol, Linkage, Visibility)]) {
let ty = self.get_type_of_global(aliasee);
for (alias, linkage, visibility) in aliases {
tracing::debug!("ALIAS: {alias:?} {linkage:?} {visibility:?}");
let lldecl = llvm::add_alias(
self.llmod,
ty,
AddressSpace::ZERO,
aliasee,
&CString::new(alias.as_str()).unwrap(),
);
llvm::set_visibility(lldecl, base::visibility_to_llvm(*visibility));
llvm::set_linkage(lldecl, base::linkage_to_llvm(*linkage));
}
}
/// Whether 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.

View file

@ -277,7 +277,7 @@ pub fn each_linked_rlib(
}
let crate_name = info.crate_name[&cnum];
let used_crate_source = &info.used_crate_source[&cnum];
if let Some((path, _)) = &used_crate_source.rlib {
if let Some(path) = &used_crate_source.rlib {
f(cnum, path);
} else if used_crate_source.rmeta.is_some() {
return Err(errors::LinkRlibError::OnlyRmetaFound { crate_name });
@ -541,7 +541,7 @@ fn link_staticlib(
};
let crate_name = codegen_results.crate_info.crate_name[&cnum];
let used_crate_source = &codegen_results.crate_info.used_crate_source[&cnum];
if let Some((path, _)) = &used_crate_source.dylib {
if let Some(path) = &used_crate_source.dylib {
all_rust_dylibs.push(&**path);
} else if used_crate_source.rmeta.is_some() {
sess.dcx().emit_fatal(errors::LinkRlibError::OnlyRmetaFound { crate_name });
@ -619,7 +619,6 @@ fn link_dwarf_object(sess: &Session, cg_results: &CodegenResults, executable_out
.used_crate_source
.items()
.filter_map(|(_, csource)| csource.rlib.as_ref())
.map(|(path, _)| path)
.into_sorted_stable_ord();
for input_rlib in input_rlibs {
@ -2173,12 +2172,7 @@ fn add_rpath_args(
.crate_info
.used_crates
.iter()
.filter_map(|cnum| {
codegen_results.crate_info.used_crate_source[cnum]
.dylib
.as_ref()
.map(|(path, _)| &**path)
})
.filter_map(|cnum| codegen_results.crate_info.used_crate_source[cnum].dylib.as_deref())
.collect::<Vec<_>>();
let rpath_config = RPathConfig {
libs: &*libs,
@ -2656,7 +2650,7 @@ fn add_native_libs_from_crate(
if link_static && cnum != LOCAL_CRATE && !bundled_libs.is_empty() {
// If rlib contains native libs as archives, unpack them to tmpdir.
let rlib = &codegen_results.crate_info.used_crate_source[&cnum].rlib.as_ref().unwrap().0;
let rlib = codegen_results.crate_info.used_crate_source[&cnum].rlib.as_ref().unwrap();
archive_builder_builder
.extract_bundled_libs(rlib, tmpdir, bundled_libs)
.unwrap_or_else(|e| sess.dcx().emit_fatal(e));
@ -2827,7 +2821,7 @@ fn add_upstream_rust_crates(
}
Linkage::Dynamic => {
let src = &codegen_results.crate_info.used_crate_source[&cnum];
add_dynamic_crate(cmd, sess, &src.dylib.as_ref().unwrap().0);
add_dynamic_crate(cmd, sess, src.dylib.as_ref().unwrap());
}
}
@ -2955,7 +2949,7 @@ fn add_static_crate(
bundled_lib_file_names: &FxIndexSet<Symbol>,
) {
let src = &codegen_results.crate_info.used_crate_source[&cnum];
let cratepath = &src.rlib.as_ref().unwrap().0;
let cratepath = src.rlib.as_ref().unwrap();
let mut link_upstream =
|path: &Path| cmd.link_staticlib_by_path(&rehome_lib_path(sess, path), false);

View file

@ -127,7 +127,10 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER),
rustc_std_internal_symbol: codegen_attrs
.flags
.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL),
.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
|| codegen_attrs
.flags
.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM),
};
(def_id.to_def_id(), info)
})
@ -519,10 +522,12 @@ fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel
let is_extern = codegen_fn_attrs.contains_extern_indicator();
let std_internal =
codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
let eii = codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM);
if is_extern && !std_internal {
if is_extern && !std_internal && !eii {
let target = &tcx.sess.target.llvm_target;
// WebAssembly cannot export data symbols, so reduce their export level
// FIXME(jdonszelmann) don't do a substring match here.
if target.contains("emscripten") {
if let DefKind::Static { .. } = tcx.def_kind(sym_def_id) {
return SymbolExportLevel::Rust;

View file

@ -3,19 +3,22 @@ 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, InstructionSetAttr, RtsanSetting, UsedBy};
use rustc_hir::attrs::{
AttributeKind, InlineAttr, InstructionSetAttr, 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};
use rustc_middle::middle::codegen_fn_attrs::{
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs,
};
use rustc_middle::mir::mono::Visibility;
use rustc_middle::query::Providers;
use rustc_middle::span_bug;
use rustc_middle::ty::{self as ty, TyCtxt};
use rustc_middle::ty::{self as ty, Instance, TyCtxt};
use rustc_session::lint;
use rustc_session::parse::feature_err;
use rustc_span::{Ident, Span, sym};
use rustc_span::{Ident, Span, Symbol, sym};
use rustc_target::spec::Os;
use crate::errors;
@ -310,6 +313,43 @@ fn process_builtin_attrs(
AttributeKind::ObjcSelector { methname, .. } => {
codegen_fn_attrs.objc_selector = Some(*methname);
}
AttributeKind::EiiExternItem => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM;
}
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 symbol_name = tcx.symbol_name(Instance::mono(tcx, extern_item));
// 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
// what happens, either rustc deduplicates the symbol or llvm, or it's random/order-dependent.
// However, the fact that the default one of has weak linkage isn't considered and you sometimes get that
// the default implementation is used while an explicit implementation is given.
if
// if this is a default impl
i.is_default
// 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)
{
continue;
}
codegen_fn_attrs.foreign_item_symbol_aliases.push((
Symbol::intern(symbol_name.name),
if i.is_default { Linkage::LinkOnceAny } else { Linkage::External },
Visibility::Default,
));
codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM;
}
}
_ => {}
}
}
@ -411,6 +451,12 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code
// * `#[rustc_std_internal_symbol]` mangles the symbol name in a special way
// both for exports and imports through foreign items. This is handled further,
// during symbol mangling logic.
} else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM)
{
// * externally implementable items keep their mangled symbol name.
// multiple EIIs can have the same name, so not mangling them would be a bug.
// Implementing an EII does the appropriate name resolution to make sure the implementations
// get the same symbol name as the *mangled* foreign item they refer to so that's all good.
} else if codegen_fn_attrs.symbol_name.is_some() {
// * This can be overridden with the `#[link_name]` attribute
} else {

View file

@ -208,15 +208,10 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
use rustc_span::RemapPathScopeComponents;
(
Symbol::intern(
&caller
.file
.name
.for_scope(self.tcx.sess, RemapPathScopeComponents::DIAGNOSTICS)
.to_string_lossy(),
&caller.file.name.display(RemapPathScopeComponents::DIAGNOSTICS).to_string_lossy(),
),
u32::try_from(caller.line).unwrap(),
u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(),

View file

@ -1,6 +1,6 @@
use std::hash::BuildHasherDefault;
pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
pub use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet, FxHasher};
pub type StdEntry<'a, K, V> = std::collections::hash_map::Entry<'a, K, V>;

View file

@ -181,7 +181,7 @@ impl<'tcx> pprust_hir::PpAnn for HirTypedAnn<'tcx> {
}
fn get_source(sess: &Session) -> (String, FileName) {
let src_name = sess.io.input.source_name();
let src_name = sess.io.input.file_name(&sess);
let src = String::clone(
sess.source_map()
.get_source_file(&src_name)

View file

@ -1,8 +1,10 @@
#### Note: this error code is no longer emitted by the compiler.
An unknown meta item was used.
Erroneous code example:
```compile_fail,E0541
```compile_fail (no longer emitted)
#[deprecated(
since="1.0.0",
// error: unknown meta item

View file

@ -0,0 +1,68 @@
An externally implementable item is not compatible with its declaration.
Erroneous code example:
```rust,edition2021,compile_fail,E0806
#![feature(extern_item_impls)]
#[eii(foo)]
fn x();
#[foo]
fn y(a: u64) -> u64 {
//~^ ERROR E0806
a
}
fn main() {}
```
The error here is caused by the fact that `y` implements the
externally implementable item `foo`.
It can only do so if the signature of the implementation `y` matches
that of the declaration of `foo`.
So, to fix this, `y`'s signature must be changed to match that of `x`:
```rust,edition2021
#![feature(extern_item_impls)]
#[eii(foo)]
fn x();
#[foo]
fn y() {}
fn main() {}
```
One common way this can be triggered is by using the wrong
signature for `#[panic_handler]`.
The signature is provided by `core`.
```rust,edition2021,ignore
#![no_std]
#[panic_handler]
fn on_panic() -> ! {
//~^ ERROR E0806
loop {}
}
fn main() {}
```
Should be:
```rust,edition2021,ignore
#![no_std]
#[panic_handler]
fn on_panic(info: &core::panic::PanicInfo<'_>) -> ! {
loop {}
}
fn main() {}
```

View file

@ -543,6 +543,7 @@ macro_rules! error_codes {
0803,
0804,
0805,
0806,
);
)
}

View file

@ -11,7 +11,7 @@
use std::error::Report;
use std::io::{self, Write};
use std::path::Path;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::vec;
@ -20,9 +20,9 @@ use derive_setters::Setters;
use rustc_data_structures::sync::IntoDynSyncSend;
use rustc_error_messages::FluentArgs;
use rustc_lint_defs::Applicability;
use rustc_span::Span;
use rustc_span::hygiene::ExpnData;
use rustc_span::source_map::{FilePathMapping, SourceMap};
use rustc_span::{FileName, RealFileName, Span};
use serde::Serialize;
use crate::annotate_snippet_emitter_writer::AnnotateSnippetEmitter;
@ -490,8 +490,14 @@ impl DiagnosticSpan {
None => {
span = rustc_span::DUMMY_SP;
empty_source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
empty_source_map
.new_source_file(std::path::PathBuf::from("empty.rs").into(), String::new());
empty_source_map.new_source_file(
FileName::Real(
empty_source_map
.path_mapping()
.to_real_filename(&RealFileName::empty(), PathBuf::from("empty.rs")),
),
String::new(),
);
&empty_source_map
}
};

View file

@ -36,11 +36,15 @@ impl<T: Write> Write for Shared<T> {
}
}
fn filename(sm: &SourceMap, path: &str) -> FileName {
FileName::Real(sm.path_mapping().to_real_filename(sm.working_dir(), PathBuf::from(path)))
}
/// Test the span yields correct positions in JSON.
fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
rustc_span::create_default_session_globals_then(|| {
let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned());
sm.new_source_file(filename(&sm, "test.rs"), code.to_owned());
let translator =
Translator::with_fallback_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);

View file

@ -470,7 +470,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
FileName::Real(name) => name
.into_local_path()
.expect("attempting to resolve a file path in an external file"),
other => PathBuf::from(other.prefer_local().to_string()),
other => PathBuf::from(other.prefer_local_unconditionally().to_string()),
};
let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();
self.cx.root_path = dir_path.clone();

View file

@ -138,10 +138,7 @@ pub(crate) fn update_macro_stats(
if false {
let name = ExpnKind::Macro(macro_kind, name).descr();
let crate_name = &ecx.ecfg.crate_name;
let span = ecx
.sess
.source_map()
.span_to_string(span, rustc_span::FileNameDisplayPreference::Local);
let span = ecx.sess.source_map().span_to_diagnostic_string(span);
eprint!(
"\
-------------------------------\n\

View file

@ -961,6 +961,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
allow_internal_unsafe, Normal, template!(Word), WarnFollowing,
EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint",
),
gated!(
rustc_eii_extern_item, Normal, template!(Word),
ErrorFollowing, EncodeCrossCrate::Yes, eii_internals,
"used internally to mark types with a `transparent` representation when it is guaranteed by the documentation",
),
rustc_attr!(
rustc_allowed_through_unstable_modules, Normal, template!(NameValueStr: "deprecation message"),
WarnFollowing, EncodeCrossCrate::No,

View file

@ -221,6 +221,8 @@ declare_features! (
(internal, compiler_builtins, "1.13.0", None),
/// Allows writing custom MIR
(internal, custom_mir, "1.65.0", None),
/// Implementation details of externally implementatble items
(internal, eii_internals, "CURRENT_RUSTC_VERSION", None),
/// Outputs useful `assert!` messages
(unstable, generic_assert, "1.63.0", None),
/// Allows using the #[rustc_intrinsic] attribute.
@ -501,6 +503,8 @@ declare_features! (
(incomplete, explicit_tail_calls, "1.72.0", Some(112788)),
/// Allows using `#[export_stable]` which indicates that an item is exportable.
(incomplete, export_stable, "1.88.0", Some(139939)),
/// Externally implementatble items
(unstable, extern_item_impls, "CURRENT_RUSTC_VERSION", Some(125418)),
/// Allows defining `extern type`s.
(unstable, extern_types, "1.23.0", Some(43467)),
/// Allow using 128-bit (quad precision) floating point numbers.

View file

@ -19,6 +19,23 @@ 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 struct EiiImpl {
pub eii_macro: DefId,
pub impl_marked_unsafe: bool,
pub span: Span,
pub inner_span: Span,
pub is_default: bool,
}
#[derive(Copy, Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
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,
}
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)]
pub enum InlineAttr {
None,
@ -692,6 +709,15 @@ pub enum AttributeKind {
/// Represents `#[rustc_dummy]`.
Dummy,
/// Implementation detail of `#[eii]`
EiiExternItem,
/// Implementation detail of `#[eii]`
EiiExternTarget(EiiDecl),
/// Implementation detail of `#[eii]`
EiiImpls(ThinVec<EiiImpl>),
/// Represents [`#[export_name]`](https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute).
ExportName {
/// The name to export this item with.

View file

@ -43,6 +43,9 @@ impl AttributeKind {
Doc(_) => Yes,
DocComment { .. } => Yes,
Dummy => No,
EiiExternItem => No,
EiiExternTarget(_) => Yes,
EiiImpls(..) => No,
ExportName { .. } => Yes,
ExportStable => No,
FfiConst(..) => No,

View file

@ -5,6 +5,7 @@ use rustc_ast::token::{CommentKind, DocFragmentKind};
use rustc_ast::{AttrStyle, IntTy, UintTy};
use rustc_ast_pretty::pp::Printer;
use rustc_data_structures::fx::FxIndexMap;
use rustc_span::def_id::DefId;
use rustc_span::hygiene::Transparency;
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
use rustc_target::spec::SanitizerSet;
@ -170,4 +171,5 @@ print_debug!(
DocFragmentKind,
Transparency,
SanitizerSet,
DefId,
);

View file

@ -4164,6 +4164,9 @@ pub struct Item<'hir> {
pub span: Span,
pub vis_span: Span,
pub has_delayed_lints: bool,
/// hint to speed up collection: true if the item is a static or function and has
/// either an `EiiImpls` or `EiiExternTarget` attribute
pub eii: bool,
}
impl<'hir> Item<'hir> {

View file

@ -532,7 +532,7 @@ pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) ->
}
pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::Result {
let Item { owner_id: _, kind, span: _, vis_span: _, has_delayed_lints: _ } = item;
let Item { owner_id: _, kind, span: _, vis_span: _, has_delayed_lints: _, eii: _ } = item;
try_visit!(visitor.visit_id(item.hir_id()));
match *kind {
ItemKind::ExternCrate(orig_name, ident) => {

View file

@ -165,6 +165,10 @@ hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported
hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice
.label = parameter captured again here
hir_analysis_eii_with_generics =
#[{$eii_name}] cannot have generic parameters other than lifetimes
.label = required by this attribute
hir_analysis_empty_specialization = specialization impl does not specialize any associated items
.note = impl is a specialization of this impl
@ -300,6 +304,13 @@ hir_analysis_lifetime_not_captured = `impl Trait` captures lifetime parameter, b
.label = lifetime captured due to being mentioned in the bounds of the `impl Trait`
.param_label = this lifetime parameter is captured
hir_analysis_lifetimes_or_bounds_mismatch_on_eii =
lifetime parameters or bounds of `{$ident}` do not match the declaration
.label = lifetimes do not match
.generics_label = lifetimes in impl do not match this signature
.where_label = this `where` clause might not match the one in the declaration
.bounds_label = this bound might be missing in the implementation
hir_analysis_lifetimes_or_bounds_mismatch_on_trait =
lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration
.label = lifetimes do not match {$item_kind} in trait

View file

@ -0,0 +1,433 @@
//! This module is very similar to `compare_impl_item`.
//! Most logic is taken from there,
//! since in a very similar way we're comparing some declaration of a signature to an implementation.
//! The major difference is that we don't bother with self types, since for EIIs we're comparing freestanding item.
use std::borrow::Cow;
use std::iter;
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{Applicability, E0806, struct_span_code_err};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, FnSig, HirId, ItemKind};
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::{ObligationCause, ObligationCauseCode};
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, TypingMode};
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::regions::InferCtxtRegionExt;
use rustc_trait_selection::traits::{self, ObligationCtxt};
use tracing::{debug, instrument};
use super::potentially_plural_count;
use crate::check::compare_impl_item::{
CheckNumberOfEarlyBoundRegionsError, check_number_of_early_bound_regions,
};
use crate::errors::{EiiWithGenerics, LifetimesOrBoundsMismatchOnEii};
/// Checks whether the signature of some `external_impl`, matches
/// the signature of `declaration`, which it is supposed to be compatible
/// with in order to implement the item.
pub(crate) fn compare_eii_function_types<'tcx>(
tcx: TyCtxt<'tcx>,
external_impl: LocalDefId,
declaration: DefId,
eii_name: Symbol,
eii_attr_span: Span,
) -> Result<(), ErrorGuaranteed> {
check_is_structurally_compatible(tcx, external_impl, declaration, 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 },
);
// 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 infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis());
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
// We now need to check that the signature of the implementation is
// compatible with that of the declaration. We do this by
// checking that `impl_fty <: trait_fty`.
//
// FIXME: We manually instantiate the declaration here as we need
// to manually compute its implied bounds. Otherwise this could just
// be ocx.sub(impl_sig, trait_sig).
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.liberate_late_bound_regions(external_impl.into(), declaration_sig);
debug!(?declaration_sig);
let unnormalized_external_impl_sig = infcx.instantiate_binder_with_fresh_vars(
external_impl_span,
infer::BoundRegionConversionTime::HigherRankedType,
tcx.fn_sig(external_impl).instantiate(
tcx,
infcx.fresh_args_for_item(external_impl_span, external_impl.to_def_id()),
),
);
let external_impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_external_impl_sig);
debug!(?external_impl_sig);
// Next, add all inputs and output as well-formed tys. Importantly,
// we have to do this before normalization, since the normalized ty may
// not contain the input parameters. See issue #87748.
wf_tys.extend(declaration_sig.inputs_and_output.iter());
let declaration_sig = ocx.normalize(&norm_cause, param_env, declaration_sig);
// We also have to add the normalized declaration
// as we don't normalize during implied bounds computation.
wf_tys.extend(external_impl_sig.inputs_and_output.iter());
// FIXME: Copied over from compare impl items, same issue:
// We'd want to keep more accurate spans than "the method signature" when
// processing the comparison between the trait and impl fn, but we sadly lose them
// and point at the whole signature when a trait bound or specific input or output
// type would be more appropriate. In other places we have a `Vec<Span>`
// corresponding to their `Vec<Predicate>`, but we don't have that here.
// Fixing this would improve the output of test `issue-83765.rs`.
let result = ocx.sup(&cause, param_env, declaration_sig, external_impl_sig);
if let Err(terr) = result {
debug!(?external_impl_sig, ?declaration_sig, ?terr, "sub_types failed");
let emitted = report_eii_mismatch(
infcx,
cause,
param_env,
terr,
(declaration, declaration_sig),
(external_impl, external_impl_sig),
eii_attr_span,
eii_name,
);
return Err(emitted);
}
if !(declaration_sig, external_impl_sig).references_error() {
for ty in unnormalized_external_impl_sig.inputs_and_output {
ocx.register_obligation(traits::Obligation::new(
infcx.tcx,
cause.clone(),
param_env,
ty::ClauseKind::WellFormed(ty.into()),
));
}
}
// Check that all obligations are satisfied by the implementation's
// version.
let errors = ocx.evaluate_obligations_error_on_ambiguity();
if !errors.is_empty() {
let reported = infcx.err_ctxt().report_fulfillment_errors(errors);
return Err(reported);
}
// Finally, resolve all regions. This catches wily misuses of
// lifetime parameters.
let errors = infcx.resolve_regions(external_impl, param_env, wf_tys);
if !errors.is_empty() {
return Err(infcx
.tainted_by_errors()
.unwrap_or_else(|| infcx.err_ctxt().report_region_errors(external_impl, &errors)));
}
Ok(())
}
/// Checks a bunch of different properties of the impl/trait methods for
/// compatibility, such as asyncness, number of argument, self receiver kind,
/// and number of early- and late-bound generics.
///
/// Corresponds to `check_method_is_structurally_compatible` for impl method compatibility checks.
fn check_is_structurally_compatible<'tcx>(
tcx: TyCtxt<'tcx>,
external_impl: LocalDefId,
declaration: DefId,
eii_name: Symbol,
eii_attr_span: Span,
) -> Result<(), ErrorGuaranteed> {
check_no_generics(tcx, external_impl, declaration, eii_name, eii_attr_span)?;
check_number_of_arguments(tcx, external_impl, declaration, eii_name, eii_attr_span)?;
check_early_region_bounds(tcx, external_impl, declaration, eii_attr_span)?;
Ok(())
}
/// externally implementable items can't have generics
fn check_no_generics<'tcx>(
tcx: TyCtxt<'tcx>,
external_impl: LocalDefId,
_declaration: DefId,
eii_name: Symbol,
eii_attr_span: Span,
) -> Result<(), ErrorGuaranteed> {
let generics = tcx.generics_of(external_impl);
if generics.own_requires_monomorphization() {
tcx.dcx().emit_err(EiiWithGenerics {
span: tcx.def_span(external_impl),
attr: eii_attr_span,
eii_name,
});
}
Ok(())
}
fn check_early_region_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
external_impl: LocalDefId,
declaration: DefId,
eii_attr_span: Span,
) -> Result<(), ErrorGuaranteed> {
let external_impl_generics = tcx.generics_of(external_impl.to_def_id());
let external_impl_params = external_impl_generics.own_counts().lifetimes;
let declaration_generics = tcx.generics_of(declaration);
let declaration_params = declaration_generics.own_counts().lifetimes;
let Err(CheckNumberOfEarlyBoundRegionsError { span, generics_span, bounds_span, where_span }) =
check_number_of_early_bound_regions(
tcx,
external_impl,
declaration,
external_impl_generics,
external_impl_params,
declaration_generics,
declaration_params,
)
else {
return Ok(());
};
let mut diag = tcx.dcx().create_err(LifetimesOrBoundsMismatchOnEii {
span,
ident: tcx.item_name(external_impl.to_def_id()),
generics_span,
bounds_span,
where_span,
});
diag.span_label(eii_attr_span, format!("required because of this attribute"));
return Err(diag.emit());
}
fn check_number_of_arguments<'tcx>(
tcx: TyCtxt<'tcx>,
external_impl: LocalDefId,
declaration: DefId,
eii_name: Symbol,
eii_attr_span: Span,
) -> Result<(), ErrorGuaranteed> {
let external_impl_fty = tcx.fn_sig(external_impl);
let declaration_fty = tcx.fn_sig(declaration);
let declaration_number_args = declaration_fty.skip_binder().inputs().skip_binder().len();
let external_impl_number_args = external_impl_fty.skip_binder().inputs().skip_binder().len();
// if the number of args are equal, we're trivially done
if declaration_number_args == external_impl_number_args {
Ok(())
} else {
Err(report_number_of_arguments_mismatch(
tcx,
external_impl,
declaration,
eii_name,
eii_attr_span,
declaration_number_args,
external_impl_number_args,
))
}
}
fn report_number_of_arguments_mismatch<'tcx>(
tcx: TyCtxt<'tcx>,
external_impl: LocalDefId,
declaration: DefId,
eii_name: Symbol,
eii_attr_span: Span,
declaration_number_args: usize,
external_impl_number_args: usize,
) -> ErrorGuaranteed {
let external_impl_name = tcx.item_name(external_impl.to_def_id());
let declaration_span = declaration
.as_local()
.and_then(|def_id| {
let declaration_sig = get_declaration_sig(tcx, def_id).expect("foreign item sig");
let pos = declaration_number_args.saturating_sub(1);
declaration_sig.decl.inputs.get(pos).map(|arg| {
if pos == 0 {
arg.span
} else {
arg.span.with_lo(declaration_sig.decl.inputs[0].span.lo())
}
})
})
.or_else(|| tcx.hir_span_if_local(declaration))
.unwrap_or_else(|| tcx.def_span(declaration));
let (_, external_impl_sig, _, _) = &tcx.hir_expect_item(external_impl).expect_fn();
let pos = external_impl_number_args.saturating_sub(1);
let impl_span = external_impl_sig
.decl
.inputs
.get(pos)
.map(|arg| {
if pos == 0 {
arg.span
} else {
arg.span.with_lo(external_impl_sig.decl.inputs[0].span.lo())
}
})
.unwrap_or_else(|| tcx.def_span(external_impl));
let mut err = struct_span_code_err!(
tcx.dcx(),
impl_span,
E0806,
"`{external_impl_name}` has {} but #[{eii_name}] requires it to have {}",
potentially_plural_count(external_impl_number_args, "parameter"),
declaration_number_args
);
err.span_label(
declaration_span,
format!("requires {}", potentially_plural_count(declaration_number_args, "parameter")),
);
err.span_label(
impl_span,
format!(
"expected {}, found {}",
potentially_plural_count(declaration_number_args, "parameter"),
external_impl_number_args
),
);
err.span_label(eii_attr_span, format!("required because of this attribute"));
err.emit()
}
fn report_eii_mismatch<'tcx>(
infcx: &InferCtxt<'tcx>,
mut cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
terr: TypeError<'tcx>,
(declaration_did, declaration_sig): (DefId, ty::FnSig<'tcx>),
(external_impl_did, external_impl_sig): (LocalDefId, ty::FnSig<'tcx>),
eii_attr_span: Span,
eii_name: Symbol,
) -> ErrorGuaranteed {
let tcx = infcx.tcx;
let (impl_err_span, trait_err_span, external_impl_name) =
extract_spans_for_error_reporting(infcx, terr, &cause, declaration_did, external_impl_did);
let mut diag = struct_span_code_err!(
tcx.dcx(),
impl_err_span,
E0806,
"function `{}` has a type that is incompatible with the declaration of `#[{eii_name}]`",
external_impl_name
);
diag.span_note(eii_attr_span, "expected this because of this attribute");
match &terr {
TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => {
if declaration_sig.inputs().len() == *i {
// Suggestion to change output type. We do not suggest in `async` functions
// to avoid complex logic or incorrect output.
if let ItemKind::Fn { sig, .. } = &tcx.hir_expect_item(external_impl_did).kind
&& !sig.header.asyncness.is_async()
{
let msg = "change the output type to match the declaration";
let ap = Applicability::MachineApplicable;
match sig.decl.output {
hir::FnRetTy::DefaultReturn(sp) => {
let sugg = format!(" -> {}", declaration_sig.output());
diag.span_suggestion_verbose(sp, msg, sugg, ap);
}
hir::FnRetTy::Return(hir_ty) => {
let sugg = declaration_sig.output();
diag.span_suggestion_verbose(hir_ty.span, msg, sugg, ap);
}
};
};
} else if let Some(trait_ty) = declaration_sig.inputs().get(*i) {
diag.span_suggestion_verbose(
impl_err_span,
"change the parameter type to match the declaration",
trait_ty,
Applicability::MachineApplicable,
);
}
}
_ => {}
}
cause.span = impl_err_span;
infcx.err_ctxt().note_type_err(
&mut diag,
&cause,
trait_err_span.map(|sp| (sp, Cow::from("type in declaration"), false)),
Some(param_env.and(infer::ValuePairs::PolySigs(ExpectedFound {
expected: ty::Binder::dummy(declaration_sig),
found: ty::Binder::dummy(external_impl_sig),
}))),
terr,
false,
None,
);
diag.emit()
}
#[instrument(level = "debug", skip(infcx))]
fn extract_spans_for_error_reporting<'tcx>(
infcx: &infer::InferCtxt<'tcx>,
terr: TypeError<'_>,
cause: &ObligationCause<'tcx>,
declaration: DefId,
external_impl: LocalDefId,
) -> (Span, Option<Span>, Ident) {
let tcx = infcx.tcx;
let (mut external_impl_args, external_impl_name) = {
let item = tcx.hir_expect_item(external_impl);
let (ident, sig, _, _) = item.expect_fn();
(sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span())), ident)
};
let declaration_args = declaration.as_local().map(|def_id| {
if let Some(sig) = get_declaration_sig(tcx, def_id) {
sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
} else {
panic!("expected {def_id:?} to be a foreign function");
}
});
match terr {
TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(ExpectedFound { .. }, i) => (
external_impl_args.nth(i).unwrap(),
declaration_args.and_then(|mut args| args.nth(i)),
external_impl_name,
),
_ => (
cause.span,
tcx.hir_span_if_local(declaration).or_else(|| Some(tcx.def_span(declaration))),
external_impl_name,
),
}
}
fn get_declaration_sig<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Option<&'tcx FnSig<'tcx>> {
let hir_id: HirId = tcx.local_def_id_to_hir_id(def_id);
tcx.hir_fn_sig_by_hir_id(hir_id)
}

View file

@ -14,8 +14,9 @@ use rustc_infer::infer::{self, BoundRegionConversionTime, InferCtxt, TyCtxtInfer
use rustc_infer::traits::util;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{
self, BottomUpFolder, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeFoldable, TypeFolder,
TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast,
self, BottomUpFolder, GenericArgs, GenericParamDefKind, Generics, Ty, TyCtxt, TypeFoldable,
TypeFolder, TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode,
Upcast,
};
use rustc_middle::{bug, span_bug};
use rustc_span::{DUMMY_SP, Span};
@ -352,6 +353,7 @@ fn compare_method_predicate_entailment<'tcx>(
// type would be more appropriate. In other places we have a `Vec<Span>`
// corresponding to their `Vec<Predicate>`, but we don't have that here.
// Fixing this would improve the output of test `issue-83765.rs`.
// There's the same issue in compare_eii code.
let result = ocx.sup(&cause, param_env, trait_sig, impl_sig);
if let Err(terr) = result {
@ -1093,6 +1095,55 @@ fn check_region_bounds_on_impl_item<'tcx>(
let trait_generics = tcx.generics_of(trait_m.def_id);
let trait_params = trait_generics.own_counts().lifetimes;
let Err(CheckNumberOfEarlyBoundRegionsError { span, generics_span, bounds_span, where_span }) =
check_number_of_early_bound_regions(
tcx,
impl_m.def_id.expect_local(),
trait_m.def_id,
impl_generics,
impl_params,
trait_generics,
trait_params,
)
else {
return Ok(());
};
if !delay && let Some(guar) = check_region_late_boundedness(tcx, impl_m, trait_m) {
return Err(guar);
}
let reported = tcx
.dcx()
.create_err(LifetimesOrBoundsMismatchOnTrait {
span,
item_kind: impl_m.descr(),
ident: impl_m.ident(tcx),
generics_span,
bounds_span,
where_span,
})
.emit_unless_delay(delay);
Err(reported)
}
pub(super) struct CheckNumberOfEarlyBoundRegionsError {
pub(super) span: Span,
pub(super) generics_span: Span,
pub(super) bounds_span: Vec<Span>,
pub(super) where_span: Option<Span>,
}
pub(super) fn check_number_of_early_bound_regions<'tcx>(
tcx: TyCtxt<'tcx>,
impl_def_id: LocalDefId,
trait_def_id: DefId,
impl_generics: &Generics,
impl_params: usize,
trait_generics: &Generics,
trait_params: usize,
) -> Result<(), CheckNumberOfEarlyBoundRegionsError> {
debug!(?trait_generics, ?impl_generics);
// Must have same number of early-bound lifetime parameters.
@ -1108,20 +1159,16 @@ fn check_region_bounds_on_impl_item<'tcx>(
return Ok(());
}
if !delay && let Some(guar) = check_region_late_boundedness(tcx, impl_m, trait_m) {
return Err(guar);
}
let span = tcx
.hir_get_generics(impl_m.def_id.expect_local())
.hir_get_generics(impl_def_id)
.expect("expected impl item to have generics or else we can't compare them")
.span;
let mut generics_span = tcx.def_span(trait_m.def_id);
let mut generics_span = tcx.def_span(trait_def_id);
let mut bounds_span = vec![];
let mut where_span = None;
if let Some(trait_node) = tcx.hir_get_if_local(trait_m.def_id)
if let Some(trait_node) = tcx.hir_get_if_local(trait_def_id)
&& let Some(trait_generics) = trait_node.generics()
{
generics_span = trait_generics.span;
@ -1146,7 +1193,7 @@ fn check_region_bounds_on_impl_item<'tcx>(
_ => {}
}
}
if let Some(impl_node) = tcx.hir_get_if_local(impl_m.def_id)
if let Some(impl_node) = tcx.hir_get_if_local(impl_def_id.into())
&& let Some(impl_generics) = impl_node.generics()
{
let mut impl_bounds = 0;
@ -1177,19 +1224,7 @@ fn check_region_bounds_on_impl_item<'tcx>(
}
}
let reported = tcx
.dcx()
.create_err(LifetimesOrBoundsMismatchOnTrait {
span,
item_kind: impl_m.descr(),
ident: impl_m.ident(tcx),
generics_span,
bounds_span,
where_span,
})
.emit_unless_delay(delay);
Err(reported)
Err(CheckNumberOfEarlyBoundRegionsError { span, generics_span, bounds_span, where_span })
}
#[allow(unused)]

View file

@ -64,6 +64,7 @@ a type parameter).
pub mod always_applicable;
mod check;
mod compare_eii;
mod compare_impl_item;
mod entry;
pub mod intrinsic;

View file

@ -6,7 +6,7 @@ use rustc_abi::ExternAbi;
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;
use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
@ -39,6 +39,7 @@ use rustc_trait_selection::traits::{
use tracing::{debug, instrument};
use {rustc_ast as ast, rustc_hir as hir};
use super::compare_eii::compare_eii_function_types;
use crate::autoderef::Autoderef;
use crate::constrained_generic_params::{Parameter, identify_constrained_generic_params};
use crate::errors::InvalidReceiverTyHint;
@ -1170,12 +1171,41 @@ fn check_item_fn(
decl: &hir::FnDecl<'_>,
) -> Result<(), ErrorGuaranteed> {
enter_wf_checking_ctxt(tcx, def_id, |wfcx| {
check_eiis(tcx, def_id);
let sig = tcx.fn_sig(def_id).instantiate_identity();
check_fn_or_method(wfcx, sig, decl, def_id);
Ok(())
})
}
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
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"
)
}
}
}
#[instrument(level = "debug", skip(tcx))]
pub(crate) fn check_static_item<'tcx>(
tcx: TyCtxt<'tcx>,

View file

@ -1697,3 +1697,28 @@ pub(crate) struct AsyncDropWithoutSyncDrop {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_lifetimes_or_bounds_mismatch_on_eii)]
pub(crate) struct LifetimesOrBoundsMismatchOnEii {
#[primary_span]
#[label]
pub span: Span,
#[label(hir_analysis_generics_label)]
pub generics_span: Span,
#[label(hir_analysis_where_label)]
pub where_span: Option<Span>,
#[label(hir_analysis_bounds_label)]
pub bounds_span: Vec<Span>,
pub ident: Symbol,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_eii_with_generics)]
pub(crate) struct EiiWithGenerics {
#[primary_span]
pub span: Span,
#[label]
pub attr: Span,
pub eii_name: Symbol,
}

View file

@ -40,8 +40,7 @@ use rustc_session::output::{collect_crate_types, filename_for_input};
use rustc_session::parse::feature_err;
use rustc_session::search_paths::PathKind;
use rustc_span::{
DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, SourceFileHash, SourceFileHashAlgorithm, Span,
Symbol, sym,
DUMMY_SP, ErrorGuaranteed, ExpnKind, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym,
};
use rustc_trait_selection::{solve, traits};
use tracing::{info, instrument};
@ -595,7 +594,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
.filter(|fmap| !fmap.is_imported())
.map(|fmap| {
(
escape_dep_filename(&fmap.name.prefer_local().to_string()),
escape_dep_filename(&fmap.name.prefer_local_unconditionally().to_string()),
// This needs to be unnormalized,
// as external tools wouldn't know how rustc normalizes them
fmap.unnormalized_source_len as u64,
@ -610,10 +609,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
// (e.g. accessed in proc macros).
let file_depinfo = sess.psess.file_depinfo.borrow();
let normalize_path = |path: PathBuf| {
let file = FileName::from(path);
escape_dep_filename(&file.prefer_local().to_string())
};
let normalize_path = |path: PathBuf| escape_dep_filename(&path.to_string_lossy());
// The entries will be used to declare dependencies between files in a
// Makefile-like output, so the iteration order does not matter.
@ -684,19 +680,19 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
for &cnum in tcx.crates(()) {
let source = tcx.used_crate_source(cnum);
if let Some((path, _)) = &source.dylib {
if let Some(path) = &source.dylib {
files.extend(hash_iter_files(
iter::once(escape_dep_filename(&path.display().to_string())),
checksum_hash_algo,
));
}
if let Some((path, _)) = &source.rlib {
if let Some(path) = &source.rlib {
files.extend(hash_iter_files(
iter::once(escape_dep_filename(&path.display().to_string())),
checksum_hash_algo,
));
}
if let Some((path, _)) = &source.rmeta {
if let Some(path) = &source.rmeta {
files.extend(hash_iter_files(
iter::once(escape_dep_filename(&path.display().to_string())),
checksum_hash_algo,
@ -1061,6 +1057,9 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
parallel!(
{
sess.time("looking_for_entry_point", || tcx.ensure_ok().entry_fn(()));
sess.time("check_externally_implementable_items", || {
tcx.ensure_ok().check_externally_implementable_items(())
});
sess.time("looking_for_derive_registrar", || {
tcx.ensure_ok().proc_macro_decls_static(())

View file

@ -205,15 +205,17 @@ lint_dangling_pointers_from_locals = {$fn_kind} returns a dangling pointer to dr
.ret_ty = return type is `{$ret_ty}`
.local_var = local variable `{$local_var_name}` is dropped at the end of the {$fn_kind}
.created_at = dangling pointer created here
.note = a dangling pointer is safe, but dereferencing one is undefined behavior
.note_safe = a dangling pointer is safe, but dereferencing one is undefined behavior
.note_more_info = for more information, see <https://doc.rust-lang.org/reference/destructors.html>
lint_dangling_pointers_from_temporaries = this creates a dangling pointer because temporary `{$ty}` is dropped at end of statement
.label_ptr = pointer created here
.label_temporary = this `{$ty}` is dropped at end of statement
.help_bind = bind the `{$ty}` to a variable such that it outlives the pointer returned by `{$callee}`
.note_safe = a dangling pointer is safe, but dereferencing one is undefined behavior
.note_return = returning a pointer to a local variable will always result in a dangling pointer
.note_more_info = for more information, see <https://doc.rust-lang.org/reference/destructors.html>
lint_dangling_pointers_from_temporaries = a dangling pointer will be produced because the temporary `{$ty}` will be dropped
.label_ptr = this pointer will immediately be invalid
.label_temporary = this `{$ty}` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
.note = pointers do not have a lifetime; when calling `{$callee}` the `{$ty}` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
.help_bind = you must make sure that the variable you bind the `{$ty}` to lives at least as long as the pointer returned by the call to `{$callee}`
.help_returned = in particular, if this pointer is returned from the current function, binding the `{$ty}` inside the function will not suffice
.help_visit = for more information, see <https://doc.rust-lang.org/reference/destructors.html>
lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance
.note = a `use rustc_data_structures::fx::{$preferred}` may be necessary

View file

@ -1138,10 +1138,10 @@ pub(crate) struct IgnoredUnlessCrateSpecified<'a> {
// dangling.rs
#[derive(LintDiagnostic)]
#[diag(lint_dangling_pointers_from_temporaries)]
#[note]
#[help(lint_help_bind)]
#[help(lint_help_returned)]
#[help(lint_help_visit)]
#[note(lint_note_safe)]
#[note(lint_note_return)]
#[note(lint_note_more_info)]
// FIXME: put #[primary_span] on `ptr_span` once it does not cause conflicts
pub(crate) struct DanglingPointersFromTemporaries<'tcx> {
pub callee: Ident,
@ -1154,7 +1154,8 @@ pub(crate) struct DanglingPointersFromTemporaries<'tcx> {
#[derive(LintDiagnostic)]
#[diag(lint_dangling_pointers_from_locals)]
#[note]
#[note(lint_note_safe)]
#[note(lint_note_more_info)]
pub(crate) struct DanglingPointersFromLocals<'tcx> {
pub ret_ty: Ty<'tcx>,
#[label(lint_ret_ty)]

View file

@ -135,16 +135,16 @@ impl<'a> std::fmt::Debug for CrateDump<'a> {
writeln!(fmt, " priv: {:?}", data.is_private_dep())?;
let CrateSource { dylib, rlib, rmeta, sdylib_interface } = data.source();
if let Some(dylib) = dylib {
writeln!(fmt, " dylib: {}", dylib.0.display())?;
writeln!(fmt, " dylib: {}", dylib.display())?;
}
if let Some(rlib) = rlib {
writeln!(fmt, " rlib: {}", rlib.0.display())?;
writeln!(fmt, " rlib: {}", rlib.display())?;
}
if let Some(rmeta) = rmeta {
writeln!(fmt, " rmeta: {}", rmeta.0.display())?;
writeln!(fmt, " rmeta: {}", rmeta.display())?;
}
if let Some(sdylib_interface) = sdylib_interface {
writeln!(fmt, " sdylib interface: {}", sdylib_interface.0.display())?;
writeln!(fmt, " sdylib interface: {}", sdylib_interface.display())?;
}
}
Ok(())
@ -515,73 +515,19 @@ impl CStore {
}
}
fn existing_match(
&self,
externs: &Externs,
name: Symbol,
hash: Option<Svh>,
kind: PathKind,
) -> Option<CrateNum> {
fn existing_match(&self, name: Symbol, hash: Option<Svh>) -> Option<CrateNum> {
let hash = hash?;
for (cnum, data) in self.iter_crate_data() {
if data.name() != name {
trace!("{} did not match {}", data.name(), name);
continue;
}
match hash {
Some(hash) if hash == data.hash() => return Some(cnum),
Some(hash) => {
debug!("actual hash {} did not match expected {}", hash, data.hash());
continue;
}
None => {}
}
// When the hash is None we're dealing with a top-level dependency
// in which case we may have a specification on the command line for
// this library. Even though an upstream library may have loaded
// something of the same name, we have to make sure it was loaded
// from the exact same location as well.
//
// We're also sure to compare *paths*, not actual byte slices. The
// `source` stores paths which are normalized which may be different
// from the strings on the command line.
let source = data.source();
if let Some(entry) = externs.get(name.as_str()) {
// Only use `--extern crate_name=path` here, not `--extern crate_name`.
if let Some(mut files) = entry.files() {
if files.any(|l| {
let l = l.canonicalized();
source.dylib.as_ref().map(|(p, _)| p) == Some(l)
|| source.rlib.as_ref().map(|(p, _)| p) == Some(l)
|| source.rmeta.as_ref().map(|(p, _)| p) == Some(l)
}) {
return Some(cnum);
}
}
continue;
}
// Alright, so we've gotten this far which means that `data` has the
// right name, we don't have a hash, and we don't have a --extern
// pointing for ourselves. We're still not quite yet done because we
// have to make sure that this crate was found in the crate lookup
// path (this is a top-level dependency) as we don't want to
// implicitly load anything inside the dependency lookup path.
let prev_kind = source
.dylib
.as_ref()
.or(source.rlib.as_ref())
.or(source.rmeta.as_ref())
.expect("No sources for crate")
.1;
if kind.matches(prev_kind) {
if hash == data.hash() {
return Some(cnum);
} else {
debug!(
"failed to load existing crate {}; kind {:?} did not match prev_kind {:?}",
name, kind, prev_kind
);
debug!("actual hash {} did not match expected {}", hash, data.hash());
}
}
@ -678,7 +624,7 @@ impl CStore {
None => (&source, &crate_root),
};
let dlsym_dylib = dlsym_source.dylib.as_ref().expect("no dylib for a proc-macro crate");
Some(self.dlsym_proc_macros(tcx.sess, &dlsym_dylib.0, dlsym_root.stable_crate_id())?)
Some(self.dlsym_proc_macros(tcx.sess, dlsym_dylib, dlsym_root.stable_crate_id())?)
} else {
None
};
@ -819,9 +765,7 @@ impl CStore {
let path_kind = if dep.is_some() { PathKind::Dependency } else { PathKind::Crate };
let private_dep = origin.private_dep();
let result = if let Some(cnum) =
self.existing_match(&tcx.sess.opts.externs, name, hash, path_kind)
{
let result = if let Some(cnum) = self.existing_match(name, hash) {
(LoadResult::Previous(cnum), None)
} else {
info!("falling back to a load");

View file

@ -0,0 +1,47 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl};
use rustc_hir::def_id::DefId;
use rustc_hir::find_attr;
use rustc_middle::query::LocalCrate;
use rustc_middle::ty::TyCtxt;
// basically the map below but flattened out
pub(crate) type EiiMapEncodedKeyValue = (DefId, (EiiDecl, Vec<(DefId, EiiImpl)>));
pub(crate) type EiiMap = FxIndexMap<
DefId, // the defid of the macro that declared the eii
(
// the corresponding declaration
EiiDecl,
// all the given implementations, indexed by defid.
// We expect there to be only one, but collect them all to give errors if there are more
// (or if there are none) in the final crate we build.
FxIndexMap<DefId, EiiImpl>,
),
>;
pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap {
let mut eiis = EiiMap::default();
// iterate over all items in the current crate
for id in tcx.hir_crate_items(()).eiis() {
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(|| {
// 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);
}
// 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
}

View file

@ -16,6 +16,7 @@
pub use rmeta::provide;
mod dependency_format;
mod eii;
mod foreign_modules;
mod native_libs;
mod rmeta;

View file

@ -218,7 +218,7 @@ use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::{cmp, fmt};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::owned_slice::{OwnedSlice, slice_owned};
use rustc_data_structures::svh::Svh;
@ -401,7 +401,7 @@ impl<'a> CrateLocator<'a> {
let mut candidates: FxIndexMap<
_,
(FxIndexMap<_, _>, FxIndexMap<_, _>, FxIndexMap<_, _>, FxIndexMap<_, _>),
(FxIndexSet<_>, FxIndexSet<_>, FxIndexSet<_>, FxIndexSet<_>),
> = Default::default();
// First, find all possible candidate rlibs and dylibs purely based on
@ -460,10 +460,10 @@ impl<'a> CrateLocator<'a> {
// filesystem code should not care, but this is nicer for diagnostics.
let path = spf.path.to_path_buf();
match kind {
CrateFlavor::Rlib => rlibs.insert(path, search_path.kind),
CrateFlavor::Rmeta => rmetas.insert(path, search_path.kind),
CrateFlavor::Dylib => dylibs.insert(path, search_path.kind),
CrateFlavor::SDylib => interfaces.insert(path, search_path.kind),
CrateFlavor::Rlib => rlibs.insert(path),
CrateFlavor::Rmeta => rmetas.insert(path),
CrateFlavor::Dylib => dylibs.insert(path),
CrateFlavor::SDylib => interfaces.insert(path),
};
}
}
@ -524,10 +524,10 @@ impl<'a> CrateLocator<'a> {
fn extract_lib(
&self,
crate_rejections: &mut CrateRejections,
rlibs: FxIndexMap<PathBuf, PathKind>,
rmetas: FxIndexMap<PathBuf, PathKind>,
dylibs: FxIndexMap<PathBuf, PathKind>,
interfaces: FxIndexMap<PathBuf, PathKind>,
rlibs: FxIndexSet<PathBuf>,
rmetas: FxIndexSet<PathBuf>,
dylibs: FxIndexSet<PathBuf>,
interfaces: FxIndexSet<PathBuf>,
) -> Result<Option<(Svh, Library)>, CrateError> {
let mut slot = None;
// Order here matters, rmeta should come first.
@ -575,10 +575,10 @@ impl<'a> CrateLocator<'a> {
fn extract_one(
&self,
crate_rejections: &mut CrateRejections,
m: FxIndexMap<PathBuf, PathKind>,
m: FxIndexSet<PathBuf>,
flavor: CrateFlavor,
slot: &mut Option<(Svh, MetadataBlob, PathBuf, CrateFlavor)>,
) -> Result<Option<(PathBuf, PathKind)>, CrateError> {
) -> Result<Option<PathBuf>, CrateError> {
// If we are producing an rlib, and we've already loaded metadata, then
// we should not attempt to discover further crate sources (unless we're
// locating a proc macro; exact logic is in needs_crate_flavor). This means
@ -594,9 +594,9 @@ impl<'a> CrateLocator<'a> {
}
}
let mut ret: Option<(PathBuf, PathKind)> = None;
let mut ret: Option<PathBuf> = None;
let mut err_data: Option<Vec<PathBuf>> = None;
for (lib, kind) in m {
for lib in m {
info!("{} reading metadata from: {}", flavor, lib.display());
if flavor == CrateFlavor::Rmeta && lib.metadata().is_ok_and(|m| m.len() == 0) {
// Empty files will cause get_metadata_section to fail. Rmeta
@ -640,7 +640,7 @@ impl<'a> CrateLocator<'a> {
info!("no metadata found: {}", err);
// Metadata was loaded from interface file earlier.
if let Some((.., CrateFlavor::SDylib)) = slot {
ret = Some((lib, kind));
ret = Some(lib);
continue;
}
// The file was present and created by the same compiler version, but we
@ -689,7 +689,7 @@ impl<'a> CrateLocator<'a> {
// As a result, we favor the sysroot crate here. Note that the
// candidates are all canonicalized, so we canonicalize the sysroot
// as well.
if let Some((prev, _)) = &ret {
if let Some(prev) = &ret {
let sysroot = self.sysroot;
let sysroot = try_canonicalize(sysroot).unwrap_or_else(|_| sysroot.to_path_buf());
if prev.starts_with(&sysroot) {
@ -714,7 +714,7 @@ impl<'a> CrateLocator<'a> {
} else {
*slot = Some((hash, metadata, lib.clone(), flavor));
}
ret = Some((lib, kind));
ret = Some(lib);
}
if let Some(candidates) = err_data {
@ -774,10 +774,10 @@ impl<'a> CrateLocator<'a> {
// First, filter out all libraries that look suspicious. We only accept
// files which actually exist that have the correct naming scheme for
// rlibs/dylibs.
let mut rlibs = FxIndexMap::default();
let mut rmetas = FxIndexMap::default();
let mut dylibs = FxIndexMap::default();
let mut sdylib_interfaces = FxIndexMap::default();
let mut rlibs = FxIndexSet::default();
let mut rmetas = FxIndexSet::default();
let mut dylibs = FxIndexSet::default();
let mut sdylib_interfaces = FxIndexSet::default();
for loc in &self.exact_paths {
let loc_canon = loc.canonicalized();
let loc_orig = loc.original();
@ -798,21 +798,21 @@ impl<'a> CrateLocator<'a> {
};
if file.starts_with("lib") {
if file.ends_with(".rlib") {
rlibs.insert(loc_canon.clone(), PathKind::ExternFlag);
rlibs.insert(loc_canon.clone());
continue;
}
if file.ends_with(".rmeta") {
rmetas.insert(loc_canon.clone(), PathKind::ExternFlag);
rmetas.insert(loc_canon.clone());
continue;
}
if file.ends_with(".rs") {
sdylib_interfaces.insert(loc_canon.clone(), PathKind::ExternFlag);
sdylib_interfaces.insert(loc_canon.clone());
}
}
let dll_prefix = self.target.dll_prefix.as_ref();
let dll_suffix = self.target.dll_suffix.as_ref();
if file.starts_with(dll_prefix) && file.ends_with(dll_suffix) {
dylibs.insert(loc_canon.clone(), PathKind::ExternFlag);
dylibs.insert(loc_canon.clone());
continue;
}
crate_rejections

View file

@ -33,12 +33,13 @@ use rustc_session::config::TargetModifier;
use rustc_session::cstore::{CrateSource, ExternCrate};
use rustc_span::hygiene::HygieneDecodeContext;
use rustc_span::{
BlobDecoder, BytePos, ByteSymbol, DUMMY_SP, Pos, SpanData, SpanDecoder, Symbol, SyntaxContext,
kw,
BlobDecoder, BytePos, ByteSymbol, DUMMY_SP, Pos, RemapPathScopeComponents, SpanData,
SpanDecoder, Symbol, SyntaxContext, kw,
};
use tracing::debug;
use crate::creader::CStore;
use crate::eii::EiiMapEncodedKeyValue;
use crate::rmeta::table::IsDefault;
use crate::rmeta::*;
@ -1487,6 +1488,13 @@ impl<'a> CrateMetadataRef<'a> {
)
}
fn get_externally_implementable_items(
self,
tcx: TyCtxt<'_>,
) -> impl Iterator<Item = EiiMapEncodedKeyValue> {
self.root.externally_implementable_items.decode((self, tcx))
}
fn get_missing_lang_items<'tcx>(self, tcx: TyCtxt<'tcx>) -> &'tcx [LangItem] {
tcx.arena.alloc_from_iter(self.root.lang_items_missing.decode((self, tcx)))
}
@ -1530,7 +1538,7 @@ impl<'a> CrateMetadataRef<'a> {
.get((self, tcx), id)
.unwrap()
.decode((self, tcx));
ast::MacroDef { macro_rules, body: Box::new(body) }
ast::MacroDef { macro_rules, body: Box::new(body), eii_extern_target: None }
}
_ => bug!(),
}
@ -1669,15 +1677,15 @@ impl<'a> CrateMetadataRef<'a> {
for virtual_dir in virtual_source_base_dir.iter().flatten() {
if let Some(real_dir) = &real_source_base_dir
&& let rustc_span::FileName::Real(old_name) = name
&& let rustc_span::RealFileName::Remapped { local_path: _, virtual_name } =
old_name
&& let Ok(rest) = virtual_name.strip_prefix(virtual_dir)
&& let (_working_dir, embeddable_name) =
old_name.embeddable_name(RemapPathScopeComponents::MACRO)
&& let Ok(rest) = embeddable_name.strip_prefix(virtual_dir)
{
let new_path = real_dir.join(rest);
debug!(
"try_to_translate_virtual_to_real: `{}` -> `{}`",
virtual_name.display(),
embeddable_name.display(),
new_path.display(),
);
@ -1686,17 +1694,12 @@ impl<'a> CrateMetadataRef<'a> {
// Note that this is a special case for imported rust-src paths specified by
// https://rust-lang.github.io/rfcs/3127-trim-paths.html#handling-sysroot-paths.
// Other imported paths are not currently remapped (see #66251).
let (user_remapped, applied) =
tcx.sess.source_map().path_mapping().map_prefix(&new_path);
let new_name = if applied {
rustc_span::RealFileName::Remapped {
local_path: Some(new_path.clone()),
virtual_name: user_remapped.to_path_buf(),
}
} else {
rustc_span::RealFileName::LocalPath(new_path)
};
*old_name = new_name;
*name = rustc_span::FileName::Real(
tcx.sess
.source_map()
.path_mapping()
.to_real_filename(&rustc_span::RealFileName::empty(), new_path),
);
}
}
};
@ -1711,15 +1714,12 @@ impl<'a> CrateMetadataRef<'a> {
&& let Some(real_dir) = real_source_base_dir
&& let rustc_span::FileName::Real(old_name) = name
{
let relative_path = match old_name {
rustc_span::RealFileName::LocalPath(local) => {
local.strip_prefix(real_dir).ok()
}
rustc_span::RealFileName::Remapped { virtual_name, .. } => {
virtual_source_base_dir
.and_then(|virtual_dir| virtual_name.strip_prefix(virtual_dir).ok())
}
};
let (_working_dir, embeddable_path) =
old_name.embeddable_name(RemapPathScopeComponents::MACRO);
let relative_path = embeddable_path.strip_prefix(real_dir).ok().or_else(|| {
virtual_source_base_dir
.and_then(|virtual_dir| embeddable_path.strip_prefix(virtual_dir).ok())
});
debug!(
?relative_path,
?virtual_dir,
@ -1727,10 +1727,10 @@ impl<'a> CrateMetadataRef<'a> {
"simulate_remapped_rust_src_base"
);
if let Some(rest) = relative_path.and_then(|p| p.strip_prefix(subdir).ok()) {
*old_name = rustc_span::RealFileName::Remapped {
local_path: None,
virtual_name: virtual_dir.join(subdir).join(rest),
};
*name =
rustc_span::FileName::Real(rustc_span::RealFileName::from_virtual_path(
&virtual_dir.join(subdir).join(rest),
))
}
}
};

View file

@ -25,7 +25,7 @@ use super::{Decodable, DecodeIterator};
use crate::creader::{CStore, LoadedMacro};
use crate::rmeta::AttrFlags;
use crate::rmeta::table::IsDefault;
use crate::{foreign_modules, native_libs};
use crate::{eii, foreign_modules, native_libs};
trait ProcessQueryValue<'tcx, T> {
fn process_decoded(self, _tcx: TyCtxt<'tcx>, _err: impl Fn() -> !) -> T;
@ -330,9 +330,22 @@ provide! { tcx, def_id, other, cdata,
is_private_dep => { cdata.private_dep }
is_panic_runtime => { cdata.root.panic_runtime }
is_compiler_builtins => { cdata.root.compiler_builtins }
// FIXME: to be replaced with externally_implementable_items below
has_global_allocator => { cdata.root.has_global_allocator }
// FIXME: to be replaced with externally_implementable_items below
has_alloc_error_handler => { cdata.root.has_alloc_error_handler }
// FIXME: to be replaced with externally_implementable_items below
has_panic_handler => { cdata.root.has_panic_handler }
externally_implementable_items => {
cdata.get_externally_implementable_items(tcx)
.map(|(decl_did, (decl, impls))| (
decl_did,
(decl, impls.into_iter().collect())
)).collect()
}
is_profiler_runtime => { cdata.root.profiler_runtime }
required_panic_strategy => { cdata.root.required_panic_strategy }
panic_in_drop_strategy => { cdata.root.panic_in_drop_strategy }
@ -430,6 +443,7 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
},
native_libraries: native_libs::collect,
foreign_modules: foreign_modules::collect,
externally_implementable_items: eii::collect,
// Returns a map from a sufficiently visible external item (i.e., an
// external item that is visible from at least one local module) to a

View file

@ -35,6 +35,7 @@ use rustc_span::{
};
use tracing::{debug, instrument, trace};
use crate::eii::EiiMapEncodedKeyValue;
use crate::errors::{FailCreateFileEncoder, FailWriteFile};
use crate::rmeta::*;
@ -541,8 +542,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
// is done.
let required_source_files = self.required_source_files.take().unwrap();
let working_directory = &self.tcx.sess.opts.working_dir;
let mut adapted = TableBuilder::default();
let local_crate_stable_id = self.tcx.stable_crate_id(LOCAL_CRATE);
@ -567,10 +566,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
match source_file.name {
FileName::Real(ref original_file_name) => {
let adapted_file_name = source_map
.path_mapping()
.to_embeddable_absolute_path(original_file_name.clone(), working_directory);
let mut adapted_file_name = original_file_name.clone();
adapted_file_name.update_for_crate_metadata();
adapted_source_file.name = FileName::Real(adapted_file_name);
}
_ => {
@ -620,6 +617,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
// We have already encoded some things. Get their combined size from the current position.
stats.push(("preamble", self.position()));
let externally_implementable_items = stat!("externally-implementable-items", || self
.encode_externally_implementable_items());
let (crate_deps, dylib_dependency_formats) =
stat!("dep", || (self.encode_crate_deps(), self.encode_dylib_dependency_formats()));
@ -738,6 +738,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
attrs,
sym::default_lib_allocator,
),
externally_implementable_items,
proc_macro_data,
debugger_visualizers,
compiler_builtins: ast::attr::contains_name(attrs, sym::compiler_builtins),
@ -1649,6 +1650,15 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
}
}
fn encode_externally_implementable_items(&mut self) -> LazyArray<EiiMapEncodedKeyValue> {
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()))
}))
}
#[instrument(level = "trace", skip(self))]
fn encode_info_for_adt(&mut self, local_def_id: LocalDefId) {
let def_id = local_def_id.to_def_id();

View file

@ -44,6 +44,7 @@ use table::TableBuilder;
use {rustc_ast as ast, rustc_hir as hir};
use crate::creader::CrateMetadataRef;
use crate::eii::EiiMapEncodedKeyValue;
mod decoder;
mod def_path_hash_map;
@ -250,6 +251,7 @@ pub(crate) struct CrateRoot {
has_alloc_error_handler: bool,
has_panic_handler: bool,
has_default_lib_allocator: bool,
externally_implementable_items: LazyArray<EiiMapEncodedKeyValue>,
crate_deps: LazyArray<CrateDep>,
dylib_dependency_formats: LazyArray<Option<LinkagePreference>>,

View file

@ -87,6 +87,8 @@ trivially_parameterized_over_tcx! {
rustc_hir::Safety,
rustc_hir::Stability,
rustc_hir::attrs::Deprecation,
rustc_hir::attrs::EiiDecl,
rustc_hir::attrs::EiiImpl,
rustc_hir::attrs::StrippedCfgItem<rustc_hir::def_id::DefIndex>,
rustc_hir::def::DefKind,
rustc_hir::def::DocLinkResMap,

View file

@ -1224,6 +1224,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod
body_owners,
opaques,
nested_bodies,
eiis,
..
} = collector;
ModuleItems {
@ -1237,6 +1238,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod
opaques: opaques.into_boxed_slice(),
nested_bodies: nested_bodies.into_boxed_slice(),
delayed_lint_items: Box::new([]),
eiis: eiis.into_boxed_slice(),
}
}
@ -1259,6 +1261,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems {
opaques,
nested_bodies,
mut delayed_lint_items,
eiis,
..
} = collector;
@ -1281,6 +1284,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems {
opaques: opaques.into_boxed_slice(),
nested_bodies: nested_bodies.into_boxed_slice(),
delayed_lint_items: delayed_lint_items.into_boxed_slice(),
eiis: eiis.into_boxed_slice(),
}
}
@ -1298,6 +1302,7 @@ struct ItemCollector<'tcx> {
opaques: Vec<LocalDefId>,
nested_bodies: Vec<LocalDefId>,
delayed_lint_items: Vec<OwnerId>,
eiis: Vec<LocalDefId>,
}
impl<'tcx> ItemCollector<'tcx> {
@ -1314,6 +1319,7 @@ impl<'tcx> ItemCollector<'tcx> {
opaques: Vec::default(),
nested_bodies: Vec::default(),
delayed_lint_items: Vec::default(),
eiis: Vec::default(),
}
}
}
@ -1335,6 +1341,12 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> {
self.delayed_lint_items.push(item.item_id().owner_id);
}
if let ItemKind::Static(..) | ItemKind::Fn { .. } | ItemKind::Macro(..) = &item.kind
&& item.eii
{
self.eiis.push(item.owner_id.def_id)
}
// Items that are modules are handled here instead of in visit_mod.
if let ItemKind::Mod(_, module) = &item.kind {
self.submodules.push(item.owner_id);

View file

@ -37,6 +37,9 @@ pub struct ModuleItems {
nested_bodies: Box<[LocalDefId]>,
// only filled with hir_crate_items, not with hir_module_items
delayed_lint_items: Box<[OwnerId]>,
/// Statics and functions with an `EiiImpls` or `EiiExternTarget` attribute
eiis: Box<[LocalDefId]>,
}
impl ModuleItems {
@ -58,6 +61,10 @@ impl ModuleItems {
self.delayed_lint_items.iter().copied()
}
pub fn eiis(&self) -> impl Iterator<Item = LocalDefId> {
self.eiis.iter().copied()
}
/// Returns all items that are associated with some `impl` block (both inherent and trait impl
/// blocks).
pub fn impl_items(&self) -> impl Iterator<Item = ImplItemId> {

View file

@ -6,6 +6,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable};
use rustc_span::Symbol;
use rustc_target::spec::SanitizerSet;
use crate::mir::mono::Visibility;
use crate::ty::{InstanceKind, TyCtxt};
impl<'tcx> TyCtxt<'tcx> {
@ -42,6 +43,10 @@ impl<'tcx> TyCtxt<'tcx> {
attrs.to_mut().flags.remove(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
}
if attrs.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM) {
attrs.to_mut().flags.remove(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM);
}
if attrs.symbol_name.is_some() {
attrs.to_mut().symbol_name = None;
}
@ -62,6 +67,12 @@ pub struct CodegenFnAttrs {
/// using the `#[export_name = "..."]` or `#[link_name = "..."]` attribute
/// depending on if this is a function definition or foreign function.
pub symbol_name: Option<Symbol>,
/// Defids of foreign items somewhere that this function should "satisfy".
/// i.e., if a foreign function has some symbol foo,
/// generate this function under its real name,
/// but *also* under the same name as this foreign function so that the foreign function has an implementation.
// FIXME: make "SymbolName<'tcx>"
pub foreign_item_symbol_aliases: Vec<(Symbol, Linkage, Visibility)>,
/// The `#[link_ordinal = "..."]` attribute, indicating an ordinal an
/// imported function has in the dynamic library. Note that this must not
/// be set when `link_name` is set. This is for foreign items with the
@ -192,6 +203,12 @@ bitflags::bitflags! {
const FOREIGN_ITEM = 1 << 16;
/// `#[rustc_offload_kernel]`: indicates that this is an offload kernel, an extra ptr arg will be added.
const OFFLOAD_KERNEL = 1 << 17;
/// Externally implementable item symbols act a little like `RUSTC_STD_INTERNAL_SYMBOL`.
/// When a crate declares an EII and dependencies expect the symbol to exist,
/// they will refer to this symbol name before a definition is given.
/// As such, we must make sure these symbols really do exist in the final binary/library.
/// This flag is put on both the implementations of EIIs and the foreign item they implement.
const EXTERNALLY_IMPLEMENTABLE_ITEM = 1 << 18;
}
}
rustc_data_structures::external_bitflags_debug! { CodegenFnAttrFlags }
@ -207,6 +224,7 @@ impl CodegenFnAttrs {
symbol_name: None,
link_ordinal: None,
target_features: vec![],
foreign_item_symbol_aliases: vec![],
safe_target_features: false,
linkage: None,
import_linkage: None,
@ -234,6 +252,9 @@ impl CodegenFnAttrs {
self.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
|| self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
// note: for these we do also set a symbol name so technically also handled by the
// condition below. However, I think that regardless these should be treated as extern.
|| self.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM)
|| self.symbol_name.is_some()
|| match self.linkage {
// These are private, so make sure we don't try to consider

View file

@ -41,6 +41,7 @@ pub struct SymbolExportInfo {
/// Was the symbol marked as `#[used(compiler)]` or `#[used(linker)]`?
pub used: bool,
/// Was the symbol marked as `#[rustc_std_internal_symbol]`?
/// We also use this for externally implementable items.
pub rustc_std_internal_symbol: bool,
}

View file

@ -3,9 +3,7 @@ use std::fmt::{self, Debug, Display, Formatter};
use rustc_abi::{HasDataLayout, Size};
use rustc_hir::def_id::DefId;
use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
use rustc_span::{DUMMY_SP, Span, Symbol};
use rustc_span::{DUMMY_SP, RemapPathScopeComponents, Span, Symbol};
use rustc_type_ir::TypeVisitableExt;
use super::interpret::ReportedErrorInfo;
@ -587,11 +585,7 @@ impl<'tcx> TyCtxt<'tcx> {
let caller = self.sess.source_map().lookup_char_pos(topmost.lo());
self.const_caller_location(
Symbol::intern(
&caller
.file
.name
.for_scope(self.sess, RemapPathScopeComponents::MACRO)
.to_string_lossy(),
&caller.file.name.display(RemapPathScopeComponents::MACRO).to_string_lossy(),
),
caller.line as u32,
caller.col_display as u32 + 1,

View file

@ -374,7 +374,7 @@ pub struct MonoItemData {
/// Visibility doesn't have any effect when linkage is internal.
///
/// DSO means dynamic shared object, that is a dynamically linked executable or dylib.
#[derive(Copy, Clone, PartialEq, Debug, HashStable)]
#[derive(Copy, Clone, PartialEq, Debug, HashStable, TyEncodable, TyDecodable)]
pub enum Visibility {
/// Export the symbol from the DSO and apply overrides of the symbol by outside DSOs to within
/// the DSO if the object file format supports this.

View file

@ -504,7 +504,7 @@ fn write_scope_tree(
"{0:1$} // at {2}",
indented_header,
ALIGN,
tcx.sess.source_map().span_to_embeddable_string(span),
tcx.sess.source_map().span_to_diagnostic_string(span),
)?;
} else {
writeln!(w, "{indented_header}")?;
@ -688,7 +688,7 @@ fn write_user_type_annotations(
"| {:?}: user_ty: {}, span: {}, inferred_ty: {}",
index.index(),
annotation.user_ty,
tcx.sess.source_map().span_to_embeddable_string(annotation.span),
tcx.sess.source_map().span_to_diagnostic_string(annotation.span),
with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)),
)?;
}
@ -1420,7 +1420,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
self.push("mir::ConstOperand");
self.push(&format!(
"+ span: {}",
self.tcx.sess.source_map().span_to_embeddable_string(*span)
self.tcx.sess.source_map().span_to_diagnostic_string(*span)
));
if let Some(user_ty) = user_ty {
self.push(&format!("+ user_ty: {user_ty:?}"));
@ -1503,7 +1503,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
}
fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
let location = tcx.sess.source_map().span_to_embeddable_string(span);
let location = tcx.sess.source_map().span_to_diagnostic_string(span);
format!("scope {} at {}", scope.index(), location,)
}

View file

@ -296,6 +296,8 @@ trivial! {
rustc_ast::expand::allocator::AllocatorKind,
rustc_hir::DefaultBodyStability,
rustc_hir::attrs::Deprecation,
rustc_hir::attrs::EiiDecl,
rustc_hir::attrs::EiiImpl,
rustc_data_structures::svh::Svh,
rustc_errors::ErrorGuaranteed,
rustc_hir::Constness,

View file

@ -76,7 +76,7 @@ use rustc_data_structures::steal::Steal;
use rustc_data_structures::svh::Svh;
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::attrs::StrippedCfgItem;
use rustc_hir::attrs::{EiiDecl, EiiImpl, StrippedCfgItem};
use rustc_hir::def::{DefKind, DocLinkResMap};
use rustc_hir::def_id::{
CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId,
@ -2753,6 +2753,18 @@ rustc_queries! {
desc { |tcx| "checking what set of sanitizers are enabled on `{}`", tcx.def_path_str(key) }
feedable
}
query check_externally_implementable_items(_: ()) {
desc { "check externally implementable items" }
}
/// Returns a list of all `externally implementable items` crate.
query externally_implementable_items(cnum: CrateNum) -> &'tcx FxIndexMap<DefId, (EiiDecl, FxIndexMap<DefId, EiiImpl>)> {
arena_cache
desc { "looking up the externally implementable items of a crate" }
cache_on_disk_if { *cnum == LOCAL_CRATE }
separate_provide_extern
}
}
rustc_with_all_queries! { define_callbacks! }

View file

@ -420,6 +420,13 @@ pub enum ObligationCauseCode<'tcx> {
/// Only reachable if the `unsized_fn_params` feature is used. Unsized function arguments must
/// be place expressions because we can't store them in MIR locals as temporaries.
UnsizedNonPlaceExpr(Span),
/// Error derived when checking an impl item is compatible with
/// its corresponding trait item's definition
CompareEii {
external_impl: LocalDefId,
declaration: DefId,
},
}
/// Whether a value can be extracted into a const.

View file

@ -16,7 +16,7 @@ use rustc_hir::definitions::{DefKey, DefPathDataName};
use rustc_hir::limit::Limit;
use rustc_macros::{Lift, extension};
use rustc_session::cstore::{ExternCrate, ExternCrateSource};
use rustc_span::{FileNameDisplayPreference, Ident, Symbol, kw, sym};
use rustc_span::{Ident, RemapPathScopeComponents, Symbol, kw, sym};
use rustc_type_ir::{Upcast as _, elaborate};
use smallvec::SmallVec;
@ -890,7 +890,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
"@{}",
// This may end up in stderr diagnostics but it may also be emitted
// into MIR. Hence we use the remapped path if available
self.tcx().sess.source_map().span_to_embeddable_string(span)
self.tcx().sess.source_map().span_to_diagnostic_string(span)
)?;
} else {
write!(self, "@")?;
@ -921,7 +921,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
"@{}",
// This may end up in stderr diagnostics but it may also be emitted
// into MIR. Hence we use the remapped path if available
self.tcx().sess.source_map().span_to_embeddable_string(span)
self.tcx().sess.source_map().span_to_diagnostic_string(span)
)?;
} else {
write!(self, "@")?;
@ -947,10 +947,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
self.print_def_path(did.to_def_id(), args)?;
} else {
let span = self.tcx().def_span(did);
let preference = if with_forced_trimmed_paths() {
FileNameDisplayPreference::Short
let loc = if with_forced_trimmed_paths() {
self.tcx().sess.source_map().span_to_short_string(
span,
RemapPathScopeComponents::DIAGNOSTICS,
)
} else {
FileNameDisplayPreference::Remapped
self.tcx().sess.source_map().span_to_diagnostic_string(span)
};
write!(
self,
@ -958,7 +961,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
// This may end up in stderr diagnostics but it may also be
// emitted into MIR. Hence we use the remapped path if
// available
self.tcx().sess.source_map().span_to_string(span, preference)
loc
)?;
}
} else {
@ -1004,18 +1007,17 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
self.print_def_path(did.to_def_id(), args)?;
} else {
let span = self.tcx().def_span(did);
let preference = if with_forced_trimmed_paths() {
FileNameDisplayPreference::Short
// This may end up in stderr diagnostics but it may also be emitted
// into MIR. Hence we use the remapped path if available
let loc = if with_forced_trimmed_paths() {
self.tcx().sess.source_map().span_to_short_string(
span,
RemapPathScopeComponents::DIAGNOSTICS,
)
} else {
FileNameDisplayPreference::Remapped
self.tcx().sess.source_map().span_to_diagnostic_string(span)
};
write!(
self,
"@{}",
// This may end up in stderr diagnostics but it may also be emitted
// into MIR. Hence we use the remapped path if available
self.tcx().sess.source_map().span_to_string(span, preference)
)?;
write!(self, "@{loc}")?;
}
} else {
write!(self, "@")?;
@ -2258,7 +2260,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
"<impl at {}>",
// This may end up in stderr diagnostics but it may also be emitted
// into MIR. Hence we use the remapped path if available
self.tcx.sess.source_map().span_to_embeddable_string(span)
self.tcx.sess.source_map().span_to_diagnostic_string(span)
)?;
self.empty_path = false;

View file

@ -1643,11 +1643,13 @@ impl<'v> RootCollector<'_, 'v> {
MonoItemCollectionStrategy::Lazy => {
self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id)
|| self.tcx.is_reachable_non_generic(def_id)
|| self
.tcx
.codegen_fn_attrs(def_id)
.flags
.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
|| {
let flags = self.tcx.codegen_fn_attrs(def_id).flags;
flags.intersects(
CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
| CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM,
)
}
}
}
}

Some files were not shown because too many files have changed in this diff Show more