1
Fork 0

Auto merge of #139169 - matthiaskrgr:rollup-nfy4aew, r=matthiaskrgr

Rollup of 6 pull requests

Successful merges:

 - #138176 (Prefer built-in sized impls (and only sized impls) for rigid types always)
 - #138749 (Fix closure recovery for missing block when return type is specified)
 - #138842 (Emit `unused_attributes` for `#[inline]` on exported functions)
 - #139153 (Encode synthetic by-move coroutine body with a different `DefPathData`)
 - #139157 (Remove mention of `exhaustive_patterns` from `never` docs)
 - #139167 (Remove Amanieu from the libs review rotation)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-03-31 15:10:21 +00:00
commit 0b45675cfc
43 changed files with 481 additions and 76 deletions

View file

@ -294,7 +294,7 @@ impl DefKind {
DefKind::GlobalAsm => DefPathData::GlobalAsm,
DefKind::Impl { .. } => DefPathData::Impl,
DefKind::Closure => DefPathData::Closure,
DefKind::SyntheticCoroutineBody => DefPathData::Closure,
DefKind::SyntheticCoroutineBody => DefPathData::SyntheticCoroutineBody,
}
}

View file

@ -291,6 +291,8 @@ pub enum DefPathData {
/// An existential `impl Trait` type node.
/// Argument position `impl Trait` have a `TypeNs` with their pretty-printed name.
OpaqueTy,
/// A synthetic body for a coroutine's by-move body.
SyntheticCoroutineBody,
}
impl Definitions {
@ -415,8 +417,16 @@ impl DefPathData {
ValueNs(name) | MacroNs(name) | LifetimeNs(name) => Some(name),
Impl | ForeignMod | CrateRoot | Use | GlobalAsm | Closure | Ctor | AnonConst
| OpaqueTy => None,
Impl
| ForeignMod
| CrateRoot
| Use
| GlobalAsm
| Closure
| Ctor
| AnonConst
| OpaqueTy
| SyntheticCoroutineBody => None,
}
}
@ -441,6 +451,7 @@ impl DefPathData {
Ctor => DefPathDataName::Anon { namespace: sym::constructor },
AnonConst => DefPathDataName::Anon { namespace: sym::constant },
OpaqueTy => DefPathDataName::Anon { namespace: sym::opaque },
SyntheticCoroutineBody => DefPathDataName::Anon { namespace: sym::synthetic },
}
}
}

View file

@ -172,6 +172,8 @@ impl CodegenFnAttrs {
/// * `#[no_mangle]` is present
/// * `#[export_name(...)]` is present
/// * `#[linkage]` is present
///
/// Keep this in sync with the logic for the unused_attributes for `#[inline]` lint.
pub fn contains_extern_indicator(&self) -> bool {
self.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
|| self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)

View file

@ -150,6 +150,7 @@ impl<'tcx> MonoItem<'tcx> {
// If the function is #[naked] or contains any other attribute that requires exactly-once
// instantiation:
// We emit an unused_attributes lint for this case, which should be kept in sync if possible.
let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id());
if codegen_fn_attrs.contains_extern_indicator()
|| codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED)

View file

