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

View file

@ -172,6 +172,8 @@ impl CodegenFnAttrs {
/// * `#[no_mangle]` is present /// * `#[no_mangle]` is present
/// * `#[export_name(...)]` is present /// * `#[export_name(...)]` is present
/// * `#[linkage]` 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 { pub fn contains_extern_indicator(&self) -> bool {
self.flags.contains(CodegenFnAttrFlags::NO_MANGLE) self.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
|| self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) || 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 // If the function is #[naked] or contains any other attribute that requires exactly-once
// instantiation: // 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()); let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id());
if codegen_fn_attrs.contains_extern_indicator() if codegen_fn_attrs.contains_extern_indicator()
|| codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) || 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. /// parameter environment.
#[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)] #[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)]
pub enum SelectionCandidate<'tcx> { 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 /// A builtin implementation for some specific traits, used in cases
/// where we cannot rely an ordinary library implementations. /// 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 /// used for the `DiscriminantKind` and `Pointee` trait, both of which have
/// an associated type. /// an associated type.
BuiltinCandidate { 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. // As a consequence, this LocalDefId is always re-created before it is needed by the incr.
// comp. engine itself. // 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: // This is fine because:
// - those queries are `eval_always` so we won't miss their result changing; // - that query is `eval_always` so we won't miss its result changing;
// - this write will have happened before these queries are called. // - this write will have happened before that query is called.
let def_id = self.untracked.definitions.write().create_def(parent, data); let def_id = self.untracked.definitions.write().create_def(parent, data);
// This function modifies `self.definitions` using a side-effect. // 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 { match key.disambiguated_data.data {
DefPathData::Closure => { DefPathData::Closure => {
// FIXME(async_closures): This is somewhat ugly. // We need to additionally print the `kind` field of a coroutine if
// We need to additionally print the `kind` field of a closure if
// it is desugared from a coroutine-closure. // it is desugared from a coroutine-closure.
if let Some(hir::CoroutineKind::Desugared( 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. // 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`. // This covers both `DefKind::AnonConst` and `DefKind::InlineConst`.
// Anon consts doesn't have their own generics, and inline consts' own // Anon consts doesn't have their own generics, and inline consts' own
// generics are their inferred types, so don't print them. // generics are their inferred types, so don't print them.

View file

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

View file

@ -2200,7 +2200,9 @@ impl<'a> Parser<'a> {
} }
fn is_array_like_block(&mut self) -> bool { 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(2, |t| t == &token::Comma)
&& self.look_ahead(3, |t| t.can_begin_expr()) && 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(); let mut snapshot = self.create_snapshot_for_diagnostic();
match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) { match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) {
Ok(arr) => { Ok(arr) => {
let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfSpaces { let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfBraces {
span: arr.span, span: arr.span,
sub: errors::ArrayBracketsInsteadOfSpacesSugg { sub: errors::ArrayBracketsInsteadOfBracesSugg {
left: lo, left: lo,
right: snapshot.prev_token.span, right: snapshot.prev_token.span,
}, },
@ -2337,7 +2339,8 @@ impl<'a> Parser<'a> {
let capture_clause = self.parse_capture_clause()?; let capture_clause = self.parse_capture_clause()?;
let (fn_decl, fn_arg_span) = self.parse_fn_block_decl()?; let (fn_decl, fn_arg_span) = self.parse_fn_block_decl()?;
let decl_hi = self.prev_token.span; 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(_) => { FnRetTy::Default(_) => {
let restrictions = let restrictions =
self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET; 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)?, Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?,
} }
} }
_ => { // Explicit return type (`->`) needs block `-> T { }`.
// If an explicit return type is given, require a block to appear (RFC 968). FnRetTy::Ty(ty) => self.parse_closure_block_body(ty.span)?,
let body_lo = self.token.span;
self.parse_expr_block(None, body_lo, BlockCheckMode::Default)?
}
}; };
match coroutine_kind { match coroutine_kind {
@ -2405,6 +2405,49 @@ impl<'a> Parser<'a> {
Ok(closure) 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. /// Parses an optional `move` or `use` prefix to a closure-like construct.
fn parse_capture_clause(&mut self) -> PResult<'a, CaptureBy> { fn parse_capture_clause(&mut self) -> PResult<'a, CaptureBy> {
if self.eat_keyword(exp!(Move)) { if self.eat_keyword(exp!(Move)) {

View file

@ -383,6 +383,10 @@ passes_inline_ignored_constants =
.warn = {-passes_previously_accepted} .warn = {-passes_previously_accepted}
.note = {-passes_see_issue(issue: "65833")} .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 = passes_inline_ignored_function_prototype =
`#[inline]` is ignored on function prototypes `#[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, /// Checks that `#[coverage(..)]` is applied to a function/closure/method,

View file

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

View file

@ -66,6 +66,7 @@ pub struct MarkFrame<'a> {
parent: Option<&'a MarkFrame<'a>>, parent: Option<&'a MarkFrame<'a>>,
} }
#[derive(Debug)]
pub(super) enum DepNodeColor { pub(super) enum DepNodeColor {
Red, Red,
Green(DepNodeIndex), 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); self.try_mark_previous_green(qcx, parent_dep_node_index, dep_dep_node, frame);
if node_index.is_some() { 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(()); return Some(());
} }
} }
@ -930,7 +931,7 @@ impl<D: Deps> DepGraphData<D> {
return Some(()); return Some(());
} }
Some(DepNodeColor::Red) => { Some(DepNodeColor::Red) => {
debug!("dependency {dep_dep_node:?} was red after forcing",); debug!("dependency {dep_dep_node:?} was red after forcing");
return None; return None;
} }
None => {} None => {}
@ -950,7 +951,7 @@ impl<D: Deps> DepGraphData<D> {
// invalid state will not be persisted to the // invalid state will not be persisted to the
// incremental compilation cache because of // incremental compilation cache because of
// compilation errors being present. // compilation errors being present.
debug!("dependency {dep_dep_node:?} resulted in compilation error",); debug!("dependency {dep_dep_node:?} resulted in compilation error");
return None; 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::Ctor => "c",
hir::definitions::DefPathData::AnonConst => "k", hir::definitions::DefPathData::AnonConst => "k",
hir::definitions::DefPathData::OpaqueTy => "i", hir::definitions::DefPathData::OpaqueTy => "i",
hir::definitions::DefPathData::SyntheticCoroutineBody => "s",
hir::definitions::DefPathData::CrateRoot hir::definitions::DefPathData::CrateRoot
| hir::definitions::DefPathData::Use | hir::definitions::DefPathData::Use
| hir::definitions::DefPathData::GlobalAsm | hir::definitions::DefPathData::GlobalAsm

View file

@ -28,7 +28,10 @@ pub(super) fn mangle<'tcx>(
loop { loop {
let key = tcx.def_key(ty_def_id); let key = tcx.def_key(ty_def_id);
match key.disambiguated_data.data { 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(); instance_ty = tcx.type_of(ty_def_id).instantiate_identity();
debug!(?instance_ty); debug!(?instance_ty);
break; break;

View file

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

View file

@ -86,10 +86,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// `Pointee` is automatically implemented for every type. // `Pointee` is automatically implemented for every type.
candidates.vec.push(BuiltinCandidate { has_nested: false }); candidates.vec.push(BuiltinCandidate { has_nested: false });
} else if tcx.is_lang_item(def_id, LangItem::Sized) { } else if tcx.is_lang_item(def_id, LangItem::Sized) {
// Sized is never implementable by end-users, it is self.assemble_builtin_sized_candidate(obligation, &mut candidates);
// always automatically computed.
let sized_conditions = self.sized_conditions(obligation);
self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates);
} else if tcx.is_lang_item(def_id, LangItem::Unsize) { } else if tcx.is_lang_item(def_id, LangItem::Unsize) {
self.assemble_candidates_for_unsizing(obligation, &mut candidates); self.assemble_candidates_for_unsizing(obligation, &mut candidates);
} else if tcx.is_lang_item(def_id, LangItem::Destruct) { } 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: /// Assembles the trait which are built-in to the language itself:
/// `Copy`, `Clone` and `Sized`. /// `Copy`, `Clone` and `Sized`.
#[instrument(level = "debug", skip(self, candidates))] #[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( fn assemble_builtin_bound_candidates(
&mut self, &mut self,
conditions: BuiltinImplConditions<'tcx>, conditions: BuiltinImplConditions<'tcx>,

View file

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

View file

@ -1801,17 +1801,21 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
return Some(candidates.pop().unwrap().candidate); return Some(candidates.pop().unwrap().candidate);
} }
// We prefer trivial builtin candidates, i.e. builtin impls without any nested // We prefer `Sized` candidates over everything.
// requirements, over all others. This is a fix for #53123 and prevents winnowing let mut sized_candidates =
// from accidentally extending the lifetime of a variable. candidates.iter().filter(|c| matches!(c.candidate, SizedCandidate { has_nested: _ }));
let mut trivial_builtin = candidates if let Some(sized_candidate) = sized_candidates.next() {
.iter() // There should only ever be a single sized candidate
.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
// as they would otherwise overlap. // as they would otherwise overlap.
debug_assert_eq!(trivial_builtin.next(), None); debug_assert_eq!(sized_candidates.next(), None);
return Some(BuiltinCandidate { has_nested: false }); // 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 // 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. // Don't use impl candidates which overlap with other candidates.
// This should pretty much only ever happen with malformed impls. // This should pretty much only ever happen with malformed impls.
if candidates.iter().all(|c| match c.candidate { if candidates.iter().all(|c| match c.candidate {
BuiltinCandidate { has_nested: _ } SizedCandidate { has_nested: _ }
| BuiltinCandidate { has_nested: _ }
| TransmutabilityCandidate | TransmutabilityCandidate
| AutoImplCandidate | AutoImplCandidate
| ClosureCandidate { .. } | ClosureCandidate { .. }

View file

@ -127,15 +127,13 @@ mod prim_bool {}
/// [`Result<String, !>`] which we can unpack like this: /// [`Result<String, !>`] which we can unpack like this:
/// ///
/// ``` /// ```
/// #![feature(exhaustive_patterns)]
/// use std::str::FromStr; /// use std::str::FromStr;
/// let Ok(s) = String::from_str("hello"); /// let Ok(s) = String::from_str("hello");
/// ``` /// ```
/// ///
/// Since the [`Err`] variant contains a `!`, it can never occur. If the `exhaustive_patterns` /// Since the [`Err`] variant contains a `!`, it can never occur. This means we can exhaustively
/// feature is present this means we can exhaustively match on [`Result<T, !>`] by just taking the /// match on [`Result<T, !>`] by just taking the [`Ok`] variant. This illustrates another behavior
/// [`Ok`] variant. This illustrates another behavior of `!` - it can be used to "delete" certain /// of `!` - it can be used to "delete" certain enum variants from generic types like `Result`.
/// enum variants from generic types like `Result`.
/// ///
/// ## Infinite loops /// ## Infinite loops
/// ///

View file

@ -38,6 +38,15 @@ Number of file 0 mappings: 1
- Code(Counter(0)) at (prev + 11, 35) to (start + 0, 36) - Code(Counter(0)) at (prev + 11, 35) to (start + 0, 36)
Highest counter ID seen: c0 Highest counter ID seen: c0
Function name: async_closure::main::{closure#0}
Raw bytes (9): 0x[01, 01, 00, 01, 01, 0b, 23, 00, 24]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 1
- Code(Counter(0)) at (prev + 11, 35) to (start + 0, 36)
Highest counter ID seen: c0
Function name: async_closure::main::{closure#0}::{closure#0}::<i16> Function name: async_closure::main::{closure#0}::{closure#0}::<i16>
Raw bytes (9): 0x[01, 01, 00, 01, 01, 0b, 22, 00, 24] Raw bytes (9): 0x[01, 01, 00, 01, 01, 0b, 22, 00, 24]
Number of files: 1 Number of files: 1
@ -47,12 +56,3 @@ Number of file 0 mappings: 1
- Code(Counter(0)) at (prev + 11, 34) to (start + 0, 36) - Code(Counter(0)) at (prev + 11, 34) to (start + 0, 36)
Highest counter ID seen: c0 Highest counter ID seen: c0
Function name: async_closure::main::{closure#0}::{closure#1}::<i32>
Raw bytes (9): 0x[01, 01, 00, 01, 01, 0b, 23, 00, 24]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 1
- Code(Counter(0)) at (prev + 11, 35) to (start + 0, 36)
Highest counter ID seen: c0

View file

@ -14,7 +14,7 @@
| async_closure::main::{closure#0}: | async_closure::main::{closure#0}:
| LL| 1| let async_closure = async || {}; | LL| 1| let async_closure = async || {};
------------------ ------------------
| async_closure::main::{closure#0}::{closure#1}::<i32>: | async_closure::main::{closure#0}:
| LL| 1| let async_closure = async || {}; | LL| 1| let async_closure = async || {};
------------------ ------------------
LL| 1| executor::block_on(async_closure()); LL| 1| executor::block_on(async_closure());

View file

@ -0,0 +1,15 @@
//@ revisions: rpass1 rpass2
//@ edition: 2024
#![allow(unused)]
fn main() {
#[cfg(rpass1)]
async || {};
#[cfg(rpass2)]
|| {
|| ();
|| ();
};
}

View file

@ -1,6 +1,6 @@
// MIR for `foo::{closure#0}::{closure#1}` after built // MIR for `foo::{closure#0}::{synthetic#0}` after built
fn foo::{closure#0}::{closure#1}(_1: {async closure body@$DIR/async_closure_fake_read_for_by_move.rs:12:27: 15:6}, _2: ResumeTy) -> () fn foo::{closure#0}::{synthetic#0}(_1: {async closure body@$DIR/async_closure_fake_read_for_by_move.rs:12:27: 15:6}, _2: ResumeTy) -> ()
yields () yields ()
{ {
debug _task_context => _2; debug _task_context => _2;

View file

@ -7,7 +7,7 @@ enum Foo {
} }
// EMIT_MIR async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir // EMIT_MIR async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir
// EMIT_MIR async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#1}.built.after.mir // EMIT_MIR async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir
fn foo(f: &Foo) { fn foo(f: &Foo) {
let x = async move || match f { let x = async move || match f {
Foo::Bar if true => {} Foo::Bar if true => {}

View file

@ -1,6 +1,6 @@
// MIR for `main::{closure#0}::{closure#0}::{closure#1}` after built // MIR for `main::{closure#0}::{closure#0}::{synthetic#0}` after built
fn main::{closure#0}::{closure#0}::{closure#1}(_1: {async closure body@$DIR/async_closure_shims.rs:53:53: 56:10}, _2: ResumeTy) -> () fn main::{closure#0}::{closure#0}::{synthetic#0}(_1: {async closure body@$DIR/async_closure_shims.rs:53:53: 56:10}, _2: ResumeTy) -> ()
yields () yields ()
{ {
debug _task_context => _2; debug _task_context => _2;

View file

@ -1,6 +1,6 @@
// MIR for `main::{closure#0}::{closure#1}::{closure#1}` after built // MIR for `main::{closure#0}::{closure#1}::{synthetic#0}` after built
fn main::{closure#0}::{closure#1}::{closure#1}(_1: {async closure body@$DIR/async_closure_shims.rs:62:48: 65:10}, _2: ResumeTy) -> () fn main::{closure#0}::{closure#1}::{synthetic#0}(_1: {async closure body@$DIR/async_closure_shims.rs:62:48: 65:10}, _2: ResumeTy) -> ()
yields () yields ()
{ {
debug _task_context => _2; debug _task_context => _2;

View file

@ -42,11 +42,11 @@ async fn call_normal_mut<F: Future<Output = ()>>(f: &mut impl FnMut(i32) -> F) {
// EMIT_MIR async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.mir // EMIT_MIR async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.mir
// EMIT_MIR async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir // EMIT_MIR async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir
// EMIT_MIR async_closure_shims.main-{closure#0}-{closure#0}-{closure#1}.built.after.mir // EMIT_MIR async_closure_shims.main-{closure#0}-{closure#0}-{synthetic#0}.built.after.mir
// EMIT_MIR async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.mir // EMIT_MIR async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.mir
// EMIT_MIR async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.mir // EMIT_MIR async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.mir
// EMIT_MIR async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir // EMIT_MIR async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir
// EMIT_MIR async_closure_shims.main-{closure#0}-{closure#1}-{closure#1}.built.after.mir // EMIT_MIR async_closure_shims.main-{closure#0}-{closure#1}-{synthetic#0}.built.after.mir
pub fn main() { pub fn main() {
block_on(async { block_on(async {
let b = 2i32; let b = 2i32;

View file

@ -0,0 +1,30 @@
//! Ensure the unused_attributes lint fires for externally exported functions with `#[inline]`,
//! because `#[inline]` is ignored for such functions.
#![crate_type = "lib"]
#![feature(linkage)]
#![feature(naked_functions)]
#![deny(unused_attributes)]
#[inline]
//~^ ERROR: `#[inline]` is ignored on externally exported functions
#[no_mangle]
fn no_mangle() {}
#[inline]
//~^ ERROR: `#[inline]` is ignored on externally exported functions
#[export_name = "export_name"]
fn export_name() {}
#[inline]
//~^ ERROR: `#[inline]` is ignored on externally exported functions
#[linkage = "external"]
fn external_linkage() {}
#[inline]
fn normal() {}
#[inline]
#[linkage = "internal"] // not exported
fn internal_linkage() {}

View file

@ -0,0 +1,31 @@
error: `#[inline]` is ignored on externally exported functions
--> $DIR/inline-exported.rs:10:1
|
LL | #[inline]
| ^^^^^^^^^
|
= help: externally exported functions are functions with `#[no_mangle]`, `#[export_name]`, or `#[linkage]`
note: the lint level is defined here
--> $DIR/inline-exported.rs:8:9
|
LL | #![deny(unused_attributes)]
| ^^^^^^^^^^^^^^^^^
error: `#[inline]` is ignored on externally exported functions
--> $DIR/inline-exported.rs:15:1
|
LL | #[inline]
| ^^^^^^^^^
|
= help: externally exported functions are functions with `#[no_mangle]`, `#[export_name]`, or `#[linkage]`
error: `#[inline]` is ignored on externally exported functions
--> $DIR/inline-exported.rs:20:1
|
LL | #[inline]
| ^^^^^^^^^
|
= help: externally exported functions are functions with `#[no_mangle]`, `#[export_name]`, or `#[linkage]`
error: aborting due to 3 previous errors

View file

@ -1,7 +1,21 @@
// Test that we cannot parse a closure with an explicit return type // Test that we cannot parse a closure with an explicit return type
// unless it uses braces. // unless it uses braces.
fn main() { fn needs_braces_1() {
let x = || -> i32 22; let x = || -> i32 22;
//~^ ERROR expected `{`, found `22` //~^ ERROR expected `{`, found `22`
} }
// Check other delimiters too.
fn needs_braces_2() {
let x = || -> (i32, i32) (1, 2);
//~^ ERROR expected `{`, found `(`
}
fn needs_braces_3() {
let c = || -> [i32; 2] [1, 2];
//~^ ERROR expected `{`, found `[`
}
fn main() {}

View file

@ -2,12 +2,40 @@ error: expected `{`, found `22`
--> $DIR/closure-return-syntax.rs:5:23 --> $DIR/closure-return-syntax.rs:5:23
| |
LL | let x = || -> i32 22; LL | let x = || -> i32 22;
| ^^ expected `{` | --- ^^
| |
| explicit return type requires closure body to be enclosed in braces
| |
help: you might have meant to write this as part of a block help: wrap the expression in curly braces
| |
LL | let x = || -> i32 { 22 }; LL | let x = || -> i32 { 22 };
| + + | + +
error: aborting due to 1 previous error error: expected `{`, found `(`
--> $DIR/closure-return-syntax.rs:12:34
|
LL | let x = || -> (i32, i32) (1, 2);
| ---------- ^
| |
| explicit return type requires closure body to be enclosed in braces
|
help: wrap the expression in curly braces
|
LL | let x = || -> (i32, i32) { (1, 2) };
| + +
error: expected `{`, found `[`
--> $DIR/closure-return-syntax.rs:17:32
|
LL | let c = || -> [i32; 2] [1, 2];
| -------- ^
| |
| explicit return type requires closure body to be enclosed in braces
|
help: wrap the expression in curly braces
|
LL | let c = || -> [i32; 2] { [1, 2] };
| + +
error: aborting due to 3 previous errors

View file

@ -0,0 +1,21 @@
//@ check-pass
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
struct W<T: ?Sized>(T);
fn is_sized<T: Sized>(x: *const T) {}
fn dummy<T: ?Sized>() -> *const T { todo!() }
fn non_param_where_bound<T: ?Sized>()
where
W<T>: Sized,
{
let x: *const W<_> = dummy();
is_sized::<W<_>>(x);
let _: *const W<T> = x;
}
fn main() {}

View file

@ -56,7 +56,7 @@ fn foo::{closure#0}::{closure#0}(_1: Pin<&mut {async closure body@$DIR/async-clo
unreachable; unreachable;
} }
} }
fn foo::{closure#0}::{closure#1}(_1: Pin<&mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}>, _2: &mut Context<'_>) -> Poll<()> { fn foo::{closure#0}::{synthetic#0}(_1: Pin<&mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}>, _2: &mut Context<'_>) -> Poll<()> {
let mut _0: Poll<()>; let mut _0: Poll<()>;
let _3: i32; let _3: i32;
let mut _4: &i32; let mut _4: &i32;

View file

@ -98,6 +98,12 @@ LL |
LL | trait Baz {} LL | trait Baz {}
| ------------ not a function definition | ------------ not a function definition
error: cannot use `#[inline(always)]` with `#[target_feature]`
--> $DIR/invalid-attribute.rs:69:1
|
LL | #[inline(always)]
| ^^^^^^^^^^^^^^^^^
error: attribute should be applied to a function definition error: attribute should be applied to a function definition
--> $DIR/invalid-attribute.rs:74:1 --> $DIR/invalid-attribute.rs:74:1
| |
@ -163,12 +169,6 @@ error: malformed `target_feature` attribute input
LL | #[target_feature(disable = "baz")] LL | #[target_feature(disable = "baz")]
| ^^^^^^^^^^^^^^^ help: must be of the form: `enable = ".."` | ^^^^^^^^^^^^^^^ help: must be of the form: `enable = ".."`
error: cannot use `#[inline(always)]` with `#[target_feature]`
--> $DIR/invalid-attribute.rs:69:1
|
LL | #[inline(always)]
| ^^^^^^^^^^^^^^^^^
error[E0046]: not all trait items implemented, missing: `foo` error[E0046]: not all trait items implemented, missing: `foo`
--> $DIR/invalid-attribute.rs:81:1 --> $DIR/invalid-attribute.rs:81:1
| |

View file

@ -0,0 +1,9 @@
error[E0282]: type annotations needed
--> $DIR/incomplete-infer-via-sized-wc.rs:15:5
|
LL | is_sized::<MaybeSized<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `is_sized`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0282`.

View file

@ -0,0 +1,9 @@
error[E0282]: type annotations needed
--> $DIR/incomplete-infer-via-sized-wc.rs:15:5
|
LL | is_sized::<MaybeSized<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `is_sized`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0282`.

View file

@ -0,0 +1,19 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
// Exercises change in <https://github.com/rust-lang/rust/pull/138176>.
struct MaybeSized<T: ?Sized>(T);
fn is_sized<T: Sized>() -> Box<T> { todo!() }
fn foo<T: ?Sized>()
where
MaybeSized<T>: Sized,
{
is_sized::<MaybeSized<_>>();
//~^ ERROR type annotations needed
}
fn main() {}

View file

@ -0,0 +1,28 @@
//@ check-pass
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
// Exercises change in <https://github.com/rust-lang/rust/pull/138176>.
trait Trait<T>: Sized {}
impl<T> Trait<T> for T {}
fn is_sized<T: Sized>() {}
fn normal_ref<'a, 'b, T>()
where
&'a u32: Trait<T>,
{
is_sized::<&'b u32>();
}
struct MyRef<'a, U: ?Sized = ()>(&'a u32, U);
fn my_ref<'a, 'b, T>()
where
MyRef<'a>: Trait<T>,
{
is_sized::<MyRef<'b>>();
}
fn main() {}

View file

@ -0,0 +1,27 @@
error[E0308]: mismatched types
--> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:13:23
|
LL | (MyType<'a, T>,): Sized,
| ^^^^^ lifetime mismatch
|
= note: expected trait `<MyType<'a, T> as Sized>`
found trait `<MyType<'static, T> as Sized>`
note: the lifetime `'a` as defined here...
--> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:11:8
|
LL | fn foo<'a, T: ?Sized>()
| ^^
= note: ...does not necessarily outlive the static lifetime
error: lifetime may not live long enough
--> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:22:5
|
LL | fn foo<'a, T: ?Sized>()
| -- lifetime `'a` defined here
...
LL | is_sized::<(MyType<'a, T>,)>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.

View file

@ -0,0 +1,25 @@
error[E0478]: lifetime bound not satisfied
--> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:13:23
|
LL | (MyType<'a, T>,): Sized,
| ^^^^^
|
note: lifetime parameter instantiated with the lifetime `'a` as defined here
--> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:11:8
|
LL | fn foo<'a, T: ?Sized>()
| ^^
= note: but lifetime parameter must outlive the static lifetime
error: lifetime may not live long enough
--> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:22:5
|
LL | fn foo<'a, T: ?Sized>()
| -- lifetime `'a` defined here
...
LL | is_sized::<(MyType<'a, T>,)>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0478`.

View file

@ -0,0 +1,26 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
// Exercises change in <https://github.com/rust-lang/rust/pull/138176>.
struct MyType<'a, T: ?Sized>(&'a (), T);
fn is_sized<T>() {}
fn foo<'a, T: ?Sized>()
where
(MyType<'a, T>,): Sized,
//[current]~^ ERROR mismatched types
//[next]~^^ ERROR lifetime bound not satisfied
MyType<'static, T>: Sized,
{
// Preferring the builtin `Sized` impl of tuples
// requires proving `MyType<'a, T>: Sized` which
// can only be proven by using the where-clause,
// adding an unnecessary `'static` constraint.
is_sized::<(MyType<'a, T>,)>();
//~^ ERROR lifetime may not live long enough
}
fn main() {}

View file

@ -1161,7 +1161,6 @@ compiler = [
] ]
libs = [ libs = [
"@Mark-Simulacrum", "@Mark-Simulacrum",
"@Amanieu",
"@Noratrieb", "@Noratrieb",
"@workingjubilee", "@workingjubilee",
"@joboet", "@joboet",