Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2025-07-14 05:07:40 +00:00
commit 8812d741eb
525 changed files with 6171 additions and 4002 deletions

View file

@ -152,6 +152,9 @@ jobs:
- name: show the current environment
run: src/ci/scripts/dump-environment.sh
- name: install rust
run: src/ci/scripts/install-rust.sh
- name: install awscli
run: src/ci/scripts/install-awscli.sh

View file

@ -4358,6 +4358,7 @@ dependencies = [
"rustc_ast_lowering",
"rustc_ast_pretty",
"rustc_attr_data_structures",
"rustc_attr_parsing",
"rustc_data_structures",
"rustc_errors",
"rustc_expand",
@ -5250,9 +5251,9 @@ dependencies = [
[[package]]
name = "sysinfo"
version = "0.35.2"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3ffa3e4ff2b324a57f7aeb3c349656c7b127c3c189520251a648102a92496e"
checksum = "aab138f5c1bb35231de19049060a87977ad23e04f2303e953bc5c2947ac7dec4"
dependencies = [
"libc",
"objc2-core-foundation",

View file

@ -27,6 +27,7 @@ features = ['unprefixed_malloc_on_supported_platforms']
[features]
# tidy-alphabetical-start
check_only = ['rustc_driver_impl/check_only']
jemalloc = ['dep:tikv-jemalloc-sys']
llvm = ['rustc_driver_impl/llvm']
max_level_info = ['rustc_driver_impl/max_level_info']

View file

@ -47,6 +47,7 @@ use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::DefId;
use rustc_middle::span_bug;
use rustc_middle::ty::{Asyncness, ResolverAstLowering};
use rustc_span::symbol::kw;
use rustc_span::{Ident, Span, Symbol};
use {rustc_ast as ast, rustc_hir as hir};
@ -61,21 +62,6 @@ pub(crate) struct DelegationResults<'hir> {
}
impl<'hir> LoweringContext<'_, 'hir> {
/// Defines whether the delegatee is an associated function whose first parameter is `self`.
pub(crate) fn delegatee_is_method(
&self,
item_id: NodeId,
path_id: NodeId,
span: Span,
is_in_trait_impl: bool,
) -> bool {
let sig_id = self.get_delegation_sig_id(item_id, path_id, span, is_in_trait_impl);
let Ok(sig_id) = sig_id else {
return false;
};
self.is_method(sig_id, span)
}
fn is_method(&self, def_id: DefId, span: Span) -> bool {
match self.tcx.def_kind(def_id) {
DefKind::Fn => false,
@ -101,10 +87,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
let sig_id = self.get_delegation_sig_id(item_id, delegation.id, span, is_in_trait_impl);
match sig_id {
Ok(sig_id) => {
let is_method = self.is_method(sig_id, span);
let (param_count, c_variadic) = self.param_count(sig_id);
let decl = self.lower_delegation_decl(sig_id, param_count, c_variadic, span);
let sig = self.lower_delegation_sig(sig_id, decl, span);
let body_id = self.lower_delegation_body(delegation, param_count, span);
let body_id = self.lower_delegation_body(delegation, is_method, param_count, span);
let ident = self.lower_ident(delegation.ident);
let generics = self.lower_delegation_generics(span);
DelegationResults { body_id, sig, ident, generics }
@ -234,10 +221,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::FnSig { decl, header, span }
}
fn generate_param(&mut self, idx: usize, span: Span) -> (hir::Param<'hir>, NodeId) {
fn generate_param(
&mut self,
is_method: bool,
idx: usize,
span: Span,
) -> (hir::Param<'hir>, NodeId) {
let pat_node_id = self.next_node_id();
let pat_id = self.lower_node_id(pat_node_id);
let ident = Ident::with_dummy_span(Symbol::intern(&format!("arg{idx}")));
// FIXME(cjgillot) AssocItem currently relies on self parameter being exactly named `self`.
let name = if is_method && idx == 0 {
kw::SelfLower
} else {
Symbol::intern(&format!("arg{idx}"))
};
let ident = Ident::with_dummy_span(name);
let pat = self.arena.alloc(hir::Pat {
hir_id: pat_id,
kind: hir::PatKind::Binding(hir::BindingMode::NONE, pat_id, ident, None),
@ -248,9 +246,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
(hir::Param { hir_id: self.next_id(), pat, ty_span: span, span }, pat_node_id)
}
fn generate_arg(&mut self, idx: usize, param_id: HirId, span: Span) -> hir::Expr<'hir> {
fn generate_arg(
&mut self,
is_method: bool,
idx: usize,
param_id: HirId,
span: Span,
) -> hir::Expr<'hir> {
// FIXME(cjgillot) AssocItem currently relies on self parameter being exactly named `self`.
let name = if is_method && idx == 0 {
kw::SelfLower
} else {
Symbol::intern(&format!("arg{idx}"))
};
let segments = self.arena.alloc_from_iter(iter::once(hir::PathSegment {
ident: Ident::with_dummy_span(Symbol::intern(&format!("arg{idx}"))),
ident: Ident::with_dummy_span(name),
hir_id: self.next_id(),
res: Res::Local(param_id),
args: None,
@ -264,6 +274,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_delegation_body(
&mut self,
delegation: &Delegation,
is_method: bool,
param_count: usize,
span: Span,
) -> BodyId {
@ -274,7 +285,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let mut args: Vec<hir::Expr<'_>> = Vec::with_capacity(param_count);
for idx in 0..param_count {
let (param, pat_node_id) = this.generate_param(idx, span);
let (param, pat_node_id) = this.generate_param(is_method, idx, span);
parameters.push(param);
let arg = if let Some(block) = block
@ -290,7 +301,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
this.ident_and_label_to_local_id.insert(pat_node_id, param.pat.hir_id.local_id);
this.lower_target_expr(&block)
} else {
this.generate_arg(idx, param.pat.hir_id, span)
this.generate_arg(is_method, idx, param.pat.hir_id, span)
};
args.push(arg);
}

View file

@ -528,7 +528,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
then: &Block,
else_opt: Option<&Expr>,
) -> hir::ExprKind<'hir> {
let lowered_cond = self.lower_cond(cond);
let lowered_cond = self.lower_expr(cond);
let then_expr = self.lower_block_expr(then);
if let Some(rslt) = else_opt {
hir::ExprKind::If(
@ -541,44 +541,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
// Lowers a condition (i.e. `cond` in `if cond` or `while cond`), wrapping it in a terminating scope
// so that temporaries created in the condition don't live beyond it.
fn lower_cond(&mut self, cond: &Expr) -> &'hir hir::Expr<'hir> {
fn has_let_expr(expr: &Expr) -> bool {
match &expr.kind {
ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs),
ExprKind::Let(..) => true,
_ => false,
}
}
// We have to take special care for `let` exprs in the condition, e.g. in
// `if let pat = val` or `if foo && let pat = val`, as we _do_ want `val` to live beyond the
// condition in this case.
//
// In order to maintain the drop behavior for the non `let` parts of the condition,
// we still wrap them in terminating scopes, e.g. `if foo && let pat = val` essentially
// gets transformed into `if { let _t = foo; _t } && let pat = val`
match &cond.kind {
ExprKind::Binary(op @ Spanned { node: ast::BinOpKind::And, .. }, lhs, rhs)
if has_let_expr(cond) =>
{
let op = self.lower_binop(*op);
let lhs = self.lower_cond(lhs);
let rhs = self.lower_cond(rhs);
self.arena.alloc(self.expr(cond.span, hir::ExprKind::Binary(op, lhs, rhs)))
}
ExprKind::Let(..) => self.lower_expr(cond),
_ => {
let cond = self.lower_expr(cond);
let reason = DesugaringKind::CondTemporary;
let span_block = self.mark_span_with_reason(reason, cond.span, None);
self.expr_drop_temps(span_block, cond)
}
}
}
// We desugar: `'label: while $cond $body` into:
//
// ```
@ -602,7 +564,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
body: &Block,
opt_label: Option<Label>,
) -> hir::ExprKind<'hir> {
let lowered_cond = self.with_loop_condition_scope(|t| t.lower_cond(cond));
let lowered_cond = self.with_loop_condition_scope(|t| t.lower_expr(cond));
let then = self.lower_block_expr(body);
let expr_break = self.expr_break(span);
let stmt_break = self.stmt_expr(span, expr_break);
@ -2091,7 +2053,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// In terms of drop order, it has the same effect as wrapping `expr` in
/// `{ let _t = $expr; _t }` but should provide better compile-time performance.
///
/// The drop order can be important in e.g. `if expr { .. }`.
/// The drop order can be important, e.g. to drop temporaries from an `async fn`
/// body before its parameters.
pub(super) fn expr_drop_temps(
&mut self,
span: Span,

View file

@ -381,28 +381,16 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
})
}
fn visit_trait_item_ref(&mut self, ii: &'hir TraitItemRef) {
// Do not visit the duplicate information in TraitItemRef. We want to
// map the actual nodes, not the duplicate ones in the *Ref.
let TraitItemRef { id, ident: _, kind: _, span: _ } = *ii;
self.visit_nested_trait_item(id);
fn visit_trait_item_ref(&mut self, id: &'hir TraitItemId) {
self.visit_nested_trait_item(*id);
}
fn visit_impl_item_ref(&mut self, ii: &'hir ImplItemRef) {
// Do not visit the duplicate information in ImplItemRef. We want to
// map the actual nodes, not the duplicate ones in the *Ref.
let ImplItemRef { id, ident: _, kind: _, span: _, trait_item_def_id: _ } = *ii;
self.visit_nested_impl_item(id);
fn visit_impl_item_ref(&mut self, id: &'hir ImplItemId) {
self.visit_nested_impl_item(*id);
}
fn visit_foreign_item_ref(&mut self, fi: &'hir ForeignItemRef) {
// Do not visit the duplicate information in ForeignItemRef. We want to
// map the actual nodes, not the duplicate ones in the *Ref.
let ForeignItemRef { id, ident: _, span: _ } = *fi;
self.visit_nested_foreign_item(id);
fn visit_foreign_item_ref(&mut self, id: &'hir ForeignItemId) {
self.visit_nested_foreign_item(*id);
}
fn visit_where_predicate(&mut self, predicate: &'hir WherePredicate<'hir>) {

View file

@ -393,11 +393,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
(trait_ref, lowered_ty)
});
let new_impl_items = self.arena.alloc_from_iter(
impl_items
.iter()
.map(|item| self.lower_impl_item_ref(item, trait_ref.is_some())),
);
let new_impl_items = self
.arena
.alloc_from_iter(impl_items.iter().map(|item| self.lower_impl_item_ref(item)));
// `defaultness.has_value()` is never called for an `impl`, always `true` in order
// to not cause an assertion failure inside the `lower_defaultness` function.
@ -706,14 +704,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.arena.alloc(item)
}
fn lower_foreign_item_ref(&mut self, i: &ForeignItem) -> hir::ForeignItemRef {
hir::ForeignItemRef {
id: hir::ForeignItemId { owner_id: self.owner_id(i.id) },
// `unwrap` is safe because `ForeignItemKind::MacCall` is the only foreign item kind
// without an identifier and it cannot reach here.
ident: self.lower_ident(i.kind.ident().unwrap()),
span: self.lower_span(i.span),
}
fn lower_foreign_item_ref(&mut self, i: &ForeignItem) -> hir::ForeignItemId {
hir::ForeignItemId { owner_id: self.owner_id(i.id) }
}
fn lower_variant(&mut self, item_kind: &ItemKind, v: &Variant) -> hir::Variant<'hir> {
@ -972,32 +964,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.arena.alloc(item)
}
fn lower_trait_item_ref(&mut self, i: &AssocItem) -> hir::TraitItemRef {
let (ident, kind) = match &i.kind {
AssocItemKind::Const(box ConstItem { ident, .. }) => {
(*ident, hir::AssocItemKind::Const)
}
AssocItemKind::Type(box TyAlias { ident, .. }) => (*ident, hir::AssocItemKind::Type),
AssocItemKind::Fn(box Fn { ident, sig, .. }) => {
(*ident, hir::AssocItemKind::Fn { has_self: sig.decl.has_self() })
}
AssocItemKind::Delegation(box delegation) => (
delegation.ident,
hir::AssocItemKind::Fn {
has_self: self.delegatee_is_method(i.id, delegation.id, i.span, false),
},
),
AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
panic!("macros should have been expanded by now")
}
};
let id = hir::TraitItemId { owner_id: self.owner_id(i.id) };
hir::TraitItemRef {
id,
ident: self.lower_ident(ident),
span: self.lower_span(i.span),
kind,
}
fn lower_trait_item_ref(&mut self, i: &AssocItem) -> hir::TraitItemId {
hir::TraitItemId { owner_id: self.owner_id(i.id) }
}
/// Construct `ExprKind::Err` for the given `span`.
@ -1128,41 +1096,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
span: self.lower_span(i.span),
defaultness,
has_delayed_lints: !self.delayed_lints.is_empty(),
};
self.arena.alloc(item)
}
fn lower_impl_item_ref(&mut self, i: &AssocItem, is_in_trait_impl: bool) -> hir::ImplItemRef {
hir::ImplItemRef {
id: hir::ImplItemId { owner_id: self.owner_id(i.id) },
// `unwrap` is safe because `AssocItemKind::{MacCall,DelegationMac}` are the only
// assoc item kinds without an identifier and they cannot reach here.
ident: self.lower_ident(i.kind.ident().unwrap()),
span: self.lower_span(i.span),
kind: match &i.kind {
AssocItemKind::Const(..) => hir::AssocItemKind::Const,
AssocItemKind::Type(..) => hir::AssocItemKind::Type,
AssocItemKind::Fn(box Fn { sig, .. }) => {
hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }
}
AssocItemKind::Delegation(box delegation) => hir::AssocItemKind::Fn {
has_self: self.delegatee_is_method(
i.id,
delegation.id,
i.span,
is_in_trait_impl,
),
},
AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
panic!("macros should have been expanded by now")
}
},
trait_item_def_id: self
.resolver
.get_partial_res(i.id)
.map(|r| r.expect_full_res().opt_def_id())
.unwrap_or(None),
}
};
self.arena.alloc(item)
}
fn lower_impl_item_ref(&mut self, i: &AssocItem) -> hir::ImplItemId {
hir::ImplItemId { owner_id: self.owner_id(i.id) }
}
fn lower_defaultness(

View file

@ -120,7 +120,7 @@ fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec<Comment>
pos += shebang_len;
}
for token in rustc_lexer::tokenize(&text[pos..]) {
for token in rustc_lexer::tokenize(&text[pos..], rustc_lexer::FrontmatterAllowed::Yes) {
let token_text = &text[pos..pos + token.len as usize];
match token.kind {
rustc_lexer::TokenKind::Whitespace => {
@ -171,6 +171,14 @@ fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec<Comment>
})
}
}
rustc_lexer::TokenKind::Frontmatter { .. } => {
code_to_the_left = false;
comments.push(Comment {
style: CommentStyle::Isolated,
lines: vec![token_text.to_string()],
pos: start_bpos + BytePos(pos as u32),
});
}
_ => {
code_to_the_left = true;
}

View file

@ -198,6 +198,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_allow_const_fn_unstable]`.
AllowConstFnUnstable(ThinVec<Symbol>, Span),
/// Represents `#[rustc_allow_incoherent_impl]`.
AllowIncoherentImpl(Span),
/// Represents `#[allow_internal_unstable]`.
AllowInternalUnstable(ThinVec<(Symbol, Span)>, Span),
@ -211,6 +214,12 @@ pub enum AttributeKind {
span: Span,
},
/// Represents `#[rustc_coherence_is_core]`.
CoherenceIsCore,
/// Represents `#[rustc_coinductive]`.
Coinductive(Span),
/// Represents `#[cold]`.
Cold(Span),
@ -234,9 +243,18 @@ pub enum AttributeKind {
/// Represents `#[rustc_const_stable_indirect]`.
ConstStabilityIndirect,
/// Represents `#[const_trait]`.
ConstTrait(Span),
///Represents `#[rustc_deny_explicit_impl]`.
DenyExplicitImpl(Span),
/// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute).
Deprecation { deprecation: Deprecation, span: Span },
/// Represents `#[rustc_do_not_implement_via_object]`.
DoNotImplementViaObject(Span),
/// Represents [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html).
DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol },
@ -260,6 +278,9 @@ pub enum AttributeKind {
/// Represents `#[ffi_pure]`.
FfiPure(Span),
/// Represents `#[fundamental]`.
Fundamental,
/// Represents `#[ignore]`
Ignore {
span: Span,
@ -282,6 +303,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_macro_transparency]`.
MacroTransparency(Transparency),
/// Represents `#[marker]`.
Marker(Span),
/// Represents [`#[may_dangle]`](https://std-dev-guide.rust-lang.org/tricky/may-dangle.html).
MayDangle(Span),
@ -307,6 +331,9 @@ pub enum AttributeKind {
/// Represents `#[optimize(size|speed)]`
Optimize(OptimizeAttr, Span),
/// Represents `#[rustc_paren_sugar]`.
ParenSugar(Span),
/// Represents `#[rustc_pass_by_value]` (used by the `rustc_pass_by_value` lint).
PassByValue(Span),
@ -331,6 +358,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_skip_during_method_dispatch]`.
SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span },
/// Represents `#[rustc_specialization_trait]`.
SpecializationTrait(Span),
/// Represents `#[stable]`, `#[unstable]` and `#[rustc_allowed_through_unstable_modules]`.
Stability {
stability: Stability,
@ -347,6 +377,12 @@ pub enum AttributeKind {
/// Represents `#[track_caller]`
TrackCaller(Span),
/// Represents `#[type_const]`.
TypeConst(Span),
/// Represents `#[rustc_unsafe_specialization_marker]`.
UnsafeSpecializationMarker(Span),
/// Represents `#[used]`
Used { used_by: UsedBy, span: Span },
// tidy-alphabetical-end

View file

@ -15,27 +15,35 @@ impl AttributeKind {
// tidy-alphabetical-start
Align { .. } => No,
AllowConstFnUnstable(..) => No,
AllowIncoherentImpl(..) => No,
AllowInternalUnstable(..) => Yes,
AsPtr(..) => Yes,
BodyStability { .. } => No,
CoherenceIsCore => No,
Coinductive(..) => No,
Cold(..) => No,
Confusables { .. } => Yes,
ConstContinue(..) => No,
ConstStability { .. } => Yes,
ConstStabilityIndirect => No,
ConstTrait(..) => No,
DenyExplicitImpl(..) => No,
Deprecation { .. } => Yes,
DoNotImplementViaObject(..) => No,
DocComment { .. } => Yes,
Dummy => No,
ExportName { .. } => Yes,
ExportStable => No,
FfiConst(..) => No,
FfiPure(..) => No,
Fundamental { .. } => Yes,
Ignore { .. } => No,
Inline(..) => No,
LinkName { .. } => Yes,
LinkSection { .. } => No,
LoopMatch(..) => No,
MacroTransparency(..) => Yes,
Marker(..) => No,
MayDangle(..) => No,
MustUse { .. } => Yes,
Naked(..) => No,
@ -43,6 +51,7 @@ impl AttributeKind {
NoMangle(..) => No,
NonExhaustive(..) => Yes,
Optimize(..) => No,
ParenSugar(..) => No,
PassByValue(..) => Yes,
Path(..) => No,
PubTransparent(..) => Yes,
@ -51,10 +60,13 @@ impl AttributeKind {
RustcLayoutScalarValidRangeStart(..) => Yes,
RustcObjectLifetimeDefault => No,
SkipDuringMethodDispatch { .. } => No,
SpecializationTrait(..) => No,
Stability { .. } => Yes,
StdInternalSymbol(..) => No,
TargetFeature(..) => No,
TrackCaller(..) => Yes,
TypeConst(..) => Yes,
UnsafeSpecializationMarker(..) => No,
Used { .. } => No,
// tidy-alphabetical-end
}

View file

@ -2,14 +2,15 @@ use core::mem;
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Symbol, sym};
use rustc_span::{Span, Symbol, sym};
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
use crate::attributes::{
AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
};
use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
pub(crate) struct SkipDuringMethodDispatchParser;
impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
const PATH: &[Symbol] = &[sym::rustc_skip_during_method_dispatch];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
@ -52,3 +53,95 @@ impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
Some(AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span: cx.attr_span })
}
}
pub(crate) struct ParenSugarParser;
impl<S: Stage> NoArgsAttributeParser<S> for ParenSugarParser {
const PATH: &[Symbol] = &[sym::rustc_paren_sugar];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ParenSugar;
}
pub(crate) struct TypeConstParser;
impl<S: Stage> NoArgsAttributeParser<S> for TypeConstParser {
const PATH: &[Symbol] = &[sym::type_const];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::TypeConst;
}
// Markers
pub(crate) struct MarkerParser;
impl<S: Stage> NoArgsAttributeParser<S> for MarkerParser {
const PATH: &[Symbol] = &[sym::marker];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Marker;
}
pub(crate) struct DenyExplicitImplParser;
impl<S: Stage> NoArgsAttributeParser<S> for DenyExplicitImplParser {
const PATH: &[Symbol] = &[sym::rustc_deny_explicit_impl];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DenyExplicitImpl;
}
pub(crate) struct DoNotImplementViaObjectParser;
impl<S: Stage> NoArgsAttributeParser<S> for DoNotImplementViaObjectParser {
const PATH: &[Symbol] = &[sym::rustc_do_not_implement_via_object];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DoNotImplementViaObject;
}
// Const traits
pub(crate) struct ConstTraitParser;
impl<S: Stage> NoArgsAttributeParser<S> for ConstTraitParser {
const PATH: &[Symbol] = &[sym::const_trait];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ConstTrait;
}
// Specialization
pub(crate) struct SpecializationTraitParser;
impl<S: Stage> NoArgsAttributeParser<S> for SpecializationTraitParser {
const PATH: &[Symbol] = &[sym::rustc_specialization_trait];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::SpecializationTrait;
}
pub(crate) struct UnsafeSpecializationMarkerParser;
impl<S: Stage> NoArgsAttributeParser<S> for UnsafeSpecializationMarkerParser {
const PATH: &[Symbol] = &[sym::rustc_unsafe_specialization_marker];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::UnsafeSpecializationMarker;
}
// Coherence
pub(crate) struct CoinductiveParser;
impl<S: Stage> NoArgsAttributeParser<S> for CoinductiveParser {
const PATH: &[Symbol] = &[sym::rustc_coinductive];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Coinductive;
}
pub(crate) struct AllowIncoherentImplParser;
impl<S: Stage> NoArgsAttributeParser<S> for AllowIncoherentImplParser {
const PATH: &[Symbol] = &[sym::rustc_allow_incoherent_impl];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::AllowIncoherentImpl;
}
pub(crate) struct CoherenceIsCoreParser;
impl<S: Stage> NoArgsAttributeParser<S> for CoherenceIsCoreParser {
const PATH: &[Symbol] = &[sym::rustc_coherence_is_core];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CoherenceIsCore;
}
pub(crate) struct FundamentalParser;
impl<S: Stage> NoArgsAttributeParser<S> for FundamentalParser {
const PATH: &[Symbol] = &[sym::fundamental];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Fundamental;
}

View file

@ -43,7 +43,12 @@ use crate::attributes::stability::{
BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
};
use crate::attributes::test_attrs::IgnoreParser;
use crate::attributes::traits::SkipDuringMethodDispatchParser;
use crate::attributes::traits::{
AllowIncoherentImplParser, CoherenceIsCoreParser, CoinductiveParser, ConstTraitParser,
DenyExplicitImplParser, DoNotImplementViaObjectParser, FundamentalParser, MarkerParser,
ParenSugarParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser,
UnsafeSpecializationMarkerParser,
};
use crate::attributes::transparency::TransparencyParser;
use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs};
use crate::parser::{ArgParser, MetaItemParser, PathParser};
@ -146,22 +151,34 @@ attribute_parsers!(
Single<RustcObjectLifetimeDefaultParser>,
Single<SkipDuringMethodDispatchParser>,
Single<TransparencyParser>,
Single<WithoutArgs<AllowIncoherentImplParser>>,
Single<WithoutArgs<AsPtrParser>>,
Single<WithoutArgs<CoherenceIsCoreParser>>,
Single<WithoutArgs<CoinductiveParser>>,
Single<WithoutArgs<ColdParser>>,
Single<WithoutArgs<ConstContinueParser>>,
Single<WithoutArgs<ConstStabilityIndirectParser>>,
Single<WithoutArgs<ConstTraitParser>>,
Single<WithoutArgs<DenyExplicitImplParser>>,
Single<WithoutArgs<DoNotImplementViaObjectParser>>,
Single<WithoutArgs<ExportStableParser>>,
Single<WithoutArgs<FfiConstParser>>,
Single<WithoutArgs<FfiPureParser>>,
Single<WithoutArgs<FundamentalParser>>,
Single<WithoutArgs<LoopMatchParser>>,
Single<WithoutArgs<MarkerParser>>,
Single<WithoutArgs<MayDangleParser>>,
Single<WithoutArgs<NoImplicitPreludeParser>>,
Single<WithoutArgs<NoMangleParser>>,
Single<WithoutArgs<NonExhaustiveParser>>,
Single<WithoutArgs<ParenSugarParser>>,
Single<WithoutArgs<PassByValueParser>>,
Single<WithoutArgs<PubTransparentParser>>,
Single<WithoutArgs<SpecializationTraitParser>>,
Single<WithoutArgs<StdInternalSymbolParser>>,
Single<WithoutArgs<TrackCallerParser>>,
Single<WithoutArgs<TypeConstParser>>,
Single<WithoutArgs<UnsafeSpecializationMarkerParser>>,
// tidy-alphabetical-end
];
);
@ -718,6 +735,11 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
attributes
}
/// Returns whether there is a parser for an attribute with this name
pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
Late::parsers().0.contains_key(path)
}
fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
match args {
ast::AttrArgs::Empty => AttrArgs::Empty,

View file

@ -681,46 +681,30 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
return (false, false, None);
}
let my_def = self.body.source.def_id();
let my_hir = self.infcx.tcx.local_def_id_to_hir_id(my_def.as_local().unwrap());
let Some(td) =
self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
else {
return (false, false, None);
};
let implemented_trait_item = self.infcx.tcx.associated_item(my_def).trait_item_def_id;
(
true,
td.is_local(),
td.as_local().and_then(|tld| match self.infcx.tcx.hir_node_by_def_id(tld) {
Node::Item(hir::Item {
kind: hir::ItemKind::Trait(_, _, _, _, _, items), ..
}) => {
let mut f_in_trait_opt = None;
for hir::TraitItemRef { id: fi, kind: k, .. } in *items {
let hi = fi.hir_id();
if !matches!(k, hir::AssocItemKind::Fn { .. }) {
continue;
}
if self.infcx.tcx.hir_name(hi) != self.infcx.tcx.hir_name(my_hir) {
continue;
}
f_in_trait_opt = Some(hi);
break;
}
f_in_trait_opt.and_then(|f_in_trait| {
if let Node::TraitItem(ti) = self.infcx.tcx.hir_node(f_in_trait)
&& let hir::TraitItemKind::Fn(sig, _) = ti.kind
&& let Some(ty) = sig.decl.inputs.get(local.index() - 1)
&& let hir::TyKind::Ref(_, mut_ty) = ty.kind
&& let hir::Mutability::Not = mut_ty.mutbl
&& sig.decl.implicit_self.has_implicit_self()
{
Some(ty.span)
} else {
None
}
})
implemented_trait_item.and_then(|f_in_trait| {
let f_in_trait = f_in_trait.as_local()?;
if let Node::TraitItem(ti) = self.infcx.tcx.hir_node_by_def_id(f_in_trait)
&& let hir::TraitItemKind::Fn(sig, _) = ti.kind
&& let Some(ty) = sig.decl.inputs.get(local.index() - 1)
&& let hir::TyKind::Ref(_, mut_ty) = ty.kind
&& let hir::Mutability::Not = mut_ty.mutbl
&& sig.decl.implicit_self.has_implicit_self()
{
Some(ty.span)
} else {
None
}
_ => None,
}),
)
}

View file

@ -81,6 +81,12 @@ builtin_macros_cfg_accessible_literal_path = `cfg_accessible` path cannot be a l
builtin_macros_cfg_accessible_multiple_paths = multiple `cfg_accessible` paths are specified
builtin_macros_cfg_accessible_unspecified_path = `cfg_accessible` path is not specified
builtin_macros_cfg_select_no_matches = none of the rules in this `cfg_select` evaluated to true
builtin_macros_cfg_select_unreachable = unreachable rule
.label = always matches
.label2 = this rules is never reached
builtin_macros_coerce_pointee_requires_maybe_sized = `derive(CoercePointee)` requires `{$name}` to be marked `?Sized`
builtin_macros_coerce_pointee_requires_one_field = `CoercePointee` can only be derived on `struct`s with at least one field

View file

@ -0,0 +1,63 @@
use rustc_ast::tokenstream::TokenStream;
use rustc_attr_parsing as attr;
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
use rustc_parse::parser::cfg_select::{CfgSelectBranches, CfgSelectRule, parse_cfg_select};
use rustc_span::{Ident, Span, sym};
use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable};
/// Selects the first arm whose rule evaluates to true.
fn select_arm(ecx: &ExtCtxt<'_>, branches: CfgSelectBranches) -> Option<(TokenStream, Span)> {
for (cfg, tt, arm_span) in branches.reachable {
if attr::cfg_matches(
&cfg,
&ecx.sess,
ecx.current_expansion.lint_node_id,
Some(ecx.ecfg.features),
) {
return Some((tt, arm_span));
}
}
branches.wildcard.map(|(_, tt, span)| (tt, span))
}
pub(super) fn expand_cfg_select<'cx>(
ecx: &'cx mut ExtCtxt<'_>,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
ExpandResult::Ready(match parse_cfg_select(&mut ecx.new_parser_from_tts(tts)) {
Ok(branches) => {
if let Some((underscore, _, _)) = branches.wildcard {
// Warn for every unreachable rule. We store the fully parsed branch for rustfmt.
for (rule, _, _) in &branches.unreachable {
let span = match rule {
CfgSelectRule::Wildcard(underscore) => underscore.span,
CfgSelectRule::Cfg(cfg) => cfg.span(),
};
let err = CfgSelectUnreachable { span, wildcard_span: underscore.span };
ecx.dcx().emit_warn(err);
}
}
if let Some((tts, arm_span)) = select_arm(ecx, branches) {
return ExpandResult::from_tts(
ecx,
tts,
sp,
arm_span,
Ident::with_dummy_span(sym::cfg_select),
);
} else {
// Emit a compiler error when none of the rules matched.
let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp });
DummyResult::any(sp, guar)
}
}
Err(err) => {
let guar = err.emit();
DummyResult::any(sp, guar)
}
})
}