@ -95,10 +95,16 @@ pub type EvaluationCache<'tcx, ENV> = Cache<(ENV, ty::PolyTraitPredicate<'tcx>),
/// parameter environment.
#[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)]
pub enum SelectionCandidate<'tcx> {
/// A built-in implementation for the `Sized` trait. This is preferred
/// over all other candidates.
SizedCandidate {
has_nested: bool,
},
/// A builtin implementation for some specific traits, used in cases
/// where we cannot rely an ordinary library implementations.
///
/// The most notable examples are `sized`, `Copy` and `Clone`. This is also
/// The most notable examples are `Copy` and `Clone`. This is also
/// used for the `DiscriminantKind` and `Pointee` trait, both of which have
/// an associated type.
BuiltinCandidate {

View file

@ -1930,10 +1930,10 @@ impl<'tcx> TyCtxt<'tcx> {
// As a consequence, this LocalDefId is always re-created before it is needed by the incr.
// comp. engine itself.
//
// This call also writes to the value of `source_span` and `expn_that_defined` queries.
// This call also writes to the value of the `source_span` query.
// This is fine because:
// - those queries are `eval_always` so we won't miss their result changing;
// - this write will have happened before these queries are called.
// - that query is `eval_always` so we won't miss its result changing;
// - this write will have happened before that query is called.
let def_id = self.untracked.definitions.write().create_def(parent, data);
// This function modifies `self.definitions` using a side-effect.

View file

@ -139,8 +139,7 @@ pub trait Printer<'tcx>: Sized {
match key.disambiguated_data.data {
DefPathData::Closure => {
// FIXME(async_closures): This is somewhat ugly.
// We need to additionally print the `kind` field of a closure if
// We need to additionally print the `kind` field of a coroutine if
// it is desugared from a coroutine-closure.
if let Some(hir::CoroutineKind::Desugared(
_,
@ -156,6 +155,10 @@ pub trait Printer<'tcx>: Sized {
// Closures' own generics are only captures, don't print them.
}
}
DefPathData::SyntheticCoroutineBody => {
// Synthetic coroutine bodies have no distinct generics, since like
// closures they're all just internal state of the coroutine.
}
// This covers both `DefKind::AnonConst` and `DefKind::InlineConst`.
// Anon consts doesn't have their own generics, and inline consts' own
// generics are their inferred types, so don't print them.

View file

@ -810,16 +810,16 @@ pub(crate) enum WrapInParentheses {
#[derive(Diagnostic)]
#[diag(parse_array_brackets_instead_of_braces)]
pub(crate) struct ArrayBracketsInsteadOfSpaces {
pub(crate) struct ArrayBracketsInsteadOfBraces {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sub: ArrayBracketsInsteadOfSpacesSugg,
pub sub: ArrayBracketsInsteadOfBracesSugg,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")]
pub(crate) struct ArrayBracketsInsteadOfSpacesSugg {
pub(crate) struct ArrayBracketsInsteadOfBracesSugg {
#[suggestion_part(code = "[")]
pub left: Span,
#[suggestion_part(code = "]")]

View file

@ -2200,7 +2200,9 @@ impl<'a> Parser<'a> {
}
fn is_array_like_block(&mut self) -> bool {
self.look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_)))
matches!(self.token.kind, TokenKind::OpenDelim(Delimiter::Brace))
&& self
.look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_)))
&& self.look_ahead(2, |t| t == &token::Comma)
&& self.look_ahead(3, |t| t.can_begin_expr())
}
@ -2212,9 +2214,9 @@ impl<'a> Parser<'a> {
let mut snapshot = self.create_snapshot_for_diagnostic();
match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) {
Ok(arr) => {
let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfSpaces {
let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfBraces {
span: arr.span,
sub: errors::ArrayBracketsInsteadOfSpacesSugg {
sub: errors::ArrayBracketsInsteadOfBracesSugg {
left: lo,
right: snapshot.prev_token.span,
},
@ -2337,7 +2339,8 @@ impl<'a> Parser<'a> {
let capture_clause = self.parse_capture_clause()?;
let (fn_decl, fn_arg_span) = self.parse_fn_block_decl()?;
let decl_hi = self.prev_token.span;
let mut body = match fn_decl.output {
let mut body = match &fn_decl.output {
// No return type.
FnRetTy::Default(_) => {
let restrictions =
self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET;
@ -2349,11 +2352,8 @@ impl<'a> Parser<'a> {
Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?,
}
}
_ => {
// If an explicit return type is given, require a block to appear (RFC 968).
let body_lo = self.token.span;
self.parse_expr_block(None, body_lo, BlockCheckMode::Default)?
}
// Explicit return type (`->`) needs block `-> T { }`.
FnRetTy::Ty(ty) => self.parse_closure_block_body(ty.span)?,
};
match coroutine_kind {
@ -2405,6 +2405,49 @@ impl<'a> Parser<'a> {
Ok(closure)
}
/// If an explicit return type is given, require a block to appear (RFC 968).
fn parse_closure_block_body(&mut self, ret_span: Span) -> PResult<'a, P<Expr>> {
if self.may_recover()
&& self.token.can_begin_expr()
&& !matches!(self.token.kind, TokenKind::OpenDelim(Delimiter::Brace))
&& !self.token.is_whole_block()
{
let snapshot = self.create_snapshot_for_diagnostic();
let restrictions =
self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET;
let tok = self.token.clone();
match self.parse_expr_res(restrictions, AttrWrapper::empty()) {
Ok((expr, _)) => {
let descr = super::token_descr(&tok);
let mut diag = self
.dcx()
.struct_span_err(tok.span, format!("expected `{{`, found {descr}"));
diag.span_label(
ret_span,
"explicit return type requires closure body to be enclosed in braces",
);
diag.multipart_suggestion_verbose(
"wrap the expression in curly braces",
vec![
(expr.span.shrink_to_lo(), "{ ".to_string()),
(expr.span.shrink_to_hi(), " }".to_string()),
],
Applicability::MachineApplicable,
);
diag.emit();
return Ok(expr);
}
Err(diag) => {
diag.cancel();
self.restore_snapshot(snapshot);
}
}
}
let body_lo = self.token.span;
self.parse_expr_block(None, body_lo, BlockCheckMode::Default)
}
/// Parses an optional `move` or `use` prefix to a closure-like construct.
fn parse_capture_clause(&mut self) -> PResult<'a, CaptureBy> {
if self.eat_keyword(exp!(Move)) {

View file

@ -383,6 +383,10 @@ passes_inline_ignored_constants =
.warn = {-passes_previously_accepted}
.note = {-passes_see_issue(issue: "65833")}
passes_inline_ignored_for_exported =
`#[inline]` is ignored on externally exported functions
.help = externally exported functions are functions with `#[no_mangle]`, `#[export_name]`, or `#[linkage]`
passes_inline_ignored_function_prototype =
`#[inline]` is ignored on function prototypes

View file

@ -451,6 +451,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
});
}
}
// `#[inline]` is ignored if the symbol must be codegened upstream because it's exported.
if let Some(did) = hir_id.as_owner()
&& self.tcx.def_kind(did).has_codegen_attrs()
&& !matches!(attr.meta_item_list().as_deref(), Some([item]) if item.has_name(sym::never))
{
let attrs = self.tcx.codegen_fn_attrs(did);
// Not checking naked as `#[inline]` is forbidden for naked functions anyways.
if attrs.contains_extern_indicator() {
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span(),
errors::InlineIgnoredForExported {},
);
}
}
}
/// Checks that `#[coverage(..)]` is applied to a function/closure/method,

View file

@ -1441,6 +1441,11 @@ pub(crate) struct OnlyHasEffectOn {
pub target_name: String,
}
#[derive(LintDiagnostic)]
#[diag(passes_inline_ignored_for_exported)]
#[help]
pub(crate) struct InlineIgnoredForExported {}
#[derive(Diagnostic)]
#[diag(passes_object_lifetime_err)]
pub(crate) struct ObjectLifetimeErr {

View file

@ -66,6 +66,7 @@ pub struct MarkFrame<'a> {
parent: Option<&'a MarkFrame<'a>>,
}
#[derive(Debug)]
pub(super) enum DepNodeColor {
Red,
Green(DepNodeIndex),
@ -909,7 +910,7 @@ impl<D: Deps> DepGraphData<D> {
self.try_mark_previous_green(qcx, parent_dep_node_index, dep_dep_node, frame);
if node_index.is_some() {
debug!("managed to MARK dependency {dep_dep_node:?} as green",);
debug!("managed to MARK dependency {dep_dep_node:?} as green");
return Some(());
}
}
@ -930,7 +931,7 @@ impl<D: Deps> DepGraphData<D> {
return Some(());
}
Some(DepNodeColor::Red) => {
debug!("dependency {dep_dep_node:?} was red after forcing",);
debug!("dependency {dep_dep_node:?} was red after forcing");
return None;
}
None => {}
@ -950,7 +951,7 @@ impl<D: Deps> DepGraphData<D> {
// invalid state will not be persisted to the
// incremental compilation cache because of
// compilation errors being present.
debug!("dependency {dep_dep_node:?} resulted in compilation error",);
debug!("dependency {dep_dep_node:?} resulted in compilation error");
return None;
}

View file

@ -716,6 +716,7 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
hir::definitions::DefPathData::Ctor => "c",
hir::definitions::DefPathData::AnonConst => "k",
hir::definitions::DefPathData::OpaqueTy => "i",
hir::definitions::DefPathData::SyntheticCoroutineBody => "s",
hir::definitions::DefPathData::CrateRoot
| hir::definitions::DefPathData::Use
| hir::definitions::DefPathData::GlobalAsm

View file

@ -28,7 +28,10 @@ pub(super) fn mangle<'tcx>(
loop {
let key = tcx.def_key(ty_def_id);
match key.disambiguated_data.data {
DefPathData::TypeNs(_) | DefPathData::ValueNs(_) | DefPathData::Closure => {
DefPathData::TypeNs(_)
| DefPathData::ValueNs(_)
| DefPathData::Closure
| DefPathData::SyntheticCoroutineBody => {
instance_ty = tcx.type_of(ty_def_id).instantiate_identity();
debug!(?instance_ty);
break;

View file

@ -850,6 +850,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
DefPathData::Ctor => 'c',
DefPathData::AnonConst => 'k',
DefPathData::OpaqueTy => 'i',
DefPathData::SyntheticCoroutineBody => 's',
// These should never show up as `path_append` arguments.
DefPathData::CrateRoot

View file

@ -86,10 +86,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// `Pointee` is automatically implemented for every type.
candidates.vec.push(BuiltinCandidate { has_nested: false });
} else if tcx.is_lang_item(def_id, LangItem::Sized) {
// Sized is never implementable by end-users, it is
// always automatically computed.
let sized_conditions = self.sized_conditions(obligation);
self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates);
self.assemble_builtin_sized_candidate(obligation, &mut candidates);
} else if tcx.is_lang_item(def_id, LangItem::Unsize) {
self.assemble_candidates_for_unsizing(obligation, &mut candidates);
} else if tcx.is_lang_item(def_id, LangItem::Destruct) {
@ -1061,6 +1058,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// Assembles the trait which are built-in to the language itself:
/// `Copy`, `Clone` and `Sized`.
#[instrument(level = "debug", skip(self, candidates))]
fn assemble_builtin_sized_candidate(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
match self.sized_conditions(obligation) {
BuiltinImplConditions::Where(nested) => {
candidates
.vec
.push(SizedCandidate { has_nested: !nested.skip_binder().is_empty() });
}
BuiltinImplConditions::None => {}
BuiltinImplConditions::Ambiguous => {
candidates.ambiguous = true;
}
}
}
/// Assembles the trait which are built-in to the language itself:
/// e.g. `Copy` and `Clone`.
#[instrument(level = "debug", skip(self, candidates))]
fn assemble_builtin_bound_candidates(
&mut self,
conditions: BuiltinImplConditions<'tcx>,

View file

@ -40,6 +40,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
candidate: SelectionCandidate<'tcx>,
) -> Result<Selection<'tcx>, SelectionError<'tcx>> {
let mut impl_src = match candidate {
SizedCandidate { has_nested } => {
let data = self.confirm_builtin_candidate(obligation, has_nested);
ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
BuiltinCandidate { has_nested } => {
let data = self.confirm_builtin_candidate(obligation, has_nested);
ImplSource::Builtin(BuiltinImplSource::Misc, data)

View file

@ -1801,17 +1801,21 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
return Some(candidates.pop().unwrap().candidate);
}
// We prefer trivial builtin candidates, i.e. builtin impls without any nested
// requirements, over all others. This is a fix for #53123 and prevents winnowing
// from accidentally extending the lifetime of a variable.
let mut trivial_builtin = candidates
.iter()
.filter(|c| matches!(c.candidate, BuiltinCandidate { has_nested: false }));
if let Some(_trivial) = trivial_builtin.next() {
// There should only ever be a single trivial builtin candidate
// We prefer `Sized` candidates over everything.
let mut sized_candidates =
candidates.iter().filter(|c| matches!(c.candidate, SizedCandidate { has_nested: _ }));
if let Some(sized_candidate) = sized_candidates.next() {
// There should only ever be a single sized candidate
// as they would otherwise overlap.
debug_assert_eq!(trivial_builtin.next(), None);
return Some(BuiltinCandidate { has_nested: false });
debug_assert_eq!(sized_candidates.next(), None);
// Only prefer the built-in `Sized` candidate if its nested goals are certain.
// Otherwise, we may encounter failure later on if inference causes this candidate
// to not hold, but a where clause would've applied instead.
if sized_candidate.evaluation.must_apply_modulo_regions() {
return Some(sized_candidate.candidate.clone());
} else {
return None;
}
}
// Before we consider where-bounds, we have to deduplicate them here and also
@ -1940,7 +1944,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
// Don't use impl candidates which overlap with other candidates.
// This should pretty much only ever happen with malformed impls.
if candidates.iter().all(|c| match c.candidate {
BuiltinCandidate { has_nested: _ }
SizedCandidate { has_nested: _ }
| BuiltinCandidate { has_nested: _ }
| TransmutabilityCandidate
| AutoImplCandidate
| ClosureCandidate { .. }