diff options
206 files changed, 4694 insertions, 1247 deletions
diff --git a/Cargo.lock b/Cargo.lock index c8d26559728..51e21f6dc82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3685,6 +3685,8 @@ version = "0.0.0" dependencies = [ "rustc_data_structures", "rustc_span", + "serde", + "serde_json", ] [[package]] diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 062447ea03f..d188750bfe1 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -209,6 +209,24 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { } } + pub fn is_single_vector_element<C>(self, cx: &C, expected_size: Size) -> bool + where + Ty: TyAbiInterface<'a, C>, + C: HasDataLayout, + { + match self.backend_repr { + BackendRepr::Vector { .. } => self.size == expected_size, + BackendRepr::Memory { .. } => { + if self.fields.count() == 1 && self.fields.offset(0).bytes() == 0 { + self.field(cx, 0).is_single_vector_element(cx, expected_size) + } else { + false + } + } + _ => false, + } + } + pub fn is_adt<C>(self) -> bool where Ty: TyAbiInterface<'a, C>, diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 54e826585d2..888b13efa31 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -457,7 +457,7 @@ impl MetaItemKind { tokens: &mut impl Iterator<Item = &'a TokenTree>, ) -> Option<MetaItemKind> { match tokens.next() { - Some(TokenTree::Delimited(.., Delimiter::Invisible, inner_tokens)) => { + Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => { MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees()) } Some(TokenTree::Token(token, _)) => { @@ -605,7 +605,7 @@ impl MetaItemInner { tokens.next(); return Some(MetaItemInner::Lit(lit)); } - Some(TokenTree::Delimited(.., Delimiter::Invisible, inner_tokens)) => { + Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => { tokens.next(); return MetaItemInner::from_tokens(&mut inner_tokens.trees().peekable()); } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 811cb0be9f9..61f2f91635d 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -104,8 +104,16 @@ pub trait MutVisitor: Sized { walk_use_tree(self, use_tree); } + fn visit_foreign_item(&mut self, ni: &mut P<ForeignItem>) { + walk_item(self, ni); + } + fn flat_map_foreign_item(&mut self, ni: P<ForeignItem>) -> SmallVec<[P<ForeignItem>; 1]> { - walk_flat_map_item(self, ni) + walk_flat_map_foreign_item(self, ni) + } + + fn visit_item(&mut self, i: &mut P<Item>) { + walk_item(self, i); } fn flat_map_item(&mut self, i: P<Item>) -> SmallVec<[P<Item>; 1]> { @@ -116,10 +124,18 @@ pub trait MutVisitor: Sized { walk_fn_header(self, header); } + fn visit_field_def(&mut self, fd: &mut FieldDef) { + walk_field_def(self, fd); + } + fn flat_map_field_def(&mut self, fd: FieldDef) -> SmallVec<[FieldDef; 1]> { walk_flat_map_field_def(self, fd) } + fn visit_assoc_item(&mut self, i: &mut P<AssocItem>, ctxt: AssocCtxt) { + walk_assoc_item(self, i, ctxt) + } + fn flat_map_assoc_item( &mut self, i: P<AssocItem>, @@ -153,6 +169,10 @@ pub trait MutVisitor: Sized { walk_flat_map_stmt(self, s) } + fn visit_arm(&mut self, arm: &mut Arm) { + walk_arm(self, arm); + } + fn flat_map_arm(&mut self, arm: Arm) -> SmallVec<[Arm; 1]> { walk_flat_map_arm(self, arm) } @@ -199,6 +219,10 @@ pub trait MutVisitor: Sized { walk_foreign_mod(self, nm); } + fn visit_variant(&mut self, v: &mut Variant) { + walk_variant(self, v); + } + fn flat_map_variant(&mut self, v: Variant) -> SmallVec<[Variant; 1]> { walk_flat_map_variant(self, v) } @@ -251,6 +275,10 @@ pub trait MutVisitor: Sized { walk_attribute(self, at); } + fn visit_param(&mut self, param: &mut Param) { + walk_param(self, param); + } + fn flat_map_param(&mut self, param: Param) -> SmallVec<[Param; 1]> { walk_flat_map_param(self, param) } @@ -271,6 +299,10 @@ pub trait MutVisitor: Sized { walk_variant_data(self, vdata); } + fn visit_generic_param(&mut self, param: &mut GenericParam) { + walk_generic_param(self, param) + } + fn flat_map_generic_param(&mut self, param: GenericParam) -> SmallVec<[GenericParam; 1]> { walk_flat_map_generic_param(self, param) } @@ -287,6 +319,10 @@ pub trait MutVisitor: Sized { walk_mt(self, mt); } + fn visit_expr_field(&mut self, f: &mut ExprField) { + walk_expr_field(self, f); + } + fn flat_map_expr_field(&mut self, f: ExprField) -> SmallVec<[ExprField; 1]> { walk_flat_map_expr_field(self, f) } @@ -311,6 +347,10 @@ pub trait MutVisitor: Sized { // Do nothing. } + fn visit_pat_field(&mut self, fp: &mut PatField) { + walk_pat_field(self, fp) + } + fn flat_map_pat_field(&mut self, fp: PatField) -> SmallVec<[PatField; 1]> { walk_flat_map_pat_field(self, fp) } @@ -429,16 +469,20 @@ pub fn visit_delim_span<T: MutVisitor>(vis: &mut T, DelimSpan { open, close }: & vis.visit_span(close); } -pub fn walk_flat_map_pat_field<T: MutVisitor>( - vis: &mut T, - mut fp: PatField, -) -> SmallVec<[PatField; 1]> { - let PatField { attrs, id, ident, is_placeholder: _, is_shorthand: _, pat, span } = &mut fp; +pub fn walk_pat_field<T: MutVisitor>(vis: &mut T, fp: &mut PatField) { + let PatField { attrs, id, ident, is_placeholder: _, is_shorthand: _, pat, span } = fp; vis.visit_id(id); visit_attrs(vis, attrs); vis.visit_ident(ident); vis.visit_pat(pat); vis.visit_span(span); +} + +pub fn walk_flat_map_pat_field<T: MutVisitor>( + vis: &mut T, + mut fp: PatField, +) -> SmallVec<[PatField; 1]> { + vis.visit_pat_field(&mut fp); smallvec![fp] } @@ -459,14 +503,18 @@ fn walk_use_tree<T: MutVisitor>(vis: &mut T, use_tree: &mut UseTree) { vis.visit_span(span); } -pub fn walk_flat_map_arm<T: MutVisitor>(vis: &mut T, mut arm: Arm) -> SmallVec<[Arm; 1]> { - let Arm { attrs, pat, guard, body, span, id, is_placeholder: _ } = &mut arm; +pub fn walk_arm<T: MutVisitor>(vis: &mut T, arm: &mut Arm) { + let Arm { attrs, pat, guard, body, span, id, is_placeholder: _ } = arm; vis.visit_id(id); visit_attrs(vis, attrs); vis.visit_pat(pat); visit_opt(guard, |guard| vis.visit_expr(guard)); visit_opt(body, |body| vis.visit_expr(body)); vis.visit_span(span); +} + +pub fn walk_flat_map_arm<T: MutVisitor>(vis: &mut T, mut arm: Arm) -> SmallVec<[Arm; 1]> { + vis.visit_arm(&mut arm); smallvec![arm] } @@ -543,11 +591,8 @@ fn walk_foreign_mod<T: MutVisitor>(vis: &mut T, foreign_mod: &mut ForeignMod) { items.flat_map_in_place(|item| vis.flat_map_foreign_item(item)); } -pub fn walk_flat_map_variant<T: MutVisitor>( - visitor: &mut T, - mut variant: Variant, -) -> SmallVec<[Variant; 1]> { - let Variant { ident, vis, attrs, id, data, disr_expr, span, is_placeholder: _ } = &mut variant; +pub fn walk_variant<T: MutVisitor>(visitor: &mut T, variant: &mut Variant) { + let Variant { ident, vis, attrs, id, data, disr_expr, span, is_placeholder: _ } = variant; visitor.visit_id(id); visit_attrs(visitor, attrs); visitor.visit_vis(vis); @@ -555,6 +600,13 @@ pub fn walk_flat_map_variant<T: MutVisitor>( visitor.visit_variant_data(data); visit_opt(disr_expr, |disr_expr| visitor.visit_anon_const(disr_expr)); visitor.visit_span(span); +} + +pub fn walk_flat_map_variant<T: MutVisitor>( + vis: &mut T, + mut variant: Variant, +) -> SmallVec<[Variant; 1]> { + vis.visit_variant(&mut variant); smallvec![variant] } @@ -685,13 +737,17 @@ fn walk_meta_item<T: MutVisitor>(vis: &mut T, mi: &mut MetaItem) { vis.visit_span(span); } -pub fn walk_flat_map_param<T: MutVisitor>(vis: &mut T, mut param: Param) -> SmallVec<[Param; 1]> { - let Param { attrs, id, pat, span, ty, is_placeholder: _ } = &mut param; +pub fn walk_param<T: MutVisitor>(vis: &mut T, param: &mut Param) { + let Param { attrs, id, pat, span, ty, is_placeholder: _ } = param; vis.visit_id(id); visit_attrs(vis, attrs); vis.visit_pat(pat); vis.visit_ty(ty); vis.visit_span(span); +} + +pub fn walk_flat_map_param<T: MutVisitor>(vis: &mut T, mut param: Param) -> SmallVec<[Param; 1]> { + vis.visit_param(&mut param); smallvec![param] } @@ -950,11 +1006,8 @@ fn walk_precise_capturing_arg<T: MutVisitor>(vis: &mut T, arg: &mut PreciseCaptu } } -pub fn walk_flat_map_generic_param<T: MutVisitor>( - vis: &mut T, - mut param: GenericParam, -) -> SmallVec<[GenericParam; 1]> { - let GenericParam { id, ident, attrs, bounds, kind, colon_span, is_placeholder: _ } = &mut param; +pub fn walk_generic_param<T: MutVisitor>(vis: &mut T, param: &mut GenericParam) { + let GenericParam { id, ident, attrs, bounds, kind, colon_span, is_placeholder: _ } = param; vis.visit_id(id); visit_attrs(vis, attrs); vis.visit_ident(ident); @@ -972,6 +1025,13 @@ pub fn walk_flat_map_generic_param<T: MutVisitor>( if let Some(colon_span) = colon_span { vis.visit_span(colon_span); } +} + +pub fn walk_flat_map_generic_param<T: MutVisitor>( + vis: &mut T, + mut param: GenericParam, +) -> SmallVec<[GenericParam; 1]> { + vis.visit_generic_param(&mut param); smallvec![param] } @@ -1054,30 +1114,38 @@ fn walk_poly_trait_ref<T: MutVisitor>(vis: &mut T, p: &mut PolyTraitRef) { vis.visit_span(span); } -pub fn walk_flat_map_field_def<T: MutVisitor>( - visitor: &mut T, - mut fd: FieldDef, -) -> SmallVec<[FieldDef; 1]> { - let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _ } = &mut fd; +pub fn walk_field_def<T: MutVisitor>(visitor: &mut T, fd: &mut FieldDef) { + let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _ } = fd; visitor.visit_id(id); visit_attrs(visitor, attrs); visitor.visit_vis(vis); visit_opt(ident, |ident| visitor.visit_ident(ident)); visitor.visit_ty(ty); visitor.visit_span(span); - smallvec![fd] } -pub fn walk_flat_map_expr_field<T: MutVisitor>( +pub fn walk_flat_map_field_def<T: MutVisitor>( vis: &mut T, - mut f: ExprField, -) -> SmallVec<[ExprField; 1]> { - let ExprField { ident, expr, span, is_shorthand: _, attrs, id, is_placeholder: _ } = &mut f; + mut fd: FieldDef, +) -> SmallVec<[FieldDef; 1]> { + vis.visit_field_def(&mut fd); + smallvec![fd] +} + +pub fn walk_expr_field<T: MutVisitor>(vis: &mut T, f: &mut ExprField) { + let ExprField { ident, expr, span, is_shorthand: _, attrs, id, is_placeholder: _ } = f; vis.visit_id(id); visit_attrs(vis, attrs); vis.visit_ident(ident); vis.visit_expr(expr); vis.visit_span(span); +} + +pub fn walk_flat_map_expr_field<T: MutVisitor>( + vis: &mut T, + mut f: ExprField, +) -> SmallVec<[ExprField; 1]> { + vis.visit_expr_field(&mut f); smallvec![f] } @@ -1331,18 +1399,19 @@ pub fn walk_crate<T: MutVisitor>(vis: &mut T, krate: &mut Crate) { vis.visit_span(inject_use_span); } -pub fn walk_flat_map_item<K: WalkItemKind<Ctxt = ()>>( - visitor: &mut impl MutVisitor, - item: P<Item<K>>, -) -> SmallVec<[P<Item<K>>; 1]> { - walk_flat_map_assoc_item(visitor, item, ()) +pub fn walk_item(visitor: &mut impl MutVisitor, item: &mut P<Item<impl WalkItemKind<Ctxt = ()>>>) { + walk_item_ctxt(visitor, item, ()) +} + +pub fn walk_assoc_item(visitor: &mut impl MutVisitor, item: &mut P<AssocItem>, ctxt: AssocCtxt) { + walk_item_ctxt(visitor, item, ctxt) } -pub fn walk_flat_map_assoc_item<K: WalkItemKind>( +fn walk_item_ctxt<K: WalkItemKind>( visitor: &mut impl MutVisitor, - mut item: P<Item<K>>, + item: &mut P<Item<K>>, ctxt: K::Ctxt, -) -> SmallVec<[P<Item<K>>; 1]> { +) { let Item { ident, attrs, id, kind, vis, span, tokens } = item.deref_mut(); visitor.visit_id(id); visit_attrs(visitor, attrs); @@ -1351,6 +1420,27 @@ pub fn walk_flat_map_assoc_item<K: WalkItemKind>( kind.walk(*span, *id, ident, vis, ctxt, visitor); visit_lazy_tts(visitor, tokens); visitor.visit_span(span); +} + +pub fn walk_flat_map_item(vis: &mut impl MutVisitor, mut item: P<Item>) -> SmallVec<[P<Item>; 1]> { + vis.visit_item(&mut item); + smallvec![item] +} + +pub fn walk_flat_map_foreign_item( + vis: &mut impl MutVisitor, + mut item: P<ForeignItem>, +) -> SmallVec<[P<ForeignItem>; 1]> { + vis.visit_foreign_item(&mut item); + smallvec![item] +} + +pub fn walk_flat_map_assoc_item( + vis: &mut impl MutVisitor, + mut item: P<AssocItem>, + ctxt: AssocCtxt, +) -> SmallVec<[P<AssocItem>; 1]> { + vis.visit_assoc_item(&mut item, ctxt); smallvec![item] } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 3b9edef0615..678f43e3511 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -42,11 +42,86 @@ pub enum BinOpToken { Shr, } +// This type must not implement `Hash` due to the unusual `PartialEq` impl below. +#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic)] +pub enum InvisibleOrigin { + // From the expansion of a metavariable in a declarative macro. + MetaVar(MetaVarKind), + + // Converted from `proc_macro::Delimiter` in + // `proc_macro::Delimiter::to_internal`, i.e. returned by a proc macro. + ProcMacro, + + // Converted from `TokenKind::Interpolated` in + // `TokenStream::flatten_token`. Treated similarly to `ProcMacro`. + FlattenToken, +} + +impl PartialEq for InvisibleOrigin { + #[inline] + fn eq(&self, _other: &InvisibleOrigin) -> bool { + // When we had AST-based nonterminals we couldn't compare them, and the + // old `Nonterminal` type had an `eq` that always returned false, + // resulting in this restriction: + // https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment + // This `eq` emulates that behaviour. We could consider lifting this + // restriction now but there are still cases involving invisible + // delimiters that make it harder than it first appears. + false + } +} + +/// Annoyingly similar to `NonterminalKind`, but the slight differences are important. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)] +pub enum MetaVarKind { + Item, + Block, + Stmt, + Pat(NtPatKind), + Expr { + kind: NtExprKind, + // This field is needed for `Token::can_begin_literal_maybe_minus`. + can_begin_literal_maybe_minus: bool, + // This field is needed for `Token::can_begin_string_literal`. + can_begin_string_literal: bool, + }, + Ty, + Ident, + Lifetime, + Literal, + Meta, + Path, + Vis, + TT, +} + +impl fmt::Display for MetaVarKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let sym = match self { + MetaVarKind::Item => sym::item, + MetaVarKind::Block => sym::block, + MetaVarKind::Stmt => sym::stmt, + MetaVarKind::Pat(PatParam { inferred: true } | PatWithOr) => sym::pat, + MetaVarKind::Pat(PatParam { inferred: false }) => sym::pat_param, + MetaVarKind::Expr { kind: Expr2021 { inferred: true } | Expr, .. } => sym::expr, + MetaVarKind::Expr { kind: Expr2021 { inferred: false }, .. } => sym::expr_2021, + MetaVarKind::Ty => sym::ty, + MetaVarKind::Ident => sym::ident, + MetaVarKind::Lifetime => sym::lifetime, + MetaVarKind::Literal => sym::literal, + MetaVarKind::Meta => sym::meta, + MetaVarKind::Path => sym::path, + MetaVarKind::Vis => sym::vis, + MetaVarKind::TT => sym::tt, + }; + write!(f, "{sym}") + } +} + /// Describes how a sequence of token trees is delimited. /// Cannot use `proc_macro::Delimiter` directly because this /// structure should implement some additional traits. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[derive(Encodable, Decodable, Hash, HashStable_Generic)] +#[derive(Copy, Clone, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)] pub enum Delimiter { /// `( ... )` Parenthesis, @@ -59,7 +134,34 @@ pub enum Delimiter { /// "macro variable" `$var`. It is important to preserve operator priorities in cases like /// `$var * 3` where `$var` is `1 + 2`. /// Invisible delimiters might not survive roundtrip of a token stream through a string. - Invisible, + Invisible(InvisibleOrigin), +} + +impl Delimiter { + // Should the parser skip these delimiters? Only happens for certain kinds + // of invisible delimiters. Ideally this function will eventually disappear + // and no invisible delimiters will be skipped. + #[inline] + pub fn skip(&self) -> bool { + match self { + Delimiter::Parenthesis | Delimiter::Bracket | Delimiter::Brace => false, + Delimiter::Invisible(InvisibleOrigin::MetaVar(_)) => false, + Delimiter::Invisible(InvisibleOrigin::FlattenToken | InvisibleOrigin::ProcMacro) => { + true + } + } + } + + // This exists because `InvisibleOrigin`s should be compared. It is only used for assertions. + pub fn eq_ignoring_invisible_origin(&self, other: &Delimiter) -> bool { + match (self, other) { + (Delimiter::Parenthesis, Delimiter::Parenthesis) => true, + (Delimiter::Brace, Delimiter::Brace) => true, + (Delimiter::Bracket, Delimiter::Bracket) => true, + (Delimiter::Invisible(_), Delimiter::Invisible(_)) => true, + _ => false, + } + } } // Note that the suffix is *not* considered when deciding the `LitKind` in this @@ -496,10 +598,11 @@ impl Token { /// **NB**: Take care when modifying this function, since it will change /// the stable set of tokens that are allowed to match an expr nonterminal. pub fn can_begin_expr(&self) -> bool { + use Delimiter::*; match self.uninterpolate().kind { Ident(name, is_raw) => ident_can_begin_expr(name, self.span, is_raw), // value name or keyword - OpenDelim(..) | // tuple, array or block + OpenDelim(Parenthesis | Brace | Bracket) | // tuple, array or block Literal(..) | // literal Not | // operator not BinOp(Minus) | // unary minus @@ -510,7 +613,7 @@ impl Token { // DotDotDot is no longer supported, but we need some way to display the error DotDot | DotDotDot | DotDotEq | // range notation Lt | BinOp(Shl) | // associated path - PathSep | // global path + PathSep | // global path Lifetime(..) | // labeled loop Pound => true, // expression attributes Interpolated(ref nt) => @@ -520,6 +623,12 @@ impl Token { NtLiteral(..) | NtPath(..) ), + OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + MetaVarKind::Block | + MetaVarKind::Expr { .. } | + MetaVarKind::Literal | + MetaVarKind::Path + ))) => true, _ => false, } } @@ -553,6 +662,14 @@ impl Token { | NtPath(..) | NtTy(..) ), + OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + MetaVarKind::Expr { .. } | + MetaVarKind::Literal | + MetaVarKind::Meta | + MetaVarKind::Pat(_) | + MetaVarKind::Path | + MetaVarKind::Ty + ))) => true, _ => false, } } @@ -573,6 +690,10 @@ impl Token { Lt | BinOp(Shl) | // associated path PathSep => true, // global path Interpolated(ref nt) => matches!(&**nt, NtTy(..) | NtPath(..)), + OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + MetaVarKind::Ty | + MetaVarKind::Path + ))) => true, // For anonymous structs or unions, which only appear in specific positions // (type of struct fields or union fields), we don't consider them as regular types _ => false, @@ -585,6 +706,9 @@ impl Token { OpenDelim(Delimiter::Brace) | Literal(..) | BinOp(Minus) => true, Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true, Interpolated(ref nt) => matches!(&**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)), + OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + MetaVarKind::Expr { .. } | MetaVarKind::Block | MetaVarKind::Literal, + ))) => true, _ => false, } } @@ -641,6 +765,13 @@ impl Token { }, _ => false, }, + OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind))) => match mv_kind { + MetaVarKind::Literal => true, + MetaVarKind::Expr { can_begin_literal_maybe_minus, .. } => { + can_begin_literal_maybe_minus + } + _ => false, + }, _ => false, } } @@ -656,6 +787,11 @@ impl Token { }, _ => false, }, + OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind))) => match mv_kind { + MetaVarKind::Literal => true, + MetaVarKind::Expr { can_begin_string_literal, .. } => can_begin_string_literal, + _ => false, + }, _ => false, } } @@ -896,7 +1032,7 @@ impl PartialEq<TokenKind> for Token { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)] pub enum NtPatKind { // Matches or-patterns. Was written using `pat` in edition 2021 or later. PatWithOr, @@ -906,7 +1042,7 @@ pub enum NtPatKind { PatParam { inferred: bool }, } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)] pub enum NtExprKind { // Matches expressions using the post-edition 2024. Was written using // `expr` in edition 2024 or later. @@ -933,7 +1069,7 @@ pub enum Nonterminal { NtVis(P<ast::Visibility>), } -#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)] pub enum NonterminalKind { Item, Block, diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 0b4bfc0b36a..c6b6addc946 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -24,7 +24,7 @@ use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym}; use crate::ast::{AttrStyle, StmtKind}; use crate::ast_traits::{HasAttrs, HasTokens}; -use crate::token::{self, Delimiter, Nonterminal, Token, TokenKind}; +use crate::token::{self, Delimiter, InvisibleOrigin, Nonterminal, Token, TokenKind}; use crate::{AttrVec, Attribute}; /// Part of a `TokenStream`. @@ -484,13 +484,13 @@ impl TokenStream { token::NtLifetime(ident, is_raw) => TokenTree::Delimited( DelimSpan::from_single(token.span), DelimSpacing::new(Spacing::JointHidden, spacing), - Delimiter::Invisible, + Delimiter::Invisible(InvisibleOrigin::FlattenToken), TokenStream::token_alone(token::Lifetime(ident.name, is_raw), ident.span), ), token::Interpolated(ref nt) => TokenTree::Delimited( DelimSpan::from_single(token.span), DelimSpacing::new(Spacing::JointHidden, spacing), - Delimiter::Invisible, + Delimiter::Invisible(InvisibleOrigin::FlattenToken), TokenStream::from_nonterminal_ast(&nt).flattened(), ), _ => TokenTree::Token(token.clone(), spacing), diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index c121e7711ee..0302c9fa7f8 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -463,13 +463,6 @@ impl WalkItemKind for ItemKind { } } -pub fn walk_item<'a, V: Visitor<'a>>( - visitor: &mut V, - item: &'a Item<impl WalkItemKind<Ctxt = ()>>, -) -> V::Result { - walk_assoc_item(visitor, item, ()) -} - pub fn walk_enum_def<'a, V: Visitor<'a>>( visitor: &mut V, EnumDef { variants }: &'a EnumDef, @@ -931,7 +924,22 @@ impl WalkItemKind for AssocItemKind { } } -pub fn walk_assoc_item<'a, V: Visitor<'a>, K: WalkItemKind>( +pub fn walk_item<'a, V: Visitor<'a>>( + visitor: &mut V, + item: &'a Item<impl WalkItemKind<Ctxt = ()>>, +) -> V::Result { + walk_item_ctxt(visitor, item, ()) +} + +pub fn walk_assoc_item<'a, V: Visitor<'a>>( + visitor: &mut V, + item: &'a AssocItem, + ctxt: AssocCtxt, +) -> V::Result { + walk_item_ctxt(visitor, item, ctxt) +} + +fn walk_item_ctxt<'a, V: Visitor<'a>, K: WalkItemKind>( visitor: &mut V, item: &'a Item<K>, ctxt: K::Ctxt, diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index de9f5187be7..d7c531f3760 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -942,9 +942,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere token::CloseDelim(Delimiter::Bracket) => "]".into(), token::OpenDelim(Delimiter::Brace) => "{".into(), token::CloseDelim(Delimiter::Brace) => "}".into(), - token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) => { - "".into() - } + token::OpenDelim(Delimiter::Invisible(_)) + | token::CloseDelim(Delimiter::Invisible(_)) => "".into(), token::Pound => "#".into(), token::Dollar => "$".into(), token::Question => "?".into(), diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 7adc7a8863e..452038bc328 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -641,6 +641,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { | mir::StatementKind::Coverage(..) | mir::StatementKind::Intrinsic(..) | mir::StatementKind::ConstEvalCounter + | mir::StatementKind::BackwardIncompatibleDropHint { .. } | mir::StatementKind::Nop => {} } } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 03f7b05d1e3..16a4f699177 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -652,6 +652,8 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt< | StatementKind::Coverage(..) // These do not actually affect borrowck | StatementKind::ConstEvalCounter + // This do not affect borrowck + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::StorageLive(..) => {} StatementKind::StorageDead(local) => { self.access_place( diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs index d1b65943199..f646beeecf7 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs @@ -88,6 +88,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { | StatementKind::Nop | StatementKind::Retag { .. } | StatementKind::Deinit(..) + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::SetDiscriminant { .. } => { bug!("Statement not allowed in this MIR phase") } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index c6b34d5bf1d..3a7ed711f68 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1252,6 +1252,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | StatementKind::Coverage(..) | StatementKind::ConstEvalCounter | StatementKind::PlaceMention(..) + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => {} StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { bug!("Statement not allowed in this MIR phase") diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index e7ee6b43e27..d46a1bd3d31 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -215,7 +215,7 @@ impl MutVisitor for CfgEval<'_> { foreign_item: P<ast::ForeignItem>, ) -> SmallVec<[P<ast::ForeignItem>; 1]> { let foreign_item = configure!(self, foreign_item); - mut_visit::walk_flat_map_item(self, foreign_item) + mut_visit::walk_flat_map_foreign_item(self, foreign_item) } fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> { diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index ba5d34359aa..b2048c534a4 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -1,6 +1,6 @@ // Code that generates a test runner to run all the tests in a crate -use std::{iter, mem}; +use std::mem; use rustc_ast as ast; use rustc_ast::entry::EntryPointType; @@ -19,7 +19,7 @@ use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency}; use rustc_span::symbol::{Ident, Symbol, sym}; use rustc_span::{DUMMY_SP, Span}; use rustc_target::spec::PanicStrategy; -use smallvec::{SmallVec, smallvec}; +use smallvec::smallvec; use thin_vec::{ThinVec, thin_vec}; use tracing::debug; @@ -129,8 +129,9 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { c.items.push(mk_main(&mut self.cx)); } - fn flat_map_item(&mut self, mut i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> { - let item = &mut *i; + fn visit_item(&mut self, item: &mut P<ast::Item>) { + let item = &mut **item; + if let Some(name) = get_test_name(&item) { debug!("this is a test item"); @@ -158,7 +159,6 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { // But in those cases, we emit a lint to warn the user of these missing tests. walk_item(&mut InnerItemLinter { sess: self.cx.ext_cx.sess }, &item); } - smallvec![i] } } @@ -198,40 +198,30 @@ struct EntryPointCleaner<'a> { } impl<'a> MutVisitor for EntryPointCleaner<'a> { - fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> { + fn visit_item(&mut self, item: &mut P<ast::Item>) { self.depth += 1; - let item = walk_flat_map_item(self, i).expect_one("noop did something"); + ast::mut_visit::walk_item(self, item); self.depth -= 1; // Remove any #[rustc_main] or #[start] from the AST so it doesn't // clash with the one we're going to add, but mark it as // #[allow(dead_code)] to avoid printing warnings. - let item = match entry_point_type(&item, self.depth == 0) { + match entry_point_type(&item, self.depth == 0) { EntryPointType::MainNamed | EntryPointType::RustcMainAttr | EntryPointType::Start => { - item.map(|ast::Item { id, ident, attrs, kind, vis, span, tokens }| { - let allow_dead_code = attr::mk_attr_nested_word( - &self.sess.psess.attr_id_generator, - ast::AttrStyle::Outer, - ast::Safety::Default, - sym::allow, - sym::dead_code, - self.def_site, - ); - let attrs = attrs - .into_iter() - .filter(|attr| { - !attr.has_name(sym::rustc_main) && !attr.has_name(sym::start) - }) - .chain(iter::once(allow_dead_code)) - .collect(); - - ast::Item { id, ident, attrs, kind, vis, span, tokens } - }) + let allow_dead_code = attr::mk_attr_nested_word( + &self.sess.psess.attr_id_generator, + ast::AttrStyle::Outer, + ast::Safety::Default, + sym::allow, + sym::dead_code, + self.def_site, + ); + item.attrs + .retain(|attr| !attr.has_name(sym::rustc_main) && !attr.has_name(sym::start)); + item.attrs.push(allow_dead_code); } - EntryPointType::None | EntryPointType::OtherMain => item, + EntryPointType::None | EntryPointType::OtherMain => {} }; - - smallvec![item] } } @@ -292,7 +282,7 @@ fn generate_test_harness( /// Most of the Ident have the usual def-site hygiene for the AST pass. The /// exception is the `test_const`s. These have a syntax context that has two /// opaque marks: one from the expansion of `test` or `test_case`, and one -/// generated in `TestHarnessGenerator::flat_map_item`. When resolving this +/// generated in `TestHarnessGenerator::visit_item`. When resolving this /// identifier after failing to find a matching identifier in the root module /// we remove the outer mark, and try resolving at its def-site, which will /// then resolve to `test_const`. diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 77ee9773940..70b7d92ce15 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -924,6 +924,7 @@ fn codegen_stmt<'tcx>( | StatementKind::FakeRead(..) | StatementKind::Retag { .. } | StatementKind::PlaceMention(..) + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::AscribeUserType(..) => {} StatementKind::Coverage { .. } => unreachable!(), diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 5311547309c..abe6085b04f 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -583,6 +583,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( | StatementKind::PlaceMention(..) | StatementKind::Coverage(_) | StatementKind::ConstEvalCounter + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => {} } } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 4f3664a503d..6ee599c9964 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1279,7 +1279,7 @@ impl<'a> WasmLd<'a> { let mut wasm_ld = WasmLd { cmd, sess }; if sess.target_features.contains(&sym::atomics) { wasm_ld.link_args(&["--shared-memory", "--max-memory=1073741824", "--import-memory"]); - if sess.target.os == "unknown" { + if sess.target.os == "unknown" || sess.target.os == "none" { wasm_ld.link_args(&[ "--export=__wasm_init_tls", "--export=__tls_size", @@ -1403,7 +1403,7 @@ impl<'a> Linker for WasmLd<'a> { // symbols explicitly passed via the `--export` flags above and hides all // others. Various bits and pieces of wasm32-unknown-unknown tooling use // this, so be sure these symbols make their way out of the linker as well. - if self.sess.target.os == "unknown" { + if self.sess.target.os == "unknown" || self.sess.target.os == "none" { self.link_args(&["--export=__heap_base", "--export=__data_end"]); } } diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 1681ea1de5f..cd55a838a75 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -92,6 +92,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | mir::StatementKind::AscribeUserType(..) | mir::StatementKind::ConstEvalCounter | mir::StatementKind::PlaceMention(..) + | mir::StatementKind::BackwardIncompatibleDropHint { .. } | mir::StatementKind::Nop => {} } } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 8e96d365beb..1129b5caec5 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -609,6 +609,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { | StatementKind::Coverage(..) | StatementKind::Intrinsic(..) | StatementKind::ConstEvalCounter + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => {} } } diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 64331eea75c..647d880e2bf 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -7,7 +7,7 @@ use rustc_middle::bug; use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo}; use rustc_middle::mir::{self, ConstAlloc, ConstValue}; use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; @@ -30,7 +30,6 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( cid: GlobalId<'tcx>, body: &'tcx mir::Body<'tcx>, ) -> InterpResult<'tcx, R> { - trace!(?ecx.typing_env); let tcx = *ecx.tcx; assert!( cid.promoted.is_some() @@ -220,7 +219,7 @@ pub(super) fn op_to_const<'tcx>( let pointee_ty = imm.layout.ty.builtin_deref(false).unwrap(); // `false` = no raw ptrs debug_assert!( matches!( - ecx.tcx.struct_tail_for_codegen(pointee_ty, ecx.typing_env).kind(), + ecx.tcx.struct_tail_for_codegen(pointee_ty, ecx.typing_env()).kind(), ty::Str | ty::Slice(..), ), "`ConstValue::Slice` is for slice-tailed types only, but got {}", diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index db82050c73c..b27e3606f38 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -9,7 +9,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem}; use rustc_middle::mir::AssertMessage; use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::Span; @@ -667,7 +667,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { .is_some_and(|p| !p.immutable()) { // That next check is expensive, that's why we have all the guards above. - let is_immutable = ty.is_freeze(*ecx.tcx, ecx.typing_env); + let is_immutable = ty.is_freeze(*ecx.tcx, ecx.typing_env()); let place = ecx.ref_to_mplace(val)?; let new_place = if is_immutable { place.map_provenance(CtfeProvenance::as_immutable) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 42fdf32e91e..fe93a48c2f2 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -39,8 +39,8 @@ pub struct InterpCx<'tcx, M: Machine<'tcx>> { pub tcx: TyCtxtAt<'tcx>, /// The current context in case we're evaluating in a - /// polymorphic context. This always uses `ty::TypingMode::PostAnalysis` - pub typing_env: ty::TypingEnv<'tcx>, + /// polymorphic context. This always uses `ty::TypingMode::PostAnalysis`. + pub(super) typing_env: ty::TypingEnv<'tcx>, /// The virtual memory system. pub memory: Memory<'tcx, M>, diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 98aca37e73e..b61865be667 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -143,6 +143,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. Nop => {} + + // Only used for temporary lifetime lints + BackwardIncompatibleDropHint { .. } => {} } interp_ok(()) diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs index bafb16a8b5e..34895d3efe6 100644 --- a/compiler/rustc_data_structures/src/unord.rs +++ b/compiler/rustc_data_structures/src/unord.rs @@ -602,6 +602,11 @@ impl<K: Eq + Hash, V> UnordMap<K, V> { .into_iter() .map(|(_, v)| v) } + + #[inline] + pub fn clear(&mut self) { + self.inner.clear() + } } impl<K, Q: ?Sized, V> Index<&Q> for UnordMap<K, V> diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl index 31837e01764..05e11c4527f 100644 --- a/compiler/rustc_driver_impl/messages.ftl +++ b/compiler/rustc_driver_impl/messages.ftl @@ -23,3 +23,5 @@ driver_impl_rlink_rustc_version_mismatch = .rlink file was produced by rustc ver driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}` driver_impl_rlink_wrong_file_type = The input does not look like a .rlink file + +driver_impl_unstable_feature_usage = cannot dump feature usage metrics: {$error} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 8dd043be6ad..c270ce16726 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -15,6 +15,7 @@ #![feature(panic_update_hook)] #![feature(result_flattening)] #![feature(rustdoc_internals)] +#![feature(try_blocks)] #![warn(unreachable_pub)] // tidy-alphabetical-end @@ -50,6 +51,7 @@ use rustc_interface::{Linker, Queries, interface, passes}; use rustc_lint::unerased_lint_store; use rustc_metadata::creader::MetadataLoader; use rustc_metadata::locator; +use rustc_middle::ty::TyCtxt; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_session::config::{ CG_OPTIONS, ErrorOutputType, Input, OutFileName, OutputType, UnstableOptions, Z_OPTIONS, @@ -102,7 +104,7 @@ mod signal_handler { use crate::session_diagnostics::{ RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch, - RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead, + RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead, UnstableFeatureUsage, }; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -430,6 +432,10 @@ fn run_compiler( // Make sure name resolution and macro expansion is run. queries.global_ctxt()?.enter(|tcx| tcx.resolver_for_lowering()); + if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir { + queries.global_ctxt()?.enter(|tcxt| dump_feature_usage_metrics(tcxt, metrics_dir)); + } + if callbacks.after_expansion(compiler, queries) == Compilation::Stop { return early_exit(); } @@ -474,6 +480,23 @@ fn run_compiler( }) } +fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &PathBuf) { + let output_filenames = tcxt.output_filenames(()); + let mut metrics_file_name = std::ffi::OsString::from("unstable_feature_usage_metrics-"); + let mut metrics_path = output_filenames.with_directory_and_extension(metrics_dir, "json"); + let metrics_file_stem = + metrics_path.file_name().expect("there should be a valid default output filename"); + metrics_file_name.push(metrics_file_stem); + metrics_path.pop(); + metrics_path.push(metrics_file_name); + if let Err(error) = tcxt.features().dump_feature_usage_metrics(metrics_path) { + // FIXME(yaahc): once metrics can be enabled by default we will want "failure to emit + // default metrics" to only produce a warning when metrics are enabled by default and emit + // an error only when the user manually enables metrics + tcxt.dcx().emit_err(UnstableFeatureUsage { error }); + } +} + // Extract output directory and file from matches. fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<OutFileName>) { let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o)); @@ -564,71 +587,63 @@ fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, col } } -/// If color is always or auto, print formatted & colorized markdown. If color is never or -/// if formatted printing fails, print the raw text. +/// If `color` is `always` or `auto`, try to print pretty (formatted & colorized) markdown. If +/// that fails or `color` is `never`, print the raw markdown. /// -/// Prefers a pager, falls back standard print +/// Uses a pager if possible, falls back to stdout. fn show_md_content_with_pager(content: &str, color: ColorConfig) { - let mut fallback_to_println = false; let pager_name = env::var_os("PAGER").unwrap_or_else(|| { if cfg!(windows) { OsString::from("more.com") } else { OsString::from("less") } }); let mut cmd = Command::new(&pager_name); - // FIXME: find if other pagers accept color options - let mut print_formatted = if pager_name == "less" { - cmd.arg("-R"); - true - } else { - ["bat", "catbat", "delta"].iter().any(|v| *v == pager_name) - }; - - if color == ColorConfig::Never { - print_formatted = false; - } else if color == ColorConfig::Always { - print_formatted = true; - } - - let mdstream = markdown::MdStream::parse_str(content); - let bufwtr = markdown::create_stdout_bufwtr(); - let mut mdbuf = bufwtr.buffer(); - if mdstream.write_termcolor_buf(&mut mdbuf).is_err() { - print_formatted = false; + if pager_name == "less" { + cmd.arg("-R"); // allows color escape sequences } - if let Ok(mut pager) = cmd.stdin(Stdio::piped()).spawn() { - if let Some(pipe) = pager.stdin.as_mut() { - let res = if print_formatted { - pipe.write_all(mdbuf.as_slice()) - } else { - pipe.write_all(content.as_bytes()) - }; - - if res.is_err() { - fallback_to_println = true; - } + let pretty_on_pager = match color { + ColorConfig::Auto => { + // Add other pagers that accept color escape sequences here. + ["less", "bat", "batcat", "delta"].iter().any(|v| *v == pager_name) } + ColorConfig::Always => true, + ColorConfig::Never => false, + }; - if pager.wait().is_err() { - fallback_to_println = true; - } - } else { - fallback_to_println = true; - } + // Try to prettify the raw markdown text. The result can be used by the pager or on stdout. + let pretty_data = { + let mdstream = markdown::MdStream::parse_str(content); + let bufwtr = markdown::create_stdout_bufwtr(); + let mut mdbuf = bufwtr.buffer(); + if mdstream.write_termcolor_buf(&mut mdbuf).is_ok() { Some((bufwtr, mdbuf)) } else { None } + }; + + // Try to print via the pager, pretty output if possible. + let pager_res: Option<()> = try { + let mut pager = cmd.stdin(Stdio::piped()).spawn().ok()?; - // If pager fails for whatever reason, we should still print the content - // to standard output - if fallback_to_println { - let fmt_success = match color { - ColorConfig::Auto => io::stdout().is_terminal() && bufwtr.print(&mdbuf).is_ok(), - ColorConfig::Always => bufwtr.print(&mdbuf).is_ok(), - ColorConfig::Never => false, + let pager_stdin = pager.stdin.as_mut()?; + if pretty_on_pager && let Some((_, mdbuf)) = &pretty_data { + pager_stdin.write_all(mdbuf.as_slice()).ok()?; + } else { + pager_stdin.write_all(content.as_bytes()).ok()?; }; - if !fmt_success { - safe_print!("{content}"); - } + pager.wait().ok()?; + }; + if pager_res.is_some() { + return; + } + + // The pager failed. Try to print pretty output to stdout. + if let Some((bufwtr, mdbuf)) = &pretty_data + && bufwtr.print(&mdbuf).is_ok() + { + return; } + + // Everything failed. Print the raw markdown text. + safe_print!("{content}"); } fn process_rlink(sess: &Session, compiler: &interface::Compiler) { diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index 449878f28c4..e06c56539d1 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -1,3 +1,5 @@ +use std::error::Error; + use rustc_macros::{Diagnostic, Subdiagnostic}; #[derive(Diagnostic)] @@ -93,3 +95,9 @@ pub(crate) struct IceFlags { #[derive(Diagnostic)] #[diag(driver_impl_ice_exclude_cargo_defaults)] pub(crate) struct IceExcludeCargoDefaults; + +#[derive(Diagnostic)] +#[diag(driver_impl_unstable_feature_usage)] +pub(crate) struct UnstableFeatureUsage { + pub error: Box<dyn Error>, +} diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 91786462b40..19c2d466f7c 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1382,7 +1382,7 @@ impl InvocationCollectorNode for P<ast::ForeignItem> { fragment.make_foreign_items() } fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { - walk_flat_map_item(visitor, self) + walk_flat_map_foreign_item(visitor, self) } fn is_mac_call(&self) -> bool { matches!(self.kind, ForeignItemKind::MacCall(..)) diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index ffcce1e52f6..77b8d228922 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use rustc_ast::token::{self, Token, TokenKind}; +use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage}; use rustc_macros::Subdiagnostic; @@ -68,7 +68,9 @@ pub(super) fn failed_to_match_macro( if let MatcherLoc::Token { token: expected_token } = &remaining_matcher && (matches!(expected_token.kind, TokenKind::Interpolated(_)) - || matches!(token.kind, TokenKind::Interpolated(_))) + || matches!(token.kind, TokenKind::Interpolated(_)) + || matches!(expected_token.kind, TokenKind::OpenDelim(Delimiter::Invisible(_))) + || matches!(token.kind, TokenKind::OpenDelim(Delimiter::Invisible(_)))) { err.note("captured metavariables except for `:tt`, `:ident` and `:lifetime` cannot be compared to other tokens"); err.note("see <https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment> for more information"); diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index fcc90c3ce0d..a373c753cc1 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -693,7 +693,7 @@ fn has_compile_error_macro(rhs: &mbe::TokenTree) -> bool { && let mbe::TokenTree::Token(bang) = bang && let TokenKind::Not = bang.kind && let mbe::TokenTree::Delimited(.., del) = args - && del.delim != Delimiter::Invisible + && !del.delim.skip() { true } else { diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 1345f06d5ac..36094707fac 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -165,11 +165,12 @@ fn parse_tree<'a>( // during parsing. let mut next = outer_trees.next(); let mut trees: Box<dyn Iterator<Item = &tokenstream::TokenTree>>; - if let Some(tokenstream::TokenTree::Delimited(.., Delimiter::Invisible, tts)) = next { - trees = Box::new(tts.trees()); - next = trees.next(); - } else { - trees = Box::new(outer_trees); + match next { + Some(tokenstream::TokenTree::Delimited(.., delim, tts)) if delim.skip() => { + trees = Box::new(tts.trees()); + next = trees.next(); + } + _ => trees = Box::new(outer_trees), } match next { diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 90206b19bd5..bae16a18bcb 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -296,7 +296,7 @@ impl MutVisitor for PlaceholderExpander { ) -> SmallVec<[P<ast::ForeignItem>; 1]> { match item.kind { ast::ForeignItemKind::MacCall(_) => self.remove(item.id).make_foreign_items(), - _ => walk_flat_map_item(self, item), + _ => walk_flat_map_foreign_item(self, item), } } diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 0dc35618ff8..263df235b3e 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -38,7 +38,7 @@ impl FromInternal<token::Delimiter> for Delimiter { token::Delimiter::Parenthesis => Delimiter::Parenthesis, token::Delimiter::Brace => Delimiter::Brace, token::Delimiter::Bracket => Delimiter::Bracket, - token::Delimiter::Invisible => Delimiter::None, + token::Delimiter::Invisible(_) => Delimiter::None, } } } @@ -49,7 +49,7 @@ impl ToInternal<token::Delimiter> for Delimiter { Delimiter::Parenthesis => token::Delimiter::Parenthesis, Delimiter::Brace => token::Delimiter::Brace, Delimiter::Bracket => token::Delimiter::Bracket, - Delimiter::None => token::Delimiter::Invisible, + Delimiter::None => token::Delimiter::Invisible(token::InvisibleOrigin::ProcMacro), } } } diff --git a/compiler/rustc_feature/Cargo.toml b/compiler/rustc_feature/Cargo.toml index 9df320e1279..77de7fabd4f 100644 --- a/compiler/rustc_feature/Cargo.toml +++ b/compiler/rustc_feature/Cargo.toml @@ -7,4 +7,6 @@ edition = "2021" # tidy-alphabetical-start rustc_data_structures = { path = "../rustc_data_structures" } rustc_span = { path = "../rustc_span" } +serde = { version = "1.0.125", features = [ "derive" ] } +serde_json = "1.0.59" # tidy-alphabetical-end diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 2acebebb419..e3dc73c1401 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -1,5 +1,7 @@ //! List of the unstable feature gates. +use std::path::PathBuf; + use rustc_data_structures::fx::FxHashSet; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; @@ -651,6 +653,54 @@ declare_features! ( // ------------------------------------------------------------------------- ); +impl Features { + pub fn dump_feature_usage_metrics( + &self, + metrics_path: PathBuf, + ) -> Result<(), Box<dyn std::error::Error>> { + #[derive(serde::Serialize)] + struct LibFeature { + symbol: String, + } + + #[derive(serde::Serialize)] + struct LangFeature { + symbol: String, + since: Option<String>, + } + + #[derive(serde::Serialize)] + struct FeatureUsage { + lib_features: Vec<LibFeature>, + lang_features: Vec<LangFeature>, + } + + let metrics_file = std::fs::File::create(metrics_path)?; + let metrics_file = std::io::BufWriter::new(metrics_file); + + let lib_features = self + .enabled_lib_features + .iter() + .map(|EnabledLibFeature { gate_name, .. }| LibFeature { symbol: gate_name.to_string() }) + .collect(); + + let lang_features = self + .enabled_lang_features + .iter() + .map(|EnabledLangFeature { gate_name, stable_since, .. }| LangFeature { + symbol: gate_name.to_string(), + since: stable_since.map(|since| since.to_string()), + }) + .collect(); + + let feature_usage = FeatureUsage { lib_features, lang_features }; + + serde_json::to_writer(metrics_file, &feature_usage)?; + + Ok(()) + } +} + /// Some features are not allowed to be used together at the same time, if /// the two are present, produce an error. /// diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index aa9d303cacb..cf3f3003bf5 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -339,6 +339,8 @@ fn check_opaque_meets_bounds<'tcx>( let misc_cause = ObligationCause::misc(span, def_id); // FIXME: We should just register the item bounds here, rather than equating. + // FIXME(const_trait_impl): When we do that, please make sure to also register + // the `~const` bounds. match ocx.eq(&misc_cause, param_env, opaque_ty, hidden_ty) { Ok(()) => {} Err(ty_err) => { diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 8a051e34f82..bd0b0ceb92d 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -2083,7 +2083,7 @@ pub(super) fn check_type_bounds<'tcx>( // Only in a const implementation do we need to check that the `~const` item bounds hold. if tcx.is_conditionally_const(impl_ty_def_id) { obligations.extend( - tcx.implied_const_bounds(trait_ty.def_id) + tcx.explicit_implied_const_bounds(trait_ty.def_id) .iter_instantiated_copied(tcx, rebased_args) .map(|(c, span)| { traits::Obligation::new( diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 679f6ccb816..b9cb48cafdc 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -17,6 +17,7 @@ use rustc_index::Idx; use rustc_middle::bug; use rustc_middle::middle::region::*; use rustc_middle::ty::TyCtxt; +use rustc_session::lint; use rustc_span::source_map; use tracing::debug; @@ -167,8 +168,23 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h } } if let Some(tail_expr) = blk.expr { - if blk.span.edition().at_least_rust_2024() { - visitor.terminating_scopes.insert(tail_expr.hir_id.local_id); + let local_id = tail_expr.hir_id.local_id; + let edition = blk.span.edition(); + if edition.at_least_rust_2024() { + visitor.terminating_scopes.insert(local_id); + } else if !visitor + .tcx + .lints_that_dont_need_to_run(()) + .contains(&lint::LintId::of(lint::builtin::TAIL_EXPR_DROP_ORDER)) + { + // If this temporary scope will be changing once the codebase adopts Rust 2024, + // and we are linting about possible semantic changes that would result, + // then record this node-id in the field `backwards_incompatible_scope` + // for future reference. + visitor + .scope_tree + .backwards_incompatible_scope + .insert(local_id, Scope { id: local_id, data: ScopeData::Node }); } visitor.visit_expr(tail_expr); } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index c2ad61820a7..3b49bc41ffe 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -37,22 +37,19 @@ pub(super) fn check_trait<'tcx>( ) -> Result<(), ErrorGuaranteed> { let lang_items = tcx.lang_items(); let checker = Checker { tcx, trait_def_id, impl_def_id, impl_header }; - let mut res = checker.check(lang_items.drop_trait(), visit_implementation_of_drop); - res = res.and(checker.check(lang_items.copy_trait(), visit_implementation_of_copy)); - res = res.and(checker.check(lang_items.const_param_ty_trait(), |checker| { + checker.check(lang_items.drop_trait(), visit_implementation_of_drop)?; + checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?; + checker.check(lang_items.const_param_ty_trait(), |checker| { visit_implementation_of_const_param_ty(checker, LangItem::ConstParamTy) - })); - res = res.and(checker.check(lang_items.unsized_const_param_ty_trait(), |checker| { + })?; + checker.check(lang_items.unsized_const_param_ty_trait(), |checker| { visit_implementation_of_const_param_ty(checker, LangItem::UnsizedConstParamTy) - })); - - res = res.and( - checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized), - ); - res.and( - checker - .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn), - ) + })?; + checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)?; + checker + .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn)?; + checker.check(lang_items.pointer_like(), visit_implementation_of_pointer_like)?; + Ok(()) } struct Checker<'tcx> { @@ -663,3 +660,63 @@ fn infringing_fields_error<'tcx>( err.emit() } + +fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let typing_env = ty::TypingEnv::non_body_analysis(tcx, checker.impl_def_id); + let impl_span = tcx.def_span(checker.impl_def_id); + let self_ty = tcx.impl_trait_ref(checker.impl_def_id).unwrap().instantiate_identity().self_ty(); + + // If an ADT is repr(transparent)... + if let ty::Adt(def, args) = *self_ty.kind() + && def.repr().transparent() + { + // FIXME(compiler-errors): This should and could be deduplicated into a query. + // Find the nontrivial field. + let adt_typing_env = ty::TypingEnv::non_body_analysis(tcx, def.did()); + let nontrivial_field = def.all_fields().find(|field_def| { + let field_ty = tcx.type_of(field_def.did).instantiate_identity(); + !tcx.layout_of(adt_typing_env.as_query_input(field_ty)) + .is_ok_and(|layout| layout.layout.is_1zst()) + }); + + if let Some(nontrivial_field) = nontrivial_field { + // Check that the nontrivial field implements `PointerLike`. + let nontrivial_field = nontrivial_field.ty(tcx, args); + let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); + let ocx = ObligationCtxt::new(&infcx); + ocx.register_bound( + ObligationCause::misc(impl_span, checker.impl_def_id), + param_env, + nontrivial_field, + tcx.lang_items().pointer_like().unwrap(), + ); + // FIXME(dyn-star): We should regionck this implementation. + if ocx.select_all_or_error().is_empty() { + return Ok(()); + } + } + } + + let is_permitted_primitive = match *self_ty.kind() { + ty::Adt(def, _) => def.is_box(), + ty::Uint(..) | ty::Int(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true, + _ => false, + }; + + if is_permitted_primitive + && let Ok(layout) = tcx.layout_of(typing_env.as_query_input(self_ty)) + && layout.layout.is_pointer_like(&tcx.data_layout) + { + return Ok(()); + } + + Err(tcx + .dcx() + .struct_span_err( + impl_span, + "implementation must be applied to type that has the same ABI as a pointer, \ + or is `repr(transparent)` and whose field is `PointerLike`", + ) + .emit()) +} diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 1a925597c6c..73b73afb0a5 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -78,7 +78,7 @@ pub fn provide(providers: &mut Providers) { predicates_of::explicit_supertraits_containing_assoc_item, trait_explicit_predicates_and_bounds: predicates_of::trait_explicit_predicates_and_bounds, const_conditions: predicates_of::const_conditions, - implied_const_bounds: predicates_of::implied_const_bounds, + explicit_implied_const_bounds: predicates_of::explicit_implied_const_bounds, type_param_predicates: predicates_of::type_param_predicates, trait_def, adt_def, @@ -340,6 +340,10 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { self.tcx.ensure().explicit_item_super_predicates(def_id); self.tcx.ensure().item_bounds(def_id); self.tcx.ensure().item_super_predicates(def_id); + if self.tcx.is_conditionally_const(def_id) { + self.tcx.ensure().explicit_implied_const_bounds(def_id); + self.tcx.ensure().const_conditions(def_id); + } intravisit::walk_opaque_ty(self, opaque); } @@ -682,6 +686,10 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.ensure().generics_of(item.owner_id); tcx.ensure().type_of(item.owner_id); tcx.ensure().predicates_of(item.owner_id); + if tcx.is_conditionally_const(def_id) { + tcx.ensure().explicit_implied_const_bounds(def_id); + tcx.ensure().const_conditions(def_id); + } match item.kind { hir::ForeignItemKind::Fn(..) => { tcx.ensure().codegen_fn_attrs(item.owner_id); diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 0f37d61beb0..b5dee5bd021 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -959,6 +959,12 @@ pub(super) fn const_conditions<'tcx>( hir::ForeignItemKind::Fn(_, _, generics) => (generics, None, false), _ => bug!("const_conditions called on wrong item: {def_id:?}"), }, + Node::OpaqueTy(opaque) => match opaque.origin { + hir::OpaqueTyOrigin::FnReturn { parent, .. } => return tcx.const_conditions(parent), + hir::OpaqueTyOrigin::AsyncFn { .. } | hir::OpaqueTyOrigin::TyAlias { .. } => { + unreachable!() + } + }, // N.B. Tuple ctors are unconditionally constant. Node::Ctor(hir::VariantData::Tuple { .. }) => return Default::default(), _ => bug!("const_conditions called on wrong item: {def_id:?}"), @@ -1018,7 +1024,7 @@ pub(super) fn const_conditions<'tcx>( } } -pub(super) fn implied_const_bounds<'tcx>( +pub(super) fn explicit_implied_const_bounds<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::PolyTraitRef<'tcx>, Span)]> { @@ -1034,10 +1040,11 @@ pub(super) fn implied_const_bounds<'tcx>( PredicateFilter::SelfConstIfConst, ) } - Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(..), .. }) => { + Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(..), .. }) + | Node::OpaqueTy(_) => { explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::ConstIfConst) } - _ => bug!("implied_const_bounds called on wrong item: {def_id:?}"), + _ => bug!("explicit_implied_const_bounds called on wrong item: {def_id:?}"), }; bounds.map_bound(|bounds| { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index dd0f250a8e2..4e5a8271e9e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -1106,7 +1106,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .collect::<String>() ), [(only, _)] => only.to_string(), - [] => "this type".to_string(), + [] => bug!("expected one segment to deny"), }; let arg_spans: Vec<Span> = segments @@ -1136,7 +1136,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { "s", ), [only] => (only.to_string(), ""), - [] => unreachable!("expected at least one generic to prohibit"), + [] => bug!("expected at least one generic to prohibit"), }; let last_span = *arg_spans.last().unwrap(); let span: MultiSpan = arg_spans.into(); diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 8772599e316..cff2aa68993 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -31,6 +31,7 @@ use rustc_span::symbol::{Ident, kw, sym}; use rustc_span::{ DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span, Symbol, edit_distance, }; +use rustc_trait_selection::error_reporting::traits::DefIdOrName; use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -45,50 +46,6 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem}; use crate::{Expectation, FnCtxt}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { - let tcx = self.tcx; - match ty.kind() { - // Not all of these (e.g., unsafe fns) implement `FnOnce`, - // so we look for these beforehand. - // FIXME(async_closures): These don't impl `FnOnce` by default. - ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(..) => true, - // If it's not a simple function, look for things which implement `FnOnce`. - _ => { - let Some(fn_once) = tcx.lang_items().fn_once_trait() else { - return false; - }; - - // This conditional prevents us from asking to call errors and unresolved types. - // It might seem that we can use `predicate_must_hold_modulo_regions`, - // but since a Dummy binder is used to fill in the FnOnce trait's arguments, - // type resolution always gives a "maybe" here. - if self.autoderef(span, ty).silence_errors().any(|(ty, _)| { - info!("check deref {:?} error", ty); - matches!(ty.kind(), ty::Error(_) | ty::Infer(_)) - }) { - return false; - } - - self.autoderef(span, ty).silence_errors().any(|(ty, _)| { - info!("check deref {:?} impl FnOnce", ty); - self.probe(|_| { - let trait_ref = - ty::TraitRef::new(tcx, fn_once, [ty, self.next_ty_var(span)]); - let poly_trait_ref = ty::Binder::dummy(trait_ref); - let obligation = Obligation::misc( - tcx, - span, - self.body_id, - self.param_env, - poly_trait_ref, - ); - self.predicate_may_hold(&obligation) - }) - }) - } - } - } - fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { self.autoderef(span, ty) .silence_errors() @@ -2367,12 +2324,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let is_accessible = field.vis.is_accessible_from(scope, tcx); if is_accessible { - if self.is_fn_ty(field_ty, span) { + if let Some((what, _, _)) = self.extract_callable_info(field_ty) { + let what = match what { + DefIdOrName::DefId(def_id) => self.tcx.def_descr(def_id), + DefIdOrName::Name(what) => what, + }; let expr_span = expr.span.to(item_name.span); err.multipart_suggestion( format!( - "to call the function stored in `{item_name}`, \ - surround the field access with parentheses", + "to call the {what} stored in `{item_name}`, \ + surround the field access with parentheses", ), vec![ (expr_span.shrink_to_lo(), '('.to_string()), diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index c2b9cae680b..a9239489222 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -460,6 +460,10 @@ impl<T: Idx> ChunkedBitSet<T> { self.chunks.iter().map(|chunk| chunk.count()).sum() } + pub fn is_empty(&self) -> bool { + self.chunks.iter().all(|chunk| matches!(chunk, Chunk::Zeros(..) | Chunk::Ones(0))) + } + /// Returns `true` if `self` contains `elem`. #[inline] pub fn contains(&self, elem: T) -> bool { @@ -668,12 +672,140 @@ impl<T: Idx> BitRelations<ChunkedBitSet<T>> for ChunkedBitSet<T> { changed } - fn subtract(&mut self, _other: &ChunkedBitSet<T>) -> bool { - unimplemented!("implement if/when necessary"); + fn subtract(&mut self, other: &ChunkedBitSet<T>) -> bool { + assert_eq!(self.domain_size, other.domain_size); + debug_assert_eq!(self.chunks.len(), other.chunks.len()); + + let mut changed = false; + for (mut self_chunk, other_chunk) in self.chunks.iter_mut().zip(other.chunks.iter()) { + match (&mut self_chunk, &other_chunk) { + (Zeros(..), _) | (_, Zeros(..)) => {} + ( + Ones(self_chunk_domain_size) | Mixed(self_chunk_domain_size, _, _), + Ones(other_chunk_domain_size), + ) => { + debug_assert_eq!(self_chunk_domain_size, other_chunk_domain_size); + changed = true; + *self_chunk = Zeros(*self_chunk_domain_size); + } + ( + Ones(self_chunk_domain_size), + Mixed(other_chunk_domain_size, other_chunk_count, other_chunk_words), + ) => { + debug_assert_eq!(self_chunk_domain_size, other_chunk_domain_size); + changed = true; + let num_words = num_words(*self_chunk_domain_size as usize); + debug_assert!(num_words > 0 && num_words <= CHUNK_WORDS); + let mut tail_mask = + 1 << (*other_chunk_domain_size - ((num_words - 1) * WORD_BITS) as u16) - 1; + let mut self_chunk_words = **other_chunk_words; + for word in self_chunk_words[0..num_words].iter_mut().rev() { + *word = !*word & tail_mask; + tail_mask = u64::MAX; + } + let self_chunk_count = *self_chunk_domain_size - *other_chunk_count; + debug_assert_eq!( + self_chunk_count, + self_chunk_words[0..num_words] + .iter() + .map(|w| w.count_ones() as ChunkSize) + .sum() + ); + *self_chunk = + Mixed(*self_chunk_domain_size, self_chunk_count, Rc::new(self_chunk_words)); + } + ( + Mixed( + self_chunk_domain_size, + ref mut self_chunk_count, + ref mut self_chunk_words, + ), + Mixed(_other_chunk_domain_size, _other_chunk_count, other_chunk_words), + ) => { + // See [`<Self as BitRelations<ChunkedBitSet<T>>>::union`] for the explanation + let op = |a: u64, b: u64| a & !b; + let num_words = num_words(*self_chunk_domain_size as usize); + if bitwise_changes( + &self_chunk_words[0..num_words], + &other_chunk_words[0..num_words], + op, + ) { + let self_chunk_words = Rc::make_mut(self_chunk_words); + let has_changed = bitwise( + &mut self_chunk_words[0..num_words], + &other_chunk_words[0..num_words], + op, + ); + debug_assert!(has_changed); + *self_chunk_count = self_chunk_words[0..num_words] + .iter() + .map(|w| w.count_ones() as ChunkSize) + .sum(); + if *self_chunk_count == 0 { + *self_chunk = Zeros(*self_chunk_domain_size); + } + changed = true; + } + } + } + } + changed } - fn intersect(&mut self, _other: &ChunkedBitSet<T>) -> bool { - unimplemented!("implement if/when necessary"); + fn intersect(&mut self, other: &ChunkedBitSet<T>) -> bool { + assert_eq!(self.domain_size, other.domain_size); + debug_assert_eq!(self.chunks.len(), other.chunks.len()); + + let mut changed = false; + for (mut self_chunk, other_chunk) in self.chunks.iter_mut().zip(other.chunks.iter()) { + match (&mut self_chunk, &other_chunk) { + (Zeros(..), _) | (_, Ones(..)) => {} + ( + Ones(self_chunk_domain_size), + Zeros(other_chunk_domain_size) | Mixed(other_chunk_domain_size, ..), + ) + | (Mixed(self_chunk_domain_size, ..), Zeros(other_chunk_domain_size)) => { + debug_assert_eq!(self_chunk_domain_size, other_chunk_domain_size); + changed = true; + *self_chunk = other_chunk.clone(); + } + ( + Mixed( + self_chunk_domain_size, + ref mut self_chunk_count, + ref mut self_chunk_words, + ), + Mixed(_other_chunk_domain_size, _other_chunk_count, other_chunk_words), + ) => { + // See [`<Self as BitRelations<ChunkedBitSet<T>>>::union`] for the explanation + let op = |a, b| a & b; + let num_words = num_words(*self_chunk_domain_size as usize); + if bitwise_changes( + &self_chunk_words[0..num_words], + &other_chunk_words[0..num_words], + op, + ) { + let self_chunk_words = Rc::make_mut(self_chunk_words); + let has_changed = bitwise( + &mut self_chunk_words[0..num_words], + &other_chunk_words[0..num_words], + op, + ); + debug_assert!(has_changed); + *self_chunk_count = self_chunk_words[0..num_words] + .iter() + .map(|w| w.count_ones() as ChunkSize) + .sum(); + if *self_chunk_count == 0 { + *self_chunk = Zeros(*self_chunk_domain_size); + } + changed = true; + } + } + } + } + + changed } } diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index d65ed72a8e8..0aff4620314 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -574,9 +574,8 @@ impl<'tcx> InferCtxt<'tcx> { // unexpected region errors. goals.push(Goal::new(tcx, param_env, ty::ClauseKind::WellFormed(hidden_ty.into()))); - let item_bounds = tcx.explicit_item_bounds(def_id); - for (predicate, _) in item_bounds.iter_instantiated_copied(tcx, args) { - let predicate = predicate.fold_with(&mut BottomUpFolder { + let replace_opaques_in = |clause: ty::Clause<'tcx>, goals: &mut Vec<_>| { + clause.fold_with(&mut BottomUpFolder { tcx, ty_op: |ty| match *ty.kind() { // We can't normalize associated types from `rustc_infer`, @@ -612,11 +611,31 @@ impl<'tcx> InferCtxt<'tcx> { }, lt_op: |lt| lt, ct_op: |ct| ct, - }); + }) + }; + + let item_bounds = tcx.explicit_item_bounds(def_id); + for (predicate, _) in item_bounds.iter_instantiated_copied(tcx, args) { + let predicate = replace_opaques_in(predicate, goals); // Require that the predicate holds for the concrete type. debug!(?predicate); goals.push(Goal::new(self.tcx, param_env, predicate)); } + + // If this opaque is being defined and it's conditionally const, + if self.tcx.is_conditionally_const(def_id) { + let item_bounds = tcx.explicit_implied_const_bounds(def_id); + for (predicate, _) in item_bounds.iter_instantiated_copied(tcx, args) { + let predicate = replace_opaques_in( + predicate.to_host_effect_clause(self.tcx, ty::BoundConstness::Maybe), + goals, + ); + + // Require that the predicate holds for the concrete type. + debug!(?predicate); + goals.push(Goal::new(self.tcx, param_env, predicate)); + } + } } } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 6e35d89b488..69fd7f2d8b2 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -772,9 +772,6 @@ lint_suspicious_double_ref_clone = lint_suspicious_double_ref_deref = using `.deref()` on a double reference, which returns `{$ty}` instead of dereferencing the inner type -lint_tail_expr_drop_order = these values and local bindings have significant drop implementation that will have a different drop order from that of Edition 2021 - .label = these values have significant drop implementation and will observe changes in drop order under Edition 2024 - lint_trailing_semi_macro = trailing semicolon in macro used in expression position .note1 = macro invocations at the end of a block are treated as expressions .note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}` diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index ef9b3dbd13b..4f3184f1d7c 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -230,15 +230,16 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> } fn visit_assoc_item(&mut self, item: &'a ast::AssocItem, ctxt: ast_visit::AssocCtxt) { - self.with_lint_attrs(item.id, &item.attrs, |cx| match ctxt { - ast_visit::AssocCtxt::Trait => { - lint_callback!(cx, check_trait_item, item); - ast_visit::walk_assoc_item(cx, item, ctxt); - } - ast_visit::AssocCtxt::Impl => { - lint_callback!(cx, check_impl_item, item); - ast_visit::walk_assoc_item(cx, item, ctxt); + self.with_lint_attrs(item.id, &item.attrs, |cx| { + match ctxt { + ast_visit::AssocCtxt::Trait => { + lint_callback!(cx, check_trait_item, item); + } + ast_visit::AssocCtxt::Impl => { + lint_callback!(cx, check_impl_item, item); + } } + ast_visit::walk_assoc_item(cx, item, ctxt); }); } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 86112277504..4cf5c7b4ff9 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -75,7 +75,6 @@ mod redundant_semicolon; mod reference_casting; mod shadowed_into_iter; mod static_mut_refs; -mod tail_expr_drop_order; mod traits; mod types; mod unit_bindings; @@ -116,7 +115,6 @@ use rustc_middle::ty::TyCtxt; use shadowed_into_iter::ShadowedIntoIter; pub use shadowed_into_iter::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; use static_mut_refs::*; -use tail_expr_drop_order::TailExprDropOrder; use traits::*; use types::*; use unit_bindings::*; @@ -240,7 +238,6 @@ late_lint_methods!( AsyncFnInTrait: AsyncFnInTrait, NonLocalDefinitions: NonLocalDefinitions::default(), ImplTraitOvercaptures: ImplTraitOvercaptures, - TailExprDropOrder: TailExprDropOrder, IfLetRescope: IfLetRescope::default(), StaticMutRefs: StaticMutRefs, UnqualifiedLocalImports: UnqualifiedLocalImports, diff --git a/compiler/rustc_lint/src/tail_expr_drop_order.rs b/compiler/rustc_lint/src/tail_expr_drop_order.rs deleted file mode 100644 index 19763ce1ec5..00000000000 --- a/compiler/rustc_lint/src/tail_expr_drop_order.rs +++ /dev/null @@ -1,308 +0,0 @@ -use std::mem::swap; - -use rustc_ast::UnOp; -use rustc_hir::def::Res; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Block, Expr, ExprKind, LetStmt, Pat, PatKind, QPath, StmtKind}; -use rustc_macros::LintDiagnostic; -use rustc_middle::ty; -use rustc_session::lint::FutureIncompatibilityReason; -use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::Span; -use rustc_span::edition::Edition; - -use crate::{LateContext, LateLintPass}; - -declare_lint! { - /// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location, - /// that runs a custom `Drop` destructor. - /// Some of them may be dropped earlier in Edition 2024 that they used to in Edition 2021 and prior. - /// This lint detects those cases and provides you information on those values and their custom destructor implementations. - /// Your discretion on this information is required. - /// - /// ### Example - /// ```rust,edition2021 - /// #![warn(tail_expr_drop_order)] - /// struct Droppy(i32); - /// impl Droppy { - /// fn get(&self) -> i32 { - /// self.0 - /// } - /// } - /// impl Drop for Droppy { - /// fn drop(&mut self) { - /// // This is a custom destructor and it induces side-effects that is observable - /// // especially when the drop order at a tail expression changes. - /// println!("loud drop {}", self.0); - /// } - /// } - /// fn edition_2021() -> i32 { - /// let another_droppy = Droppy(0); - /// Droppy(1).get() - /// } - /// fn main() { - /// edition_2021(); - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// In tail expression of blocks or function bodies, - /// values of type with significant `Drop` implementation has an ill-specified drop order - /// before Edition 2024 so that they are dropped only after dropping local variables. - /// Edition 2024 introduces a new rule with drop orders for them, - /// so that they are dropped first before dropping local variables. - /// - /// A significant `Drop::drop` destructor here refers to an explicit, arbitrary - /// implementation of the `Drop` trait on the type, with exceptions including `Vec`, - /// `Box`, `Rc`, `BTreeMap` and `HashMap` that are marked by the compiler otherwise - /// so long that the generic types have no significant destructor recursively. - /// In other words, a type has a significant drop destructor when it has a `Drop` implementation - /// or its destructor invokes a significant destructor on a type. - /// Since we cannot completely reason about the change by just inspecting the existence of - /// a significant destructor, this lint remains only a suggestion and is set to `allow` by default. - /// - /// This lint only points out the issue with `Droppy`, which will be dropped before `another_droppy` - /// does in Edition 2024. - /// No fix will be proposed by this lint. - /// However, the most probable fix is to hoist `Droppy` into its own local variable binding. - /// ```rust - /// struct Droppy(i32); - /// impl Droppy { - /// fn get(&self) -> i32 { - /// self.0 - /// } - /// } - /// fn edition_2024() -> i32 { - /// let value = Droppy(0); - /// let another_droppy = Droppy(1); - /// value.get() - /// } - /// ``` - pub TAIL_EXPR_DROP_ORDER, - Allow, - "Detect and warn on significant change in drop order in tail expression location", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), - reference: "issue #123739 <https://github.com/rust-lang/rust/issues/123739>", - }; -} - -declare_lint_pass!(TailExprDropOrder => [TAIL_EXPR_DROP_ORDER]); - -impl TailExprDropOrder { - fn check_fn_or_closure<'tcx>( - cx: &LateContext<'tcx>, - fn_kind: hir::intravisit::FnKind<'tcx>, - body: &'tcx hir::Body<'tcx>, - def_id: rustc_span::def_id::LocalDefId, - ) { - let mut locals = vec![]; - if matches!(fn_kind, hir::intravisit::FnKind::Closure) { - for &capture in cx.tcx.closure_captures(def_id) { - if matches!(capture.info.capture_kind, ty::UpvarCapture::ByValue) - && capture.place.ty().has_significant_drop(cx.tcx, cx.typing_env()) - { - locals.push(capture.var_ident.span); - } - } - } - for param in body.params { - if cx - .typeck_results() - .node_type(param.hir_id) - .has_significant_drop(cx.tcx, cx.typing_env()) - { - locals.push(param.span); - } - } - if let hir::ExprKind::Block(block, _) = body.value.kind { - LintVisitor { cx, locals }.check_block_inner(block); - } else { - LintTailExpr { cx, locals: &locals, is_root_tail_expr: true }.visit_expr(body.value); - } - } -} - -impl<'tcx> LateLintPass<'tcx> for TailExprDropOrder { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - fn_kind: hir::intravisit::FnKind<'tcx>, - _: &'tcx hir::FnDecl<'tcx>, - body: &'tcx hir::Body<'tcx>, - _: Span, - def_id: rustc_span::def_id::LocalDefId, - ) { - if !body.value.span.edition().at_least_rust_2024() { - Self::check_fn_or_closure(cx, fn_kind, body, def_id); - } - } -} - -struct LintVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - // We only record locals that have significant drops - locals: Vec<Span>, -} - -struct LocalCollector<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - locals: &'a mut Vec<Span>, -} - -impl<'a, 'tcx> Visitor<'tcx> for LocalCollector<'a, 'tcx> { - type Result = (); - fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { - if let PatKind::Binding(_binding_mode, id, ident, pat) = pat.kind { - let ty = self.cx.typeck_results().node_type(id); - if ty.has_significant_drop(self.cx.tcx, self.cx.typing_env()) { - self.locals.push(ident.span); - } - if let Some(pat) = pat { - self.visit_pat(pat); - } - } else { - intravisit::walk_pat(self, pat); - } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for LintVisitor<'a, 'tcx> { - fn visit_block(&mut self, block: &'tcx Block<'tcx>) { - let mut locals = <_>::default(); - swap(&mut locals, &mut self.locals); - self.check_block_inner(block); - swap(&mut locals, &mut self.locals); - } - fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) { - LocalCollector { cx: self.cx, locals: &mut self.locals }.visit_local(local); - } -} - -impl<'a, 'tcx> LintVisitor<'a, 'tcx> { - fn check_block_inner(&mut self, block: &Block<'tcx>) { - if block.span.at_least_rust_2024() { - // We only lint up to Edition 2021 - return; - } - let Some(tail_expr) = block.expr else { return }; - for stmt in block.stmts { - match stmt.kind { - StmtKind::Let(let_stmt) => self.visit_local(let_stmt), - StmtKind::Item(_) => {} - StmtKind::Expr(e) | StmtKind::Semi(e) => self.visit_expr(e), - } - } - if self.locals.is_empty() { - return; - } - LintTailExpr { cx: self.cx, locals: &self.locals, is_root_tail_expr: true } - .visit_expr(tail_expr); - } -} - -struct LintTailExpr<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - is_root_tail_expr: bool, - locals: &'a [Span], -} - -impl<'a, 'tcx> LintTailExpr<'a, 'tcx> { - fn expr_eventually_point_into_local(mut expr: &Expr<'tcx>) -> bool { - loop { - match expr.kind { - ExprKind::Index(access, _, _) | ExprKind::Field(access, _) => expr = access, - ExprKind::AddrOf(_, _, referee) | ExprKind::Unary(UnOp::Deref, referee) => { - expr = referee - } - ExprKind::Path(_) - if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind - && let [local, ..] = path.segments - && let Res::Local(_) = local.res => - { - return true; - } - _ => return false, - } - } - } - - fn expr_generates_nonlocal_droppy_value(&self, expr: &Expr<'tcx>) -> bool { - if Self::expr_eventually_point_into_local(expr) { - return false; - } - self.cx - .typeck_results() - .expr_ty(expr) - .has_significant_drop(self.cx.tcx, self.cx.typing_env()) - } -} - -impl<'a, 'tcx> Visitor<'tcx> for LintTailExpr<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if self.is_root_tail_expr { - self.is_root_tail_expr = false; - } else if self.expr_generates_nonlocal_droppy_value(expr) { - self.cx.tcx.emit_node_span_lint( - TAIL_EXPR_DROP_ORDER, - expr.hir_id, - expr.span, - TailExprDropOrderLint { spans: self.locals.to_vec() }, - ); - return; - } - match expr.kind { - ExprKind::Match(scrutinee, _, _) => self.visit_expr(scrutinee), - - ExprKind::ConstBlock(_) - | ExprKind::Array(_) - | ExprKind::Break(_, _) - | ExprKind::Continue(_) - | ExprKind::Ret(_) - | ExprKind::Become(_) - | ExprKind::Yield(_, _) - | ExprKind::InlineAsm(_) - | ExprKind::If(_, _, _) - | ExprKind::Loop(_, _, _, _) - | ExprKind::Closure(_) - | ExprKind::DropTemps(_) - | ExprKind::OffsetOf(_, _) - | ExprKind::Assign(_, _, _) - | ExprKind::AssignOp(_, _, _) - | ExprKind::Lit(_) - | ExprKind::Err(_) => {} - - ExprKind::MethodCall(_, _, _, _) - | ExprKind::Call(_, _) - | ExprKind::Type(_, _) - | ExprKind::Tup(_) - | ExprKind::Binary(_, _, _) - | ExprKind::Unary(_, _) - | ExprKind::Path(_) - | ExprKind::Let(_) - | ExprKind::Cast(_, _) - | ExprKind::Field(_, _) - | ExprKind::Index(_, _, _) - | ExprKind::AddrOf(_, _, _) - | ExprKind::Struct(_, _, _) - | ExprKind::Repeat(_, _) => intravisit::walk_expr(self, expr), - - ExprKind::Block(_, _) => { - // We do not lint further because the drop order stays the same inside the block - } - } - } - fn visit_block(&mut self, block: &'tcx Block<'tcx>) { - LintVisitor { cx: self.cx, locals: <_>::default() }.check_block_inner(block); - } -} - -#[derive(LintDiagnostic)] -#[diag(lint_tail_expr_drop_order)] -struct TailExprDropOrderLint { - #[label] - pub spans: Vec<Span>, -} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 313a7badf19..9036741e078 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -101,6 +101,7 @@ declare_lint_pass! { SINGLE_USE_LIFETIMES, SOFT_UNSTABLE, STABLE_FEATURES, + TAIL_EXPR_DROP_ORDER, TEST_UNSTABLE_LINT, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, TRIVIAL_CASTS, @@ -4995,6 +4996,83 @@ declare_lint! { } declare_lint! { + /// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location, + /// that runs a custom `Drop` destructor. + /// Some of them may be dropped earlier in Edition 2024 that they used to in Edition 2021 and prior. + /// This lint detects those cases and provides you information on those values and their custom destructor implementations. + /// Your discretion on this information is required. + /// + /// ### Example + /// ```rust,edition2021 + /// #![warn(tail_expr_drop_order)] + /// struct Droppy(i32); + /// impl Droppy { + /// fn get(&self) -> i32 { + /// self.0 + /// } + /// } + /// impl Drop for Droppy { + /// fn drop(&mut self) { + /// // This is a custom destructor and it induces side-effects that is observable + /// // especially when the drop order at a tail expression changes. + /// println!("loud drop {}", self.0); + /// } + /// } + /// fn edition_2021() -> i32 { + /// let another_droppy = Droppy(0); + /// Droppy(1).get() + /// } + /// fn main() { + /// edition_2021(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In tail expression of blocks or function bodies, + /// values of type with significant `Drop` implementation has an ill-specified drop order + /// before Edition 2024 so that they are dropped only after dropping local variables. + /// Edition 2024 introduces a new rule with drop orders for them, + /// so that they are dropped first before dropping local variables. + /// + /// A significant `Drop::drop` destructor here refers to an explicit, arbitrary + /// implementation of the `Drop` trait on the type, with exceptions including `Vec`, + /// `Box`, `Rc`, `BTreeMap` and `HashMap` that are marked by the compiler otherwise + /// so long that the generic types have no significant destructor recursively. + /// In other words, a type has a significant drop destructor when it has a `Drop` implementation + /// or its destructor invokes a significant destructor on a type. + /// Since we cannot completely reason about the change by just inspecting the existence of + /// a significant destructor, this lint remains only a suggestion and is set to `allow` by default. + /// + /// This lint only points out the issue with `Droppy`, which will be dropped before `another_droppy` + /// does in Edition 2024. + /// No fix will be proposed by this lint. + /// However, the most probable fix is to hoist `Droppy` into its own local variable binding. + /// ```rust + /// struct Droppy(i32); + /// impl Droppy { + /// fn get(&self) -> i32 { + /// self.0 + /// } + /// } + /// fn edition_2024() -> i32 { + /// let value = Droppy(0); + /// let another_droppy = Droppy(1); + /// value.get() + /// } + /// ``` + pub TAIL_EXPR_DROP_ORDER, + Allow, + "Detect and warn on significant change in drop order in tail expression location", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), + reference: "issue #123739 <https://github.com/rust-lang/rust/issues/123739>", + }; +} + +declare_lint! { /// The `rust_2024_guarded_string_incompatible_syntax` lint detects `#` tokens /// that will be parsed as part of a guarded string literal in Rust 2024. /// diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 045fd0565ba..a89096beb8c 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -275,7 +275,7 @@ provide! { tcx, def_id, other, cdata, defaultness => { table_direct } constness => { table_direct } const_conditions => { table } - implied_const_bounds => { table_defaulted_array } + explicit_implied_const_bounds => { table_defaulted_array } coerce_unsized_info => { Ok(cdata .root diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index b5391247cea..8378e7c6e9b 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1463,8 +1463,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record_array!(self.tables.module_children_non_reexports[def_id] <- module_children.iter().map(|child| child.res.def_id().index)); if self.tcx.is_const_trait(def_id) { - record_defaulted_array!(self.tables.implied_const_bounds[def_id] - <- self.tcx.implied_const_bounds(def_id).skip_binder()); + record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] + <- self.tcx.explicit_implied_const_bounds(def_id).skip_binder()); } } if let DefKind::TraitAlias = def_kind { @@ -1532,6 +1532,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.encode_explicit_item_super_predicates(def_id); record!(self.tables.opaque_ty_origin[def_id] <- self.tcx.opaque_ty_origin(def_id)); self.encode_precise_capturing_args(def_id); + if tcx.is_conditionally_const(def_id) { + record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] + <- tcx.explicit_implied_const_bounds(def_id).skip_binder()); + } } if tcx.impl_method_has_trait_impl_trait_tys(def_id) && let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id) @@ -1654,8 +1658,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.encode_explicit_item_bounds(def_id); self.encode_explicit_item_super_predicates(def_id); if tcx.is_conditionally_const(def_id) { - record_defaulted_array!(self.tables.implied_const_bounds[def_id] - <- self.tcx.implied_const_bounds(def_id).skip_binder()); + record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] + <- self.tcx.explicit_implied_const_bounds(def_id).skip_binder()); } } } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 58f58efb116..4a8f8521b4f 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -391,7 +391,7 @@ define_tables! { inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, explicit_super_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, explicit_implied_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, - implied_const_bounds: Table<DefIndex, LazyArray<(ty::PolyTraitRef<'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>>>, diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 4e44de33611..114211b27c1 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -236,6 +236,11 @@ pub struct ScopeTree { /// during type check based on a traversal of the AST. pub rvalue_candidates: HirIdMap<RvalueCandidateType>, + /// Backwards incompatible scoping that will be introduced in future editions. + /// This information is used later for linting to identify locals and + /// temporary values that will receive backwards-incompatible drop orders. + pub backwards_incompatible_scope: UnordMap<hir::ItemLocalId, Scope>, + /// If there are any `yield` nested within a scope, this map /// stores the `Span` of the last one and its index in the /// postorder of the Visitor traversal on the HIR. diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index f0e2b7a376c..2bfcd0a6227 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -834,6 +834,11 @@ impl Debug for Statement<'_> { Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"), ConstEvalCounter => write!(fmt, "ConstEvalCounter"), Nop => write!(fmt, "nop"), + BackwardIncompatibleDropHint { ref place, reason: _ } => { + // For now, we don't record the reason because there is only one use case, + // which is to report breaking change in drop order by Edition 2024 + write!(fmt, "backward incompatible drop({place:?})") + } } } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 2083279e128..fea940ea47c 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -432,6 +432,18 @@ pub enum StatementKind<'tcx> { /// No-op. Useful for deleting instructions without affecting statement indices. Nop, + + /// Marker statement indicating where `place` would be dropped. + /// This is semantically equivalent to `Nop`, so codegen and MIRI should interpret this + /// statement as such. + /// The only use case of this statement is for linting in MIR to detect temporary lifetime + /// changes. + BackwardIncompatibleDropHint { + /// Place to drop + place: Box<Place<'tcx>>, + /// Reason for backward incompatibility + reason: BackwardIncompatibleDropReason, + }, } impl StatementKind<'_> { @@ -452,6 +464,7 @@ impl StatementKind<'_> { StatementKind::Intrinsic(..) => "Intrinsic", StatementKind::ConstEvalCounter => "ConstEvalCounter", StatementKind::Nop => "Nop", + StatementKind::BackwardIncompatibleDropHint { .. } => "BackwardIncompatibleDropHint", } } } @@ -897,6 +910,21 @@ pub enum TerminatorKind<'tcx> { }, } +#[derive( + Clone, + Debug, + TyEncodable, + TyDecodable, + Hash, + HashStable, + PartialEq, + TypeFoldable, + TypeVisitable +)] +pub enum BackwardIncompatibleDropReason { + Edition2024, +} + impl TerminatorKind<'_> { /// Returns a simple string representation of a `TerminatorKind` variant, independent of any /// values it might hold (e.g. `TerminatorKind::Call` always returns `"Call"`). diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 9f9ee8497b6..62c340d99e3 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -452,6 +452,7 @@ macro_rules! make_mir_visitor { } StatementKind::ConstEvalCounter => {} StatementKind::Nop => {} + StatementKind::BackwardIncompatibleDropHint { .. } => {} } } diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 8fd99303622..970dc72e1ff 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -360,6 +360,14 @@ impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::TraitRef<'tcx>) { } } +impl<'tcx> Key for ty::ParamEnvAnd<'tcx, Ty<'tcx>> { + type Cache<V> = DefaultCache<Self, V>; + + fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + impl<'tcx> Key for ty::TraitRef<'tcx> { type Cache<V> = DefaultCache<Self, V>; diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 9ae519dfe7e..76338be33aa 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -697,7 +697,7 @@ rustc_queries! { separate_provide_extern } - query implied_const_bounds( + query explicit_implied_const_bounds( key: DefId ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::PolyTraitRef<'tcx>, Span)]> { desc { |tcx| "computing the implied `~const` bounds for `{}`", @@ -1448,6 +1448,28 @@ rustc_queries! { cache_on_disk_if { false } } + /// Returns a list of types which (a) have a potentially significant destructor + /// and (b) may be dropped as a result of dropping a value of some type `ty` + /// (in the given environment). + /// + /// The idea of "significant" drop is somewhat informal and is used only for + /// diagnostics and edition migrations. The idea is that a significant drop may have + /// some visible side-effect on execution; freeing memory is NOT considered a side-effect. + /// The rules are as follows: + /// * Type with no explicit drop impl do not have significant drop. + /// * Types with a drop impl are assumed to have significant drop unless they have a `#[rustc_insignificant_dtor]` annotation. + /// + /// Note that insignificant drop is a "shallow" property. A type like `Vec<LockGuard>` does not + /// have significant drop but the type `LockGuard` does, and so if `ty = Vec<LockGuard>` + /// then the return value would be `&[LockGuard]`. + /// *IMPORTANT*: *DO NOT* run this query before promoted MIR body is constructed, + /// because this query partially depends on that query. + /// Otherwise, there is a risk of query cycles. + query list_significant_drop_tys(ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx ty::List<Ty<'tcx>> { + desc { |tcx| "computing when `{}` has a significant destructor", ty.value } + cache_on_disk_if { false } + } + /// Computes the layout of a type. Note that this implicitly /// executes in "reveal all" mode, and will normalize the input type. query layout_of( diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 8f26e05cb72..9cf6bc1b777 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -247,13 +247,24 @@ pub struct Expr<'tcx> { pub ty: Ty<'tcx>, /// The lifetime of this expression if it should be spilled into a - /// temporary; should be `None` only if in a constant context - pub temp_lifetime: Option<region::Scope>, + /// temporary + pub temp_lifetime: TempLifetime, /// span of the expression in the source pub span: Span, } +/// Temporary lifetime information for THIR expressions +#[derive(Clone, Copy, Debug, HashStable)] +pub struct TempLifetime { + /// Lifetime for temporaries as expected. + /// This should be `None` in a constant context. + pub temp_lifetime: Option<region::Scope>, + /// If `Some(lt)`, indicates that the lifetime of this temporary will change to `lt` in a future edition. + /// If `None`, then no changes are expected, or lints are disabled. + pub backwards_incompatible: Option<region::Scope>, +} + #[derive(Clone, Debug, HashStable)] pub enum ExprKind<'tcx> { /// `Scope`s are used to explicitly mark destruction scopes, @@ -645,7 +656,7 @@ impl<'tcx> Pat<'tcx> { | Binding { subpattern: Some(subpattern), .. } | Deref { subpattern } | DerefPattern { subpattern, .. } - | InlineConstant { subpattern, .. } => subpattern.walk_(it), + | ExpandedConstant { subpattern, .. } => subpattern.walk_(it), Leaf { subpatterns } | Variant { subpatterns, .. } => { subpatterns.iter().for_each(|field| field.pattern.walk_(it)) } @@ -788,12 +799,17 @@ pub enum PatKind<'tcx> { value: mir::Const<'tcx>, }, - /// Inline constant found while lowering a pattern. - InlineConstant { - /// [LocalDefId] of the constant, we need this so that we have a + /// Pattern obtained by converting a constant (inline or named) to its pattern + /// representation using `const_to_pat`. + ExpandedConstant { + /// [DefId] of the constant, we need this so that we have a /// reference that can be used by unsafety checking to visit nested - /// unevaluated constants. - def: LocalDefId, + /// unevaluated constants and for diagnostics. If the `DefId` doesn't + /// correspond to a local crate, it points at the `const` item. + def_id: DefId, + /// If `false`, then `def_id` points at a `const` item, otherwise it + /// corresponds to a local inline const. + is_inline: bool, /// If the inline constant is used in a range pattern, this subpattern /// represents the range (if both ends are inline constants, there will /// be multiple InlineConstant wrappers). @@ -1087,7 +1103,7 @@ mod size_asserts { use super::*; // tidy-alphabetical-start static_assert_size!(Block, 48); - static_assert_size!(Expr<'_>, 64); + static_assert_size!(Expr<'_>, 72); static_assert_size!(ExprKind<'_>, 40); static_assert_size!(Pat<'_>, 64); static_assert_size!(PatKind<'_>, 48); diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 36f0e3d890c..81202a6eaad 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -247,7 +247,7 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( } } Constant { value: _ } => {} - InlineConstant { def: _, subpattern } => visitor.visit_pat(subpattern), + ExpandedConstant { def_id: _, is_inline: _, subpattern } => visitor.visit_pat(subpattern), Range(_) => {} Slice { prefix, slice, suffix } | Array { prefix, slice, suffix } => { for subpattern in prefix.iter() { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index ad42eacf823..55c29825fbc 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -393,12 +393,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) } - fn implied_const_bounds( + fn explicit_implied_const_bounds( self, def_id: DefId, ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Binder<'tcx, ty::TraitRef<'tcx>>>> { ty::EarlyBinder::bind( - self.implied_const_bounds(def_id).iter_identity_copied().map(|(c, _)| c), + self.explicit_implied_const_bounds(def_id).iter_identity_copied().map(|(c, _)| c), ) } @@ -602,19 +602,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.coroutine_is_async_gen(coroutine_def_id) } - // We don't use `TypingEnv` here as it's only defined in `rustc_middle` and - // `rustc_next_trait_solver` shouldn't have to know about it. - fn layout_is_pointer_like( - self, - typing_mode: ty::TypingMode<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - ) -> bool { - let typing_env = ty::TypingEnv { typing_mode, param_env }; - self.layout_of(self.erase_regions(typing_env).as_query_input(self.erase_regions(ty))) - .is_ok_and(|layout| layout.layout.is_pointer_like(&self.data_layout)) - } - type UnsizingParams = &'tcx rustc_index::bit_set::BitSet<u32>; fn unsizing_params_for_adt(self, adt_def_id: DefId) -> Self::UnsizingParams { self.unsizing_params_for_adt(adt_def_id) @@ -688,7 +675,6 @@ bidirectional_lang_item_map! { Metadata, Option, PointeeTrait, - PointerLike, Poll, Sized, TransmuteTrait, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index dd8286c8eb9..2bc055453a4 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2110,7 +2110,13 @@ impl<'tcx> TyCtxt<'tcx> { _ => bug!("unexpected parent item of associated item: {parent_def_id:?}"), } } - DefKind::Closure | DefKind::OpaqueTy => { + DefKind::OpaqueTy => match self.opaque_ty_origin(def_id) { + hir::OpaqueTyOrigin::FnReturn { parent, .. } => self.is_conditionally_const(parent), + hir::OpaqueTyOrigin::AsyncFn { .. } => false, + // FIXME(const_trait_impl): ATPITs could be conditionally const? + hir::OpaqueTyOrigin::TyAlias { .. } => false, + }, + DefKind::Closure => { // Closures and RPITs will eventually have const conditions // for `~const` bounds. false diff --git a/compiler/rustc_middle/src/ty/rvalue_scopes.rs b/compiler/rustc_middle/src/ty/rvalue_scopes.rs index 37dcf7c0d64..57c2d7623d2 100644 --- a/compiler/rustc_middle/src/ty/rvalue_scopes.rs +++ b/compiler/rustc_middle/src/ty/rvalue_scopes.rs @@ -18,15 +18,17 @@ impl RvalueScopes { } /// Returns the scope when the temp created by `expr_id` will be cleaned up. + /// It also emits a lint on potential backwards incompatible change to the temporary scope + /// which is *for now* always shortening. pub fn temporary_scope( &self, region_scope_tree: &ScopeTree, expr_id: hir::ItemLocalId, - ) -> Option<Scope> { + ) -> (Option<Scope>, Option<Scope>) { // Check for a designated rvalue scope. if let Some(&s) = self.map.get(&expr_id) { debug!("temporary_scope({expr_id:?}) = {s:?} [custom]"); - return s; + return (s, None); } // Otherwise, locate the innermost terminating scope @@ -34,27 +36,40 @@ impl RvalueScopes { // have an enclosing scope, hence no scope will be // returned. let mut id = Scope { id: expr_id, data: ScopeData::Node }; + let mut backwards_incompatible = None; while let Some(&(p, _)) = region_scope_tree.parent_map.get(&id) { match p.data { ScopeData::Destruction => { debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]"); - return Some(id); + return (Some(id), backwards_incompatible); } ScopeData::IfThenRescope => { debug!("temporary_scope({expr_id:?}) = {p:?} [enclosing]"); - return Some(p); + return (Some(p), backwards_incompatible); } ScopeData::Node | ScopeData::CallSite | ScopeData::Arguments | ScopeData::IfThen - | ScopeData::Remainder(_) => id = p, + | ScopeData::Remainder(_) => { + // If we haven't already passed through a backwards-incompatible node, + // then check if we are passing through one now and record it if so. + // This is for now only working for cases where a temporary lifetime is + // *shortened*. + if backwards_incompatible.is_none() { + backwards_incompatible = region_scope_tree + .backwards_incompatible_scope + .get(&p.item_local_id()) + .copied(); + } + id = p + } } } debug!("temporary_scope({expr_id:?}) = None"); - None + (None, backwards_incompatible) } /// Make an association between a sub-expression and an extended lifetime diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index d08809ef67b..c3e9bd302de 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -144,12 +144,20 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let mut targets = Vec::new(); for arm in rest { let arm = &self.thir[*arm]; - let PatKind::Constant { value } = arm.pattern.kind else { - return Err(ParseError { - span: arm.pattern.span, - item_description: format!("{:?}", arm.pattern.kind), - expected: "constant pattern".to_string(), - }); + let value = match arm.pattern.kind { + PatKind::Constant { value } => value, + PatKind::ExpandedConstant { ref subpattern, def_id: _, is_inline: false } + if let PatKind::Constant { value } = subpattern.kind => + { + value + } + _ => { + return Err(ParseError { + span: arm.pattern.span, + item_description: format!("{:?}", arm.pattern.kind), + expected: "constant pattern".to_string(), + }); + } }; values.push(value.eval_bits(self.tcx, self.typing_env)); targets.push(self.parse_block(arm.body)?); diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/build/expr/as_operand.rs index 159959d4f1f..0cab853196b 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs @@ -1,6 +1,5 @@ //! See docs in build/expr/mod.rs -use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::thir::*; use tracing::{debug, instrument}; @@ -9,6 +8,12 @@ use crate::build::expr::category::Category; use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary}; impl<'a, 'tcx> Builder<'a, 'tcx> { + /// Construct a temporary lifetime restricted to just the local scope + pub(crate) fn local_temp_lifetime(&self) -> TempLifetime { + let local_scope = self.local_scope(); + TempLifetime { temp_lifetime: Some(local_scope), backwards_incompatible: None } + } + /// Returns an operand suitable for use until the end of the current /// scope expression. /// @@ -21,8 +26,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block: BasicBlock, expr_id: ExprId, ) -> BlockAnd<Operand<'tcx>> { - let local_scope = self.local_scope(); - self.as_operand(block, Some(local_scope), expr_id, LocalInfo::Boring, NeedsTemporary::Maybe) + self.as_operand( + block, + self.local_temp_lifetime(), + expr_id, + LocalInfo::Boring, + NeedsTemporary::Maybe, + ) } /// Returns an operand suitable for use until the end of the current scope expression and @@ -80,8 +90,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block: BasicBlock, expr: ExprId, ) -> BlockAnd<Operand<'tcx>> { - let local_scope = self.local_scope(); - self.as_call_operand(block, Some(local_scope), expr) + self.as_call_operand(block, self.local_temp_lifetime(), expr) } /// Compile `expr` into a value that can be used as an operand. @@ -102,7 +111,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub(crate) fn as_operand( &mut self, mut block: BasicBlock, - scope: Option<region::Scope>, + scope: TempLifetime, expr_id: ExprId, local_info: LocalInfo<'tcx>, needs_temporary: NeedsTemporary, @@ -146,7 +155,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub(crate) fn as_call_operand( &mut self, mut block: BasicBlock, - scope: Option<region::Scope>, + scope: TempLifetime, expr_id: ExprId, ) -> BlockAnd<Operand<'tcx>> { let this = self; diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 9f6e0735b48..6ce88cdc39d 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -6,7 +6,6 @@ use std::iter; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_hir::def_id::LocalDefId; use rustc_middle::hir::place::{Projection as HirProjection, ProjectionKind as HirProjectionKind}; -use rustc_middle::middle::region; use rustc_middle::mir::AssertKind::BoundsCheck; use rustc_middle::mir::*; use rustc_middle::thir::*; @@ -598,7 +597,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { index: ExprId, mutability: Mutability, fake_borrow_temps: Option<&mut Vec<Local>>, - temp_lifetime: Option<region::Scope>, + temp_lifetime: TempLifetime, expr_span: Span, source_info: SourceInfo, ) -> BlockAnd<PlaceBuilder<'tcx>> { diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index a3fee38908b..3f89e337781 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -33,14 +33,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr_id: ExprId, ) -> BlockAnd<Rvalue<'tcx>> { let local_scope = self.local_scope(); - self.as_rvalue(block, Some(local_scope), expr_id) + self.as_rvalue( + block, + TempLifetime { temp_lifetime: Some(local_scope), backwards_incompatible: None }, + expr_id, + ) } /// Compile `expr`, yielding an rvalue. pub(crate) fn as_rvalue( &mut self, mut block: BasicBlock, - scope: Option<region::Scope>, + scope: TempLifetime, expr_id: ExprId, ) -> BlockAnd<Rvalue<'tcx>> { let this = self; @@ -171,7 +175,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info, kind: StatementKind::StorageLive(result), }); - if let Some(scope) = scope { + if let Some(scope) = scope.temp_lifetime { // schedule a shallow free of that memory, lest we unwind: this.schedule_drop_storage_and_value(expr_span, scope, result); } @@ -445,7 +449,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block = this.limit_capture_mutability( upvar_expr.span, upvar_expr.ty, - scope, + scope.temp_lifetime, block, arg, ) @@ -705,7 +709,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, mut block: BasicBlock, value: ExprId, - scope: Option<region::Scope>, + scope: TempLifetime, outer_source_info: SourceInfo, ) -> BlockAnd<Rvalue<'tcx>> { let this = self; diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs index b8b5e4634ed..466f67b1ba4 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs @@ -2,7 +2,6 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::HirId; -use rustc_middle::middle::region; use rustc_middle::middle::region::{Scope, ScopeData}; use rustc_middle::mir::*; use rustc_middle::thir::*; @@ -17,7 +16,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub(crate) fn as_temp( &mut self, block: BasicBlock, - temp_lifetime: Option<region::Scope>, + temp_lifetime: TempLifetime, expr_id: ExprId, mutability: Mutability, ) -> BlockAnd<Local> { @@ -31,7 +30,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn as_temp_inner( &mut self, mut block: BasicBlock, - temp_lifetime: Option<region::Scope>, + temp_lifetime: TempLifetime, expr_id: ExprId, mutability: Mutability, ) -> BlockAnd<Local> { @@ -47,8 +46,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } let expr_ty = expr.ty; - let deduplicate_temps = - this.fixed_temps_scope.is_some() && this.fixed_temps_scope == temp_lifetime; + let deduplicate_temps = this.fixed_temps_scope.is_some() + && this.fixed_temps_scope == temp_lifetime.temp_lifetime; let temp = if deduplicate_temps && let Some(temp_index) = this.fixed_temps.get(&expr_id) { *temp_index } else { @@ -76,7 +75,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { LocalInfo::BlockTailTemp(tail_info) } - _ if let Some(Scope { data: ScopeData::IfThenRescope, id }) = temp_lifetime => { + _ if let Some(Scope { data: ScopeData::IfThenRescope, id }) = + temp_lifetime.temp_lifetime => + { LocalInfo::IfThenRescopeTemp { if_then: HirId { owner: this.hir_id.owner, local_id: id }, } @@ -117,7 +118,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Anything with a shorter lifetime (e.g the `&foo()` in // `bar(&foo())` or anything within a block will keep the // regular drops just like runtime code. - if let Some(temp_lifetime) = temp_lifetime { + if let Some(temp_lifetime) = temp_lifetime.temp_lifetime { this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Storage); } } @@ -125,10 +126,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block = this.expr_into_dest(temp_place, block, expr_id).into_block(); - if let Some(temp_lifetime) = temp_lifetime { + if let Some(temp_lifetime) = temp_lifetime.temp_lifetime { this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value); } + if let Some(backwards_incompatible) = temp_lifetime.backwards_incompatible { + this.schedule_backwards_incompatible_drop(expr_span, backwards_incompatible, temp); + } + block.and(temp) } } diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 0dec56d21ae..bebb44faba6 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -139,7 +139,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // (#66975) Source could be a const of type `!`, so has to // exist in the generated MIR. unpack!( - block = this.as_temp(block, Some(this.local_scope()), source, Mutability::Mut) + block = + this.as_temp(block, this.local_temp_lifetime(), source, Mutability::Mut) ); // This is an optimization. If the expression was a call then we already have an @@ -321,7 +322,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let is_union = adt_def.is_union(); let active_field_index = is_union.then(|| fields[0].name); - let scope = this.local_scope(); + let scope = this.local_temp_lifetime(); // first process the set of fields that were provided // (evaluating them in order given by user) @@ -333,7 +334,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { unpack!( block = this.as_operand( block, - Some(scope), + scope, f.expr, LocalInfo::AggregateTemp, NeedsTemporary::Maybe, @@ -548,15 +549,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } ExprKind::Yield { value } => { - let scope = this.local_scope(); + let scope = this.local_temp_lifetime(); let value = unpack!( - block = this.as_operand( - block, - Some(scope), - value, - LocalInfo::Boring, - NeedsTemporary::No - ) + block = + this.as_operand(block, scope, value, LocalInfo::Boring, NeedsTemporary::No) ); let resume = this.cfg.start_new_block(); this.cfg.terminate(block, source_info, TerminatorKind::Yield { diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs index 02ca12028d3..15ee6dd014c 100644 --- a/compiler/rustc_mir_build/src/build/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs @@ -172,8 +172,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None }; - let temp = - unpack!(block = this.as_temp(block, statement_scope, expr_id, Mutability::Not)); + let temp = unpack!( + block = this.as_temp( + block, + TempLifetime { + temp_lifetime: statement_scope, + backwards_incompatible: None + }, + expr_id, + Mutability::Not + ) + ); if let Some(span) = adjusted_span { this.local_decls[temp].source_info.span = span; diff --git a/compiler/rustc_mir_build/src/build/matches/match_pair.rs b/compiler/rustc_mir_build/src/build/matches/match_pair.rs index fcbf84a41d9..2815b390375 100644 --- a/compiler/rustc_mir_build/src/build/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/build/matches/match_pair.rs @@ -162,7 +162,11 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { TestCase::Irrefutable { ascription: None, binding } } - PatKind::InlineConstant { subpattern: ref pattern, def, .. } => { + PatKind::ExpandedConstant { subpattern: ref pattern, def_id: _, is_inline: false } => { + subpairs.push(MatchPairTree::for_pattern(place_builder, pattern, cx)); + default_irrefutable() + } + PatKind::ExpandedConstant { subpattern: ref pattern, def_id, is_inline: true } => { // Apply a type ascription for the inline constant to the value at `match_pair.place` let ascription = place.map(|source| { let span = pattern.span; @@ -173,7 +177,7 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { }) .args; let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::TypeOf( - def.to_def_id(), + def_id, ty::UserArgs { args, user_self_ty: None }, )); let annotation = ty::CanonicalUserTypeAnnotation { diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index a62d4e9d873..5791460a6b1 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -202,8 +202,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Increment the decision depth, in case we encounter boolean expressions // further down. this.mcdc_increment_depth_if_enabled(); - let place = - unpack!(block = this.as_temp(block, Some(temp_scope), expr_id, mutability)); + let place = unpack!( + block = this.as_temp( + block, + TempLifetime { + temp_lifetime: Some(temp_scope), + backwards_incompatible: None + }, + expr_id, + mutability + ) + ); this.mcdc_decrement_depth_if_enabled(); let operand = Operand::Move(Place::from(place)); @@ -917,7 +926,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.visit_primary_bindings(subpattern, subpattern_user_ty, f) } - PatKind::InlineConstant { ref subpattern, .. } => { + PatKind::ExpandedConstant { ref subpattern, .. } => { self.visit_primary_bindings(subpattern, pattern_user_ty, f) } diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index e63fbeeac66..636e47b7ad2 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -89,7 +89,7 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::thir::{ExprId, LintLevel}; -use rustc_middle::{bug, span_bug}; +use rustc_middle::{bug, span_bug, ty}; use rustc_session::lint::Level; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Span}; @@ -151,6 +151,9 @@ struct DropData { /// Whether this is a value Drop or a StorageDead. kind: DropKind, + + /// Whether this is a backwards-incompatible drop lint + backwards_incompatible_lint: bool, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -274,8 +277,12 @@ impl DropTree { // represents the block in the tree that should be jumped to once all // of the required drops have been performed. let fake_source_info = SourceInfo::outermost(DUMMY_SP); - let fake_data = - DropData { source_info: fake_source_info, local: Local::MAX, kind: DropKind::Storage }; + let fake_data = DropData { + source_info: fake_source_info, + local: Local::MAX, + kind: DropKind::Storage, + backwards_incompatible_lint: false, + }; let drops = IndexVec::from_raw(vec![DropNode { data: fake_data, next: DropIdx::MAX }]); Self { drops, entry_points: Vec::new(), existing_drops_map: FxHashMap::default() } } @@ -763,7 +770,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let local = place.as_local().unwrap_or_else(|| bug!("projection in tail call args")); - Some(DropData { source_info, local, kind: DropKind::Value }) + Some(DropData { + source_info, + local, + kind: DropKind::Value, + backwards_incompatible_lint: false, + }) } Operand::Constant(_) => None, }) @@ -1019,9 +1031,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if local.index() <= self.arg_count { span_bug!( span, - "`schedule_drop` called with local {:?} and arg_count {}", + "`schedule_drop` called with body argument {:?} \ + but its storage does not require a drop", local, - self.arg_count, ) } false @@ -1089,6 +1101,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info: SourceInfo { span: scope_end, scope: scope.source_scope }, local, kind: drop_kind, + backwards_incompatible_lint: false, }); return; @@ -1098,6 +1111,45 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span_bug!(span, "region scope {:?} not in scope to drop {:?}", region_scope, local); } + /// Schedule emission of a backwards incompatible drop lint hint. + /// Applicable only to temporary values for now. + pub(crate) fn schedule_backwards_incompatible_drop( + &mut self, + span: Span, + region_scope: region::Scope, + local: Local, + ) { + if !self.local_decls[local].ty.has_significant_drop(self.tcx, ty::TypingEnv { + typing_mode: ty::TypingMode::non_body_analysis(), + param_env: self.param_env, + }) { + return; + } + for scope in self.scopes.scopes.iter_mut().rev() { + // Since we are inserting linting MIR statement, we have to invalidate the caches + scope.invalidate_cache(); + if scope.region_scope == region_scope { + let region_scope_span = region_scope.span(self.tcx, self.region_scope_tree); + let scope_end = self.tcx.sess.source_map().end_point(region_scope_span); + + scope.drops.push(DropData { + source_info: SourceInfo { span: scope_end, scope: scope.source_scope }, + local, + kind: DropKind::Value, + backwards_incompatible_lint: true, + }); + + return; + } + } + span_bug!( + span, + "region scope {:?} not in scope to drop {:?} for linting", + region_scope, + local + ); + } + /// Indicates that the "local operand" stored in `local` is /// *moved* at some point during execution (see `local_scope` for /// more information about what a "local operand" is -- in short, @@ -1378,16 +1430,25 @@ fn build_scope_drops<'tcx>( continue; } - unwind_drops.add_entry_point(block, unwind_to); - - let next = cfg.start_new_block(); - cfg.terminate(block, source_info, TerminatorKind::Drop { - place: local.into(), - target: next, - unwind: UnwindAction::Continue, - replace: false, - }); - block = next; + if drop_data.backwards_incompatible_lint { + cfg.push(block, Statement { + source_info, + kind: StatementKind::BackwardIncompatibleDropHint { + place: Box::new(local.into()), + reason: BackwardIncompatibleDropReason::Edition2024, + }, + }); + } else { + unwind_drops.add_entry_point(block, unwind_to); + let next = cfg.start_new_block(); + cfg.terminate(block, source_info, TerminatorKind::Drop { + place: local.into(), + target: next, + unwind: UnwindAction::Continue, + replace: false, + }); + block = next; + } } DropKind::Storage => { if storage_dead_on_unwind { diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 4a64a1f0f3e..b497e54d480 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -332,7 +332,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { PatKind::Wild | // these just wrap other patterns, which we recurse on below. PatKind::Or { .. } | - PatKind::InlineConstant { .. } | + PatKind::ExpandedConstant { .. } | PatKind::AscribeUserType { .. } | PatKind::Error(_) => {} } @@ -386,8 +386,12 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { visit::walk_pat(self, pat); self.inside_adt = old_inside_adt; } - PatKind::InlineConstant { def, .. } => { - self.visit_inner_body(*def); + PatKind::ExpandedConstant { def_id, is_inline, .. } => { + if let Some(def) = def_id.as_local() + && *is_inline + { + self.visit_inner_body(def); + } visit::walk_pat(self, pat); } _ => { @@ -533,8 +537,45 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { self.requires_unsafe(expr.span, DerefOfRawPointer); } } - ExprKind::InlineAsm { .. } => { + ExprKind::InlineAsm(box InlineAsmExpr { + asm_macro: _, + ref operands, + template: _, + options: _, + line_spans: _, + }) => { self.requires_unsafe(expr.span, UseOfInlineAssembly); + + // For inline asm, do not use `walk_expr`, since we want to handle the label block + // specially. + for op in &**operands { + use rustc_middle::thir::InlineAsmOperand::*; + match op { + In { expr, reg: _ } + | Out { expr: Some(expr), reg: _, late: _ } + | InOut { expr, reg: _, late: _ } => self.visit_expr(&self.thir()[*expr]), + SplitInOut { in_expr, out_expr, reg: _, late: _ } => { + self.visit_expr(&self.thir()[*in_expr]); + if let Some(out_expr) = out_expr { + self.visit_expr(&self.thir()[*out_expr]); + } + } + Out { expr: None, reg: _, late: _ } + | Const { value: _, span: _ } + | SymFn { value: _, span: _ } + | SymStatic { def_id: _ } => {} + Label { block } => { + // Label blocks are safe context. + // `asm!()` is forced to be wrapped inside unsafe. If there's no special + // treatment, the label blocks would also always be unsafe with no way + // of opting out. + self.in_safety_context(SafetyContext::Safe, |this| { + visit::walk_block(this, &this.thir()[*block]) + }); + } + } + } + return; } ExprKind::Adt(box AdtExpr { adt_def, diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 62c6d85b73f..676f7c98b8f 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -860,8 +860,10 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> { pub(crate) uncovered: Uncovered, #[subdiagnostic] pub(crate) inform: Option<Inform>, + #[label(mir_build_confused)] + pub(crate) interpreted_as_const: Option<Span>, #[subdiagnostic] - pub(crate) interpreted_as_const: Option<InterpretedAsConst>, + pub(crate) interpreted_as_const_sugg: Option<InterpretedAsConst>, #[subdiagnostic] pub(crate) adt_defined_here: Option<AdtDefinedHere<'tcx>>, #[note(mir_build_privately_uninhabited)] @@ -911,9 +913,9 @@ impl<'tcx> Subdiagnostic for AdtDefinedHere<'tcx> { #[suggestion( mir_build_interpreted_as_const, code = "{variable}_var", - applicability = "maybe-incorrect" + applicability = "maybe-incorrect", + style = "verbose" )] -#[label(mir_build_confused)] pub(crate) struct InterpretedAsConst { #[primary_span] pub(crate) span: Span, diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 198fa4ffb7a..47b7f332951 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -23,7 +23,6 @@ use tracing::{debug, info, instrument, trace}; use crate::errors; use crate::thir::cx::Cx; -use crate::thir::cx::region::Scope; use crate::thir::util::UserAnnotatedTyHelpers; impl<'tcx> Cx<'tcx> { @@ -240,7 +239,7 @@ impl<'tcx> Cx<'tcx> { fn mirror_expr_cast( &mut self, source: &'tcx hir::Expr<'tcx>, - temp_lifetime: Option<Scope>, + temp_lifetime: TempLifetime, span: Span, ) -> ExprKind<'tcx> { let tcx = self.tcx; @@ -325,7 +324,7 @@ impl<'tcx> Cx<'tcx> { fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> { let tcx = self.tcx; let expr_ty = self.typeck_results().expr_ty(expr); - let temp_lifetime = + let (temp_lifetime, backwards_incompatible) = self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id); let kind = match expr.kind { @@ -361,7 +360,7 @@ impl<'tcx> Cx<'tcx> { let arg_tys = args.iter().map(|e| self.typeck_results().expr_ty_adjusted(e)); let tupled_args = Expr { ty: Ty::new_tup_from_iter(tcx, arg_tys), - temp_lifetime, + temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, span: expr.span, kind: ExprKind::Tuple { fields: self.mirror_exprs(args) }, }; @@ -391,7 +390,10 @@ impl<'tcx> Cx<'tcx> { && let [value] = args { return Expr { - temp_lifetime, + temp_lifetime: TempLifetime { + temp_lifetime, + backwards_incompatible, + }, ty: expr_ty, span: expr.span, kind: ExprKind::Box { value: self.mirror_expr(value) }, @@ -811,13 +813,13 @@ impl<'tcx> Cx<'tcx> { }, hir::ExprKind::Loop(body, ..) => { let block_ty = self.typeck_results().node_type(body.hir_id); - let temp_lifetime = self + let (temp_lifetime, backwards_incompatible) = self .rvalue_scopes .temporary_scope(self.region_scope_tree, body.hir_id.local_id); let block = self.mirror_block(body); let body = self.thir.exprs.push(Expr { ty: block_ty, - temp_lifetime, + temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, span: self.thir[block].span, kind: ExprKind::Block { block }, }); @@ -838,13 +840,17 @@ impl<'tcx> Cx<'tcx> { expr, cast_ty.hir_id, user_ty, ); - let cast = self.mirror_expr_cast(source, temp_lifetime, expr.span); + let cast = self.mirror_expr_cast( + source, + TempLifetime { temp_lifetime, backwards_incompatible }, + expr.span, + ); if let Some(user_ty) = user_ty { // NOTE: Creating a new Expr and wrapping a Cast inside of it may be // inefficient, revisit this when performance becomes an issue. let cast_expr = self.thir.exprs.push(Expr { - temp_lifetime, + temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, ty: expr_ty, span: expr.span, kind: cast, @@ -887,7 +893,12 @@ impl<'tcx> Cx<'tcx> { hir::ExprKind::Err(_) => unreachable!("cannot lower a `hir::ExprKind::Err` to THIR"), }; - Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind } + Expr { + temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + ty: expr_ty, + span: expr.span, + kind, + } } fn user_args_applied_to_res( @@ -931,7 +942,7 @@ impl<'tcx> Cx<'tcx> { span: Span, overloaded_callee: Option<Ty<'tcx>>, ) -> Expr<'tcx> { - let temp_lifetime = + let (temp_lifetime, backwards_incompatible) = self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id); let (ty, user_ty) = match overloaded_callee { Some(fn_def) => (fn_def, None), @@ -952,7 +963,12 @@ impl<'tcx> Cx<'tcx> { ) } }; - Expr { temp_lifetime, ty, span, kind: ExprKind::ZstLiteral { user_ty } } + Expr { + temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + ty, + span, + kind: ExprKind::ZstLiteral { user_ty }, + } } fn convert_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) -> ArmId { @@ -1025,7 +1041,7 @@ impl<'tcx> Cx<'tcx> { Res::Def(DefKind::Static { .. }, id) => { // this is &raw for extern static or static mut, and & for other statics let ty = self.tcx.static_ptr_ty(id, self.typing_env()); - let temp_lifetime = self + let (temp_lifetime, backwards_incompatible) = self .rvalue_scopes .temporary_scope(self.region_scope_tree, expr.hir_id.local_id); let kind = if self.tcx.is_thread_local_static(id) { @@ -1035,7 +1051,12 @@ impl<'tcx> Cx<'tcx> { ExprKind::StaticRef { alloc_id, ty, def_id: id } }; ExprKind::Deref { - arg: self.thir.exprs.push(Expr { ty, temp_lifetime, span: expr.span, kind }), + arg: self.thir.exprs.push(Expr { + ty, + temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + span: expr.span, + kind, + }), } } @@ -1106,13 +1127,13 @@ impl<'tcx> Cx<'tcx> { // construct the complete expression `foo()` for the overloaded call, // which will yield the &T type - let temp_lifetime = + let (temp_lifetime, backwards_incompatible) = self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id); let fun = self.method_callee(expr, span, overloaded_callee); let fun = self.thir.exprs.push(fun); let fun_ty = self.thir[fun].ty; let ref_expr = self.thir.exprs.push(Expr { - temp_lifetime, + temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, ty: ref_ty, span, kind: ExprKind::Call { ty: fun_ty, fun, args, from_hir_call: false, fn_span: span }, @@ -1127,7 +1148,7 @@ impl<'tcx> Cx<'tcx> { closure_expr: &'tcx hir::Expr<'tcx>, place: HirPlace<'tcx>, ) -> Expr<'tcx> { - let temp_lifetime = self + let (temp_lifetime, backwards_incompatible) = self .rvalue_scopes .temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id); let var_ty = place.base_ty; @@ -1143,7 +1164,7 @@ impl<'tcx> Cx<'tcx> { }; let mut captured_place_expr = Expr { - temp_lifetime, + temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, ty: var_ty, span: closure_expr.span, kind: self.convert_var(var_hir_id), @@ -1168,8 +1189,12 @@ impl<'tcx> Cx<'tcx> { } }; - captured_place_expr = - Expr { temp_lifetime, ty: proj.ty, span: closure_expr.span, kind }; + captured_place_expr = Expr { + temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + ty: proj.ty, + span: closure_expr.span, + kind, + }; } captured_place_expr @@ -1184,7 +1209,7 @@ impl<'tcx> Cx<'tcx> { let upvar_capture = captured_place.info.capture_kind; let captured_place_expr = self.convert_captured_hir_place(closure_expr, captured_place.place.clone()); - let temp_lifetime = self + let (temp_lifetime, backwards_incompatible) = self .rvalue_scopes .temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id); @@ -1201,7 +1226,7 @@ impl<'tcx> Cx<'tcx> { } }; Expr { - temp_lifetime, + temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, ty: upvar_ty, span: closure_expr.span, kind: ExprKind::Borrow { diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 033501c66db..27920008c80 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -678,8 +678,25 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { let mut let_suggestion = None; let mut misc_suggestion = None; let mut interpreted_as_const = None; + let mut interpreted_as_const_sugg = None; - if let PatKind::Constant { .. } + if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } + | PatKind::AscribeUserType { + subpattern: + box Pat { kind: PatKind::ExpandedConstant { def_id, is_inline: false, .. }, .. }, + .. + } = pat.kind + && let DefKind::Const = self.tcx.def_kind(def_id) + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span) + // We filter out paths with multiple path::segments. + && snippet.chars().all(|c| c.is_alphanumeric() || c == '_') + { + let span = self.tcx.def_span(def_id); + let variable = self.tcx.item_name(def_id).to_string(); + // When we encounter a constant as the binding name, point at the `const` definition. + interpreted_as_const = Some(span); + interpreted_as_const_sugg = Some(InterpretedAsConst { span: pat.span, variable }); + } else if let PatKind::Constant { .. } | PatKind::AscribeUserType { subpattern: box Pat { kind: PatKind::Constant { .. }, .. }, .. @@ -692,9 +709,6 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { misc_suggestion = Some(MiscPatternSuggestion::AttemptedIntegerLiteral { start_span: pat.span.shrink_to_lo(), }); - } else if snippet.chars().all(|c| c.is_alphanumeric() || c == '_') { - interpreted_as_const = - Some(InterpretedAsConst { span: pat.span, variable: snippet }); } } @@ -743,6 +757,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { uncovered: Uncovered::new(pat.span, &cx, witnesses), inform, interpreted_as_const, + interpreted_as_const_sugg, witness_1_is_privately_uninhabited, _p: (), pattern_ty, @@ -1112,13 +1127,13 @@ fn report_non_exhaustive_match<'p, 'tcx>( if ty.is_ptr_sized_integral() { if ty.inner() == cx.tcx.types.usize { err.note(format!( - "`{ty}` does not have a fixed maximum value, so half-open ranges are necessary to match \ - exhaustively", + "`{ty}` does not have a fixed maximum value, so half-open ranges are \ + necessary to match exhaustively", )); } else if ty.inner() == cx.tcx.types.isize { err.note(format!( - "`{ty}` does not have fixed minimum and maximum values, so half-open ranges are necessary to match \ - exhaustively", + "`{ty}` does not have fixed minimum and maximum values, so half-open \ + ranges are necessary to match exhaustively", )); } } else if ty.inner() == cx.tcx.types.str_ { @@ -1139,6 +1154,31 @@ fn report_non_exhaustive_match<'p, 'tcx>( } } + for &arm in arms { + let arm = &thir.arms[arm]; + if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } = arm.pattern.kind + && let Ok(snippet) = cx.tcx.sess.source_map().span_to_snippet(arm.pattern.span) + // We filter out paths with multiple path::segments. + && snippet.chars().all(|c| c.is_alphanumeric() || c == '_') + { + let const_name = cx.tcx.item_name(def_id); + err.span_label( + arm.pattern.span, + format!( + "this pattern doesn't introduce a new catch-all binding, but rather pattern \ + matches against the value of constant `{const_name}`", + ), + ); + err.span_note(cx.tcx.def_span(def_id), format!("constant `{const_name}` defined here")); + err.span_suggestion_verbose( + arm.pattern.span.shrink_to_hi(), + "if you meant to introduce a binding, use a different name", + "_var".to_string(), + Applicability::MaybeIncorrect, + ); + } + } + // Whether we suggest the actual missing patterns or `_`. let suggest_the_witnesses = witnesses.len() < 4; let suggested_arm = if suggest_the_witnesses { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 6b9e3b85999..08c6b4abd3b 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -149,21 +149,30 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { None => Ok((None, None, None)), Some(expr) => { let (kind, ascr, inline_const) = match self.lower_lit(expr) { - PatKind::InlineConstant { subpattern, def } => { - (subpattern.kind, None, Some(def)) + PatKind::ExpandedConstant { subpattern, def_id, is_inline: true } => { + (subpattern.kind, None, def_id.as_local()) + } + PatKind::ExpandedConstant { subpattern, is_inline: false, .. } => { + (subpattern.kind, None, None) } PatKind::AscribeUserType { ascription, subpattern: box Pat { kind, .. } } => { (kind, Some(ascription), None) } kind => (kind, None, None), }; - let value = if let PatKind::Constant { value } = kind { - value - } else { - let msg = format!( - "found bad range pattern endpoint `{expr:?}` outside of error recovery" - ); - return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg)); + let value = match kind { + PatKind::Constant { value } => value, + PatKind::ExpandedConstant { subpattern, .. } + if let PatKind::Constant { value } = subpattern.kind => + { + value + } + _ => { + let msg = format!( + "found bad range pattern endpoint `{expr:?}` outside of error recovery" + ); + return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg)); + } }; Ok((Some(PatRangeBoundary::Finite(value)), ascr, inline_const)) } @@ -288,7 +297,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }; } for def in [lo_inline, hi_inline].into_iter().flatten() { - kind = PatKind::InlineConstant { def, subpattern: Box::new(Pat { span, ty, kind }) }; + kind = PatKind::ExpandedConstant { + def_id: def.to_def_id(), + is_inline: true, + subpattern: Box::new(Pat { span, ty, kind }), + }; } Ok(kind) } @@ -562,7 +575,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let args = self.typeck_results.node_args(id); let c = ty::Const::new_unevaluated(self.tcx, ty::UnevaluatedConst { def: def_id, args }); - let pattern = self.const_to_pat(c, ty, id, span); + let subpattern = self.const_to_pat(c, ty, id, span); + let pattern = Box::new(Pat { + span, + ty, + kind: PatKind::ExpandedConstant { subpattern, def_id, is_inline: false }, + }); if !is_associated_const { return pattern; @@ -637,7 +655,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let ct = ty::UnevaluatedConst { def: def_id.to_def_id(), args }; let subpattern = self.const_to_pat(ty::Const::new_unevaluated(self.tcx, ct), ty, id, span); - PatKind::InlineConstant { subpattern, def: def_id } + PatKind::ExpandedConstant { subpattern, def_id: def_id.to_def_id(), is_inline: true } } /// Converts literals, paths and negation of literals to patterns. diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index dae13df4054..6be0ed5fb31 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -707,9 +707,10 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, format!("value: {:?}", value), depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } - PatKind::InlineConstant { def, subpattern } => { - print_indented!(self, "InlineConstant {", depth_lvl + 1); - print_indented!(self, format!("def: {:?}", def), depth_lvl + 2); + PatKind::ExpandedConstant { def_id, is_inline, subpattern } => { + print_indented!(self, "ExpandedConstant {", depth_lvl + 1); + print_indented!(self, format!("def_id: {def_id:?}"), depth_lvl + 2); + print_indented!(self, format!("is_inline: {is_inline:?}"), depth_lvl + 2); print_indented!(self, "subpattern:", depth_lvl + 2); self.print_pat(subpattern, depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index e06c1f2bb49..fd7254a0210 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -253,6 +253,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { | StatementKind::Coverage(..) | StatementKind::Intrinsic(..) | StatementKind::ConstEvalCounter + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => None, }; if let Some(destination) = destination { diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index c5fd2a631ff..576289e228a 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -157,6 +157,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { | StatementKind::Nop | StatementKind::Retag(..) | StatementKind::Intrinsic(..) + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::StorageLive(..) => {} } } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index fd8e403ebc2..0880364bfca 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -385,6 +385,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | StatementKind::Coverage(..) | StatementKind::Intrinsic(..) | StatementKind::ConstEvalCounter + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => {} } } diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index 9bbfae17fd9..d00bfc66a6a 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -25,6 +25,31 @@ mir_transform_must_not_suspend = {$pre}`{$def_path}`{$post} held across a suspen .help = consider using a block (`{"{ ... }"}`) to shrink the value's scope, ending before the suspend point mir_transform_operation_will_panic = this operation will panic at runtime +mir_transform_tail_expr_drop_order = relative drop order changing in Rust 2024 + .temporaries = in Rust 2024, this temporary value will be dropped first + .observers = in Rust 2024, this local variable or temporary value will be dropped second + .note_dtors = + dropping the temporary value runs this custom `Drop` impl, which we could not prove to be side-effect free + .note_observer_dtors = + dropping the local runs this custom `Drop` impl, which we could not prove to be side-effect free + .drop_location = + now the temporary value is dropped here, before the local variables in the block or statement + .note_epilogue = most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages + .label_local_epilogue = {$is_dropped_first_edition_2024 -> + [true] up until Edition 2021 `{$name}` is dropped last but will be dropped earlier in Edition 2024 + *[false] `{$name}` will be dropped later as of Edition 2024 + } + +mir_transform_tail_expr_dtor = {$dtor_kind -> + [dyn] `{$name}` may invoke a custom destructor because it contains a trait object + *[concrete] `{$name}` invokes this custom destructor + } + +mir_transform_tail_expr_local = {$is_generated_name -> + [true] this value will be stored in a temporary; let us call it `{$name}` + *[false] `{$name}` calls a custom destructor + } + mir_transform_unaligned_packed_ref = reference to packed field is unaligned .note = packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses .note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index d38a1dd11dc..8295a806d71 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1772,6 +1772,7 @@ impl<'tcx> Visitor<'tcx> for EnsureCoroutineFieldAssignmentsNeverAlias<'_> { | StatementKind::Coverage(..) | StatementKind::Intrinsic(..) | StatementKind::ConstEvalCounter + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => {} } } diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 875db23ce09..824d657e1fc 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -97,6 +97,7 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> { StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::ConstEvalCounter + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => None, // FIXME(#78546): MIR InstrumentCoverage - Can the source_info.span for `FakeRead` diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 26480be29f3..d017202f48b 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -186,7 +186,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { | StatementKind::FakeRead(..) | StatementKind::PlaceMention(..) | StatementKind::Coverage(..) - | StatementKind::AscribeUserType(..) => (), + | StatementKind::BackwardIncompatibleDropHint { .. } + | StatementKind::AscribeUserType(..) => {} } } diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 2898f82e25c..0c75cdadc92 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -99,7 +99,8 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { | StatementKind::Intrinsic(_) | StatementKind::ConstEvalCounter | StatementKind::PlaceMention(_) - | StatementKind::Nop => (), + | StatementKind::BackwardIncompatibleDropHint { .. } + | StatementKind::Nop => {} StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => { bug!("{:?} not found in this MIR phase!", statement.kind) diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index beeab0d4a66..9c74b2f0839 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -581,7 +581,7 @@ impl WriteInfo { | Rvalue::RawPtr(_, _) | Rvalue::Len(_) | Rvalue::Discriminant(_) - | Rvalue::CopyForDeref(_) => (), + | Rvalue::CopyForDeref(_) => {} } } // Retags are technically also reads, but reporting them as a write suffices @@ -596,7 +596,8 @@ impl WriteInfo { | StatementKind::Coverage(_) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) - | StatementKind::PlaceMention(_) => (), + | StatementKind::BackwardIncompatibleDropHint { .. } + | StatementKind::PlaceMention(_) => {} StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => { bug!("{:?} not found in this MIR phase", statement) } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index b3d9af37204..976f4a8e919 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -100,7 +100,7 @@ use rustc_middle::bug; use rustc_middle::mir::interpret::GlobalAlloc; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; -use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; use rustc_span::def_id::DefId; @@ -294,7 +294,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } fn typing_env(&self) -> ty::TypingEnv<'tcx> { - self.ecx.typing_env + self.ecx.typing_env() } #[instrument(level = "trace", skip(self), ret)] diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 5c2c36db0f7..beed007589b 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -352,6 +352,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { | StatementKind::FakeRead(..) | StatementKind::ConstEvalCounter | StatementKind::PlaceMention(..) + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => None, } } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 5651bf469d5..bfb842e4485 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -50,6 +50,7 @@ mod deduce_param_attrs; mod errors; mod ffi_unwind_calls; mod lint; +mod lint_tail_expr_drop_order; mod shim; mod ssa; @@ -490,6 +491,7 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & } let (body, _) = tcx.mir_promoted(def); + lint_tail_expr_drop_order::run_lint(tcx, def, &body.borrow()); let mut body = body.steal(); if let Some(error_reported) = tainted_by_errors { diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs new file mode 100644 index 00000000000..b8502fcbafb --- /dev/null +++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs @@ -0,0 +1,701 @@ +use std::cell::RefCell; +use std::collections::hash_map; +use std::rc::Rc; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::unord::{UnordMap, UnordSet}; +use rustc_errors::Subdiagnostic; +use rustc_hir::CRATE_HIR_ID; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_index::bit_set::ChunkedBitSet; +use rustc_index::{IndexSlice, IndexVec}; +use rustc_macros::{LintDiagnostic, Subdiagnostic}; +use rustc_middle::bug; +use rustc_middle::mir::{ + self, BasicBlock, Body, ClearCrossCrate, Local, Location, Place, StatementKind, TerminatorKind, + dump_mir, +}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_mir_dataflow::impls::MaybeInitializedPlaces; +use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex}; +use rustc_mir_dataflow::{Analysis, MaybeReachable, ResultsCursor}; +use rustc_session::lint::builtin::TAIL_EXPR_DROP_ORDER; +use rustc_session::lint::{self}; +use rustc_span::{DUMMY_SP, Span, Symbol}; +use rustc_type_ir::data_structures::IndexMap; +use smallvec::{SmallVec, smallvec}; +use tracing::{debug, instrument}; + +fn place_has_common_prefix<'tcx>(left: &Place<'tcx>, right: &Place<'tcx>) -> bool { + left.local == right.local + && left.projection.iter().zip(right.projection).all(|(left, right)| left == right) +} + +/// Cache entry of `drop` at a `BasicBlock` +#[derive(Debug, Clone, Copy)] +enum MovePathIndexAtBlock { + /// We know nothing yet + Unknown, + /// We know that the `drop` here has no effect + None, + /// We know that the `drop` here will invoke a destructor + Some(MovePathIndex), +} + +struct DropsReachable<'a, 'mir, 'tcx> { + body: &'a Body<'tcx>, + place: &'a Place<'tcx>, + drop_span: &'a mut Option<Span>, + move_data: &'a MoveData<'tcx>, + maybe_init: &'a mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, + block_drop_value_info: &'a mut IndexSlice<BasicBlock, MovePathIndexAtBlock>, + collected_drops: &'a mut ChunkedBitSet<MovePathIndex>, + visited: FxHashMap<BasicBlock, Rc<RefCell<ChunkedBitSet<MovePathIndex>>>>, +} + +impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> { + fn visit(&mut self, block: BasicBlock) { + let move_set_size = self.move_data.move_paths.len(); + let make_new_path_set = || Rc::new(RefCell::new(ChunkedBitSet::new_empty(move_set_size))); + + let data = &self.body.basic_blocks[block]; + let Some(terminator) = &data.terminator else { return }; + // Given that we observe these dropped locals here at `block` so far, + // we will try to update the successor blocks. + // An occupied entry at `block` in `self.visited` signals that we have visited `block` before. + let dropped_local_here = + Rc::clone(self.visited.entry(block).or_insert_with(make_new_path_set)); + // We could have invoked reverse lookup for a `MovePathIndex` every time, but unfortunately it is expensive. + // Let's cache them in `self.block_drop_value_info`. + match self.block_drop_value_info[block] { + MovePathIndexAtBlock::Some(dropped) => { + dropped_local_here.borrow_mut().insert(dropped); + } + MovePathIndexAtBlock::Unknown => { + if let TerminatorKind::Drop { place, .. } = &terminator.kind + && let LookupResult::Exact(idx) | LookupResult::Parent(Some(idx)) = + self.move_data.rev_lookup.find(place.as_ref()) + { + // Since we are working with MIRs at a very early stage, + // observing a `drop` terminator is not indicative enough that + // the drop will definitely happen. + // That is decided in the drop elaboration pass instead. + // Therefore, we need to consult with the maybe-initialization information. + self.maybe_init.seek_before_primary_effect(Location { + block, + statement_index: data.statements.len(), + }); + + // Check if the drop of `place` under inspection is really in effect. + // This is true only when `place` may have been initialized along a control flow path from a BID to the drop program point today. + // In other words, this is where the drop of `place` will happen in the future instead. + if let MaybeReachable::Reachable(maybe_init) = self.maybe_init.get() + && maybe_init.contains(idx) + { + // We also cache the drop information, so that we do not need to check on data-flow cursor again + self.block_drop_value_info[block] = MovePathIndexAtBlock::Some(idx); + dropped_local_here.borrow_mut().insert(idx); + } else { + self.block_drop_value_info[block] = MovePathIndexAtBlock::None; + } + } + } + MovePathIndexAtBlock::None => {} + } + + for succ in terminator.successors() { + let target = &self.body.basic_blocks[succ]; + if target.is_cleanup { + continue; + } + + // As long as we are passing through a new block, or new dropped places to propagate, + // we will proceed with `succ` + let dropped_local_there = match self.visited.entry(succ) { + hash_map::Entry::Occupied(occupied_entry) => { + if succ == block + || !occupied_entry.get().borrow_mut().union(&*dropped_local_here.borrow()) + { + // `succ` has been visited but no new drops observed so far, + // so we can bail on `succ` until new drop information arrives + continue; + } + Rc::clone(occupied_entry.get()) + } + hash_map::Entry::Vacant(vacant_entry) => Rc::clone( + vacant_entry.insert(Rc::new(RefCell::new(dropped_local_here.borrow().clone()))), + ), + }; + if let Some(terminator) = &target.terminator + && let TerminatorKind::Drop { + place: dropped_place, + target: _, + unwind: _, + replace: _, + } = &terminator.kind + && place_has_common_prefix(dropped_place, self.place) + { + // We have now reached the current drop of the `place`. + // Let's check the observed dropped places in. + self.collected_drops.union(&*dropped_local_there.borrow()); + if self.drop_span.is_none() { + // FIXME(@dingxiangfei2009): it turns out that `self.body.source_scopes` are still a bit wonky. + // There is a high chance that this span still points to a block rather than a statement semicolon. + *self.drop_span = Some(terminator.source_info.span); + } + // Now we have discovered a simple control flow path from a future drop point + // to the current drop point. + // We will not continue from there. + } else { + self.visit(succ) + } + } + } +} + +/// An additional filter to exclude well-known types from the ecosystem +/// because their drops are trivial. +/// This returns additional types to check if the drops are delegated to those. +/// A typical example is `hashbrown::HashMap<K, V>`, whose drop is delegated to `K` and `V`. +fn true_significant_drop_ty<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Option<SmallVec<[Ty<'tcx>; 2]>> { + if let ty::Adt(def, args) = ty.kind() { + let mut did = def.did(); + let mut name_rev = vec![]; + loop { + let key = tcx.def_key(did); + + match key.disambiguated_data.data { + rustc_hir::definitions::DefPathData::CrateRoot => { + name_rev.push(tcx.crate_name(did.krate)) + } + rustc_hir::definitions::DefPathData::TypeNs(symbol) => name_rev.push(symbol), + _ => return None, + } + if let Some(parent) = key.parent { + did = DefId { krate: did.krate, index: parent }; + } else { + break; + } + } + let name_str: Vec<_> = name_rev.iter().rev().map(|x| x.as_str()).collect(); + debug!(?name_str); + match name_str[..] { + // These are the types from Rust core ecosystem + ["sym" | "proc_macro2", ..] + | ["core" | "std", "task", "LocalWaker" | "Waker"] + | ["core" | "std", "task", "wake", "LocalWaker" | "Waker"] => Some(smallvec![]), + // These are important types from Rust ecosystem + ["tracing", "instrument", "Instrumented"] | ["bytes", "Bytes"] => Some(smallvec![]), + ["hashbrown", "raw", "RawTable" | "RawIntoIter"] => { + if let [ty, ..] = &***args + && let Some(ty) = ty.as_type() + { + Some(smallvec![ty]) + } else { + None + } + } + ["hashbrown", "raw", "RawDrain"] => { + if let [_, ty, ..] = &***args + && let Some(ty) = ty.as_type() + { + Some(smallvec![ty]) + } else { + None + } + } + _ => None, + } + } else { + None + } +} + +/// Returns the list of types with a "potentially sigificant" that may be dropped +/// by dropping a value of type `ty`. +#[instrument(level = "debug", skip(tcx, param_env))] +fn extract_component_raw<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + ty_seen: &mut UnordSet<Ty<'tcx>>, +) -> SmallVec<[Ty<'tcx>; 4]> { + // Droppiness does not depend on regions, so let us erase them. + let ty = tcx + .try_normalize_erasing_regions( + ty::TypingEnv { param_env, typing_mode: ty::TypingMode::PostAnalysis }, + ty, + ) + .unwrap_or(ty); + + let tys = tcx.list_significant_drop_tys(param_env.and(ty)); + debug!(?ty, "components"); + let mut out_tys = smallvec![]; + for ty in tys { + if let Some(tys) = true_significant_drop_ty(tcx, ty) { + // Some types can be further opened up because the drop is simply delegated + for ty in tys { + if ty_seen.insert(ty) { + out_tys.extend(extract_component_raw(tcx, param_env, ty, ty_seen)); + } + } + } else { + if ty_seen.insert(ty) { + out_tys.push(ty); + } + } + } + out_tys +} + +#[instrument(level = "debug", skip(tcx, param_env))] +fn extract_component_with_significant_dtor<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, +) -> SmallVec<[Ty<'tcx>; 4]> { + let mut tys = extract_component_raw(tcx, param_env, ty, &mut Default::default()); + let mut deduplicate = FxHashSet::default(); + tys.retain(|oty| deduplicate.insert(*oty)); + tys.into_iter().collect() +} + +/// Extract the span of the custom destructor of a type +/// especially the span of the `impl Drop` header or its entire block +/// when we are working with current local crate. +#[instrument(level = "debug", skip(tcx))] +fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Span> { + match ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Error(_) + | ty::Str + | ty::Never + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnPtr(_, _) + | ty::Tuple(_) + | ty::Dynamic(_, _, _) + | ty::Alias(_, _) + | ty::Bound(_, _) + | ty::Pat(_, _) + | ty::Placeholder(_) + | ty::Infer(_) + | ty::Slice(_) + | ty::Array(_, _) => None, + ty::Adt(adt_def, _) => { + let did = adt_def.did(); + let try_local_did_span = |did: DefId| { + if let Some(local) = did.as_local() { + tcx.source_span(local) + } else { + tcx.def_span(did) + } + }; + let dtor = if let Some(dtor) = tcx.adt_destructor(did) { + dtor.did + } else if let Some(dtor) = tcx.adt_async_destructor(did) { + dtor.future + } else { + return Some(try_local_did_span(did)); + }; + let def_key = tcx.def_key(dtor); + let Some(parent_index) = def_key.parent else { return Some(try_local_did_span(dtor)) }; + let parent_did = DefId { index: parent_index, krate: dtor.krate }; + Some(try_local_did_span(parent_did)) + } + ty::Coroutine(did, _) + | ty::CoroutineWitness(did, _) + | ty::CoroutineClosure(did, _) + | ty::Closure(did, _) + | ty::FnDef(did, _) + | ty::Foreign(did) => Some(tcx.def_span(did)), + ty::Param(_) => None, + } +} + +/// Check if a moved place at `idx` is a part of a BID. +/// The use of this check is that we will consider drops on these +/// as a drop of the overall BID and, thus, we can exclude it from the diagnosis. +fn place_descendent_of_bids<'tcx>( + mut idx: MovePathIndex, + move_data: &MoveData<'tcx>, + bids: &UnordSet<&Place<'tcx>>, +) -> bool { + loop { + let path = &move_data.move_paths[idx]; + if bids.contains(&path.place) { + return true; + } + if let Some(parent) = path.parent { + idx = parent; + } else { + return false; + } + } +} + +/// The core of the lint `tail-expr-drop-order` +pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<'tcx>) { + if matches!(tcx.def_kind(def_id), rustc_hir::def::DefKind::SyntheticCoroutineBody) { + // A synthetic coroutine has no HIR body and it is enough to just analyse the original body + return; + } + if body.span.edition().at_least_rust_2024() + || tcx.lints_that_dont_need_to_run(()).contains(&lint::LintId::of(TAIL_EXPR_DROP_ORDER)) + { + return; + } + // ## About BIDs in blocks ## + // Track the set of blocks that contain a backwards-incompatible drop (BID) + // and, for each block, the vector of locations. + // + // We group them per-block because they tend to scheduled in the same drop ladder block. + let mut bid_per_block = IndexMap::default(); + let mut bid_places = UnordSet::new(); + let param_env = tcx.param_env(def_id).with_reveal_all_normalized(tcx); + let mut ty_dropped_components = UnordMap::default(); + for (block, data) in body.basic_blocks.iter_enumerated() { + for (statement_index, stmt) in data.statements.iter().enumerate() { + if let StatementKind::BackwardIncompatibleDropHint { place, reason: _ } = &stmt.kind { + let ty = place.ty(body, tcx).ty; + if ty_dropped_components + .entry(ty) + .or_insert_with(|| extract_component_with_significant_dtor(tcx, param_env, ty)) + .is_empty() + { + continue; + } + bid_per_block + .entry(block) + .or_insert(vec![]) + .push((Location { block, statement_index }, &**place)); + bid_places.insert(&**place); + } + } + } + if bid_per_block.is_empty() { + return; + } + + dump_mir(tcx, false, "lint_tail_expr_drop_order", &0 as _, body, |_, _| Ok(())); + let locals_with_user_names = collect_user_names(body); + let is_closure_like = tcx.is_closure_like(def_id.to_def_id()); + + // Compute the "maybe initialized" information for this body. + // When we encounter a DROP of some place P we only care + // about the drop if `P` may be initialized. + let move_data = MoveData::gather_moves(body, tcx, |_| true); + let maybe_init = MaybeInitializedPlaces::new(tcx, body, &move_data); + let mut maybe_init = maybe_init.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body); + let mut block_drop_value_info = + IndexVec::from_elem_n(MovePathIndexAtBlock::Unknown, body.basic_blocks.len()); + for (&block, candidates) in &bid_per_block { + // We will collect drops on locals on paths between BID points to their actual drop locations + // into `all_locals_dropped`. + let mut all_locals_dropped = ChunkedBitSet::new_empty(move_data.move_paths.len()); + let mut drop_span = None; + for &(_, place) in candidates.iter() { + let mut collected_drops = ChunkedBitSet::new_empty(move_data.move_paths.len()); + // ## On detecting change in relative drop order ## + // Iterate through each BID-containing block `block`. + // If the place `P` targeted by the BID is "maybe initialized", + // then search forward to find the actual `DROP(P)` point. + // Everything dropped between the BID and the actual drop point + // is something whose relative drop order will change. + DropsReachable { + body, + place, + drop_span: &mut drop_span, + move_data: &move_data, + maybe_init: &mut maybe_init, + block_drop_value_info: &mut block_drop_value_info, + collected_drops: &mut collected_drops, + visited: Default::default(), + } + .visit(block); + // Compute the set `all_locals_dropped` of local variables that are dropped + // after the BID point but before the current drop point. + // + // These are the variables whose drop impls will be reordered with respect + // to `place`. + all_locals_dropped.union(&collected_drops); + } + + // We shall now exclude some local bindings for the following cases. + { + let mut to_exclude = ChunkedBitSet::new_empty(all_locals_dropped.domain_size()); + // We will now do subtraction from the candidate dropped locals, because of the following reasons. + for path_idx in all_locals_dropped.iter() { + let move_path = &move_data.move_paths[path_idx]; + let dropped_local = move_path.place.local; + // a) A return value _0 will eventually be used + // Example: + // fn f() -> Droppy { + // let _x = Droppy; + // Droppy + // } + // _0 holds the literal `Droppy` and rightfully `_x` has to be dropped first + if dropped_local == Local::ZERO { + debug!(?dropped_local, "skip return value"); + to_exclude.insert(path_idx); + continue; + } + // b) If we are analysing a closure, the captures are still dropped last. + // This is part of the closure capture lifetime contract. + // They are similar to the return value _0 with respect to lifetime rules. + if is_closure_like && matches!(dropped_local, ty::CAPTURE_STRUCT_LOCAL) { + debug!(?dropped_local, "skip closure captures"); + to_exclude.insert(path_idx); + continue; + } + // c) Sometimes we collect places that are projections into the BID locals, + // so they are considered dropped now. + // Example: + // struct NotVeryDroppy(Droppy); + // impl Drop for Droppy {..} + // fn f() -> NotVeryDroppy { + // let x = NotVeryDroppy(droppy()); + // { + // let y: Droppy = x.0; + // NotVeryDroppy(y) + // } + // } + // `y` takes `x.0`, which invalidates `x` as a complete `NotVeryDroppy` + // so there is no point in linting against `x` any more. + if place_descendent_of_bids(path_idx, &move_data, &bid_places) { + debug!(?dropped_local, "skip descendent of bids"); + to_exclude.insert(path_idx); + continue; + } + let observer_ty = move_path.place.ty(body, tcx).ty; + // d) The collected local has no custom destructor that passes our ecosystem filter. + if ty_dropped_components + .entry(observer_ty) + .or_insert_with(|| { + extract_component_with_significant_dtor(tcx, param_env, observer_ty) + }) + .is_empty() + { + debug!(?dropped_local, "skip non-droppy types"); + to_exclude.insert(path_idx); + continue; + } + } + // Suppose that all BIDs point into the same local, + // we can remove the this local from the observed drops, + // so that we can focus our diagnosis more on the others. + if candidates.iter().all(|&(_, place)| candidates[0].1.local == place.local) { + for path_idx in all_locals_dropped.iter() { + if move_data.move_paths[path_idx].place.local == candidates[0].1.local { + to_exclude.insert(path_idx); + } + } + } + all_locals_dropped.subtract(&to_exclude); + } + if all_locals_dropped.is_empty() { + // No drop effect is observable, so let us move on. + continue; + } + + // ## The final work to assemble the diagnosis ## + // First collect or generate fresh names for local variable bindings and temporary values. + let local_names = assign_observables_names( + all_locals_dropped + .iter() + .map(|path_idx| move_data.move_paths[path_idx].place.local) + .chain(candidates.iter().map(|(_, place)| place.local)), + &locals_with_user_names, + ); + + let mut lint_root = None; + let mut local_labels = vec![]; + // We now collect the types with custom destructors. + for &(_, place) in candidates { + let linted_local_decl = &body.local_decls[place.local]; + let Some(&(ref name, is_generated_name)) = local_names.get(&place.local) else { + bug!("a name should have been assigned") + }; + let name = name.as_str(); + + if lint_root.is_none() + && let ClearCrossCrate::Set(data) = + &body.source_scopes[linted_local_decl.source_info.scope].local_data + { + lint_root = Some(data.lint_root); + } + + // Collect spans of the custom destructors. + let mut seen_dyn = false; + let destructors = ty_dropped_components + .get(&linted_local_decl.ty) + .unwrap() + .iter() + .filter_map(|&ty| { + if let Some(span) = ty_dtor_span(tcx, ty) { + Some(DestructorLabel { span, name, dtor_kind: "concrete" }) + } else if matches!(ty.kind(), ty::Dynamic(..)) { + if seen_dyn { + None + } else { + seen_dyn = true; + Some(DestructorLabel { span: DUMMY_SP, name, dtor_kind: "dyn" }) + } + } else { + None + } + }) + .collect(); + local_labels.push(LocalLabel { + span: linted_local_decl.source_info.span, + destructors, + name, + is_generated_name, + is_dropped_first_edition_2024: true, + }); + } + + // Similarly, custom destructors of the observed drops. + for path_idx in all_locals_dropped.iter() { + let place = &move_data.move_paths[path_idx].place; + // We are not using the type of the local because the drop may be partial. + let observer_ty = place.ty(body, tcx).ty; + + let observer_local_decl = &body.local_decls[place.local]; + let Some(&(ref name, is_generated_name)) = local_names.get(&place.local) else { + bug!("a name should have been assigned") + }; + let name = name.as_str(); + + let mut seen_dyn = false; + let destructors = extract_component_with_significant_dtor(tcx, param_env, observer_ty) + .into_iter() + .filter_map(|ty| { + if let Some(span) = ty_dtor_span(tcx, ty) { + Some(DestructorLabel { span, name, dtor_kind: "concrete" }) + } else if matches!(ty.kind(), ty::Dynamic(..)) { + if seen_dyn { + None + } else { + seen_dyn = true; + Some(DestructorLabel { span: DUMMY_SP, name, dtor_kind: "dyn" }) + } + } else { + None + } + }) + .collect(); + local_labels.push(LocalLabel { + span: observer_local_decl.source_info.span, + destructors, + name, + is_generated_name, + is_dropped_first_edition_2024: false, + }); + } + + let span = local_labels[0].span; + tcx.emit_node_span_lint( + lint::builtin::TAIL_EXPR_DROP_ORDER, + lint_root.unwrap_or(CRATE_HIR_ID), + span, + TailExprDropOrderLint { local_labels, drop_span, _epilogue: () }, + ); + } +} + +/// Extract binding names if available for diagnosis +fn collect_user_names(body: &Body<'_>) -> IndexMap<Local, Symbol> { + let mut names = IndexMap::default(); + for var_debug_info in &body.var_debug_info { + if let mir::VarDebugInfoContents::Place(place) = &var_debug_info.value + && let Some(local) = place.local_or_deref_local() + { + names.entry(local).or_insert(var_debug_info.name); + } + } + names +} + +/// Assign names for anonymous or temporary values for diagnosis +fn assign_observables_names( + locals: impl IntoIterator<Item = Local>, + user_names: &IndexMap<Local, Symbol>, +) -> IndexMap<Local, (String, bool)> { + let mut names = IndexMap::default(); + let mut assigned_names = FxHashSet::default(); + let mut idx = 0u64; + let mut fresh_name = || { + idx += 1; + (format!("#{idx}"), true) + }; + for local in locals { + let name = if let Some(name) = user_names.get(&local) { + let name = name.as_str(); + if assigned_names.contains(name) { fresh_name() } else { (name.to_owned(), false) } + } else { + fresh_name() + }; + assigned_names.insert(name.0.clone()); + names.insert(local, name); + } + names +} + +#[derive(LintDiagnostic)] +#[diag(mir_transform_tail_expr_drop_order)] +struct TailExprDropOrderLint<'a> { + #[subdiagnostic] + local_labels: Vec<LocalLabel<'a>>, + #[label(mir_transform_drop_location)] + drop_span: Option<Span>, + #[note(mir_transform_note_epilogue)] + _epilogue: (), +} + +struct LocalLabel<'a> { + span: Span, + name: &'a str, + is_generated_name: bool, + is_dropped_first_edition_2024: bool, + destructors: Vec<DestructorLabel<'a>>, +} + +/// A custom `Subdiagnostic` implementation so that the notes are delivered in a specific order +impl Subdiagnostic for LocalLabel<'_> { + fn add_to_diag_with< + G: rustc_errors::EmissionGuarantee, + F: rustc_errors::SubdiagMessageOp<G>, + >( + self, + diag: &mut rustc_errors::Diag<'_, G>, + f: &F, + ) { + diag.arg("name", self.name); + diag.arg("is_generated_name", self.is_generated_name); + diag.arg("is_dropped_first_edition_2024", self.is_dropped_first_edition_2024); + let msg = f(diag, crate::fluent_generated::mir_transform_tail_expr_local.into()); + diag.span_label(self.span, msg); + for dtor in self.destructors { + dtor.add_to_diag_with(diag, f); + } + let msg = f(diag, crate::fluent_generated::mir_transform_label_local_epilogue.into()); + diag.span_label(self.span, msg); + } +} + +#[derive(Subdiagnostic)] +#[note(mir_transform_tail_expr_dtor)] +struct DestructorLabel<'a> { + #[primary_span] + span: Span, + dtor_kind: &'static str, + name: &'a str, +} diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 55394e93a5c..fd49e956f43 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -92,6 +92,7 @@ impl RemoveNoopLandingPads { | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::ConstEvalCounter + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => { // These are all noops in a landing pad } diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 2f723bccc19..6fd70fbe9b0 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -125,6 +125,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { StatementKind::Coverage(_) | StatementKind::Intrinsic(_) | StatementKind::Nop + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::ConstEvalCounter => None, }; if let Some(place_for_ty) = place_for_ty diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 7ed43547e11..4f312ed2aaa 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -523,7 +523,8 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { } StatementKind::SetDiscriminant { ref place, variant_index: _ } - | StatementKind::Deinit(ref place) => { + | StatementKind::Deinit(ref place) + | StatementKind::BackwardIncompatibleDropHint { ref place, reason: _ } => { self.visit_lhs(place, location); } } @@ -560,6 +561,7 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod StatementKind::Assign(box (place, _)) => used_locals.is_used(place.local), StatementKind::SetDiscriminant { ref place, .. } + | StatementKind::BackwardIncompatibleDropHint { ref place, reason: _ } | StatementKind::Deinit(ref place) => used_locals.is_used(place.local), StatementKind::Nop => false, _ => true, @@ -587,6 +589,20 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> { self.tcx } + fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) { + if let StatementKind::BackwardIncompatibleDropHint { place, reason: _ } = + &mut statement.kind + { + self.visit_local( + &mut place.local, + PlaceContext::MutatingUse(MutatingUseContext::Store), + location, + ); + } else { + self.super_statement(statement, location); + } + } + fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) { *l = self.map[*l].unwrap(); } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 724238ecfc9..1739fdcc9af 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -343,6 +343,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { | StatementKind::Intrinsic(_) | StatementKind::ConstEvalCounter | StatementKind::PlaceMention(..) + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => {} } @@ -1493,6 +1494,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | StatementKind::Coverage(_) | StatementKind::ConstEvalCounter | StatementKind::PlaceMention(..) + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => {} } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index ebf7372926f..545ab15b945 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -102,7 +102,7 @@ where /// Assemble additional assumptions for an alias that are not included /// in the item bounds of the alias. For now, this is limited to the - /// `implied_const_bounds` for an associated type. + /// `explicit_implied_const_bounds` for an associated type. fn consider_additional_alias_assumptions( ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, @@ -159,13 +159,6 @@ where goal: Goal<I, Self>, ) -> Result<Candidate<I>, NoSolution>; - /// A type is `PointerLike` if we can compute its layout, and that layout - /// matches the layout of `usize`. - fn consider_builtin_pointer_like_candidate( - ecx: &mut EvalCtxt<'_, D>, - goal: Goal<I, Self>, - ) -> Result<Candidate<I>, NoSolution>; - /// A type is a `FnPtr` if it is of `FnPtr` type. fn consider_builtin_fn_ptr_trait_candidate( ecx: &mut EvalCtxt<'_, D>, @@ -449,9 +442,6 @@ where ty::ClosureKind::FnOnce, ) } - Some(TraitSolverLangItem::PointerLike) => { - G::consider_builtin_pointer_like_candidate(self, goal) - } Some(TraitSolverLangItem::FnPtrTrait) => { G::consider_builtin_fn_ptr_trait_candidate(self, goal) } diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 282ca2fedbc..603a68eb890 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -84,12 +84,9 @@ where let cx = ecx.cx(); let mut candidates = vec![]; - // FIXME(const_trait_impl): We elaborate here because the implied const bounds - // aren't necessarily elaborated. We probably should prefix this query - // with `explicit_`... for clause in elaborate::elaborate( cx, - cx.implied_const_bounds(alias_ty.def_id) + cx.explicit_implied_const_bounds(alias_ty.def_id) .iter_instantiated(cx, alias_ty.args) .map(|trait_ref| trait_ref.to_host_effect_clause(cx, goal.predicate.constness)), ) { @@ -210,13 +207,6 @@ where Err(NoSolution) } - fn consider_builtin_pointer_like_candidate( - _ecx: &mut EvalCtxt<'_, D>, - _goal: Goal<I, Self>, - ) -> Result<Candidate<I>, NoSolution> { - unreachable!("PointerLike is not const") - } - fn consider_builtin_fn_ptr_trait_candidate( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal<I, Self>, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 8a01659953d..6b407640426 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -363,13 +363,6 @@ where panic!("`Copy`/`Clone` does not have an associated type: {:?}", goal); } - fn consider_builtin_pointer_like_candidate( - _ecx: &mut EvalCtxt<'_, D>, - goal: Goal<I, Self>, - ) -> Result<Candidate<I>, NoSolution> { - panic!("`PointerLike` does not have an associated type: {:?}", goal); - } - fn consider_builtin_fn_ptr_trait_candidate( _ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index e64d4eed9d8..ce16258d180 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -248,32 +248,6 @@ where ) } - fn consider_builtin_pointer_like_candidate( - ecx: &mut EvalCtxt<'_, D>, - goal: Goal<I, Self>, - ) -> Result<Candidate<I>, NoSolution> { - if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); - } - - let cx = ecx.cx(); - // But if there are inference variables, we have to wait until it's resolved. - if (goal.param_env, goal.predicate.self_ty()).has_non_region_infer() { - return ecx.forced_ambiguity(MaybeCause::Ambiguity); - } - - if cx.layout_is_pointer_like( - ecx.typing_mode(goal.param_env), - goal.param_env, - goal.predicate.self_ty(), - ) { - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) - .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) - } else { - Err(NoSolution) - } - } - fn consider_builtin_fn_ptr_trait_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index ef259703f0c..cafd4b6dca2 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -216,6 +216,9 @@ parse_expected_identifier_found_doc_comment = expected identifier, found doc com parse_expected_identifier_found_doc_comment_str = expected identifier, found doc comment `{$token}` parse_expected_identifier_found_keyword = expected identifier, found keyword parse_expected_identifier_found_keyword_str = expected identifier, found keyword `{$token}` +parse_expected_identifier_found_metavar = expected identifier, found metavariable +# This one deliberately doesn't print a token. +parse_expected_identifier_found_metavar_str = expected identifier, found metavariable parse_expected_identifier_found_reserved_identifier = expected identifier, found reserved identifier parse_expected_identifier_found_reserved_identifier_str = expected identifier, found reserved identifier `{$token}` parse_expected_identifier_found_reserved_keyword = expected identifier, found reserved keyword @@ -227,6 +230,8 @@ parse_expected_mut_or_const_in_raw_pointer_type = expected `mut` or `const` keyw parse_expected_semi_found_doc_comment_str = expected `;`, found doc comment `{$token}` parse_expected_semi_found_keyword_str = expected `;`, found keyword `{$token}` +# This one deliberately doesn't print a token. +parse_expected_semi_found_metavar_str = expected `;`, found metavariable parse_expected_semi_found_reserved_identifier_str = expected `;`, found reserved identifier `{$token}` parse_expected_semi_found_reserved_keyword_str = expected `;`, found reserved keyword `{$token}` parse_expected_semi_found_str = expected `;`, found `{$token}` @@ -864,6 +869,8 @@ parse_unexpected_token_after_not_logical = use `!` to perform logical negation parse_unexpected_token_after_struct_name = expected `where`, `{"{"}`, `(`, or `;` after struct name parse_unexpected_token_after_struct_name_found_doc_comment = expected `where`, `{"{"}`, `(`, or `;` after struct name, found doc comment `{$token}` parse_unexpected_token_after_struct_name_found_keyword = expected `where`, `{"{"}`, `(`, or `;` after struct name, found keyword `{$token}` +# This one deliberately doesn't print a token. +parse_unexpected_token_after_struct_name_found_metavar = expected `where`, `{"{"}`, `(`, or `;` after struct name, found metavar parse_unexpected_token_after_struct_name_found_other = expected `where`, `{"{"}`, `(`, or `;` after struct name, found `{$token}` parse_unexpected_token_after_struct_name_found_reserved_identifier = expected `where`, `{"{"}`, `(`, or `;` after struct name, found reserved identifier `{$token}` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 37eb463cba6..9bdb99dc000 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1086,6 +1086,8 @@ pub(crate) enum ExpectedIdentifierFound { ReservedKeyword(#[primary_span] Span), #[label(parse_expected_identifier_found_doc_comment)] DocComment(#[primary_span] Span), + #[label(parse_expected_identifier_found_metavar)] + MetaVar(#[primary_span] Span), #[label(parse_expected_identifier)] Other(#[primary_span] Span), } @@ -1099,6 +1101,7 @@ impl ExpectedIdentifierFound { Some(TokenDescription::Keyword) => ExpectedIdentifierFound::Keyword, Some(TokenDescription::ReservedKeyword) => ExpectedIdentifierFound::ReservedKeyword, Some(TokenDescription::DocComment) => ExpectedIdentifierFound::DocComment, + Some(TokenDescription::MetaVar(_)) => ExpectedIdentifierFound::MetaVar, None => ExpectedIdentifierFound::Other, })(span) } @@ -1117,6 +1120,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedIdentifier { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let token_descr = TokenDescription::from_token(&self.token); + let mut add_token = true; let mut diag = Diag::new(dcx, level, match token_descr { Some(TokenDescription::ReservedIdentifier) => { fluent::parse_expected_identifier_found_reserved_identifier_str @@ -1128,10 +1132,16 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedIdentifier { Some(TokenDescription::DocComment) => { fluent::parse_expected_identifier_found_doc_comment_str } + Some(TokenDescription::MetaVar(_)) => { + add_token = false; + fluent::parse_expected_identifier_found_metavar_str + } None => fluent::parse_expected_identifier_found_str, }); diag.span(self.span); - diag.arg("token", self.token); + if add_token { + diag.arg("token", self.token); + } if let Some(sugg) = self.suggest_raw { sugg.add_to_diag(&mut diag); @@ -1171,6 +1181,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedSemi { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let token_descr = TokenDescription::from_token(&self.token); + let mut add_token = true; let mut diag = Diag::new(dcx, level, match token_descr { Some(TokenDescription::ReservedIdentifier) => { fluent::parse_expected_semi_found_reserved_identifier_str @@ -1180,10 +1191,16 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedSemi { fluent::parse_expected_semi_found_reserved_keyword_str } Some(TokenDescription::DocComment) => fluent::parse_expected_semi_found_doc_comment_str, + Some(TokenDescription::MetaVar(_)) => { + add_token = false; + fluent::parse_expected_semi_found_metavar_str + } None => fluent::parse_expected_semi_found_str, }); diag.span(self.span); - diag.arg("token", self.token); + if add_token { + diag.arg("token", self.token); + } if let Some(unexpected_token_label) = self.unexpected_token_label { diag.span_label(unexpected_token_label, fluent::parse_label_unexpected_token); @@ -1925,6 +1942,12 @@ pub(crate) enum UnexpectedTokenAfterStructName { span: Span, token: Token, }, + #[diag(parse_unexpected_token_after_struct_name_found_metavar)] + MetaVar { + #[primary_span] + #[label(parse_unexpected_token_after_struct_name)] + span: Span, + }, #[diag(parse_unexpected_token_after_struct_name_found_other)] Other { #[primary_span] @@ -1941,6 +1964,7 @@ impl UnexpectedTokenAfterStructName { Some(TokenDescription::Keyword) => Self::Keyword { span, token }, Some(TokenDescription::ReservedKeyword) => Self::ReservedKeyword { span, token }, Some(TokenDescription::DocComment) => Self::DocComment { span, token }, + Some(TokenDescription::MetaVar(_)) => Self::MetaVar { span }, None => Self::Other { span, token }, } } diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index d35c9c7bae7..7b21ffacc84 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -43,11 +43,19 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { let mut buf = Vec::new(); loop { match self.token.kind { - token::OpenDelim(delim) => buf.push(match self.lex_token_tree_open_delim(delim) { - Ok(val) => val, - Err(errs) => return (open_spacing, TokenStream::new(buf), Err(errs)), - }), + token::OpenDelim(delim) => { + // Invisible delimiters cannot occur here because `TokenTreesReader` parses + // code directly from strings, with no macro expansion involved. + debug_assert!(!matches!(delim, Delimiter::Invisible(_))); + buf.push(match self.lex_token_tree_open_delim(delim) { + Ok(val) => val, + Err(errs) => return (open_spacing, TokenStream::new(buf), Err(errs)), + }) + } token::CloseDelim(delim) => { + // Invisible delimiters cannot occur here because `TokenTreesReader` parses + // code directly from strings, with no macro expansion involved. + debug_assert!(!matches!(delim, Delimiter::Invisible(_))); return ( open_spacing, TokenStream::new(buf), diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index c85d0bd05cb..434f71beac2 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -510,8 +510,8 @@ fn make_attr_token_stream( FlatToken::Token((Token { kind: TokenKind::CloseDelim(delim), span }, spacing)) => { let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap()); let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap(); - assert_eq!( - open_delim, delim, + assert!( + open_delim.eq_ignoring_invisible_origin(&delim), "Mismatched open/close delims: open={open_delim:?} close={span:?}" ); let dspan = DelimSpan::from_pair(open_sp, span); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 0012db471ef..aa5e9586daf 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -49,7 +49,7 @@ pub(super) enum DestructuredFloat { /// 1.2 | 1.2e3 MiddleDot(Symbol, Span, Span, Symbol, Span), /// Invalid - Error(ErrorGuaranteed), + Error, } impl<'a> Parser<'a> { @@ -1005,7 +1005,7 @@ impl<'a> Parser<'a> { self.mk_expr_tuple_field_access(lo, ident1_span, base, sym1, None); self.mk_expr_tuple_field_access(lo, ident2_span, base1, sym2, suffix) } - DestructuredFloat::Error(_) => base, + DestructuredFloat::Error => base, }) } _ => { @@ -1015,7 +1015,7 @@ impl<'a> Parser<'a> { } } - fn error_unexpected_after_dot(&self) -> ErrorGuaranteed { + fn error_unexpected_after_dot(&self) { let actual = pprust::token_to_string(&self.token); let span = self.token.span; let sm = self.psess.source_map(); @@ -1025,7 +1025,7 @@ impl<'a> Parser<'a> { } _ => (span, actual), }; - self.dcx().emit_err(errors::UnexpectedTokenAfterDot { span, actual }) + self.dcx().emit_err(errors::UnexpectedTokenAfterDot { span, actual }); } /// We need an identifier or integer, but the next token is a float. @@ -1111,8 +1111,8 @@ impl<'a> Parser<'a> { // 1.2e+3 | 1.2e-3 [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => { // See the FIXME about `TokenCursor` above. - let guar = self.error_unexpected_after_dot(); - DestructuredFloat::Error(guar) + self.error_unexpected_after_dot(); + DestructuredFloat::Error } _ => panic!("unexpected components in a float token: {components:?}"), } @@ -1178,7 +1178,7 @@ impl<'a> Parser<'a> { fields.insert(start_idx, Ident::new(symbol2, span2)); fields.insert(start_idx, Ident::new(symbol1, span1)); } - DestructuredFloat::Error(_) => { + DestructuredFloat::Error => { trailing_dot = None; fields.insert(start_idx, Ident::new(symbol, self.prev_token.span)); } @@ -3591,11 +3591,19 @@ impl<'a> Parser<'a> { && !self.token.is_reserved_ident() && self.look_ahead(1, |t| { AssocOp::from_token(t).is_some() - || matches!(t.kind, token::OpenDelim(_)) + || matches!( + t.kind, + token::OpenDelim( + Delimiter::Parenthesis + | Delimiter::Bracket + | Delimiter::Brace + ) + ) || *t == token::Dot }) { - // Looks like they tried to write a shorthand, complex expression. + // Looks like they tried to write a shorthand, complex expression, + // E.g.: `n + m`, `f(a)`, `a[i]`, `S { x: 3 }`, or `x.y`. e.span_suggestion_verbose( self.token.span.shrink_to_lo(), "try naming a field", diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 042ee96bbe8..0ed8d152d2d 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -21,7 +21,9 @@ pub(crate) use item::FnParseMode; pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; use path::PathStyle; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind}; +use rustc_ast::token::{ + self, Delimiter, IdentIsRaw, InvisibleOrigin, MetaVarKind, Nonterminal, Token, TokenKind, +}; use rustc_ast::tokenstream::{ AttrsTarget, DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree, TokenTreeCursor, }; @@ -317,7 +319,7 @@ impl TokenCursor { spacing, delim, )); - if delim != Delimiter::Invisible { + if !delim.skip() { return (Token::new(token::OpenDelim(delim), sp.open), spacing.open); } // No open delimiter to return; continue on to the next iteration. @@ -326,7 +328,7 @@ impl TokenCursor { } else if let Some((tree_cursor, span, spacing, delim)) = self.stack.pop() { // We have exhausted this token stream. Move back to its parent token stream. self.tree_cursor = tree_cursor; - if delim != Delimiter::Invisible { + if !delim.skip() { return (Token::new(token::CloseDelim(delim), span.close), spacing.close); } // No close delimiter to return; continue on to the next iteration. @@ -410,6 +412,12 @@ pub(super) enum TokenDescription { Keyword, ReservedKeyword, DocComment, + + // Expanded metavariables are wrapped in invisible delimiters which aren't + // pretty-printed. In error messages we must handle these specially + // otherwise we get confusing things in messages like "expected `(`, found + // ``". It's better to say e.g. "expected `(`, found type metavariable". + MetaVar(MetaVarKind), } impl TokenDescription { @@ -419,26 +427,29 @@ impl TokenDescription { _ if token.is_used_keyword() => Some(TokenDescription::Keyword), _ if token.is_unused_keyword() => Some(TokenDescription::ReservedKeyword), token::DocComment(..) => Some(TokenDescription::DocComment), + token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(kind))) => { + Some(TokenDescription::MetaVar(kind)) + } _ => None, } } } pub fn token_descr(token: &Token) -> String { - let name = pprust::token_to_string(token).to_string(); - - let kind = match (TokenDescription::from_token(token), &token.kind) { - (Some(TokenDescription::ReservedIdentifier), _) => Some("reserved identifier"), - (Some(TokenDescription::Keyword), _) => Some("keyword"), - (Some(TokenDescription::ReservedKeyword), _) => Some("reserved keyword"), - (Some(TokenDescription::DocComment), _) => Some("doc comment"), - (None, TokenKind::NtIdent(..)) => Some("identifier"), - (None, TokenKind::NtLifetime(..)) => Some("lifetime"), - (None, TokenKind::Interpolated(node)) => Some(node.descr()), - (None, _) => None, - }; - - if let Some(kind) = kind { format!("{kind} `{name}`") } else { format!("`{name}`") } + let s = pprust::token_to_string(token).to_string(); + + match (TokenDescription::from_token(token), &token.kind) { + (Some(TokenDescription::ReservedIdentifier), _) => format!("reserved identifier `{s}`"), + (Some(TokenDescription::Keyword), _) => format!("keyword `{s}`"), + (Some(TokenDescription::ReservedKeyword), _) => format!("reserved keyword `{s}`"), + (Some(TokenDescription::DocComment), _) => format!("doc comment `{s}`"), + // Deliberately doesn't print `s`, which is empty. + (Some(TokenDescription::MetaVar(kind)), _) => format!("`{kind}` metavariable"), + (None, TokenKind::NtIdent(..)) => format!("identifier `{s}`"), + (None, TokenKind::NtLifetime(..)) => format!("lifetime `{s}`"), + (None, TokenKind::Interpolated(node)) => format!("{} `{s}`", node.descr()), + (None, _) => format!("`{s}`"), + } } impl<'a> Parser<'a> { @@ -1163,7 +1174,7 @@ impl<'a> Parser<'a> { } debug_assert!(!matches!( next.0.kind, - token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) + token::OpenDelim(delim) | token::CloseDelim(delim) if delim.skip() )); self.inlined_bump_with(next) } @@ -1187,7 +1198,7 @@ impl<'a> Parser<'a> { match tree { TokenTree::Token(token, _) => return looker(token), &TokenTree::Delimited(dspan, _, delim, _) => { - if delim != Delimiter::Invisible { + if !delim.skip() { return looker(&Token::new(token::OpenDelim(delim), dspan.open)); } } @@ -1197,7 +1208,7 @@ impl<'a> Parser<'a> { // The tree cursor lookahead went (one) past the end of the // current token tree. Try to return a close delimiter. if let Some(&(_, span, _, delim)) = self.token_cursor.stack.last() - && delim != Delimiter::Invisible + && !delim.skip() { // We are not in the outermost token stream, so we have // delimiters. Also, those delimiters are not skipped. @@ -1216,7 +1227,7 @@ impl<'a> Parser<'a> { token = cursor.next().0; if matches!( token.kind, - token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) + token::OpenDelim(delim) | token::CloseDelim(delim) if delim.skip() ) { continue; } diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 43c3de90d9d..8fb6f85d0dd 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -3,7 +3,9 @@ use rustc_ast::ptr::P; use rustc_ast::token::Nonterminal::*; use rustc_ast::token::NtExprKind::*; use rustc_ast::token::NtPatKind::*; -use rustc_ast::token::{self, Delimiter, NonterminalKind, Token}; +use rustc_ast::token::{ + self, Delimiter, InvisibleOrigin, MetaVarKind, Nonterminal, NonterminalKind, Token, +}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::PResult; @@ -22,7 +24,28 @@ impl<'a> Parser<'a> { #[inline] pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool { /// Checks whether the non-terminal may contain a single (non-keyword) identifier. - fn may_be_ident(nt: &token::Nonterminal) -> bool { + fn may_be_ident(kind: MetaVarKind) -> bool { + match kind { + MetaVarKind::Stmt + | MetaVarKind::Pat(_) + | MetaVarKind::Expr { .. } + | MetaVarKind::Ty + | MetaVarKind::Literal // `true`, `false` + | MetaVarKind::Meta + | MetaVarKind::Path => true, + + MetaVarKind::Item + | MetaVarKind::Block + | MetaVarKind::Vis => false, + + MetaVarKind::Ident + | MetaVarKind::Lifetime + | MetaVarKind::TT => unreachable!(), + } + } + + /// Old variant of `may_be_ident`. Being phased out. + fn nt_may_be_ident(nt: &Nonterminal) -> bool { match nt { NtStmt(_) | NtPat(_) @@ -69,7 +92,8 @@ impl<'a> Parser<'a> { | token::Ident(..) | token::NtIdent(..) | token::NtLifetime(..) - | token::Interpolated(_) => true, + | token::Interpolated(_) + | token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))) => true, _ => token.can_begin_type(), }, NonterminalKind::Block => match &token.kind { @@ -79,11 +103,29 @@ impl<'a> Parser<'a> { NtBlock(_) | NtStmt(_) | NtExpr(_) | NtLiteral(_) => true, NtItem(_) | NtPat(_) | NtTy(_) | NtMeta(_) | NtPath(_) | NtVis(_) => false, }, + token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(k))) => match k { + MetaVarKind::Block + | MetaVarKind::Stmt + | MetaVarKind::Expr { .. } + | MetaVarKind::Literal => true, + MetaVarKind::Item + | MetaVarKind::Pat(_) + | MetaVarKind::Ty + | MetaVarKind::Meta + | MetaVarKind::Path + | MetaVarKind::Vis => false, + MetaVarKind::Lifetime | MetaVarKind::Ident | MetaVarKind::TT => { + unreachable!() + } + }, _ => false, }, NonterminalKind::Path | NonterminalKind::Meta => match &token.kind { token::PathSep | token::Ident(..) | token::NtIdent(..) => true, - token::Interpolated(nt) => may_be_ident(nt), + token::Interpolated(nt) => nt_may_be_ident(nt), + token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(kind))) => { + may_be_ident(*kind) + } _ => false, }, NonterminalKind::Pat(pat_kind) => token.can_begin_pattern(pat_kind), diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 936e5235c55..cc0763ac751 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -465,7 +465,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { let fields: Vec<_>; match &pat.kind { PatKind::AscribeUserType { subpattern, .. } - | PatKind::InlineConstant { subpattern, .. } => return self.lower_pat(subpattern), + | PatKind::ExpandedConstant { subpattern, .. } => return self.lower_pat(subpattern), PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat), PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { ctor = Wildcard; diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index ad825d7813d..466e190028a 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1478,9 +1478,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if segment_idx == 0 { if name == kw::SelfLower { let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0(); - module = Some(ModuleOrUniformRoot::Module( - self.resolve_self(&mut ctxt, parent_scope.module), - )); + let self_mod = self.resolve_self(&mut ctxt, parent_scope.module); + if let Some(res) = self_mod.res() { + record_segment_res(self, res); + } + module = Some(ModuleOrUniformRoot::Module(self_mod)); continue; } if name == kw::PathRoot && ident.span.at_least_rust_2018() { @@ -1497,7 +1499,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } if name == kw::PathRoot || name == kw::Crate || name == kw::DollarCrate { // `::a::b`, `crate::a::b` or `$crate::a::b` - module = Some(ModuleOrUniformRoot::Module(self.resolve_crate_root(ident))); + let crate_root = self.resolve_crate_root(ident); + if let Some(res) = crate_root.res() { + record_segment_res(self, res); + } + module = Some(ModuleOrUniformRoot::Module(crate_root)); continue; } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f6e6fd33c48..d60c56fee75 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1069,7 +1069,7 @@ impl OutputFilenames { self.with_directory_and_extension(&self.out_directory, extension) } - fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf { + pub fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf { let mut path = directory.join(&self.filestem); path.set_extension(extension); path diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index d94b503de18..cbfe5d22f1d 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1887,7 +1887,7 @@ options! { meta_stats: bool = (false, parse_bool, [UNTRACKED], "gather metadata statistics (default: no)"), metrics_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED], - "stores metrics about the errors being emitted by rustc to disk"), + "the directory metrics emitted by rustc are dumped into (implicitly enables default set of metrics)"), mir_emit_retag: bool = (false, parse_bool, [TRACKED], "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ (default: no)"), diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 820d8a6be25..fcdf8703b14 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -151,6 +151,10 @@ impl<'tcx> Stable<'tcx> for mir::StatementKind<'tcx> { mir::StatementKind::ConstEvalCounter => { stable_mir::mir::StatementKind::ConstEvalCounter } + // BackwardIncompatibleDropHint has no semantics, so it is translated to Nop. + mir::StatementKind::BackwardIncompatibleDropHint { .. } => { + stable_mir::mir::StatementKind::Nop + } mir::StatementKind::Nop => stable_mir::mir::StatementKind::Nop, } } diff --git a/compiler/rustc_target/src/callconv/s390x.rs b/compiler/rustc_target/src/callconv/s390x.rs index 502e7331267..c99eb9226ef 100644 --- a/compiler/rustc_target/src/callconv/s390x.rs +++ b/compiler/rustc_target/src/callconv/s390x.rs @@ -1,12 +1,16 @@ -// FIXME: The assumes we're using the non-vector ABI, i.e., compiling -// for a pre-z13 machine or using -mno-vx. +// Reference: ELF Application Binary Interface s390x Supplement +// https://github.com/IBM/s390x-abi -use crate::abi::call::{ArgAbi, FnAbi, Reg}; -use crate::abi::{HasDataLayout, TyAbiInterface}; +use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind}; +use crate::abi::{BackendRepr, HasDataLayout, TyAbiInterface}; use crate::spec::HasTargetSpec; fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) { - if !ret.layout.is_aggregate() && ret.layout.size.bits() <= 64 { + let size = ret.layout.size; + if size.bits() <= 128 && matches!(ret.layout.backend_repr, BackendRepr::Vector { .. }) { + return; + } + if !ret.layout.is_aggregate() && size.bits() <= 64 { ret.extend_integer_width_to(64); } else { ret.make_indirect(); @@ -32,19 +36,25 @@ where } return; } - if !arg.layout.is_aggregate() && arg.layout.size.bits() <= 64 { + + let size = arg.layout.size; + if size.bits() <= 128 && arg.layout.is_single_vector_element(cx, size) { + arg.cast_to(Reg { kind: RegKind::Vector, size }); + return; + } + if !arg.layout.is_aggregate() && size.bits() <= 64 { arg.extend_integer_width_to(64); return; } if arg.layout.is_single_fp_element(cx) { - match arg.layout.size.bytes() { + match size.bytes() { 4 => arg.cast_to(Reg::f32()), 8 => arg.cast_to(Reg::f64()), _ => arg.make_indirect(), } } else { - match arg.layout.size.bytes() { + match size.bytes() { 1 => arg.cast_to(Reg::i8()), 2 => arg.cast_to(Reg::i16()), 4 => arg.cast_to(Reg::i32()), diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs index 3efbb464836..a84a18a433f 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs @@ -6,9 +6,6 @@ pub(crate) fn target() -> Target { base.endian = Endian::Big; // z10 is the oldest CPU supported by LLVM base.cpu = "z10".into(); - // FIXME: The ABI implementation in abi/call/s390x.rs is for now hard-coded to assume the no-vector - // ABI. Pass the -vector feature string to LLVM to respect this assumption. - base.features = "-vector".into(); base.max_atomic_width = Some(128); base.min_global_align = Some(16); base.stack_probes = StackProbeType::Inline; diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs index 65b5c1167bd..4bde0fb729c 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs @@ -6,9 +6,6 @@ pub(crate) fn target() -> Target { base.endian = Endian::Big; // z10 is the oldest CPU supported by LLVM base.cpu = "z10".into(); - // FIXME: The ABI implementation in abi/call/s390x.rs is for now hard-coded to assume the no-vector - // ABI. Pass the -vector feature string to LLVM to respect this assumption. - base.features = "-vector".into(); base.max_atomic_width = Some(128); base.min_global_align = Some(16); base.static_position_independent_executables = true; diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs index a213adadbea..a70cebbd9c8 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs @@ -5,7 +5,8 @@ use crate::spec::{ pub(crate) fn target() -> Target { // Reset flags for non-Em flavors back to empty to satisfy sanity checking tests. let pre_link_args = LinkArgs::new(); - let post_link_args = TargetOptions::link_args(LinkerFlavor::EmCc, &["-sABORTING_MALLOC=0"]); + let post_link_args = + TargetOptions::link_args(LinkerFlavor::EmCc, &["-sABORTING_MALLOC=0", "-sWASM_BIGINT"]); let opts = TargetOptions { os: "emscripten".into(), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_msvc.rs index 3a3716db350..f42188ec61a 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_msvc.rs @@ -8,7 +8,7 @@ pub(crate) fn target() -> Target { base.vendor = "win7".into(); Target { - llvm_target: "x86_64-win7-windows-msvc".into(), + llvm_target: "x86_64-pc-windows-msvc".into(), metadata: crate::spec::TargetMetadata { description: Some("64-bit Windows 7 support".into()), tier: Some(3), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index a5e364d49f7..5ad15feadff 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1075,93 +1075,110 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> { // Autoderef is useful here because sometimes we box callables, etc. let Some((def_id_or_name, output, inputs)) = - (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| { - match *found.kind() { - ty::FnPtr(sig_tys, _) => Some(( - DefIdOrName::Name("function pointer"), - sig_tys.output(), - sig_tys.inputs(), - )), - ty::FnDef(def_id, _) => { - let fn_sig = found.fn_sig(self.tcx); - Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs())) - } - ty::Closure(def_id, args) => { - let fn_sig = args.as_closure().sig(); - Some(( - DefIdOrName::DefId(def_id), - fn_sig.output(), - fn_sig.inputs().map_bound(|inputs| &inputs[1..]), - )) - } - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { - self.tcx - .item_super_predicates(def_id) - .instantiate(self.tcx, args) - .iter() - .find_map(|pred| { - if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() - && self.tcx.is_lang_item(proj.projection_term.def_id,LangItem::FnOnceOutput) - // args tuple will always be args[1] - && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind() - { - Some(( - DefIdOrName::DefId(def_id), - pred.kind().rebind(proj.term.expect_type()), - pred.kind().rebind(args.as_slice()), - )) - } else { - None - } - }) - } - ty::Dynamic(data, _, ty::Dyn) => { - data.iter().find_map(|pred| { - if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() + (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| match *found.kind() { + ty::FnPtr(sig_tys, _) => Some(( + DefIdOrName::Name("function pointer"), + sig_tys.output(), + sig_tys.inputs(), + )), + ty::FnDef(def_id, _) => { + let fn_sig = found.fn_sig(self.tcx); + Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs())) + } + ty::Closure(def_id, args) => { + let fn_sig = args.as_closure().sig(); + Some(( + DefIdOrName::DefId(def_id), + fn_sig.output(), + fn_sig.inputs().map_bound(|inputs| inputs[0].tuple_fields().as_slice()), + )) + } + ty::CoroutineClosure(def_id, args) => { + let sig_parts = args.as_coroutine_closure().coroutine_closure_sig(); + Some(( + DefIdOrName::DefId(def_id), + sig_parts.map_bound(|sig| { + sig.to_coroutine( + self.tcx, + args.as_coroutine_closure().parent_args(), + // Just use infer vars here, since we don't really care + // what these types are, just that we're returning a coroutine. + self.next_ty_var(DUMMY_SP), + self.tcx.coroutine_for_closure(def_id), + self.next_ty_var(DUMMY_SP), + ) + }), + sig_parts.map_bound(|sig| sig.tupled_inputs_ty.tuple_fields().as_slice()), + )) + } + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self + .tcx + .item_super_predicates(def_id) + .instantiate(self.tcx, args) + .iter() + .find_map(|pred| { + if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() + && self + .tcx + .is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput) + // args tuple will always be args[1] + && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind() + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.expect_type()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }), + ty::Dynamic(data, _, ty::Dyn) => data.iter().find_map(|pred| { + if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput) // for existential projection, args are shifted over by 1 && let ty::Tuple(args) = proj.args.type_at(0).kind() - { - Some(( - DefIdOrName::Name("trait object"), - pred.rebind(proj.term.expect_type()), - pred.rebind(args.as_slice()), - )) - } else { - None - } - }) + { + Some(( + DefIdOrName::Name("trait object"), + pred.rebind(proj.term.expect_type()), + pred.rebind(args.as_slice()), + )) + } else { + None } - ty::Param(param) => { - let generics = self.tcx.generics_of(body_id); - let name = if generics.count() > param.index as usize - && let def = generics.param_at(param.index as usize, self.tcx) - && matches!(def.kind, ty::GenericParamDefKind::Type { .. }) - && def.name == param.name + }), + ty::Param(param) => { + let generics = self.tcx.generics_of(body_id); + let name = if generics.count() > param.index as usize + && let def = generics.param_at(param.index as usize, self.tcx) + && matches!(def.kind, ty::GenericParamDefKind::Type { .. }) + && def.name == param.name + { + DefIdOrName::DefId(def.def_id) + } else { + DefIdOrName::Name("type parameter") + }; + param_env.caller_bounds().iter().find_map(|pred| { + if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() + && self + .tcx + .is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput) + && proj.projection_term.self_ty() == found + // args tuple will always be args[1] + && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind() { - DefIdOrName::DefId(def.def_id) + Some(( + name, + pred.kind().rebind(proj.term.expect_type()), + pred.kind().rebind(args.as_slice()), + )) } else { - DefIdOrName::Name("type parameter") - }; - param_env.caller_bounds().iter().find_map(|pred| { - if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() - && self.tcx.is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput) - && proj.projection_term.self_ty() == found - // args tuple will always be args[1] - && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind() - { - Some(( - name, - pred.kind().rebind(proj.term.expect_type()), - pred.kind().rebind(args.as_slice()), - )) - } else { - None - } - }) - } - _ => None, + None + } + }) } + _ => None, }) else { return None; diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 1e0c487c4d4..345e1cc31f3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -111,8 +111,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_candidates_for_transmutability(obligation, &mut candidates); } else if tcx.is_lang_item(def_id, LangItem::Tuple) { self.assemble_candidate_for_tuple(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::PointerLike) { - self.assemble_candidate_for_pointer_like(obligation, &mut candidates); } else if tcx.is_lang_item(def_id, LangItem::FnPtrTrait) { self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates); } else { @@ -1216,35 +1214,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - fn assemble_candidate_for_pointer_like( - &mut self, - obligation: &PolyTraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) { - // The regions of a type don't affect the size of the type - let tcx = self.tcx(); - let self_ty = tcx.instantiate_bound_regions_with_erased(obligation.predicate.self_ty()); - - // But if there are inference variables, we have to wait until it's resolved. - if (obligation.param_env, self_ty).has_non_region_infer() { - candidates.ambiguous = true; - return; - } - - // We should erase regions from both the param-env and type, since both - // may have infer regions. Specifically, after canonicalizing and instantiating, - // early bound regions turn into region vars in both the new and old solver. - let key = self.infcx.pseudo_canonicalize_query( - tcx.erase_regions(obligation.param_env), - tcx.erase_regions(self_ty), - ); - if let Ok(layout) = tcx.layout_of(key) - && layout.layout.is_pointer_like(&tcx.data_layout) - { - candidates.vec.push(BuiltinCandidate { has_nested: false }); - } - } - fn assemble_candidates_for_fn_ptr_trait( &mut self, obligation: &PolyTraitObligation<'tcx>, diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 03ab3a55486..469a4ac3e41 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::util::{AlwaysRequiresDrop, needs_drop_components}; use rustc_middle::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt}; use rustc_session::Limit; use rustc_span::sym; -use tracing::debug; +use tracing::{debug, instrument}; use crate::errors::NeedsDropOverflow; @@ -23,7 +23,7 @@ fn needs_drop_raw<'tcx>( // needs drop. let adt_has_dtor = |adt_def: ty::AdtDef<'tcx>| adt_def.destructor(tcx).map(|_| DtorType::Significant); - let res = drop_tys_helper(tcx, query.value, query.typing_env, adt_has_dtor, false) + let res = drop_tys_helper(tcx, query.value, query.typing_env, adt_has_dtor, false, false) .filter(filter_array_elements(tcx, query.typing_env)) .next() .is_some(); @@ -41,7 +41,7 @@ fn needs_async_drop_raw<'tcx>( // it needs async drop. let adt_has_async_dtor = |adt_def: ty::AdtDef<'tcx>| adt_def.async_destructor(tcx).map(|_| DtorType::Significant); - let res = drop_tys_helper(tcx, query.value, query.typing_env, adt_has_async_dtor, false) + let res = drop_tys_helper(tcx, query.value, query.typing_env, adt_has_async_dtor, false, false) .filter(filter_array_elements(tcx, query.typing_env)) .next() .is_some(); @@ -77,6 +77,7 @@ fn has_significant_drop_raw<'tcx>( query.typing_env, adt_consider_insignificant_dtor(tcx), true, + false, ) .filter(filter_array_elements(tcx, query.typing_env)) .next() @@ -88,8 +89,8 @@ fn has_significant_drop_raw<'tcx>( struct NeedsDropTypes<'tcx, F> { tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - // Whether to reveal coroutine witnesses, this is set - // to `false` unless we compute `needs_drop` for a coroutine witness. + /// Whether to reveal coroutine witnesses, this is set + /// to `false` unless we compute `needs_drop` for a coroutine witness. reveal_coroutine_witnesses: bool, query_ty: Ty<'tcx>, seen_tys: FxHashSet<Ty<'tcx>>, @@ -100,6 +101,9 @@ struct NeedsDropTypes<'tcx, F> { unchecked_tys: Vec<(Ty<'tcx>, usize)>, recursion_limit: Limit, adt_components: F, + /// Set this to true if an exhaustive list of types involved in + /// drop obligation is requested. + exhaustive: bool, } impl<'tcx, F> NeedsDropTypes<'tcx, F> { @@ -107,6 +111,7 @@ impl<'tcx, F> NeedsDropTypes<'tcx, F> { tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, + exhaustive: bool, adt_components: F, ) -> Self { let mut seen_tys = FxHashSet::default(); @@ -114,14 +119,22 @@ impl<'tcx, F> NeedsDropTypes<'tcx, F> { Self { tcx, typing_env, - reveal_coroutine_witnesses: false, + reveal_coroutine_witnesses: exhaustive, seen_tys, query_ty: ty, unchecked_tys: vec![(ty, 0)], recursion_limit: tcx.recursion_limit(), adt_components, + exhaustive, } } + + /// Called when `ty` is found to always require drop. + /// If the exhaustive flag is true, then `Ok(ty)` is returned like any other type. + /// Otherwise, `Err(AlwaysRequireDrop)` is returned, which will cause iteration to abort. + fn always_drop_component(&self, ty: Ty<'tcx>) -> NeedsDropResult<Ty<'tcx>> { + if self.exhaustive { Ok(ty) } else { Err(AlwaysRequiresDrop) } + } } impl<'tcx, F, I> Iterator for NeedsDropTypes<'tcx, F> @@ -131,19 +144,22 @@ where { type Item = NeedsDropResult<Ty<'tcx>>; + #[instrument(level = "debug", skip(self), ret)] fn next(&mut self) -> Option<NeedsDropResult<Ty<'tcx>>> { let tcx = self.tcx; while let Some((ty, level)) = self.unchecked_tys.pop() { + debug!(?ty, "needs_drop_components: inspect"); if !self.recursion_limit.value_within_limit(level) { // Not having a `Span` isn't great. But there's hopefully some other // recursion limit error as well. + debug!("needs_drop_components: recursion limit exceeded"); tcx.dcx().emit_err(NeedsDropOverflow { query_ty: self.query_ty }); - return Some(Err(AlwaysRequiresDrop)); + return Some(self.always_drop_component(ty)); } let components = match needs_drop_components(tcx, ty) { - Err(e) => return Some(Err(e)), + Err(AlwaysRequiresDrop) => return Some(self.always_drop_component(ty)), Ok(components) => components, }; debug!("needs_drop_components({:?}) = {:?}", ty, components); @@ -171,7 +187,7 @@ where if self.reveal_coroutine_witnesses { queue_type(self, args.as_coroutine().witness()); } else { - return Some(Err(AlwaysRequiresDrop)); + return Some(self.always_drop_component(ty)); } } ty::CoroutineWitness(def_id, args) => { @@ -186,7 +202,7 @@ where } } - _ if component.is_copy_modulo_regions(tcx, self.typing_env) => (), + _ if component.is_copy_modulo_regions(tcx, self.typing_env) => {} ty::Closure(_, args) => { for upvar in args.as_closure().upvar_tys() { @@ -205,7 +221,9 @@ where // impl then check whether the field types need `Drop`. ty::Adt(adt_def, args) => { let tys = match (self.adt_components)(adt_def, args) { - Err(e) => return Some(Err(e)), + Err(AlwaysRequiresDrop) => { + return Some(self.always_drop_component(ty)); + } Ok(tys) => tys, }; for required_ty in tys { @@ -230,7 +248,8 @@ where } ty::Foreign(_) | ty::Dynamic(..) => { - return Some(Err(AlwaysRequiresDrop)); + debug!("needs_drop_components: foreign or dynamic"); + return Some(self.always_drop_component(ty)); } ty::Bool @@ -280,6 +299,7 @@ fn drop_tys_helper<'tcx>( typing_env: ty::TypingEnv<'tcx>, adt_has_dtor: impl Fn(ty::AdtDef<'tcx>) -> Option<DtorType>, only_significant: bool, + exhaustive: bool, ) -> impl Iterator<Item = NeedsDropResult<Ty<'tcx>>> { fn with_query_cache<'tcx>( tcx: TyCtxt<'tcx>, @@ -343,7 +363,7 @@ fn drop_tys_helper<'tcx>( .map(|v| v.into_iter()) }; - NeedsDropTypes::new(tcx, typing_env, ty, adt_components) + NeedsDropTypes::new(tcx, typing_env, ty, exhaustive, adt_components) } fn adt_consider_insignificant_dtor<'tcx>( @@ -384,6 +404,7 @@ fn adt_drop_tys<'tcx>( ty::TypingEnv::non_body_analysis(tcx, def_id), adt_has_dtor, false, + false, ) .collect::<Result<Vec<_>, _>>() .map(|components| tcx.mk_type_list(&components)) @@ -401,11 +422,31 @@ fn adt_significant_drop_tys( ty::TypingEnv::non_body_analysis(tcx, def_id), adt_consider_insignificant_dtor(tcx), true, + false, ) .collect::<Result<Vec<_>, _>>() .map(|components| tcx.mk_type_list(&components)) } +#[instrument(level = "debug", skip(tcx), ret)] +fn list_significant_drop_tys<'tcx>( + tcx: TyCtxt<'tcx>, + ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, +) -> &'tcx ty::List<Ty<'tcx>> { + tcx.mk_type_list( + &drop_tys_helper( + tcx, + ty.value, + ty::TypingEnv { typing_mode: ty::TypingMode::PostAnalysis, param_env: ty.param_env }, + adt_consider_insignificant_dtor(tcx), + true, + true, + ) + .filter_map(|res| res.ok()) + .collect::<Vec<_>>(), + ) +} + pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { needs_drop_raw, @@ -413,6 +454,7 @@ pub(crate) fn provide(providers: &mut Providers) { has_significant_drop_raw, adt_drop_tys, adt_significant_drop_tys, + list_significant_drop_tys, ..*providers }; } diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 3fbce7886ed..2c1ad9de9aa 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -157,7 +157,7 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> { } // `T: ~const Trait` implies `T: ~const Supertrait`. ty::ClauseKind::HostEffect(data) => self.extend_deduped( - cx.implied_const_bounds(data.def_id()).iter_identity().map(|trait_ref| { + cx.explicit_implied_const_bounds(data.def_id()).iter_identity().map(|trait_ref| { elaboratable.child( trait_ref .to_host_effect_clause(cx, data.constness) diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 6e6cf91d855..93b9c2e2892 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -13,7 +13,7 @@ use crate::lang_items::TraitSolverLangItem; use crate::relate::Relate; use crate::solve::{CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult}; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; -use crate::{self as ty, TypingMode, search_graph}; +use crate::{self as ty, search_graph}; pub trait Interner: Sized @@ -229,7 +229,7 @@ pub trait Interner: self, def_id: Self::DefId, ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = ty::Binder<Self, ty::TraitRef<Self>>>>; - fn implied_const_bounds( + fn explicit_implied_const_bounds( self, def_id: Self::DefId, ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = ty::Binder<Self, ty::TraitRef<Self>>>>; @@ -278,13 +278,6 @@ pub trait Interner: fn coroutine_is_gen(self, coroutine_def_id: Self::DefId) -> bool; fn coroutine_is_async_gen(self, coroutine_def_id: Self::DefId) -> bool; - fn layout_is_pointer_like( - self, - typing_mode: TypingMode<Self>, - param_env: Self::ParamEnv, - ty: Self::Ty, - ) -> bool; - type UnsizingParams: Deref<Target = BitSet<u32>>; fn unsizing_params_for_adt(self, adt_def_id: Self::DefId) -> Self::UnsizingParams; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index d6ca22a90a4..df43346065d 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -31,7 +31,6 @@ pub enum TraitSolverLangItem { Metadata, Option, PointeeTrait, - PointerLike, Poll, Sized, TransmuteTrait, diff --git a/library/Cargo.lock b/library/Cargo.lock index 70ecac4e17c..97996d5f0b2 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -158,9 +158,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.161" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" dependencies = [ "rustc-std-workspace-core", ] diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index ac3e4626ee5..ee60ec0fbac 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -191,6 +191,8 @@ use core::error::{self, Error}; use core::fmt; use core::future::Future; use core::hash::{Hash, Hasher}; +#[cfg(not(bootstrap))] +use core::marker::PointerLike; use core::marker::{Tuple, Unsize}; use core::mem::{self, SizedTypeProperties}; use core::ops::{ @@ -2131,3 +2133,7 @@ impl<E: Error> Error for Box<E> { Error::provide(&**self, request); } } + +#[cfg(not(bootstrap))] +#[unstable(feature = "pointer_like_trait", issue = "none")] +impl<T> PointerLike for Box<T> {} diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 7839fe04b8d..041ff37897f 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -136,6 +136,7 @@ #![feature(panic_internals)] #![feature(pattern)] #![feature(pin_coerce_unsized_trait)] +#![feature(pointer_like_trait)] #![feature(ptr_internals)] #![feature(ptr_metadata)] #![feature(ptr_sub_ptr)] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 3b8ac20e527..d30bf96cfd4 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -121,7 +121,6 @@ #![feature(const_float_methods)] #![feature(const_heap)] #![feature(const_nonnull_new)] -#![feature(const_pin_2)] #![feature(const_ptr_sub_ptr)] #![feature(const_raw_ptr_comparison)] #![feature(const_size_of_val)] diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 1c5c58d64a2..c8ea52a1fb0 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -981,6 +981,18 @@ pub trait Tuple {} )] pub trait PointerLike {} +#[cfg(not(bootstrap))] +marker_impls! { + #[unstable(feature = "pointer_like_trait", issue = "none")] + PointerLike for + usize, + {T} &T, + {T} &mut T, + {T} *const T, + {T} *mut T, + {T: PointerLike} crate::pin::Pin<T>, +} + /// A marker for types which can be used as types of `const` generic parameters. /// /// These types must have a proper equivalence relation (`Eq`) and it must be automatically diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 254b306fcaa..c14c49a0d92 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1214,7 +1214,8 @@ impl<Ptr: Deref<Target: Unpin>> Pin<Ptr> { /// assert_eq!(*r, 5); /// ``` #[inline(always)] - #[rustc_const_unstable(feature = "const_pin_2", issue = "76654")] + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin_into_inner", since = "1.39.0")] pub const fn into_inner(pin: Pin<Ptr>) -> Ptr { pin.__pointer @@ -1503,7 +1504,8 @@ impl<Ptr: Deref> Pin<Ptr> { /// If the underlying data is [`Unpin`], [`Pin::into_inner`] should be used /// instead. #[inline(always)] - #[rustc_const_unstable(feature = "const_pin_2", issue = "76654")] + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin_into_inner", since = "1.39.0")] pub const unsafe fn into_inner_unchecked(pin: Pin<Ptr>) -> Ptr { pin.__pointer diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index f515e9e4109..f7825571cd7 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -21,7 +21,6 @@ #![feature(const_eval_select)] #![feature(const_heap)] #![feature(const_nonnull_new)] -#![feature(const_pin_2)] #![feature(const_trait_impl)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index b732fdf1696..c1ab70b714a 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -34,7 +34,7 @@ miniz_oxide = { version = "0.7.0", optional = true, default-features = false } addr2line = { version = "0.22.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.161", default-features = false, features = [ +libc = { version = "0.2.162", default-features = false, features = [ 'rustc-dep-of-std', ], public = true } diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 9f913eae095..fbfdb4fa023 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -153,7 +153,8 @@ impl<T> Cursor<T> { /// let reference = buff.get_mut(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut T { + #[rustc_const_unstable(feature = "const_mut_cursor", issue = "130801")] + pub const fn get_mut(&mut self) -> &mut T { &mut self.inner } @@ -200,7 +201,8 @@ impl<T> Cursor<T> { /// assert_eq!(buff.position(), 4); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn set_position(&mut self, pos: u64) { + #[rustc_const_unstable(feature = "const_mut_cursor", issue = "130801")] + pub const fn set_position(&mut self, pos: u64) { self.pos = pos; } } diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs index 0cc9cecb89d..1b83f4b0aee 100644 --- a/library/std/src/sys/pal/uefi/process.rs +++ b/library/std/src/sys/pal/uefi/process.rs @@ -18,6 +18,7 @@ use crate::{fmt, io}; #[derive(Debug)] pub struct Command { prog: OsString, + args: Vec<OsString>, stdout: Option<Stdio>, stderr: Option<Stdio>, } @@ -39,12 +40,11 @@ pub enum Stdio { impl Command { pub fn new(program: &OsStr) -> Command { - Command { prog: program.to_os_string(), stdout: None, stderr: None } + Command { prog: program.to_os_string(), args: Vec::new(), stdout: None, stderr: None } } - // FIXME: Implement arguments as reverse of parsing algorithm - pub fn arg(&mut self, _arg: &OsStr) { - panic!("unsupported") + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(arg.to_os_string()); } pub fn env_mut(&mut self) -> &mut CommandEnv { @@ -72,7 +72,7 @@ impl Command { } pub fn get_args(&self) -> CommandArgs<'_> { - panic!("unsupported") + CommandArgs { iter: self.args.iter() } } pub fn get_envs(&self) -> CommandEnvs<'_> { @@ -116,6 +116,12 @@ impl Command { pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> { let mut cmd = uefi_command_internal::Image::load_image(&self.prog)?; + // UEFI adds the bin name by default + if !self.args.is_empty() { + let args = uefi_command_internal::create_args(&self.prog, &self.args); + cmd.set_args(args); + } + // Setup Stdout let stdout = self.stdout.unwrap_or(Stdio::MakePipe); let stdout = Self::create_pipe(stdout)?; @@ -315,7 +321,7 @@ mod uefi_command_internal { stdout: Option<helpers::OwnedProtocol<PipeProtocol>>, stderr: Option<helpers::OwnedProtocol<PipeProtocol>>, st: OwnedTable<r_efi::efi::SystemTable>, - args: Option<Vec<u16>>, + args: Option<(*mut u16, usize)>, } impl Image { @@ -449,20 +455,20 @@ mod uefi_command_internal { } } - pub fn set_args(&mut self, args: &OsStr) { + pub fn set_args(&mut self, args: Box<[u16]>) { let loaded_image: NonNull<loaded_image::Protocol> = helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); - let mut args = args.encode_wide().collect::<Vec<u16>>(); - let args_size = (crate::mem::size_of::<u16>() * args.len()) as u32; + let len = args.len(); + let args_size: u32 = crate::mem::size_of_val(&args).try_into().unwrap(); + let ptr = Box::into_raw(args).as_mut_ptr(); unsafe { - (*loaded_image.as_ptr()).load_options = - args.as_mut_ptr() as *mut crate::ffi::c_void; + (*loaded_image.as_ptr()).load_options = ptr as *mut crate::ffi::c_void; (*loaded_image.as_ptr()).load_options_size = args_size; } - self.args = Some(args); + self.args = Some((ptr, len)); } fn update_st_crc32(&mut self) -> io::Result<()> { @@ -502,6 +508,10 @@ mod uefi_command_internal { ((*bt.as_ptr()).unload_image)(self.handle.as_ptr()); } } + + if let Some((ptr, len)) = self.args { + let _ = unsafe { Box::from_raw(crate::ptr::slice_from_raw_parts_mut(ptr, len)) }; + } } } @@ -681,4 +691,38 @@ mod uefi_command_internal { } } } + + pub fn create_args(prog: &OsStr, args: &[OsString]) -> Box<[u16]> { + const QUOTE: u16 = 0x0022; + const SPACE: u16 = 0x0020; + const CARET: u16 = 0x005e; + const NULL: u16 = 0; + + // This is the lower bound on the final length under the assumption that + // the arguments only contain ASCII characters. + let mut res = Vec::with_capacity(args.iter().map(|arg| arg.len() + 3).sum()); + + // Wrap program name in quotes to avoid any problems + res.push(QUOTE); + res.extend(prog.encode_wide()); + res.push(QUOTE); + res.push(SPACE); + + for arg in args { + // Wrap the argument in quotes to be treat as single arg + res.push(QUOTE); + for c in arg.encode_wide() { + // CARET in quotes is used to escape CARET or QUOTE + if c == QUOTE || c == CARET { + res.push(CARET); + } + res.push(c); + } + res.push(QUOTE); + + res.push(SPACE); + } + + res.into_boxed_slice() + } } diff --git a/src/doc/unstable-book/src/language-features/asm-goto.md b/src/doc/unstable-book/src/language-features/asm-goto.md index d72eb7c0c6e..823118bcae1 100644 --- a/src/doc/unstable-book/src/language-features/asm-goto.md +++ b/src/doc/unstable-book/src/language-features/asm-goto.md @@ -21,7 +21,9 @@ unsafe { } ``` -The block must have unit type or diverge. +The block must have unit type or diverge. The block starts a new safety context, +so despite outer `unsafe`, you need extra unsafe to perform unsafe operations +within `label <block>`. When `label <block>` is used together with `noreturn` option, it means that the assembly will not fallthrough. It's allowed to jump to a label within the diff --git a/src/llvm-project b/src/llvm-project -Subproject b35599be758613448201a49f4b8c7ebfba5558a +Subproject 104d0d16c3c7c3fef2435fef6efb2d57b70fff7 diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 69e595908e2c420e7f0d1be34e6c5b984c8cfb8 +Subproject 66221abdeca2002d318fde6efff516aab091df0 diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index abadca71400..345c46f944a 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -233,6 +233,7 @@ fn check_statement<'tcx>( | StatementKind::PlaceMention(..) | StatementKind::Coverage(..) | StatementKind::ConstEvalCounter + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => Ok(()), } } diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 9627447b342..a855603eeb3 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -12,6 +12,7 @@ use std::{cmp, mem}; use rustc_abi::{BackendRepr, Size}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::{Mutability, RetagKind}; +use rustc_middle::ty::layout::HasTypingEnv; use rustc_middle::ty::{self, Ty}; use self::diagnostics::{RetagCause, RetagInfo}; @@ -70,7 +71,7 @@ impl NewPermission { access: None, protector: None, } - } else if pointee.is_unpin(*cx.tcx, cx.typing_env) { + } else if pointee.is_unpin(*cx.tcx, cx.typing_env()) { // A regular full mutable reference. On `FnEntry` this is `noalias` and `dereferenceable`. NewPermission::Uniform { perm: Permission::Unique, @@ -128,7 +129,7 @@ impl NewPermission { fn from_box_ty<'tcx>(ty: Ty<'tcx>, kind: RetagKind, cx: &crate::MiriInterpCx<'tcx>) -> Self { // `ty` is not the `Box` but the field of the Box with this pointer (due to allocator handling). let pointee = ty.builtin_deref(true).unwrap(); - if pointee.is_unpin(*cx.tcx, cx.typing_env) { + if pointee.is_unpin(*cx.tcx, cx.typing_env()) { // A regular box. On `FnEntry` this is `noalias`, but not `dereferenceable` (hence only // a weak protector). NewPermission::Uniform { @@ -607,7 +608,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { match new_perm { NewPermission::Uniform { perm, .. } => write!(kind_str, "{perm:?} permission").unwrap(), - NewPermission::FreezeSensitive { freeze_perm, .. } if ty.is_freeze(*this.tcx, this.typing_env) => + NewPermission::FreezeSensitive { freeze_perm, .. } if ty.is_freeze(*this.tcx, this.typing_env()) => write!(kind_str, "{freeze_perm:?} permission").unwrap(), NewPermission::FreezeSensitive { freeze_perm, nonfreeze_perm, .. } => write!(kind_str, "{freeze_perm:?}/{nonfreeze_perm:?} permission for frozen/non-frozen parts").unwrap(), diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 61a2e2bc8d9..8469744bbc4 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -1,5 +1,6 @@ use rustc_abi::{BackendRepr, Size}; use rustc_middle::mir::{Mutability, RetagKind}; +use rustc_middle::ty::layout::HasTypingEnv; use rustc_middle::ty::{self, Ty}; use rustc_span::def_id::DefId; @@ -131,8 +132,8 @@ impl<'tcx> NewPermission { kind: RetagKind, cx: &crate::MiriInterpCx<'tcx>, ) -> Option<Self> { - let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env); - let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env); + let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env()); + let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env()); let is_protected = kind == RetagKind::FnEntry; // As demonstrated by `tests/fail/tree_borrows/reservedim_spurious_write.rs`, // interior mutability and protectors interact poorly. @@ -163,10 +164,10 @@ impl<'tcx> NewPermission { zero_size: bool, ) -> Option<Self> { let pointee = ty.builtin_deref(true).unwrap(); - pointee.is_unpin(*cx.tcx, cx.typing_env).then_some(()).map(|()| { + pointee.is_unpin(*cx.tcx, cx.typing_env()).then_some(()).map(|()| { // Regular `Unpin` box, give it `noalias` but only a weak protector // because it is valid to deallocate it within the function. - let ty_is_freeze = ty.is_freeze(*cx.tcx, cx.typing_env); + let ty_is_freeze = ty.is_freeze(*cx.tcx, cx.typing_env()); let protected = kind == RetagKind::FnEntry; let initial_state = Permission::new_reserved(ty_is_freeze, protected); Self { @@ -520,7 +521,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Note: if we were to inline `new_reserved` below we would find out that // `ty_is_freeze` is eventually unused because it appears in a `ty_is_freeze || true`. // We are nevertheless including it here for clarity. - let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env); + let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env()); // Retag it. With protection! That is the entire point. let new_perm = NewPermission { initial_state: Permission::new_reserved(ty_is_freeze, /* protected */ true), diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 3ab606e5847..7b03aed4763 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -17,7 +17,9 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::static_assert_size; use rustc_middle::mir; use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout}; +use rustc_middle::ty::layout::{ + HasTyCtxt, HasTypingEnv, LayoutCx, LayoutError, LayoutOf, TyAndLayout, +}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::config::InliningThreshold; use rustc_span::def_id::{CrateNum, DefId}; @@ -1127,9 +1129,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { }; let info = ecx.get_alloc_info(alloc_id); let def_ty = ecx.tcx.type_of(def_id).instantiate_identity(); - let extern_decl_layout = ecx.tcx.layout_of( - ecx.typing_env.as_query_input(def_ty) - ).unwrap(); + let extern_decl_layout = + ecx.tcx.layout_of(ecx.typing_env().as_query_input(def_ty)).unwrap(); if extern_decl_layout.size != info.size || extern_decl_layout.align.abi != info.align { throw_unsup_format!( "extern static `{link_name}` has been declared as `{krate}::{name}` \ diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 5a35e115d8f..4083d9398f6 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -620,7 +620,7 @@ fn delim_token_to_str( ("{ ", " }") } } - Delimiter::Invisible => unreachable!(), + Delimiter::Invisible(_) => unreachable!(), }; if use_multiple_lines { let indent_str = shape.indent.to_string_with_newline(context.config); diff --git a/tests/assembly/s390x-vector-abi.rs b/tests/assembly/s390x-vector-abi.rs new file mode 100644 index 00000000000..c1935582561 --- /dev/null +++ b/tests/assembly/s390x-vector-abi.rs @@ -0,0 +1,322 @@ +//@ revisions: z10 z10_vector z13 z13_no_vector +// ignore-tidy-linelength +//@ assembly-output: emit-asm +//@ compile-flags: -O -Z merge-functions=disabled +//@[z10] compile-flags: --target s390x-unknown-linux-gnu --cfg no_vector +//@[z10] needs-llvm-components: systemz +//@[z10_vector] compile-flags: --target s390x-unknown-linux-gnu -C target-feature=+vector +//@[z10_vector] needs-llvm-components: systemz +//@[z13] compile-flags: --target s390x-unknown-linux-gnu -C target-cpu=z13 +//@[z13] needs-llvm-components: systemz +//@[z13_no_vector] compile-flags: --target s390x-unknown-linux-gnu -C target-cpu=z13 -C target-feature=-vector --cfg no_vector +//@[z13_no_vector] needs-llvm-components: systemz + +#![feature(no_core, lang_items, repr_simd, s390x_target_feature)] +#![no_core] +#![crate_type = "lib"] +#![allow(non_camel_case_types)] + +// Cases where vector feature is disabled are rejected. +// See tests/ui/simd-abi-checks-s390x.rs for test for them. + +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} +#[lang = "freeze"] +pub trait Freeze {} + +impl<T: Copy, const N: usize> Copy for [T; N] {} + +#[lang = "phantom_data"] +pub struct PhantomData<T: ?Sized>; +impl<T: ?Sized> Copy for PhantomData<T> {} + +#[repr(simd)] +pub struct i8x8([i8; 8]); +#[repr(simd)] +pub struct i8x16([i8; 16]); +#[repr(simd)] +pub struct i8x32([i8; 32]); +#[repr(C)] +pub struct Wrapper<T>(T); +#[repr(C, align(16))] +pub struct WrapperAlign16<T>(T); +#[repr(C)] +pub struct WrapperWithZst<T>(T, PhantomData<()>); +#[repr(transparent)] +pub struct TransparentWrapper<T>(T); + +impl Copy for i8 {} +impl Copy for i64 {} +impl Copy for i8x8 {} +impl Copy for i8x16 {} +impl Copy for i8x32 {} +impl<T: Copy> Copy for Wrapper<T> {} +impl<T: Copy> Copy for WrapperAlign16<T> {} +impl<T: Copy> Copy for WrapperWithZst<T> {} +impl<T: Copy> Copy for TransparentWrapper<T> {} + +// CHECK-LABEL: vector_ret_small: +// CHECK: vlrepg %v24, 0(%r2) +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { + *x +} +// CHECK-LABEL: vector_ret: +// CHECK: vl %v24, 0(%r2), 3 +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_ret(x: &i8x16) -> i8x16 { + *x +} +// CHECK-LABEL: vector_ret_large: +// z10: vl %v0, 16(%r3), 4 +// z10-NEXT: vl %v1, 0(%r3), 4 +// z10-NEXT: vst %v0, 16(%r2), 4 +// z10-NEXT: vst %v1, 0(%r2), 4 +// z10-NEXT: br %r14 +// z13: vl %v0, 0(%r3), 4 +// z13-NEXT: vl %v1, 16(%r3), 4 +// z13-NEXT: vst %v1, 16(%r2), 4 +// z13-NEXT: vst %v0, 0(%r2), 4 +// z13-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_ret_large(x: &i8x32) -> i8x32 { + *x +} + +// CHECK-LABEL: vector_wrapper_ret_small: +// CHECK: mvc 0(8,%r2), 0(%r3) +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_wrapper_ret_small(x: &Wrapper<i8x8>) -> Wrapper<i8x8> { + *x +} +// CHECK-LABEL: vector_wrapper_ret: +// CHECK: mvc 0(16,%r2), 0(%r3) +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_wrapper_ret(x: &Wrapper<i8x16>) -> Wrapper<i8x16> { + *x +} +// CHECK-LABEL: vector_wrapper_ret_large: +// z10: vl %v0, 16(%r3), 4 +// z10-NEXT: vl %v1, 0(%r3), 4 +// z10-NEXT: vst %v0, 16(%r2), 4 +// z10-NEXT: vst %v1, 0(%r2), 4 +// z10-NEXT: br %r14 +// z13: vl %v0, 16(%r3), 4 +// z13-NEXT: vst %v0, 16(%r2), 4 +// z13-NEXT: vl %v0, 0(%r3), 4 +// z13-NEXT: vst %v0, 0(%r2), 4 +// z13-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_wrapper_ret_large(x: &Wrapper<i8x32>) -> Wrapper<i8x32> { + *x +} + +// CHECK-LABEL: vector_wrapper_padding_ret: +// CHECK: mvc 0(16,%r2), 0(%r3) +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_wrapper_padding_ret(x: &WrapperAlign16<i8x8>) -> WrapperAlign16<i8x8> { + *x +} + +// CHECK-LABEL: vector_wrapper_with_zst_ret_small: +// CHECK: mvc 0(8,%r2), 0(%r3) +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_wrapper_with_zst_ret_small( + x: &WrapperWithZst<i8x8>, +) -> WrapperWithZst<i8x8> { + *x +} +// CHECK-LABEL: vector_wrapper_with_zst_ret: +// CHECK: mvc 0(16,%r2), 0(%r3) +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_wrapper_with_zst_ret( + x: &WrapperWithZst<i8x16>, +) -> WrapperWithZst<i8x16> { + *x +} +// CHECK-LABEL: vector_wrapper_with_zst_ret_large: +// z10: vl %v0, 16(%r3), 4 +// z10-NEXT: vl %v1, 0(%r3), 4 +// z10-NEXT: vst %v0, 16(%r2), 4 +// z10-NEXT: vst %v1, 0(%r2), 4 +// z10-NEXT: br %r14 +// z13: vl %v0, 16(%r3), 4 +// z13-NEXT: vst %v0, 16(%r2), 4 +// z13-NEXT: vl %v0, 0(%r3), 4 +// z13-NEXT: vst %v0, 0(%r2), 4 +// z13-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_wrapper_with_zst_ret_large( + x: &WrapperWithZst<i8x32>, +) -> WrapperWithZst<i8x32> { + *x +} + +// CHECK-LABEL: vector_transparent_wrapper_ret_small: +// CHECK: vlrepg %v24, 0(%r2) +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_transparent_wrapper_ret_small( + x: &TransparentWrapper<i8x8>, +) -> TransparentWrapper<i8x8> { + *x +} +// CHECK-LABEL: vector_transparent_wrapper_ret: +// CHECK: vl %v24, 0(%r2), 3 +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_transparent_wrapper_ret( + x: &TransparentWrapper<i8x16>, +) -> TransparentWrapper<i8x16> { + *x +} +// CHECK-LABEL: vector_transparent_wrapper_ret_large: +// z10: vl %v0, 16(%r3), 4 +// z10-NEXT: vl %v1, 0(%r3), 4 +// z10-NEXT: vst %v0, 16(%r2), 4 +// z10-NEXT: vst %v1, 0(%r2), 4 +// z10-NEXT: br %r14 +// z13: vl %v0, 0(%r3), 4 +// z13-NEXT: vl %v1, 16(%r3), 4 +// z13-NEXT: vst %v1, 16(%r2), 4 +// z13-NEXT: vst %v0, 0(%r2), 4 +// z13-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_transparent_wrapper_ret_large( + x: &TransparentWrapper<i8x32>, +) -> TransparentWrapper<i8x32> { + *x +} + +// CHECK-LABEL: vector_arg_small: +// CHECK: vlgvg %r2, %v24, 0 +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_arg_small(x: i8x8) -> i64 { + unsafe { *(&x as *const i8x8 as *const i64) } +} +// CHECK-LABEL: vector_arg: +// CHECK: vlgvg %r2, %v24, 0 +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_arg(x: i8x16) -> i64 { + unsafe { *(&x as *const i8x16 as *const i64) } +} +// CHECK-LABEL: vector_arg_large: +// CHECK: lg %r2, 0(%r2) +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_arg_large(x: i8x32) -> i64 { + unsafe { *(&x as *const i8x32 as *const i64) } +} + +// CHECK-LABEL: vector_wrapper_arg_small: +// CHECK: vlgvg %r2, %v24, 0 +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 { + unsafe { *(&x as *const Wrapper<i8x8> as *const i64) } +} +// CHECK-LABEL: vector_wrapper_arg: +// CHECK: vlgvg %r2, %v24, 0 +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 { + unsafe { *(&x as *const Wrapper<i8x16> as *const i64) } +} +// CHECK-LABEL: vector_wrapper_arg_large: +// CHECK: lg %r2, 0(%r2) +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_wrapper_arg_large(x: Wrapper<i8x32>) -> i64 { + unsafe { *(&x as *const Wrapper<i8x32> as *const i64) } +} + +// https://github.com/rust-lang/rust/pull/131586#discussion_r1837071121 +// CHECK-LABEL: vector_wrapper_padding_arg: +// CHECK: lg %r2, 0(%r2) +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_wrapper_padding_arg(x: WrapperAlign16<i8x8>) -> i64 { + unsafe { *(&x as *const WrapperAlign16<i8x8> as *const i64) } +} + +// CHECK-LABEL: vector_wrapper_with_zst_arg_small: +// CHECK: .cfi_startproc +// CHECK-NOT: vlgvg +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_wrapper_with_zst_arg_small(x: WrapperWithZst<i8x8>) -> i64 { + unsafe { *(&x as *const WrapperWithZst<i8x8> as *const i64) } +} +// CHECK-LABEL: vector_wrapper_with_zst_arg: +// CHECK: lg %r2, 0(%r2) +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_wrapper_with_zst_arg(x: WrapperWithZst<i8x16>) -> i64 { + unsafe { *(&x as *const WrapperWithZst<i8x16> as *const i64) } +} +// CHECK-LABEL: vector_wrapper_with_zst_arg_large: +// CHECK: lg %r2, 0(%r2) +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_wrapper_with_zst_arg_large(x: WrapperWithZst<i8x32>) -> i64 { + unsafe { *(&x as *const WrapperWithZst<i8x32> as *const i64) } +} + +// CHECK-LABEL: vector_transparent_wrapper_arg_small: +// CHECK: vlgvg %r2, %v24, 0 +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8x8>) -> i64 { + unsafe { *(&x as *const TransparentWrapper<i8x8> as *const i64) } +} +// CHECK-LABEL: vector_transparent_wrapper_arg: +// CHECK: vlgvg %r2, %v24, 0 +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper<i8x16>) -> i64 { + unsafe { *(&x as *const TransparentWrapper<i8x16> as *const i64) } +} +// CHECK-LABEL: vector_transparent_wrapper_arg_large: +// CHECK: lg %r2, 0(%r2) +// CHECK-NEXT: br %r14 +#[cfg_attr(no_vector, target_feature(enable = "vector"))] +#[no_mangle] +unsafe extern "C" fn vector_transparent_wrapper_arg_large(x: TransparentWrapper<i8x32>) -> i64 { + unsafe { *(&x as *const TransparentWrapper<i8x32> as *const i64) } +} diff --git a/tests/crashes/113280.rs b/tests/crashes/113280.rs deleted file mode 100644 index 86677f416fe..00000000000 --- a/tests/crashes/113280.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ known-bug: #113280 -//@ only-x86_64 - -#![feature(dyn_star, pointer_like_trait)] -#![allow(incomplete_features)] - -use std::fmt::Debug; -use std::marker::PointerLike; - -fn make_dyn_star<'a>(t: impl PointerLike + Debug + 'a) -> dyn* Debug + 'a { - f32::from_bits(0x1) as f64 -} - -fn main() { - println!("{:?}", make_dyn_star(Box::new(1i32))); -} diff --git a/tests/crashes/127676.rs b/tests/crashes/127676.rs deleted file mode 100644 index 81149c2ef84..00000000000 --- a/tests/crashes/127676.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ known-bug: #127676 -//@ edition:2018 - -#![feature(dyn_star,const_async_blocks)] - -static S: dyn* Send + Sync = async { 42 }; - -pub fn main() {} diff --git a/tests/run-make/unstable-feature-usage-metrics/lib.rs b/tests/run-make/unstable-feature-usage-metrics/lib.rs new file mode 100644 index 00000000000..2202d722c49 --- /dev/null +++ b/tests/run-make/unstable-feature-usage-metrics/lib.rs @@ -0,0 +1,9 @@ +#![feature(ascii_char)] // random lib feature +#![feature(box_patterns)] // random lang feature + +// picked arbitrary unstable features, just need a random lib and lang feature, ideally ones that +// won't be stabilized any time soon so we don't have to update this test + +fn main() { + println!("foobar"); +} diff --git a/tests/run-make/unstable-feature-usage-metrics/rmake.rs b/tests/run-make/unstable-feature-usage-metrics/rmake.rs new file mode 100644 index 00000000000..1397548a6fc --- /dev/null +++ b/tests/run-make/unstable-feature-usage-metrics/rmake.rs @@ -0,0 +1,87 @@ +//! This test checks if unstable feature usage metric dump files `unstable-feature-usage*.json` work +//! as expected. +//! +//! - Basic sanity checks on a default ICE dump. +//! +//! See <https://github.com/rust-lang/rust/issues/129485>. +//! +//! # Test history +//! +//! - forked from dump-ice-to-disk test, which has flakeyness issues on i686-mingw, I'm assuming +//! those will be present in this test as well on the same platform + +//@ ignore-windows +//FIXME(#128911): still flakey on i686-mingw. + +use std::path::{Path, PathBuf}; + +use run_make_support::rfs::create_dir_all; +use run_make_support::{ + cwd, filename_contains, has_extension, rfs, run_in_tmpdir, rustc, serde_json, + shallow_find_files, +}; + +fn find_feature_usage_metrics<P: AsRef<Path>>(dir: P) -> Vec<PathBuf> { + shallow_find_files(dir, |path| { + if filename_contains(path, "unstable_feature_usage") && has_extension(path, "json") { + true + } else { + dbg!(path); + false + } + }) +} + +fn main() { + test_metrics_dump(); + test_metrics_errors(); +} + +#[track_caller] +fn test_metrics_dump() { + run_in_tmpdir(|| { + let metrics_dir = cwd().join("metrics"); + create_dir_all(&metrics_dir); + rustc() + .input("lib.rs") + .env("RUST_BACKTRACE", "short") + .arg(format!("-Zmetrics-dir={}", metrics_dir.display())) + .run(); + let mut metrics = find_feature_usage_metrics(&metrics_dir); + let json_path = + metrics.pop().expect("there should be one metrics file in the output directory"); + + // After the `pop` above, there should be no files left. + assert!( + metrics.is_empty(), + "there should be no more than one metrics file in the output directory" + ); + + let message = rfs::read_to_string(json_path); + let parsed: serde_json::Value = + serde_json::from_str(&message).expect("metrics should be dumped as json"); + let expected = serde_json::json!( + { + "lib_features":[{"symbol":"ascii_char"}], + "lang_features":[{"symbol":"box_patterns","since":null}] + } + ); + + assert_eq!(expected, parsed); + }); +} + +#[track_caller] +fn test_metrics_errors() { + run_in_tmpdir(|| { + rustc() + .input("lib.rs") + .env("RUST_BACKTRACE", "short") + .arg("-Zmetrics-dir=invaliddirectorythatdefinitelydoesntexist") + .run_fail() + .assert_stderr_contains( + "error: cannot dump feature usage metrics: No such file or directory", + ) + .assert_stdout_not_contains("internal compiler error"); + }); +} diff --git a/tests/ui/asm/x86_64/goto-block-safe.rs b/tests/ui/asm/x86_64/goto-block-safe.rs new file mode 100644 index 00000000000..ee833a48a4b --- /dev/null +++ b/tests/ui/asm/x86_64/goto-block-safe.rs @@ -0,0 +1,23 @@ +//@ only-x86_64 +//@ needs-asm-support + +#![deny(unreachable_code)] +#![feature(asm_goto)] + +use std::arch::asm; + +fn goto_fallthough() { + unsafe { + asm!( + "/* {} */", + label { + core::hint::unreachable_unchecked(); + //~^ ERROR [E0133] + } + ) + } +} + +fn main() { + goto_fallthough(); +} diff --git a/tests/ui/asm/x86_64/goto-block-safe.stderr b/tests/ui/asm/x86_64/goto-block-safe.stderr new file mode 100644 index 00000000000..49818db7484 --- /dev/null +++ b/tests/ui/asm/x86_64/goto-block-safe.stderr @@ -0,0 +1,14 @@ +error[E0133]: call to unsafe function `unreachable_unchecked` is unsafe and requires unsafe function or block + --> $DIR/goto-block-safe.rs:14:17 + | +LL | unsafe { + | ------ items do not inherit unsafety from separate enclosing items +... +LL | core::hint::unreachable_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/closures/2229_closure_analysis/bad-pattern.stderr b/tests/ui/closures/2229_closure_analysis/bad-pattern.stderr index ca8c2a16d32..5f980c46a1f 100644 --- a/tests/ui/closures/2229_closure_analysis/bad-pattern.stderr +++ b/tests/ui/closures/2229_closure_analysis/bad-pattern.stderr @@ -97,16 +97,19 @@ LL | if let Refutable::A = v3 { todo!() }; error[E0005]: refutable pattern in local binding --> $DIR/bad-pattern.rs:19:13 | +LL | const PAT: u32 = 0; + | -------------- missing patterns are not covered because `PAT` is interpreted as a constant pattern, not a new variable +... LL | let PAT = v1; - | ^^^ - | | - | pattern `1_u32..=u32::MAX` not covered - | missing patterns are not covered because `PAT` is interpreted as a constant pattern, not a new variable - | help: introduce a variable instead: `PAT_var` + | ^^^ pattern `1_u32..=u32::MAX` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `u32` +help: introduce a variable instead + | +LL | let PAT_var = v1; + | ~~~~~~~ error: aborting due to 7 previous errors diff --git a/tests/ui/closures/correct-args-on-call-suggestion.rs b/tests/ui/closures/correct-args-on-call-suggestion.rs new file mode 100644 index 00000000000..fa7915a7c03 --- /dev/null +++ b/tests/ui/closures/correct-args-on-call-suggestion.rs @@ -0,0 +1,7 @@ +// Ensure we give the right args when we suggest calling a closure. + +fn main() { + let x = |a: i32, b: i32| a + b; + let y: i32 = x; + //~^ ERROR mismatched types +} diff --git a/tests/ui/closures/correct-args-on-call-suggestion.stderr b/tests/ui/closures/correct-args-on-call-suggestion.stderr new file mode 100644 index 00000000000..2613c7776b2 --- /dev/null +++ b/tests/ui/closures/correct-args-on-call-suggestion.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/correct-args-on-call-suggestion.rs:5:18 + | +LL | let x = |a: i32, b: i32| a + b; + | ---------------- the found closure +LL | let y: i32 = x; + | --- ^ expected `i32`, found closure + | | + | expected due to this + | + = note: expected type `i32` + found closure `{closure@$DIR/correct-args-on-call-suggestion.rs:4:13: 4:29}` +help: use parentheses to call this closure + | +LL | let y: i32 = x(/* i32 */, /* i32 */); + | ++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/confuse-field-and-method/issue-18343.stderr b/tests/ui/confuse-field-and-method/issue-18343.stderr index a51fd4f02aa..e50c971d837 100644 --- a/tests/ui/confuse-field-and-method/issue-18343.stderr +++ b/tests/ui/confuse-field-and-method/issue-18343.stderr @@ -7,7 +7,7 @@ LL | struct Obj<F> where F: FnMut() -> u32 { LL | o.closure(); | ^^^^^^^ field, not a method | -help: to call the function stored in `closure`, surround the field access with parentheses +help: to call the closure stored in `closure`, surround the field access with parentheses | LL | (o.closure)(); | + + diff --git a/tests/ui/confuse-field-and-method/issue-2392.stderr b/tests/ui/confuse-field-and-method/issue-2392.stderr index 440fbb27c00..77930de44a7 100644 --- a/tests/ui/confuse-field-and-method/issue-2392.stderr +++ b/tests/ui/confuse-field-and-method/issue-2392.stderr @@ -7,7 +7,7 @@ LL | struct Obj<F> where F: FnOnce() -> u32 { LL | o_closure.closure(); | ^^^^^^^ field, not a method | -help: to call the function stored in `closure`, surround the field access with parentheses +help: to call the closure stored in `closure`, surround the field access with parentheses | LL | (o_closure.closure)(); | + + @@ -46,7 +46,7 @@ LL | struct BoxedObj { LL | boxed_fn.boxed_closure(); | ^^^^^^^^^^^^^ field, not a method | -help: to call the function stored in `boxed_closure`, surround the field access with parentheses +help: to call the trait object stored in `boxed_closure`, surround the field access with parentheses | LL | (boxed_fn.boxed_closure)(); | + + @@ -60,7 +60,7 @@ LL | struct BoxedObj { LL | boxed_closure.boxed_closure(); | ^^^^^^^^^^^^^ field, not a method | -help: to call the function stored in `boxed_closure`, surround the field access with parentheses +help: to call the trait object stored in `boxed_closure`, surround the field access with parentheses | LL | (boxed_closure.boxed_closure)(); | + + @@ -99,7 +99,7 @@ LL | struct Obj<F> where F: FnOnce() -> u32 { LL | check_expression().closure(); | ^^^^^^^ field, not a method | -help: to call the function stored in `closure`, surround the field access with parentheses +help: to call the trait object stored in `closure`, surround the field access with parentheses | LL | (check_expression().closure)(); | + + @@ -113,7 +113,7 @@ LL | struct FuncContainer { LL | (*self.container).f1(1); | ^^ field, not a method | -help: to call the function stored in `f1`, surround the field access with parentheses +help: to call the function pointer stored in `f1`, surround the field access with parentheses | LL | ((*self.container).f1)(1); | + + @@ -127,7 +127,7 @@ LL | struct FuncContainer { LL | (*self.container).f2(1); | ^^ field, not a method | -help: to call the function stored in `f2`, surround the field access with parentheses +help: to call the function pointer stored in `f2`, surround the field access with parentheses | LL | ((*self.container).f2)(1); | + + @@ -141,7 +141,7 @@ LL | struct FuncContainer { LL | (*self.container).f3(1); | ^^ field, not a method | -help: to call the function stored in `f3`, surround the field access with parentheses +help: to call the function pointer stored in `f3`, surround the field access with parentheses | LL | ((*self.container).f3)(1); | + + diff --git a/tests/ui/confuse-field-and-method/issue-32128.stderr b/tests/ui/confuse-field-and-method/issue-32128.stderr index 3d860d8c85a..aaec15a41dc 100644 --- a/tests/ui/confuse-field-and-method/issue-32128.stderr +++ b/tests/ui/confuse-field-and-method/issue-32128.stderr @@ -7,7 +7,7 @@ LL | struct Example { LL | demo.example(1); | ^^^^^^^ field, not a method | -help: to call the function stored in `example`, surround the field access with parentheses +help: to call the trait object stored in `example`, surround the field access with parentheses | LL | (demo.example)(1); | + + diff --git a/tests/ui/confuse-field-and-method/issue-33784.stderr b/tests/ui/confuse-field-and-method/issue-33784.stderr index 8acd1f8ff1e..59a6f4fccd8 100644 --- a/tests/ui/confuse-field-and-method/issue-33784.stderr +++ b/tests/ui/confuse-field-and-method/issue-33784.stderr @@ -4,7 +4,7 @@ error[E0599]: no method named `closure` found for reference `&Obj<{closure@$DIR/ LL | p.closure(); | ^^^^^^^ field, not a method | -help: to call the function stored in `closure`, surround the field access with parentheses +help: to call the closure stored in `closure`, surround the field access with parentheses | LL | (p.closure)(); | + + @@ -19,7 +19,7 @@ error[E0599]: no method named `fn_ptr` found for reference `&&Obj<{closure@$DIR/ LL | q.fn_ptr(); | ^^^^^^ field, not a method | -help: to call the function stored in `fn_ptr`, surround the field access with parentheses +help: to call the function pointer stored in `fn_ptr`, surround the field access with parentheses | LL | (q.fn_ptr)(); | + + @@ -30,7 +30,7 @@ error[E0599]: no method named `c_fn_ptr` found for reference `&D` in the current LL | s.c_fn_ptr(); | ^^^^^^^^ field, not a method | -help: to call the function stored in `c_fn_ptr`, surround the field access with parentheses +help: to call the function pointer stored in `c_fn_ptr`, surround the field access with parentheses | LL | (s.c_fn_ptr)(); | + + diff --git a/tests/ui/consts/const-pattern-irrefutable.rs b/tests/ui/consts/const-pattern-irrefutable.rs index 61bdf57ffdb..759d2e8b2ed 100644 --- a/tests/ui/consts/const-pattern-irrefutable.rs +++ b/tests/ui/consts/const-pattern-irrefutable.rs @@ -1,28 +1,43 @@ mod foo { pub const b: u8 = 2; - pub const d: u8 = 2; + //~^ missing patterns are not covered because `b` is interpreted as a constant pattern, not a new variable + pub const d: (u8, u8) = (2, 1); + //~^ missing patterns are not covered because `d` is interpreted as a constant pattern, not a new variable } use foo::b as c; use foo::d; const a: u8 = 2; +//~^ missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable + +#[derive(PartialEq)] +struct S { + foo: u8, +} + +const e: S = S { + foo: 0, +}; fn main() { let a = 4; //~^ ERROR refutable pattern in local binding //~| patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered - //~| missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable //~| HELP introduce a variable instead let c = 4; //~^ ERROR refutable pattern in local binding //~| patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered - //~| missing patterns are not covered because `c` is interpreted as a constant pattern, not a new variable //~| HELP introduce a variable instead - let d = 4; + let d = (4, 4); //~^ ERROR refutable pattern in local binding - //~| patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered - //~| missing patterns are not covered because `d` is interpreted as a constant pattern, not a new variable + //~| patterns `(0_u8..=1_u8, _)` and `(3_u8..=u8::MAX, _)` not covered + //~| HELP introduce a variable instead + let e = S { + //~^ ERROR refutable pattern in local binding + //~| pattern `S { foo: 1_u8..=u8::MAX }` not covered //~| HELP introduce a variable instead + foo: 1, + }; fn f() {} // Check that the `NOTE`s still work with an item here (cf. issue #35115). } diff --git a/tests/ui/consts/const-pattern-irrefutable.stderr b/tests/ui/consts/const-pattern-irrefutable.stderr index 2aed68bdd64..afb67a3a118 100644 --- a/tests/ui/consts/const-pattern-irrefutable.stderr +++ b/tests/ui/consts/const-pattern-irrefutable.stderr @@ -1,45 +1,76 @@ error[E0005]: refutable pattern in local binding - --> $DIR/const-pattern-irrefutable.rs:12:9 + --> $DIR/const-pattern-irrefutable.rs:24:9 | +LL | const a: u8 = 2; + | ----------- missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable +... LL | let a = 4; - | ^ - | | - | patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered - | missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable - | help: introduce a variable instead: `a_var` + | ^ patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `u8` +help: introduce a variable instead + | +LL | let a_var = 4; + | ~~~~~ error[E0005]: refutable pattern in local binding - --> $DIR/const-pattern-irrefutable.rs:17:9 + --> $DIR/const-pattern-irrefutable.rs:28:9 | +LL | pub const b: u8 = 2; + | --------------- missing patterns are not covered because `b` is interpreted as a constant pattern, not a new variable +... LL | let c = 4; - | ^ - | | - | patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered - | missing patterns are not covered because `c` is interpreted as a constant pattern, not a new variable - | help: introduce a variable instead: `c_var` + | ^ patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `u8` +help: introduce a variable instead + | +LL | let b_var = 4; + | ~~~~~ error[E0005]: refutable pattern in local binding - --> $DIR/const-pattern-irrefutable.rs:22:9 + --> $DIR/const-pattern-irrefutable.rs:32:9 | -LL | let d = 4; - | ^ - | | - | patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered - | missing patterns are not covered because `d` is interpreted as a constant pattern, not a new variable - | help: introduce a variable instead: `d_var` +LL | pub const d: (u8, u8) = (2, 1); + | --------------------- missing patterns are not covered because `d` is interpreted as a constant pattern, not a new variable +... +LL | let d = (4, 4); + | ^ patterns `(0_u8..=1_u8, _)` and `(3_u8..=u8::MAX, _)` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html - = note: the matched value is of type `u8` + = note: the matched value is of type `(u8, u8)` +help: introduce a variable instead + | +LL | let d_var = (4, 4); + | ~~~~~ + +error[E0005]: refutable pattern in local binding + --> $DIR/const-pattern-irrefutable.rs:36:9 + | +LL | const e: S = S { + | ---------- missing patterns are not covered because `e` is interpreted as a constant pattern, not a new variable +... +LL | let e = S { + | ^ pattern `S { foo: 1_u8..=u8::MAX }` not covered + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html +note: `S` defined here + --> $DIR/const-pattern-irrefutable.rs:15:8 + | +LL | struct S { + | ^ + = note: the matched value is of type `S` +help: introduce a variable instead + | +LL | let e_var = S { + | ~~~~~ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0005`. diff --git a/tests/ui/consts/const_in_pattern/incomplete-slice.stderr b/tests/ui/consts/const_in_pattern/incomplete-slice.stderr index bd61f43727b..c73d1f05900 100644 --- a/tests/ui/consts/const_in_pattern/incomplete-slice.stderr +++ b/tests/ui/consts/const_in_pattern/incomplete-slice.stderr @@ -3,8 +3,20 @@ error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered | LL | match &[][..] { | ^^^^^^^ patterns `&[]` and `&[_, _, ..]` not covered +LL | +LL | E_SL => {} + | ---- this pattern doesn't introduce a new catch-all binding, but rather pattern matches against the value of constant `E_SL` | = note: the matched value is of type `&[E]` +note: constant `E_SL` defined here + --> $DIR/incomplete-slice.rs:6:1 + | +LL | const E_SL: &[E] = &[E::A]; + | ^^^^^^^^^^^^^^^^ +help: if you meant to introduce a binding, use a different name + | +LL | E_SL_var => {} + | ++++ help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL ~ E_SL => {}, diff --git a/tests/ui/drop/lint-tail-expr-drop-order-gated.rs b/tests/ui/drop/lint-tail-expr-drop-order-gated.rs index fde542c756f..508e7bdbf99 100644 --- a/tests/ui/drop/lint-tail-expr-drop-order-gated.rs +++ b/tests/ui/drop/lint-tail-expr-drop-order-gated.rs @@ -1,9 +1,10 @@ -// This test is to demonstrate that the lint is gated behind Edition and -// is triggered only for Edition 2021 and before. +// This test ensures that `tail_expr_drop_order` does not activate in case Edition 2024 is used +// because this is a migration lint. +// Only `cargo fix --edition 2024` shall activate this lint. //@ check-pass -//@ edition: 2024 //@ compile-flags: -Z unstable-options +//@ edition: 2024 #![deny(tail_expr_drop_order)] diff --git a/tests/ui/drop/lint-tail-expr-drop-order.rs b/tests/ui/drop/lint-tail-expr-drop-order.rs index d61abae5187..0fabc1f085c 100644 --- a/tests/ui/drop/lint-tail-expr-drop-order.rs +++ b/tests/ui/drop/lint-tail-expr-drop-order.rs @@ -1,13 +1,26 @@ -//@ edition: 2021 - // Edition 2024 lint for change in drop order at tail expression // This lint is to capture potential change in program semantics // due to implementation of RFC 3606 <https://github.com/rust-lang/rfcs/pull/3606> +//@ edition: 2021 +//@ build-fail -#![deny(tail_expr_drop_order)] +#![deny(tail_expr_drop_order)] //~ NOTE: the lint level is defined here +#![allow(dropping_copy_types)] struct LoudDropper; impl Drop for LoudDropper { + //~^ NOTE: `#1` invokes this custom destructor + //~| NOTE: `x` invokes this custom destructor + //~| NOTE: `#1` invokes this custom destructor + //~| NOTE: `x` invokes this custom destructor + //~| NOTE: `#1` invokes this custom destructor + //~| NOTE: `x` invokes this custom destructor + //~| NOTE: `#1` invokes this custom destructor + //~| NOTE: `x` invokes this custom destructor + //~| NOTE: `#1` invokes this custom destructor + //~| NOTE: `future` invokes this custom destructor + //~| NOTE: `_x` invokes this custom destructor + //~| NOTE: `#1` invokes this custom destructor fn drop(&mut self) { // This destructor should be considered significant because it is a custom destructor // and we will assume that the destructor can generate side effects arbitrarily so that @@ -23,30 +36,70 @@ impl LoudDropper { fn should_lint() -> i32 { let x = LoudDropper; + //~^ NOTE: `x` calls a custom destructor + //~| NOTE: `x` will be dropped later as of Edition 2024 // Should lint x.get() + LoudDropper.get() - //~^ ERROR: these values and local bindings have significant drop implementation that will have a different drop order from that of Edition 2021 + //~^ ERROR: relative drop order changing in Rust 2024 + //~| NOTE: this value will be stored in a temporary; let us call it `#1` + //~| NOTE: up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects + //~| NOTE: for more information, see issue #123739 } +//~^ NOTE: now the temporary value is dropped here, before the local variables in the block or statement -fn should_lint_closure() -> impl FnOnce() -> i32 { +fn should_not_lint_closure() -> impl FnOnce() -> i32 { let x = LoudDropper; - move || x.get() + LoudDropper.get() - //~^ ERROR: these values and local bindings have significant drop implementation that will have a different drop order from that of Edition 2021 - //~| WARN: this changes meaning in Rust 2024 + move || { + // Should not lint because ... + x.get() + LoudDropper.get() + } + // ^ closure captures like `x` are always dropped last by contract +} + +fn should_lint_in_nested_items() { + fn should_lint_me() -> i32 { + let x = LoudDropper; + //~^ NOTE: `x` calls a custom destructor + //~| NOTE: `x` will be dropped later as of Edition 2024 + // Should lint + x.get() + LoudDropper.get() + //~^ ERROR: relative drop order changing in Rust 2024 + //~| NOTE: this value will be stored in a temporary; let us call it `#1` + //~| NOTE: up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects + //~| NOTE: for more information, see issue #123739 + } + //~^ NOTE: now the temporary value is dropped here, before the local variables in the block or statement } +fn should_not_lint_params(x: LoudDropper) -> i32 { + // Should not lint because ... + x.get() + LoudDropper.get() +} +// ^ function parameters like `x` are always dropped last + fn should_not_lint() -> i32 { let x = LoudDropper; // Should not lint x.get() } -fn should_not_lint_in_nested_block() -> i32 { +fn should_lint_in_nested_block() -> i32 { let x = LoudDropper; - // Should not lint because Edition 2021 drops temporaries in blocks earlier already + //~^ NOTE: `x` calls a custom destructor + //~| NOTE: `x` will be dropped later as of Edition 2024 { LoudDropper.get() } + //~^ ERROR: relative drop order changing in Rust 2024 + //~| NOTE: this value will be stored in a temporary; let us call it `#1` + //~| NOTE: up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects + //~| NOTE: for more information, see issue #123739 } +//~^ NOTE: now the temporary value is dropped here, before the local variables in the block or statement fn should_not_lint_in_match_arm() -> i32 { let x = LoudDropper; @@ -56,14 +109,144 @@ fn should_not_lint_in_match_arm() -> i32 { } } -fn should_lint_in_nested_items() { - fn should_lint_me() -> i32 { +fn should_not_lint_when_consumed() -> (LoudDropper, i32) { + let x = LoudDropper; + // Should not lint because `LoudDropper` is consumed by the return value + (LoudDropper, x.get()) +} + +struct MyAdt { + a: LoudDropper, + b: LoudDropper, +} + +fn should_not_lint_when_consumed_in_ctor() -> MyAdt { + let a = LoudDropper; + // Should not lint + MyAdt { a, b: LoudDropper } +} + +fn should_not_lint_when_moved() -> i32 { + let x = LoudDropper; + drop(x); + // Should not lint because `x` is not live + LoudDropper.get() +} + +fn should_lint_into_async_body() -> i32 { + async fn f() { + async fn f() {} let x = LoudDropper; - // Should lint - x.get() + LoudDropper.get() - //~^ ERROR: these values and local bindings have significant drop implementation that will have a different drop order from that of Edition 2021 - //~| WARN: this changes meaning in Rust 2024 + f().await; + drop(x); + } + + let future = f(); + //~^ NOTE: `future` calls a custom destructor + //~| NOTE: `future` will be dropped later as of Edition 2024 + LoudDropper.get() + //~^ ERROR: relative drop order changing in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: this value will be stored in a temporary; let us call it `#1` + //~| NOTE: up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 + //~| NOTE: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects + //~| NOTE: for more information, see issue #123739 +} +//~^ NOTE: now the temporary value is dropped here, before the local variables in the block or statement + +fn should_lint_generics<T: Default>() -> &'static str { + fn extract<T>(_: &T) -> &'static str { + todo!() + } + let x = T::default(); + //~^ NOTE: `x` calls a custom destructor + //~| NOTE: `x` will be dropped later as of Edition 2024 + extract(&T::default()) + //~^ ERROR: relative drop order changing in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: this value will be stored in a temporary; let us call it `#1` + //~| NOTE: up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 + //~| NOTE: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects + //~| NOTE: for more information, see issue #123739 +} +//~^ NOTE: now the temporary value is dropped here, before the local variables in the block or statement + +fn should_lint_adt() -> i32 { + let x: Result<LoudDropper, ()> = Ok(LoudDropper); + //~^ NOTE: `x` calls a custom destructor + //~| NOTE: `x` will be dropped later as of Edition 2024 + LoudDropper.get() + //~^ ERROR: relative drop order changing in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: this value will be stored in a temporary; let us call it `#1` + //~| NOTE: up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 + //~| NOTE: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects + //~| NOTE: for more information, see issue #123739 +} +//~^ NOTE: now the temporary value is dropped here, before the local variables in the block or statement + +fn should_not_lint_insign_dtor() -> i32 { + let x = String::new(); + LoudDropper.get() +} + +fn should_lint_with_dtor_span() -> i32 { + struct LoudDropper3; + impl Drop for LoudDropper3 { + //~^ NOTE: `#1` invokes this custom destructor + fn drop(&mut self) { + println!("loud drop"); + } } + impl LoudDropper3 { + fn get(&self) -> i32 { + 0 + } + } + struct LoudDropper2; + impl Drop for LoudDropper2 { + //~^ NOTE: `x` invokes this custom destructor + fn drop(&mut self) { + println!("loud drop"); + } + } + impl LoudDropper2 { + fn get(&self) -> i32 { + 0 + } + } + + let x = LoudDropper2; + //~^ NOTE: `x` calls a custom destructor + //~| NOTE: `x` will be dropped later as of Edition 2024 + LoudDropper3.get() + //~^ ERROR: relative drop order changing in Rust 2024 + //~| NOTE: this value will be stored in a temporary; let us call it `#1` + //~| NOTE: up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects + //~| NOTE: for more information, see issue #123739 +} +//~^ NOTE: now the temporary value is dropped here, before the local variables in the block or statement + +fn should_lint_with_transient_drops() { + drop(( + { + LoudDropper.get() + //~^ ERROR: relative drop order changing in Rust 2024 + //~| NOTE: this value will be stored in a temporary; let us call it `#1` + //~| NOTE: up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects + //~| NOTE: for more information, see issue #123739 + }, + { + let _x = LoudDropper; + //~^ NOTE: `_x` calls a custom destructor + //~| NOTE: `_x` will be dropped later as of Edition 2024 + }, + )); + //~^ NOTE: now the temporary value is dropped here, before the local variables in the block or statement } fn main() {} diff --git a/tests/ui/drop/lint-tail-expr-drop-order.stderr b/tests/ui/drop/lint-tail-expr-drop-order.stderr index 6775c4ce6d1..f0da24605e6 100644 --- a/tests/ui/drop/lint-tail-expr-drop-order.stderr +++ b/tests/ui/drop/lint-tail-expr-drop-order.stderr @@ -1,42 +1,335 @@ -error: these values and local bindings have significant drop implementation that will have a different drop order from that of Edition 2021 - --> $DIR/lint-tail-expr-drop-order.rs:27:15 +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order.rs:42:15 | LL | let x = LoudDropper; - | - these values have significant drop implementation and will observe changes in drop order under Edition 2024 -LL | // Should lint + | - + | | + | `x` calls a custom destructor + | `x` will be dropped later as of Edition 2024 +... LL | x.get() + LoudDropper.get() | ^^^^^^^^^^^ + | | + | this value will be stored in a temporary; let us call it `#1` + | up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 +... +LL | } + | - now the temporary value is dropped here, before the local variables in the block or statement | = warning: this changes meaning in Rust 2024 = note: for more information, see issue #123739 <https://github.com/rust-lang/rust/issues/123739> +note: `#1` invokes this custom destructor + --> $DIR/lint-tail-expr-drop-order.rs:11:1 + | +LL | / impl Drop for LoudDropper { +LL | | +LL | | +LL | | +... | +LL | | } +LL | | } + | |_^ +note: `x` invokes this custom destructor + --> $DIR/lint-tail-expr-drop-order.rs:11:1 + | +LL | / impl Drop for LoudDropper { +LL | | +LL | | +LL | | +... | +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages note: the lint level is defined here --> $DIR/lint-tail-expr-drop-order.rs:7:9 | LL | #![deny(tail_expr_drop_order)] | ^^^^^^^^^^^^^^^^^^^^ -error: these values and local bindings have significant drop implementation that will have a different drop order from that of Edition 2021 - --> $DIR/lint-tail-expr-drop-order.rs:34:23 +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order.rs:67:19 + | +LL | let x = LoudDropper; + | - + | | + | `x` calls a custom destructor + | `x` will be dropped later as of Edition 2024 +... +LL | x.get() + LoudDropper.get() + | ^^^^^^^^^^^ + | | + | this value will be stored in a temporary; let us call it `#1` + | up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 +... +LL | } + | - now the temporary value is dropped here, before the local variables in the block or statement + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #123739 <https://github.com/rust-lang/rust/issues/123739> +note: `#1` invokes this custom destructor + --> $DIR/lint-tail-expr-drop-order.rs:11:1 + | +LL | / impl Drop for LoudDropper { +LL | | +LL | | +LL | | +... | +LL | | } +LL | | } + | |_^ +note: `x` invokes this custom destructor + --> $DIR/lint-tail-expr-drop-order.rs:11:1 + | +LL | / impl Drop for LoudDropper { +LL | | +LL | | +LL | | +... | +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages + +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order.rs:94:7 | LL | let x = LoudDropper; - | - these values have significant drop implementation and will observe changes in drop order under Edition 2024 -LL | move || x.get() + LoudDropper.get() - | ^^^^^^^^^^^ + | - + | | + | `x` calls a custom destructor + | `x` will be dropped later as of Edition 2024 +... +LL | { LoudDropper.get() } + | ^^^^^^^^^^^ + | | + | this value will be stored in a temporary; let us call it `#1` + | up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 +... +LL | } + | - now the temporary value is dropped here, before the local variables in the block or statement | = warning: this changes meaning in Rust 2024 = note: for more information, see issue #123739 <https://github.com/rust-lang/rust/issues/123739> +note: `#1` invokes this custom destructor + --> $DIR/lint-tail-expr-drop-order.rs:11:1 + | +LL | / impl Drop for LoudDropper { +LL | | +LL | | +LL | | +... | +LL | | } +LL | | } + | |_^ +note: `x` invokes this custom destructor + --> $DIR/lint-tail-expr-drop-order.rs:11:1 + | +LL | / impl Drop for LoudDropper { +LL | | +LL | | +LL | | +... | +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages -error: these values and local bindings have significant drop implementation that will have a different drop order from that of Edition 2021 - --> $DIR/lint-tail-expr-drop-order.rs:63:19 +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order.rs:147:5 | -LL | let x = LoudDropper; - | - these values have significant drop implementation and will observe changes in drop order under Edition 2024 -LL | // Should lint -LL | x.get() + LoudDropper.get() - | ^^^^^^^^^^^ +LL | let future = f(); + | ------ + | | + | `future` calls a custom destructor + | `future` will be dropped later as of Edition 2024 +... +LL | LoudDropper.get() + | ^^^^^^^^^^^ + | | + | this value will be stored in a temporary; let us call it `#1` + | up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 +... +LL | } + | - now the temporary value is dropped here, before the local variables in the block or statement | = warning: this changes meaning in Rust 2024 = note: for more information, see issue #123739 <https://github.com/rust-lang/rust/issues/123739> +note: `#1` invokes this custom destructor + --> $DIR/lint-tail-expr-drop-order.rs:11:1 + | +LL | / impl Drop for LoudDropper { +LL | | +LL | | +LL | | +... | +LL | | } +LL | | } + | |_^ +note: `future` invokes this custom destructor + --> $DIR/lint-tail-expr-drop-order.rs:11:1 + | +LL | / impl Drop for LoudDropper { +LL | | +LL | | +LL | | +... | +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages + +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order.rs:164:14 + | +LL | let x = T::default(); + | - + | | + | `x` calls a custom destructor + | `x` will be dropped later as of Edition 2024 +... +LL | extract(&T::default()) + | ^^^^^^^^^^^^ + | | + | this value will be stored in a temporary; let us call it `#1` + | up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 +... +LL | } + | - now the temporary value is dropped here, before the local variables in the block or statement + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #123739 <https://github.com/rust-lang/rust/issues/123739> + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages + +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order.rs:178:5 + | +LL | let x: Result<LoudDropper, ()> = Ok(LoudDropper); + | - + | | + | `x` calls a custom destructor + | `x` will be dropped later as of Edition 2024 +... +LL | LoudDropper.get() + | ^^^^^^^^^^^ + | | + | this value will be stored in a temporary; let us call it `#1` + | up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 +... +LL | } + | - now the temporary value is dropped here, before the local variables in the block or statement + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #123739 <https://github.com/rust-lang/rust/issues/123739> +note: `#1` invokes this custom destructor + --> $DIR/lint-tail-expr-drop-order.rs:11:1 + | +LL | / impl Drop for LoudDropper { +LL | | +LL | | +LL | | +... | +LL | | } +LL | | } + | |_^ +note: `x` invokes this custom destructor + --> $DIR/lint-tail-expr-drop-order.rs:11:1 + | +LL | / impl Drop for LoudDropper { +LL | | +LL | | +LL | | +... | +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages + +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order.rs:222:5 + | +LL | let x = LoudDropper2; + | - + | | + | `x` calls a custom destructor + | `x` will be dropped later as of Edition 2024 +... +LL | LoudDropper3.get() + | ^^^^^^^^^^^^ + | | + | this value will be stored in a temporary; let us call it `#1` + | up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 +... +LL | } + | - now the temporary value is dropped here, before the local variables in the block or statement + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #123739 <https://github.com/rust-lang/rust/issues/123739> +note: `#1` invokes this custom destructor + --> $DIR/lint-tail-expr-drop-order.rs:195:5 + | +LL | / impl Drop for LoudDropper3 { +LL | | +LL | | fn drop(&mut self) { +LL | | println!("loud drop"); +LL | | } +LL | | } + | |_____^ +note: `x` invokes this custom destructor + --> $DIR/lint-tail-expr-drop-order.rs:207:5 + | +LL | / impl Drop for LoudDropper2 { +LL | | +LL | | fn drop(&mut self) { +LL | | println!("loud drop"); +LL | | } +LL | | } + | |_____^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages + +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order.rs:235:13 + | +LL | LoudDropper.get() + | ^^^^^^^^^^^ + | | + | this value will be stored in a temporary; let us call it `#1` + | up until Edition 2021 `#1` is dropped last but will be dropped earlier in Edition 2024 +... +LL | let _x = LoudDropper; + | -- + | | + | `_x` calls a custom destructor + | `_x` will be dropped later as of Edition 2024 +... +LL | )); + | - now the temporary value is dropped here, before the local variables in the block or statement + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #123739 <https://github.com/rust-lang/rust/issues/123739> +note: `#1` invokes this custom destructor + --> $DIR/lint-tail-expr-drop-order.rs:11:1 + | +LL | / impl Drop for LoudDropper { +LL | | +LL | | +LL | | +... | +LL | | } +LL | | } + | |_^ +note: `_x` invokes this custom destructor + --> $DIR/lint-tail-expr-drop-order.rs:11:1 + | +LL | / impl Drop for LoudDropper { +LL | | +LL | | +LL | | +... | +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages -error: aborting due to 3 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/dyn-star/async-block-dyn-star.rs b/tests/ui/dyn-star/async-block-dyn-star.rs new file mode 100644 index 00000000000..9bffd6c6725 --- /dev/null +++ b/tests/ui/dyn-star/async-block-dyn-star.rs @@ -0,0 +1,9 @@ +//@ edition:2018 + +#![feature(dyn_star, const_async_blocks)] +//~^ WARN the feature `dyn_star` is incomplete + +static S: dyn* Send + Sync = async { 42 }; +//~^ needs to have the same ABI as a pointer + +pub fn main() {} diff --git a/tests/ui/dyn-star/async-block-dyn-star.stderr b/tests/ui/dyn-star/async-block-dyn-star.stderr new file mode 100644 index 00000000000..f62c85c0ad2 --- /dev/null +++ b/tests/ui/dyn-star/async-block-dyn-star.stderr @@ -0,0 +1,20 @@ +warning: the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/async-block-dyn-star.rs:3:12 + | +LL | #![feature(dyn_star, const_async_blocks)] + | ^^^^^^^^ + | + = note: see issue #102425 <https://github.com/rust-lang/rust/issues/102425> for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: `{async block@$DIR/async-block-dyn-star.rs:6:30: 6:35}` needs to have the same ABI as a pointer + --> $DIR/async-block-dyn-star.rs:6:30 + | +LL | static S: dyn* Send + Sync = async { 42 }; + | ^^^^^^^^^^^^ `{async block@$DIR/async-block-dyn-star.rs:6:30: 6:35}` needs to be a pointer-like type + | + = help: the trait `PointerLike` is not implemented for `{async block@$DIR/async-block-dyn-star.rs:6:30: 6:35}` + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.current.stderr b/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.current.stderr index 7b5ea7bb707..a0aff69f396 100644 --- a/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.current.stderr +++ b/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.current.stderr @@ -1,14 +1,17 @@ error[E0277]: `&T` needs to have the same ABI as a pointer --> $DIR/check-size-at-cast-polymorphic-bad.rs:15:15 | +LL | fn polymorphic<T: Debug + ?Sized>(t: &T) { + | - this type parameter needs to be `Sized` LL | dyn_debug(t); | ^ `&T` needs to be a pointer-like type | - = help: the trait `PointerLike` is not implemented for `&T` -help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + = note: required for `&T` to implement `PointerLike` +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - fn polymorphic<T: Debug + ?Sized>(t: &T) { +LL + fn polymorphic<T: Debug>(t: &T) { | -LL | fn polymorphic<T: Debug + ?Sized>(t: &T) where &T: PointerLike { - | +++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.next.stderr b/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.next.stderr index 7b5ea7bb707..a0aff69f396 100644 --- a/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.next.stderr +++ b/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.next.stderr @@ -1,14 +1,17 @@ error[E0277]: `&T` needs to have the same ABI as a pointer --> $DIR/check-size-at-cast-polymorphic-bad.rs:15:15 | +LL | fn polymorphic<T: Debug + ?Sized>(t: &T) { + | - this type parameter needs to be `Sized` LL | dyn_debug(t); | ^ `&T` needs to be a pointer-like type | - = help: the trait `PointerLike` is not implemented for `&T` -help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + = note: required for `&T` to implement `PointerLike` +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - fn polymorphic<T: Debug + ?Sized>(t: &T) { +LL + fn polymorphic<T: Debug>(t: &T) { | -LL | fn polymorphic<T: Debug + ?Sized>(t: &T) where &T: PointerLike { - | +++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/dyn-star/drop.rs b/tests/ui/dyn-star/drop.rs index ca86f1b5b01..bc746331527 100644 --- a/tests/ui/dyn-star/drop.rs +++ b/tests/ui/dyn-star/drop.rs @@ -1,13 +1,18 @@ //@ run-pass //@ check-run-results -#![feature(dyn_star)] +#![feature(dyn_star, pointer_like_trait)] #![allow(incomplete_features)] use std::fmt::Debug; +use std::marker::PointerLike; #[derive(Debug)] +#[repr(transparent)] struct Foo(#[allow(dead_code)] usize); +// FIXME(dyn_star): Make this into a derive. +impl PointerLike for Foo {} + impl Drop for Foo { fn drop(&mut self) { println!("destructor called"); diff --git a/tests/ui/dyn-star/enum-cast.rs b/tests/ui/dyn-star/enum-cast.rs index 6e895e9527a..3cc7390eb12 100644 --- a/tests/ui/dyn-star/enum-cast.rs +++ b/tests/ui/dyn-star/enum-cast.rs @@ -3,13 +3,18 @@ // This used to ICE, because the compiler confused a pointer-like to dyn* coercion // with a c-like enum to integer cast. -#![feature(dyn_star)] +#![feature(dyn_star, pointer_like_trait)] #![expect(incomplete_features)] +use std::marker::PointerLike; + +#[repr(transparent)] enum E { Num(usize), } +impl PointerLike for E {} + trait Trait {} impl Trait for E {} diff --git a/tests/ui/dyn-star/error.rs b/tests/ui/dyn-star/error.rs index d8261387efa..7288596f3fa 100644 --- a/tests/ui/dyn-star/error.rs +++ b/tests/ui/dyn-star/error.rs @@ -7,7 +7,7 @@ trait Foo {} fn make_dyn_star() { let i = 42; - let dyn_i: dyn* Foo = i; //~ ERROR trait bound `{integer}: Foo` is not satisfied + let dyn_i: dyn* Foo = i; //~ ERROR trait bound `usize: Foo` is not satisfied } fn main() {} diff --git a/tests/ui/dyn-star/error.stderr b/tests/ui/dyn-star/error.stderr index a9f4a054519..55981c03bac 100644 --- a/tests/ui/dyn-star/error.stderr +++ b/tests/ui/dyn-star/error.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `{integer}: Foo` is not satisfied +error[E0277]: the trait bound `usize: Foo` is not satisfied --> $DIR/error.rs:10:27 | LL | let dyn_i: dyn* Foo = i; - | ^ the trait `Foo` is not implemented for `{integer}` + | ^ the trait `Foo` is not implemented for `usize` | help: this trait has no implementations, consider adding one --> $DIR/error.rs:6:1 diff --git a/tests/ui/dyn-star/float-as-dyn-star.rs b/tests/ui/dyn-star/float-as-dyn-star.rs new file mode 100644 index 00000000000..1b629c64c25 --- /dev/null +++ b/tests/ui/dyn-star/float-as-dyn-star.rs @@ -0,0 +1,16 @@ +//@ only-x86_64 + +#![feature(dyn_star, pointer_like_trait)] +//~^ WARN the feature `dyn_star` is incomplete + +use std::fmt::Debug; +use std::marker::PointerLike; + +fn make_dyn_star() -> dyn* Debug + 'static { + f32::from_bits(0x1) as f64 + //~^ ERROR `f64` needs to have the same ABI as a pointer +} + +fn main() { + println!("{:?}", make_dyn_star()); +} diff --git a/tests/ui/dyn-star/float-as-dyn-star.stderr b/tests/ui/dyn-star/float-as-dyn-star.stderr new file mode 100644 index 00000000000..9caba512e5f --- /dev/null +++ b/tests/ui/dyn-star/float-as-dyn-star.stderr @@ -0,0 +1,21 @@ +warning: the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/float-as-dyn-star.rs:3:12 + | +LL | #![feature(dyn_star, pointer_like_trait)] + | ^^^^^^^^ + | + = note: see issue #102425 <https://github.com/rust-lang/rust/issues/102425> for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: `f64` needs to have the same ABI as a pointer + --> $DIR/float-as-dyn-star.rs:10:5 + | +LL | f32::from_bits(0x1) as f64 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `f64` needs to be a pointer-like type + | + = help: the trait `PointerLike` is not implemented for `f64` + = help: the trait `PointerLike` is implemented for `usize` + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dyn-star/upcast.stderr b/tests/ui/dyn-star/upcast.stderr index adef9525bf1..801e1c233c1 100644 --- a/tests/ui/dyn-star/upcast.stderr +++ b/tests/ui/dyn-star/upcast.stderr @@ -7,6 +7,14 @@ LL | #![feature(dyn_star, trait_upcasting)] = note: see issue #102425 <https://github.com/rust-lang/rust/issues/102425> for more information = note: `#[warn(incomplete_features)]` on by default +error[E0277]: `W` needs to have the same ABI as a pointer + --> $DIR/upcast.rs:28:23 + | +LL | let w: dyn* Foo = W(0); + | ^^^^ `W` needs to be a pointer-like type + | + = help: the trait `PointerLike` is not implemented for `W` + error[E0277]: `dyn* Foo` needs to have the same ABI as a pointer --> $DIR/upcast.rs:30:23 | @@ -15,6 +23,6 @@ LL | let w: dyn* Bar = w; | = help: the trait `PointerLike` is not implemented for `dyn* Foo` -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/generics/generics-on-self-mod-segment.rs b/tests/ui/generics/generics-on-self-mod-segment.rs new file mode 100644 index 00000000000..ef229eeba53 --- /dev/null +++ b/tests/ui/generics/generics-on-self-mod-segment.rs @@ -0,0 +1,18 @@ +struct Ty; + +fn self_(_: self::<i32>::Ty) {} +//~^ ERROR type arguments are not allowed on module `generics_on_self_mod_segment` + +fn crate_(_: crate::<i32>::Ty) {} +//~^ ERROR type arguments are not allowed on module `generics_on_self_mod_segment` + +macro_rules! dollar_crate { + () => { + fn dollar_crate_(_: $crate::<i32>::Ty) {} + //~^ ERROR type arguments are not allowed on module `generics_on_self_mod_segment` + } +} + +dollar_crate!(); + +fn main() {} diff --git a/tests/ui/generics/generics-on-self-mod-segment.stderr b/tests/ui/generics/generics-on-self-mod-segment.stderr new file mode 100644 index 00000000000..4a2d5939a3e --- /dev/null +++ b/tests/ui/generics/generics-on-self-mod-segment.stderr @@ -0,0 +1,32 @@ +error[E0109]: type arguments are not allowed on module `generics_on_self_mod_segment` + --> $DIR/generics-on-self-mod-segment.rs:3:20 + | +LL | fn self_(_: self::<i32>::Ty) {} + | ---- ^^^ type argument not allowed + | | + | not allowed on module `generics_on_self_mod_segment` + +error[E0109]: type arguments are not allowed on module `generics_on_self_mod_segment` + --> $DIR/generics-on-self-mod-segment.rs:6:22 + | +LL | fn crate_(_: crate::<i32>::Ty) {} + | ----- ^^^ type argument not allowed + | | + | not allowed on module `generics_on_self_mod_segment` + +error[E0109]: type arguments are not allowed on module `generics_on_self_mod_segment` + --> $DIR/generics-on-self-mod-segment.rs:11:38 + | +LL | fn dollar_crate_(_: $crate::<i32>::Ty) {} + | ------ ^^^ type argument not allowed + | | + | not allowed on module `generics_on_self_mod_segment` +... +LL | dollar_crate!(); + | --------------- in this macro invocation + | + = note: this error originates in the macro `dollar_crate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0109`. diff --git a/tests/ui/inline-const/const_block_pat_liveness.rs b/tests/ui/inline-const/const-block-pat-liveness.rs index 26393a4f65b..26393a4f65b 100644 --- a/tests/ui/inline-const/const_block_pat_liveness.rs +++ b/tests/ui/inline-const/const-block-pat-liveness.rs diff --git a/tests/ui/inline-const/cross_const_control_flow.rs b/tests/ui/inline-const/cross-const-control-flow-125846.rs index 109764a1812..109764a1812 100644 --- a/tests/ui/inline-const/cross_const_control_flow.rs +++ b/tests/ui/inline-const/cross-const-control-flow-125846.rs diff --git a/tests/ui/inline-const/cross_const_control_flow.stderr b/tests/ui/inline-const/cross-const-control-flow-125846.stderr index ecfa921edd2..4aa1c273504 100644 --- a/tests/ui/inline-const/cross_const_control_flow.stderr +++ b/tests/ui/inline-const/cross-const-control-flow-125846.stderr @@ -1,5 +1,5 @@ error[E0767]: use of unreachable label `'a` - --> $DIR/cross_const_control_flow.rs:9:25 + --> $DIR/cross-const-control-flow-125846.rs:9:25 | LL | 'a: { const { break 'a } } | -- ^^ unreachable label `'a` @@ -9,7 +9,7 @@ LL | 'a: { const { break 'a } } = note: labels are unreachable through functions, closures, async blocks and modules error[E0767]: use of unreachable label `'a` - --> $DIR/cross_const_control_flow.rs:22:28 + --> $DIR/cross-const-control-flow-125846.rs:22:28 | LL | 'a: { const { continue 'a } } | -- ^^ unreachable label `'a` @@ -19,7 +19,7 @@ LL | 'a: { const { continue 'a } } = note: labels are unreachable through functions, closures, async blocks and modules error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/cross_const_control_flow.rs:41:14 + --> $DIR/cross-const-control-flow-125846.rs:41:14 | LL | const { &x }; | ^ non-constant value @@ -30,7 +30,7 @@ LL | const x: /* Type */ = 1; | ~~~~~ ++++++++++++ error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/cross_const_control_flow.rs:35:22 + --> $DIR/cross-const-control-flow-125846.rs:35:22 | LL | const { async {}.await } | -----------^^^^^-- @@ -39,31 +39,31 @@ LL | const { async {}.await } | this is not `async` error[E0268]: `break` outside of a loop or labeled block - --> $DIR/cross_const_control_flow.rs:9:19 + --> $DIR/cross-const-control-flow-125846.rs:9:19 | LL | 'a: { const { break 'a } } | ^^^^^^^^ cannot `break` outside of a loop or labeled block error[E0268]: `break` outside of a loop or labeled block - --> $DIR/cross_const_control_flow.rs:16:17 + --> $DIR/cross-const-control-flow-125846.rs:16:17 | LL | const { break } | ^^^^^ cannot `break` outside of a loop or labeled block error[E0268]: `continue` outside of a loop - --> $DIR/cross_const_control_flow.rs:22:19 + --> $DIR/cross-const-control-flow-125846.rs:22:19 | LL | 'a: { const { continue 'a } } | ^^^^^^^^^^^ cannot `continue` outside of a loop error[E0268]: `continue` outside of a loop - --> $DIR/cross_const_control_flow.rs:29:17 + --> $DIR/cross-const-control-flow-125846.rs:29:17 | LL | const { continue } | ^^^^^^^^ cannot `continue` outside of a loop error[E0572]: return statement outside of function body - --> $DIR/cross_const_control_flow.rs:4:13 + --> $DIR/cross-const-control-flow-125846.rs:4:13 | LL | / fn foo() { LL | | const { return } diff --git a/tests/ui/inline-const/referencing_local_variables.rs b/tests/ui/inline-const/referencing-local-variables.rs index f9f0fef07f0..f9f0fef07f0 100644 --- a/tests/ui/inline-const/referencing_local_variables.rs +++ b/tests/ui/inline-const/referencing-local-variables.rs diff --git a/tests/ui/inline-const/referencing_local_variables.stderr b/tests/ui/inline-const/referencing-local-variables.stderr index 4a0a5406602..7e1cecdddcb 100644 --- a/tests/ui/inline-const/referencing_local_variables.stderr +++ b/tests/ui/inline-const/referencing-local-variables.stderr @@ -1,5 +1,5 @@ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/referencing_local_variables.rs:2:13 + --> $DIR/referencing-local-variables.rs:2:13 | LL | const fn test_me<T>(a: usize) -> usize { | - this would need to be a `const` diff --git a/tests/ui/inline-const/uninit_local.rs b/tests/ui/inline-const/uninit-local.rs index 548c053affc..548c053affc 100644 --- a/tests/ui/inline-const/uninit_local.rs +++ b/tests/ui/inline-const/uninit-local.rs diff --git a/tests/ui/inline-const/uninit_local.stderr b/tests/ui/inline-const/uninit-local.stderr index 37b78e337e7..fab65ffb5fa 100644 --- a/tests/ui/inline-const/uninit_local.stderr +++ b/tests/ui/inline-const/uninit-local.stderr @@ -1,5 +1,5 @@ error[E0381]: used binding `x` isn't initialized - --> $DIR/uninit_local.rs:4:15 + --> $DIR/uninit-local.rs:4:15 | LL | let x: bool; | - binding declared here but left uninitialized diff --git a/tests/ui/match/intended-binding-pattern-is-const.rs b/tests/ui/match/intended-binding-pattern-is-const.rs new file mode 100644 index 00000000000..95c8119cdb9 --- /dev/null +++ b/tests/ui/match/intended-binding-pattern-is-const.rs @@ -0,0 +1,10 @@ +fn main() { + match 1 { //~ ERROR non-exhaustive patterns + //~^ patterns `i32::MIN..=3_i32` and `5_i32..=i32::MAX` not covered + //~| the matched value is of type `i32` + x => {} //~ this pattern doesn't introduce a new catch-all binding + //~^ HELP ensure that all possible cases are being handled + //~| HELP if you meant to introduce a binding, use a different name + } + const x: i32 = 4; //~ NOTE constant `x` defined here +} diff --git a/tests/ui/match/intended-binding-pattern-is-const.stderr b/tests/ui/match/intended-binding-pattern-is-const.stderr new file mode 100644 index 00000000000..99af1c7a16e --- /dev/null +++ b/tests/ui/match/intended-binding-pattern-is-const.stderr @@ -0,0 +1,27 @@ +error[E0004]: non-exhaustive patterns: `i32::MIN..=3_i32` and `5_i32..=i32::MAX` not covered + --> $DIR/intended-binding-pattern-is-const.rs:2:11 + | +LL | match 1 { + | ^ patterns `i32::MIN..=3_i32` and `5_i32..=i32::MAX` not covered +... +LL | x => {} + | - this pattern doesn't introduce a new catch-all binding, but rather pattern matches against the value of constant `x` + | + = note: the matched value is of type `i32` +note: constant `x` defined here + --> $DIR/intended-binding-pattern-is-const.rs:9:5 + | +LL | const x: i32 = 4; + | ^^^^^^^^^^^^ +help: if you meant to introduce a binding, use a different name + | +LL | x_var => {} + | ++++ +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms + | +LL | x => {}, i32::MIN..=3_i32 | 5_i32..=i32::MAX => todo!() + | ++++++++++++++++++++++++++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/mir/issue-112269.stderr b/tests/ui/mir/issue-112269.stderr index f5b79602797..adb662c98a7 100644 --- a/tests/ui/mir/issue-112269.stderr +++ b/tests/ui/mir/issue-112269.stderr @@ -1,30 +1,34 @@ error[E0005]: refutable pattern in local binding --> $DIR/issue-112269.rs:3:9 | +LL | const x: i32 = 4; + | ------------ missing patterns are not covered because `x` is interpreted as a constant pattern, not a new variable LL | let x: i32 = 3; - | ^ - | | - | patterns `i32::MIN..=3_i32` and `5_i32..=i32::MAX` not covered - | missing patterns are not covered because `x` is interpreted as a constant pattern, not a new variable - | help: introduce a variable instead: `x_var` + | ^ patterns `i32::MIN..=3_i32` and `5_i32..=i32::MAX` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` +help: introduce a variable instead + | +LL | let x_var: i32 = 3; + | ~~~~~ error[E0005]: refutable pattern in local binding --> $DIR/issue-112269.rs:7:9 | +LL | const y: i32 = 3; + | ------------ missing patterns are not covered because `y` is interpreted as a constant pattern, not a new variable LL | let y = 4; - | ^ - | | - | patterns `i32::MIN..=2_i32` and `4_i32..=i32::MAX` not covered - | missing patterns are not covered because `y` is interpreted as a constant pattern, not a new variable - | help: introduce a variable instead: `y_var` + | ^ patterns `i32::MIN..=2_i32` and `4_i32..=i32::MAX` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` +help: introduce a variable instead + | +LL | let y_var = 4; + | ~~~~~ error: aborting due to 2 previous errors diff --git a/tests/ui/pattern/usefulness/match-arm-statics-2.stderr b/tests/ui/pattern/usefulness/match-arm-statics-2.stderr index e4dd35a5995..60b4fcca286 100644 --- a/tests/ui/pattern/usefulness/match-arm-statics-2.stderr +++ b/tests/ui/pattern/usefulness/match-arm-statics-2.stderr @@ -3,8 +3,20 @@ error[E0004]: non-exhaustive patterns: `(true, false)` not covered | LL | match (true, false) { | ^^^^^^^^^^^^^ pattern `(true, false)` not covered +LL | +LL | TRUE_TRUE => (), + | --------- this pattern doesn't introduce a new catch-all binding, but rather pattern matches against the value of constant `TRUE_TRUE` | = note: the matched value is of type `(bool, bool)` +note: constant `TRUE_TRUE` defined here + --> $DIR/match-arm-statics-2.rs:14:1 + | +LL | const TRUE_TRUE: (bool, bool) = (true, true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: if you meant to introduce a binding, use a different name + | +LL | TRUE_TRUE_var => (), + | ++++ help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ (false, true) => (), diff --git a/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr b/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr index a8786d02414..0a3991fe3d1 100644 --- a/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr +++ b/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr @@ -199,8 +199,20 @@ error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered | LL | match s { | ^ patterns `&[]` and `&[_, _, ..]` not covered +LL | +LL | CONST => {} + | ----- this pattern doesn't introduce a new catch-all binding, but rather pattern matches against the value of constant `CONST` | = note: the matched value is of type `&[bool]` +note: constant `CONST` defined here + --> $DIR/slice-patterns-exhaustiveness.rs:88:5 + | +LL | const CONST: &[bool] = &[true]; + | ^^^^^^^^^^^^^^^^^^^^ +help: if you meant to introduce a binding, use a different name + | +LL | CONST_var => {} + | ++++ help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL ~ CONST => {}, @@ -212,8 +224,20 @@ error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered | LL | match s { | ^ patterns `&[]` and `&[_, _, ..]` not covered +LL | +LL | CONST => {} + | ----- this pattern doesn't introduce a new catch-all binding, but rather pattern matches against the value of constant `CONST` | = note: the matched value is of type `&[bool]` +note: constant `CONST` defined here + --> $DIR/slice-patterns-exhaustiveness.rs:88:5 + | +LL | const CONST: &[bool] = &[true]; + | ^^^^^^^^^^^^^^^^^^^^ +help: if you meant to introduce a binding, use a different name + | +LL | CONST_var => {} + | ++++ help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL ~ &[false] => {}, @@ -225,8 +249,20 @@ error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered | LL | match s { | ^ patterns `&[]` and `&[_, _, ..]` not covered +... +LL | CONST => {} + | ----- this pattern doesn't introduce a new catch-all binding, but rather pattern matches against the value of constant `CONST` | = note: the matched value is of type `&[bool]` +note: constant `CONST` defined here + --> $DIR/slice-patterns-exhaustiveness.rs:88:5 + | +LL | const CONST: &[bool] = &[true]; + | ^^^^^^^^^^^^^^^^^^^^ +help: if you meant to introduce a binding, use a different name + | +LL | CONST_var => {} + | ++++ help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL ~ CONST => {}, @@ -238,8 +274,20 @@ error[E0004]: non-exhaustive patterns: `&[_, _, ..]` not covered | LL | match s { | ^ pattern `&[_, _, ..]` not covered +... +LL | CONST => {} + | ----- this pattern doesn't introduce a new catch-all binding, but rather pattern matches against the value of constant `CONST` | = note: the matched value is of type `&[bool]` +note: constant `CONST` defined here + --> $DIR/slice-patterns-exhaustiveness.rs:88:5 + | +LL | const CONST: &[bool] = &[true]; + | ^^^^^^^^^^^^^^^^^^^^ +help: if you meant to introduce a binding, use a different name + | +LL | CONST_var => {} + | ++++ help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ CONST => {}, @@ -251,8 +299,20 @@ error[E0004]: non-exhaustive patterns: `&[false]` not covered | LL | match s { | ^ pattern `&[false]` not covered +... +LL | CONST => {} + | ----- this pattern doesn't introduce a new catch-all binding, but rather pattern matches against the value of constant `CONST` | = note: the matched value is of type `&[bool]` +note: constant `CONST` defined here + --> $DIR/slice-patterns-exhaustiveness.rs:88:5 + | +LL | const CONST: &[bool] = &[true]; + | ^^^^^^^^^^^^^^^^^^^^ +help: if you meant to introduce a binding, use a different name + | +LL | CONST_var => {} + | ++++ help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ &[_, _, ..] => {}, @@ -264,8 +324,20 @@ error[E0004]: non-exhaustive patterns: `&[false]` not covered | LL | match s1 { | ^^ pattern `&[false]` not covered +LL | +LL | CONST1 => {} + | ------ this pattern doesn't introduce a new catch-all binding, but rather pattern matches against the value of constant `CONST1` | = note: the matched value is of type `&[bool; 1]` +note: constant `CONST1` defined here + --> $DIR/slice-patterns-exhaustiveness.rs:124:5 + | +LL | const CONST1: &[bool; 1] = &[true]; + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: if you meant to introduce a binding, use a different name + | +LL | CONST1_var => {} + | ++++ help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ CONST1 => {}, diff --git a/tests/ui/simd-abi-checks-s390x.rs b/tests/ui/simd-abi-checks-s390x.rs new file mode 100644 index 00000000000..15df66a2ced --- /dev/null +++ b/tests/ui/simd-abi-checks-s390x.rs @@ -0,0 +1,174 @@ +//@ revisions: z10 z13_no_vector z13_soft_float +//@ build-fail +//@[z10] compile-flags: --target s390x-unknown-linux-gnu +//@[z10] needs-llvm-components: systemz +//@[z13_no_vector] compile-flags: --target s390x-unknown-linux-gnu -C target-cpu=z13 -C target-feature=-vector +//@[z13_no_vector] needs-llvm-components: systemz +// FIXME: +soft-float itself doesn't set -vector +//@[z13_soft_float] compile-flags: --target s390x-unknown-linux-gnu -C target-cpu=z13 -C target-feature=-vector,+soft-float +//@[z13_soft_float] needs-llvm-components: systemz + +#![feature(no_core, lang_items, repr_simd, s390x_target_feature)] +#![no_core] +#![crate_type = "lib"] +#![allow(non_camel_case_types, improper_ctypes_definitions)] +#![deny(abi_unsupported_vector_types)] + +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} +#[lang = "freeze"] +pub trait Freeze {} + +impl<T: Copy, const N: usize> Copy for [T; N] {} + +#[repr(simd)] +pub struct i8x8([i8; 8]); +#[repr(simd)] +pub struct i8x16([i8; 16]); +#[repr(simd)] +pub struct i8x32([i8; 32]); +#[repr(C)] +pub struct Wrapper<T>(T); +#[repr(transparent)] +pub struct TransparentWrapper<T>(T); + +impl Copy for i8 {} +impl Copy for i64 {} +impl Copy for i8x8 {} +impl Copy for i8x16 {} +impl Copy for i8x32 {} +impl<T: Copy> Copy for Wrapper<T> {} +impl<T: Copy> Copy for TransparentWrapper<T> {} + +#[no_mangle] +extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { + //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + //~^^ WARN this was previously accepted + *x +} +#[no_mangle] +extern "C" fn vector_ret(x: &i8x16) -> i8x16 { + //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + //~^^ WARN this was previously accepted + *x +} +#[no_mangle] +extern "C" fn vector_ret_large(x: &i8x32) -> i8x32 { + // Ok + *x +} + +#[no_mangle] +#[target_feature(enable = "vector")] +unsafe extern "C" fn vector_ret_target_feature_small(x: &i8x8) -> i8x8 { + // Ok + *x +} +#[no_mangle] +#[target_feature(enable = "vector")] +unsafe extern "C" fn vector_target_feature_ret(x: &i8x16) -> i8x16 { + // Ok + *x +} +#[no_mangle] +#[target_feature(enable = "vector")] +unsafe extern "C" fn vector_ret_target_feature_large(x: &i8x32) -> i8x32 { + // Ok + *x +} + +#[no_mangle] +extern "C" fn vector_wrapper_ret_small(x: &Wrapper<i8x8>) -> Wrapper<i8x8> { + // Ok + *x +} +#[no_mangle] +extern "C" fn vector_wrapper_ret(x: &Wrapper<i8x16>) -> Wrapper<i8x16> { + // Ok + *x +} +#[no_mangle] +extern "C" fn vector_wrapper_ret_large(x: &Wrapper<i8x32>) -> Wrapper<i8x32> { + // Ok + *x +} + +#[no_mangle] +extern "C" fn vector_transparent_wrapper_ret_small( + x: &TransparentWrapper<i8x8>, +) -> TransparentWrapper<i8x8> { + //~^^^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + //~^^^^ WARN this was previously accepted + *x +} +#[no_mangle] +extern "C" fn vector_transparent_wrapper_ret( + x: &TransparentWrapper<i8x16>, +) -> TransparentWrapper<i8x16> { + //~^^^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + //~^^^^ WARN this was previously accepted + *x +} +#[no_mangle] +extern "C" fn vector_transparent_wrapper_ret_large( + x: &TransparentWrapper<i8x32>, +) -> TransparentWrapper<i8x32> { + // Ok + *x +} + +#[no_mangle] +extern "C" fn vector_arg_small(x: i8x8) -> i64 { + //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + //~^^ WARN this was previously accepted + unsafe { *(&x as *const i8x8 as *const i64) } +} +#[no_mangle] +extern "C" fn vector_arg(x: i8x16) -> i64 { + //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + //~^^ WARN this was previously accepted + unsafe { *(&x as *const i8x16 as *const i64) } +} +#[no_mangle] +extern "C" fn vector_arg_large(x: i8x32) -> i64 { + // Ok + unsafe { *(&x as *const i8x32 as *const i64) } +} + +#[no_mangle] +extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 { + //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + //~^^ WARN this was previously accepted + unsafe { *(&x as *const Wrapper<i8x8> as *const i64) } +} +#[no_mangle] +extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 { + //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + //~^^ WARN this was previously accepted + unsafe { *(&x as *const Wrapper<i8x16> as *const i64) } +} +#[no_mangle] +extern "C" fn vector_wrapper_arg_large(x: Wrapper<i8x32>) -> i64 { + // Ok + unsafe { *(&x as *const Wrapper<i8x32> as *const i64) } +} + +#[no_mangle] +extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8x8>) -> i64 { + //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + //~^^ WARN this was previously accepted + unsafe { *(&x as *const TransparentWrapper<i8x8> as *const i64) } +} +#[no_mangle] +extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper<i8x16>) -> i64 { + //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + //~^^ WARN this was previously accepted + unsafe { *(&x as *const TransparentWrapper<i8x16> as *const i64) } +} +#[no_mangle] +extern "C" fn vector_transparent_wrapper_arg_large(x: TransparentWrapper<i8x32>) -> i64 { + // Ok + unsafe { *(&x as *const TransparentWrapper<i8x32> as *const i64) } +} diff --git a/tests/ui/simd-abi-checks-s390x.z10.stderr b/tests/ui/simd-abi-checks-s390x.z10.stderr new file mode 100644 index 00000000000..a91322ec058 --- /dev/null +++ b/tests/ui/simd-abi-checks-s390x.z10.stderr @@ -0,0 +1,111 @@ +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:46:1 + | +LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) +note: the lint level is defined here + --> $DIR/simd-abi-checks-s390x.rs:15:9 + | +LL | #![deny(abi_unsupported_vector_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:52:1 + | +LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:99:1 + | +LL | / extern "C" fn vector_transparent_wrapper_ret_small( +LL | | x: &TransparentWrapper<i8x8>, +LL | | ) -> TransparentWrapper<i8x8> { + | |_____________________________^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:107:1 + | +LL | / extern "C" fn vector_transparent_wrapper_ret( +LL | | x: &TransparentWrapper<i8x16>, +LL | | ) -> TransparentWrapper<i8x16> { + | |______________________________^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:123:1 + | +LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:129:1 + | +LL | extern "C" fn vector_arg(x: i8x16) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:141:1 + | +LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:147:1 + | +LL | extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:159:1 + | +LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8x8>) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:165:1 + | +LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper<i8x16>) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: aborting due to 10 previous errors + diff --git a/tests/ui/simd-abi-checks-s390x.z13_no_vector.stderr b/tests/ui/simd-abi-checks-s390x.z13_no_vector.stderr new file mode 100644 index 00000000000..a91322ec058 --- /dev/null +++ b/tests/ui/simd-abi-checks-s390x.z13_no_vector.stderr @@ -0,0 +1,111 @@ +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:46:1 + | +LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) +note: the lint level is defined here + --> $DIR/simd-abi-checks-s390x.rs:15:9 + | +LL | #![deny(abi_unsupported_vector_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:52:1 + | +LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:99:1 + | +LL | / extern "C" fn vector_transparent_wrapper_ret_small( +LL | | x: &TransparentWrapper<i8x8>, +LL | | ) -> TransparentWrapper<i8x8> { + | |_____________________________^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:107:1 + | +LL | / extern "C" fn vector_transparent_wrapper_ret( +LL | | x: &TransparentWrapper<i8x16>, +LL | | ) -> TransparentWrapper<i8x16> { + | |______________________________^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:123:1 + | +LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:129:1 + | +LL | extern "C" fn vector_arg(x: i8x16) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:141:1 + | +LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:147:1 + | +LL | extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:159:1 + | +LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8x8>) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:165:1 + | +LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper<i8x16>) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: aborting due to 10 previous errors + diff --git a/tests/ui/simd-abi-checks-s390x.z13_soft_float.stderr b/tests/ui/simd-abi-checks-s390x.z13_soft_float.stderr new file mode 100644 index 00000000000..a91322ec058 --- /dev/null +++ b/tests/ui/simd-abi-checks-s390x.z13_soft_float.stderr @@ -0,0 +1,111 @@ +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:46:1 + | +LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) +note: the lint level is defined here + --> $DIR/simd-abi-checks-s390x.rs:15:9 + | +LL | #![deny(abi_unsupported_vector_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:52:1 + | +LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:99:1 + | +LL | / extern "C" fn vector_transparent_wrapper_ret_small( +LL | | x: &TransparentWrapper<i8x8>, +LL | | ) -> TransparentWrapper<i8x8> { + | |_____________________________^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:107:1 + | +LL | / extern "C" fn vector_transparent_wrapper_ret( +LL | | x: &TransparentWrapper<i8x16>, +LL | | ) -> TransparentWrapper<i8x16> { + | |______________________________^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:123:1 + | +LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:129:1 + | +LL | extern "C" fn vector_arg(x: i8x16) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:141:1 + | +LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:147:1 + | +LL | extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:159:1 + | +LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8x8>) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled + --> $DIR/simd-abi-checks-s390x.rs:165:1 + | +LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper<i8x16>) -> i64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558> + = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) + +error: aborting due to 10 previous errors + diff --git a/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr b/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr index 05e087fd9f9..a040e71cf3b 100644 --- a/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr +++ b/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr @@ -31,6 +31,10 @@ note: required by a bound in `bar` | LL | fn bar(f: impl Future<Output=()>) {} | ^^^^^^^^^^^^^^^^^ required by this bound in `bar` +help: use parentheses to call this closure + | +LL | bar(async_closure()); + | ++ error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs index af47ba8baa3..1a440a90cd7 100644 --- a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs +++ b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs @@ -2,9 +2,9 @@ fn main() { let A = 3; //~^ ERROR refutable pattern in local binding //~| patterns `i32::MIN..=1_i32` and `3_i32..=i32::MAX` not covered - //~| missing patterns are not covered because `A` is interpreted as a constant pattern, not a new variable //~| HELP introduce a variable instead //~| SUGGESTION A_var const A: i32 = 2; + //~^ missing patterns are not covered because `A` is interpreted as a constant pattern, not a new variable } diff --git a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr index b6c28612802..a275d8e4e83 100644 --- a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr +++ b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr @@ -2,15 +2,18 @@ error[E0005]: refutable pattern in local binding --> $DIR/const-pat-non-exaustive-let-new-var.rs:2:9 | LL | let A = 3; - | ^ - | | - | patterns `i32::MIN..=1_i32` and `3_i32..=i32::MAX` not covered - | missing patterns are not covered because `A` is interpreted as a constant pattern, not a new variable - | help: introduce a variable instead: `A_var` + | ^ patterns `i32::MIN..=1_i32` and `3_i32..=i32::MAX` not covered +... +LL | const A: i32 = 2; + | ------------ missing patterns are not covered because `A` is interpreted as a constant pattern, not a new variable | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` +help: introduce a variable instead + | +LL | let A_var = 3; + | ~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/thir-print/thir-flat-const-variant.stdout b/tests/ui/thir-print/thir-flat-const-variant.stdout index 1840be7885b..5588cfdfa5c 100644 --- a/tests/ui/thir-print/thir-flat-const-variant.stdout +++ b/tests/ui/thir-print/thir-flat-const-variant.stdout @@ -11,9 +11,12 @@ Thir { fields: [], }, ty: (), - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:12:32: 12:34 (#0), }, Expr { @@ -25,9 +28,12 @@ Thir { value: e0, }, ty: (), - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:12:32: 12:34 (#0), }, Expr { @@ -47,9 +53,12 @@ Thir { }, ), ty: Foo, - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:12:23: 12:35 (#0), }, Expr { @@ -61,9 +70,12 @@ Thir { value: e2, }, ty: Foo, - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:12:23: 12:35 (#0), }, ], @@ -84,9 +96,12 @@ Thir { fields: [], }, ty: (), - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:13:33: 13:35 (#0), }, Expr { @@ -98,9 +113,12 @@ Thir { value: e0, }, ty: (), - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:13:33: 13:35 (#0), }, Expr { @@ -120,9 +138,12 @@ Thir { }, ), ty: Foo, - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:13:23: 13:36 (#0), }, Expr { @@ -134,9 +155,12 @@ Thir { value: e2, }, ty: Foo, - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:13:23: 13:36 (#0), }, ], @@ -157,9 +181,12 @@ Thir { fields: [], }, ty: (), - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:14:33: 14:35 (#0), }, Expr { @@ -171,9 +198,12 @@ Thir { value: e0, }, ty: (), - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:14:33: 14:35 (#0), }, Expr { @@ -193,9 +223,12 @@ Thir { }, ), ty: Foo, - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:14:24: 14:36 (#0), }, Expr { @@ -207,9 +240,12 @@ Thir { value: e2, }, ty: Foo, - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:14:24: 14:36 (#0), }, ], @@ -230,9 +266,12 @@ Thir { fields: [], }, ty: (), - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:15:34: 15:36 (#0), }, Expr { @@ -244,9 +283,12 @@ Thir { value: e0, }, ty: (), - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:15:34: 15:36 (#0), }, Expr { @@ -266,9 +308,12 @@ Thir { }, ), ty: Foo, - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:15:24: 15:37 (#0), }, Expr { @@ -280,9 +325,12 @@ Thir { value: e2, }, ty: Foo, - temp_lifetime: Some( - Node(3), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(3), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:15:24: 15:37 (#0), }, ], @@ -312,9 +360,12 @@ Thir { block: b0, }, ty: (), - temp_lifetime: Some( - Node(2), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(2), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:18:11: 18:13 (#0), }, Expr { @@ -326,9 +377,12 @@ Thir { value: e0, }, ty: (), - temp_lifetime: Some( - Node(2), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(2), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat-const-variant.rs:18:11: 18:13 (#0), }, ], diff --git a/tests/ui/thir-print/thir-flat.stdout b/tests/ui/thir-print/thir-flat.stdout index a31d08adab6..59cecfe511c 100644 --- a/tests/ui/thir-print/thir-flat.stdout +++ b/tests/ui/thir-print/thir-flat.stdout @@ -20,9 +20,12 @@ Thir { block: b0, }, ty: (), - temp_lifetime: Some( - Node(2), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(2), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat.rs:4:15: 4:17 (#0), }, Expr { @@ -34,9 +37,12 @@ Thir { value: e0, }, ty: (), - temp_lifetime: Some( - Node(2), - ), + temp_lifetime: TempLifetime { + temp_lifetime: Some( + Node(2), + ), + backwards_incompatible: None, + }, span: $DIR/thir-flat.rs:4:15: 4:17 (#0), }, ], diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout index 8cff7887661..a9d6985928a 100644 --- a/tests/ui/thir-print/thir-tree-match.stdout +++ b/tests/ui/thir-print/thir-tree-match.stdout @@ -26,7 +26,7 @@ params: [ body: Expr { ty: bool - temp_lifetime: Some(Node(26)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:15:32: 21:2 (#0) kind: Scope { @@ -35,7 +35,7 @@ body: value: Expr { ty: bool - temp_lifetime: Some(Node(26)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:15:32: 21:2 (#0) kind: Block { @@ -47,7 +47,7 @@ body: expr: Expr { ty: bool - temp_lifetime: Some(Node(26)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:5: 20:6 (#0) kind: Scope { @@ -56,14 +56,14 @@ body: value: Expr { ty: bool - temp_lifetime: Some(Node(26)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:5: 20:6 (#0) kind: Match { scrutinee: Expr { ty: Foo - temp_lifetime: Some(Node(26)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0) kind: Scope { @@ -72,7 +72,7 @@ body: value: Expr { ty: Foo - temp_lifetime: Some(Node(26)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0) kind: VarRef { @@ -123,7 +123,7 @@ body: body: Expr { ty: bool - temp_lifetime: Some(Node(13)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(13)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Scope { @@ -132,7 +132,7 @@ body: value: Expr { ty: bool - temp_lifetime: Some(Node(13)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(13)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) }, neg: false) @@ -175,7 +175,7 @@ body: body: Expr { ty: bool - temp_lifetime: Some(Node(19)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(19)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Scope { @@ -184,7 +184,7 @@ body: value: Expr { ty: bool - temp_lifetime: Some(Node(19)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(19)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Literal( lit: Spanned { node: Bool(false), span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) }, neg: false) @@ -219,7 +219,7 @@ body: body: Expr { ty: bool - temp_lifetime: Some(Node(24)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Scope { @@ -228,7 +228,7 @@ body: value: Expr { ty: bool - temp_lifetime: Some(Node(24)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) }, neg: false) @@ -257,7 +257,7 @@ params: [ body: Expr { ty: () - temp_lifetime: Some(Node(2)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(2)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:23:11: 23:13 (#0) kind: Scope { @@ -266,7 +266,7 @@ body: value: Expr { ty: () - temp_lifetime: Some(Node(2)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(2)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:23:11: 23:13 (#0) kind: Block { diff --git a/tests/ui/thir-print/thir-tree.stdout b/tests/ui/thir-print/thir-tree.stdout index ef6db368dbe..b39581ad841 100644 --- a/tests/ui/thir-print/thir-tree.stdout +++ b/tests/ui/thir-print/thir-tree.stdout @@ -4,7 +4,7 @@ params: [ body: Expr { ty: () - temp_lifetime: Some(Node(2)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(2)), backwards_incompatible: None } span: $DIR/thir-tree.rs:4:15: 4:17 (#0) kind: Scope { @@ -13,7 +13,7 @@ body: value: Expr { ty: () - temp_lifetime: Some(Node(2)) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(2)), backwards_incompatible: None } span: $DIR/thir-tree.rs:4:15: 4:17 (#0) kind: Block { diff --git a/tests/ui/trait-bounds/maybe-bound-has-path-args.rs b/tests/ui/trait-bounds/maybe-bound-has-path-args.rs index 2cb63f25d06..fd0e9691700 100644 --- a/tests/ui/trait-bounds/maybe-bound-has-path-args.rs +++ b/tests/ui/trait-bounds/maybe-bound-has-path-args.rs @@ -1,7 +1,7 @@ trait Trait {} fn test<T: ?self::<i32>::Trait>() {} -//~^ ERROR type arguments are not allowed on this type +//~^ ERROR type arguments are not allowed on module `maybe_bound_has_path_args` //~| WARN relaxing a default bound only does something for `?Sized` fn main() {} diff --git a/tests/ui/trait-bounds/maybe-bound-has-path-args.stderr b/tests/ui/trait-bounds/maybe-bound-has-path-args.stderr index 701e493f5a5..0c167fff940 100644 --- a/tests/ui/trait-bounds/maybe-bound-has-path-args.stderr +++ b/tests/ui/trait-bounds/maybe-bound-has-path-args.stderr @@ -4,13 +4,13 @@ warning: relaxing a default bound only does something for `?Sized`; all other tr LL | fn test<T: ?self::<i32>::Trait>() {} | ^^^^^^^^^^^^^^^^^^^ -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on module `maybe_bound_has_path_args` --> $DIR/maybe-bound-has-path-args.rs:3:20 | LL | fn test<T: ?self::<i32>::Trait>() {} | ---- ^^^ type argument not allowed | | - | not allowed on this type + | not allowed on module `maybe_bound_has_path_args` error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/traits/const-traits/const-closure-parse-not-item.stderr b/tests/ui/traits/const-traits/const-closure-parse-not-item.stderr index 25c81ff900f..0970cd5225f 100644 --- a/tests/ui/traits/const-traits/const-closure-parse-not-item.stderr +++ b/tests/ui/traits/const-traits/const-closure-parse-not-item.stderr @@ -12,5 +12,13 @@ LL | const fn test() -> impl ~const Fn() { | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 2 previous errors +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-closure-parse-not-item.rs:7:25 + | +LL | const fn test() -> impl ~const Fn() { + | ^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 3 previous errors diff --git a/tests/ui/traits/const-traits/const-opaque.no.stderr b/tests/ui/traits/const-traits/const-opaque.no.stderr new file mode 100644 index 00000000000..e43a6b603fd --- /dev/null +++ b/tests/ui/traits/const-traits/const-opaque.no.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `(): const Foo` is not satisfied + --> $DIR/const-opaque.rs:31:18 + | +LL | let opaque = bar(()); + | ^^^^^^^ + +error[E0277]: the trait bound `(): const Foo` is not satisfied + --> $DIR/const-opaque.rs:33:5 + | +LL | opaque.method(); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-opaque.rs b/tests/ui/traits/const-traits/const-opaque.rs new file mode 100644 index 00000000000..96cdd7d9f26 --- /dev/null +++ b/tests/ui/traits/const-traits/const-opaque.rs @@ -0,0 +1,38 @@ +//@ revisions: yes no +//@ compile-flags: -Znext-solver +//@[yes] check-pass + +#![feature(const_trait_impl)] + +#[const_trait] +trait Foo { + fn method(&self); +} + +impl<T: ~const Foo> const Foo for (T,) { + fn method(&self) {} +} + +#[cfg(yes)] +impl const Foo for () { + fn method(&self) {} +} + +#[cfg(no)] +impl Foo for () { + fn method(&self) {} +} + +const fn bar<T: ~const Foo>(t: T) -> impl ~const Foo { + (t,) +} + +const _: () = { + let opaque = bar(()); + //[no]~^ ERROR the trait bound `(): const Foo` is not satisfied + opaque.method(); + //[no]~^ ERROR the trait bound `(): const Foo` is not satisfied + std::mem::forget(opaque); +}; + +fn main() {} diff --git a/tests/ui/traits/const-traits/effects/ice-112822-expected-type-for-param.rs b/tests/ui/traits/const-traits/effects/ice-112822-expected-type-for-param.rs index c467088ab3d..8ff15dd09cc 100644 --- a/tests/ui/traits/const-traits/effects/ice-112822-expected-type-for-param.rs +++ b/tests/ui/traits/const-traits/effects/ice-112822-expected-type-for-param.rs @@ -3,6 +3,7 @@ const fn test() -> impl ~const Fn() { //~^ ERROR `~const` can only be applied to `#[const_trait]` traits //~| ERROR `~const` can only be applied to `#[const_trait]` traits + //~| ERROR `~const` can only be applied to `#[const_trait]` traits const move || { //~ ERROR const closures are experimental let sl: &[u8] = b"foo"; diff --git a/tests/ui/traits/const-traits/effects/ice-112822-expected-type-for-param.stderr b/tests/ui/traits/const-traits/effects/ice-112822-expected-type-for-param.stderr index 6d7edaf19f2..879d966b1f9 100644 --- a/tests/ui/traits/const-traits/effects/ice-112822-expected-type-for-param.stderr +++ b/tests/ui/traits/const-traits/effects/ice-112822-expected-type-for-param.stderr @@ -1,5 +1,5 @@ error[E0658]: const closures are experimental - --> $DIR/ice-112822-expected-type-for-param.rs:6:5 + --> $DIR/ice-112822-expected-type-for-param.rs:7:5 | LL | const move || { | ^^^^^ @@ -22,8 +22,16 @@ LL | const fn test() -> impl ~const Fn() { | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/ice-112822-expected-type-for-param.rs:3:25 + | +LL | const fn test() -> impl ~const Fn() { + | ^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0015]: cannot call non-const operator in constant functions - --> $DIR/ice-112822-expected-type-for-param.rs:11:17 + --> $DIR/ice-112822-expected-type-for-param.rs:12:17 | LL | assert_eq!(first, &b'f'); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +40,7 @@ LL | assert_eq!(first, &b'f'); = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const fn `core::panicking::assert_failed::<&u8, &u8>` in constant functions - --> $DIR/ice-112822-expected-type-for-param.rs:11:17 + --> $DIR/ice-112822-expected-type-for-param.rs:12:17 | LL | assert_eq!(first, &b'f'); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +48,7 @@ LL | assert_eq!(first, &b'f'); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors Some errors have detailed explanations: E0015, E0658. For more information about an error, try `rustc --explain E0015`. diff --git a/triagebot.toml b/triagebot.toml index 2483bfc4a41..b7a9b794c8b 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -451,7 +451,6 @@ new_issue = true exclude_labels = [ "C-tracking-issue", "A-diagnostics", - "relnotes-tracking-issue", ] [autolabel."WG-trait-system-refactor"] |