View file

@ -954,3 +954,21 @@ pub(crate) struct AsmExpectedOther {
pub(crate) span: Span,
pub(crate) is_inline_asm: bool,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_cfg_select_no_matches)]
pub(crate) struct CfgSelectNoMatches {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_cfg_select_unreachable)]
pub(crate) struct CfgSelectUnreachable {
#[primary_span]
#[label(builtin_macros_label2)]
pub span: Span,
#[label]
pub wildcard_span: Span,
}

View file

@ -33,6 +33,7 @@ mod autodiff;
mod cfg;
mod cfg_accessible;
mod cfg_eval;
mod cfg_select;
mod compile_error;
mod concat;
mod concat_bytes;
@ -79,6 +80,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
asm: asm::expand_asm,
assert: assert::expand_assert,
cfg: cfg::expand_cfg,
cfg_select: cfg_select::expand_cfg_select,
column: source_util::expand_column,
compile_error: compile_error::expand_compile_error,
concat: concat::expand_concat,

View file

@ -926,10 +926,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
.get_address(self.location)
}
fn dynamic_alloca(&mut self, _len: RValue<'gcc>, _align: Align) -> RValue<'gcc> {
unimplemented!();
}
fn load(&mut self, pointee_ty: Type<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> {
let block = self.llbb();
let function = block.get_function();

View file

@ -43,3 +43,6 @@ serde_json = "1"
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
tracing = "0.1"
# tidy-alphabetical-end
[features]
check_only = ["rustc_llvm/check_only"]

View file

@ -384,15 +384,19 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
) {
let asm_arch = self.tcx.sess.asm_arch.unwrap();
// Default to Intel syntax on x86
let intel_syntax = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64)
&& !options.contains(InlineAsmOptions::ATT_SYNTAX);
// Build the template string
let mut template_str = String::new();
if intel_syntax {
template_str.push_str(".intel_syntax\n");
// On X86 platforms there are two assembly syntaxes. Rust uses intel by default,
// but AT&T can be specified explicitly.
if matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64) {
if options.contains(InlineAsmOptions::ATT_SYNTAX) {
template_str.push_str(".att_syntax\n")
} else {
template_str.push_str(".intel_syntax\n")
}
}
for piece in template {
match *piece {
InlineAsmTemplatePiece::String(ref s) => template_str.push_str(s),
@ -431,7 +435,11 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
}
}
}
if intel_syntax {
// Just to play it safe, if intel was used, reset the assembly syntax to att.
if matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64)
&& !options.contains(InlineAsmOptions::ATT_SYNTAX)
{
template_str.push_str("\n.att_syntax\n");
}

View file

@ -680,7 +680,7 @@ pub(crate) fn run_pass_manager(
if attributes::has_string_attr(function, enzyme_marker) {
// Sanity check: Ensure 'noinline' is present before replacing it.
assert!(
!attributes::has_attr(function, Function, llvm::AttributeKind::NoInline),
attributes::has_attr(function, Function, llvm::AttributeKind::NoInline),
"Expected __enzyme function to have 'noinline' before adding 'alwaysinline'"
);

View file

@ -1182,7 +1182,7 @@ fn create_msvc_imps(
.filter_map(|val| {
// Exclude some symbols that we know are not Rust symbols.
let name = llvm::get_value_name(val);
if ignored(name) { None } else { Some((val, name)) }
if ignored(&name) { None } else { Some((val, name)) }
})
.map(move |(val, name)| {
let mut imp_name = prefix.as_bytes().to_vec();

View file

@ -538,16 +538,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
}
}
fn dynamic_alloca(&mut self, size: &'ll Value, align: Align) -> &'ll Value {
unsafe {
let alloca =
llvm::LLVMBuildArrayAlloca(self.llbuilder, self.cx().type_i8(), size, UNNAMED);
llvm::LLVMSetAlignment(alloca, align.bytes() as c_uint);
// Cast to default addrspace if necessary
llvm::LLVMBuildPointerCast(self.llbuilder, alloca, self.cx().type_ptr(), UNNAMED)
}
}
fn load(&mut self, ty: &'ll Type, ptr: &'ll Value, align: Align) -> &'ll Value {
unsafe {
let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED);

View file

@ -306,7 +306,7 @@ fn generate_enzyme_call<'ll>(
// add outer_fn name to ad_name to make it unique, in case users apply autodiff to multiple
// functions. Unwrap will only panic, if LLVM gave us an invalid string.
let name = llvm::get_value_name(outer_fn);
let outer_fn_name = std::str::from_utf8(name).unwrap();
let outer_fn_name = std::str::from_utf8(&name).unwrap();
ad_name.push_str(outer_fn_name);
// Let us assume the user wrote the following function square:

View file

@ -429,7 +429,7 @@ impl<'ll> CodegenCx<'ll, '_> {
// specific rules on what can be cast. So instead of adding a new way to
// generate static initializers that match the static's type, we picked
// the easier option and retroactively change the type of the static item itself.
let name = llvm::get_value_name(g).to_vec();
let name = llvm::get_value_name(g);
llvm::set_value_name(g, b"");
let linkage = llvm::get_linkage(g);

View file

@ -1492,12 +1492,6 @@ unsafe extern "C" {
Ty: &'a Type,
Name: *const c_char,
) -> &'a Value;
pub(crate) fn LLVMBuildArrayAlloca<'a>(
B: &Builder<'a>,
Ty: &'a Type,
Val: &'a Value,
Name: *const c_char,
) -> &'a Value;
pub(crate) fn LLVMBuildLoad2<'a>(
B: &Builder<'a>,
Ty: &'a Type,
@ -1980,12 +1974,12 @@ unsafe extern "C" {
pub(crate) fn LLVMRustBuildMinNum<'a>(
B: &Builder<'a>,
LHS: &'a Value,
LHS: &'a Value,
RHS: &'a Value,
) -> &'a Value;
pub(crate) fn LLVMRustBuildMaxNum<'a>(
B: &Builder<'a>,
LHS: &'a Value,
LHS: &'a Value,
RHS: &'a Value,
) -> &'a Value;
// Atomic Operations

View file

@ -211,7 +211,7 @@ pub(crate) fn SetFunctionCallConv(fn_: &Value, cc: CallConv) {
// function.
// For more details on COMDAT sections see e.g., https://www.airs.com/blog/archives/52
pub(crate) fn SetUniqueComdat(llmod: &Module, val: &Value) {
let name_buf = get_value_name(val).to_vec();
let name_buf = get_value_name(val);
let name =
CString::from_vec_with_nul(name_buf).or_else(|buf| CString::new(buf.into_bytes())).unwrap();
set_comdat(llmod, val, &name);
@ -319,12 +319,14 @@ pub(crate) fn get_param(llfn: &Value, index: c_uint) -> &Value {
}
}
/// Safe wrapper for `LLVMGetValueName2` into a byte slice
pub(crate) fn get_value_name(value: &Value) -> &[u8] {
/// Safe wrapper for `LLVMGetValueName2`
/// Needs to allocate the value, because `set_value_name` will invalidate
/// the pointer.
pub(crate) fn get_value_name(value: &Value) -> Vec<u8> {
unsafe {
let mut len = 0;
let data = LLVMGetValueName2(value, &mut len);
std::slice::from_raw_parts(data.cast(), len)
std::slice::from_raw_parts(data.cast(), len).to_vec()
}
}

View file

@ -800,9 +800,7 @@ impl<'a> Linker for GccLinker<'a> {
return;
}
let is_windows = self.sess.target.is_like_windows;
let path = tmpdir.join(if is_windows { "list.def" } else { "list" });
let path = tmpdir.join(if self.sess.target.is_like_windows { "list.def" } else { "list" });
debug!("EXPORTED SYMBOLS:");
if self.sess.target.is_like_darwin {
@ -817,7 +815,8 @@ impl<'a> Linker for GccLinker<'a> {
if let Err(error) = res {
self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error });
}
} else if is_windows {
self.link_arg("-exported_symbols_list").link_arg(path);
} else if self.sess.target.is_like_windows {
let res: io::Result<()> = try {
let mut f = File::create_buffered(&path)?;
@ -835,6 +834,21 @@ impl<'a> Linker for GccLinker<'a> {
if let Err(error) = res {
self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error });
}
self.link_arg(path);
} else if crate_type == CrateType::Executable && !self.sess.target.is_like_solaris {
let res: io::Result<()> = try {
let mut f = File::create_buffered(&path)?;
writeln!(f, "{{")?;
for (sym, _) in symbols {
debug!(sym);
writeln!(f, " {sym};")?;
}
writeln!(f, "}};")?;
};
if let Err(error) = res {
self.sess.dcx().emit_fatal(errors::VersionScriptWriteFailure { error });
}
self.link_arg("--dynamic-list").link_arg(path);
} else {
// Write an LD version script
let res: io::Result<()> = try {
@ -852,18 +866,13 @@ impl<'a> Linker for GccLinker<'a> {
if let Err(error) = res {
self.sess.dcx().emit_fatal(errors::VersionScriptWriteFailure { error });
}
}
if self.sess.target.is_like_darwin {
self.link_arg("-exported_symbols_list").link_arg(path);
} else if self.sess.target.is_like_solaris {
self.link_arg("-M").link_arg(path);
} else if is_windows {
self.link_arg(path);
} else {
let mut arg = OsString::from("--version-script=");
arg.push(path);
self.link_arg(arg).link_arg("--no-undefined-version");
if self.sess.target.is_like_solaris {
self.link_arg("-M").link_arg(path);
} else {
let mut arg = OsString::from("--version-script=");
arg.push(path);
self.link_arg(arg).link_arg("--no-undefined-version");
}
}
}

View file

@ -140,8 +140,13 @@ enum LocalRef<'tcx, V> {
Place(PlaceRef<'tcx, V>),
/// `UnsizedPlace(p)`: `p` itself is a thin pointer (indirect place).
/// `*p` is the wide pointer that references the actual unsized place.
/// Every time it is initialized, we have to reallocate the place
/// and update the wide pointer. That's the reason why it is indirect.
///
/// MIR only supports unsized args, not dynamically-sized locals, so
/// new unsized temps don't exist and we must reuse the referred-to place.
///
/// FIXME: Since the removal of unsized locals in <https://github.com/rust-lang/rust/pull/142911>,
/// can we maybe use `Place` here? Or refactor it in another way? There are quite a few
/// `UnsizedPlace => bug` branches now.
UnsizedPlace(PlaceRef<'tcx, V>),
/// The backend [`OperandValue`] has already been generated.
Operand(OperandRef<'tcx, V>),
@ -498,7 +503,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout))
}
}
// Unsized indirect qrguments
// Unsized indirect arguments
PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
// As the storage for the indirect argument lives during
// the whole function call, we just copy the wide pointer.

View file

@ -16,9 +16,9 @@ use tracing::{debug, instrument};
use super::place::{PlaceRef, PlaceValue};
use super::rvalue::transmute_scalar;
use super::{FunctionCx, LocalRef};
use crate::MemFlags;
use crate::common::IntPredicate;
use crate::traits::*;
use crate::{MemFlags, size_of_val};
/// The representation of a Rust value. The enum variant is in fact
/// uniquely determined by the value's type, but is kept as a
@ -861,44 +861,6 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
}
}
}
pub fn store_unsized<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
self,
bx: &mut Bx,
indirect_dest: PlaceRef<'tcx, V>,
) {
debug!("OperandRef::store_unsized: operand={:?}, indirect_dest={:?}", self, indirect_dest);
// `indirect_dest` must have `*mut T` type. We extract `T` out of it.
let unsized_ty = indirect_dest
.layout
.ty
.builtin_deref(true)
.unwrap_or_else(|| bug!("indirect_dest has non-pointer type: {:?}", indirect_dest));
let OperandValue::Ref(PlaceValue { llval: llptr, llextra: Some(llextra), .. }) = self
else {
bug!("store_unsized called with a sized value (or with an extern type)")
};
// Allocate an appropriate region on the stack, and copy the value into it. Since alloca
// doesn't support dynamic alignment, we allocate an extra align - 1 bytes, and align the
// pointer manually.
let (size, align) = size_of_val::size_and_align_of_dst(bx, unsized_ty, Some(llextra));
let one = bx.const_usize(1);
let align_minus_1 = bx.sub(align, one);
let size_extra = bx.add(size, align_minus_1);
let min_align = Align::ONE;
let alloca = bx.dynamic_alloca(size_extra, min_align);
let address = bx.ptrtoint(alloca, bx.type_isize());
let neg_address = bx.neg(address);
let offset = bx.and(neg_address, align_minus_1);
let dst = bx.inbounds_ptradd(alloca, offset);
bx.memcpy(dst, min_align, llptr, min_align, size, MemFlags::empty());
// Store the allocated region and the extra to the indirect place.
let indirect_operand = OperandValue::Pair(dst, llextra);
indirect_operand.store(bx, indirect_dest);
}
}
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {

View file

@ -207,9 +207,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
{
// These cases are all UB to actually hit, so don't emit code for them.
// (The size mismatches are reachable via `transmute_unchecked`.)
// We can't use unreachable because that's a terminator, and we
// need something that can be in the middle of a basic block.
bx.assume(bx.cx().const_bool(false))
bx.unreachable_nonterminator();
} else {
// Since in this path we have a place anyway, we can store or copy to it,
// making sure we use the destination place's alignment even if the
@ -236,14 +234,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|| operand.layout.is_uninhabited()
|| cast.is_uninhabited()
{
if !operand.layout.is_uninhabited() {
// Since this is known statically and the input could have existed
// without already having hit UB, might as well trap for it.
bx.abort();
}
bx.unreachable_nonterminator();
// Because this transmute is UB, return something easy to generate,
// since it's fine that later uses of the value are probably UB.
// We still need to return a value of the appropriate type, but
// it's already UB so do the easiest thing available.
return OperandValue::poison(bx, cast);
}
@ -327,27 +321,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
Some(imm)
}
pub(crate) fn codegen_rvalue_unsized(
&mut self,
bx: &mut Bx,
indirect_dest: PlaceRef<'tcx, Bx::Value>,
rvalue: &mir::Rvalue<'tcx>,
) {
debug!(
"codegen_rvalue_unsized(indirect_dest.llval={:?}, rvalue={:?})",
indirect_dest.val.llval, rvalue
);
match *rvalue {
mir::Rvalue::Use(ref operand) => {
let cg_operand = self.codegen_operand(bx, operand);
cg_operand.val.store_unsized(bx, indirect_dest);
}
_ => bug!("unsized assignment other than `Rvalue::Use`"),
}
}
pub(crate) fn codegen_rvalue_operand(
&mut self,
bx: &mut Bx,

View file

@ -15,7 +15,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
match self.locals[index] {
LocalRef::Place(cg_dest) => self.codegen_rvalue(bx, cg_dest, rvalue),
LocalRef::UnsizedPlace(cg_indirect_dest) => {
self.codegen_rvalue_unsized(bx, cg_indirect_dest, rvalue)
let ty = cg_indirect_dest.layout.ty;
span_bug!(
statement.source_info.span,
"cannot reallocate from `UnsizedPlace({ty})` \
into `{rvalue:?}`; dynamic alloca is not supported",
);
}
LocalRef::PendingOperand => {
let operand = self.codegen_rvalue_operand(bx, rvalue);

View file

@ -136,6 +136,16 @@ pub trait BuilderMethods<'a, 'tcx>:
) -> Self::Value;
fn unreachable(&mut self);
/// Like [`Self::unreachable`], but for use in the middle of a basic block.
fn unreachable_nonterminator(&mut self) {
// This is the preferred LLVM incantation for this per
// https://llvm.org/docs/Frontend/PerformanceTips.html#other-things-to-consider
// Other backends may override if they have a better way.
let const_true = self.cx().const_bool(true);
let poison_ptr = self.const_poison(self.cx().type_ptr());
self.store(const_true, poison_ptr, Align::ONE);
}
fn add(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
fn fadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
fn fadd_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
@ -224,7 +234,6 @@ pub trait BuilderMethods<'a, 'tcx>:
fn to_immediate_scalar(&mut self, val: Self::Value, scalar: Scalar) -> Self::Value;
fn alloca(&mut self, size: Size, align: Align) -> Self::Value;
fn dynamic_alloca(&mut self, size: Self::Value, align: Align) -> Self::Value;
fn load(&mut self, ty: Self::Type, ptr: Self::Value, align: Align) -> Self::Value;
fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value) -> Self::Value;
@ -555,12 +564,33 @@ pub trait BuilderMethods<'a, 'tcx>:
/// Called for `StorageDead`
fn lifetime_end(&mut self, ptr: Self::Value, size: Size);
/// "Finally codegen the call"
///
/// ## Arguments
///
/// The `fn_attrs`, `fn_abi`, and `instance` arguments are Options because they are advisory.
/// They relate to optional codegen enhancements like LLVM CFI, and do not affect ABI per se.
/// Any ABI-related transformations should be handled by different, earlier stages of codegen.
/// For instance, in the caller of `BuilderMethods::call`.
///
/// This means that a codegen backend which disregards `fn_attrs`, `fn_abi`, and `instance`
/// should still do correct codegen, and code should not be miscompiled if they are omitted.
/// It is not a miscompilation in this sense if it fails to run under CFI, other sanitizers, or
/// in the context of other compiler-enhanced security features.
///
/// The typical case that they are None is during the codegen of intrinsics and lang-items,
/// as those are "fake functions" with only a trivial ABI if any, et cetera.
///
/// ## Return
///
/// Must return the value the function will return so it can be written to the destination,
/// assuming the function does not explicitly pass the destination as a pointer in `args`.
fn call(
&mut self,
llty: Self::Type,
fn_attrs: Option<&CodegenFnAttrs>,
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
llfn: Self::Value,
fn_val: Self::Value,
args: &[Self::Value],
funclet: Option<&Self::Funclet>,
instance: Option<Instance<'tcx>>,

View file

@ -6,16 +6,22 @@ use crate::mir::operand::OperandRef;
use crate::mir::place::PlaceRef;
pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes {
/// Higher-level interface to emitting calls to intrinsics
///
/// Remember to add all intrinsics here, in `compiler/rustc_hir_analysis/src/check/mod.rs`,
/// and in `library/core/src/intrinsics.rs`; if you need access to any LLVM intrinsics,
/// add them to `compiler/rustc_codegen_llvm/src/context.rs`.
/// Returns `Err` if another instance should be called instead. This is used to invoke
/// intrinsic default bodies in case an intrinsic is not implemented by the backend.
///
/// NOTE: allowed to call [`BuilderMethods::call`]
///
/// [`BuilderMethods::call`]: super::builder::BuilderMethods::call
fn codegen_intrinsic_call(
&mut self,
instance: ty::Instance<'tcx>,
args: &[OperandRef<'tcx, Self::Value>],
result: PlaceRef<'tcx, Self::Value>,
result_dest: PlaceRef<'tcx, Self::Value>,
span: Span,
) -> Result<(), ty::Instance<'tcx>>;

View file

@ -1498,7 +1498,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
dest_alloc
.write_uninit(&tcx, dest_range)
.map_err(|e| e.to_interp_error(dest_alloc_id))?;
// We can forget about the provenance, this is all not initialized anyway.
// `write_uninit` also resets the provenance, so we are done.
return interp_ok(());
}

View file

@ -72,6 +72,7 @@ ctrlc = "3.4.4"
[features]
# tidy-alphabetical-start
check_only = ['rustc_interface/check_only']
llvm = ['rustc_interface/llvm']
max_level_info = ['rustc_log/max_level_info']
rustc_randomized_layouts = [

View file

@ -1165,7 +1165,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
self.push_suggestion(CodeSuggestion {
substitutions,
msg: self.subdiagnostic_message_to_diagnostic_message(msg),
style: SuggestionStyle::ShowCode,
style: SuggestionStyle::ShowAlways,
applicability,
});
self

View file

@ -2446,17 +2446,22 @@ impl HumanEmitter {
| DisplaySuggestion::Underline => row_num - 1,
DisplaySuggestion::None => row_num,
};
self.draw_col_separator_end(&mut buffer, row, max_line_num_len + 1);
if other_suggestions > 0 {
self.draw_col_separator_no_space(&mut buffer, row, max_line_num_len + 1);
} else {
self.draw_col_separator_end(&mut buffer, row, max_line_num_len + 1);
}
row_num = row + 1;
}
}
if other_suggestions > 0 {
self.draw_note_separator(&mut buffer, row_num, max_line_num_len + 1, false);
let msg = format!(
"and {} other candidate{}",
other_suggestions,
pluralize!(other_suggestions)
);
buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle);
buffer.append(row_num, &msg, Style::NoStyle);
}
emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;

View file

@ -34,6 +34,7 @@ use thin_vec::ThinVec;
use crate::base::ast::MetaItemInner;
use crate::errors;
use crate::expand::{self, AstFragment, Invocation};
use crate::mbe::macro_rules::ParserAnyMacro;
use crate::module::DirOwnership;
use crate::stats::MacroStat;
@ -262,6 +263,25 @@ impl<T, U> ExpandResult<T, U> {
}
}
impl<'cx> MacroExpanderResult<'cx> {
/// Creates a [`MacroExpanderResult::Ready`] from a [`TokenStream`].
///
/// The `TokenStream` is forwarded without any expansion.
pub fn from_tts(
cx: &'cx mut ExtCtxt<'_>,
tts: TokenStream,
site_span: Span,
arm_span: Span,
macro_ident: Ident,
) -> Self {
// Emit the SEMICOLON_IN_EXPRESSIONS_FROM_MACROS deprecation lint.
let is_local = true;
let parser = ParserAnyMacro::from_tts(cx, tts, site_span, arm_span, is_local, macro_ident);
ExpandResult::Ready(Box::new(parser))
}
}
pub trait MultiItemModifier {
/// `meta_item` is the attribute, and `item` is the item being modified.
fn expand(

View file

@ -96,6 +96,30 @@ impl<'a> ParserAnyMacro<'a> {
ensure_complete_parse(parser, &path, kind.name(), site_span);
fragment
}
#[instrument(skip(cx, tts))]
pub(crate) fn from_tts<'cx>(
cx: &'cx mut ExtCtxt<'a>,
tts: TokenStream,
site_span: Span,
arm_span: Span,
is_local: bool,
macro_ident: Ident,
) -> Self {
Self {
parser: Parser::new(&cx.sess.psess, tts, None),
// Pass along the original expansion site and the name of the macro
// so we can print a useful error message if the parse of the expanded
// macro leaves unparsed tokens.
site_span,
macro_ident,
lint_node_id: cx.current_expansion.lint_node_id,
is_trailing_mac: cx.current_expansion.is_trailing_mac,
arm_span,
is_local,
}
}
}
pub(super) struct MacroRule {
@ -207,9 +231,6 @@ fn expand_macro<'cx>(
rules: &[MacroRule],
) -> Box<dyn MacResult + 'cx> {
let psess = &cx.sess.psess;
// Macros defined in the current crate have a real node id,
// whereas macros from an external crate have a dummy id.
let is_local = node_id != DUMMY_NODE_ID;
if cx.trace_macros() {
let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(&arg));
@ -220,7 +241,7 @@ fn expand_macro<'cx>(
let try_success_result = try_match_macro(psess, name, &arg, rules, &mut NoopTracker);
match try_success_result {
Ok((i, rule, named_matches)) => {
Ok((rule_index, rule, named_matches)) => {
let mbe::TokenTree::Delimited(rhs_span, _, ref rhs) = rule.rhs else {
cx.dcx().span_bug(sp, "malformed macro rhs");
};
@ -241,27 +262,13 @@ fn expand_macro<'cx>(
trace_macros_note(&mut cx.expansions, sp, msg);
}
let p = Parser::new(psess, tts, None);
let is_local = is_defined_in_current_crate(node_id);
if is_local {
cx.resolver.record_macro_rule_usage(node_id, i);
cx.resolver.record_macro_rule_usage(node_id, rule_index);
}
// Let the context choose how to interpret the result.
// Weird, but useful for X-macros.
Box::new(ParserAnyMacro {
parser: p,
// Pass along the original expansion site and the name of the macro
// so we can print a useful error message if the parse of the expanded
// macro leaves unparsed tokens.
site_span: sp,
macro_ident: name,
lint_node_id: cx.current_expansion.lint_node_id,
is_trailing_mac: cx.current_expansion.is_trailing_mac,
arm_span,
is_local,
})
// Let the context choose how to interpret the result. Weird, but useful for X-macros.
Box::new(ParserAnyMacro::from_tts(cx, tts, sp, arm_span, is_local, name))
}
Err(CanRetry::No(guar)) => {
debug!("Will not retry matching as an error was emitted already");
@ -373,9 +380,9 @@ pub fn compile_declarative_macro(
node_id: NodeId,
edition: Edition,
) -> (SyntaxExtension, usize) {
let is_local = node_id != DUMMY_NODE_ID;
let mk_syn_ext = |expander| {
let kind = SyntaxExtensionKind::LegacyBang(expander);
let is_local = is_defined_in_current_crate(node_id);
SyntaxExtension::new(sess, kind, span, Vec::new(), edition, ident.name, attrs, is_local)
};
let dummy_syn_ext = |guar| (mk_syn_ext(Arc::new(DummyExpander(guar))), 0);
@ -439,7 +446,7 @@ pub fn compile_declarative_macro(
}
// Return the number of rules for unused rule linting, if this is a local macro.
let nrules = if is_local { rules.len() } else { 0 };
let nrules = if is_defined_in_current_crate(node_id) { rules.len() } else { 0 };
let expander =
Arc::new(MacroRulesMacroExpander { name: ident, span, node_id, transparency, rules });
@ -1034,9 +1041,7 @@ fn check_matcher_core<'tt>(
// definition of this macro_rules, not while (re)parsing
// the macro when compiling another crate that is using the
// macro. (See #86567.)
// Macros defined in the current crate have a real node id,
// whereas macros from an external crate have a dummy id.
if node_id != DUMMY_NODE_ID
if is_defined_in_current_crate(node_id)
&& matches!(kind, NonterminalKind::Pat(PatParam { inferred: true }))
&& matches!(
next_token,
@ -1296,6 +1301,12 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
}
}
fn is_defined_in_current_crate(node_id: NodeId) -> bool {
// Macros defined in the current crate have a real node id,
// whereas macros from an external crate have a dummy id.
node_id != DUMMY_NODE_ID
}
pub(super) fn parser_from_cx(
psess: &ParseSess,
mut tts: TokenStream,

View file

@ -3172,6 +3172,8 @@ pub struct ImplItem<'hir> {
pub span: Span,
pub vis_span: Span,
pub has_delayed_lints: bool,
/// When we are in a trait impl, link to the trait-item's id.
pub trait_item_def_id: Option<DefId>,
}
impl<'hir> ImplItem<'hir> {
@ -4136,7 +4138,7 @@ impl<'hir> Item<'hir> {
expect_mod, (Ident, &'hir Mod<'hir>), ItemKind::Mod(ident, m), (*ident, m);
expect_foreign_mod, (ExternAbi, &'hir [ForeignItemRef]),
expect_foreign_mod, (ExternAbi, &'hir [ForeignItemId]),
ItemKind::ForeignMod { abi, items }, (*abi, items);
expect_global_asm, &'hir InlineAsm<'hir>, ItemKind::GlobalAsm { asm, .. }, asm;
@ -4160,7 +4162,7 @@ impl<'hir> Item<'hir> {
Ident,
&'hir Generics<'hir>,
GenericBounds<'hir>,
&'hir [TraitItemRef]
&'hir [TraitItemId]
),
ItemKind::Trait(is_auto, safety, ident, generics, bounds, items),
(*is_auto, *safety, *ident, generics, bounds, items);
@ -4313,7 +4315,7 @@ pub enum ItemKind<'hir> {
/// A module.
Mod(Ident, &'hir Mod<'hir>),
/// An external module, e.g. `extern { .. }`.
ForeignMod { abi: ExternAbi, items: &'hir [ForeignItemRef] },
ForeignMod { abi: ExternAbi, items: &'hir [ForeignItemId] },
/// Module-level inline assembly (from `global_asm!`).
GlobalAsm {
asm: &'hir InlineAsm<'hir>,
@ -4333,7 +4335,7 @@ pub enum ItemKind<'hir> {
/// A union definition, e.g., `union Foo<A, B> {x: A, y: B}`.
Union(Ident, &'hir Generics<'hir>, VariantData<'hir>),
/// A trait definition.
Trait(IsAuto, Safety, Ident, &'hir Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemRef]),
Trait(IsAuto, Safety, Ident, &'hir Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemId]),
/// A trait alias.
TraitAlias(Ident, &'hir Generics<'hir>, GenericBounds<'hir>),
@ -4360,7 +4362,7 @@ pub struct Impl<'hir> {
pub of_trait: Option<TraitRef<'hir>>,
pub self_ty: &'hir Ty<'hir>,
pub items: &'hir [ImplItemRef],
pub items: &'hir [ImplItemId],
}
impl ItemKind<'_> {
@ -4403,43 +4405,6 @@ impl ItemKind<'_> {
}
}
/// A reference from an trait to one of its associated items. This
/// contains the item's id, naturally, but also the item's name and
/// some other high-level details (like whether it is an associated
/// type or method, and whether it is public). This allows other
/// passes to find the impl they want without loading the ID (which
/// means fewer edges in the incremental compilation graph).
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct TraitItemRef {
pub id: TraitItemId,
pub ident: Ident,
pub kind: AssocItemKind,
pub span: Span,
}
/// A reference from an impl to one of its associated items. This
/// contains the item's ID, naturally, but also the item's name and
/// some other high-level details (like whether it is an associated
/// type or method, and whether it is public). This allows other
/// passes to find the impl they want without loading the ID (which
/// means fewer edges in the incremental compilation graph).
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct ImplItemRef {
pub id: ImplItemId,
pub ident: Ident,
pub kind: AssocItemKind,
pub span: Span,
/// When we are in a trait impl, link to the trait-item's id.
pub trait_item_def_id: Option<DefId>,
}
#[derive(Copy, Clone, PartialEq, Debug, HashStable_Generic)]
pub enum AssocItemKind {
Const,
Fn { has_self: bool },
Type,
}
// The bodies for items are stored "out of line", in a separate
// hashmap in the `Crate`. Here we just record the hir-id of the item
// so it can fetched later.
@ -4456,19 +4421,6 @@ impl ForeignItemId {
}
}
/// A reference from a foreign block to one of its items. This
/// contains the item's ID, naturally, but also the item's name and
/// some other high-level details (like whether it is an associated
/// type or method, and whether it is public). This allows other
/// passes to find the impl they want without loading the ID (which
/// means fewer edges in the incremental compilation graph).
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct ForeignItemRef {
pub id: ForeignItemId,
pub ident: Ident,
pub span: Span,
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct ForeignItem<'hir> {
pub ident: Ident,
@ -4969,7 +4921,7 @@ mod size_asserts {
static_assert_size!(GenericBound<'_>, 64);
static_assert_size!(Generics<'_>, 56);
static_assert_size!(Impl<'_>, 80);
static_assert_size!(ImplItem<'_>, 88);
static_assert_size!(ImplItem<'_>, 96);
static_assert_size!(ImplItemKind<'_>, 40);
static_assert_size!(Item<'_>, 88);
static_assert_size!(ItemKind<'_>, 64);

View file

@ -435,17 +435,17 @@ pub trait Visitor<'v>: Sized {
fn visit_trait_item(&mut self, ti: &'v TraitItem<'v>) -> Self::Result {
walk_trait_item(self, ti)
}
fn visit_trait_item_ref(&mut self, ii: &'v TraitItemRef) -> Self::Result {
walk_trait_item_ref(self, ii)
fn visit_trait_item_ref(&mut self, ii: &'v TraitItemId) -> Self::Result {
walk_trait_item_ref(self, *ii)
}
fn visit_impl_item(&mut self, ii: &'v ImplItem<'v>) -> Self::Result {
walk_impl_item(self, ii)
}
fn visit_foreign_item_ref(&mut self, ii: &'v ForeignItemRef) -> Self::Result {
walk_foreign_item_ref(self, ii)
fn visit_foreign_item_ref(&mut self, ii: &'v ForeignItemId) -> Self::Result {
walk_foreign_item_ref(self, *ii)
}
fn visit_impl_item_ref(&mut self, ii: &'v ImplItemRef) -> Self::Result {
walk_impl_item_ref(self, ii)
fn visit_impl_item_ref(&mut self, ii: &'v ImplItemId) -> Self::Result {
walk_impl_item_ref(self, *ii)
}
fn visit_trait_ref(&mut self, t: &'v TraitRef<'v>) -> Self::Result {
walk_trait_ref(self, t)
@ -499,9 +499,6 @@ pub trait Visitor<'v>: Sized {
fn visit_attribute(&mut self, _attr: &'v Attribute) -> Self::Result {
Self::Result::output()
}
fn visit_associated_item_kind(&mut self, kind: &'v AssocItemKind) -> Self::Result {
walk_associated_item_kind(self, kind)
}
fn visit_defaultness(&mut self, defaultness: &'v Defaultness) -> Self::Result {
walk_defaultness(self, defaultness)
}
@ -1248,14 +1245,8 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(
V::Result::output()
}
pub fn walk_trait_item_ref<'v, V: Visitor<'v>>(
visitor: &mut V,
trait_item_ref: &'v TraitItemRef,
) -> V::Result {
let TraitItemRef { id, ident, ref kind, span: _ } = *trait_item_ref;
try_visit!(visitor.visit_nested_trait_item(id));
try_visit!(visitor.visit_ident(ident));
visitor.visit_associated_item_kind(kind)
pub fn walk_trait_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, id: TraitItemId) -> V::Result {
visitor.visit_nested_trait_item(id)
}
pub fn walk_impl_item<'v, V: Visitor<'v>>(
@ -1271,6 +1262,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(
span: _,
vis_span: _,
has_delayed_lints: _,
trait_item_def_id: _,
} = *impl_item;
try_visit!(visitor.visit_ident(ident));
@ -1293,23 +1285,12 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(
}
}
pub fn walk_foreign_item_ref<'v, V: Visitor<'v>>(
visitor: &mut V,
foreign_item_ref: &'v ForeignItemRef,
) -> V::Result {
let ForeignItemRef { id, ident, span: _ } = *foreign_item_ref;
try_visit!(visitor.visit_nested_foreign_item(id));
visitor.visit_ident(ident)
pub fn walk_foreign_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, id: ForeignItemId) -> V::Result {
visitor.visit_nested_foreign_item(id)
}
pub fn walk_impl_item_ref<'v, V: Visitor<'v>>(
visitor: &mut V,
impl_item_ref: &'v ImplItemRef,
) -> V::Result {
let ImplItemRef { id, ident, ref kind, span: _, trait_item_def_id: _ } = *impl_item_ref;
try_visit!(visitor.visit_nested_impl_item(id));
try_visit!(visitor.visit_ident(ident));
visitor.visit_associated_item_kind(kind)
pub fn walk_impl_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, id: ImplItemId) -> V::Result {
visitor.visit_nested_impl_item(id)
}
pub fn walk_trait_ref<'v, V: Visitor<'v>>(
@ -1483,13 +1464,6 @@ pub fn walk_assoc_item_constraint<'v, V: Visitor<'v>>(
V::Result::output()
}
pub fn walk_associated_item_kind<'v, V: Visitor<'v>>(_: &mut V, _: &'v AssocItemKind) -> V::Result {
// No visitable content here: this fn exists so you can call it if
// the right thing to do, should content be added in the future,
// would be to walk it.
V::Result::output()
}
pub fn walk_defaultness<'v, V: Visitor<'v>>(_: &mut V, _: &'v Defaultness) -> V::Result {
// No visitable content here: this fn exists so you can call it if
// the right thing to do, should content be added in the future,

View file

@ -930,8 +930,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
check_abi(tcx, it.hir_id(), it.span, abi);
for item in items {
let def_id = item.id.owner_id.def_id;
for &item in items {
let def_id = item.owner_id.def_id;
let generics = tcx.generics_of(def_id);
let own_counts = generics.own_counts();
@ -943,13 +943,14 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
(0, _) => ("const", "consts", None),
_ => ("type or const", "types or consts", None),
};
let span = tcx.def_span(def_id);
struct_span_code_err!(
tcx.dcx(),
item.span,
span,
E0044,
"foreign items may not have {kinds} parameters",
)
.with_span_label(item.span, format!("can't have {kinds} parameters"))
.with_span_label(span, format!("can't have {kinds} parameters"))
.with_help(
// FIXME: once we start storing spans for type arguments, turn this
// into a suggestion.
@ -963,22 +964,23 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
.emit();
}
let item = tcx.hir_foreign_item(item.id);
tcx.ensure_ok().generics_of(item.owner_id);
tcx.ensure_ok().type_of(item.owner_id);
tcx.ensure_ok().predicates_of(item.owner_id);
tcx.ensure_ok().generics_of(def_id);
tcx.ensure_ok().type_of(def_id);
tcx.ensure_ok().predicates_of(def_id);
if tcx.is_conditionally_const(def_id) {
tcx.ensure_ok().explicit_implied_const_bounds(def_id);
tcx.ensure_ok().const_conditions(def_id);
}
match item.kind {
hir::ForeignItemKind::Fn(sig, ..) => {
tcx.ensure_ok().codegen_fn_attrs(item.owner_id);
tcx.ensure_ok().fn_sig(item.owner_id);
match tcx.def_kind(def_id) {
DefKind::Fn => {
tcx.ensure_ok().codegen_fn_attrs(def_id);
tcx.ensure_ok().fn_sig(def_id);
let item = tcx.hir_foreign_item(item);
let hir::ForeignItemKind::Fn(sig, ..) = item.kind else { bug!() };
require_c_abi_if_c_variadic(tcx, sig.decl, abi, item.span);
}
hir::ForeignItemKind::Static(..) => {
tcx.ensure_ok().codegen_fn_attrs(item.owner_id);
DefKind::Static { .. } => {
tcx.ensure_ok().codegen_fn_attrs(def_id);
}
_ => (),
}

View file

@ -59,7 +59,7 @@ fn equate_intrinsic_type<'tcx>(
/// Returns the unsafety of the given intrinsic.
fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hir::Safety {
let is_in_list = match tcx.item_name(intrinsic_id.into()) {
let is_in_list = match tcx.item_name(intrinsic_id) {
// When adding a new intrinsic to this list,
// it's usually worth updating that intrinsic's documentation
// to note that it's safe to call, since
@ -144,7 +144,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
tcx.def_span(intrinsic_id),
DiagMessage::from(format!(
"intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `{}`",
tcx.item_name(intrinsic_id.into())
tcx.item_name(intrinsic_id)
)
)).emit();
}

View file

@ -167,15 +167,31 @@ fn resolve_block<'tcx>(
visitor.cx = prev_cx;
}
fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) {
fn has_let_expr(expr: &Expr<'_>) -> bool {
match &expr.kind {
hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs),
hir::ExprKind::Let(..) => true,
_ => false,
}
}
/// Resolve a condition from an `if` expression or match guard so that it is a terminating scope
/// if it doesn't contain `let` expressions.
fn resolve_cond<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, cond: &'tcx hir::Expr<'tcx>) {
let terminate = match cond.kind {
// Temporaries for `let` expressions must live into the success branch.
hir::ExprKind::Let(_) => false,
// Logical operator chains are handled in `resolve_expr`. Since logical operator chains in
// conditions are lowered to control-flow rather than boolean temporaries, there's no
// temporary to drop for logical operators themselves. `resolve_expr` will also recursively
// wrap any operands in terminating scopes, other than `let` expressions (which we shouldn't
// terminate) and other logical operators (which don't need a terminating scope, since their
// operands will be terminated). Any temporaries that would need to be dropped will be
// dropped before we leave this operator's scope; terminating them here would be redundant.
hir::ExprKind::Binary(
source_map::Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
_,
_,
) => false,
// Otherwise, conditions should always drop their temporaries.
_ => true,
};
resolve_expr(visitor, cond, terminate);
}
fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) {
let prev_cx = visitor.cx;
visitor.enter_node_scope_with_dtor(arm.hir_id.local_id, true);
@ -183,7 +199,7 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir:
resolve_pat(visitor, arm.pat);
if let Some(guard) = arm.guard {
resolve_expr(visitor, guard, !has_let_expr(guard));
resolve_cond(visitor, guard);
}
resolve_expr(visitor, arm.body, false);
@ -340,7 +356,7 @@ fn resolve_expr<'tcx>(
};
visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data });
visitor.cx.var_parent = visitor.cx.parent;
visitor.visit_expr(cond);
resolve_cond(visitor, cond);
resolve_expr(visitor, then, true);
visitor.cx = expr_cx;
resolve_expr(visitor, otherwise, true);
@ -355,7 +371,7 @@ fn resolve_expr<'tcx>(
};
visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data });
visitor.cx.var_parent = visitor.cx.parent;
visitor.visit_expr(cond);
resolve_cond(visitor, cond);
resolve_expr(visitor, then, true);
visitor.cx = expr_cx;
}

View file

@ -326,7 +326,9 @@ pub(crate) fn check_trait_item<'tcx>(
let mut res = Ok(());
if matches!(tcx.def_kind(def_id), DefKind::AssocFn) {
for &assoc_ty_def_id in tcx.associated_types_for_impl_traits_in_associated_fn(def_id) {
for &assoc_ty_def_id in
tcx.associated_types_for_impl_traits_in_associated_fn(def_id.to_def_id())
{
res = res.and(check_associated_item(tcx, assoc_ty_def_id.expect_local()));
}
}

View file

@ -7,6 +7,7 @@
//! `tcx.inherent_impls(def_id)`). That value, however,
//! is computed by selecting an idea from this table.
use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
@ -85,7 +86,10 @@ impl<'tcx> InherentCollect<'tcx> {
}
for &impl_item in items {
if !self.tcx.has_attr(impl_item, sym::rustc_allow_incoherent_impl) {
if !find_attr!(
self.tcx.get_all_attrs(impl_item),
AttributeKind::AllowIncoherentImpl(_)
) {
let impl_span = self.tcx.def_span(impl_def_id);
return Err(self.tcx.dcx().emit_err(errors::InherentTyOutsideRelevant {
span: impl_span,
@ -116,7 +120,10 @@ impl<'tcx> InherentCollect<'tcx> {
if !self.tcx.hir_rustc_coherence_is_core() {
if self.tcx.features().rustc_attrs() {
for &impl_item in items {
if !self.tcx.has_attr(impl_item, sym::rustc_allow_incoherent_impl) {
if !find_attr!(
self.tcx.get_all_attrs(impl_item),
AttributeKind::AllowIncoherentImpl(_)
) {
let span = self.tcx.def_span(impl_def_id);
return Err(self.tcx.dcx().emit_err(errors::InherentTyOutsidePrimitive {
span,

View file

@ -844,47 +844,47 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
let item = tcx.hir_expect_item(def_id);
let (is_alias, is_auto, safety, items) = match item.kind {
hir::ItemKind::Trait(is_auto, safety, .., items) => {
(false, is_auto == hir::IsAuto::Yes, safety, items)
}
hir::ItemKind::TraitAlias(..) => (true, false, hir::Safety::Safe, &[][..]),
let (is_alias, is_auto, safety) = match item.kind {
hir::ItemKind::Trait(is_auto, safety, ..) => (false, is_auto == hir::IsAuto::Yes, safety),
hir::ItemKind::TraitAlias(..) => (true, false, hir::Safety::Safe),
_ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"),
};
let attrs = tcx.get_all_attrs(def_id);
// Only regular traits can be const.
let constness = if !is_alias && tcx.has_attr(def_id, sym::const_trait) {
let constness = if !is_alias && find_attr!(attrs, AttributeKind::ConstTrait(_)) {
hir::Constness::Const
} else {
hir::Constness::NotConst
};
let paren_sugar = tcx.has_attr(def_id, sym::rustc_paren_sugar);
let paren_sugar = find_attr!(attrs, AttributeKind::ParenSugar(_));
if paren_sugar && !tcx.features().unboxed_closures() {
tcx.dcx().emit_err(errors::ParenSugarAttribute { span: item.span });
}
// Only regular traits can be marker.
let is_marker = !is_alias && tcx.has_attr(def_id, sym::marker);
let is_marker = !is_alias && find_attr!(attrs, AttributeKind::Marker(_));
let rustc_coinductive = tcx.has_attr(def_id, sym::rustc_coinductive);
let is_fundamental = tcx.has_attr(def_id, sym::fundamental);
let rustc_coinductive = find_attr!(attrs, AttributeKind::Coinductive(_));
let is_fundamental = find_attr!(attrs, AttributeKind::Fundamental);
let [skip_array_during_method_dispatch, skip_boxed_slice_during_method_dispatch] = find_attr!(
tcx.get_all_attrs(def_id),
AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span:_ } => [*array, *boxed_slice]
attrs,
AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span: _ } => [*array, *boxed_slice]
)
.unwrap_or([false; 2]);
let specialization_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) {
let specialization_kind = if find_attr!(attrs, AttributeKind::UnsafeSpecializationMarker(_)) {
ty::trait_def::TraitSpecializationKind::Marker
} else if tcx.has_attr(def_id, sym::rustc_specialization_trait) {
} else if find_attr!(attrs, AttributeKind::SpecializationTrait(_)) {
ty::trait_def::TraitSpecializationKind::AlwaysApplicable
} else {
ty::trait_def::TraitSpecializationKind::None
};
let must_implement_one_of = tcx
.get_attr(def_id, sym::rustc_must_implement_one_of)
let must_implement_one_of = attrs
.iter()
.find(|attr| attr.has_name(sym::rustc_must_implement_one_of))
// Check that there are at least 2 arguments of `#[rustc_must_implement_one_of]`
// and that they are all identifiers
.and_then(|attr| match attr.meta_item_list() {
@ -909,13 +909,16 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
// functions in the trait with default implementations
.and_then(|(list, attr_span)| {
let errors = list.iter().filter_map(|ident| {
let item = items.iter().find(|item| item.ident == *ident);
let item = tcx
.associated_items(def_id)
.filter_by_name_unhygienic(ident.name)
.find(|item| item.ident(tcx) == *ident);
match item {
Some(item) if matches!(item.kind, hir::AssocItemKind::Fn { .. }) => {
if !tcx.defaultness(item.id.owner_id).has_value() {
Some(item) if matches!(item.kind, ty::AssocKind::Fn { .. }) => {
if !item.defaultness(tcx).has_value() {
tcx.dcx().emit_err(errors::FunctionNotHaveDefaultImplementation {
span: item.span,
span: tcx.def_span(item.def_id),
note_span: attr_span,
});
@ -926,7 +929,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
}
Some(item) => {
tcx.dcx().emit_err(errors::MustImplementNotFunction {
span: item.span,
span: tcx.def_span(item.def_id),
span_note: errors::MustImplementNotFunctionSpanNote { span: attr_span },
note: errors::MustImplementNotFunctionNote {},
});
@ -958,8 +961,8 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
no_dups.then_some(list)
});
let deny_explicit_impl = tcx.has_attr(def_id, sym::rustc_deny_explicit_impl);
let implement_via_object = !tcx.has_attr(def_id, sym::rustc_do_not_implement_via_object);
let deny_explicit_impl = find_attr!(attrs, AttributeKind::DenyExplicitImpl(_));
let implement_via_object = !find_attr!(attrs, AttributeKind::DoNotImplementViaObject(_));
ty::TraitDef {
def_id: def_id.to_def_id(),

View file

@ -2602,7 +2602,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// do a linear search to map this to the synthetic associated type that
// it will be lowered to.
let def_id = if let Some(parent_def_id) = in_trait {
*tcx.associated_types_for_impl_traits_in_associated_fn(parent_def_id)
*tcx.associated_types_for_impl_traits_in_associated_fn(parent_def_id.to_def_id())
.iter()
.find(|rpitit| match tcx.opt_rpitit_info(**rpitit) {
Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => {

View file

@ -654,8 +654,8 @@ impl<'a> State<'a> {
let (cb, ib) = self.head("extern");
self.word_nbsp(abi.to_string());
self.bopen(ib);
for item in items {
self.ann.nested(self, Nested::ForeignItem(item.id));
for &foreign_item in items {
self.ann.nested(self, Nested::ForeignItem(foreign_item));
}
self.bclose(item.span, cb);
}
@ -730,8 +730,8 @@ impl<'a> State<'a> {
self.space();
self.bopen(ib);
for impl_item in items {
self.ann.nested(self, Nested::ImplItem(impl_item.id));
for &impl_item in items {
self.ann.nested(self, Nested::ImplItem(impl_item));
}
self.bclose(item.span, cb);
}
@ -746,8 +746,8 @@ impl<'a> State<'a> {
self.print_where_clause(generics);
self.word(" ");
self.bopen(ib);
for trait_item in trait_items {
self.ann.nested(self, Nested::TraitItem(trait_item.id));
for &trait_item in trait_items {
self.ann.nested(self, Nested::TraitItem(trait_item));
}
self.bclose(item.span, cb);
}

View file

@ -51,12 +51,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
match span.desugaring_kind() {
// If span arose from a desugaring of `if` or `while`, then it is the condition
// itself, which diverges, that we are about to lint on. This gives suboptimal
// diagnostics. Instead, stop here so that the `if`- or `while`-expression's
// block is linted instead.
Some(DesugaringKind::CondTemporary) => return,
// Don't lint if the result of an async block or async function is `!`.
// This does not affect the unreachable lints *within* the body.
Some(DesugaringKind::Async) => return,

View file

@ -241,6 +241,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
arg_expr.span,
ObligationCauseCode::WellFormed(None),
);
self.check_place_expr_if_unsized(fn_input_ty, arg_expr);
}
// First, let's unify the formal method signature with the expectation eagerly.
@ -543,6 +545,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
/// If `unsized_fn_params` is active, check that unsized values are place expressions. Since
/// the removal of `unsized_locals` in <https://github.com/rust-lang/rust/pull/142911> we can't
/// store them in MIR locals as temporaries.
///
/// If `unsized_fn_params` is inactive, this will be checked in borrowck instead.
fn check_place_expr_if_unsized(&self, ty: Ty<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
if self.tcx.features().unsized_fn_params() && !expr.is_syntactic_place_expr() {
self.require_type_is_sized(
ty,
expr.span,
ObligationCauseCode::UnsizedNonPlaceExpr(expr.span),
);
}
}
fn report_arg_errors(
&self,
compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>,
@ -1873,7 +1890,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
}
hir::StmtKind::Semi(expr) => {
self.check_expr(expr);
let ty = self.check_expr(expr);
self.check_place_expr_if_unsized(ty, expr);
}
}

View file

@ -428,11 +428,9 @@ fn report_unexpected_variant_res(
}
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(..), hir_id, .. }) => {
suggestion.push((expr.span.shrink_to_lo(), "(".to_string()));
if let hir::Node::Expr(drop_temps) = tcx.parent_hir_node(*hir_id)
&& let hir::ExprKind::DropTemps(_) = drop_temps.kind
&& let hir::Node::Expr(parent) = tcx.parent_hir_node(drop_temps.hir_id)
if let hir::Node::Expr(parent) = tcx.parent_hir_node(*hir_id)
&& let hir::ExprKind::If(condition, block, None) = parent.kind
&& condition.hir_id == drop_temps.hir_id
&& condition.hir_id == *hir_id
&& let hir::ExprKind::Block(block, _) = block.kind
&& block.stmts.is_empty()
&& let Some(expr) = block.expr

View file

@ -24,7 +24,6 @@ use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_session::parse::feature_err;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::edition::Edition;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::source_map::Spanned;
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, kw, sym};
use rustc_trait_selection::infer::InferCtxtExt;
@ -902,16 +901,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// then that's equivalent to there existing a LUB.
let cause = self.pattern_cause(ti, span);
if let Err(err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) {
err.emit_unless(
ti.span
.filter(|&s| {
// In the case of `if`- and `while`-expressions we've already checked
// that `scrutinee: bool`. We know that the pattern is `true`,
// so an error here would be a duplicate and from the wrong POV.
s.is_desugaring(DesugaringKind::CondTemporary)
})
.is_some(),
);
err.emit();
}
pat_ty

View file

@ -56,5 +56,6 @@ rustc_abi = { path = "../rustc_abi" }
[features]
# tidy-alphabetical-start
check_only = ['rustc_codegen_llvm?/check_only']
llvm = ['dep:rustc_codegen_llvm']
# tidy-alphabetical-end

View file

@ -273,14 +273,15 @@ pub fn strip_shebang(input: &str) -> Option<usize> {
if let Some(input_tail) = input.strip_prefix("#!") {
// Ok, this is a shebang but if the next non-whitespace token is `[`,
// then it may be valid Rust code, so consider it Rust code.
let next_non_whitespace_token = tokenize(input_tail).map(|tok| tok.kind).find(|tok| {
!matches!(
tok,
TokenKind::Whitespace
| TokenKind::LineComment { doc_style: None }
| TokenKind::BlockComment { doc_style: None, .. }
)
});
let next_non_whitespace_token =
tokenize(input_tail, FrontmatterAllowed::No).map(|tok| tok.kind).find(|tok| {
!matches!(
tok,
TokenKind::Whitespace
| TokenKind::LineComment { doc_style: None }
| TokenKind::BlockComment { doc_style: None, .. }
)
});
if next_non_whitespace_token != Some(TokenKind::OpenBracket) {
// No other choice than to consider this a shebang.
return Some(2 + input_tail.lines().next().unwrap_or_default().len());
@ -303,8 +304,16 @@ pub fn validate_raw_str(input: &str, prefix_len: u32) -> Result<(), RawStrError>
}
/// Creates an iterator that produces tokens from the input string.
pub fn tokenize(input: &str) -> impl Iterator<Item = Token> {
let mut cursor = Cursor::new(input, FrontmatterAllowed::No);
///
/// When parsing a full Rust document,
/// first [`strip_shebang`] and then allow frontmatters with [`FrontmatterAllowed::Yes`].
///
/// When tokenizing a slice of a document, be sure to disallow frontmatters with [`FrontmatterAllowed::No`]
pub fn tokenize(
input: &str,
frontmatter_allowed: FrontmatterAllowed,
) -> impl Iterator<Item = Token> {
let mut cursor = Cursor::new(input, frontmatter_allowed);
std::iter::from_fn(move || {
let token = cursor.advance_token();
if token.kind != TokenKind::Eof { Some(token) } else { None }

View file

@ -124,8 +124,9 @@ fn test_valid_shebang() {
assert_eq!(strip_shebang(input), None);
}
fn check_lexing(src: &str, expect: Expect) {
let actual: String = tokenize(src).map(|token| format!("{:?}\n", token)).collect();
fn check_lexing(src: &str, frontmatter_allowed: FrontmatterAllowed, expect: Expect) {
let actual: String =
tokenize(src, frontmatter_allowed).map(|token| format!("{:?}\n", token)).collect();
expect.assert_eq(&actual)
}
@ -133,6 +134,7 @@ fn check_lexing(src: &str, expect: Expect) {
fn smoke_test() {
check_lexing(
"/* my source file */ fn main() { println!(\"zebra\"); }\n",
FrontmatterAllowed::No,
expect![[r#"
Token { kind: BlockComment { doc_style: None, terminated: true }, len: 20 }
Token { kind: Whitespace, len: 1 }
@ -171,6 +173,7 @@ fn comment_flavors() {
/** outer doc block */
/*! inner doc block */
",
FrontmatterAllowed::No,
expect![[r#"
Token { kind: Whitespace, len: 1 }
Token { kind: LineComment { doc_style: None }, len: 7 }
@ -199,6 +202,7 @@ fn comment_flavors() {
fn nested_block_comments() {
check_lexing(
"/* /* */ */'a'",
FrontmatterAllowed::No,
expect![[r#"
Token { kind: BlockComment { doc_style: None, terminated: true }, len: 11 }
Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 }
@ -210,6 +214,7 @@ fn nested_block_comments() {
fn characters() {
check_lexing(
"'a' ' ' '\\n'",
FrontmatterAllowed::No,
expect![[r#"
Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 }
Token { kind: Whitespace, len: 1 }
@ -224,6 +229,7 @@ fn characters() {
fn lifetime() {
check_lexing(
"'abc",
FrontmatterAllowed::No,
expect![[r#"
Token { kind: Lifetime { starts_with_number: false }, len: 4 }
"#]],
@ -234,6 +240,7 @@ fn lifetime() {
fn raw_string() {
check_lexing(
"r###\"\"#a\\b\x00c\"\"###",
FrontmatterAllowed::No,
expect![[r#"
Token { kind: Literal { kind: RawStr { n_hashes: Some(3) }, suffix_start: 17 }, len: 17 }
"#]],
@ -257,6 +264,7 @@ b"a"
r###"raw"###suffix
br###"raw"###suffix
"####,
FrontmatterAllowed::No,
expect![[r#"
Token { kind: Whitespace, len: 1 }
Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 }
@ -286,3 +294,78 @@ br###"raw"###suffix
"#]],
)
}
#[test]
fn frontmatter_allowed() {
check_lexing(
r#"
---cargo
[dependencies]
clap = "4"
---
fn main() {}
"#,
FrontmatterAllowed::Yes,
expect![[r#"
Token { kind: Whitespace, len: 1 }
Token { kind: Frontmatter { has_invalid_preceding_whitespace: false, invalid_infostring: false }, len: 38 }
Token { kind: Whitespace, len: 2 }
Token { kind: Ident, len: 2 }
Token { kind: Whitespace, len: 1 }
Token { kind: Ident, len: 4 }
Token { kind: OpenParen, len: 1 }
Token { kind: CloseParen, len: 1 }
Token { kind: Whitespace, len: 1 }
Token { kind: OpenBrace, len: 1 }
Token { kind: CloseBrace, len: 1 }
Token { kind: Whitespace, len: 1 }
"#]],
)
}
#[test]
fn frontmatter_disallowed() {
check_lexing(
r#"
---cargo
[dependencies]
clap = "4"
---
fn main() {}
"#,
FrontmatterAllowed::No,
expect![[r#"
Token { kind: Whitespace, len: 1 }
Token { kind: Minus, len: 1 }
Token { kind: Minus, len: 1 }
Token { kind: Minus, len: 1 }
Token { kind: Ident, len: 5 }
Token { kind: Whitespace, len: 1 }
Token { kind: OpenBracket, len: 1 }
Token { kind: Ident, len: 12 }
Token { kind: CloseBracket, len: 1 }
Token { kind: Whitespace, len: 1 }
Token { kind: Ident, len: 4 }
Token { kind: Whitespace, len: 1 }
Token { kind: Eq, len: 1 }
Token { kind: Whitespace, len: 1 }
Token { kind: Literal { kind: Str { terminated: true }, suffix_start: 3 }, len: 3 }
Token { kind: Whitespace, len: 1 }
Token { kind: Minus, len: 1 }
Token { kind: Minus, len: 1 }
Token { kind: Minus, len: 1 }
Token { kind: Whitespace, len: 2 }
Token { kind: Ident, len: 2 }
Token { kind: Whitespace, len: 1 }
Token { kind: Ident, len: 4 }
Token { kind: OpenParen, len: 1 }
Token { kind: CloseParen, len: 1 }
Token { kind: Whitespace, len: 1 }
Token { kind: OpenBrace, len: 1 }
Token { kind: CloseBrace, len: 1 }
Token { kind: Whitespace, len: 1 }
"#]],
)
}

View file

@ -28,7 +28,7 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
use rustc_hir::intravisit::FnKind as HirFnKind;
use rustc_hir::{Body, FnDecl, GenericParamKind, PatKind, PredicateOrigin};
use rustc_hir::{Body, FnDecl, PatKind, PredicateOrigin};
use rustc_middle::bug;
use rustc_middle::lint::LevelAndSource;
use rustc_middle::ty::layout::LayoutOf;
@ -952,36 +952,34 @@ declare_lint! {
declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GENERIC_ITEMS]);
impl InvalidNoMangleItems {
fn check_no_mangle_on_generic_fn(
&self,
cx: &LateContext<'_>,
attr_span: Span,
def_id: LocalDefId,
) {
let generics = cx.tcx.generics_of(def_id);
if generics.requires_monomorphization(cx.tcx) {
cx.emit_span_lint(
NO_MANGLE_GENERIC_ITEMS,
cx.tcx.def_span(def_id),
BuiltinNoMangleGeneric { suggestion: attr_span },
);
}
}
}
impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
let attrs = cx.tcx.hir_attrs(it.hir_id());
let check_no_mangle_on_generic_fn = |attr_span: Span,
impl_generics: Option<&hir::Generics<'_>>,
generics: &hir::Generics<'_>,
span| {
for param in
generics.params.iter().chain(impl_generics.map(|g| g.params).into_iter().flatten())
{
match param.kind {
GenericParamKind::Lifetime { .. } => {}
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
cx.emit_span_lint(
NO_MANGLE_GENERIC_ITEMS,
span,
BuiltinNoMangleGeneric { suggestion: attr_span },
);
break;
}
}
}
};
match it.kind {
hir::ItemKind::Fn { generics, .. } => {
hir::ItemKind::Fn { .. } => {
if let Some(attr_span) =
find_attr!(attrs, AttributeKind::ExportName {span, ..} => *span)
.or_else(|| find_attr!(attrs, AttributeKind::NoMangle(span) => *span))
{
check_no_mangle_on_generic_fn(attr_span, None, generics, it.span);
self.check_no_mangle_on_generic_fn(cx, attr_span, it.owner_id.def_id);
}
}
hir::ItemKind::Const(..) => {
@ -1006,24 +1004,19 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
);
}
}
hir::ItemKind::Impl(hir::Impl { generics, items, .. }) => {
for it in *items {
if let hir::AssocItemKind::Fn { .. } = it.kind {
let attrs = cx.tcx.hir_attrs(it.id.hir_id());
if let Some(attr_span) =
find_attr!(attrs, AttributeKind::ExportName {span, ..} => *span)
.or_else(
|| find_attr!(attrs, AttributeKind::NoMangle(span) => *span),
)
{
check_no_mangle_on_generic_fn(
attr_span,
Some(generics),
cx.tcx.hir_get_generics(it.id.owner_id.def_id).unwrap(),
it.span,
);
}
}
_ => {}
}
}
fn check_impl_item(&mut self, cx: &LateContext<'_>, it: &hir::ImplItem<'_>) {
let attrs = cx.tcx.hir_attrs(it.hir_id());
match it.kind {
hir::ImplItemKind::Fn { .. } => {
if let Some(attr_span) =
find_attr!(attrs, AttributeKind::ExportName {span, ..} => *span)
.or_else(|| find_attr!(attrs, AttributeKind::NoMangle(span) => *span))
{
self.check_no_mangle_on_generic_fn(cx, attr_span, it.owner_id.def_id);
}
}
_ => {}

View file

@ -1,7 +1,7 @@
use rustc_hir::{self as hir, LangItem};
use rustc_middle::ty;
use rustc_session::{declare_lint, declare_lint_pass};
use rustc_span::sym;
use rustc_span::{Ident, sym};
use rustc_trait_selection::traits::supertraits;
use crate::lints::{SupertraitAsDerefTarget, SupertraitAsDerefTargetLabel};
@ -79,11 +79,15 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait {
// erase regions in self type for better diagnostic presentation
let (self_ty, target_principal, supertrait_principal) =
tcx.erase_regions((self_ty, target_principal, supertrait_principal));
let label2 = impl_
.items
.iter()
.find_map(|i| (i.ident.name == sym::Target).then_some(i.span))
.map(|label| SupertraitAsDerefTargetLabel { label });
let label2 = tcx
.associated_items(item.owner_id)
.find_by_ident_and_kind(
tcx,
Ident::with_dummy_span(sym::Target),
ty::AssocTag::Type,
item.owner_id.to_def_id(),
)
.map(|label| SupertraitAsDerefTargetLabel { label: tcx.def_span(label.def_id) });
let span = tcx.def_span(item.owner_id.def_id);
cx.emit_span_lint(
DEREF_INTO_DYN_SUPERTRAIT,

View file

@ -339,6 +339,14 @@ fn register_builtins(store: &mut LintStore) {
add_lint_group!("deprecated_safe", DEPRECATED_SAFE_2024);
add_lint_group!(
"unknown_or_malformed_diagnostic_attributes",
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
UNKNOWN_DIAGNOSTIC_ATTRIBUTES
);
// Register renamed and removed lints.
store.register_renamed("single_use_lifetime", "single_use_lifetimes");
store.register_renamed("elided_lifetime_in_path", "elided_lifetimes_in_paths");

View file

@ -63,7 +63,10 @@ declare_lint_pass! {
LOSSY_PROVENANCE_CASTS,
MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
MACRO_USE_EXTERN_CRATE,
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
META_VARIABLE_MISUSE,
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
MISSING_ABI,
MISSING_UNSAFE_ON_EXTERN,
MUST_NOT_SUSPEND,
@ -112,8 +115,8 @@ declare_lint_pass! {
UNFULFILLED_LINT_EXPECTATIONS,
UNINHABITED_STATIC,
UNKNOWN_CRATE_TYPES,
UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
UNKNOWN_LINTS,
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
UNNAMEABLE_TEST_ITEMS,
UNNAMEABLE_TYPES,
UNREACHABLE_CODE,
@ -4284,31 +4287,105 @@ declare_lint! {
}
declare_lint! {
/// The `unknown_or_malformed_diagnostic_attributes` lint detects unrecognized or otherwise malformed
/// diagnostic attributes.
/// The `malformed_diagnostic_attributes` lint detects malformed diagnostic attributes.
///
/// ### Example
///
/// ```rust
/// #![feature(diagnostic_namespace)]
/// #[diagnostic::does_not_exist]
/// struct Foo;
/// #[diagnostic::do_not_recommend(message = "message")]
/// trait Trait {}
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// It is usually a mistake to use options or syntax that is not supported. Check the spelling,
/// and check the diagnostic attribute listing for the correct name and syntax. Also consider if
/// you are using an old version of the compiler; perhaps the option or syntax is only available
/// in a newer version. See the [reference] for a list of diagnostic attributes and the syntax
/// of each.
///
/// [reference]: https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-diagnostic-tool-attribute-namespace
pub MALFORMED_DIAGNOSTIC_ATTRIBUTES,
Warn,
"detects malformed diagnostic attributes",
}
declare_lint! {
/// The `misplaced_diagnostic_attributes` lint detects wrongly placed diagnostic attributes.
///
/// ### Example
///
/// ```rust
/// #[diagnostic::do_not_recommend]
/// struct NotUserFacing;
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// It is usually a mistake to specify a diagnostic attribute that does not exist. Check
/// the spelling, and check the diagnostic attribute listing for the correct name. Also
/// consider if you are using an old version of the compiler, and the attribute
/// is only available in a newer version.
pub UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
/// It is usually a mistake to specify a diagnostic attribute on an item it is not meant for.
/// For example, `#[diagnostic::do_not_recommend]` can only be placed on trait implementations,
/// and does nothing if placed elsewhere. See the [reference] for a list of diagnostic
/// attributes and their correct positions.
///
/// [reference]: https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-diagnostic-tool-attribute-namespace
pub MISPLACED_DIAGNOSTIC_ATTRIBUTES,
Warn,
"unrecognized or malformed diagnostic attribute",
"detects diagnostic attributes that are placed on the wrong item",
}
declare_lint! {
/// The `unknown_diagnostic_attributes` lint detects unknown diagnostic attributes.
///
/// ### Example
///
/// ```rust
/// #[diagnostic::does_not_exist]
/// struct Thing;
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// It is usually a mistake to specify a diagnostic attribute that does not exist. Check the
/// spelling, and check the diagnostic attribute listing for the correct name. Also consider if
/// you are using an old version of the compiler and the attribute is only available in a newer
/// version. See the [reference] for the list of diagnostic attributes.
///
/// [reference]: https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-diagnostic-tool-attribute-namespace
pub UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
Warn,
"detects unknown diagnostic attributes",
}
declare_lint! {
/// The `malformed_diagnostic_format_literals` lint detects malformed diagnostic format
/// literals.
///
/// ### Example
///
/// ```rust
/// #[diagnostic::on_unimplemented(message = "{Self}} does not implement `Trait`")]
/// trait Trait {}
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// The `#[diagnostic::on_unimplemented]` attribute accepts string literal values that are
/// similar to `format!`'s string literal. See the [reference] for details on what is permitted
/// in this string literal.
///
/// [reference]: https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-diagnostic-tool-attribute-namespace
pub MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
Warn,
"detects diagnostic attribute with malformed diagnostic format literals",
}
declare_lint! {
/// The `ambiguous_glob_imports` lint detects glob imports that should report ambiguity
/// errors, but previously didn't do that due to rustc bugs.

View file

@ -14,3 +14,7 @@ libc = "0.2.73"
# pinned `cc` in `rustc_codegen_ssa` if you update `cc` here.
cc = "=1.2.16"
# tidy-alphabetical-end
[features]
# Used by ./x.py check --compile-time-deps to skip building C++ code
check_only = []

View file

@ -106,6 +106,10 @@ fn output(cmd: &mut Command) -> String {
}
fn main() {
if cfg!(feature = "check_only") {
return;
}
for component in REQUIRED_COMPONENTS.iter().chain(OPTIONAL_COMPONENTS.iter()) {
println!("cargo:rustc-check-cfg=cfg(llvm_component,values(\"{component}\"))");
}

View file

@ -19,7 +19,7 @@ pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> FxIndexMap<Def
let item = tcx.hir_item(id);
if let hir::ItemKind::ForeignMod { abi, items } = item.kind {
let foreign_items = items.iter().map(|it| it.id.owner_id.to_def_id()).collect();
let foreign_items = items.iter().map(|it| it.owner_id.to_def_id()).collect();
modules.insert(def_id, ForeignModule { def_id, abi, foreign_items });
}
}

View file

@ -326,7 +326,7 @@ provide! { tcx, def_id, other, cdata,
.process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys")))
}
associated_types_for_impl_traits_in_associated_fn => { table_defaulted_array }
associated_types_for_impl_traits_in_trait_or_impl => { table }
visibility => { cdata.get_visibility(def_id.index) }
adt_def => { cdata.get_adt_def(def_id.index, tcx) }

View file

@ -1382,17 +1382,6 @@ fn should_encode_const(def_kind: DefKind) -> bool {
}
}
fn should_encode_fn_impl_trait_in_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
if let Some(assoc_item) = tcx.opt_associated_item(def_id)
&& assoc_item.container == ty::AssocItemContainer::Trait
&& assoc_item.is_fn()
{
true
} else {
false
}
}
impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
fn encode_attrs(&mut self, def_id: LocalDefId) {
let tcx = self.tcx;
@ -1617,9 +1606,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
{
record!(self.tables.trait_impl_trait_tys[def_id] <- table);
}
if should_encode_fn_impl_trait_in_trait(tcx, def_id) {
let table = tcx.associated_types_for_impl_traits_in_associated_fn(def_id);
record_defaulted_array!(self.tables.associated_types_for_impl_traits_in_associated_fn[def_id] <- table);
if let DefKind::Impl { .. } | DefKind::Trait = def_kind {
let table = tcx.associated_types_for_impl_traits_in_trait_or_impl(def_id);
record!(self.tables.associated_types_for_impl_traits_in_trait_or_impl[def_id] <- table);
}
}

View file

@ -403,7 +403,6 @@ define_tables! {
explicit_implied_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
explicit_implied_const_bounds: Table<DefIndex, LazyArray<(ty::PolyTraitRef<'static>, Span)>>,
inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
associated_types_for_impl_traits_in_associated_fn: Table<DefIndex, LazyArray<DefId>>,
opt_rpitit_info: Table<DefIndex, Option<LazyValue<ty::ImplTraitInTraitData>>>,
// Reexported names are not associated with individual `DefId`s,
// e.g. a glob import can introduce a lot of names, all with the same `DefId`.
@ -482,6 +481,7 @@ define_tables! {
assumed_wf_types_for_rpitit: Table<DefIndex, LazyArray<(Ty<'static>, Span)>>,
opaque_ty_origin: Table<DefIndex, LazyValue<hir::OpaqueTyOrigin<DefId>>>,
anon_const_kind: Table<DefIndex, LazyValue<ty::AnonConstKind>>,
associated_types_for_impl_traits_in_trait_or_impl: Table<DefIndex, LazyValue<DefIdMap<Vec<DefId>>>>,
}
#[derive(TyEncodable, TyDecodable)]

View file

@ -4,6 +4,7 @@
use rustc_abi::ExternAbi;
use rustc_ast::visit::{VisitorResult, walk_list};
use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::svh::Svh;
@ -15,7 +16,7 @@ use rustc_hir::intravisit::Visitor;
use rustc_hir::*;
use rustc_hir_pretty as pprust_hir;
use rustc_span::def_id::StableCrateId;
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym, with_metavar_spans};
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, with_metavar_spans};
use crate::hir::{ModuleItems, nested_filter};
use crate::middle::debugger_visualizer::DebuggerVisualizerFile;
@ -369,7 +370,7 @@ impl<'tcx> TyCtxt<'tcx> {
}
pub fn hir_rustc_coherence_is_core(self) -> bool {
self.hir_krate_attrs().iter().any(|attr| attr.has_name(sym::rustc_coherence_is_core))
find_attr!(self.hir_krate_attrs(), AttributeKind::CoherenceIsCore)
}
pub fn hir_get_module(self, module: LocalModDefId) -> (&'tcx Mod<'tcx>, Span, HirId) {

View file

@ -1,6 +1,5 @@
use std::sync::OnceLock;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::graph;
use rustc_data_structures::graph::dominators::{Dominators, dominators};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@ -10,7 +9,7 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use smallvec::SmallVec;
use crate::mir::traversal::Postorder;
use crate::mir::{BasicBlock, BasicBlockData, START_BLOCK, Terminator, TerminatorKind};
use crate::mir::{BasicBlock, BasicBlockData, START_BLOCK};
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)]
pub struct BasicBlocks<'tcx> {
@ -21,15 +20,6 @@ pub struct BasicBlocks<'tcx> {
// Typically 95%+ of basic blocks have 4 or fewer predecessors.
type Predecessors = IndexVec<BasicBlock, SmallVec<[BasicBlock; 4]>>;
/// Each `(target, switch)` entry in the map contains a list of switch values
/// that lead to a `target` block from a `switch` block.
///
/// Note: this type is currently never instantiated, because it's only used for
/// `BasicBlocks::switch_sources`, which is only called by backwards analyses
/// that do `SwitchInt` handling, and we don't have any of those, not even in
/// tests. See #95120 and #94576.
type SwitchSources = FxHashMap<(BasicBlock, BasicBlock), SmallVec<[SwitchTargetValue; 1]>>;
#[derive(Debug, Clone, Copy)]
pub enum SwitchTargetValue {
// A normal switch value.
@ -41,7 +31,6 @@ pub enum SwitchTargetValue {
#[derive(Clone, Default, Debug)]
struct Cache {
predecessors: OnceLock<Predecessors>,
switch_sources: OnceLock<SwitchSources>,
reverse_postorder: OnceLock<Vec<BasicBlock>>,
dominators: OnceLock<Dominators<BasicBlock>>,
}
@ -86,33 +75,6 @@ impl<'tcx> BasicBlocks<'tcx> {
})
}
/// Returns info about switch values that lead from one block to another
/// block. See `SwitchSources`.
#[inline]
pub fn switch_sources(&self) -> &SwitchSources {
self.cache.switch_sources.get_or_init(|| {
let mut switch_sources: SwitchSources = FxHashMap::default();
for (bb, data) in self.basic_blocks.iter_enumerated() {
if let Some(Terminator {
kind: TerminatorKind::SwitchInt { targets, .. }, ..
}) = &data.terminator
{
for (value, target) in targets.iter() {
switch_sources
.entry((target, bb))
.or_default()
.push(SwitchTargetValue::Normal(value));
}
switch_sources
.entry((targets.otherwise(), bb))
.or_default()
.push(SwitchTargetValue::Otherwise);
}
}
switch_sources
})
}
/// Returns mutable reference to basic blocks. Invalidates CFG cache.
#[inline]
pub fn as_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {

View file

@ -101,6 +101,8 @@ pub struct Allocation<Prov: Provenance = CtfeProvenance, Extra = (), Bytes = Box
/// at the given offset.
provenance: ProvenanceMap<Prov>,
/// Denotes which part of this allocation is initialized.
///
/// Invariant: the uninitialized parts have no provenance.
init_mask: InitMask,
/// The alignment of the allocation to detect unaligned reads.
/// (`Align` guarantees that this is a power of two.)
@ -796,24 +798,19 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
Ok(())
}
/// Initialize all previously uninitialized bytes in the entire allocation, and set
/// provenance of everything to `Wildcard`. Before calling this, make sure all
/// provenance in this allocation is exposed!
pub fn prepare_for_native_access(&mut self) {
let full_range = AllocRange { start: Size::ZERO, size: Size::from_bytes(self.len()) };
// Overwrite uninitialized bytes with 0, to ensure we don't leak whatever their value happens to be.
for chunk in self.init_mask.range_as_init_chunks(full_range) {
if !chunk.is_init() {
let uninit_bytes = &mut self.bytes
[chunk.range().start.bytes_usize()..chunk.range().end.bytes_usize()];
uninit_bytes.fill(0);
}
}
// Mark everything as initialized now.
self.mark_init(full_range, true);
// Set provenance of all bytes to wildcard.
self.provenance.write_wildcards(self.len());
/// Mark all bytes in the given range as initialised and reset the provenance
/// to wildcards. This entirely breaks the normal mechanisms for tracking
/// initialisation and is only provided for Miri operating in native-lib
/// mode. UB will be missed if the underlying bytes were not actually written to.
///
/// If `range` is `None`, defaults to performing this on the whole allocation.
pub fn process_native_write(&mut self, cx: &impl HasDataLayout, range: Option<AllocRange>) {
let range = range.unwrap_or_else(|| AllocRange {
start: Size::ZERO,
size: Size::from_bytes(self.len()),
});
self.mark_init(range, true);
self.provenance.write_wildcards(cx, range);
}
/// Remove all provenance in the given memory range.

View file

@ -212,21 +212,37 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
Ok(())
}
/// Overwrites all provenance in the allocation with wildcard provenance.
/// Overwrites all provenance in the given range with wildcard provenance.
/// Pointers partially overwritten will have their provenances preserved
/// bytewise on their remaining bytes.
///
/// Provided for usage in Miri and panics otherwise.
pub fn write_wildcards(&mut self, alloc_size: usize) {
pub fn write_wildcards(&mut self, cx: &impl HasDataLayout, range: AllocRange) {
assert!(
Prov::OFFSET_IS_ADDR,
"writing wildcard provenance is not supported when `OFFSET_IS_ADDR` is false"
);
let wildcard = Prov::WILDCARD.unwrap();
// Remove all pointer provenances, then write wildcards into the whole byte range.
self.ptrs.clear();
let last = Size::from_bytes(alloc_size);
let bytes = self.bytes.get_or_insert_with(Box::default);
for offset in Size::ZERO..last {
// Remove pointer provenances that overlap with the range, then readd the edge ones bytewise.
let ptr_range = Self::adjusted_range_ptrs(range, cx);
let ptrs = self.ptrs.range(ptr_range.clone());
if let Some((offset, prov)) = ptrs.first() {
for byte_ofs in *offset..range.start {
bytes.insert(byte_ofs, *prov);
}
}
if let Some((offset, prov)) = ptrs.last() {
for byte_ofs in range.end()..*offset + cx.data_layout().pointer_size() {
bytes.insert(byte_ofs, *prov);
}
}
self.ptrs.remove_range(ptr_range);
// Overwrite bytewise provenance.
for offset in range.start..range.end() {
bytes.insert(offset, wildcard);
}
}

View file

@ -1079,15 +1079,11 @@ rustc_queries! {
desc { |tcx| "comparing impl items against trait for `{}`", tcx.def_path_str(impl_id) }
}
/// Given `fn_def_id` of a trait or of an impl that implements a given trait:
/// if `fn_def_id` is the def id of a function defined inside a trait, then it creates and returns
/// the associated items that correspond to each impl trait in return position for that trait.
/// if `fn_def_id` is the def id of a function defined inside an impl that implements a trait, then it
/// creates and returns the associated items that correspond to each impl trait in return position
/// of the implemented trait.
query associated_types_for_impl_traits_in_associated_fn(fn_def_id: DefId) -> &'tcx [DefId] {
desc { |tcx| "creating associated items for opaque types returned by `{}`", tcx.def_path_str(fn_def_id) }
cache_on_disk_if { fn_def_id.is_local() }
/// Given the `item_def_id` of a trait or impl, return a mapping from associated fn def id
/// to its associated type items that correspond to the RPITITs in its signature.
query associated_types_for_impl_traits_in_trait_or_impl(item_def_id: DefId) -> &'tcx DefIdMap<Vec<DefId>> {
arena_cache
desc { |tcx| "synthesizing RPITIT items for the opaque types for methods in `{}`", tcx.def_path_str(item_def_id) }
separate_provide_extern
}

View file

@ -412,6 +412,10 @@ pub enum ObligationCauseCode<'tcx> {
/// Obligations emitted during the normalization of a free type alias.
TypeAlias(ObligationCauseCodeHandle<'tcx>, Span, DefId),
/// 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),
}
/// Whether a value can be extracted into a const.

View file

@ -17,7 +17,6 @@ use rustc_index::{IndexSlice, IndexVec};
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
use rustc_query_system::ich::StableHashingContext;
use rustc_session::DataTypeKind;
use rustc_span::sym;
use rustc_type_ir::solve::AdtDestructorKind;
use tracing::{debug, info, trace};
@ -296,7 +295,7 @@ impl AdtDefData {
flags |= AdtFlags::HAS_CTOR;
}
if tcx.has_attr(did, sym::fundamental) {
if find_attr!(tcx.get_all_attrs(did), AttributeKind::Fundamental) {
flags |= AdtFlags::IS_FUNDAMENTAL;
}
if tcx.is_lang_item(did, LangItem::PhantomData) {

View file

@ -1,9 +1,10 @@
use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_data_structures::sorted_map::SortedIndexMultiMap;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace};
use rustc_hir::def_id::DefId;
use rustc_macros::{Decodable, Encodable, HashStable};
use rustc_span::{Ident, Symbol, sym};
use rustc_span::{Ident, Symbol};
use super::{TyCtxt, Visibility};
use crate::ty;
@ -160,7 +161,7 @@ impl AssocItem {
// Inherent impl but this attr is only applied to trait assoc items.
(AssocItemContainer::Impl, None) => return true,
};
tcx.has_attr(def_id, sym::type_const)
find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TypeConst(_))
}
}
@ -256,6 +257,16 @@ impl AssocItems {
self.items.get_by_key(Some(name))
}
/// Returns the associated item with the given identifier and `AssocKind`, if one exists.
/// The identifier is ignoring hygiene. This is meant to be used for lints and diagnostics.
pub fn filter_by_name_unhygienic_and_kind(
&self,
name: Symbol,
assoc_tag: AssocTag,
) -> impl '_ + Iterator<Item = &ty::AssocItem> {
self.filter_by_name_unhygienic(name).filter(move |item| item.as_tag() == assoc_tag)
}
/// Returns the associated item with the given identifier and `AssocKind`, if one exists.
/// The identifier is matched hygienically.
pub fn find_by_ident_and_kind(
@ -284,3 +295,22 @@ impl AssocItems {
.find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id))
}
}
impl<'tcx> TyCtxt<'tcx> {
/// Given an `fn_def_id` of a trait or a trait implementation:
///
/// if `fn_def_id` is a function defined inside a trait, then it synthesizes
/// a new def id corresponding to a new associated type for each return-
/// position `impl Trait` in the signature.
///
/// if `fn_def_id` is a function inside of an impl, then for each synthetic
/// associated type generated for the corresponding trait function described
/// above, synthesize a corresponding associated type in the impl.
pub fn associated_types_for_impl_traits_in_associated_fn(
self,
fn_def_id: DefId,
) -> &'tcx [DefId] {
let parent_def_id = self.parent(fn_def_id);
&self.associated_types_for_impl_traits_in_trait_or_impl(parent_def_id)[&fn_def_id]
}
}

View file

@ -991,18 +991,16 @@ fn needs_fn_once_adapter_shim(
Ok(false)
}
(ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {
// The closure fn `llfn` is a `fn(&self, ...)`. We want a
// `fn(&mut self, ...)`. In fact, at codegen time, these are
// basically the same thing, so we can just return llfn.
// The closure fn is a `fn(&self, ...)`, but we want a `fn(&mut self, ...)`.
// At codegen time, these are basically the same, so we can just return the closure.
Ok(false)
}
(ty::ClosureKind::Fn | ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
// The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut
// self, ...)`. We want a `fn(self, ...)`. We can produce
// this by doing something like:
// The closure fn is a `fn(&self, ...)` or `fn(&mut self, ...)`, but
// we want a `fn(self, ...)`. We can produce this by doing something like:
//
// fn call_once(self, ...) { call_mut(&self, ...) }
// fn call_once(mut self, ...) { call_mut(&mut self, ...) }
// fn call_once(self, ...) { Fn::call(&self, ...) }
// fn call_once(mut self, ...) { FnMut::call_mut(&mut self, ...) }
//
// These are both the same at codegen time.
Ok(true)

View file

@ -1590,7 +1590,8 @@ impl<'tcx> TyCtxt<'tcx> {
}
/// Look up the name of a definition across crates. This does not look at HIR.
pub fn opt_item_name(self, def_id: DefId) -> Option<Symbol> {
pub fn opt_item_name(self, def_id: impl IntoQueryParam<DefId>) -> Option<Symbol> {
let def_id = def_id.into_query_param();
if let Some(cnum) = def_id.as_crate_root() {
Some(self.crate_name(cnum))
} else {
@ -1610,7 +1611,8 @@ impl<'tcx> TyCtxt<'tcx> {
/// [`opt_item_name`] instead.
///
/// [`opt_item_name`]: Self::opt_item_name
pub fn item_name(self, id: DefId) -> Symbol {
pub fn item_name(self, id: impl IntoQueryParam<DefId>) -> Symbol {
let id = id.into_query_param();
self.opt_item_name(id).unwrap_or_else(|| {
bug!("item_name: no name for {:?}", self.def_path(id));
})
@ -1619,7 +1621,8 @@ impl<'tcx> TyCtxt<'tcx> {
/// Look up the name and span of a definition.
///
/// See [`item_name`][Self::item_name] for more information.
pub fn opt_item_ident(self, def_id: DefId) -> Option<Ident> {
pub fn opt_item_ident(self, def_id: impl IntoQueryParam<DefId>) -> Option<Ident> {
let def_id = def_id.into_query_param();
let def = self.opt_item_name(def_id)?;
let span = self
.def_ident_span(def_id)
@ -1630,7 +1633,8 @@ impl<'tcx> TyCtxt<'tcx> {
/// Look up the name and span of a definition.
///
/// See [`item_name`][Self::item_name] for more information.
pub fn item_ident(self, def_id: DefId) -> Ident {
pub fn item_ident(self, def_id: impl IntoQueryParam<DefId>) -> Ident {
let def_id = def_id.into_query_param();
self.opt_item_ident(def_id).unwrap_or_else(|| {
bug!("item_ident: no name for {:?}", self.def_path(def_id));
})
@ -1782,21 +1786,18 @@ impl<'tcx> TyCtxt<'tcx> {
did: impl Into<DefId>,
attr: Symbol,
) -> impl Iterator<Item = &'tcx hir::Attribute> {
self.get_all_attrs(did).filter(move |a: &&hir::Attribute| a.has_name(attr))
self.get_all_attrs(did).iter().filter(move |a: &&hir::Attribute| a.has_name(attr))
}
/// Gets all attributes.
///
/// To see if an item has a specific attribute, you should use [`rustc_attr_data_structures::find_attr!`] so you can use matching.
pub fn get_all_attrs(
self,
did: impl Into<DefId>,
) -> impl Iterator<Item = &'tcx hir::Attribute> {
pub fn get_all_attrs(self, did: impl Into<DefId>) -> &'tcx [hir::Attribute] {
let did: DefId = did.into();
if let Some(did) = did.as_local() {
self.hir_attrs(self.local_def_id_to_hir_id(did)).iter()
self.hir_attrs(self.local_def_id_to_hir_id(did))
} else {
self.attrs_for_def(did).iter()
self.attrs_for_def(did)
}
}

View file

@ -23,6 +23,10 @@ impl<A: ParameterizedOverTcx, B: ParameterizedOverTcx> ParameterizedOverTcx for
type Value<'tcx> = (A::Value<'tcx>, B::Value<'tcx>);
}
impl<T: ParameterizedOverTcx> ParameterizedOverTcx for Vec<T> {
type Value<'tcx> = Vec<T::Value<'tcx>>;
}
impl<I: Idx + 'static, T: ParameterizedOverTcx> ParameterizedOverTcx for IndexVec<I, T> {
type Value<'tcx> = IndexVec<I, T::Value<'tcx>>;
}

View file

@ -1052,9 +1052,11 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for FreeAliasTypeExpander<'tcx> {
}
self.depth += 1;
ensure_sufficient_stack(|| {
let ty = ensure_sufficient_stack(|| {
self.tcx.type_of(alias.def_id).instantiate(self.tcx, alias.args).fold_with(self)
})
});
self.depth -= 1;
ty
}
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
@ -1681,7 +1683,7 @@ pub fn intrinsic_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Intrinsi
_ => true,
};
Some(ty::IntrinsicDef {
name: tcx.item_name(def_id.into()),
name: tcx.item_name(def_id),
must_be_overridden,
const_stable: tcx.has_attr(def_id, sym::rustc_intrinsic_const_stable_indirect),
})

View file

@ -1084,7 +1084,7 @@ fn find_fallback_pattern_typo<'tcx>(
&& infcx.can_eq(param_env, ty, cx.tcx.type_of(item.owner_id).instantiate_identity())
{
// Look for local consts.
let item_name = cx.tcx.item_name(item.owner_id.into());
let item_name = cx.tcx.item_name(item.owner_id);
let vis = cx.tcx.visibility(item.owner_id);
if vis.is_accessible_from(parent, cx.tcx) {
accessible.push(item_name);

View file

@ -1,5 +1,6 @@
use std::ops::RangeInclusive;
use rustc_middle::bug;
use rustc_middle::mir::{
self, BasicBlock, CallReturnPlaces, Location, SwitchTargetValue, TerminatorEdges,
};
@ -112,15 +113,11 @@ impl Direction for Backward {
propagate(pred, &tmp);
}
mir::TerminatorKind::SwitchInt { ref targets, ref discr } => {
if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
let mut tmp = analysis.bottom_value(body);
for &value in &body.basic_blocks.switch_sources()[&(block, pred)] {
tmp.clone_from(exit_state);
analysis
.apply_switch_int_edge_effect(&mut data, &mut tmp, value, targets);
propagate(pred, &tmp);
}
mir::TerminatorKind::SwitchInt { ref discr, .. } => {
if let Some(_data) = analysis.get_switch_int_data(pred, discr) {
bug!(
"SwitchInt edge effects are unsupported in backward dataflow analyses"
);
} else {
propagate(pred, exit_state)
}

View file

@ -293,10 +293,6 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor<'_, 'tcx> {
fn compute_copy_classes(ssa: &mut SsaLocals, body: &Body<'_>) {
let mut direct_uses = std::mem::take(&mut ssa.direct_uses);
let mut copies = IndexVec::from_fn_n(|l| l, body.local_decls.len());
// We must not unify two locals that are borrowed. But this is fine if one is borrowed and
// the other is not. This bitset is keyed by *class head* and contains whether any member of
// the class is borrowed.
let mut borrowed_classes = ssa.borrowed_locals().clone();
for (local, rvalue, _) in ssa.assignments(body) {
let (Rvalue::Use(Operand::Copy(place) | Operand::Move(place))
@ -322,8 +318,12 @@ fn compute_copy_classes(ssa: &mut SsaLocals, body: &Body<'_>) {
// visited before `local`, and we just have to copy the representing local.
let head = copies[rhs];
// Do not unify borrowed locals.
if borrowed_classes.contains(local) || borrowed_classes.contains(head) {
// When propagating from `head` to `local` we need to ensure that changes to the address
// are not observable, so at most one the locals involved can be borrowed. Additionally, we
// need to ensure that the definition of `head` dominates all uses of `local`. When `local`
// is borrowed, there might exist an indirect use of `local` that isn't dominated by the
// definition, so we have to reject copy propagation.
if ssa.borrowed_locals().contains(local) {
continue;
}
@ -339,21 +339,14 @@ fn compute_copy_classes(ssa: &mut SsaLocals, body: &Body<'_>) {
*h = RETURN_PLACE;
}
}
if borrowed_classes.contains(head) {
borrowed_classes.insert(RETURN_PLACE);
}
} else {
copies[local] = head;
if borrowed_classes.contains(local) {
borrowed_classes.insert(head);
}
}
direct_uses[rhs] -= 1;
}
debug!(?copies);
debug!(?direct_uses);
debug!(?borrowed_classes);
// Invariant: `copies` must point to the head of an equivalence class.
#[cfg(debug_assertions)]
@ -362,13 +355,6 @@ fn compute_copy_classes(ssa: &mut SsaLocals, body: &Body<'_>) {
}
debug_assert_eq!(copies[RETURN_PLACE], RETURN_PLACE);
// Invariant: `borrowed_classes` must be true if any member of the class is borrowed.
#[cfg(debug_assertions)]
for &head in copies.iter() {
let any_borrowed = ssa.borrowed_locals.iter().any(|l| copies[l] == head);
assert_eq!(borrowed_classes.contains(head), any_borrowed);
}
ssa.direct_uses = direct_uses;
ssa.copy_classes = copies;
}

View file

@ -0,0 +1,73 @@
use rustc_ast::token::Token;
use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_ast::{MetaItemInner, token};
use rustc_errors::PResult;
use rustc_span::Span;
use crate::exp;
use crate::parser::Parser;
pub enum CfgSelectRule {
Cfg(MetaItemInner),
Wildcard(Token),
}
#[derive(Default)]
pub struct CfgSelectBranches {
/// All the conditional branches.
pub reachable: Vec<(MetaItemInner, TokenStream, Span)>,
/// The first wildcard `_ => { ... }` branch.
pub wildcard: Option<(Token, TokenStream, Span)>,
/// All branches after the first wildcard, including further wildcards.
/// These branches are kept for formatting.
pub unreachable: Vec<(CfgSelectRule, TokenStream, Span)>,
}
/// Parses a `TokenTree` that must be of the form `{ /* ... */ }`, and returns a `TokenStream` where
/// the surrounding braces are stripped.
fn parse_token_tree<'a>(p: &mut Parser<'a>) -> PResult<'a, TokenStream> {
// Generate an error if the `=>` is not followed by `{`.
if p.token != token::OpenBrace {
p.expect(exp!(OpenBrace))?;
}
// Strip the outer '{' and '}'.
match p.parse_token_tree() {
TokenTree::Token(..) => unreachable!("because of the expect above"),
TokenTree::Delimited(.., tts) => Ok(tts),
}
}
pub fn parse_cfg_select<'a>(p: &mut Parser<'a>) -> PResult<'a, CfgSelectBranches> {
let mut branches = CfgSelectBranches::default();
while p.token != token::Eof {
if p.eat_keyword(exp!(Underscore)) {
let underscore = p.prev_token;
p.expect(exp!(FatArrow))?;
let tts = parse_token_tree(p)?;
let span = underscore.span.to(p.token.span);
match branches.wildcard {
None => branches.wildcard = Some((underscore, tts, span)),
Some(_) => {
branches.unreachable.push((CfgSelectRule::Wildcard(underscore), tts, span))
}
}
} else {
let meta_item = p.parse_meta_item_inner()?;
p.expect(exp!(FatArrow))?;
let tts = parse_token_tree(p)?;
let span = meta_item.span().to(p.token.span);
match branches.wildcard {
None => branches.reachable.push((meta_item, tts, span)),
Some(_) => branches.unreachable.push((CfgSelectRule::Cfg(meta_item), tts, span)),
}
}
}
Ok(branches)
}

View file

@ -1,4 +1,3 @@
pub mod asm;
pub mod attr;
mod attr_wrapper;
mod diagnostics;
@ -12,6 +11,11 @@ mod stmt;
pub mod token_type;
mod ty;
// Parsers for non-functionlike builtin macros are defined in rustc_parse so they can be used by
// both rustc_builtin_macros and rustfmt.
pub mod asm;
pub mod cfg_select;
use std::assert_matches::debug_assert_matches;
use std::{fmt, mem, slice};

View file

@ -283,6 +283,18 @@ pub fn check_builtin_meta_item(
| sym::rustc_confusables
| sym::rustc_skip_during_method_dispatch
| sym::rustc_pass_by_value
| sym::rustc_deny_explicit_impl
| sym::rustc_do_not_implement_via_object
| sym::rustc_coinductive
| sym::const_trait
| sym::rustc_specialization_trait
| sym::rustc_unsafe_specialization_marker
| sym::rustc_allow_incoherent_impl
| sym::rustc_coherence_is_core
| sym::marker
| sym::fundamental
| sym::rustc_paren_sugar
| sym::type_const
| sym::repr
| sym::align
| sym::deprecated

View file

@ -10,6 +10,7 @@ rustc_ast = { path = "../rustc_ast" }
rustc_ast_lowering = { path = "../rustc_ast_lowering" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_expand = { path = "../rustc_expand" }

View file

@ -79,7 +79,7 @@ fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut
for meta_item in meta_items {
match meta_item.name() {
Some(sym::debug) => {
let fn_name = tcx.item_name(item_def_id.into());
let fn_name = tcx.item_name(item_def_id);
tcx.dcx().emit_err(AbiOf {
span: tcx.def_span(item_def_id),
fn_name,
@ -135,7 +135,7 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut
item_def_id,
);
let fn_name = tcx.item_name(item_def_id.into());
let fn_name = tcx.item_name(item_def_id);
tcx.dcx().emit_err(AbiOf { span, fn_name, fn_abi: format!("{:#?}", abi) });
}
Some(sym::assert_eq) => {

View file

@ -7,10 +7,12 @@
use std::cell::Cell;
use std::collections::hash_map::Entry;
use std::slice;
use rustc_abi::{Align, ExternAbi, Size};
use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, ast};
use rustc_attr_data_structures::{AttributeKind, InlineAttr, ReprAttr, find_attr};
use rustc_attr_parsing::{AttributeParser, Late};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey};
use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
@ -18,8 +20,8 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalModDefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{
self as hir, self, AssocItemKind, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem,
HirId, Item, ItemKind, MethodKind, Safety, Target, TraitItem,
self as hir, self, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem, HirId, Item,
ItemKind, MethodKind, Safety, Target, TraitItem,
};
use rustc_macros::LintDiagnostic;
use rustc_middle::hir::nested_filter;
@ -33,7 +35,7 @@ use rustc_session::config::CrateType;
use rustc_session::lint;
use rustc_session::lint::builtin::{
CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS,
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
MALFORMED_DIAGNOSTIC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
};
use rustc_session::parse::feature_err;
use rustc_span::edition::Edition;
@ -120,12 +122,35 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
for attr in attrs {
let mut style = None;
match attr {
Attribute::Parsed(AttributeKind::SkipDuringMethodDispatch {
span: attr_span,
..
}) => {
Attribute::Parsed(
AttributeKind::SkipDuringMethodDispatch { span: attr_span, .. }
| AttributeKind::Coinductive(attr_span)
| AttributeKind::ConstTrait(attr_span)
| AttributeKind::DenyExplicitImpl(attr_span)
| AttributeKind::DoNotImplementViaObject(attr_span),
) => {
self.check_must_be_applied_to_trait(*attr_span, span, target);
}
&Attribute::Parsed(
AttributeKind::SpecializationTrait(attr_span)
| AttributeKind::UnsafeSpecializationMarker(attr_span)
| AttributeKind::ParenSugar(attr_span),
) => {
// FIXME: more validation is needed
self.check_must_be_applied_to_trait(attr_span, span, target);
}
&Attribute::Parsed(AttributeKind::TypeConst(attr_span)) => {
self.check_type_const(hir_id, attr_span, target)
}
&Attribute::Parsed(AttributeKind::Marker(attr_span)) => {
self.check_marker(hir_id, attr_span, span, target)
}
Attribute::Parsed(AttributeKind::Fundamental | AttributeKind::CoherenceIsCore) => {
// FIXME: add validation
}
&Attribute::Parsed(AttributeKind::AllowIncoherentImpl(attr_span)) => {
self.check_allow_incoherent_impl(attr_span, span, target)
}
Attribute::Parsed(AttributeKind::Confusables { first_span, .. }) => {
self.check_confusables(*first_span, target);
}
@ -259,7 +284,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
[sym::no_sanitize, ..] => {
self.check_no_sanitize(attr, span, target)
}
[sym::marker, ..] => self.check_marker(hir_id, attr, span, target),
[sym::thread_local, ..] => self.check_thread_local(attr, span, target),
[sym::doc, ..] => self.check_doc_attrs(
attr,
@ -297,16 +321,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| [sym::rustc_dirty, ..]
| [sym::rustc_if_this_changed, ..]
| [sym::rustc_then_this_would_need, ..] => self.check_rustc_dirty_clean(attr),
[sym::rustc_coinductive, ..]
| [sym::rustc_must_implement_one_of, ..]
| [sym::rustc_deny_explicit_impl, ..]
| [sym::rustc_do_not_implement_via_object, ..]
| [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr.span(), span, target),
[sym::rustc_must_implement_one_of, ..] => self.check_must_be_applied_to_trait(attr.span(), span, target),
[sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target),
[sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target),
[sym::rustc_allow_incoherent_impl, ..] => {
self.check_allow_incoherent_impl(attr, span, target)
}
[sym::rustc_has_incoherent_inherent_impls, ..] => {
self.check_has_incoherent_inherent_impls(attr, span, target)
}
@ -339,9 +356,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
[sym::coroutine, ..] => {
self.check_coroutine(attr, target);
}
[sym::type_const, ..] => {
self.check_type_const(hir_id,attr, target);
}
[sym::linkage, ..] => self.check_linkage(attr, span, target),
[
// ok
@ -366,18 +380,24 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| sym::prelude_import
| sym::panic_handler
| sym::allow_internal_unsafe
| sym::fundamental
| sym::lang
| sym::needs_allocator
| sym::default_lib_allocator
| sym::custom_mir,
..
] => {}
[name, ..] => {
[name, rest@..] => {
match BUILTIN_ATTRIBUTE_MAP.get(name) {
// checked below
Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) => {}
Some(_) => {
if rest.len() > 0 && AttributeParser::<Late>::is_parsed_attribute(slice::from_ref(name)) {
// Check if we tried to use a builtin attribute as an attribute namespace, like `#[must_use::skip]`.
// This check is here to solve https://github.com/rust-lang/rust/issues/137590
// An error is already produced for this case elsewhere
continue
}
// FIXME: differentiate between unstable and internal attributes just
// like we do with features instead of just accepting `rustc_`
// attributes by name. That should allow trimming the above list, too.
@ -449,7 +469,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
);
}
/// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl.
/// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl and that it has no
/// arguments.
fn check_do_not_recommend(
&self,
attr_span: Span,
@ -466,7 +487,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
)
{
self.tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
hir_id,
attr_span,
errors::IncorrectDoNotRecommendLocation,
@ -474,7 +495,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
if !attr.is_word() {
self.tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
hir_id,
attr_span,
errors::DoNotRecommendDoesNotExpectArgs,
@ -486,7 +507,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) {
if !matches!(target, Target::Trait) {
self.tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
hir_id,
attr_span,
DiagnosticOnUnimplementedOnlyForTraits,
@ -830,7 +851,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
/// Checks if the `#[marker]` attribute on an `item` is valid.
fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
fn check_marker(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) {
match target {
Target::Trait => {}
// FIXME(#80564): We permit struct fields, match arms and macro defs to have an
@ -838,13 +859,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "marker");
self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "marker");
}
_ => {
self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait {
attr_span: attr.span(),
defn_span: span,
});
self.dcx()
.emit_err(errors::AttrShouldBeAppliedToTrait { attr_span, defn_span: span });
}
}
}
@ -1132,7 +1151,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
if generics.params.len() != 0 => {}
ItemKind::Trait(_, _, _, generics, _, items)
if generics.params.len() != 0
|| items.iter().any(|item| matches!(item.kind, AssocItemKind::Type)) => {}
|| items.iter().any(|item| {
matches!(self.tcx.def_kind(item.owner_id), DefKind::AssocTy)
}) => {}
ItemKind::TyAlias(_, generics, _) if generics.params.len() != 0 => {}
_ => {
self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() });
@ -1489,11 +1510,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) {
fn check_allow_incoherent_impl(&self, attr_span: Span, span: Span, target: Target) {
match target {
Target::Method(MethodKind::Inherent) => {}
_ => {
self.dcx().emit_err(errors::AllowIncoherentImpl { attr_span: attr.span(), span });
self.dcx().emit_err(errors::AllowIncoherentImpl { attr_span, span });
}
}
}
@ -2514,7 +2535,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
fn check_type_const(&self, hir_id: HirId, attr: &Attribute, target: Target) {
fn check_type_const(&self, hir_id: HirId, attr_span: Span, target: Target) {
let tcx = self.tcx;
if target == Target::AssocConst
&& let parent = tcx.parent(hir_id.expect_owner().to_def_id())
@ -2524,7 +2545,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
} else {
self.dcx()
.struct_span_err(
attr.span(),
attr_span,
"`#[type_const]` must only be applied to trait associated constants",
)
.emit();

View file

@ -18,7 +18,7 @@ use rustc_hir::{self as hir, ImplItem, ImplItemKind, Node, PatKind, QPath, TyKin
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::middle::privacy::Level;
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::ty::{self, AssocTag, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_session::lint::builtin::DEAD_CODE;
use rustc_session::lint::{self, LintExpectationId};
@ -115,7 +115,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
fn handle_res(&mut self, res: Res) {
match res {
Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias, def_id) => {
Res::Def(
DefKind::Const | DefKind::AssocConst | DefKind::AssocTy | DefKind::TyAlias,
def_id,
) => {
self.check_def_id(def_id);
}
_ if self.in_pat => {}
@ -415,8 +418,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
hir::ItemKind::Trait(.., trait_item_refs) => {
// mark assoc ty live if the trait is live
for trait_item in trait_item_refs {
if matches!(trait_item.kind, hir::AssocItemKind::Type) {
self.check_def_id(trait_item.id.owner_id.to_def_id());
if matches!(self.tcx.def_kind(trait_item.owner_id), DefKind::AssocTy) {
self.check_def_id(trait_item.owner_id.to_def_id());
}
}
intravisit::walk_item(self, item)
@ -482,7 +485,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
) -> bool {
let trait_def_id = match self.tcx.def_kind(local_def_id) {
// assoc impl items of traits are live if the corresponding trait items are live
DefKind::AssocFn => self
DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => self
.tcx
.associated_item(local_def_id)
.trait_item_def_id
@ -647,6 +650,31 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
self.in_pat = in_pat;
}
fn visit_trait_ref(&mut self, t: &'tcx hir::TraitRef<'tcx>) {
if let Some(trait_def_id) = t.path.res.opt_def_id()
&& let Some(segment) = t.path.segments.last()
&& let Some(args) = segment.args
{
for constraint in args.constraints {
if let Some(local_def_id) = self
.tcx
.associated_items(trait_def_id)
.find_by_ident_and_kind(
self.tcx,
constraint.ident,
AssocTag::Const,
trait_def_id,
)
.and_then(|item| item.def_id.as_local())
{
self.worklist.push((local_def_id, ComesFromAllowExpect::No));
}
}
}
intravisit::walk_trait_ref(self, t);
}
}
fn has_allow_dead_code_or_lang_attr(
@ -744,18 +772,12 @@ fn check_item<'tcx>(
{
worklist.push((local_def_id, comes_from_allow));
} else if of_trait {
// FIXME: This condition can be removed
// if we support dead check for assoc consts and tys.
if !matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) {
worklist.push((local_def_id, ComesFromAllowExpect::No));
} else {
// We only care about associated items of traits,
// because they cannot be visited directly,
// so we later mark them as live if their corresponding traits
// or trait items and self types are both live,
// but inherent associated items can be visited and marked directly.
unsolved_items.push((id, local_def_id));
}
// We only care about associated items of traits,
// because they cannot be visited directly,
// so we later mark them as live if their corresponding traits
// or trait items and self types are both live,
// but inherent associated items can be visited and marked directly.
unsolved_items.push((id, local_def_id));
}
}
}
@ -791,15 +813,14 @@ fn check_trait_item(
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
id: hir::TraitItemId,
) {
use hir::TraitItemKind::{Const, Fn};
if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) {
let trait_item = tcx.hir_trait_item(id);
if matches!(trait_item.kind, Const(_, Some(_)) | Fn(..))
&& let Some(comes_from_allow) =
has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id)
{
worklist.push((trait_item.owner_id.def_id, comes_from_allow));
}
use hir::TraitItemKind::{Const, Fn, Type};
let trait_item = tcx.hir_trait_item(id);
if matches!(trait_item.kind, Const(_, Some(_)) | Type(_, Some(_)) | Fn(..))
&& let Some(comes_from_allow) =
has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id)
{
worklist.push((trait_item.owner_id.def_id, comes_from_allow));
}
}
@ -1163,6 +1184,7 @@ impl<'tcx> DeadVisitor<'tcx> {
}
match self.tcx.def_kind(def_id) {
DefKind::AssocConst
| DefKind::AssocTy
| DefKind::AssocFn
| DefKind::Fn
| DefKind::Static { .. }

View file

@ -467,9 +467,9 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
hir_visit::walk_trait_item(self, ti)
}
fn visit_trait_item_ref(&mut self, ti: &'v hir::TraitItemRef) {
self.record("TraitItemRef", Some(ti.id.hir_id()), ti);
hir_visit::walk_trait_item_ref(self, ti)
fn visit_trait_item_ref(&mut self, ti: &'v hir::TraitItemId) {
self.record("TraitItemId", Some(ti.hir_id()), ti);
hir_visit::walk_trait_item_ref(self, *ti)
}
fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) {
@ -480,14 +480,14 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
hir_visit::walk_impl_item(self, ii)
}
fn visit_foreign_item_ref(&mut self, fi: &'v hir::ForeignItemRef) {
self.record("ForeignItemRef", Some(fi.id.hir_id()), fi);
hir_visit::walk_foreign_item_ref(self, fi)
fn visit_foreign_item_ref(&mut self, fi: &'v hir::ForeignItemId) {
self.record("ForeignItemId", Some(fi.hir_id()), fi);
hir_visit::walk_foreign_item_ref(self, *fi)
}
fn visit_impl_item_ref(&mut self, ii: &'v hir::ImplItemRef) {
self.record("ImplItemRef", Some(ii.id.hir_id()), ii);
hir_visit::walk_impl_item_ref(self, ii)
fn visit_impl_item_ref(&mut self, ii: &'v hir::ImplItemId) {
self.record("ImplItemId", Some(ii.hir_id()), ii);
hir_visit::walk_impl_item_ref(self, *ii)
}
fn visit_param_bound(&mut self, b: &'v hir::GenericBound<'v>) {

View file

@ -880,11 +880,16 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
}
for impl_item_ref in *items {
let impl_item = self.tcx.associated_item(impl_item_ref.id.owner_id);
let impl_item = self.tcx.associated_item(impl_item_ref.owner_id);
if let Some(def_id) = impl_item.trait_item_def_id {
// Pass `None` to skip deprecation warnings.
self.tcx.check_stability(def_id, None, impl_item_ref.span, None);
self.tcx.check_stability(
def_id,
None,
self.tcx.def_span(impl_item_ref.owner_id),
None,
);
}
}
}

View file

@ -26,7 +26,7 @@ use rustc_errors::{MultiSpan, listify};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId, LocalModDefId};
use rustc_hir::intravisit::{self, InferKind, Visitor};
use rustc_hir::{AmbigArg, AssocItemKind, ForeignItemKind, ItemId, ItemKind, PatKind};
use rustc_hir::{AmbigArg, ForeignItemKind, ItemId, ItemKind, PatKind};
use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level};
use rustc_middle::query::Providers;
use rustc_middle::ty::print::PrintTraitRefExt as _;
@ -672,14 +672,14 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
self.reach(item.owner_id.def_id, item_ev).generics().predicates();
for trait_item_ref in trait_item_refs {
self.update(trait_item_ref.id.owner_id.def_id, item_ev, Level::Reachable);
self.update(trait_item_ref.owner_id.def_id, item_ev, Level::Reachable);
let tcx = self.tcx;
let mut reach = self.reach(trait_item_ref.id.owner_id.def_id, item_ev);
let mut reach = self.reach(trait_item_ref.owner_id.def_id, item_ev);
reach.generics().predicates();
if trait_item_ref.kind == AssocItemKind::Type
&& !tcx.defaultness(trait_item_ref.id.owner_id).has_value()
if let DefKind::AssocTy = tcx.def_kind(trait_item_ref.owner_id)
&& !tcx.defaultness(trait_item_ref.owner_id).has_value()
{
// No type to visit.
} else {
@ -715,7 +715,7 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
self.reach(item.owner_id.def_id, item_ev).generics().predicates().ty().trait_ref();
for impl_item_ref in impl_.items {
let def_id = impl_item_ref.id.owner_id.def_id;
let def_id = impl_item_ref.owner_id.def_id;
let max_vis =
impl_.of_trait.is_none().then(|| self.tcx.local_visibility(def_id));
self.update_eff_vis(def_id, item_ev, max_vis, Level::Direct);
@ -755,8 +755,8 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
}
hir::ItemKind::ForeignMod { items, .. } => {
for foreign_item in items {
if let Some(foreign_item_ev) = self.get(foreign_item.id.owner_id.def_id) {
self.reach(foreign_item.id.owner_id.def_id, foreign_item_ev)
if let Some(foreign_item_ev) = self.get(foreign_item.owner_id.def_id) {
self.reach(foreign_item.owner_id.def_id, foreign_item_ev)
.generics()
.predicates()
.ty();
@ -1576,16 +1576,15 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> {
fn check_assoc_item(
&self,
def_id: LocalDefId,
assoc_item_kind: AssocItemKind,
item: &ty::AssocItem,
vis: ty::Visibility,
effective_vis: Option<EffectiveVisibility>,
) {
let mut check = self.check(def_id, vis, effective_vis);
let mut check = self.check(item.def_id.expect_local(), vis, effective_vis);
let (check_ty, is_assoc_ty) = match assoc_item_kind {
AssocItemKind::Const | AssocItemKind::Fn { .. } => (true, false),
AssocItemKind::Type => (self.tcx.defaultness(def_id).has_value(), true),
let (check_ty, is_assoc_ty) = match item.kind {
ty::AssocKind::Const { .. } | ty::AssocKind::Fn { .. } => (true, false),
ty::AssocKind::Type { .. } => (item.defaultness(self.tcx).has_value(), true),
};
check.in_assoc_ty = is_assoc_ty;
@ -1619,30 +1618,20 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> {
self.check(def_id, item_visibility, effective_vis).generics().bounds();
}
DefKind::Trait => {
let item = tcx.hir_item(id);
if let hir::ItemKind::Trait(.., trait_item_refs) = item.kind {
self.check_unnameable(item.owner_id.def_id, effective_vis);
self.check_unnameable(def_id, effective_vis);
self.check(item.owner_id.def_id, item_visibility, effective_vis)
.generics()
.predicates();
self.check(def_id, item_visibility, effective_vis).generics().predicates();
for trait_item_ref in trait_item_refs {
self.check_assoc_item(
trait_item_ref.id.owner_id.def_id,
trait_item_ref.kind,
for assoc_item in tcx.associated_items(id.owner_id).in_definition_order() {
self.check_assoc_item(assoc_item, item_visibility, effective_vis);
if assoc_item.is_type() {
self.check(
assoc_item.def_id.expect_local(),
item_visibility,
effective_vis,
);
if let AssocItemKind::Type = trait_item_ref.kind {
self.check(
trait_item_ref.id.owner_id.def_id,
item_visibility,
effective_vis,
)
.bounds();
}
)
.bounds();
}
}
}
@ -1669,8 +1658,8 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> {
DefKind::ForeignMod => {
let item = tcx.hir_item(id);
if let hir::ItemKind::ForeignMod { items, .. } = item.kind {
for foreign_item in items {
let foreign_item = tcx.hir_foreign_item(foreign_item.id);
for &foreign_item in items {
let foreign_item = tcx.hir_foreign_item(foreign_item);
let ev = self.get(foreign_item.owner_id.def_id);
let vis = tcx.local_visibility(foreign_item.owner_id.def_id);
@ -1714,69 +1703,52 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> {
// Subitems of inherent impls have their own publicity.
// A trait impl is public when both its type and its trait are public
// Subitems of trait impls have inherited publicity.
DefKind::Impl { .. } => {
let item = tcx.hir_item(id);
if let hir::ItemKind::Impl(impl_) = item.kind {
let impl_vis = ty::Visibility::of_impl::<false>(
item.owner_id.def_id,
tcx,
&Default::default(),
);
DefKind::Impl { of_trait } => {
let impl_vis = ty::Visibility::of_impl::<false>(def_id, tcx, &Default::default());
// We are using the non-shallow version here, unlike when building the
// effective visisibilities table to avoid large number of false positives.
// For example in
//
// impl From<Priv> for Pub {
// fn from(_: Priv) -> Pub {...}
// }
//
// lints shouldn't be emitted even if `from` effective visibility
// is larger than `Priv` nominal visibility and if `Priv` can leak
// in some scenarios due to type inference.
let impl_ev = EffectiveVisibility::of_impl::<false>(
item.owner_id.def_id,
tcx,
self.effective_visibilities,
);
// We are using the non-shallow version here, unlike when building the
// effective visisibilities table to avoid large number of false positives.
// For example in
//
// impl From<Priv> for Pub {
// fn from(_: Priv) -> Pub {...}
// }
//
// lints shouldn't be emitted even if `from` effective visibility
// is larger than `Priv` nominal visibility and if `Priv` can leak
// in some scenarios due to type inference.
let impl_ev =
EffectiveVisibility::of_impl::<false>(def_id, tcx, self.effective_visibilities);
let mut check = self.check(item.owner_id.def_id, impl_vis, Some(impl_ev));
// Generics and predicates of trait impls are intentionally not checked
// for private components (#90586).
if impl_.of_trait.is_none() {
check.generics().predicates();
}
// Skip checking private components in associated types, due to lack of full
// normalization they produce very ridiculous false positives.
// FIXME: Remove this when full normalization is implemented.
check.skip_assoc_tys = true;
check.ty().trait_ref();
let mut check = self.check(def_id, impl_vis, Some(impl_ev));
for impl_item_ref in impl_.items {
let impl_item_vis = if impl_.of_trait.is_none() {
min(
tcx.local_visibility(impl_item_ref.id.owner_id.def_id),
impl_vis,
tcx,
)
} else {
impl_vis
};
// Generics and predicates of trait impls are intentionally not checked
// for private components (#90586).
if !of_trait {
check.generics().predicates();
}
let impl_item_ev = if impl_.of_trait.is_none() {
self.get(impl_item_ref.id.owner_id.def_id)
.map(|ev| ev.min(impl_ev, self.tcx))
} else {
Some(impl_ev)
};
// Skip checking private components in associated types, due to lack of full
// normalization they produce very ridiculous false positives.
// FIXME: Remove this when full normalization is implemented.
check.skip_assoc_tys = true;
check.ty().trait_ref();
self.check_assoc_item(
impl_item_ref.id.owner_id.def_id,
impl_item_ref.kind,
impl_item_vis,
impl_item_ev,
);
}
for assoc_item in tcx.associated_items(id.owner_id).in_definition_order() {
let impl_item_vis = if !of_trait {
min(tcx.local_visibility(assoc_item.def_id.expect_local()), impl_vis, tcx)
} else {
impl_vis
};
let impl_item_ev = if !of_trait {
self.get(assoc_item.def_id.expect_local())
.map(|ev| ev.min(impl_ev, self.tcx))
} else {
Some(impl_ev)
};
self.check_assoc_item(assoc_item, impl_item_vis, impl_item_ev);
}
}
_ => {}

View file

@ -32,10 +32,9 @@ use crate::def_collector::collect_definitions;
use crate::imports::{ImportData, ImportKind};
use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
use crate::{
BindingKey, Determinacy, ExternPreludeEntry, Finalize, MacroData, Module, ModuleKind,
ModuleOrUniformRoot, NameBinding, NameBindingData, NameBindingKind, ParentScope, PathResult,
ResolutionError, Resolver, ResolverArenas, Segment, ToNameBinding, Used, VisResolutionError,
errors,
BindingKey, ExternPreludeEntry, Finalize, MacroData, Module, ModuleKind, ModuleOrUniformRoot,
NameBinding, NameBindingData, NameBindingKind, ParentScope, PathResult, ResolutionError,
Resolver, ResolverArenas, Segment, ToNameBinding, Used, VisResolutionError, errors,
};
type Res = def::Res<NodeId>;
@ -620,16 +619,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let kind = ImportKind::Single {
source: source.ident,
target: ident,
source_bindings: PerNS {
type_ns: Cell::new(Err(Determinacy::Undetermined)),
value_ns: Cell::new(Err(Determinacy::Undetermined)),
macro_ns: Cell::new(Err(Determinacy::Undetermined)),
},
target_bindings: PerNS {
type_ns: Cell::new(None),
value_ns: Cell::new(None),
macro_ns: Cell::new(None),
},
bindings: Default::default(),
type_ns_only,
nested,
id,

View file

@ -511,7 +511,7 @@ impl Resolver<'_, '_> {
for (_key, resolution) in self.resolutions(*module).borrow().iter() {
let resolution = resolution.borrow();
if let Some(binding) = resolution.binding
if let Some(binding) = resolution.best_binding()
&& let NameBindingKind::Import { import, .. } = binding.kind
&& let ImportKind::Single { id, .. } = import.kind
{

View file

@ -1440,7 +1440,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|(key, name_resolution)| {
if key.ns == TypeNS
&& key.ident == ident
&& let Some(binding) = name_resolution.borrow().binding
&& let Some(binding) = name_resolution.borrow().best_binding()
{
match binding.res() {
// No disambiguation needed if the identically named item we
@ -1494,7 +1494,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
return None;
};
for (_, resolution) in this.resolutions(m).borrow().iter() {
let Some(binding) = resolution.borrow().binding else {
let Some(binding) = resolution.borrow().best_binding() else {
continue;
};
let Res::Def(DefKind::Macro(MacroKind::Derive | MacroKind::Attr), def_id) =

View file

@ -13,7 +13,7 @@ use rustc_span::{Ident, Span, kw, sym};
use tracing::{debug, instrument};
use crate::errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst};
use crate::imports::Import;
use crate::imports::{Import, NameResolution};
use crate::late::{ConstantHasGenerics, NoConstantGenericsReason, PathSource, Rib, RibKind};
use crate::macros::{MacroRulesScope, sub_namespace_match};
use crate::{
@ -37,7 +37,7 @@ impl From<UsePrelude> for bool {
}
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone, Copy)]
enum Shadowing {
Restricted,
Unrestricted,
@ -875,57 +875,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// binding if it exists. What we really want here is having two separate scopes in
// a module - one for non-globs and one for globs, but until that's done use this
// hack to avoid inconsistent resolution ICEs during import validation.
let binding = [resolution.binding, resolution.shadowed_glob]
let binding = [resolution.non_glob_binding, resolution.glob_binding]
.into_iter()
.find_map(|binding| if binding == ignore_binding { None } else { binding });
if let Some(Finalize { path_span, report_private, used, root_span, .. }) = finalize {
let Some(binding) = binding else {
return Err((Determined, Weak::No));
};
if !self.is_accessible_from(binding.vis, parent_scope.module) {
if report_private {
self.privacy_errors.push(PrivacyError {
ident,
binding,
dedup_span: path_span,
outermost_res: None,
parent_scope: *parent_scope,
single_nested: path_span != root_span,
});
} else {
return Err((Determined, Weak::No));
}
}
// Forbid expanded shadowing to avoid time travel.
if let Some(shadowed_glob) = resolution.shadowed_glob
&& shadowing == Shadowing::Restricted
&& binding.expansion != LocalExpnId::ROOT
&& binding.res() != shadowed_glob.res()
{
self.ambiguity_errors.push(AmbiguityError {
kind: AmbiguityKind::GlobVsExpanded,
ident,
b1: binding,
b2: shadowed_glob,
warning: false,
misc1: AmbiguityErrorMisc::None,
misc2: AmbiguityErrorMisc::None,
});
}
if shadowing == Shadowing::Unrestricted
&& binding.expansion != LocalExpnId::ROOT
&& let NameBindingKind::Import { import, .. } = binding.kind
&& matches!(import.kind, ImportKind::MacroExport)
{
self.macro_expanded_macro_export_errors.insert((path_span, binding.span));
}
self.record_use(ident, binding, used);
return Ok(binding);
if let Some(finalize) = finalize {
return self.finalize_module_binding(
ident,
binding,
if resolution.non_glob_binding.is_some() { resolution.glob_binding } else { None },
parent_scope,
finalize,
shadowing,
);
}
let check_usable = |this: &mut Self, binding: NameBinding<'ra>| {
@ -944,75 +906,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// Check if one of single imports can still define the name,
// if it can then our result is not determined and can be invalidated.
for single_import in &resolution.single_imports {
if ignore_import == Some(*single_import) {
// This branch handles a cycle in single imports.
//
// For example:
// ```
// use a::b;
// use b as a;
// ```
// 1. Record `use a::b` as the `ignore_import` and attempt to locate `a` in the
// current module.
// 2. Encounter the import `use b as a`, which is a `single_import` for `a`,
// and try to find `b` in the current module.
// 3. Re-encounter the `use a::b` import since it's a `single_import` of `b`.
// This leads to entering this branch.
continue;
}
if !self.is_accessible_from(single_import.vis, parent_scope.module) {
continue;
}
if let Some(ignored) = ignore_binding
&& let NameBindingKind::Import { import, .. } = ignored.kind
&& import == *single_import
{
// Ignore not just the binding itself, but if it has a shadowed_glob,
// ignore that, too, because this loop is supposed to only process
// named imports.
continue;
}
let Some(module) = single_import.imported_module.get() else {
return Err((Undetermined, Weak::No));
};
let ImportKind::Single { source, target, target_bindings, .. } = &single_import.kind
else {
unreachable!();
};
if source != target {
// This branch allows the binding to be defined or updated later if the target name
// can hide the source.
if target_bindings.iter().all(|binding| binding.get().is_none()) {
// None of the target bindings are available, so we can't determine
// if this binding is correct or not.
// See more details in #124840
return Err((Undetermined, Weak::No));
} else if target_bindings[ns].get().is_none() && binding.is_some() {
// `binding.is_some()` avoids the condition where the binding
// truly doesn't exist in this namespace and should return `Err(Determined)`.
return Err((Undetermined, Weak::No));
}
}
match self.resolve_ident_in_module(
module,
*source,
ns,
&single_import.parent_scope,
None,
ignore_binding,
ignore_import,
) {
Err((Determined, _)) => continue,
Ok(binding)
if !self.is_accessible_from(binding.vis, single_import.parent_scope.module) =>
{
continue;
}
Ok(_) | Err((Undetermined, _)) => return Err((Undetermined, Weak::No)),
}
if self.single_import_can_define_name(
&resolution,
binding,
ns,
ignore_import,
ignore_binding,
parent_scope,
) {
return Err((Undetermined, Weak::No));
}
// So we have a resolution that's from a glob import. This resolution is determined
@ -1101,6 +1003,128 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
Err((Determined, Weak::No))
}
fn finalize_module_binding(
&mut self,
ident: Ident,
binding: Option<NameBinding<'ra>>,
shadowed_glob: Option<NameBinding<'ra>>,
parent_scope: &ParentScope<'ra>,
finalize: Finalize,
shadowing: Shadowing,
) -> Result<NameBinding<'ra>, (Determinacy, Weak)> {
let Finalize { path_span, report_private, used, root_span, .. } = finalize;
let Some(binding) = binding else {
return Err((Determined, Weak::No));
};
if !self.is_accessible_from(binding.vis, parent_scope.module) {
if report_private {
self.privacy_errors.push(PrivacyError {
ident,
binding,
dedup_span: path_span,
outermost_res: None,
parent_scope: *parent_scope,
single_nested: path_span != root_span,
});
} else {
return Err((Determined, Weak::No));
}
}
// Forbid expanded shadowing to avoid time travel.
if let Some(shadowed_glob) = shadowed_glob
&& shadowing == Shadowing::Restricted
&& binding.expansion != LocalExpnId::ROOT
&& binding.res() != shadowed_glob.res()
{
self.ambiguity_errors.push(AmbiguityError {
kind: AmbiguityKind::GlobVsExpanded,
ident,
b1: binding,
b2: shadowed_glob,
warning: false,
misc1: AmbiguityErrorMisc::None,
misc2: AmbiguityErrorMisc::None,
});
}
if shadowing == Shadowing::Unrestricted
&& binding.expansion != LocalExpnId::ROOT
&& let NameBindingKind::Import { import, .. } = binding.kind
&& matches!(import.kind, ImportKind::MacroExport)
{
self.macro_expanded_macro_export_errors.insert((path_span, binding.span));
}
self.record_use(ident, binding, used);
return Ok(binding);
}
// Checks if a single import can define the `Ident` corresponding to `binding`.
// This is used to check whether we can definitively accept a glob as a resolution.
fn single_import_can_define_name(
&mut self,
resolution: &NameResolution<'ra>,
binding: Option<NameBinding<'ra>>,
ns: Namespace,
ignore_import: Option<Import<'ra>>,
ignore_binding: Option<NameBinding<'ra>>,
parent_scope: &ParentScope<'ra>,
) -> bool {
for single_import in &resolution.single_imports {
if ignore_import == Some(*single_import) {
continue;
}
if !self.is_accessible_from(single_import.vis, parent_scope.module) {
continue;
}
if let Some(ignored) = ignore_binding
&& let NameBindingKind::Import { import, .. } = ignored.kind
&& import == *single_import
{
continue;
}
let Some(module) = single_import.imported_module.get() else {
return true;
};
let ImportKind::Single { source, target, bindings, .. } = &single_import.kind else {
unreachable!();
};
if source != target {
if bindings.iter().all(|binding| binding.get().binding().is_none()) {
return true;
} else if bindings[ns].get().binding().is_none() && binding.is_some() {
return true;
}
}
match self.resolve_ident_in_module(
module,
*source,
ns,
&single_import.parent_scope,
None,
ignore_binding,
ignore_import,
) {
Err((Determined, _)) => continue,
Ok(binding)
if !self.is_accessible_from(binding.vis, single_import.parent_scope.module) =>
{
continue;
}
Ok(_) | Err((Undetermined, _)) => {
return true;
}
}
}
false
}
/// Validate a local resolution (from ribs).
#[instrument(level = "debug", skip(self, all_ribs))]
fn validate_res_from_ribs(

View file

@ -24,7 +24,6 @@ use rustc_span::{Ident, Span, Symbol, kw, sym};
use smallvec::SmallVec;
use tracing::debug;
use crate::Determinacy::{self, *};
use crate::Namespace::*;
use crate::diagnostics::{DiagMode, Suggestion, import_candidates};
use crate::errors::{
@ -33,13 +32,30 @@ use crate::errors::{
ConsiderAddingMacroExport, ConsiderMarkingAsPub,
};
use crate::{
AmbiguityError, AmbiguityKind, BindingKey, Finalize, ImportSuggestion, Module,
AmbiguityError, AmbiguityKind, BindingKey, Determinacy, Finalize, ImportSuggestion, Module,
ModuleOrUniformRoot, NameBinding, NameBindingData, NameBindingKind, ParentScope, PathResult,
PerNS, ResolutionError, Resolver, ScopeSet, Segment, Used, module_to_string, names_to_string,
};
type Res = def::Res<NodeId>;
/// A [`NameBinding`] in the process of being resolved.
#[derive(Clone, Copy, Default, PartialEq)]
pub(crate) enum PendingBinding<'ra> {
Ready(Option<NameBinding<'ra>>),
#[default]
Pending,
}
impl<'ra> PendingBinding<'ra> {
pub(crate) fn binding(self) -> Option<NameBinding<'ra>> {
match self {
PendingBinding::Ready(binding) => binding,
PendingBinding::Pending => None,
}
}
}
/// Contains data for specific kinds of imports.
#[derive(Clone)]
pub(crate) enum ImportKind<'ra> {
@ -49,10 +65,8 @@ pub(crate) enum ImportKind<'ra> {
/// `target` in `use prefix::source as target`.
/// It will directly use `source` when the format is `use prefix::source`.
target: Ident,
/// Bindings to which `source` refers to.
source_bindings: PerNS<Cell<Result<NameBinding<'ra>, Determinacy>>>,
/// Bindings introduced by `target`.
target_bindings: PerNS<Cell<Option<NameBinding<'ra>>>>,
/// Bindings introduced by the import.
bindings: PerNS<Cell<PendingBinding<'ra>>>,
/// `true` for `...::{self [as target]}` imports, `false` otherwise.
type_ns_only: bool,
/// Did this import result from a nested import? ie. `use foo::{bar, baz};`
@ -96,26 +110,14 @@ impl<'ra> std::fmt::Debug for ImportKind<'ra> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use ImportKind::*;
match self {
Single {
source,
target,
source_bindings,
target_bindings,
type_ns_only,
nested,
id,
} => f
Single { source, target, bindings, type_ns_only, nested, id, .. } => f
.debug_struct("Single")
.field("source", source)
.field("target", target)
// Ignore the nested bindings to avoid an infinite loop while printing.
.field(
"source_bindings",
&source_bindings.clone().map(|b| b.into_inner().map(|_| format_args!(".."))),
)
.field(
"target_bindings",
&target_bindings.clone().map(|b| b.into_inner().map(|_| format_args!(".."))),
"bindings",
&bindings.clone().map(|b| b.into_inner().binding().map(|_| format_args!(".."))),
)
.field("type_ns_only", type_ns_only)
.field("nested", nested)
@ -242,15 +244,16 @@ pub(crate) struct NameResolution<'ra> {
/// Single imports that may define the name in the namespace.
/// Imports are arena-allocated, so it's ok to use pointers as keys.
pub single_imports: FxIndexSet<Import<'ra>>,
/// The least shadowable known binding for this name, or None if there are no known bindings.
pub binding: Option<NameBinding<'ra>>,
pub shadowed_glob: Option<NameBinding<'ra>>,
/// The non-glob binding for this name, if it is known to exist.
pub non_glob_binding: Option<NameBinding<'ra>>,
/// The glob binding for this name, if it is known to exist.
pub glob_binding: Option<NameBinding<'ra>>,
}
impl<'ra> NameResolution<'ra> {
/// Returns the binding for the name if it is known or None if it not known.
pub(crate) fn binding(&self) -> Option<NameBinding<'ra>> {
self.binding.and_then(|binding| {
self.best_binding().and_then(|binding| {
if !binding.is_glob_import() || self.single_imports.is_empty() {
Some(binding)
} else {
@ -258,6 +261,10 @@ impl<'ra> NameResolution<'ra> {
}
})
}
pub(crate) fn best_binding(&self) -> Option<NameBinding<'ra>> {
self.non_glob_binding.or(self.glob_binding)
}
}
/// An error that may be transformed into a diagnostic later. Used to combine multiple unresolved
@ -338,69 +345,71 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
self.check_reserved_macro_name(key.ident, res);
self.set_binding_parent_module(binding, module);
self.update_resolution(module, key, warn_ambiguity, |this, resolution| {
if let Some(old_binding) = resolution.binding {
if let Some(old_binding) = resolution.best_binding() {
if res == Res::Err && old_binding.res() != Res::Err {
// Do not override real bindings with `Res::Err`s from error recovery.
return Ok(());
}
match (old_binding.is_glob_import(), binding.is_glob_import()) {
(true, true) => {
let (glob_binding, old_glob_binding) = (binding, old_binding);
// FIXME: remove `!binding.is_ambiguity_recursive()` after delete the warning ambiguity.
if !binding.is_ambiguity_recursive()
&& let NameBindingKind::Import { import: old_import, .. } =
old_binding.kind
&& let NameBindingKind::Import { import, .. } = binding.kind
old_glob_binding.kind
&& let NameBindingKind::Import { import, .. } = glob_binding.kind
&& old_import == import
{
// We should replace the `old_binding` with `binding` regardless
// of whether they has same resolution or not when they are
// imported from the same glob-import statement.
resolution.binding = Some(binding);
} else if res != old_binding.res() {
resolution.binding = Some(this.new_ambiguity_binding(
// When imported from the same glob-import statement, we should replace
// `old_glob_binding` with `glob_binding`, regardless of whether
// they have the same resolution or not.
resolution.glob_binding = Some(glob_binding);
} else if res != old_glob_binding.res() {
resolution.glob_binding = Some(this.new_ambiguity_binding(
AmbiguityKind::GlobVsGlob,
old_binding,
binding,
old_glob_binding,
glob_binding,
warn_ambiguity,
));
} else if !old_binding.vis.is_at_least(binding.vis, this.tcx) {
// We are glob-importing the same item but with greater visibility.
resolution.binding = Some(binding);
resolution.glob_binding = Some(glob_binding);
} else if binding.is_ambiguity_recursive() {
resolution.binding = Some(this.new_warn_ambiguity_binding(binding));
resolution.glob_binding =
Some(this.new_warn_ambiguity_binding(glob_binding));
}
}
(old_glob @ true, false) | (old_glob @ false, true) => {
let (glob_binding, nonglob_binding) =
let (glob_binding, non_glob_binding) =
if old_glob { (old_binding, binding) } else { (binding, old_binding) };
if key.ns == MacroNS
&& nonglob_binding.expansion != LocalExpnId::ROOT
&& glob_binding.res() != nonglob_binding.res()
&& non_glob_binding.expansion != LocalExpnId::ROOT
&& glob_binding.res() != non_glob_binding.res()
{
resolution.binding = Some(this.new_ambiguity_binding(
resolution.non_glob_binding = Some(this.new_ambiguity_binding(
AmbiguityKind::GlobVsExpanded,
nonglob_binding,
non_glob_binding,
glob_binding,
false,
));
} else {
resolution.binding = Some(nonglob_binding);
resolution.non_glob_binding = Some(non_glob_binding);
}
if let Some(old_shadowed_glob) = resolution.shadowed_glob {
assert!(old_shadowed_glob.is_glob_import());
if glob_binding.res() != old_shadowed_glob.res() {
resolution.shadowed_glob = Some(this.new_ambiguity_binding(
if let Some(old_glob_binding) = resolution.glob_binding {
assert!(old_glob_binding.is_glob_import());
if glob_binding.res() != old_glob_binding.res() {
resolution.glob_binding = Some(this.new_ambiguity_binding(
AmbiguityKind::GlobVsGlob,
old_shadowed_glob,
old_glob_binding,
glob_binding,
false,
));
} else if !old_shadowed_glob.vis.is_at_least(binding.vis, this.tcx) {
resolution.shadowed_glob = Some(glob_binding);
} else if !old_glob_binding.vis.is_at_least(binding.vis, this.tcx) {
resolution.glob_binding = Some(glob_binding);
}
} else {
resolution.shadowed_glob = Some(glob_binding);
resolution.glob_binding = Some(glob_binding);
}
}
(false, false) => {
@ -408,7 +417,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
}
} else {
resolution.binding = Some(binding);
if binding.is_glob_import() {
resolution.glob_binding = Some(binding);
} else {
resolution.non_glob_binding = Some(binding);
}
}
Ok(())
@ -491,8 +504,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// Define a dummy resolution containing a `Res::Err` as a placeholder for a failed
// or indeterminate resolution, also mark such failed imports as used to avoid duplicate diagnostics.
fn import_dummy_binding(&mut self, import: Import<'ra>, is_indeterminate: bool) {
if let ImportKind::Single { target, ref target_bindings, .. } = import.kind {
if !(is_indeterminate || target_bindings.iter().all(|binding| binding.get().is_none()))
if let ImportKind::Single { target, ref bindings, .. } = import.kind {
if !(is_indeterminate
|| bindings.iter().all(|binding| binding.get().binding().is_none()))
{
return; // Has resolution, do not create the dummy binding
}
@ -567,10 +581,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
glob_error |= import.is_glob();
if let ImportKind::Single { source, ref source_bindings, .. } = import.kind
if let ImportKind::Single { source, ref bindings, .. } = import.kind
&& source.name == kw::SelfLower
// Silence `unresolved import` error if E0429 is already emitted
&& let Err(Determined) = source_bindings.value_ns.get()
&& let PendingBinding::Ready(None) = bindings.value_ns.get()
{
continue;
}
@ -628,7 +642,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
for (key, resolution) in self.resolutions(*module).borrow().iter() {
let resolution = resolution.borrow();
let Some(binding) = resolution.binding else { continue };
let Some(binding) = resolution.best_binding() else { continue };
if let NameBindingKind::Import { import, .. } = binding.kind
&& let Some((amb_binding, _)) = binding.ambiguity
@ -648,7 +662,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
);
}
if let Some(glob_binding) = resolution.shadowed_glob {
if let Some(glob_binding) = resolution.glob_binding
&& resolution.non_glob_binding.is_some()
{
if binding.res() != Res::Err
&& glob_binding.res() != Res::Err
&& let NameBindingKind::Import { import: glob_import, .. } =
@ -819,15 +835,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
};
import.imported_module.set(Some(module));
let (source, target, source_bindings, target_bindings, type_ns_only) = match import.kind {
ImportKind::Single {
source,
target,
ref source_bindings,
ref target_bindings,
type_ns_only,
..
} => (source, target, source_bindings, target_bindings, type_ns_only),
let (source, target, bindings, type_ns_only) = match import.kind {
ImportKind::Single { source, target, ref bindings, type_ns_only, .. } => {
(source, target, bindings, type_ns_only)
}
ImportKind::Glob { .. } => {
self.resolve_glob_import(import);
return 0;
@ -838,21 +849,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let mut indeterminate_count = 0;
self.per_ns(|this, ns| {
if !type_ns_only || ns == TypeNS {
if let Err(Undetermined) = source_bindings[ns].get() {
let binding = this.maybe_resolve_ident_in_module(
module,
source,
ns,
&import.parent_scope,
Some(import),
);
source_bindings[ns].set(binding);
} else {
if bindings[ns].get() != PendingBinding::Pending {
return;
};
let binding_result = this.maybe_resolve_ident_in_module(
module,
source,
ns,
&import.parent_scope,
Some(import),
);
let parent = import.parent_scope.module;
match source_bindings[ns].get() {
let binding = match binding_result {
Ok(binding) => {
if binding.is_assoc_item()
&& !this.tcx.features().import_trait_associated_functions()
@ -865,12 +873,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
)
.emit();
}
// We need the `target`, `source` can be extracted.
let imported_binding = this.import(binding, import);
target_bindings[ns].set(Some(imported_binding));
this.define(parent, target, ns, imported_binding);
PendingBinding::Ready(Some(imported_binding))
}
Err(Determined) => {
Err(Determinacy::Determined) => {
// Don't update the resolution for underscores, because it was never added.
if target.name != kw::Underscore {
let key = BindingKey::new(target, ns);
@ -878,9 +886,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
resolution.single_imports.swap_remove(&import);
});
}
PendingBinding::Ready(None)
}
Err(Undetermined) => indeterminate_count += 1,
}
Err(Determinacy::Undetermined) => {
indeterminate_count += 1;
PendingBinding::Pending
}
};
bindings[ns].set(binding);
}
});
@ -893,7 +906,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
/// consolidate multiple unresolved import errors into a single diagnostic.
fn finalize_import(&mut self, import: Import<'ra>) -> Option<UnresolvedImportError> {
let ignore_binding = match &import.kind {
ImportKind::Single { target_bindings, .. } => target_bindings[TypeNS].get(),
ImportKind::Single { bindings, .. } => bindings[TypeNS].get().binding(),
_ => None,
};
let ambiguity_errors_len =
@ -1011,60 +1024,53 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
PathResult::Indeterminate => unreachable!(),
};
let (ident, target, source_bindings, target_bindings, type_ns_only, import_id) =
match import.kind {
ImportKind::Single {
source,
target,
ref source_bindings,
ref target_bindings,
type_ns_only,
id,
..
} => (source, target, source_bindings, target_bindings, type_ns_only, id),
ImportKind::Glob { is_prelude, ref max_vis, id } => {
if import.module_path.len() <= 1 {
// HACK(eddyb) `lint_if_path_starts_with_module` needs at least
// 2 segments, so the `resolve_path` above won't trigger it.
let mut full_path = import.module_path.clone();
full_path.push(Segment::from_ident(Ident::dummy()));
self.lint_if_path_starts_with_module(Some(finalize), &full_path, None);
}
if let ModuleOrUniformRoot::Module(module) = module
&& module == import.parent_scope.module
{
// Importing a module into itself is not allowed.
return Some(UnresolvedImportError {
span: import.span,
label: Some(String::from("cannot glob-import a module into itself")),
note: None,
suggestion: None,
candidates: None,
segment: None,
module: None,
});
}
if !is_prelude
&& let Some(max_vis) = max_vis.get()
&& !max_vis.is_at_least(import.vis, self.tcx)
{
let def_id = self.local_def_id(id);
self.lint_buffer.buffer_lint(
UNUSED_IMPORTS,
id,
import.span,
BuiltinLintDiag::RedundantImportVisibility {
max_vis: max_vis.to_string(def_id, self.tcx),
import_vis: import.vis.to_string(def_id, self.tcx),
span: import.span,
},
);
}
return None;
let (ident, target, bindings, type_ns_only, import_id) = match import.kind {
ImportKind::Single { source, target, ref bindings, type_ns_only, id, .. } => {
(source, target, bindings, type_ns_only, id)
}
ImportKind::Glob { is_prelude, ref max_vis, id } => {
if import.module_path.len() <= 1 {
// HACK(eddyb) `lint_if_path_starts_with_module` needs at least
// 2 segments, so the `resolve_path` above won't trigger it.
let mut full_path = import.module_path.clone();
full_path.push(Segment::from_ident(Ident::dummy()));
self.lint_if_path_starts_with_module(Some(finalize), &full_path, None);
}
_ => unreachable!(),
};
if let ModuleOrUniformRoot::Module(module) = module
&& module == import.parent_scope.module
{
// Importing a module into itself is not allowed.
return Some(UnresolvedImportError {
span: import.span,
label: Some(String::from("cannot glob-import a module into itself")),
note: None,
suggestion: None,
candidates: None,
segment: None,
module: None,
});
}
if !is_prelude
&& let Some(max_vis) = max_vis.get()
&& !max_vis.is_at_least(import.vis, self.tcx)
{
let def_id = self.local_def_id(id);
self.lint_buffer.buffer_lint(
UNUSED_IMPORTS,
id,
import.span,
BuiltinLintDiag::RedundantImportVisibility {
max_vis: max_vis.to_string(def_id, self.tcx),
import_vis: import.vis.to_string(def_id, self.tcx),
span: import.span,
},
);
}
return None;
}
_ => unreachable!(),
};
if self.privacy_errors.len() != privacy_errors_len {
// Get the Res for the last element, so that we can point to alternative ways of
@ -1095,17 +1101,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
ns,
&import.parent_scope,
Some(Finalize { report_private: false, ..finalize }),
target_bindings[ns].get(),
bindings[ns].get().binding(),
Some(import),
);
match binding {
Ok(binding) => {
// Consistency checks, analogous to `finalize_macro_resolutions`.
let initial_res = source_bindings[ns].get().map(|initial_binding| {
let initial_res = bindings[ns].get().binding().map(|binding| {
let initial_binding = binding.import_source();
all_ns_err = false;
if let Some(target_binding) = target_bindings[ns].get()
&& target.name == kw::Underscore
if target.name == kw::Underscore
&& initial_binding.is_extern_crate()
&& !initial_binding.is_import()
{
@ -1114,7 +1120,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
} else {
Used::Other
};
this.record_use(ident, target_binding, used);
this.record_use(ident, binding, used);
}
initial_binding.res()
});
@ -1126,7 +1132,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
.span_delayed_bug(import.span, "some error happened for an import");
return;
}
if let Ok(initial_res) = initial_res {
if let Some(initial_res) = initial_res {
if res != initial_res {
span_bug!(import.span, "inconsistent resolution for an import");
}
@ -1179,7 +1185,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
return None;
} // Never suggest the same name
match *resolution.borrow() {
NameResolution { binding: Some(name_binding), .. } => {
ref resolution
if let Some(name_binding) = resolution.best_binding() =>
{
match name_binding.kind {
NameBindingKind::Import { binding, .. } => {
match binding.kind {
@ -1269,7 +1277,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let mut any_successful_reexport = false;
let mut crate_private_reexport = false;
self.per_ns(|this, ns| {
let Ok(binding) = source_bindings[ns].get() else {
let Some(binding) = bindings[ns].get().binding().map(|b| b.import_source()) else {
return;
};
@ -1340,7 +1348,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let mut full_path = import.module_path.clone();
full_path.push(Segment::from_ident(ident));
self.per_ns(|this, ns| {
if let Ok(binding) = source_bindings[ns].get() {
if let Some(binding) = bindings[ns].get().binding().map(|b| b.import_source()) {
this.lint_if_path_starts_with_module(Some(finalize), &full_path, Some(binding));
}
});
@ -1350,7 +1358,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// this may resolve to either a value or a type, but for documentation
// purposes it's good enough to just favor one over the other.
self.per_ns(|this, ns| {
if let Ok(binding) = source_bindings[ns].get() {
if let Some(binding) = bindings[ns].get().binding().map(|b| b.import_source()) {
this.import_res_map.entry(import_id).or_default()[ns] = Some(binding.res());
}
});
@ -1361,10 +1369,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'ra>) -> bool {
// This function is only called for single imports.
let ImportKind::Single {
source, target, ref source_bindings, ref target_bindings, id, ..
} = import.kind
else {
let ImportKind::Single { source, target, ref bindings, id, .. } = import.kind else {
unreachable!()
};
@ -1391,7 +1396,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let mut is_redundant = true;
let mut redundant_span = PerNS { value_ns: None, type_ns: None, macro_ns: None };
self.per_ns(|this, ns| {
if is_redundant && let Ok(binding) = source_bindings[ns].get() {
let binding = bindings[ns].get().binding().map(|b| b.import_source());
if is_redundant && let Some(binding) = binding {
if binding.res() == Res::Err {
return;
}
@ -1402,7 +1408,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
&import.parent_scope,
None,
false,
target_bindings[ns].get(),
bindings[ns].get().binding(),
None,
) {
Ok(other_binding) => {

View file

@ -3437,7 +3437,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
};
ident.span.normalize_to_macros_2_0_and_adjust(module.expansion);
let key = BindingKey::new(ident, ns);
let mut binding = self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.binding);
let mut binding =
self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.best_binding());
debug!(?binding);
if binding.is_none() {
// We could not find the trait item in the correct namespace.
@ -3448,7 +3449,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
_ => ns,
};
let key = BindingKey::new(ident, ns);
binding = self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.binding);
binding =
self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.best_binding());
debug!(?binding);
}

View file

@ -880,8 +880,10 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
fn lookup_doc_alias_name(&mut self, path: &[Segment], ns: Namespace) -> Option<(DefId, Ident)> {
let find_doc_alias_name = |r: &mut Resolver<'ra, '_>, m: Module<'ra>, item_name: Symbol| {
for resolution in r.resolutions(m).borrow().values() {
let Some(did) =
resolution.borrow().binding.and_then(|binding| binding.res().opt_def_id())
let Some(did) = resolution
.borrow()
.best_binding()
.and_then(|binding| binding.res().opt_def_id())
else {
continue;
};
@ -1464,15 +1466,16 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
self.resolve_path(mod_path, None, None)
{
let resolutions = self.r.resolutions(module).borrow();
let targets: Vec<_> =
resolutions
.iter()
.filter_map(|(key, resolution)| {
resolution.borrow().binding.map(|binding| binding.res()).and_then(
|res| if filter_fn(res) { Some((key, res)) } else { None },
)
})
.collect();
let targets: Vec<_> = resolutions
.iter()
.filter_map(|(key, resolution)| {
resolution
.borrow()
.best_binding()
.map(|binding| binding.res())
.and_then(|res| if filter_fn(res) { Some((key, res)) } else { None })
})
.collect();
if let [target] = targets.as_slice() {
return Some(TypoSuggestion::single_item_from_ident(target.0.ident, target.1));
}
@ -2305,7 +2308,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
let targets = resolutions
.borrow()
.iter()
.filter_map(|(key, res)| res.borrow().binding.map(|binding| (key, binding.res())))
.filter_map(|(key, res)| {
res.borrow().best_binding().map(|binding| (key, binding.res()))
})
.filter(|(_, res)| match (kind, res) {
(AssocItemKind::Const(..), Res::Def(DefKind::AssocConst, _)) => true,
(AssocItemKind::Fn(_), Res::Def(DefKind::AssocFn, _)) => true,

View file

@ -641,7 +641,7 @@ impl<'ra> Module<'ra> {
F: FnMut(&mut R, Ident, Namespace, NameBinding<'ra>),
{
for (key, name_resolution) in resolver.as_mut().resolutions(self).borrow().iter() {
if let Some(binding) = name_resolution.borrow().binding {
if let Some(binding) = name_resolution.borrow().best_binding() {
f(resolver, key.ident, key.ns, binding);
}
}
@ -891,6 +891,13 @@ impl<'ra> NameBindingData<'ra> {
}
}
fn import_source(&self) -> NameBinding<'ra> {
match self.kind {
NameBindingKind::Import { binding, .. } => binding,
_ => unreachable!(),
}
}
fn is_ambiguity_recursive(&self) -> bool {
self.ambiguity.is_some()
|| match self.kind {

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