diff options
Diffstat (limited to 'compiler')
467 files changed, 9059 insertions, 8333 deletions
diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index f85c30ac7ec..d24b630516a 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -20,14 +20,14 @@ rustc_smir = { path = "../rustc_smir" } stable_mir = { path = "../stable_mir" } # tidy-alphabetical-end -[dependencies.jemalloc-sys] -version = "0.5.0" +[dependencies.tikv-jemalloc-sys] +version = "0.6.0" optional = true features = ['unprefixed_malloc_on_supported_platforms'] [features] # tidy-alphabetical-start -jemalloc = ['dep:jemalloc-sys'] +jemalloc = ['dep:tikv-jemalloc-sys'] llvm = ['rustc_driver_impl/llvm'] max_level_info = ['rustc_driver_impl/max_level_info'] rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts'] diff --git a/compiler/rustc/src/main.rs b/compiler/rustc/src/main.rs index ccf88d8ff4b..a55a63a7bf1 100644 --- a/compiler/rustc/src/main.rs +++ b/compiler/rustc/src/main.rs @@ -43,6 +43,8 @@ fn main() { { use std::os::raw::{c_int, c_void}; + use tikv_jemalloc_sys as jemalloc_sys; + #[used] static _F1: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::calloc; #[used] diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 2e51753ede6..15a27c0b6ee 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1215,6 +1215,15 @@ impl Scalar { Scalar::Union { .. } => true, } } + + /// Returns `true` if this is a signed integer scalar + #[inline] + pub fn is_signed(&self) -> bool { + match self.primitive() { + Primitive::Int(_, signed) => signed, + _ => false, + } + } } // NOTE: This struct is generic over the FieldIdx for rust-analyzer usage. @@ -1401,10 +1410,7 @@ impl BackendRepr { #[inline] pub fn is_signed(&self) -> bool { match self { - BackendRepr::Scalar(scal) => match scal.primitive() { - Primitive::Int(_, signed) => signed, - _ => false, - }, + BackendRepr::Scalar(scal) => scal.is_signed(), _ => panic!("`is_signed` on non-scalar ABI {self:?}"), } } @@ -1499,7 +1505,11 @@ impl BackendRepr { #[cfg_attr(feature = "nightly", derive(HashStable_Generic))] pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> { /// Single enum variants, structs/tuples, unions, and all non-ADTs. - Single { index: VariantIdx }, + Single { + /// Always 0 for non-enums/generators. + /// For enums without a variant, this is an invalid index! + index: VariantIdx, + }, /// Enum-likes with more than one variant: each variant comes with /// a *discriminant* (usually the same as the variant index but the user can @@ -1528,14 +1538,22 @@ pub enum TagEncoding<VariantIdx: Idx> { /// The variant `untagged_variant` contains a niche at an arbitrary /// offset (field `tag_field` of the enum), which for a variant with /// discriminant `d` is set to - /// `(d - niche_variants.start).wrapping_add(niche_start)`. + /// `(d - niche_variants.start).wrapping_add(niche_start)` + /// (this is wrapping arithmetic using the type of the niche field). /// /// For example, `Option<(usize, &T)>` is represented such that /// `None` has a null pointer for the second tuple field, and /// `Some` is the identity function (with a non-null reference). + /// + /// Other variants that are not `untagged_variant` and that are outside the `niche_variants` + /// range cannot be represented; they must be uninhabited. Niche { untagged_variant: VariantIdx, + /// This range *may* contain `untagged_variant`; that is then just a "dead value" and + /// not used to encode anything. niche_variants: RangeInclusive<VariantIdx>, + /// This is inbounds of the type of the niche field + /// (not sign-extended, i.e., all bits beyond the niche field size are 0). niche_start: u128, }, } diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 792de77e9d4..650525a2f52 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -39,7 +39,7 @@ pub use crate::format::*; use crate::ptr::P; use crate::token::{self, CommentKind, Delimiter}; use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream}; -pub use crate::util::parser::ExprPrecedence; +use crate::util::parser::{AssocOp, ExprPrecedence}; /// A "Label" is an identifier of some point in sources, /// e.g. in the following code: @@ -428,7 +428,15 @@ impl Default for WhereClause { /// A single predicate in a where-clause. #[derive(Clone, Encodable, Decodable, Debug)] -pub enum WherePredicate { +pub struct WherePredicate { + pub kind: WherePredicateKind, + pub id: NodeId, + pub span: Span, +} + +/// Predicate kind in where-clause. +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum WherePredicateKind { /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). BoundPredicate(WhereBoundPredicate), /// A lifetime predicate (e.g., `'a: 'b + 'c`). @@ -437,22 +445,11 @@ pub enum WherePredicate { EqPredicate(WhereEqPredicate), } -impl WherePredicate { - pub fn span(&self) -> Span { - match self { - WherePredicate::BoundPredicate(p) => p.span, - WherePredicate::RegionPredicate(p) => p.span, - WherePredicate::EqPredicate(p) => p.span, - } - } -} - /// A type bound. /// /// E.g., `for<'c> Foo: Send + Clone + 'c`. #[derive(Clone, Encodable, Decodable, Debug)] pub struct WhereBoundPredicate { - pub span: Span, /// Any generics from a `for` binding. pub bound_generic_params: ThinVec<GenericParam>, /// The type being bounded. @@ -466,7 +463,6 @@ pub struct WhereBoundPredicate { /// E.g., `'a: 'b + 'c`. #[derive(Clone, Encodable, Decodable, Debug)] pub struct WhereRegionPredicate { - pub span: Span, pub lifetime: Lifetime, pub bounds: GenericBounds, } @@ -476,7 +472,6 @@ pub struct WhereRegionPredicate { /// E.g., `T = int`. #[derive(Clone, Encodable, Decodable, Debug)] pub struct WhereEqPredicate { - pub span: Span, pub lhs_ty: P<Ty>, pub rhs_ty: P<Ty>, } @@ -632,9 +627,11 @@ impl Pat { | PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)), // Trivial wrappers over inner patterns. - PatKind::Box(s) | PatKind::Deref(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => { - s.walk(it) - } + PatKind::Box(s) + | PatKind::Deref(s) + | PatKind::Ref(s, _) + | PatKind::Paren(s) + | PatKind::Guard(s, _) => s.walk(it), // These patterns do not contain subpatterns, skip. PatKind::Wild @@ -844,6 +841,9 @@ pub enum PatKind { // A never pattern `!`. Never, + /// A guard pattern (e.g., `x if guard(x)`). + Guard(P<Pat>, P<Expr>), + /// Parentheses in patterns used for grouping (i.e., `(PAT)`). Paren(P<Pat>), @@ -1187,14 +1187,15 @@ pub struct Expr { } impl Expr { - /// Is this expr either `N`, or `{ N }`. + /// Could this expr be either `N`, or `{ N }`, where `N` is a const parameter. /// /// If this is not the case, name resolution does not resolve `N` when using /// `min_const_generics` as more complex expressions are not supported. /// /// Does not ensure that the path resolves to a const param, the caller should check this. - pub fn is_potential_trivial_const_arg(&self, strip_identity_block: bool) -> bool { - let this = if strip_identity_block { self.maybe_unwrap_block() } else { self }; + /// This also does not consider macros, so it's only correct after macro-expansion. + pub fn is_potential_trivial_const_arg(&self) -> bool { + let this = self.maybe_unwrap_block(); if let ExprKind::Path(None, path) = &this.kind && path.is_potential_trivial_const_arg() @@ -1321,51 +1322,69 @@ impl Expr { pub fn precedence(&self) -> ExprPrecedence { match self.kind { - ExprKind::Array(_) => ExprPrecedence::Array, - ExprKind::ConstBlock(_) => ExprPrecedence::ConstBlock, - ExprKind::Call(..) => ExprPrecedence::Call, - ExprKind::MethodCall(..) => ExprPrecedence::MethodCall, - ExprKind::Tup(_) => ExprPrecedence::Tup, - ExprKind::Binary(op, ..) => ExprPrecedence::Binary(op.node), - ExprKind::Unary(..) => ExprPrecedence::Unary, - ExprKind::Lit(_) | ExprKind::IncludedBytes(..) => ExprPrecedence::Lit, - ExprKind::Cast(..) => ExprPrecedence::Cast, - ExprKind::Let(..) => ExprPrecedence::Let, - ExprKind::If(..) => ExprPrecedence::If, - ExprKind::While(..) => ExprPrecedence::While, - ExprKind::ForLoop { .. } => ExprPrecedence::ForLoop, - ExprKind::Loop(..) => ExprPrecedence::Loop, - ExprKind::Match(_, _, MatchKind::Prefix) => ExprPrecedence::Match, - ExprKind::Match(_, _, MatchKind::Postfix) => ExprPrecedence::PostfixMatch, ExprKind::Closure(..) => ExprPrecedence::Closure, - ExprKind::Block(..) => ExprPrecedence::Block, - ExprKind::TryBlock(..) => ExprPrecedence::TryBlock, - ExprKind::Gen(..) => ExprPrecedence::Gen, - ExprKind::Await(..) => ExprPrecedence::Await, - ExprKind::Assign(..) => ExprPrecedence::Assign, - ExprKind::AssignOp(..) => ExprPrecedence::AssignOp, - ExprKind::Field(..) => ExprPrecedence::Field, - ExprKind::Index(..) => ExprPrecedence::Index, + + ExprKind::Break(..) + | ExprKind::Continue(..) + | ExprKind::Ret(..) + | ExprKind::Yield(..) + | ExprKind::Yeet(..) + | ExprKind::Become(..) => ExprPrecedence::Jump, + + // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to + // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence + // ensures that `pprust` will add parentheses in the right places to get the desired + // parse. ExprKind::Range(..) => ExprPrecedence::Range, - ExprKind::Underscore => ExprPrecedence::Path, - ExprKind::Path(..) => ExprPrecedence::Path, - ExprKind::AddrOf(..) => ExprPrecedence::AddrOf, - ExprKind::Break(..) => ExprPrecedence::Break, - ExprKind::Continue(..) => ExprPrecedence::Continue, - ExprKind::Ret(..) => ExprPrecedence::Ret, - ExprKind::Struct(..) => ExprPrecedence::Struct, - ExprKind::Repeat(..) => ExprPrecedence::Repeat, - ExprKind::Paren(..) => ExprPrecedence::Paren, - ExprKind::Try(..) => ExprPrecedence::Try, - ExprKind::Yield(..) => ExprPrecedence::Yield, - ExprKind::Yeet(..) => ExprPrecedence::Yeet, - ExprKind::Become(..) => ExprPrecedence::Become, - ExprKind::InlineAsm(..) - | ExprKind::Type(..) - | ExprKind::OffsetOf(..) + + // Binop-like expr kinds, handled by `AssocOp`. + ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(), + ExprKind::Cast(..) => ExprPrecedence::Cast, + + ExprKind::Assign(..) | + ExprKind::AssignOp(..) => ExprPrecedence::Assign, + + // Unary, prefix + ExprKind::AddrOf(..) + // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`. + // However, this is not exactly right. When `let _ = a` is the LHS of a binop we + // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` + // but we need to print `(let _ = a) < b` as-is with parens. + | ExprKind::Let(..) + | ExprKind::Unary(..) => ExprPrecedence::Prefix, + + // Never need parens + ExprKind::Array(_) + | ExprKind::Await(..) + | ExprKind::Block(..) + | ExprKind::Call(..) + | ExprKind::ConstBlock(_) + | ExprKind::Field(..) + | ExprKind::ForLoop { .. } | ExprKind::FormatArgs(..) - | ExprKind::MacCall(..) => ExprPrecedence::Mac, - ExprKind::Err(_) | ExprKind::Dummy => ExprPrecedence::Err, + | ExprKind::Gen(..) + | ExprKind::If(..) + | ExprKind::IncludedBytes(..) + | ExprKind::Index(..) + | ExprKind::InlineAsm(..) + | ExprKind::Lit(_) + | ExprKind::Loop(..) + | ExprKind::MacCall(..) + | ExprKind::Match(..) + | ExprKind::MethodCall(..) + | ExprKind::OffsetOf(..) + | ExprKind::Paren(..) + | ExprKind::Path(..) + | ExprKind::Repeat(..) + | ExprKind::Struct(..) + | ExprKind::Try(..) + | ExprKind::TryBlock(..) + | ExprKind::Tup(_) + | ExprKind::Type(..) + | ExprKind::Underscore + | ExprKind::While(..) + | ExprKind::Err(_) + | ExprKind::Dummy => ExprPrecedence::Unambiguous, } } @@ -1717,12 +1736,12 @@ pub enum AttrArgs { /// Delimited arguments: `#[attr()/[]/{}]`. Delimited(DelimArgs), /// Arguments of a key-value attribute: `#[attr = "value"]`. - Eq( + Eq { /// Span of the `=` token. - Span, - /// The "value". - AttrArgsEq, - ), + eq_span: Span, + + value: AttrArgsEq, + }, } // The RHS of an `AttrArgs::Eq` starts out as an expression. Once macro @@ -1734,15 +1753,39 @@ pub enum AttrArgsEq { Hir(MetaItemLit), } +impl AttrArgsEq { + pub fn span(&self) -> Span { + match self { + AttrArgsEq::Ast(p) => p.span, + AttrArgsEq::Hir(lit) => lit.span, + } + } + + pub fn unwrap_ast(&self) -> &Expr { + match self { + AttrArgsEq::Ast(p) => p, + AttrArgsEq::Hir(lit) => { + unreachable!("in literal form when getting inner tokens: {lit:?}") + } + } + } + + pub fn unwrap_ast_mut(&mut self) -> &mut P<Expr> { + match self { + AttrArgsEq::Ast(p) => p, + AttrArgsEq::Hir(lit) => { + unreachable!("in literal form when getting inner tokens: {lit:?}") + } + } + } +} + impl AttrArgs { pub fn span(&self) -> Option<Span> { match self { AttrArgs::Empty => None, AttrArgs::Delimited(args) => Some(args.dspan.entire()), - AttrArgs::Eq(eq_span, AttrArgsEq::Ast(expr)) => Some(eq_span.to(expr.span)), - AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => { - unreachable!("in literal form when getting span: {:?}", lit); - } + AttrArgs::Eq { eq_span, value } => Some(eq_span.to(value.span())), } } @@ -1752,10 +1795,7 @@ impl AttrArgs { match self { AttrArgs::Empty => TokenStream::default(), AttrArgs::Delimited(args) => args.tokens.clone(), - AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => TokenStream::from_ast(expr), - AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => { - unreachable!("in literal form when getting inner tokens: {:?}", lit) - } + AttrArgs::Eq { value, .. } => TokenStream::from_ast(value.unwrap_ast()), } } } @@ -1769,10 +1809,10 @@ where match self { AttrArgs::Empty => {} AttrArgs::Delimited(args) => args.hash_stable(ctx, hasher), - AttrArgs::Eq(_eq_span, AttrArgsEq::Ast(expr)) => { + AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => { unreachable!("hash_stable {:?}", expr); } - AttrArgs::Eq(eq_span, AttrArgsEq::Hir(lit)) => { + AttrArgs::Eq { eq_span, value: AttrArgsEq::Hir(lit) } => { eq_span.hash_stable(ctx, hasher); lit.hash_stable(ctx, hasher); } @@ -2531,6 +2571,18 @@ pub enum SelfKind { Explicit(P<Ty>, Mutability), } +impl SelfKind { + pub fn to_ref_suggestion(&self) -> String { + match self { + SelfKind::Region(None, mutbl) => mutbl.ref_prefix_str().to_string(), + SelfKind::Region(Some(lt), mutbl) => format!("&{lt} {}", mutbl.prefix_str()), + SelfKind::Value(_) | SelfKind::Explicit(_, _) => { + unreachable!("if we had an explicit self, we wouldn't be here") + } + } + } +} + pub type ExplicitSelf = Spanned<SelfKind>; impl Param { @@ -3067,6 +3119,7 @@ pub struct FieldDef { pub ident: Option<Ident>, pub ty: P<Ty>, + pub default: Option<AnonConst>, pub is_placeholder: bool, } diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 888b13efa31..0d79cadef34 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -250,7 +250,7 @@ impl AttrItem { AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => { MetaItemKind::list_from_tokens(args.tokens.clone()) } - AttrArgs::Delimited(_) | AttrArgs::Eq(..) | AttrArgs::Empty => None, + AttrArgs::Delimited(_) | AttrArgs::Eq { .. } | AttrArgs::Empty => None, } } @@ -268,7 +268,7 @@ impl AttrItem { /// ``` fn value_str(&self) -> Option<Symbol> { match &self.args { - AttrArgs::Eq(_, args) => args.value_str(), + AttrArgs::Eq { value, .. } => value.value_str(), AttrArgs::Delimited(_) | AttrArgs::Empty => None, } } @@ -492,7 +492,7 @@ impl MetaItemKind { MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List) } AttrArgs::Delimited(..) => None, - AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => match expr.kind { + AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => match expr.kind { ExprKind::Lit(token_lit) => { // Turn failures to `None`, we'll get parse errors elsewhere. MetaItemLit::from_token_lit(token_lit, expr.span) @@ -501,7 +501,9 @@ impl MetaItemKind { } _ => None, }, - AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())), + AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => { + Some(MetaItemKind::NameValue(lit.clone())) + } } } } @@ -702,7 +704,7 @@ pub fn mk_attr_name_value_str( tokens: None, }); let path = Path::from_ident(Ident::new(name, span)); - let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr)); + let args = AttrArgs::Eq { eq_span: span, value: AttrArgsEq::Ast(expr) }; mk_attr(g, style, unsafety, path, args, span) } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 198e1bca774..2c09059fe19 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -332,7 +332,11 @@ pub trait MutVisitor: Sized { } fn visit_where_predicate(&mut self, where_predicate: &mut WherePredicate) { - walk_where_predicate(self, where_predicate); + walk_where_predicate(self, where_predicate) + } + + fn visit_where_predicate_kind(&mut self, kind: &mut WherePredicateKind) { + walk_where_predicate_kind(self, kind) } fn visit_vis(&mut self, vis: &mut Visibility) { @@ -447,13 +451,10 @@ fn visit_attr_args<T: MutVisitor>(vis: &mut T, args: &mut AttrArgs) { match args { AttrArgs::Empty => {} AttrArgs::Delimited(args) => visit_delim_args(vis, args), - AttrArgs::Eq(eq_span, AttrArgsEq::Ast(expr)) => { - vis.visit_expr(expr); + AttrArgs::Eq { eq_span, value } => { + vis.visit_expr(value.unwrap_ast_mut()); vis.visit_span(eq_span); } - AttrArgs::Eq(_eq_span, AttrArgsEq::Hir(lit)) => { - unreachable!("in literal form when visiting mac args eq: {:?}", lit) - } } } @@ -1065,26 +1066,30 @@ fn walk_where_clause<T: MutVisitor>(vis: &mut T, wc: &mut WhereClause) { vis.visit_span(span); } -fn walk_where_predicate<T: MutVisitor>(vis: &mut T, pred: &mut WherePredicate) { - match pred { - WherePredicate::BoundPredicate(bp) => { - let WhereBoundPredicate { span, bound_generic_params, bounded_ty, bounds } = bp; +pub fn walk_where_predicate<T: MutVisitor>(vis: &mut T, pred: &mut WherePredicate) { + let WherePredicate { kind, id, span } = pred; + vis.visit_id(id); + vis.visit_where_predicate_kind(kind); + vis.visit_span(span); +} + +pub fn walk_where_predicate_kind<T: MutVisitor>(vis: &mut T, kind: &mut WherePredicateKind) { + match kind { + WherePredicateKind::BoundPredicate(bp) => { + let WhereBoundPredicate { bound_generic_params, bounded_ty, bounds } = bp; bound_generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param)); vis.visit_ty(bounded_ty); visit_vec(bounds, |bound| vis.visit_param_bound(bound, BoundKind::Bound)); - vis.visit_span(span); } - WherePredicate::RegionPredicate(rp) => { - let WhereRegionPredicate { span, lifetime, bounds } = rp; + WherePredicateKind::RegionPredicate(rp) => { + let WhereRegionPredicate { lifetime, bounds } = rp; vis.visit_lifetime(lifetime); visit_vec(bounds, |bound| vis.visit_param_bound(bound, BoundKind::Bound)); - vis.visit_span(span); } - WherePredicate::EqPredicate(ep) => { - let WhereEqPredicate { span, lhs_ty, rhs_ty } = ep; + WherePredicateKind::EqPredicate(ep) => { + let WhereEqPredicate { lhs_ty, rhs_ty } = ep; vis.visit_ty(lhs_ty); vis.visit_ty(rhs_ty); - vis.visit_span(span); } } } @@ -1115,13 +1120,14 @@ fn walk_poly_trait_ref<T: MutVisitor>(vis: &mut T, p: &mut PolyTraitRef) { } pub fn walk_field_def<T: MutVisitor>(visitor: &mut T, fd: &mut FieldDef) { - let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety } = fd; + let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety, default } = fd; visitor.visit_id(id); visit_attrs(visitor, attrs); visitor.visit_vis(vis); visit_safety(visitor, safety); visit_opt(ident, |ident| visitor.visit_ident(ident)); visitor.visit_ty(ty); + visit_opt(default, |default| visitor.visit_anon_const(default)); visitor.visit_span(span); } @@ -1520,6 +1526,10 @@ pub fn walk_pat<T: MutVisitor>(vis: &mut T, pat: &mut P<Pat>) { visit_opt(e2, |e| vis.visit_expr(e)); vis.visit_span(span); } + PatKind::Guard(p, e) => { + vis.visit_pat(p); + vis.visit_expr(e); + } PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => { visit_thin_vec(elems, |elem| vis.visit_pat(elem)) } @@ -1620,9 +1630,10 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token visit_thin_exprs(vis, call_args); vis.visit_span(span); } - ExprKind::Binary(_binop, lhs, rhs) => { + ExprKind::Binary(binop, lhs, rhs) => { vis.visit_expr(lhs); vis.visit_expr(rhs); + vis.visit_span(&mut binop.span); } ExprKind::Unary(_unop, ohs) => vis.visit_expr(ohs), ExprKind::Cast(expr, ty) => { @@ -1780,20 +1791,21 @@ pub fn noop_filter_map_expr<T: MutVisitor>(vis: &mut T, mut e: P<Expr>) -> Optio pub fn walk_flat_map_stmt<T: MutVisitor>( vis: &mut T, - Stmt { kind, mut span, mut id }: Stmt, + Stmt { kind, span, mut id }: Stmt, ) -> SmallVec<[Stmt; 1]> { vis.visit_id(&mut id); - let stmts: SmallVec<_> = walk_flat_map_stmt_kind(vis, kind) + let mut stmts: SmallVec<[Stmt; 1]> = walk_flat_map_stmt_kind(vis, kind) .into_iter() .map(|kind| Stmt { id, kind, span }) .collect(); - if stmts.len() > 1 { - panic!( + match stmts.len() { + 0 => {} + 1 => vis.visit_span(&mut stmts[0].span), + 2.. => panic!( "cloning statement `NodeId`s is prohibited by default, \ the visitor should implement custom statement visiting" - ); + ), } - vis.visit_span(&mut span); stmts } diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index d8dad4550c0..e88bf27021a 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -128,21 +128,21 @@ impl AssocOp { } /// Gets the precedence of this operator - pub fn precedence(&self) -> usize { + pub fn precedence(&self) -> ExprPrecedence { use AssocOp::*; match *self { - As => 14, - Multiply | Divide | Modulus => 13, - Add | Subtract => 12, - ShiftLeft | ShiftRight => 11, - BitAnd => 10, - BitXor => 9, - BitOr => 8, - Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7, - LAnd => 6, - LOr => 5, - DotDot | DotDotEq => 4, - Assign | AssignOp(_) => 2, + As => ExprPrecedence::Cast, + Multiply | Divide | Modulus => ExprPrecedence::Product, + Add | Subtract => ExprPrecedence::Sum, + ShiftLeft | ShiftRight => ExprPrecedence::Shift, + BitAnd => ExprPrecedence::BitAnd, + BitXor => ExprPrecedence::BitXor, + BitOr => ExprPrecedence::BitOr, + Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => ExprPrecedence::Compare, + LAnd => ExprPrecedence::LAnd, + LOr => ExprPrecedence::LOr, + DotDot | DotDotEq => ExprPrecedence::Range, + Assign | AssignOp(_) => ExprPrecedence::Assign, } } @@ -229,132 +229,44 @@ impl AssocOp { } } -pub const PREC_CLOSURE: i8 = -40; -pub const PREC_JUMP: i8 = -30; -pub const PREC_RANGE: i8 = -10; -// The range 2..=14 is reserved for AssocOp binary operator precedences. -pub const PREC_PREFIX: i8 = 50; -pub const PREC_UNAMBIGUOUS: i8 = 60; -pub const PREC_FORCE_PAREN: i8 = 100; - -#[derive(Debug, Clone, Copy)] +#[derive(Clone, Copy, PartialEq, PartialOrd)] pub enum ExprPrecedence { Closure, - Break, - Continue, - Ret, - Yield, - Yeet, - Become, - + // return, break, yield + Jump, + // = += -= *= /= %= &= |= ^= <<= >>= + Assign, + // .. ..= Range, - - Binary(BinOpKind), - + // || + LOr, + // && + LAnd, + // == != < > <= >= + Compare, + // | + BitOr, + // ^ + BitXor, + // & + BitAnd, + // << >> + Shift, + // + - + Sum, + // * / % + Product, + // as Cast, - - Assign, - AssignOp, - - AddrOf, - Let, - Unary, - - Call, - MethodCall, - Field, - Index, - Try, - Mac, - - Array, - Repeat, - Tup, - Lit, - Path, - Paren, - If, - While, - ForLoop, - Loop, - Match, - PostfixMatch, - ConstBlock, - Block, - TryBlock, - Struct, - Gen, - Await, - Err, -} - -impl ExprPrecedence { - pub fn order(self) -> i8 { - match self { - ExprPrecedence::Closure => PREC_CLOSURE, - - ExprPrecedence::Break - | ExprPrecedence::Continue - | ExprPrecedence::Ret - | ExprPrecedence::Yield - | ExprPrecedence::Yeet - | ExprPrecedence::Become => PREC_JUMP, - - // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to - // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence - // ensures that `pprust` will add parentheses in the right places to get the desired - // parse. - ExprPrecedence::Range => PREC_RANGE, - - // Binop-like expr kinds, handled by `AssocOp`. - ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8, - ExprPrecedence::Cast => AssocOp::As.precedence() as i8, - - ExprPrecedence::Assign | - ExprPrecedence::AssignOp => AssocOp::Assign.precedence() as i8, - - // Unary, prefix - ExprPrecedence::AddrOf - // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`. - // However, this is not exactly right. When `let _ = a` is the LHS of a binop we - // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` - // but we need to print `(let _ = a) < b` as-is with parens. - | ExprPrecedence::Let - | ExprPrecedence::Unary => PREC_PREFIX, - - // Never need parens - ExprPrecedence::Array - | ExprPrecedence::Await - | ExprPrecedence::Block - | ExprPrecedence::Call - | ExprPrecedence::ConstBlock - | ExprPrecedence::Field - | ExprPrecedence::ForLoop - | ExprPrecedence::Gen - | ExprPrecedence::If - | ExprPrecedence::Index - | ExprPrecedence::Lit - | ExprPrecedence::Loop - | ExprPrecedence::Mac - | ExprPrecedence::Match - | ExprPrecedence::MethodCall - | ExprPrecedence::Paren - | ExprPrecedence::Path - | ExprPrecedence::PostfixMatch - | ExprPrecedence::Repeat - | ExprPrecedence::Struct - | ExprPrecedence::Try - | ExprPrecedence::TryBlock - | ExprPrecedence::Tup - | ExprPrecedence::While - | ExprPrecedence::Err => PREC_UNAMBIGUOUS, - } - } + // unary - * ! & &mut + Prefix, + // paths, loops, function calls, array indexing, field expressions, method calls + Unambiguous, } /// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`. -pub fn prec_let_scrutinee_needs_par() -> usize { - AssocOp::LAnd.precedence() +pub fn prec_let_scrutinee_needs_par() -> ExprPrecedence { + ExprPrecedence::LAnd } /// Suppose we have `let _ = e` and the `order` of `e`. @@ -362,8 +274,8 @@ pub fn prec_let_scrutinee_needs_par() -> usize { /// /// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`. /// Can we print this as `let _ = a OP b`? -pub fn needs_par_as_let_scrutinee(order: i8) -> bool { - order <= prec_let_scrutinee_needs_par() as i8 +pub fn needs_par_as_let_scrutinee(order: ExprPrecedence) -> bool { + order <= prec_let_scrutinee_needs_par() } /// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 20ac9fa02bb..a7f7c37693a 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -192,6 +192,9 @@ pub trait Visitor<'ast>: Sized { fn visit_where_predicate(&mut self, p: &'ast WherePredicate) -> Self::Result { walk_where_predicate(self, p) } + fn visit_where_predicate_kind(&mut self, k: &'ast WherePredicateKind) -> Self::Result { + walk_where_predicate_kind(self, k) + } fn visit_fn(&mut self, fk: FnKind<'ast>, _: Span, _: NodeId) -> Self::Result { walk_fn(self, fk) } @@ -679,6 +682,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res visit_opt!(visitor, visit_expr, lower_bound); visit_opt!(visitor, visit_expr, upper_bound); } + PatKind::Guard(subpattern, guard_condition) => { + try_visit!(visitor.visit_pat(subpattern)); + try_visit!(visitor.visit_expr(guard_condition)); + } PatKind::Wild | PatKind::Rest | PatKind::Never => {} PatKind::Err(_guar) => {} PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => { @@ -794,22 +801,29 @@ pub fn walk_where_predicate<'a, V: Visitor<'a>>( visitor: &mut V, predicate: &'a WherePredicate, ) -> V::Result { - match predicate { - WherePredicate::BoundPredicate(WhereBoundPredicate { + let WherePredicate { kind, id: _, span: _ } = predicate; + visitor.visit_where_predicate_kind(kind) +} + +pub fn walk_where_predicate_kind<'a, V: Visitor<'a>>( + visitor: &mut V, + kind: &'a WherePredicateKind, +) -> V::Result { + match kind { + WherePredicateKind::BoundPredicate(WhereBoundPredicate { bounded_ty, bounds, bound_generic_params, - span: _, }) => { walk_list!(visitor, visit_generic_param, bound_generic_params); try_visit!(visitor.visit_ty(bounded_ty)); walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); } - WherePredicate::RegionPredicate(WhereRegionPredicate { lifetime, bounds, span: _ }) => { + WherePredicateKind::RegionPredicate(WhereRegionPredicate { lifetime, bounds }) => { try_visit!(visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound)); walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); } - WherePredicate::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty, span: _ }) => { + WherePredicateKind::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty }) => { try_visit!(visitor.visit_ty(lhs_ty)); try_visit!(visitor.visit_ty(rhs_ty)); } @@ -961,11 +975,13 @@ pub fn walk_struct_def<'a, V: Visitor<'a>>( } pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) -> V::Result { - let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _ } = field; + let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _, default } = + field; walk_list!(visitor, visit_attribute, attrs); try_visit!(visitor.visit_vis(vis)); visit_opt!(visitor, visit_ident, ident); try_visit!(visitor.visit_ty(ty)); + visit_opt!(visitor, visit_anon_const, &*default); V::Result::output() } @@ -1263,10 +1279,7 @@ pub fn walk_attr_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a AttrArgs) - match args { AttrArgs::Empty => {} AttrArgs::Delimited(_args) => {} - AttrArgs::Eq(_eq_span, AttrArgsEq::Ast(expr)) => try_visit!(visitor.visit_expr(expr)), - AttrArgs::Eq(_eq_span, AttrArgsEq::Hir(lit)) => { - unreachable!("in literal form when walking mac args eq: {:?}", lit) - } + AttrArgs::Eq { value, .. } => try_visit!(visitor.visit_expr(value.unwrap_ast())), } V::Result::output() } diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index a4dbf981115..f96c9fe8e32 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -45,10 +45,6 @@ ast_lowering_bad_return_type_notation_output = ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet -ast_lowering_base_expression_double_dot = - base expression required after `..` - .suggestion = add a base expression here - ast_lowering_clobber_abi_not_supported = `clobber_abi` is not supported on this target @@ -57,6 +53,9 @@ ast_lowering_closure_cannot_be_static = closures cannot be static ast_lowering_coroutine_too_many_parameters = too many parameters for a coroutine (expected 0 or 1 parameters) +ast_lowering_default_field_in_tuple = default fields are not supported in tuple structs + .label = default fields are only supported on structs + ast_lowering_does_not_support_modifiers = the `{$class_name}` register class does not support template modifiers diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index ff803e50997..569a15b0e07 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -82,7 +82,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let mut clobber_abis = FxIndexMap::default(); if let Some(asm_arch) = asm_arch { for (abi_name, abi_span) in &asm.clobber_abis { - match asm::InlineAsmClobberAbi::parse(asm_arch, &self.tcx.sess.target, *abi_name) { + match asm::InlineAsmClobberAbi::parse( + asm_arch, + &self.tcx.sess.target, + &self.tcx.sess.unstable_target_features, + *abi_name, + ) { Ok(abi) => { // If the abi was already in the list, emit an error match clobber_abis.get(&abi) { @@ -222,18 +227,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; // Wrap the expression in an AnonConst. - let parent_def_id = self.current_def_id_parent; + let parent_def_id = self.current_hir_id_owner.def_id; let node_id = self.next_node_id(); - // HACK(min_generic_const_args): see lower_anon_const - if !expr.is_potential_trivial_const_arg(true) { - self.create_def( - parent_def_id, - node_id, - kw::Empty, - DefKind::AnonConst, - *op_sp, - ); - } + self.create_def( + parent_def_id, + node_id, + kw::Empty, + DefKind::AnonConst, + *op_sp, + ); let anon_const = AnonConst { id: node_id, value: P(expr) }; hir::InlineAsmOperand::SymFn { anon_const: self.lower_anon_const_to_anon_const(&anon_const), diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 447af57354f..2564d4e2772 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -38,6 +38,14 @@ pub(crate) struct InvalidAbi { pub suggestion: Option<InvalidAbiSuggestion>, } +#[derive(Diagnostic)] +#[diag(ast_lowering_default_field_in_tuple)] +pub(crate) struct TupleStructWithDefault { + #[primary_span] + #[label] + pub span: Span, +} + pub(crate) struct InvalidAbiReason(pub &'static str); impl Subdiagnostic for InvalidAbiReason { @@ -115,14 +123,6 @@ pub(crate) struct UnderscoreExprLhsAssign { } #[derive(Diagnostic)] -#[diag(ast_lowering_base_expression_double_dot, code = E0797)] -pub(crate) struct BaseExpressionDoubleDot { - #[primary_span] - #[suggestion(code = "/* expr */", applicability = "has-placeholders", style = "verbose")] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(ast_lowering_await_only_in_async_fn_and_blocks, code = E0728)] pub(crate) struct AwaitOnlyInAsyncFnAndBlocks { #[primary_span] diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 3af29838b72..2ad0ff3200e 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -19,10 +19,10 @@ use thin_vec::{ThinVec, thin_vec}; use visit::{Visitor, walk_expr}; use super::errors::{ - AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot, - ClosureCannotBeStatic, CoroutineTooManyParameters, - FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody, - NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign, + AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, ClosureCannotBeStatic, + CoroutineTooManyParameters, FunctionalRecordUpdateDestructuringAssignment, + InclusiveRangeWithNoEnd, MatchArmWithNoBody, NeverPatternWithBody, NeverPatternWithGuard, + UnderscoreExprLhsAssign, }; use super::{ GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt, @@ -109,16 +109,14 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ConstBlock { def_id, hir_id: this.lower_node_id(c.id), - body: this.with_def_id_parent(def_id, |this| { - this.lower_const_body(c.value.span, Some(&c.value)) - }), + body: this.lower_const_body(c.value.span, Some(&c.value)), } }); hir::ExprKind::ConstBlock(c) } ExprKind::Repeat(expr, count) => { let expr = self.lower_expr(expr); - let count = self.lower_array_length(count); + let count = self.lower_array_length_to_const_arg(count); hir::ExprKind::Repeat(expr, count) } ExprKind::Tup(elts) => hir::ExprKind::Tup(self.lower_exprs(elts)), @@ -359,12 +357,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ), ExprKind::Struct(se) => { let rest = match &se.rest { - StructRest::Base(e) => Some(self.lower_expr(e)), - StructRest::Rest(sp) => { - let guar = self.dcx().emit_err(BaseExpressionDoubleDot { span: *sp }); - Some(&*self.arena.alloc(self.expr_err(*sp, guar))) - } - StructRest::None => None, + StructRest::Base(e) => hir::StructTailExpr::Base(self.lower_expr(e)), + StructRest::Rest(sp) => hir::StructTailExpr::DefaultFields(*sp), + StructRest::None => hir::StructTailExpr::None, }; hir::ExprKind::Struct( self.arena.alloc(self.lower_qpath( @@ -452,15 +447,9 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut generic_args = ThinVec::new(); for (idx, arg) in args.iter().cloned().enumerate() { if legacy_args_idx.contains(&idx) { - let parent_def_id = self.current_def_id_parent; + let parent_def_id = self.current_hir_id_owner.def_id; let node_id = self.next_node_id(); - - // HACK(min_generic_const_args): see lower_anon_const - if !arg.is_potential_trivial_const_arg(true) { - // Add a definition for the in-band const def. - self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, f.span); - } - + self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, f.span); let mut visitor = WillCreateDefIdsVisitor {}; let const_value = if let ControlFlow::Break(span) = visitor.visit_expr(&arg) { AstP(Expr { @@ -759,19 +748,17 @@ impl<'hir> LoweringContext<'_, 'hir> { lifetime_elision_allowed: false, }); - let body = self.with_def_id_parent(closure_def_id, move |this| { - this.lower_body(move |this| { - this.coroutine_kind = Some(coroutine_kind); + let body = self.lower_body(move |this| { + this.coroutine_kind = Some(coroutine_kind); - let old_ctx = this.task_context; - if task_context.is_some() { - this.task_context = task_context; - } - let res = body(this); - this.task_context = old_ctx; + let old_ctx = this.task_context; + if task_context.is_some() { + this.task_context = task_context; + } + let res = body(this); + this.task_context = old_ctx; - (params, res) - }) + (params, res) }); // `static |<_task_context?>| -> <return_ty> { <body> }`: @@ -1056,26 +1043,24 @@ impl<'hir> LoweringContext<'_, 'hir> { let (binder_clause, generic_params) = self.lower_closure_binder(binder); let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| { - this.with_def_id_parent(closure_def_id, move |this| { - let mut coroutine_kind = if this - .attrs - .get(&closure_hir_id.local_id) - .is_some_and(|attrs| attrs.iter().any(|attr| attr.has_name(sym::coroutine))) - { - Some(hir::CoroutineKind::Coroutine(Movability::Movable)) - } else { - None - }; - let body_id = this.lower_fn_body(decl, |this| { - this.coroutine_kind = coroutine_kind; - let e = this.lower_expr_mut(body); - coroutine_kind = this.coroutine_kind; - e - }); - let coroutine_option = - this.closure_movability_for_fn(decl, fn_decl_span, coroutine_kind, movability); - (body_id, coroutine_option) - }) + let mut coroutine_kind = if this + .attrs + .get(&closure_hir_id.local_id) + .is_some_and(|attrs| attrs.iter().any(|attr| attr.has_name(sym::coroutine))) + { + Some(hir::CoroutineKind::Coroutine(Movability::Movable)) + } else { + None + }; + let body_id = this.lower_fn_body(decl, |this| { + this.coroutine_kind = coroutine_kind; + let e = this.lower_expr_mut(body); + coroutine_kind = this.coroutine_kind; + e + }); + let coroutine_option = + this.closure_movability_for_fn(decl, fn_decl_span, coroutine_kind, movability); + (body_id, coroutine_option) }); let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params); @@ -1165,28 +1150,26 @@ impl<'hir> LoweringContext<'_, 'hir> { ); let body = self.with_new_scopes(fn_decl_span, |this| { - this.with_def_id_parent(closure_def_id, |this| { - let inner_decl = - FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) }; - - // Transform `async |x: u8| -> X { ... }` into - // `|x: u8| || -> X { ... }`. - let body_id = this.lower_body(|this| { - let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments( - &inner_decl, - |this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)), - fn_decl_span, - body.span, - coroutine_kind, - hir::CoroutineSource::Closure, - ); + let inner_decl = + FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) }; + + // Transform `async |x: u8| -> X { ... }` into + // `|x: u8| || -> X { ... }`. + let body_id = this.lower_body(|this| { + let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments( + &inner_decl, + |this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)), + fn_decl_span, + body.span, + coroutine_kind, + hir::CoroutineSource::Closure, + ); - this.maybe_forward_track_caller(body.span, closure_hir_id, expr.hir_id); + this.maybe_forward_track_caller(body.span, closure_hir_id, expr.hir_id); - (parameters, expr) - }); - body_id - }) + (parameters, expr) + }); + body_id }); let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params); @@ -1540,7 +1523,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Struct( self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))), fields, - None, + hir::StructTailExpr::None, ) } diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 6289966561f..c3ff7b4b897 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -235,8 +235,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { } fn visit_anon_const(&mut self, constant: &'hir AnonConst) { - // FIXME: use real span? - self.insert(DUMMY_SP, constant.hir_id, Node::AnonConst(constant)); + self.insert(constant.span, constant.hir_id, Node::AnonConst(constant)); self.with_parent(constant.hir_id, |this| { intravisit::walk_anon_const(this, constant); @@ -252,8 +251,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { } fn visit_const_arg(&mut self, const_arg: &'hir ConstArg<'hir>) { - // FIXME: use real span? - self.insert(DUMMY_SP, const_arg.hir_id, Node::ConstArg(const_arg)); + self.insert(const_arg.span(), const_arg.hir_id, Node::ConstArg(const_arg)); self.with_parent(const_arg.hir_id, |this| { intravisit::walk_const_arg(this, const_arg); @@ -381,22 +379,10 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { } fn visit_where_predicate(&mut self, predicate: &'hir WherePredicate<'hir>) { - match predicate { - WherePredicate::BoundPredicate(pred) => { - self.insert(pred.span, pred.hir_id, Node::WhereBoundPredicate(pred)); - self.with_parent(pred.hir_id, |this| { - intravisit::walk_where_predicate(this, predicate) - }) - } - _ => intravisit::walk_where_predicate(self, predicate), - } - } - - fn visit_array_length(&mut self, len: &'hir ArrayLen<'hir>) { - match len { - ArrayLen::Infer(inf) => self.insert(inf.span, inf.hir_id, Node::ArrayLenInfer(inf)), - ArrayLen::Body(..) => intravisit::walk_array_len(self, len), - } + self.insert(predicate.span, predicate.hir_id, Node::WherePredicate(predicate)); + self.with_parent(predicate.hir_id, |this| { + intravisit::walk_where_predicate(this, predicate) + }); } fn visit_pattern_type_pattern(&mut self, p: &'hir hir::Pat<'hir>) { diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 48b42fa2e97..7d6c41992eb 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -17,7 +17,10 @@ use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; use tracing::instrument; -use super::errors::{InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound}; +use super::errors::{ + InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound, + TupleStructWithDefault, +}; use super::{ AstOwner, FnDeclKind, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode, ResolverAstLoweringExt, @@ -690,13 +693,27 @@ impl<'hir> LoweringContext<'_, 'hir> { VariantData::Tuple(fields, id) => { let ctor_id = self.lower_node_id(*id); self.alias_attrs(ctor_id, parent_id); - hir::VariantData::Tuple( - self.arena.alloc_from_iter( - fields.iter().enumerate().map(|f| self.lower_field_def(f)), - ), - ctor_id, - self.local_def_id(*id), - ) + let fields = self + .arena + .alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f))); + for field in &fields[..] { + if let Some(default) = field.default { + // Default values in tuple struct and tuple variants are not allowed by the + // RFC due to concerns about the syntax, both in the item definition and the + // expression. We could in the future allow `struct S(i32 = 0);` and force + // users to construct the value with `let _ = S { .. };`. + if self.tcx.features().default_field_values() { + self.dcx().emit_err(TupleStructWithDefault { span: default.span }); + } else { + let _ = self.dcx().span_delayed_bug( + default.span, + "expected `default values on `struct` fields aren't supported` \ + feature-gate error but none was produced", + ); + } + } + } + hir::VariantData::Tuple(fields, ctor_id, self.local_def_id(*id)) } VariantData::Unit(id) => { let ctor_id = self.lower_node_id(*id); @@ -723,6 +740,7 @@ impl<'hir> LoweringContext<'_, 'hir> { None => Ident::new(sym::integer(index), self.lower_span(f.span)), }, vis_span: self.lower_span(f.vis.span), + default: f.default.as_ref().map(|v| self.lower_anon_const_to_anon_const(v)), ty, safety: self.lower_safety(f.safety, hir::Safety::Safe), } @@ -1401,7 +1419,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // keep track of the Span info. Now, `<dyn HirTyLowerer>::add_implicit_sized_bound` // checks both param bounds and where clauses for `?Sized`. for pred in &generics.where_clause.predicates { - let WherePredicate::BoundPredicate(bound_pred) = pred else { + let WherePredicateKind::BoundPredicate(bound_pred) = &pred.kind else { continue; }; let compute_is_param = || { @@ -1538,9 +1556,9 @@ impl<'hir> LoweringContext<'_, 'hir> { } }); let span = self.lower_span(span); - - match kind { - GenericParamKind::Const { .. } => None, + let hir_id = self.next_id(); + let kind = self.arena.alloc(match kind { + GenericParamKind::Const { .. } => return None, GenericParamKind::Type { .. } => { let def_id = self.local_def_id(id).to_def_id(); let hir_id = self.next_id(); @@ -1555,38 +1573,36 @@ impl<'hir> LoweringContext<'_, 'hir> { let ty_id = self.next_id(); let bounded_ty = self.ty_path(ty_id, param_span, hir::QPath::Resolved(None, ty_path)); - Some(hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - hir_id: self.next_id(), + hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate { bounded_ty: self.arena.alloc(bounded_ty), bounds, - span, bound_generic_params: &[], origin, - })) + }) } GenericParamKind::Lifetime => { let ident = self.lower_ident(ident); let lt_id = self.next_node_id(); let lifetime = self.new_named_lifetime(id, lt_id, ident); - Some(hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { + hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate { lifetime, - span, bounds, in_where_clause: false, - })) + }) } - } + }); + Some(hir::WherePredicate { hir_id, span, kind }) } fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> { - match pred { - WherePredicate::BoundPredicate(WhereBoundPredicate { + let hir_id = self.lower_node_id(pred.id); + let span = self.lower_span(pred.span); + let kind = self.arena.alloc(match &pred.kind { + WherePredicateKind::BoundPredicate(WhereBoundPredicate { bound_generic_params, bounded_ty, bounds, - span, - }) => hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - hir_id: self.next_id(), + }) => hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate { bound_generic_params: self .lower_generic_params(bound_generic_params, hir::GenericParamSource::Binder), bounded_ty: self @@ -1595,12 +1611,10 @@ impl<'hir> LoweringContext<'_, 'hir> { bounds, ImplTraitContext::Disallowed(ImplTraitPosition::Bound), ), - span: self.lower_span(*span), origin: PredicateOrigin::WhereClause, }), - WherePredicate::RegionPredicate(WhereRegionPredicate { lifetime, bounds, span }) => { - hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { - span: self.lower_span(*span), + WherePredicateKind::RegionPredicate(WhereRegionPredicate { lifetime, bounds }) => { + hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate { lifetime: self.lower_lifetime(lifetime), bounds: self.lower_param_bounds( bounds, @@ -1609,15 +1623,15 @@ impl<'hir> LoweringContext<'_, 'hir> { in_where_clause: true, }) } - WherePredicate::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty, span }) => { - hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { + WherePredicateKind::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty }) => { + hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate { lhs_ty: self .lower_ty(lhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Bound)), rhs_ty: self .lower_ty(rhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Bound)), - span: self.lower_span(*span), }) } - } + }); + hir::WherePredicate { hir_id, span, kind } } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0b2969a49ba..bac3f974cca 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -117,18 +117,6 @@ struct LoweringContext<'a, 'hir> { is_in_dyn_type: bool, current_hir_id_owner: hir::OwnerId, - /// Why do we need this in addition to [`Self::current_hir_id_owner`]? - /// - /// Currently (as of June 2024), anonymous constants are not HIR owners; however, - /// they do get their own DefIds. Some of these DefIds have to be created during - /// AST lowering, rather than def collection, because we can't tell until after - /// name resolution whether an anonymous constant will end up instead being a - /// [`hir::ConstArgKind::Path`]. However, to compute which generics are - /// available to an anonymous constant nested inside another, we need to make - /// sure that the parent is recorded as the parent anon const, not the enclosing - /// item. So we need to track parent defs differently from HIR owners, since they - /// will be finer-grained in the case of anon consts. - current_def_id_parent: LocalDefId, item_local_id_counter: hir::ItemLocalId, trait_map: ItemLocalMap<Box<[TraitCandidate]>>, @@ -161,7 +149,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { attrs: SortedMap::default(), children: Vec::default(), current_hir_id_owner: hir::CRATE_OWNER_ID, - current_def_id_parent: CRATE_DEF_ID, item_local_id_counter: hir::ItemLocalId::ZERO, ident_and_label_to_local_id: Default::default(), #[cfg(debug_assertions)] @@ -565,7 +552,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { debug_assert_eq!(_old, None); } - let item = self.with_def_id_parent(def_id, f); + let item = f(self); debug_assert_eq!(def_id, item.def_id().def_id); // `f` should have consumed all the elements in these vectors when constructing `item`. debug_assert!(self.impl_trait_defs.is_empty()); @@ -590,13 +577,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.children.push((def_id, hir::MaybeOwner::Owner(info))); } - fn with_def_id_parent<T>(&mut self, parent: LocalDefId, f: impl FnOnce(&mut Self) -> T) -> T { - let current_def_id_parent = std::mem::replace(&mut self.current_def_id_parent, parent); - let result = f(self); - self.current_def_id_parent = current_def_id_parent; - result - } - fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> &'hir hir::OwnerInfo<'hir> { let attrs = std::mem::take(&mut self.attrs); let mut bodies = std::mem::take(&mut self.bodies); @@ -773,7 +753,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { LifetimeRes::Fresh { param, kind, .. } => { // Late resolution delegates to us the creation of the `LocalDefId`. let _def_id = self.create_def( - self.current_hir_id_owner.def_id, // FIXME: should this use self.current_def_id_parent? + self.current_hir_id_owner.def_id, param, kw::UnderscoreLifetime, DefKind::LifetimeParam, @@ -909,7 +889,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // This is an inert key-value attribute - it will never be visible to macros // after it gets lowered to HIR. Therefore, we can extract literals to handle // nonterminals in `#[doc]` (e.g. `#[doc = $e]`). - AttrArgs::Eq(eq_span, AttrArgsEq::Ast(expr)) => { + &AttrArgs::Eq { eq_span, ref value } => { + let expr = value.unwrap_ast(); // In valid code the value always ends up as a single literal. Otherwise, a dummy // literal suffices because the error is handled elsewhere. let lit = if let ExprKind::Lit(token_lit) = expr.kind @@ -925,10 +906,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: DUMMY_SP, } }; - AttrArgs::Eq(*eq_span, AttrArgsEq::Hir(lit)) - } - AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => { - unreachable!("in literal form when lowering mac args eq: {:?}", lit) + AttrArgs::Eq { eq_span, value: AttrArgsEq::Hir(lit) } } } } @@ -1277,9 +1255,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }), )) } - TyKind::Array(ty, length) => { - hir::TyKind::Array(self.lower_ty(ty, itctx), self.lower_array_length(length)) - } + TyKind::Array(ty, length) => hir::TyKind::Array( + self.lower_ty(ty, itctx), + self.lower_array_length_to_const_arg(length), + ), TyKind::Typeof(expr) => hir::TyKind::Typeof(self.lower_anon_const_to_anon_const(expr)), TyKind::TraitObject(bounds, kind) => { let mut lifetime_bound = None; @@ -1466,17 +1445,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let opaque_ty_hir_id = self.lower_node_id(opaque_ty_node_id); debug!(?opaque_ty_def_id, ?opaque_ty_hir_id); - let opaque_ty_def = self.with_def_id_parent(opaque_ty_def_id, |this| { - let bounds = lower_item_bounds(this); - let opaque_ty_def = hir::OpaqueTy { - hir_id: opaque_ty_hir_id, - def_id: opaque_ty_def_id, - bounds, - origin, - span: this.lower_span(opaque_ty_span), - }; - this.arena.alloc(opaque_ty_def) - }); + let bounds = lower_item_bounds(self); + let opaque_ty_def = hir::OpaqueTy { + hir_id: opaque_ty_hir_id, + def_id: opaque_ty_def_id, + bounds, + origin, + span: self.lower_span(opaque_ty_span), + }; + let opaque_ty_def = self.arena.alloc(opaque_ty_def); hir::TyKind::OpaqueDef(opaque_ty_def) } @@ -2029,14 +2006,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.expr_block(block) } - fn lower_array_length(&mut self, c: &AnonConst) -> hir::ArrayLen<'hir> { + fn lower_array_length_to_const_arg(&mut self, c: &AnonConst) -> &'hir hir::ConstArg<'hir> { match c.value.kind { ExprKind::Underscore => { if self.tcx.features().generic_arg_infer() { - hir::ArrayLen::Infer(hir::InferArg { - hir_id: self.lower_node_id(c.id), - span: self.lower_span(c.value.span), - }) + let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span)); + self.arena + .alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind }) } else { feature_err( &self.tcx.sess, @@ -2045,10 +2021,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fluent_generated::ast_lowering_underscore_array_length_unstable, ) .stash(c.value.span, StashKey::UnderscoreForArrayLengths); - hir::ArrayLen::Body(self.lower_anon_const_to_const_arg(c)) + self.lower_anon_const_to_const_arg(c) } } - _ => hir::ArrayLen::Body(self.lower_anon_const_to_const_arg(c)), + _ => self.lower_anon_const_to_const_arg(c), } } @@ -2084,7 +2060,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } else { // Construct an AnonConst where the expr is the "ty"'s path. - let parent_def_id = self.current_def_id_parent; + let parent_def_id = self.current_hir_id_owner.def_id; let node_id = self.next_node_id(); let span = self.lower_span(span); @@ -2108,20 +2084,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.arena.alloc(hir::AnonConst { def_id, hir_id, - body: this.with_def_id_parent(def_id, |this| { - this.lower_const_body(path_expr.span, Some(&path_expr)) - }), + body: this.lower_const_body(path_expr.span, Some(&path_expr)), span, }) }); hir::ConstArgKind::Anon(ct) }; - self.arena.alloc(hir::ConstArg { - hir_id: self.next_id(), - kind: ct_kind, - is_desugared_from_effects: false, - }) + self.arena.alloc(hir::ConstArg { hir_id: self.next_id(), kind: ct_kind }) } /// See [`hir::ConstArg`] for when to use this function vs @@ -2164,46 +2134,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ); return ConstArg { - hir_id: self.next_id(), + hir_id: self.lower_node_id(anon.id), kind: hir::ConstArgKind::Path(qpath), - is_desugared_from_effects: false, }; } let lowered_anon = self.lower_anon_const_to_anon_const(anon); - ConstArg { - hir_id: self.next_id(), - kind: hir::ConstArgKind::Anon(lowered_anon), - is_desugared_from_effects: false, - } + ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Anon(lowered_anon) } } /// See [`hir::ConstArg`] for when to use this function vs /// [`Self::lower_anon_const_to_const_arg`]. fn lower_anon_const_to_anon_const(&mut self, c: &AnonConst) -> &'hir hir::AnonConst { - if c.value.is_potential_trivial_const_arg(true) { - // HACK(min_generic_const_args): see DefCollector::visit_anon_const - // Over there, we guess if this is a bare param and only create a def if - // we think it's not. However we may can guess wrong (see there for example) - // in which case we have to create the def here. - self.create_def( - self.current_def_id_parent, - c.id, - kw::Empty, - DefKind::AnonConst, - c.value.span, - ); - } - self.arena.alloc(self.with_new_scopes(c.value.span, |this| { let def_id = this.local_def_id(c.id); let hir_id = this.lower_node_id(c.id); hir::AnonConst { def_id, hir_id, - body: this.with_def_id_parent(def_id, |this| { - this.lower_const_body(c.value.span, Some(&c.value)) - }), + body: this.lower_const_body(c.value.span, Some(&c.value)), span: this.lower_span(c.value.span), } })) diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index ace7bfb5c73..c4bae084a3f 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -114,6 +114,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_range_end(end, e2.is_some()), ); } + // FIXME(guard_patterns): lower pattern guards to HIR + PatKind::Guard(inner, _) => pattern = inner, PatKind::Slice(pats) => break self.lower_pat_slice(pats), PatKind::Rest => { // If we reach here the `..` pattern is not semantically allowed. diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index d81fd53938b..5a0ec865f9d 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -207,8 +207,6 @@ ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc} -ast_passes_show_span = {$msg} - ast_passes_stability_outside_std = stability attributes may not be used outside of the standard library ast_passes_static_without_body = diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 07a6f4e5ee7..64e91c91e2c 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1200,14 +1200,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> { validate_generic_param_order(self.dcx(), &generics.params, generics.span); for predicate in &generics.where_clause.predicates { - if let WherePredicate::EqPredicate(predicate) = predicate { - deny_equality_constraints(self, predicate, generics); + let span = predicate.span; + if let WherePredicateKind::EqPredicate(predicate) = &predicate.kind { + deny_equality_constraints(self, predicate, span, generics); } } walk_list!(self, visit_generic_param, &generics.params); for predicate in &generics.where_clause.predicates { - match predicate { - WherePredicate::BoundPredicate(bound_pred) => { + match &predicate.kind { + WherePredicateKind::BoundPredicate(bound_pred) => { // This is slightly complicated. Our representation for poly-trait-refs contains a single // binder and thus we only allow a single level of quantification. However, // the syntax of Rust permits quantification in two places in where clauses, @@ -1504,9 +1505,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn deny_equality_constraints( this: &AstValidator<'_>, predicate: &WhereEqPredicate, + predicate_span: Span, generics: &Generics, ) { - let mut err = errors::EqualityInWhere { span: predicate.span, assoc: None, assoc2: None }; + let mut err = errors::EqualityInWhere { span: predicate_span, assoc: None, assoc2: None }; // Given `<A as Foo>::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`. if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind @@ -1550,7 +1552,7 @@ fn deny_equality_constraints( } } err.assoc = Some(errors::AssociatedSuggestion { - span: predicate.span, + span: predicate_span, ident: *ident, param: param.ident, path: pprust::path_to_string(&assoc_path), @@ -1580,23 +1582,23 @@ fn deny_equality_constraints( // We're removing th eonly where bound left, remove the whole thing. generics.where_clause.span } else { - let mut span = predicate.span; + let mut span = predicate_span; let mut prev: Option<Span> = None; let mut preds = generics.where_clause.predicates.iter().peekable(); // Find the predicate that shouldn't have been in the where bound list. while let Some(pred) = preds.next() { - if let WherePredicate::EqPredicate(pred) = pred - && pred.span == predicate.span + if let WherePredicateKind::EqPredicate(_) = pred.kind + && pred.span == predicate_span { if let Some(next) = preds.peek() { // This is the first predicate, remove the trailing comma as well. - span = span.with_hi(next.span().lo()); + span = span.with_hi(next.span.lo()); } else if let Some(prev) = prev { // Remove the previous comma as well. span = span.with_lo(prev.hi()); } } - prev = Some(pred.span()); + prev = Some(pred.span); } span }; @@ -1613,8 +1615,8 @@ fn deny_equality_constraints( if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind { // Given `A: Foo, Foo::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`. for bounds in generics.params.iter().map(|p| &p.bounds).chain( - generics.where_clause.predicates.iter().filter_map(|pred| match pred { - WherePredicate::BoundPredicate(p) => Some(&p.bounds), + generics.where_clause.predicates.iter().filter_map(|pred| match &pred.kind { + WherePredicateKind::BoundPredicate(p) => Some(&p.bounds), _ => None, }), ) { @@ -1637,8 +1639,8 @@ fn deny_equality_constraints( // Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`. if let [potential_param, potential_assoc] = &full_path.segments[..] { for (ident, bounds) in generics.params.iter().map(|p| (p.ident, &p.bounds)).chain( - generics.where_clause.predicates.iter().filter_map(|pred| match pred { - WherePredicate::BoundPredicate(p) + generics.where_clause.predicates.iter().filter_map(|pred| match &pred.kind { + WherePredicateKind::BoundPredicate(p) if let ast::TyKind::Path(None, path) = &p.bounded_ty.kind && let [segment] = &path.segments[..] => { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index f65056a494b..9b600e3ee92 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -780,14 +780,6 @@ pub(crate) struct IncompatibleFeatures { } #[derive(Diagnostic)] -#[diag(ast_passes_show_span)] -pub(crate) struct ShowSpan { - #[primary_span] - pub span: Span, - pub msg: &'static str, -} - -#[derive(Diagnostic)] #[diag(ast_passes_negative_bound_not_supported)] pub(crate) struct NegativeBoundUnsupported { #[primary_span] diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 8a392e4407b..aa3b772efb1 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -4,9 +4,9 @@ use rustc_ast::{NodeId, PatKind, attr, token}; use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features, GateIssue}; use rustc_session::Session; use rustc_session::parse::{feature_err, feature_err_issue, feature_warn}; +use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; +use rustc_span::symbol::{Symbol, sym}; use rustc_target::spec::abi; use thin_vec::ThinVec; @@ -345,8 +345,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { fn visit_generics(&mut self, g: &'a ast::Generics) { for predicate in &g.where_clause.predicates { - match predicate { - ast::WherePredicate::BoundPredicate(bound_pred) => { + match &predicate.kind { + ast::WherePredicateKind::BoundPredicate(bound_pred) => { // A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params); } @@ -516,6 +516,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { "async closures are unstable", "to use an async block, remove the `||`: `async {`" ); + gate_all!( + async_trait_bounds, + "`async` trait bounds are unstable", + "use the desugared name of the async trait, such as `AsyncFn`" + ); gate_all!(async_for_loop, "`for await` loops are experimental"); gate_all!( closure_lifetime_binder, @@ -551,6 +556,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); gate_all!(explicit_tail_calls, "`become` expression is experimental"); gate_all!(generic_const_items, "generic const items are experimental"); + gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards"); + gate_all!(default_field_values, "default values on fields are experimental"); gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); gate_all!(postfix_match, "postfix match is experimental"); gate_all!(mut_ref, "mutable by-reference bindings are experimental"); @@ -690,6 +697,7 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) { .find(|feat| feat.gate_name == sym::generic_const_exprs) .map(|feat| feat.attr_sp) { + #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] sess.dcx().emit_err(errors::IncompatibleFeatures { spans: vec![gce_span], f1: Symbol::intern("-Znext-solver=globally"), diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 86752da79ae..b4ed70d83e5 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -1,8 +1,6 @@ //! The `rustc_ast_passes` crate contains passes which validate the AST in `syntax` //! parsed by `rustc_parse` and then lowered, after the passes in this crate, //! by `rustc_ast_lowering`. -//! -//! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`. // tidy-alphabetical-start #![allow(internal_features)] @@ -18,6 +16,5 @@ pub mod ast_validation; mod errors; pub mod feature_gate; -pub mod show_span; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_ast_passes/src/show_span.rs b/compiler/rustc_ast_passes/src/show_span.rs deleted file mode 100644 index e7ba2e7fc30..00000000000 --- a/compiler/rustc_ast_passes/src/show_span.rs +++ /dev/null @@ -1,68 +0,0 @@ -//! Span debugger -//! -//! This module shows spans for all expressions in the crate -//! to help with compiler debugging. - -use std::str::FromStr; - -use rustc_ast as ast; -use rustc_ast::visit; -use rustc_ast::visit::Visitor; -use rustc_errors::DiagCtxtHandle; - -use crate::errors; - -enum Mode { - Expression, - Pattern, - Type, -} - -impl FromStr for Mode { - type Err = (); - fn from_str(s: &str) -> Result<Mode, ()> { - let mode = match s { - "expr" => Mode::Expression, - "pat" => Mode::Pattern, - "ty" => Mode::Type, - _ => return Err(()), - }; - Ok(mode) - } -} - -struct ShowSpanVisitor<'a> { - dcx: DiagCtxtHandle<'a>, - mode: Mode, -} - -impl<'a> Visitor<'a> for ShowSpanVisitor<'a> { - fn visit_expr(&mut self, e: &'a ast::Expr) { - if let Mode::Expression = self.mode { - self.dcx.emit_warn(errors::ShowSpan { span: e.span, msg: "expression" }); - } - visit::walk_expr(self, e); - } - - fn visit_pat(&mut self, p: &'a ast::Pat) { - if let Mode::Pattern = self.mode { - self.dcx.emit_warn(errors::ShowSpan { span: p.span, msg: "pattern" }); - } - visit::walk_pat(self, p); - } - - fn visit_ty(&mut self, t: &'a ast::Ty) { - if let Mode::Type = self.mode { - self.dcx.emit_warn(errors::ShowSpan { span: t.span, msg: "type" }); - } - visit::walk_ty(self, t); - } -} - -pub fn run(dcx: DiagCtxtHandle<'_>, mode: &str, krate: &ast::Crate) { - let Ok(mode) = mode.parse() else { - return; - }; - let mut v = ShowSpanVisitor { dcx, mode }; - visit::walk_crate(&mut v, krate); -} diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index d7c531f3760..49e4a559e73 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -648,14 +648,14 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere AttrArgs::Empty => { self.print_path(&item.path, false, 0); } - AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => { + AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => { self.print_path(&item.path, false, 0); self.space(); self.word_space("="); let token_str = self.expr_to_string(expr); self.word(token_str); } - AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => { + AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => { self.print_path(&item.path, false, 0); self.space(); self.word_space("="); @@ -1709,6 +1709,14 @@ impl<'a> State<'a> { self.print_expr(e, FixupContext::default()); } } + PatKind::Guard(subpat, condition) => { + self.popen(); + self.print_pat(subpat); + self.space(); + self.word_space("if"); + self.print_expr(condition, FixupContext::default()); + self.pclose(); + } PatKind::Slice(elts) => { self.word("["); self.commasep(Inconsistent, elts, |s, p| s.print_pat(p)); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 893bfaf8f71..c239cb249c3 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -5,7 +5,7 @@ use itertools::{Itertools, Position}; use rustc_ast::ptr::P; use rustc_ast::util::classify; use rustc_ast::util::literal::escape_byte_str_symbol; -use rustc_ast::util::parser::{self, AssocOp, Fixity}; +use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity}; use rustc_ast::{ self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount, FormatDebugHex, FormatSign, FormatTrait, token, @@ -58,10 +58,6 @@ impl<'a> State<'a> { self.pclose() } - fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8, fixup: FixupContext) { - self.print_expr_cond_paren(expr, expr.precedence().order() < prec, fixup); - } - /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in /// `if cond { ... }`. fn print_expr_as_cond(&mut self, expr: &ast::Expr) { @@ -216,9 +212,9 @@ impl<'a> State<'a> { } fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) { - let prec = match func.kind { - ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN, - _ => parser::PREC_UNAMBIGUOUS, + let needs_paren = match func.kind { + ast::ExprKind::Field(..) => true, + _ => func.precedence() < ExprPrecedence::Unambiguous, }; // Independent of parenthesization related to precedence, we must @@ -237,7 +233,7 @@ impl<'a> State<'a> { // because the latter is valid syntax but with the incorrect meaning. // It's a match-expression followed by tuple-expression, not a function // call. - self.print_expr_maybe_paren(func, prec, fixup.leftmost_subexpression()); + self.print_expr_cond_paren(func, needs_paren, fixup.leftmost_subexpression()); self.print_call_post(args) } @@ -258,7 +254,11 @@ impl<'a> State<'a> { // boundaries, `$receiver.method()` can be parsed back as a statement // containing an expression if and only if `$receiver` can be parsed as // a statement containing an expression. - self.print_expr_maybe_paren(receiver, parser::PREC_UNAMBIGUOUS, fixup); + self.print_expr_cond_paren( + receiver, + receiver.precedence() < ExprPrecedence::Unambiguous, + fixup, + ); self.word("."); self.print_ident(segment.ident); @@ -276,21 +276,22 @@ impl<'a> State<'a> { fixup: FixupContext, ) { let assoc_op = AssocOp::from_ast_binop(op.node); - let prec = assoc_op.precedence() as i8; - let fixity = assoc_op.fixity(); - - let (left_prec, right_prec) = match fixity { - Fixity::Left => (prec, prec + 1), - Fixity::Right => (prec + 1, prec), - Fixity::None => (prec + 1, prec + 1), + let binop_prec = assoc_op.precedence(); + let left_prec = lhs.precedence(); + let right_prec = rhs.precedence(); + + let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() { + Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec), + Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec), + Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec), }; - let left_prec = match (&lhs.kind, op.node) { + match (&lhs.kind, op.node) { // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead // of `(x as i32) < ...`. We need to convince it _not_ to do that. (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => { - parser::PREC_FORCE_PAREN + left_needs_paren = true; } // We are given `(let _ = a) OP b`. // @@ -300,23 +301,25 @@ impl<'a> State<'a> { // - Otherwise, e.g. when we have `(let a = b) < c` in AST, // parens are required since the parser would interpret `let a = b < c` as // `let a = (b < c)`. To achieve this, we force parens. - (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => { - parser::PREC_FORCE_PAREN + (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(binop_prec) => { + left_needs_paren = true; } - _ => left_prec, - }; - - self.print_expr_maybe_paren(lhs, left_prec, fixup.leftmost_subexpression()); + _ => {} + } + self.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression()); self.space(); self.word_space(op.node.as_str()); - - self.print_expr_maybe_paren(rhs, right_prec, fixup.subsequent_subexpression()); + self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression()); } fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) { self.word(op.as_str()); - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX, fixup.subsequent_subexpression()); + self.print_expr_cond_paren( + expr, + expr.precedence() < ExprPrecedence::Prefix, + fixup.subsequent_subexpression(), + ); } fn print_expr_addr_of( @@ -334,7 +337,11 @@ impl<'a> State<'a> { self.print_mutability(mutability, true); } } - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX, fixup.subsequent_subexpression()); + self.print_expr_cond_paren( + expr, + expr.precedence() < ExprPrecedence::Prefix, + fixup.subsequent_subexpression(), + ); } pub(super) fn print_expr(&mut self, expr: &ast::Expr, fixup: FixupContext) { @@ -416,8 +423,11 @@ impl<'a> State<'a> { self.print_token_literal(lit, expr.span) } ast::ExprKind::Cast(expr, ty) => { - let prec = AssocOp::As.precedence() as i8; - self.print_expr_maybe_paren(expr, prec, fixup.leftmost_subexpression()); + self.print_expr_cond_paren( + expr, + expr.precedence() < ExprPrecedence::Cast, + fixup.leftmost_subexpression(), + ); self.space(); self.word_space("as"); self.print_type(ty); @@ -490,7 +500,11 @@ impl<'a> State<'a> { self.space(); } MatchKind::Postfix => { - self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS, fixup); + self.print_expr_cond_paren( + expr, + expr.precedence() < ExprPrecedence::Unambiguous, + fixup, + ); self.word_nbsp(".match"); } } @@ -550,33 +564,57 @@ impl<'a> State<'a> { self.print_block_with_attrs(blk, attrs); } ast::ExprKind::Await(expr, _) => { - self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS, fixup); + self.print_expr_cond_paren( + expr, + expr.precedence() < ExprPrecedence::Unambiguous, + fixup, + ); self.word(".await"); } ast::ExprKind::Assign(lhs, rhs, _) => { - let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren(lhs, prec + 1, fixup.leftmost_subexpression()); + self.print_expr_cond_paren( + lhs, + // Ranges are allowed on the right-hand side of assignment, + // but not the left. `(a..b) = c` needs parentheses. + lhs.precedence() <= ExprPrecedence::Range, + fixup.leftmost_subexpression(), + ); self.space(); self.word_space("="); - self.print_expr_maybe_paren(rhs, prec, fixup.subsequent_subexpression()); + self.print_expr_cond_paren( + rhs, + rhs.precedence() < ExprPrecedence::Assign, + fixup.subsequent_subexpression(), + ); } ast::ExprKind::AssignOp(op, lhs, rhs) => { - let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren(lhs, prec + 1, fixup.leftmost_subexpression()); + self.print_expr_cond_paren( + lhs, + lhs.precedence() <= ExprPrecedence::Range, + fixup.leftmost_subexpression(), + ); self.space(); self.word(op.node.as_str()); self.word_space("="); - self.print_expr_maybe_paren(rhs, prec, fixup.subsequent_subexpression()); + self.print_expr_cond_paren( + rhs, + rhs.precedence() < ExprPrecedence::Assign, + fixup.subsequent_subexpression(), + ); } ast::ExprKind::Field(expr, ident) => { - self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS, fixup); + self.print_expr_cond_paren( + expr, + expr.precedence() < ExprPrecedence::Unambiguous, + fixup, + ); self.word("."); self.print_ident(*ident); } ast::ExprKind::Index(expr, index, _) => { - self.print_expr_maybe_paren( + self.print_expr_cond_paren( expr, - parser::PREC_UNAMBIGUOUS, + expr.precedence() < ExprPrecedence::Unambiguous, fixup.leftmost_subexpression(), ); self.word("["); @@ -588,16 +626,24 @@ impl<'a> State<'a> { // than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`. // Here we use a fake precedence value so that any child with lower precedence than // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.) - let fake_prec = AssocOp::LOr.precedence() as i8; + let fake_prec = ExprPrecedence::LOr; if let Some(e) = start { - self.print_expr_maybe_paren(e, fake_prec, fixup.leftmost_subexpression()); + self.print_expr_cond_paren( + e, + e.precedence() < fake_prec, + fixup.leftmost_subexpression(), + ); } match limits { ast::RangeLimits::HalfOpen => self.word(".."), ast::RangeLimits::Closed => self.word("..="), } if let Some(e) = end { - self.print_expr_maybe_paren(e, fake_prec, fixup.subsequent_subexpression()); + self.print_expr_cond_paren( + e, + e.precedence() < fake_prec, + fixup.subsequent_subexpression(), + ); } } ast::ExprKind::Underscore => self.word("_"), @@ -615,7 +661,7 @@ impl<'a> State<'a> { expr, // Parenthesize if required by precedence, or in the // case of `break 'inner: loop { break 'inner 1 } + 1` - expr.precedence().order() < parser::PREC_JUMP + expr.precedence() < ExprPrecedence::Jump || (opt_label.is_none() && classify::leading_labeled_expr(expr)), fixup.subsequent_subexpression(), ); @@ -632,9 +678,9 @@ impl<'a> State<'a> { self.word("return"); if let Some(expr) = result { self.word(" "); - self.print_expr_maybe_paren( + self.print_expr_cond_paren( expr, - parser::PREC_JUMP, + expr.precedence() < ExprPrecedence::Jump, fixup.subsequent_subexpression(), ); } @@ -645,9 +691,9 @@ impl<'a> State<'a> { self.word("yeet"); if let Some(expr) = result { self.word(" "); - self.print_expr_maybe_paren( + self.print_expr_cond_paren( expr, - parser::PREC_JUMP, + expr.precedence() < ExprPrecedence::Jump, fixup.subsequent_subexpression(), ); } @@ -655,9 +701,9 @@ impl<'a> State<'a> { ast::ExprKind::Become(result) => { self.word("become"); self.word(" "); - self.print_expr_maybe_paren( + self.print_expr_cond_paren( result, - parser::PREC_JUMP, + result.precedence() < ExprPrecedence::Jump, fixup.subsequent_subexpression(), ); } @@ -709,15 +755,15 @@ impl<'a> State<'a> { if let Some(expr) = e { self.space(); - self.print_expr_maybe_paren( + self.print_expr_cond_paren( expr, - parser::PREC_JUMP, + expr.precedence() < ExprPrecedence::Jump, fixup.subsequent_subexpression(), ); } } ast::ExprKind::Try(e) => { - self.print_expr_maybe_paren(e, parser::PREC_UNAMBIGUOUS, fixup); + self.print_expr_cond_paren(e, e.precedence() < ExprPrecedence::Unambiguous, fixup); self.word("?") } ast::ExprKind::TryBlock(blk) => { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs index 50fd12a4e8b..6f5382ce61d 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs @@ -191,6 +191,6 @@ impl FixupContext { /// "let chain". pub(crate) fn needs_par_as_let_scrutinee(self, expr: &Expr) -> bool { self.parenthesize_exterior_struct_lit && parser::contains_exterior_struct_lit(expr) - || parser::needs_par_as_let_scrutinee(expr.precedence().order()) + || parser::needs_par_as_let_scrutinee(expr.precedence()) } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 8279c66836c..1ae765c0130 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -726,11 +726,12 @@ impl<'a> State<'a> { } pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) { - match predicate { - ast::WherePredicate::BoundPredicate(where_bound_predicate) => { + let ast::WherePredicate { kind, id: _, span: _ } = predicate; + match kind { + ast::WherePredicateKind::BoundPredicate(where_bound_predicate) => { self.print_where_bound_predicate(where_bound_predicate); } - ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { + ast::WherePredicateKind::RegionPredicate(ast::WhereRegionPredicate { lifetime, bounds, .. @@ -742,7 +743,9 @@ impl<'a> State<'a> { self.print_lifetime_bounds(bounds); } } - ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { + ast::WherePredicateKind::EqPredicate(ast::WhereEqPredicate { + lhs_ty, rhs_ty, .. + }) => { self.print_type(lhs_ty); self.space(); self.word_space("="); diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index c11103af476..8dcc5324fdf 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1151,7 +1151,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { expr: &hir::Expr<'_>, ) { let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); - let hir::ExprKind::Struct(struct_qpath, fields, Some(base)) = expr.kind else { return }; + let hir::ExprKind::Struct(struct_qpath, fields, hir::StructTailExpr::Base(base)) = + expr.kind + else { + return; + }; let hir::QPath::Resolved(_, path) = struct_qpath else { return }; let hir::def::Res::Def(_, def_id) = path.res else { return }; let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return }; @@ -1239,7 +1243,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { expr: &'tcx hir::Expr<'tcx>, use_spans: Option<UseSpans<'tcx>>, ) { - if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind { + if let hir::ExprKind::Struct(_, _, hir::StructTailExpr::Base(_)) = expr.kind { // We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single // `Location` that covers both the `S { ... }` literal, all of its fields and the // `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr` @@ -1450,6 +1454,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ty::Param(param_ty) => Ok(( generics.type_param(param_ty, tcx), predicate.trait_ref.print_trait_sugared().to_string(), + Some(predicate.trait_ref.def_id), )), _ => Err(()), } @@ -1463,9 +1468,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { tcx, hir_generics, err, - predicates - .iter() - .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)), + predicates.iter().map(|(param, constraint, def_id)| { + (param.name.as_str(), &**constraint, *def_id) + }), None, ); } diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index bda96726738..92afad62aa4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1062,8 +1062,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { && let spans = hir_generics .predicates .iter() - .filter_map(|pred| match pred { - hir::WherePredicate::BoundPredicate(pred) => Some(pred), + .filter_map(|pred| match pred.kind { + hir::WherePredicateKind::BoundPredicate(pred) => Some(pred), _ => None, }) .filter(|pred| { diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index c38747f6675..6ea5c44dc01 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -5,7 +5,7 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::GenericBound::Trait; use rustc_hir::QPath::Resolved; -use rustc_hir::WherePredicate::BoundPredicate; +use rustc_hir::WherePredicateKind::BoundPredicate; use rustc_hir::def::Res::Def; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -236,7 +236,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let mut hrtb_bounds = vec![]; gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| { for pred in generics.predicates { - let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) = pred + let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) = + pred.kind else { continue; }; @@ -267,12 +268,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { }; debug!(?generics_fn); generics_fn.predicates.iter().for_each(|predicate| { - let BoundPredicate(WhereBoundPredicate { - span: bounded_span, - bounded_ty, - bounds, - .. - }) = predicate + let BoundPredicate(WhereBoundPredicate { bounded_ty, bounds, .. }) = predicate.kind else { return; }; @@ -287,7 +283,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { .rfind(|param| param.def_id.to_def_id() == defid) .is_some() { - suggestions.push((bounded_span.shrink_to_hi(), " + 'static".to_string())); + suggestions.push((predicate.span.shrink_to_hi(), " + 'static".to_string())); } }); }); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 16a4f699177..d7db92da18f 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -26,7 +26,7 @@ use rustc_data_structures::graph::dominators::Dominators; use rustc_errors::Diag; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_index::bit_set::{BitSet, ChunkedBitSet}; +use rustc_index::bit_set::{BitSet, MixedBitSet}; use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::{ InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt, @@ -34,6 +34,7 @@ use rustc_infer::infer::{ use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::*; use rustc_middle::query::Providers; +use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt, TypingMode}; use rustc_middle::{bug, span_bug}; use rustc_mir_dataflow::impls::{ @@ -502,7 +503,7 @@ impl<'tcx> BorrowckInferCtxt<'tcx> { for data in tcx.typeck(def_id).concrete_opaque_types.iter().map(|(k, v)| (*k, *v)) { // HIR typeck did not infer the regions of the opaque, so we instantiate // them with fresh inference variables. - let (key, hidden_ty) = tcx.fold_regions(data, |_, _| { + let (key, hidden_ty) = fold_regions(tcx, data, |_, _| { self.next_nll_region_var_in_universe( NllRegionVariableOrigin::Existential { from_forall: false }, ty::UniverseIndex::ROOT, @@ -1796,7 +1797,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { location: Location, desired_action: InitializationRequiringAction, place_span: (PlaceRef<'tcx>, Span), - maybe_uninits: &ChunkedBitSet<MovePathIndex>, + maybe_uninits: &MixedBitSet<MovePathIndex>, from: u64, to: u64, ) { diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 0ddb4e110e3..0eecf98a6ed 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -18,6 +18,7 @@ use rustc_middle::mir::{ TerminatorKind, }; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; +use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex}; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_span::Span; @@ -972,25 +973,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements: &mut Vec<ClosureOutlivesRequirement<'tcx>>, ) -> bool { let tcx = infcx.tcx; - - let TypeTest { generic_kind, lower_bound, span: _, verify_bound: _ } = type_test; + let TypeTest { generic_kind, lower_bound, span: blame_span, ref verify_bound } = *type_test; let generic_ty = generic_kind.to_ty(tcx); let Some(subject) = self.try_promote_type_test_subject(infcx, generic_ty) else { return false; }; - debug!("subject = {:?}", subject); - - let r_scc = self.constraint_sccs.scc(*lower_bound); - + let r_scc = self.constraint_sccs.scc(lower_bound); debug!( "lower_bound = {:?} r_scc={:?} universe={:?}", lower_bound, r_scc, self.constraint_sccs.annotation(r_scc).min_universe() ); - // If the type test requires that `T: 'a` where `'a` is a // placeholder from another universe, that effectively requires // `T: 'static`, so we have to propagate that requirement. @@ -1003,7 +999,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements.push(ClosureOutlivesRequirement { subject, outlived_free_region: static_r, - blame_span: type_test.span, + blame_span, category: ConstraintCategory::Boring, }); @@ -1030,12 +1026,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { // where `ur` is a local bound -- we are sometimes in a // position to prove things that our caller cannot. See // #53570 for an example. - if self.eval_verify_bound(infcx, generic_ty, ur, &type_test.verify_bound) { + if self.eval_verify_bound(infcx, generic_ty, ur, &verify_bound) { continue; } let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur); - debug!("try_promote_type_test: non_local_ub={:?}", non_local_ub); + debug!(?non_local_ub); // This is slightly too conservative. To show T: '1, given `'2: '1` // and `'3: '1` we only need to prove that T: '2 *or* T: '3, but to @@ -1048,10 +1044,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { let requirement = ClosureOutlivesRequirement { subject, outlived_free_region: upper_bound, - blame_span: type_test.span, + blame_span, category: ConstraintCategory::Boring, }; - debug!("try_promote_type_test: pushing {:#?}", requirement); + debug!(?requirement, "adding closure requirement"); propagated_outlives_requirements.push(requirement); } } @@ -1062,45 +1058,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// variables in the type `T` with an equal universal region from the /// closure signature. /// This is not always possible, so this is a fallible process. - #[instrument(level = "debug", skip(self, infcx))] + #[instrument(level = "debug", skip(self, infcx), ret)] fn try_promote_type_test_subject( &self, infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option<ClosureOutlivesSubject<'tcx>> { let tcx = infcx.tcx; - - // Opaque types' args may include useless lifetimes. - // We will replace them with ReStatic. - struct OpaqueFolder<'tcx> { - tcx: TyCtxt<'tcx>, - } - impl<'tcx> ty::TypeFolder<TyCtxt<'tcx>> for OpaqueFolder<'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.tcx - } - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - use ty::TypeSuperFoldable as _; - let tcx = self.tcx; - let &ty::Alias(ty::Opaque, ty::AliasTy { args, def_id, .. }) = t.kind() else { - return t.super_fold_with(self); - }; - let args = std::iter::zip(args, tcx.variances_of(def_id)).map(|(arg, v)| { - match (arg.unpack(), v) { - (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => { - tcx.lifetimes.re_static.into() - } - _ => arg.fold_with(self), - } - }); - Ty::new_opaque(tcx, def_id, tcx.mk_args_from_iter(args)) - } - } - - let ty = ty.fold_with(&mut OpaqueFolder { tcx }); let mut failed = false; - - let ty = tcx.fold_regions(ty, |r, _depth| { + let ty = fold_regions(tcx, ty, |r, _depth| { let r_vid = self.to_region_vid(r); let r_scc = self.constraint_sccs.scc(r_vid); @@ -1273,7 +1239,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { where T: TypeFoldable<TyCtxt<'tcx>>, { - tcx.fold_regions(value, |r, _db| { + fold_regions(tcx, value, |r, _db| { let vid = self.to_region_vid(r); let scc = self.constraint_sccs.scc(vid); let repr = self.scc_representative(scc); diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 993d5d86333..7164a129235 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -3,6 +3,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _}; use rustc_macros::extension; +use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{ self, GenericArgKind, GenericArgs, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, @@ -117,7 +118,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { }); debug!(?opaque_type_key, ?arg_regions); - let concrete_type = infcx.tcx.fold_regions(concrete_type, |region, _| { + let concrete_type = fold_regions(infcx.tcx, concrete_type, |region, _| { arg_regions .iter() .find(|&&(arg_vid, _)| self.eval_equal(region.as_var(), arg_vid)) @@ -204,7 +205,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { where T: TypeFoldable<TyCtxt<'tcx>>, { - tcx.fold_regions(ty, |region, _| match *region { + fold_regions(tcx, ty, |region, _| match *region { ty::ReVar(vid) => { let scc = self.constraint_sccs.scc(vid); @@ -442,7 +443,7 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> { let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); let mut seen = vec![tcx.lifetimes.re_static]; - let canonical_args = tcx.fold_regions(args, |r1, _| { + let canonical_args = fold_regions(tcx, args, |r1, _| { if r1.is_error() { r1 } else if let Some(&r2) = seen.iter().find(|&&r2| { diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs index b7aef71eb54..d83d6ade203 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -2,6 +2,7 @@ use rustc_index::IndexSlice; use rustc_infer::infer::NllRegionVariableOrigin; use rustc_middle::mir::visit::{MutVisitor, TyContext}; use rustc_middle::mir::{Body, ConstOperand, Location, Promoted}; +use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeFoldable}; use rustc_span::Symbol; use tracing::{debug, instrument}; @@ -68,7 +69,7 @@ impl<'a, 'tcx> RegionRenumberer<'a, 'tcx> { F: Fn() -> RegionCtxt, { let origin = NllRegionVariableOrigin::Existential { from_forall: false }; - self.infcx.tcx.fold_regions(value, |_region, _depth| { + fold_regions(self.infcx.tcx, value, |_region, _depth| { self.infcx.next_nll_region_var(origin, || region_ctxt_fn()) }) } diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 68b843d4d0d..585d0eabf5b 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -7,6 +7,7 @@ use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast}; use rustc_span::Span; use rustc_span::def_id::DefId; +use rustc_trait_selection::solve::NoSolution; use rustc_trait_selection::traits::ObligationCause; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput}; @@ -177,6 +178,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if self.infcx.next_trait_solver() { let body = self.body; let param_env = self.infcx.param_env; + // FIXME: Make this into a real type op? self.fully_perform_op( location.to_locations(), ConstraintCategory::Boring, @@ -214,6 +216,40 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } #[instrument(skip(self), level = "debug")] + pub(super) fn structurally_resolve( + &mut self, + ty: Ty<'tcx>, + location: impl NormalizeLocation, + ) -> Ty<'tcx> { + if self.infcx.next_trait_solver() { + let body = self.body; + let param_env = self.infcx.param_env; + // FIXME: Make this into a real type op? + self.fully_perform_op( + location.to_locations(), + ConstraintCategory::Boring, + CustomTypeOp::new( + |ocx| { + ocx.structurally_normalize( + &ObligationCause::misc( + location.to_locations().span(body), + body.source.def_id().expect_local(), + ), + param_env, + ty, + ) + .map_err(|_| NoSolution) + }, + "normalizing struct tail", + ), + ) + .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar)) + } else { + self.normalize(ty, location) + } + } + + #[instrument(skip(self), level = "debug")] pub(super) fn ascribe_user_type( &mut self, mir_ty: Ty<'tcx>, diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 67915371b1f..918efac2a20 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -8,6 +8,7 @@ use rustc_middle::bug; use rustc_middle::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory}; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; +use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_span::Span; use rustc_trait_selection::traits::ScrubbedTraitError; @@ -216,7 +217,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { /// are dealt with during trait solving. fn replace_placeholders_with_nll<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T { if value.has_placeholders() { - self.tcx.fold_regions(value, |r, _| match *r { + fold_regions(self.tcx, value, |r, _| match *r { ty::RePlaceholder(placeholder) => { self.constraints.placeholder_region(self.infcx, placeholder) } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 3a7ed711f68..89e683b8ae3 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -26,6 +26,7 @@ use rustc_middle::mir::*; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::cast::CastTy; +use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{ self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt, @@ -213,7 +214,7 @@ pub(crate) fn type_check<'a, 'tcx>( // Convert all regions to nll vars. let (opaque_type_key, hidden_type) = - infcx.tcx.fold_regions((opaque_type_key, hidden_type), |region, _| { + fold_regions(infcx.tcx, (opaque_type_key, hidden_type), |region, _| { match region.kind() { ty::ReVar(_) => region, ty::RePlaceholder(placeholder) => { @@ -1064,7 +1065,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let tcx = self.infcx.tcx; for proj in &user_ty.projs { - if let ty::Alias(ty::Opaque, ..) = curr_projected_ty.ty.kind() { + if !self.infcx.next_trait_solver() + && let ty::Alias(ty::Opaque, ..) = curr_projected_ty.ty.kind() + { // There is nothing that we can compare here if we go through an opaque type. // We're always in its defining scope as we can otherwise not project through // it, so we're constraining it anyways. @@ -1075,7 +1078,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { proj, |this, field, ()| { let ty = this.field_ty(tcx, field); - self.normalize(ty, locations) + self.structurally_resolve(ty, locations) }, |_, _| unreachable!(), ); @@ -2071,7 +2074,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); let is_implicit_coercion = coercion_source == CoercionSource::Implicit; - let unsize_to = tcx.fold_regions(ty, |r, _| { + let unsize_to = fold_regions(tcx, ty, |r, _| { if let ty::ReVar(_) = r.kind() { tcx.lifetimes.re_erased } else { r } }); self.prove_trait_ref( diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index f1c23aa26a9..bb64d646ff3 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -26,15 +26,15 @@ use rustc_hir::lang_items::LangItem; use rustc_index::IndexVec; use rustc_infer::infer::NllRegionVariableOrigin; use rustc_macros::extension; -use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::fold::{TypeFoldable, fold_regions}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ self, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, RegionVid, Ty, TyCtxt, TypeVisitableExt, }; use rustc_middle::{bug, span_bug}; +use rustc_span::ErrorGuaranteed; use rustc_span::symbol::{kw, sym}; -use rustc_span::{ErrorGuaranteed, Symbol}; use tracing::{debug, instrument}; use crate::BorrowckInferCtxt; @@ -524,7 +524,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let reg_vid = self .infcx - .next_nll_region_var(FR, || RegionCtxt::Free(Symbol::intern("c-variadic"))) + .next_nll_region_var(FR, || RegionCtxt::Free(sym::c_dash_variadic)) .as_var(); let region = ty::Region::new_var(self.infcx.tcx, reg_vid); @@ -540,10 +540,8 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { } } - let fr_fn_body = self - .infcx - .next_nll_region_var(FR, || RegionCtxt::Free(Symbol::intern("fn_body"))) - .as_var(); + let fr_fn_body = + self.infcx.next_nll_region_var(FR, || RegionCtxt::Free(sym::fn_body)).as_var(); let num_universals = self.infcx.num_region_vars(); @@ -824,7 +822,7 @@ impl<'tcx> BorrowckInferCtxt<'tcx> { where T: TypeFoldable<TyCtxt<'tcx>>, { - self.infcx.tcx.fold_regions(value, |region, _depth| { + fold_regions(self.infcx.tcx, value, |region, _depth| { let name = region.get_name_or_anon(); debug!(?region, ?name); @@ -906,7 +904,7 @@ impl<'tcx> UniversalRegionIndices<'tcx> { where T: TypeFoldable<TyCtxt<'tcx>>, { - tcx.fold_regions(value, |region, _| ty::Region::new_var(tcx, self.to_region_vid(region))) + fold_regions(tcx, value, |region, _| ty::Region::new_var(tcx, self.to_region_vid(region))) } } diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 6ebc2fd870c..87d3d288013 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -94,6 +94,21 @@ builtin_macros_cfg_accessible_indeterminate = cannot determine whether the path builtin_macros_cfg_accessible_literal_path = `cfg_accessible` path cannot be a literal builtin_macros_cfg_accessible_multiple_paths = multiple `cfg_accessible` paths are specified builtin_macros_cfg_accessible_unspecified_path = `cfg_accessible` path is not specified + +builtin_macros_coerce_pointee_requires_maybe_sized = `derive(CoercePointee)` requires `{$name}` to be marked `?Sized` + +builtin_macros_coerce_pointee_requires_one_field = `CoercePointee` can only be derived on `struct`s with at least one field + +builtin_macros_coerce_pointee_requires_one_generic = `CoercePointee` can only be derived on `struct`s that are generic over at least one type + +builtin_macros_coerce_pointee_requires_one_pointee = exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits + +builtin_macros_coerce_pointee_requires_transparent = `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` + +builtin_macros_coerce_pointee_too_many_pointees = only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits + .label = here another type parameter is marked as `#[pointee]` + + builtin_macros_concat_bytes_array = cannot concatenate doubly nested array .note = byte strings are treated as arrays of bytes .help = try flattening the array @@ -246,7 +261,7 @@ builtin_macros_non_exhaustive_default = default variant must be exhaustive builtin_macros_non_generic_pointee = the `#[pointee]` attribute may only be used on generic parameters -builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants +builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants{$post} .help = consider a manual implementation of `Default` builtin_macros_only_one_argument = {$name} takes 1 argument diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 53e938ee216..3bd8f899a4a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -9,6 +9,7 @@ use rustc_ast::{ use rustc_attr as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_macros::Diagnostic; use rustc_span::symbol::{Ident, sym}; use rustc_span::{Span, Symbol}; use thin_vec::{ThinVec, thin_vec}; @@ -38,12 +39,7 @@ pub(crate) fn expand_deriving_coerce_pointee( .any(|r| matches!(r, attr::ReprTransparent)) }); if !is_transparent { - cx.dcx() - .struct_span_err( - span, - "`CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`", - ) - .emit(); + cx.dcx().emit_err(RequireTransparent { span }); return; } if !matches!( @@ -51,22 +47,12 @@ pub(crate) fn expand_deriving_coerce_pointee( VariantData::Struct { fields, recovered: _ } | VariantData::Tuple(fields, _) if !fields.is_empty()) { - cx.dcx() - .struct_span_err( - span, - "`CoercePointee` can only be derived on `struct`s with at least one field", - ) - .emit(); + cx.dcx().emit_err(RequireOneField { span }); return; } (aitem.ident, g) } else { - cx.dcx() - .struct_span_err( - span, - "`CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`", - ) - .emit(); + cx.dcx().emit_err(RequireTransparent { span }); return; }; @@ -95,10 +81,7 @@ pub(crate) fn expand_deriving_coerce_pointee( let pointee_param_idx = if type_params.is_empty() { // `#[derive(CoercePointee)]` requires at least one generic type on the target `struct` - cx.dcx().struct_span_err( - span, - "`CoercePointee` can only be derived on `struct`s that are generic over at least one type", - ).emit(); + cx.dcx().emit_err(RequireOneGeneric { span }); return; } else if type_params.len() == 1 { // Regardless of the only type param being designed as `#[pointee]` or not, we can just use it as such @@ -111,19 +94,11 @@ pub(crate) fn expand_deriving_coerce_pointee( match (pointees.next(), pointees.next()) { (Some((idx, _span)), None) => idx, (None, _) => { - cx.dcx().struct_span_err( - span, - "exactly one generic type parameter must be marked as #[pointee] to derive CoercePointee traits", - ).emit(); + cx.dcx().emit_err(RequireOnePointee { span }); return; } (Some((_, one)), Some((_, another))) => { - cx.dcx() - .struct_span_err( - vec![one, another], - "only one type parameter can be marked as `#[pointee]` when deriving CoercePointee traits", - ) - .emit(); + cx.dcx().emit_err(TooManyPointees { one, another }); return; } } @@ -181,15 +156,10 @@ pub(crate) fn expand_deriving_coerce_pointee( pointee_ty_ident.name, ) { - cx.dcx() - .struct_span_err( - pointee_ty_ident.span, - format!( - "`derive(CoercePointee)` requires {} to be marked `?Sized`", - pointee_ty_ident.name - ), - ) - .emit(); + cx.dcx().emit_err(RequiresMaybeSized { + span: pointee_ty_ident.span, + name: pointee_ty_ident.name.to_ident_string(), + }); return; } let arg = GenericArg::Type(s_ty.clone()); @@ -288,19 +258,18 @@ pub(crate) fn expand_deriving_coerce_pointee( // // We should also write a few new `where` bounds from `#[pointee] T` to `__S` // as well as any bound that indirectly involves the `#[pointee] T` type. - for bound in &generics.where_clause.predicates { - if let ast::WherePredicate::BoundPredicate(bound) = bound { + for predicate in &generics.where_clause.predicates { + if let ast::WherePredicateKind::BoundPredicate(bound) = &predicate.kind { let mut substitution = TypeSubstitution { from_name: pointee_ty_ident.name, to_ty: &s_ty, rewritten: false, }; - let mut predicate = ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { - span: bound.span, - bound_generic_params: bound.bound_generic_params.clone(), - bounded_ty: bound.bounded_ty.clone(), - bounds: bound.bounds.clone(), - }); + let mut predicate = ast::WherePredicate { + kind: ast::WherePredicateKind::BoundPredicate(bound.clone()), + span: predicate.span, + id: ast::DUMMY_NODE_ID, + }; substitution.visit_where_predicate(&mut predicate); if substitution.rewritten { impl_generics.where_clause.predicates.push(predicate); @@ -319,7 +288,7 @@ pub(crate) fn expand_deriving_coerce_pointee( fn contains_maybe_sized_bound_on_pointee(predicates: &[WherePredicate], pointee: Symbol) -> bool { for bound in predicates { - if let ast::WherePredicate::BoundPredicate(bound) = bound + if let ast::WherePredicateKind::BoundPredicate(bound) = &bound.kind && bound.bounded_ty.kind.is_simple_path().is_some_and(|name| name == pointee) { for bound in &bound.bounds { @@ -385,8 +354,8 @@ impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> { } fn visit_where_predicate(&mut self, where_predicate: &mut ast::WherePredicate) { - match where_predicate { - rustc_ast::WherePredicate::BoundPredicate(bound) => { + match &mut where_predicate.kind { + rustc_ast::WherePredicateKind::BoundPredicate(bound) => { bound .bound_generic_params .flat_map_in_place(|param| self.flat_map_generic_param(param)); @@ -395,8 +364,8 @@ impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> { self.visit_param_bound(bound, BoundKind::Bound) } } - rustc_ast::WherePredicate::RegionPredicate(_) - | rustc_ast::WherePredicate::EqPredicate(_) => {} + rustc_ast::WherePredicateKind::RegionPredicate(_) + | rustc_ast::WherePredicateKind::EqPredicate(_) => {} } } } @@ -460,3 +429,48 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b> } } } + +#[derive(Diagnostic)] +#[diag(builtin_macros_coerce_pointee_requires_transparent)] +struct RequireTransparent { + #[primary_span] + span: Span, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_coerce_pointee_requires_one_field)] +struct RequireOneField { + #[primary_span] + span: Span, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_coerce_pointee_requires_one_generic)] +struct RequireOneGeneric { + #[primary_span] + span: Span, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_coerce_pointee_requires_one_pointee)] +struct RequireOnePointee { + #[primary_span] + span: Span, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_coerce_pointee_too_many_pointees)] +struct TooManyPointees { + #[primary_span] + one: Span, + #[label] + another: Span, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_coerce_pointee_requires_maybe_sized)] +struct RequiresMaybeSized { + #[primary_span] + span: Span, + name: String, +} diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index 686424770fc..469092e7b1c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -205,7 +205,7 @@ where let fields = fields .iter() .enumerate() - .map(|(i, &(ident, span))| { + .map(|(i, &(ident, span, _))| { let arg = getarg(cx, span, ident.name, i); cx.field_imm(span, ident, arg) }) diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index d4befd12190..6b1a6effad7 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -54,26 +54,38 @@ pub(crate) fn expand_deriving_default( trait_def.expand(cx, mitem, item, push) } +fn default_call(cx: &ExtCtxt<'_>, span: Span) -> ast::ptr::P<ast::Expr> { + // Note that `kw::Default` is "default" and `sym::Default` is "Default"! + let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]); + cx.expr_call_global(span, default_ident, ThinVec::new()) +} + fn default_struct_substructure( cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, summary: &StaticFields, ) -> BlockOrExpr { - // Note that `kw::Default` is "default" and `sym::Default` is "Default"! - let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]); - let default_call = |span| cx.expr_call_global(span, default_ident.clone(), ThinVec::new()); - let expr = match summary { Unnamed(_, IsTuple::No) => cx.expr_ident(trait_span, substr.type_ident), Unnamed(fields, IsTuple::Yes) => { - let exprs = fields.iter().map(|sp| default_call(*sp)).collect(); + let exprs = fields.iter().map(|sp| default_call(cx, *sp)).collect(); cx.expr_call_ident(trait_span, substr.type_ident, exprs) } Named(fields) => { let default_fields = fields .iter() - .map(|&(ident, span)| cx.field_imm(span, ident, default_call(span))) + .map(|(ident, span, default_val)| { + let value = match default_val { + // We use `Default::default()`. + None => default_call(cx, *span), + // We use the field default const expression. + Some(val) => { + cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone())) + } + }; + cx.field_imm(*span, *ident, value) + }) .collect(); cx.expr_struct_ident(trait_span, substr.type_ident, default_fields) } @@ -93,10 +105,38 @@ fn default_enum_substructure( } { Ok(default_variant) => { // We now know there is exactly one unit variant with exactly one `#[default]` attribute. - cx.expr_path(cx.path(default_variant.span, vec![ - Ident::new(kw::SelfUpper, default_variant.span), - default_variant.ident, - ])) + match &default_variant.data { + VariantData::Unit(_) => cx.expr_path(cx.path(default_variant.span, vec![ + Ident::new(kw::SelfUpper, default_variant.span), + default_variant.ident, + ])), + VariantData::Struct { fields, .. } => { + // This only happens if `#![feature(default_field_values)]`. We have validated + // all fields have default values in the definition. + let default_fields = fields + .iter() + .map(|field| { + cx.field_imm(field.span, field.ident.unwrap(), match &field.default { + // We use `Default::default()`. + None => default_call(cx, field.span), + // We use the field default const expression. + Some(val) => { + cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone())) + } + }) + }) + .collect(); + let path = cx.path(default_variant.span, vec![ + Ident::new(kw::SelfUpper, default_variant.span), + default_variant.ident, + ]); + cx.expr_struct(default_variant.span, path, default_fields) + } + // Logic error in `extract_default_variant`. + VariantData::Tuple(..) => { + cx.dcx().bug("encountered tuple variant annotated with `#[default]`") + } + } } Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)), }; @@ -156,8 +196,20 @@ fn extract_default_variant<'a>( } }; - if !matches!(variant.data, VariantData::Unit(..)) { - let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span }); + if cx.ecfg.features.default_field_values() + && let VariantData::Struct { fields, .. } = &variant.data + && fields.iter().all(|f| f.default.is_some()) + // Disallow `#[default] Variant {}` + && !fields.is_empty() + { + // Allowed + } else if !matches!(variant.data, VariantData::Unit(..)) { + let post = if cx.ecfg.features.default_field_values() { + " or variants where every field has a default value" + } else { + "" + }; + let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span, post }); return Err(guar); } @@ -216,7 +268,12 @@ struct DetectNonVariantDefaultAttr<'a, 'b> { impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> { fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) { if attr.has_name(kw::Default) { - self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span }); + let post = if self.cx.ecfg.features.default_field_values() { + " or variants where every field has a default value" + } else { + "" + }; + self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span, post }); } rustc_ast::visit::walk_attribute(self, attr); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 79c198ed2d0..846d8784dea 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -182,8 +182,8 @@ pub(crate) use StaticFields::*; pub(crate) use SubstructureFields::*; use rustc_ast::ptr::P; use rustc_ast::{ - self as ast, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics, - Mutability, PatKind, VariantData, + self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, + Generics, Mutability, PatKind, VariantData, }; use rustc_attr as attr; use rustc_expand::base::{Annotatable, ExtCtxt}; @@ -296,7 +296,7 @@ pub(crate) enum StaticFields { /// Tuple and unit structs/enum variants like this. Unnamed(Vec<Span>, IsTuple), /// Normal structs/struct variants. - Named(Vec<(Ident, Span)>), + Named(Vec<(Ident, Span, Option<AnonConst>)>), } /// A summary of the possible sets of fields. @@ -690,25 +690,10 @@ impl<'a> TraitDef<'a> { // and similarly for where clauses where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| { - match clause { - ast::WherePredicate::BoundPredicate(wb) => { - let span = wb.span.with_ctxt(ctxt); - ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { - span, - ..wb.clone() - }) - } - ast::WherePredicate::RegionPredicate(wr) => { - let span = wr.span.with_ctxt(ctxt); - ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { - span, - ..wr.clone() - }) - } - ast::WherePredicate::EqPredicate(we) => { - let span = we.span.with_ctxt(ctxt); - ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { span, ..we.clone() }) - } + ast::WherePredicate { + kind: clause.kind.clone(), + id: ast::DUMMY_NODE_ID, + span: clause.span.with_ctxt(ctxt), } })); @@ -757,13 +742,14 @@ impl<'a> TraitDef<'a> { if !bounds.is_empty() { let predicate = ast::WhereBoundPredicate { - span: self.span, bound_generic_params: field_ty_param.bound_generic_params, bounded_ty: field_ty_param.ty, bounds, }; - let predicate = ast::WherePredicate::BoundPredicate(predicate); + let kind = ast::WherePredicateKind::BoundPredicate(predicate); + let predicate = + ast::WherePredicate { kind, id: ast::DUMMY_NODE_ID, span: self.span }; where_clause.predicates.push(predicate); } } @@ -1449,7 +1435,7 @@ impl<'a> TraitDef<'a> { for field in struct_def.fields() { let sp = field.span.with_ctxt(self.span.ctxt()); match field.ident { - Some(ident) => named_idents.push((ident, sp)), + Some(ident) => named_idents.push((ident, sp, field.default.clone())), _ => just_spans.push(sp), } } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index f8e65661e52..c9bd3371e55 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -424,6 +424,7 @@ pub(crate) struct MultipleDefaultsSugg { pub(crate) struct NonUnitDefault { #[primary_span] pub(crate) span: Span, + pub(crate) post: &'static str, } #[derive(Diagnostic)] diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 9eee92164cf..cc4a974e757 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -5,10 +5,10 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -#![cfg_attr(not(bootstrap), feature(autodiff))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] +#![feature(autodiff)] #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index e4f77472802..d81e7214961 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -46,24 +46,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5e7afe85cadb55c4c1176268a2ac046fdff8dfaeca39e18581b9dc319ca9e" +checksum = "2ba4f80548f22dc9c43911907b5e322c5555544ee85f785115701e6a28c9abe1" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ab25ef3be935a80680e393183e1f94ef507e93a24a8369494d2c6818aedb3e3" +checksum = "005884e3649c3e5ff2dc79e8a94b138f11569cc08a91244a292714d2a86e9156" [[package]] name = "cranelift-codegen" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900a19b84545924f1851cbfe386962edfc4ecbc3366a254825cf1ecbcda8ba08" +checksum = "fe4036255ec33ce9a37495dfbcfc4e1118fd34e693eff9a1e106336b7cd16a9b" dependencies = [ "bumpalo", "cranelift-bforest", @@ -78,48 +78,49 @@ dependencies = [ "log", "regalloc2", "rustc-hash", + "serde", "smallvec", "target-lexicon", ] [[package]] name = "cranelift-codegen-meta" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c73b2395ffe9e7b4fdf7e2ebc052e7e27af13f68a964985346be4da477a5fc" +checksum = "f7ca74f4b68319da11d39e894437cb6e20ec7c2e11fbbda823c3bf207beedff7" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d9ed0854e96a4ff0879bff39d078de8dea7f002721c9494c1fdb4e1baa86ccc" +checksum = "897e54f433a0269c4187871aa06d452214d5515d228d5bdc22219585e9eef895" [[package]] name = "cranelift-control" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4aca921dd422e781409de0129c255768fec5dec1dae83239b497fb9138abb89" +checksum = "29cb4018f5bf59fb53f515fa9d80e6f8c5ce19f198dc538984ebd23ecf8965ec" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d770e6605eccee15b49decdd82cd26f2b6404767802471459ea49c57379a98" +checksum = "305399fd781a2953ac78c1396f02ff53144f39c33eb7fc7789cf4e8936d13a96" dependencies = [ "cranelift-bitset", ] [[package]] name = "cranelift-frontend" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29268711cb889cb39215b10faf88b9087d4c9e1d2633581e4f722a2bf4bb4ef9" +checksum = "9230b460a128d53653456137751d27baf567947a3ab8c0c4d6e31fd08036d81e" dependencies = [ "cranelift-codegen", "log", @@ -129,15 +130,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc65156f010aed1985767ad1bff0eb8d186743b7b03e23d0c17604a253e3f356" +checksum = "b961e24ae3ec9813a24a15ae64bbd2a42e4de4d79a7f3225a412e3b94e78d1c8" [[package]] name = "cranelift-jit" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ba6b46367a4f466cfb1abe32793fa1a0f96d862251491b01a44726b8ed9445" +checksum = "62699329d4ced20fe281fbaef45e11b473b7ab310491b4bdebcd8b818a8ef7fe" dependencies = [ "anyhow", "cranelift-codegen", @@ -155,9 +156,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "007607022a4883ebdffc46c0925e2e10babf2a565ae78518034ade722aa825d2" +checksum = "2f20b0b51ba962dac30fc7e812b86e4390d908acd4f59bcc8ac7610a8f3e0977" dependencies = [ "anyhow", "cranelift-codegen", @@ -166,9 +167,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bf9b361eaf5a7627647270fabf1dc910d993edbeaf272a652c107861ebe9c2" +checksum = "4d5bd76df6c9151188dfa428c863b33da5b34561b67f43c0cf3f24a794f9fa1f" dependencies = [ "cranelift-codegen", "libc", @@ -177,9 +178,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca5c38fa00c0cd943035391bdcc84ed00748f17c66c682e410f5a62f234d44" +checksum = "ee231640a7ecceedd0f1f2782d9288db6a6908cc70675ed9427e3bf0ea6daacd" dependencies = [ "anyhow", "cranelift-codegen", @@ -364,6 +365,26 @@ dependencies = [ ] [[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "slice-group-by" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -412,9 +433,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasmtime-jit-icache-coherence" -version = "26.0.0" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e458e6a1a010a53f86ac8d75837c0c6b2ce3e54b7503b2f1dc5629a4a541f5a" +checksum = "91b218a92866f74f35162f5d03a4e0f62cd0e1cc624285b1014275e5d4575fad" dependencies = [ "anyhow", "cfg-if", diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index f352ef72cb0..b2fed3c490e 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,12 +8,12 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.113.0", default-features = false, features = ["std", "unwind", "all-native-arch"] } -cranelift-frontend = { version = "0.113.0" } -cranelift-module = { version = "0.113.0" } -cranelift-native = { version = "0.113.0" } -cranelift-jit = { version = "0.113.0", optional = true } -cranelift-object = { version = "0.113.0" } +cranelift-codegen = { version = "0.114.0", default-features = false, features = ["std", "unwind", "all-native-arch"] } +cranelift-frontend = { version = "0.114.0" } +cranelift-module = { version = "0.114.0" } +cranelift-native = { version = "0.114.0" } +cranelift-jit = { version = "0.114.0", optional = true } +cranelift-object = { version = "0.114.0" } target-lexicon = "0.12.0" gimli = { version = "0.31", default-features = false, features = ["write"] } object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } diff --git a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs index 9292778806a..674acfbd309 100644 --- a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs +++ b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs @@ -1,4 +1,4 @@ -use crate::path::{Dirs, RelPath}; +use crate::path::Dirs; use crate::prepare::GitRepo; use crate::utils::{CargoProject, Compiler, spawn_and_wait}; use crate::{CodegenBackend, SysrootKind, build_sysroot}; @@ -20,7 +20,7 @@ pub(crate) fn run( rustup_toolchain_name: Option<&str>, bootstrap_host_compiler: &Compiler, ) { - RelPath::DOWNLOAD.ensure_exists(dirs); + std::fs::create_dir_all(&dirs.download_dir).unwrap(); ABI_CAFE_REPO.fetch(dirs); ABI_CAFE_REPO.patch(dirs); diff --git a/compiler/rustc_codegen_cranelift/build_system/bench.rs b/compiler/rustc_codegen_cranelift/build_system/bench.rs index ebeb6772250..73a0f325fc2 100644 --- a/compiler/rustc_codegen_cranelift/build_system/bench.rs +++ b/compiler/rustc_codegen_cranelift/build_system/bench.rs @@ -3,7 +3,7 @@ use std::io::Write; use std::path::Path; use std::process::Command; -use crate::path::{Dirs, RelPath}; +use crate::path::Dirs; use crate::prepare::GitRepo; use crate::rustc_info::get_file_name; use crate::utils::{Compiler, spawn_and_wait}; @@ -39,11 +39,11 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { }; eprintln!("[BENCH COMPILE] ebobby/simple-raytracer"); - let cargo_clif = RelPath::DIST - .to_path(dirs) + let cargo_clif = dirs + .dist_dir .join(get_file_name(&bootstrap_host_compiler.rustc, "cargo_clif", "bin").replace('_', "-")); let manifest_path = SIMPLE_RAYTRACER_REPO.source_dir().to_path(dirs).join("Cargo.toml"); - let target_dir = RelPath::BUILD.join("simple_raytracer").to_path(dirs); + let target_dir = dirs.build_dir.join("simple_raytracer"); let clean_cmd = format!( "RUSTC=rustc cargo clean --manifest-path {manifest_path} --target-dir {target_dir}", @@ -68,7 +68,7 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { target_dir = target_dir.display(), ); - let bench_compile_markdown = RelPath::DIST.to_path(dirs).join("bench_compile.md"); + let bench_compile_markdown = dirs.dist_dir.join("bench_compile.md"); let bench_compile = hyperfine_command( 1, @@ -92,7 +92,7 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { eprintln!("[BENCH RUN] ebobby/simple-raytracer"); - let bench_run_markdown = RelPath::DIST.to_path(dirs).join("bench_run.md"); + let bench_run_markdown = dirs.dist_dir.join("bench_run.md"); let raytracer_cg_llvm = Path::new(".").join(get_file_name( &bootstrap_host_compiler.rustc, @@ -120,7 +120,7 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { ], &bench_run_markdown, ); - bench_run.current_dir(RelPath::BUILD.to_path(dirs)); + bench_run.current_dir(&dirs.build_dir); spawn_and_wait(bench_run); if let Some(gha_step_summary) = gha_step_summary.as_mut() { diff --git a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs index 02da89f737c..72bc422523d 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs @@ -6,7 +6,7 @@ use crate::rustc_info::get_file_name; use crate::shared_utils::{rustflags_from_env, rustflags_to_cmd_env}; use crate::utils::{CargoProject, Compiler, LogGroup}; -static CG_CLIF: CargoProject = CargoProject::new(&RelPath::SOURCE, "cg_clif"); +static CG_CLIF: CargoProject = CargoProject::new(&RelPath::source("."), "cg_clif"); pub(crate) fn build_backend( dirs: &Dirs, diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index f1f4489bcbc..e47e9829916 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -22,9 +22,9 @@ pub(crate) fn build_sysroot( eprintln!("[BUILD] sysroot {:?}", sysroot_kind); - let dist_dir = RelPath::DIST.to_path(dirs); + let dist_dir = &dirs.dist_dir; - ensure_empty_dir(&dist_dir); + ensure_empty_dir(dist_dir); fs::create_dir_all(dist_dir.join("bin")).unwrap(); fs::create_dir_all(dist_dir.join("lib")).unwrap(); @@ -55,7 +55,7 @@ pub(crate) fn build_sysroot( let mut build_cargo_wrapper_cmd = Command::new(&bootstrap_host_compiler.rustc); let wrapper_path = dist_dir.join(&wrapper_name); build_cargo_wrapper_cmd - .arg(RelPath::SCRIPTS.to_path(dirs).join(&format!("{wrapper}.rs"))) + .arg(dirs.source_dir.join("scripts").join(&format!("{wrapper}.rs"))) .arg("-o") .arg(&wrapper_path) .arg("-Cstrip=debuginfo"); @@ -85,7 +85,7 @@ pub(crate) fn build_sysroot( &cg_clif_dylib_path, sysroot_kind, ); - host.install_into_sysroot(&dist_dir); + host.install_into_sysroot(dist_dir); if !is_native { build_sysroot_for_triple( @@ -99,7 +99,7 @@ pub(crate) fn build_sysroot( &cg_clif_dylib_path, sysroot_kind, ) - .install_into_sysroot(&dist_dir); + .install_into_sysroot(dist_dir); } let mut target_compiler = { @@ -143,10 +143,10 @@ impl SysrootTarget { } } -static STDLIB_SRC: RelPath = RelPath::BUILD.join("stdlib"); +static STDLIB_SRC: RelPath = RelPath::build("stdlib"); static STANDARD_LIBRARY: CargoProject = - CargoProject::new(&STDLIB_SRC.join("library/sysroot"), "stdlib_target"); -static RTSTARTUP_SYSROOT: RelPath = RelPath::BUILD.join("rtstartup"); + CargoProject::new(&RelPath::build("stdlib/library/sysroot"), "stdlib_target"); +static RTSTARTUP_SYSROOT: RelPath = RelPath::build("rtstartup"); fn build_sysroot_for_triple( dirs: &Dirs, @@ -247,6 +247,7 @@ fn build_clif_sysroot_for_triple( let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs); build_cmd.arg("--release"); build_cmd.arg("--features").arg("backtrace panic-unwind compiler-builtins-no-f16-f128"); + build_cmd.arg(format!("-Zroot-dir={}", STDLIB_SRC.to_path(dirs).display())); build_cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "true"); build_cmd.env("__CARGO_DEFAULT_LIB_METADATA", "cg_clif"); if compiler.triple.contains("apple") { @@ -281,13 +282,14 @@ fn build_rtstartup(dirs: &Dirs, compiler: &Compiler) -> Option<SysrootTarget> { return None; } - RTSTARTUP_SYSROOT.ensure_fresh(dirs); + let rtstartup_sysroot = RTSTARTUP_SYSROOT.to_path(dirs); + ensure_empty_dir(&rtstartup_sysroot); let rtstartup_src = STDLIB_SRC.to_path(dirs).join("library").join("rtstartup"); let mut target_libs = SysrootTarget { triple: compiler.triple.clone(), libs: vec![] }; for file in ["rsbegin", "rsend"] { - let obj = RTSTARTUP_SYSROOT.to_path(dirs).join(format!("{file}.o")); + let obj = rtstartup_sysroot.join(format!("{file}.o")); let mut build_rtstartup_cmd = Command::new(&compiler.rustc); build_rtstartup_cmd .arg("--target") diff --git a/compiler/rustc_codegen_cranelift/build_system/main.rs b/compiler/rustc_codegen_cranelift/build_system/main.rs index b68ac7c0926..99e6146657f 100644 --- a/compiler/rustc_codegen_cranelift/build_system/main.rs +++ b/compiler/rustc_codegen_cranelift/build_system/main.rs @@ -185,12 +185,11 @@ fn main() { frozen, }; - path::RelPath::BUILD.ensure_exists(&dirs); + std::fs::create_dir_all(&dirs.build_dir).unwrap(); { // Make sure we always explicitly specify the target dir - let target = - path::RelPath::BUILD.join("target_dir_should_be_set_explicitly").to_path(&dirs); + let target = dirs.build_dir.join("target_dir_should_be_set_explicitly"); env::set_var("CARGO_TARGET_DIR", &target); let _ = std::fs::remove_file(&target); std::fs::File::create(target).unwrap(); diff --git a/compiler/rustc_codegen_cranelift/build_system/path.rs b/compiler/rustc_codegen_cranelift/build_system/path.rs index 35e7e81c528..20a81156b71 100644 --- a/compiler/rustc_codegen_cranelift/build_system/path.rs +++ b/compiler/rustc_codegen_cranelift/build_system/path.rs @@ -1,8 +1,5 @@ -use std::fs; use std::path::PathBuf; -use crate::utils::ensure_empty_dir; - #[derive(Debug, Clone)] pub(crate) struct Dirs { pub(crate) source_dir: PathBuf, @@ -16,54 +13,34 @@ pub(crate) struct Dirs { #[derive(Debug, Copy, Clone)] pub(crate) enum PathBase { Source, - Download, Build, - Dist, } impl PathBase { fn to_path(self, dirs: &Dirs) -> PathBuf { match self { PathBase::Source => dirs.source_dir.clone(), - PathBase::Download => dirs.download_dir.clone(), PathBase::Build => dirs.build_dir.clone(), - PathBase::Dist => dirs.dist_dir.clone(), } } } #[derive(Debug, Copy, Clone)] -pub(crate) enum RelPath { - Base(PathBase), - Join(&'static RelPath, &'static str), +pub(crate) struct RelPath { + base: PathBase, + suffix: &'static str, } impl RelPath { - pub(crate) const SOURCE: RelPath = RelPath::Base(PathBase::Source); - pub(crate) const DOWNLOAD: RelPath = RelPath::Base(PathBase::Download); - pub(crate) const BUILD: RelPath = RelPath::Base(PathBase::Build); - pub(crate) const DIST: RelPath = RelPath::Base(PathBase::Dist); - - pub(crate) const SCRIPTS: RelPath = RelPath::SOURCE.join("scripts"); - pub(crate) const PATCHES: RelPath = RelPath::SOURCE.join("patches"); - - pub(crate) const fn join(&'static self, suffix: &'static str) -> RelPath { - RelPath::Join(self, suffix) + pub(crate) const fn source(suffix: &'static str) -> RelPath { + RelPath { base: PathBase::Source, suffix } } - pub(crate) fn to_path(&self, dirs: &Dirs) -> PathBuf { - match self { - RelPath::Base(base) => base.to_path(dirs), - RelPath::Join(base, suffix) => base.to_path(dirs).join(suffix), - } - } - - pub(crate) fn ensure_exists(&self, dirs: &Dirs) { - fs::create_dir_all(self.to_path(dirs)).unwrap(); + pub(crate) const fn build(suffix: &'static str) -> RelPath { + RelPath { base: PathBase::Build, suffix } } - pub(crate) fn ensure_fresh(&self, dirs: &Dirs) { - let path = self.to_path(dirs); - ensure_empty_dir(&path); + pub(crate) fn to_path(&self, dirs: &Dirs) -> PathBuf { + self.base.to_path(dirs).join(self.suffix) } } diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs index c6f979f0278..a4e9cb5f5c8 100644 --- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs +++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs @@ -8,7 +8,7 @@ use crate::path::{Dirs, RelPath}; use crate::utils::{copy_dir_recursively, ensure_empty_dir, spawn_and_wait}; pub(crate) fn prepare(dirs: &Dirs) { - RelPath::DOWNLOAD.ensure_exists(dirs); + std::fs::create_dir_all(&dirs.download_dir).unwrap(); crate::tests::RAND_REPO.fetch(dirs); crate::tests::REGEX_REPO.fetch(dirs); } @@ -79,13 +79,13 @@ impl GitRepo { fn download_dir(&self, dirs: &Dirs) -> PathBuf { match self.url { - GitRepoUrl::Github { user: _, repo } => RelPath::DOWNLOAD.join(repo).to_path(dirs), + GitRepoUrl::Github { user: _, repo } => dirs.download_dir.join(repo), } } pub(crate) const fn source_dir(&self) -> RelPath { match self.url { - GitRepoUrl::Github { user: _, repo } => RelPath::BUILD.join(repo), + GitRepoUrl::Github { user: _, repo } => RelPath::build(repo), } } @@ -130,7 +130,7 @@ impl GitRepo { } let source_lockfile = - RelPath::PATCHES.to_path(dirs).join(format!("{}-lock.toml", self.patch_name)); + dirs.source_dir.join("patches").join(format!("{}-lock.toml", self.patch_name)); let target_lockfile = download_dir.join("Cargo.lock"); if source_lockfile.exists() { assert!(!target_lockfile.exists()); @@ -191,7 +191,7 @@ fn init_git_repo(repo_dir: &Path) { } fn get_patches(dirs: &Dirs, crate_name: &str) -> Vec<PathBuf> { - let mut patches: Vec<_> = fs::read_dir(RelPath::PATCHES.to_path(dirs)) + let mut patches: Vec<_> = fs::read_dir(dirs.source_dir.join("patches")) .unwrap() .map(|entry| entry.unwrap().path()) .filter(|path| path.extension() == Some(OsStr::new("patch"))) diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index fd94e80f631..08736db8ba0 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -7,10 +7,10 @@ use crate::path::{Dirs, RelPath}; use crate::prepare::{GitRepo, apply_patches}; use crate::rustc_info::get_default_sysroot; use crate::shared_utils::rustflags_from_env; -use crate::utils::{CargoProject, Compiler, LogGroup, spawn_and_wait}; +use crate::utils::{CargoProject, Compiler, LogGroup, ensure_empty_dir, spawn_and_wait}; use crate::{CodegenBackend, SysrootKind, build_sysroot, config}; -static BUILD_EXAMPLE_OUT_DIR: RelPath = RelPath::BUILD.join("example"); +static BUILD_EXAMPLE_OUT_DIR: RelPath = RelPath::build("example"); struct TestCase { config: &'static str, @@ -92,10 +92,6 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ TestCase::build_bin_and_run("aot.mod_bench", "example/mod_bench.rs", &[]), TestCase::build_bin_and_run("aot.issue-72793", "example/issue-72793.rs", &[]), TestCase::build_bin("aot.issue-59326", "example/issue-59326.rs"), - TestCase::custom("aot.polymorphize_coroutine", &|runner| { - runner.run_rustc(&["example/polymorphize_coroutine.rs", "-Zpolymorphize"]); - runner.run_out_command("polymorphize_coroutine", &[]); - }), TestCase::build_bin_and_run("aot.neon", "example/neon.rs", &[]), TestCase::custom("aot.gen_block_iterate", &|runner| { runner.run_rustc([ @@ -129,11 +125,11 @@ pub(crate) static REGEX_REPO: GitRepo = GitRepo::github( static REGEX: CargoProject = CargoProject::new(®EX_REPO.source_dir(), "regex_target"); -static PORTABLE_SIMD_SRC: RelPath = RelPath::BUILD.join("portable-simd"); +static PORTABLE_SIMD_SRC: RelPath = RelPath::build("portable-simd"); static PORTABLE_SIMD: CargoProject = CargoProject::new(&PORTABLE_SIMD_SRC, "portable-simd_target"); -static LIBCORE_TESTS_SRC: RelPath = RelPath::BUILD.join("coretests"); +static LIBCORE_TESTS_SRC: RelPath = RelPath::build("coretests"); static LIBCORE_TESTS: CargoProject = CargoProject::new(&LIBCORE_TESTS_SRC, "coretests_target"); @@ -162,7 +158,7 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ &LIBCORE_TESTS_SRC.to_path(&runner.dirs), ); - let source_lockfile = RelPath::PATCHES.to_path(&runner.dirs).join("coretests-lock.toml"); + let source_lockfile = runner.dirs.source_dir.join("patches/coretests-lock.toml"); let target_lockfile = LIBCORE_TESTS_SRC.to_path(&runner.dirs).join("Cargo.lock"); fs::copy(source_lockfile, target_lockfile).unwrap(); @@ -267,7 +263,9 @@ pub(crate) fn run_tests( stdlib_source.clone(), ); - BUILD_EXAMPLE_OUT_DIR.ensure_fresh(dirs); + let path = BUILD_EXAMPLE_OUT_DIR.to_path(dirs); + ensure_empty_dir(&path); + runner.run_testsuite(NO_SYSROOT_SUITE); } else { eprintln!("[SKIP] no_sysroot tests"); diff --git a/compiler/rustc_codegen_cranelift/build_system/utils.rs b/compiler/rustc_codegen_cranelift/build_system/utils.rs index 22a9487a202..c2114caf869 100644 --- a/compiler/rustc_codegen_cranelift/build_system/utils.rs +++ b/compiler/rustc_codegen_cranelift/build_system/utils.rs @@ -93,7 +93,7 @@ impl CargoProject { } pub(crate) fn target_dir(&self, dirs: &Dirs) -> PathBuf { - RelPath::BUILD.join(self.target).to_path(dirs) + dirs.build_dir.join(self.target) } #[must_use] diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index 527ec5303b6..b63597f60fc 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -42,7 +42,6 @@ aot.float-minmax-pass aot.mod_bench aot.issue-72793 aot.issue-59326 -aot.polymorphize_coroutine aot.neon aot.gen_block_iterate aot.raw-dylib diff --git a/compiler/rustc_codegen_cranelift/example/polymorphize_coroutine.rs b/compiler/rustc_codegen_cranelift/example/polymorphize_coroutine.rs deleted file mode 100644 index 407da94c0f0..00000000000 --- a/compiler/rustc_codegen_cranelift/example/polymorphize_coroutine.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] - -use std::ops::Coroutine; -use std::pin::Pin; - -fn main() { - run_coroutine::<i32>(); -} - -fn run_coroutine<T>() { - let mut coroutine = #[coroutine] - || { - yield; - return; - }; - Pin::new(&mut coroutine).resume(()); -} diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index 3078288c286..0b1d83c5630 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -8,6 +8,9 @@ unboxed_closures )] #![allow(internal_features)] +// FIXME once abi_unsupported_vector_types is a hard error disable the foo test when the respective +// target feature is not enabled. +#![allow(abi_unsupported_vector_types)] #[cfg(target_arch = "x86_64")] use std::arch::x86_64::*; diff --git a/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch index 1860810e7f3..161173d4765 100644 --- a/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch +++ b/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch @@ -38,7 +38,7 @@ index 42a26ae..5ac1042 100644 @@ -1,3 +1,4 @@ +#![cfg(test)] // tidy-alphabetical-start - #![cfg_attr(bootstrap, feature(const_three_way_compare))] - #![cfg_attr(bootstrap, feature(strict_provenance))] + #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] + #![cfg_attr(test, feature(cfg_match))] -- 2.21.0 (Apple Git-122) diff --git a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch index 59653c6e875..06840624cef 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch @@ -14,10 +14,9 @@ diff --git a/lib.rs b/lib.rs index 1e336bf..35e6f54 100644 --- a/lib.rs +++ b/lib.rs -@@ -2,7 +2,6 @@ - #![cfg_attr(bootstrap, feature(const_three_way_compare))] - #![cfg_attr(bootstrap, feature(strict_provenance))] - #![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] +@@ -2,6 +2,5 @@ + #![cfg(test)] + // tidy-alphabetical-start -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_match))] #![feature(alloc_layout_extra)] diff --git a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch index ada35145e29..6012af6a8dd 100644 --- a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch +++ b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch @@ -4,22 +4,23 @@ Date: Fri, 9 Aug 2024 15:44:51 +0000 Subject: [PATCH] Disable f16 and f128 in compiler-builtins --- - library/sysroot/Cargo.toml | 2 +- + library/liballoc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) -diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml +diff --git a/library/liballoc/Cargo.toml b/library/liballoc/Cargo.toml index 7165c3e48af..968552ad435 100644 ---- a/library/sysroot/Cargo.toml -+++ b/library/sysroot/Cargo.toml +--- a/library/alloc/Cargo.toml ++++ b/library/alloc/Cargo.toml @@ -11,7 +11,7 @@ test = { path = "../test" } + edition = "2021" - # Forward features to the `std` crate as necessary - [features] --default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"] -+default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind", "compiler-builtins-no-f16-f128"] - backtrace = ["std/backtrace"] - compiler-builtins-c = ["std/compiler-builtins-c"] - compiler-builtins-mem = ["std/compiler-builtins-mem"] + [dependencies] + core = { path = "../core" } +-compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std'] } ++compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std', 'no-f16-f128'] } + + [dev-dependencies] + rand = { version = "0.8.5", default-features = false, features = ["alloc"] } -- 2.34.1 diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index a223cd7dbb8..8d935df4d1f 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-11-09" +channel = "nightly-2024-12-06" components = ["rust-src", "rustc-dev", "llvm-tools"] profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs b/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs index 92defd21cd9..a27b9983bf1 100644 --- a/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs +++ b/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs @@ -33,6 +33,11 @@ fn main() { args.push(OsString::from("--sysroot")); args.push(OsString::from(sysroot.to_str().unwrap())); } + if passed_args.is_empty() { + // Don't pass any arguments when the user didn't pass any arguments + // either to ensure the help message is shown. + args.clear(); + } args.extend(passed_args); let rustc = if let Some(rustc) = option_env!("RUSTC") { diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index 5b3f2a91207..54f6baff4fe 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -35,13 +35,14 @@ full-bootstrap = true local-rebuild = true [rust] +download-rustc = false codegen-backends = ["cranelift"] deny-warnings = false verbose-tests = false # The cg_clif sysroot doesn't contain llvm tools and unless llvm_tools is # disabled bootstrap will crash trying to copy llvm tools for the bootstrap # compiler. -llvm_tools = false +llvm-tools = false EOF popd diff --git a/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh b/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh index 770f2b6df6c..791d457993d 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh @@ -11,22 +11,5 @@ rm -r compiler/rustc_codegen_cranelift/{Cargo.*,src} cp ../Cargo.* compiler/rustc_codegen_cranelift/ cp -r ../src compiler/rustc_codegen_cranelift/src -# FIXME(rust-lang/rust#132719) remove once it doesn't break without this patch -cat <<EOF | git apply - -diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs -index 3394f2a84a0..cb980dd4d7c 100644 ---- a/src/bootstrap/src/core/build_steps/compile.rs -+++ b/src/bootstrap/src/core/build_steps/compile.rs -@@ -1976,7 +1976,7 @@ fn run(self, builder: &Builder<'_>) -> Compiler { - } - } - -- { -+ if builder.config.llvm_enabled(target_compiler.host) && builder.config.llvm_tools_enabled { - // \`llvm-strip\` is used by rustc, which is actually just a symlink to \`llvm-objcopy\`, - // so copy and rename \`llvm-objcopy\`. - let src_exe = exe("llvm-objcopy", target_compiler.host); -EOF - ./x.py build --stage 1 library/std popd diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index a820da286f5..e291ec20464 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -57,6 +57,7 @@ rm tests/ui/asm/x86_64/issue-96797.rs # const and sym inline asm operands don't rm tests/ui/asm/x86_64/goto.rs # inline asm labels not supported rm tests/ui/simd/simd-bitmask-notpow2.rs # non-pow-of-2 simd vector sizes rm -r tests/run-make/embed-source-dwarf # embedding sources in debuginfo +rm tests/ui/simd-abi-checks.rs # vector types >128bits not yet supported # requires LTO rm -r tests/run-make/cdylib @@ -75,6 +76,8 @@ rm -r tests/ui/instrument-coverage/ rm tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs rm tests/ui/asm/aarch64/type-f16.rs rm tests/ui/float/conv-bits-runtime-const.rs +rm tests/ui/consts/const-eval/float_methods.rs +rm tests/ui/match/match-float.rs # optimization tests # ================== diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index cab5b35c18d..2466bfe60c7 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -125,8 +125,9 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { returns: Vec<AbiParam>, args: &[Value], ) -> Cow<'_, [Value]> { - if self.tcx.sess.target.is_like_windows { - let (mut params, mut args): (Vec<_>, Vec<_>) = params + // Pass i128 arguments by-ref on Windows. + let (params, args): (Vec<_>, Cow<'_, [_]>) = if self.tcx.sess.target.is_like_windows { + let (params, args): (Vec<_>, Vec<_>) = params .into_iter() .zip(args) .map(|(param, &arg)| { @@ -140,29 +141,42 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { }) .unzip(); - let indirect_ret_val = returns.len() == 1 && returns[0].value_type == types::I128; + (params, args.into()) + } else { + (params, args.into()) + }; - if indirect_ret_val { - params.insert(0, AbiParam::new(self.pointer_type)); - let ret_ptr = self.create_stack_slot(16, 16); - args.insert(0, ret_ptr.get_addr(self)); - self.lib_call_unadjusted(name, params, vec![], &args); - return Cow::Owned(vec![ret_ptr.load(self, types::I128, MemFlags::trusted())]); + // Return i128 using a return area pointer on Windows and s390x. + let adjust_ret_param = + if self.tcx.sess.target.is_like_windows || self.tcx.sess.target.arch == "s390x" { + returns.len() == 1 && returns[0].value_type == types::I128 } else { - return self.lib_call_unadjusted(name, params, returns, &args); - } - } + false + }; + + if adjust_ret_param { + let mut params = params; + let mut args = args.to_vec(); + + params.insert(0, AbiParam::new(self.pointer_type)); + let ret_ptr = self.create_stack_slot(16, 16); + args.insert(0, ret_ptr.get_addr(self)); - self.lib_call_unadjusted(name, params, returns, args) + self.lib_call_unadjusted(name, params, vec![], &args); + + Cow::Owned(vec![ret_ptr.load(self, types::I128, MemFlags::trusted())]) + } else { + Cow::Borrowed(self.lib_call_unadjusted(name, params, returns, &args)) + } } - pub(crate) fn lib_call_unadjusted( + fn lib_call_unadjusted( &mut self, name: &str, params: Vec<AbiParam>, returns: Vec<AbiParam>, args: &[Value], - ) -> Cow<'_, [Value]> { + ) -> &[Value] { let sig = Signature { params, returns, call_conv: self.target_config.default_call_conv }; let func_id = self.module.declare_function(name, Linkage::Import, &sig).unwrap(); let func_ref = self.module.declare_func_in_func(func_id, &mut self.bcx.func); @@ -175,7 +189,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { } let results = self.bcx.inst_results(call_inst); assert!(results.len() <= 2, "{}", results.len()); - Cow::Borrowed(results) + results } } @@ -380,8 +394,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( def_id, fn_args, source_info.span, - ) - .polymorphize(fx.tcx); + ); if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) { if target.is_some() { @@ -684,7 +697,7 @@ pub(crate) fn codegen_drop<'tcx>( target: BasicBlock, ) { let ty = drop_place.layout().ty; - let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty).polymorphize(fx.tcx); + let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty); if let ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) = drop_instance.def diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 06cc5754894..34066eb83fc 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -6,6 +6,7 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_module::ModuleError; use rustc_ast::InlineAsmOptions; use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_index::IndexVec; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::InlineAsmMacro; @@ -16,6 +17,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use crate::constant::ConstantCx; use crate::debuginfo::{FunctionDebugContext, TypeDebugContext}; +use crate::enable_verifier; use crate::inline_asm::codegen_naked_asm; use crate::prelude::*; use crate::pretty_clif::CommentWriter; @@ -169,12 +171,13 @@ pub(crate) fn codegen_fn<'tcx>( pub(crate) fn compile_fn( cx: &mut crate::CodegenCx, + profiler: &SelfProfilerRef, cached_context: &mut Context, module: &mut dyn Module, codegened_func: CodegenedFunction, ) { let _timer = - cx.profiler.generic_activity_with_arg("compile function", &*codegened_func.symbol_name); + profiler.generic_activity_with_arg("compile function", &*codegened_func.symbol_name); let clif_comments = codegened_func.clif_comments; @@ -212,7 +215,7 @@ pub(crate) fn compile_fn( }; // Define function - cx.profiler.generic_activity("define function").run(|| { + profiler.generic_activity("define function").run(|| { context.want_disasm = cx.should_write_ir; match module.define_function(codegened_func.func_id, context) { Ok(()) => {} @@ -253,7 +256,7 @@ pub(crate) fn compile_fn( // Define debuginfo for function let debug_context = &mut cx.debug_context; - cx.profiler.generic_activity("generate debug info").run(|| { + profiler.generic_activity("generate debug info").run(|| { if let Some(debug_context) = debug_context { codegened_func.func_debug_cx.unwrap().finalize( debug_context, @@ -264,11 +267,11 @@ pub(crate) fn compile_fn( }); } -pub(crate) fn verify_func( - tcx: TyCtxt<'_>, - writer: &crate::pretty_clif::CommentWriter, - func: &Function, -) { +fn verify_func(tcx: TyCtxt<'_>, writer: &crate::pretty_clif::CommentWriter, func: &Function) { + if !enable_verifier(tcx.sess) { + return; + } + tcx.prof.generic_activity("verify clif ir").run(|| { let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder()); match cranelift_codegen::verify_function(&func, &flags) { @@ -670,8 +673,7 @@ fn codegen_stmt<'tcx>( def_id, args, ) - .unwrap() - .polymorphize(fx.tcx), + .unwrap(), ); let func_addr = fx.bcx.ins().func_addr(fx.pointer_type, func_ref); lval.write_cvalue(fx, CValue::by_val(func_addr, to_layout)); @@ -757,8 +759,7 @@ fn codegen_stmt<'tcx>( def_id, args, ty::ClosureKind::FnOnce, - ) - .polymorphize(fx.tcx); + ); let func_ref = fx.get_function_ref(instance); let func_addr = fx.bcx.ins().func_addr(fx.pointer_type, func_ref); lval.write_cvalue(fx, CValue::by_val(func_addr, lval.layout())); @@ -1084,7 +1085,7 @@ fn codegen_panic_inner<'tcx>( let def_id = fx.tcx.require_lang_item(lang_item, span); - let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx); + let instance = Instance::mono(fx.tcx, def_id); if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) { fx.bcx.ins().trap(TrapCode::user(2).unwrap()); diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index b6a4769e031..734574338d0 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -81,26 +81,6 @@ pub(crate) fn maybe_codegen_checked<'tcx>( match bin_op { BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => unreachable!(), BinOp::Add | BinOp::Sub => None, - BinOp::Mul if is_signed => { - let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]); - let oflow = CPlace::new_stack_slot(fx, fx.layout_of(fx.tcx.types.i32)); - let lhs = lhs.load_scalar(fx); - let rhs = rhs.load_scalar(fx); - let oflow_ptr = oflow.to_ptr().get_addr(fx); - let res = fx.lib_call_unadjusted( - "__muloti4", - vec![ - AbiParam::new(types::I128), - AbiParam::new(types::I128), - AbiParam::new(fx.pointer_type), - ], - vec![AbiParam::new(types::I128)], - &[lhs, rhs, oflow_ptr], - )[0]; - let oflow = oflow.to_cvalue(fx).load_scalar(fx); - let oflow = fx.bcx.ins().ireduce(types::I8, oflow); - Some(CValue::by_val_pair(res, oflow, fx.layout_of(out_ty))) - } BinOp::Mul => { let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]); let out_place = CPlace::new_stack_slot(fx, fx.layout_of(out_ty)); @@ -110,7 +90,12 @@ pub(crate) fn maybe_codegen_checked<'tcx>( AbiParam::new(types::I128), ]; let args = [out_place.to_ptr().get_addr(fx), lhs.load_scalar(fx), rhs.load_scalar(fx)]; - fx.lib_call("__rust_u128_mulo", param_types, vec![], &args); + fx.lib_call( + if is_signed { "__rust_i128_mulo" } else { "__rust_u128_mulo" }, + param_types, + vec![], + &args, + ); Some(out_place.to_cvalue(fx)) } BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(), diff --git a/compiler/rustc_codegen_cranelift/src/config.rs b/compiler/rustc_codegen_cranelift/src/config.rs index 12bce680d9e..d784f6e9d9e 100644 --- a/compiler/rustc_codegen_cranelift/src/config.rs +++ b/compiler/rustc_codegen_cranelift/src/config.rs @@ -1,10 +1,3 @@ -use std::env; -use std::str::FromStr; - -fn bool_env_var(key: &str) -> bool { - env::var(key).as_deref() == Ok("1") -} - /// The mode to use for compilation. #[derive(Copy, Clone, Debug)] pub enum CodegenMode { @@ -16,19 +9,6 @@ pub enum CodegenMode { JitLazy, } -impl FromStr for CodegenMode { - type Err = String; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "aot" => Ok(CodegenMode::Aot), - "jit" => Ok(CodegenMode::Jit), - "jit-lazy" => Ok(CodegenMode::JitLazy), - _ => Err(format!("Unknown codegen mode `{}`", s)), - } - } -} - /// Configuration of cg_clif as passed in through `-Cllvm-args` and various env vars. #[derive(Clone, Debug)] pub struct BackendConfig { @@ -41,51 +21,22 @@ pub struct BackendConfig { /// /// Defaults to the value of `CG_CLIF_JIT_ARGS`. pub jit_args: Vec<String>, - - /// Enable the Cranelift ir verifier for all compilation passes. If not set it will only run - /// once before passing the clif ir to Cranelift for compilation. - /// - /// Defaults to true when the `CG_CLIF_ENABLE_VERIFIER` env var is set to 1 or when cg_clif is - /// compiled with debug assertions enabled or false otherwise. Can be set using - /// `-Cllvm-args=enable_verifier=...`. - pub enable_verifier: bool, - - /// Don't cache object files in the incremental cache. Useful during development of cg_clif - /// to make it possible to use incremental mode for all analyses performed by rustc without - /// caching object files when their content should have been changed by a change to cg_clif. - /// - /// Defaults to true when the `CG_CLIF_DISABLE_INCR_CACHE` env var is set to 1 or false - /// otherwise. Can be set using `-Cllvm-args=disable_incr_cache=...`. - pub disable_incr_cache: bool, -} - -impl Default for BackendConfig { - fn default() -> Self { - BackendConfig { - codegen_mode: CodegenMode::Aot, - jit_args: { - match std::env::var("CG_CLIF_JIT_ARGS") { - Ok(args) => args.split(' ').map(|arg| arg.to_string()).collect(), - Err(std::env::VarError::NotPresent) => vec![], - Err(std::env::VarError::NotUnicode(s)) => { - panic!("CG_CLIF_JIT_ARGS not unicode: {:?}", s); - } - } - }, - enable_verifier: cfg!(debug_assertions) || bool_env_var("CG_CLIF_ENABLE_VERIFIER"), - disable_incr_cache: bool_env_var("CG_CLIF_DISABLE_INCR_CACHE"), - } - } } impl BackendConfig { /// Parse the configuration passed in using `-Cllvm-args`. pub fn from_opts(opts: &[String]) -> Result<Self, String> { - fn parse_bool(name: &str, value: &str) -> Result<bool, String> { - value.parse().map_err(|_| format!("failed to parse value `{}` for {}", value, name)) - } + let mut config = BackendConfig { + codegen_mode: CodegenMode::Aot, + jit_args: match std::env::var("CG_CLIF_JIT_ARGS") { + Ok(args) => args.split(' ').map(|arg| arg.to_string()).collect(), + Err(std::env::VarError::NotPresent) => vec![], + Err(std::env::VarError::NotUnicode(s)) => { + panic!("CG_CLIF_JIT_ARGS not unicode: {:?}", s); + } + }, + }; - let mut config = BackendConfig::default(); for opt in opts { if opt.starts_with("-import-instr-limit") { // Silently ignore -import-instr-limit. It is set by rust's build system even when @@ -94,9 +45,14 @@ impl BackendConfig { } if let Some((name, value)) = opt.split_once('=') { match name { - "mode" => config.codegen_mode = value.parse()?, - "enable_verifier" => config.enable_verifier = parse_bool(name, value)?, - "disable_incr_cache" => config.disable_incr_cache = parse_bool(name, value)?, + "mode" => { + config.codegen_mode = match value { + "aot" => CodegenMode::Aot, + "jit" => CodegenMode::Jit, + "jit-lazy" => CodegenMode::JitLazy, + _ => return Err(format!("Unknown codegen mode `{}`", value)), + }; + } _ => return Err(format!("Unknown option `{}`", name)), } } else { diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index abe6085b04f..3e7b81a96b6 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -452,8 +452,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant let data_id = match reloc_target_alloc { GlobalAlloc::Function { instance, .. } => { assert_eq!(addend, 0); - let func_id = - crate::abi::import_function(tcx, module, instance.polymorphize(tcx)); + let func_id = crate::abi::import_function(tcx, module, instance); let local_func_id = module.declare_func_in_data(func_id, &mut data); data.write_function_addr(offset.bytes() as u32, local_func_id); continue; diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 8eab73ad5f9..5bbcfc2cda7 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -1,6 +1,7 @@ //! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a //! standalone executable. +use std::env; use std::fs::{self, File}; use std::io::BufWriter; use std::path::{Path, PathBuf}; @@ -25,13 +26,18 @@ use rustc_middle::mir::mono::{CodegenUnit, MonoItem}; use rustc_session::Session; use rustc_session::config::{DebugInfo, OutFileName, OutputFilenames, OutputType}; -use crate::BackendConfig; +use crate::CodegenCx; +use crate::base::CodegenedFunction; use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken}; use crate::debuginfo::TypeDebugContext; use crate::global_asm::GlobalAsmConfig; use crate::prelude::*; use crate::unwind_module::UnwindModule; +fn disable_incr_cache() -> bool { + env::var("CG_CLIF_DISABLE_INCR_CACHE").as_deref() == Ok("1") +} + struct ModuleCodegenResult { module_regular: CompiledModule, module_global_asm: Option<CompiledModule>, @@ -63,10 +69,10 @@ impl OngoingCodegen { self, sess: &Session, outputs: &OutputFilenames, - backend_config: &BackendConfig, ) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) { let mut work_products = FxIndexMap::default(); let mut modules = vec![]; + let disable_incr_cache = disable_incr_cache(); for module_codegen in self.modules { let module_codegen_result = match module_codegen { @@ -87,7 +93,7 @@ impl OngoingCodegen { if let Some((work_product_id, work_product)) = existing_work_product { work_products.insert(work_product_id, work_product); } else { - let work_product = if backend_config.disable_incr_cache { + let work_product = if disable_incr_cache { None } else if let Some(module_global_asm) = &module_global_asm { rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( @@ -322,12 +328,8 @@ fn produce_final_output_artifacts( // These are used in linking steps and will be cleaned up afterward. } -fn make_module( - sess: &Session, - backend_config: &BackendConfig, - name: String, -) -> UnwindModule<ObjectModule> { - let isa = crate::build_isa(sess, backend_config); +fn make_module(sess: &Session, name: String) -> UnwindModule<ObjectModule> { + let isa = crate::build_isa(sess); let mut builder = ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap(); @@ -412,7 +414,13 @@ fn emit_module( Err(err) => return Err(format!("error writing object file: {}", err)), }; - prof.artifact_size("object_file", &*name, file.metadata().unwrap().len()); + if prof.enabled() { + prof.artifact_size( + "object_file", + tmp_file.file_name().unwrap().to_string_lossy(), + file.metadata().unwrap().len(), + ); + } Ok(CompiledModule { name, @@ -486,91 +494,101 @@ fn reuse_workproduct_for_cgu( }) } +fn codegen_cgu_content( + tcx: TyCtxt<'_>, + module: &mut dyn Module, + cgu_name: rustc_span::Symbol, +) -> (CodegenCx, Vec<CodegenedFunction>) { + let _timer = tcx.prof.generic_activity_with_arg("codegen cgu", cgu_name.as_str()); + + let cgu = tcx.codegen_unit(cgu_name); + let mono_items = cgu.items_in_deterministic_order(tcx); + + let mut cx = crate::CodegenCx::new( + tcx, + module.isa(), + tcx.sess.opts.debuginfo != DebugInfo::None, + cgu_name, + ); + let mut type_dbg = TypeDebugContext::default(); + super::predefine_mono_items(tcx, module, &mono_items); + let mut codegened_functions = vec![]; + for (mono_item, _) in mono_items { + match mono_item { + MonoItem::Fn(inst) => { + if let Some(codegened_function) = crate::base::codegen_fn( + tcx, + &mut cx, + &mut type_dbg, + Function::new(), + module, + inst, + ) { + codegened_functions.push(codegened_function); + } + } + MonoItem::Static(def_id) => { + let data_id = crate::constant::codegen_static(tcx, module, def_id); + if let Some(debug_context) = &mut cx.debug_context { + debug_context.define_static(tcx, &mut type_dbg, def_id, data_id); + } + } + MonoItem::GlobalAsm(item_id) => { + crate::global_asm::codegen_global_asm_item(tcx, &mut cx.global_asm, item_id); + } + } + } + crate::main_shim::maybe_create_entry_wrapper(tcx, module, false, cgu.is_primary()); + + (cx, codegened_functions) +} + fn module_codegen( tcx: TyCtxt<'_>, - (backend_config, global_asm_config, cgu_name, token): ( - BackendConfig, + (global_asm_config, cgu_name, token): ( Arc<GlobalAsmConfig>, rustc_span::Symbol, ConcurrencyLimiterToken, ), ) -> OngoingModuleCodegen { - let (cgu_name, mut cx, mut module, codegened_functions) = - tcx.prof.generic_activity_with_arg("codegen cgu", cgu_name.as_str()).run(|| { - let cgu = tcx.codegen_unit(cgu_name); - let mono_items = cgu.items_in_deterministic_order(tcx); - - let mut module = make_module(tcx.sess, &backend_config, cgu_name.as_str().to_string()); - - let mut cx = crate::CodegenCx::new( - tcx, - module.isa(), - tcx.sess.opts.debuginfo != DebugInfo::None, - cgu_name, - ); - let mut type_dbg = TypeDebugContext::default(); - super::predefine_mono_items(tcx, &mut module, &mono_items); - let mut codegened_functions = vec![]; - for (mono_item, _) in mono_items { - match mono_item { - MonoItem::Fn(inst) => { - if let Some(codegened_function) = crate::base::codegen_fn( - tcx, - &mut cx, - &mut type_dbg, - Function::new(), - &mut module, - inst, - ) { - codegened_functions.push(codegened_function); - } - } - MonoItem::Static(def_id) => { - let data_id = crate::constant::codegen_static(tcx, &mut module, def_id); - if let Some(debug_context) = &mut cx.debug_context { - debug_context.define_static(tcx, &mut type_dbg, def_id, data_id); - } - } - MonoItem::GlobalAsm(item_id) => { - crate::global_asm::codegen_global_asm_item( - tcx, - &mut cx.global_asm, - item_id, - ); - } - } - } - crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module, false, cgu.is_primary()); + let mut module = make_module(tcx.sess, cgu_name.as_str().to_string()); - let cgu_name = cgu.name().as_str().to_owned(); + let (mut cx, codegened_functions) = codegen_cgu_content(tcx, &mut module, cgu_name); - (cgu_name, cx, module, codegened_functions) - }); + let cgu_name = cgu_name.as_str().to_owned(); let producer = crate::debuginfo::producer(tcx.sess); + let profiler = tcx.prof.clone(); + OngoingModuleCodegen::Async(std::thread::spawn(move || { - cx.profiler.clone().generic_activity_with_arg("compile functions", &*cgu_name).run(|| { + profiler.clone().generic_activity_with_arg("compile functions", &*cgu_name).run(|| { cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler( - cx.profiler.clone(), + profiler.clone(), ))); let mut cached_context = Context::new(); for codegened_func in codegened_functions { - crate::base::compile_fn(&mut cx, &mut cached_context, &mut module, codegened_func); + crate::base::compile_fn( + &mut cx, + &profiler, + &mut cached_context, + &mut module, + codegened_func, + ); } }); let global_asm_object_file = - cx.profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { + profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, &cx.global_asm) })?; let codegen_result = - cx.profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| { + profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| { emit_cgu( &global_asm_config.output_filenames, - &cx.profiler, + &profiler, cgu_name, module, cx.debug_context, @@ -583,9 +601,63 @@ fn module_codegen( })) } +fn emit_metadata_module(tcx: TyCtxt<'_>, metadata: &EncodedMetadata) -> CompiledModule { + use rustc_middle::mir::mono::CodegenUnitNameBuilder; + + let _timer = tcx.sess.timer("write compressed metadata"); + + let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); + let metadata_cgu_name = cgu_name_builder + .build_cgu_name(LOCAL_CRATE, ["crate"], Some("metadata")) + .as_str() + .to_string(); + + let tmp_file = + tcx.output_filenames(()).temp_path(OutputType::Metadata, Some(&metadata_cgu_name)); + + let symbol_name = rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx); + let obj = create_compressed_metadata_file(tcx.sess, metadata, &symbol_name); + + if let Err(err) = std::fs::write(&tmp_file, obj) { + tcx.dcx().fatal(format!("error writing metadata object file: {}", err)); + } + + CompiledModule { + name: metadata_cgu_name, + kind: ModuleKind::Metadata, + object: Some(tmp_file), + dwarf_object: None, + bytecode: None, + assembly: None, + llvm_ir: None, + } +} + +fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option<CompiledModule> { + let mut allocator_module = make_module(tcx.sess, "allocator_shim".to_string()); + let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module); + + if created_alloc_shim { + let product = allocator_module.finish(); + + match emit_module( + tcx.output_filenames(()), + &tcx.sess.prof, + product.object, + ModuleKind::Allocator, + "allocator_shim".to_owned(), + &crate::debuginfo::producer(tcx.sess), + ) { + Ok(allocator_module) => Some(allocator_module), + Err(err) => tcx.dcx().fatal(err), + } + } else { + None + } +} + pub(crate) fn run_aot( tcx: TyCtxt<'_>, - backend_config: BackendConfig, metadata: EncodedMetadata, need_metadata_module: bool, ) -> Box<OngoingCodegen> { @@ -631,9 +703,10 @@ pub(crate) fn run_aot( let global_asm_config = Arc::new(crate::global_asm::GlobalAsmConfig::new(tcx)); + let disable_incr_cache = disable_incr_cache(); let (todo_cgus, done_cgus) = cgus.into_iter().enumerate().partition::<Vec<_>, _>(|&(i, _)| match cgu_reuse[i] { - _ if backend_config.disable_incr_cache => true, + _ if disable_incr_cache => true, CguReuse::No => true, CguReuse::PreLto | CguReuse::PostLto => false, }); @@ -647,12 +720,7 @@ pub(crate) fn run_aot( .with_task( dep_node, tcx, - ( - backend_config.clone(), - global_asm_config.clone(), - cgu.name(), - concurrency_limiter.acquire(tcx.dcx()), - ), + (global_asm_config.clone(), cgu.name(), concurrency_limiter.acquire(tcx.dcx())), module_codegen, Some(rustc_middle::dep_graph::hash_result), ) @@ -666,62 +734,10 @@ pub(crate) fn run_aot( modules }); - let mut allocator_module = make_module(tcx.sess, &backend_config, "allocator_shim".to_string()); - let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module); - - let allocator_module = if created_alloc_shim { - let product = allocator_module.finish(); + let allocator_module = emit_allocator_module(tcx); - match emit_module( - tcx.output_filenames(()), - &tcx.sess.prof, - product.object, - ModuleKind::Allocator, - "allocator_shim".to_owned(), - &crate::debuginfo::producer(tcx.sess), - ) { - Ok(allocator_module) => Some(allocator_module), - Err(err) => tcx.dcx().fatal(err), - } - } else { - None - }; - - let metadata_module = if need_metadata_module { - let (metadata_cgu_name, tmp_file) = tcx.sess.time("write compressed metadata", || { - use rustc_middle::mir::mono::CodegenUnitNameBuilder; - - let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); - let metadata_cgu_name = cgu_name_builder - .build_cgu_name(LOCAL_CRATE, ["crate"], Some("metadata")) - .as_str() - .to_string(); - - let tmp_file = - tcx.output_filenames(()).temp_path(OutputType::Metadata, Some(&metadata_cgu_name)); - - let symbol_name = rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx); - let obj = create_compressed_metadata_file(tcx.sess, &metadata, &symbol_name); - - if let Err(err) = std::fs::write(&tmp_file, obj) { - tcx.dcx().fatal(format!("error writing metadata object file: {}", err)); - } - - (metadata_cgu_name, tmp_file) - }); - - Some(CompiledModule { - name: metadata_cgu_name, - kind: ModuleKind::Metadata, - object: Some(tmp_file), - dwarf_object: None, - bytecode: None, - assembly: None, - llvm_ir: None, - }) - } else { - None - }; + let metadata_module = + if need_metadata_module { Some(emit_metadata_module(tcx, &metadata)) } else { None }; Box::new(OngoingCodegen { modules, diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 0d62a13b472..d68948966ea 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -11,12 +11,12 @@ use cranelift_jit::{JITBuilder, JITModule}; use rustc_codegen_ssa::CrateInfo; use rustc_middle::mir::mono::MonoItem; use rustc_session::Session; -use rustc_span::Symbol; +use rustc_span::sym; use crate::debuginfo::TypeDebugContext; use crate::prelude::*; use crate::unwind_module::UnwindModule; -use crate::{BackendConfig, CodegenCx, CodegenMode}; +use crate::{CodegenCx, CodegenMode}; struct JitState { jit_module: UnwindModule<JITModule>, @@ -59,14 +59,10 @@ impl UnsafeMessage { } } -fn create_jit_module( - tcx: TyCtxt<'_>, - backend_config: &BackendConfig, - hotswap: bool, -) -> (UnwindModule<JITModule>, CodegenCx) { +fn create_jit_module(tcx: TyCtxt<'_>, hotswap: bool) -> (UnwindModule<JITModule>, CodegenCx) { let crate_info = CrateInfo::new(tcx, "dummy_target_cpu".to_string()); - let isa = crate::build_isa(tcx.sess, backend_config); + let isa = crate::build_isa(tcx.sess); let mut jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); jit_builder.hotswap(hotswap); crate::compiler_builtins::register_functions_for_jit(&mut jit_builder); @@ -74,14 +70,14 @@ fn create_jit_module( jit_builder.symbol("__clif_jit_fn", clif_jit_fn as *const u8); let mut jit_module = UnwindModule::new(JITModule::new(jit_builder), false); - let cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, Symbol::intern("dummy_cgu_name")); + let cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, sym::dummy_cgu_name); crate::allocator::codegen(tcx, &mut jit_module); (jit_module, cx) } -pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { +pub(crate) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode, jit_args: Vec<String>) -> ! { if !tcx.sess.opts.output_types.should_codegen() { tcx.dcx().fatal("JIT mode doesn't work with `cargo check`"); } @@ -90,11 +86,8 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { tcx.dcx().fatal("can't jit non-executable crate"); } - let (mut jit_module, mut cx) = create_jit_module( - tcx, - &backend_config, - matches!(backend_config.codegen_mode, CodegenMode::JitLazy), - ); + let (mut jit_module, mut cx) = + create_jit_module(tcx, matches!(codegen_mode, CodegenMode::JitLazy)); let mut cached_context = Context::new(); let (_, cgus) = tcx.collect_and_partition_mono_items(()); @@ -110,7 +103,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { super::predefine_mono_items(tcx, &mut jit_module, &mono_items); for (mono_item, _) in mono_items { match mono_item { - MonoItem::Fn(inst) => match backend_config.codegen_mode { + MonoItem::Fn(inst) => match codegen_mode { CodegenMode::Aot => unreachable!(), CodegenMode::Jit => { codegen_and_compile_fn( @@ -151,7 +144,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { ); let args = std::iter::once(&*tcx.crate_name(LOCAL_CRATE).as_str().to_string()) - .chain(backend_config.jit_args.iter().map(|arg| &**arg)) + .chain(jit_args.iter().map(|arg| &**arg)) .map(|arg| CString::new(arg).unwrap()) .collect::<Vec<_>>(); @@ -211,7 +204,7 @@ pub(crate) fn codegen_and_compile_fn<'tcx>( instance: Instance<'tcx>, ) { cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler( - cx.profiler.clone(), + tcx.prof.clone(), ))); tcx.prof.generic_activity("codegen and compile fn").run(|| { @@ -227,7 +220,7 @@ pub(crate) fn codegen_and_compile_fn<'tcx>( module, instance, ) { - crate::base::compile_fn(cx, cached_context, module, codegened_func); + crate::base::compile_fn(cx, &tcx.prof, cached_context, module, codegened_func); } }); } @@ -276,12 +269,7 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) -> jit_module.module.prepare_for_function_redefine(func_id).unwrap(); - let mut cx = crate::CodegenCx::new( - tcx, - jit_module.isa(), - false, - Symbol::intern("dummy_cgu_name"), - ); + let mut cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, sym::dummy_cgu_name); codegen_and_compile_fn(tcx, &mut cx, &mut Context::new(), jit_module, instance); assert!(cx.global_asm.is_empty()); diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 7bc500b1814..33726056cc1 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -102,13 +102,12 @@ pub(crate) fn codegen_inline_asm_terminator<'tcx>( // Pass a wrapper rather than the function itself as the function itself may not // be exported from the main codegen unit and may thus be unreachable from the // object file created by an external assembler. - let inline_asm_index = fx.cx.inline_asm_index.get(); - fx.cx.inline_asm_index.set(inline_asm_index + 1); let wrapper_name = format!( "__inline_asm_{}_wrapper_n{}", fx.cx.cgu_name.as_str().replace('.', "__").replace('-', "_"), - inline_asm_index + fx.cx.inline_asm_index ); + fx.cx.inline_asm_index += 1; let sig = get_function_sig(fx.tcx, fx.target_config.default_call_conv, instance); create_wrapper_function(fx.module, sig, &wrapper_name, symbol.name); @@ -167,13 +166,12 @@ pub(crate) fn codegen_inline_asm_inner<'tcx>( asm_gen.allocate_registers(); asm_gen.allocate_stack_slots(); - let inline_asm_index = fx.cx.inline_asm_index.get(); - fx.cx.inline_asm_index.set(inline_asm_index + 1); let asm_name = format!( "__inline_asm_{}_n{}", fx.cx.cgu_name.as_str().replace('.', "__").replace('-', "_"), - inline_asm_index + fx.cx.inline_asm_index ); + fx.cx.inline_asm_index += 1; let generated_asm = asm_gen.generate_asm_wrapper(&asm_name); fx.cx.global_asm.push_str(&generated_asm); @@ -266,13 +264,12 @@ pub(crate) fn codegen_naked_asm<'tcx>( // Pass a wrapper rather than the function itself as the function itself may not // be exported from the main codegen unit and may thus be unreachable from the // object file created by an external assembler. - let inline_asm_index = cx.inline_asm_index.get(); - cx.inline_asm_index.set(inline_asm_index + 1); let wrapper_name = format!( "__inline_asm_{}_wrapper_n{}", cx.cgu_name.as_str().replace('.', "__").replace('-', "_"), - inline_asm_index + cx.inline_asm_index ); + cx.inline_asm_index += 1; let sig = get_function_sig(tcx, module.target_config().default_call_conv, instance); create_wrapper_function(module, sig, &wrapper_name, symbol.name); @@ -476,9 +473,14 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { let mut new_slot = |x| new_slot_fn(&mut slot_size, x); // Allocate stack slots for saving clobbered registers - let abi_clobber = InlineAsmClobberAbi::parse(self.arch, &self.tcx.sess.target, sym::C) - .unwrap() - .clobbered_regs(); + let abi_clobber = InlineAsmClobberAbi::parse( + self.arch, + &self.tcx.sess.target, + &self.tcx.sess.unstable_target_features, + sym::C, + ) + .unwrap() + .clobbered_regs(); for (i, reg) in self.registers.iter().enumerate().filter_map(|(i, r)| r.map(|r| (i, r))) { let mut need_save = true; // If the register overlaps with a register clobbered by function call, then diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 3318c0797ec..5f1b71eff6b 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1270,8 +1270,7 @@ fn codegen_regular_intrinsic_call<'tcx>( } sym::cold_path => { - // This is a no-op. The intrinsic is just a hint to the optimizer. - // We still have an impl here to avoid it being turned into a call. + fx.bcx.set_cold_block(fx.bcx.current_block().unwrap()); } // Unimplemented intrinsics must have a fallback body. The fallback body is obtained diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index f787b8a6fd9..e0ebe30752a 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -415,7 +415,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }); } - sym::simd_fma => { + // FIXME: simd_relaxed_fma doesn't relax to non-fused multiply-add + sym::simd_fma | sym::simd_relaxed_fma => { intrinsic_args!(fx, args => (a, b, c); intrinsic); if !a.layout().ty.is_simd() { diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index e6f6ae30581..9f552b3feb9 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -34,7 +34,7 @@ extern crate rustc_target; extern crate rustc_driver; use std::any::Any; -use std::cell::{Cell, RefCell}; +use std::env; use std::sync::Arc; use cranelift_codegen::isa::TargetIsa; @@ -42,7 +42,6 @@ use cranelift_codegen::settings::{self, Configurable}; use rustc_codegen_ssa::CodegenResults; use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_data_structures::profiling::SelfProfilerRef; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; @@ -123,11 +122,10 @@ impl<F: Fn() -> String> Drop for PrintOnPanic<F> { /// The codegen context holds any information shared between the codegen of individual functions /// inside a single codegen unit with the exception of the Cranelift [`Module`](cranelift_module::Module). struct CodegenCx { - profiler: SelfProfilerRef, output_filenames: Arc<OutputFilenames>, should_write_ir: bool, global_asm: String, - inline_asm_index: Cell<usize>, + inline_asm_index: usize, debug_context: Option<DebugContext>, cgu_name: Symbol, } @@ -142,11 +140,10 @@ impl CodegenCx { None }; CodegenCx { - profiler: tcx.prof.clone(), output_filenames: tcx.output_filenames(()).clone(), should_write_ir: crate::pretty_clif::should_write_ir(tcx), global_asm: String::new(), - inline_asm_index: Cell::new(0), + inline_asm_index: 0, debug_context, cgu_name, } @@ -154,7 +151,7 @@ impl CodegenCx { } pub struct CraneliftCodegenBackend { - pub config: RefCell<Option<BackendConfig>>, + pub config: Option<BackendConfig>, } impl CodegenBackend for CraneliftCodegenBackend { @@ -176,31 +173,19 @@ impl CodegenBackend for CraneliftCodegenBackend { sess.dcx() .fatal("`-Cinstrument-coverage` is LLVM specific and not supported by Cranelift"); } - - let mut config = self.config.borrow_mut(); - if config.is_none() { - let new_config = BackendConfig::from_opts(&sess.opts.cg.llvm_args) - .unwrap_or_else(|err| sess.dcx().fatal(err)); - *config = Some(new_config); - } } fn target_features(&self, sess: &Session, _allow_unstable: bool) -> Vec<rustc_span::Symbol> { // FIXME return the actually used target features. this is necessary for #[cfg(target_feature)] if sess.target.arch == "x86_64" && sess.target.os != "none" { // x86_64 mandates SSE2 support - vec![Symbol::intern("fxsr"), sym::sse, Symbol::intern("sse2")] + vec![sym::fsxr, sym::sse, sym::sse2] } else if sess.target.arch == "aarch64" { match &*sess.target.os { "none" => vec![], // On macOS the aes, sha2 and sha3 features are enabled by default and ring // fails to compile on macOS when they are not present. - "macos" => vec![ - sym::neon, - Symbol::intern("aes"), - Symbol::intern("sha2"), - Symbol::intern("sha3"), - ], + "macos" => vec![sym::neon, sym::aes, sym::sha2, sym::sha3], // AArch64 mandates Neon support _ => vec![sym::neon], } @@ -220,12 +205,15 @@ impl CodegenBackend for CraneliftCodegenBackend { need_metadata_module: bool, ) -> Box<dyn Any> { tcx.dcx().abort_if_errors(); - let config = self.config.borrow().clone().unwrap(); + let config = self.config.clone().unwrap_or_else(|| { + BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args) + .unwrap_or_else(|err| tcx.sess.dcx().fatal(err)) + }); match config.codegen_mode { - CodegenMode::Aot => driver::aot::run_aot(tcx, config, metadata, need_metadata_module), + CodegenMode::Aot => driver::aot::run_aot(tcx, metadata, need_metadata_module), CodegenMode::Jit | CodegenMode::JitLazy => { #[cfg(feature = "jit")] - driver::jit::run_jit(tcx, config); + driver::jit::run_jit(tcx, config.codegen_mode, config.jit_args); #[cfg(not(feature = "jit"))] tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); @@ -239,16 +227,20 @@ impl CodegenBackend for CraneliftCodegenBackend { sess: &Session, outputs: &OutputFilenames, ) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) { - let _timer = sess.timer("finish_ongoing_codegen"); - - ongoing_codegen.downcast::<driver::aot::OngoingCodegen>().unwrap().join( - sess, - outputs, - self.config.borrow().as_ref().unwrap(), - ) + ongoing_codegen.downcast::<driver::aot::OngoingCodegen>().unwrap().join(sess, outputs) } } +/// Determine if the Cranelift ir verifier should run. +/// +/// Returns true when `-Zverify-llvm-ir` is passed, the `CG_CLIF_ENABLE_VERIFIER` env var is set to +/// 1 or when cg_clif is compiled with debug assertions enabled or false otherwise. +fn enable_verifier(sess: &Session) -> bool { + sess.verify_llvm_ir() + || cfg!(debug_assertions) + || env::var("CG_CLIF_ENABLE_VERIFIER").as_deref() == Ok("1") +} + fn target_triple(sess: &Session) -> target_lexicon::Triple { // FIXME(madsmtm): Use `sess.target.llvm_target` once target-lexicon supports unversioned macOS. // See <https://github.com/bytecodealliance/target-lexicon/pull/113> @@ -258,14 +250,14 @@ fn target_triple(sess: &Session) -> target_lexicon::Triple { } } -fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc<dyn TargetIsa + 'static> { +fn build_isa(sess: &Session) -> Arc<dyn TargetIsa + 'static> { use target_lexicon::BinaryFormat; let target_triple = crate::target_triple(sess); let mut flags_builder = settings::builder(); flags_builder.enable("is_pic").unwrap(); - let enable_verifier = if backend_config.enable_verifier { "true" } else { "false" }; + let enable_verifier = if enable_verifier(sess) { "true" } else { "false" }; flags_builder.set("enable_verifier", enable_verifier).unwrap(); flags_builder.set("regalloc_checker", enable_verifier).unwrap(); @@ -300,6 +292,16 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc<dyn TargetIs } } + if let target_lexicon::OperatingSystem::Windows = target_triple.operating_system { + // FIXME remove dependency on this from the Rust ABI. cc bytecodealliance/wasmtime#9510 + flags_builder.enable("enable_multi_ret_implicit_sret").unwrap(); + } + + if let target_lexicon::Architecture::S390x = target_triple.architecture { + // FIXME remove dependency on this from the Rust ABI. cc bytecodealliance/wasmtime#9510 + flags_builder.enable("enable_multi_ret_implicit_sret").unwrap(); + } + if let target_lexicon::Architecture::Aarch64(_) | target_lexicon::Architecture::Riscv64(_) | target_lexicon::Architecture::X86_64 = target_triple.architecture @@ -352,5 +354,5 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc<dyn TargetIs /// This is the entrypoint for a hot plugged rustc_codegen_cranelift #[no_mangle] pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> { - Box::new(CraneliftCodegenBackend { config: RefCell::new(None) }) + Box::new(CraneliftCodegenBackend { config: None }) } diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index 2ee4ff5cec7..e480f21b9df 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -24,7 +24,7 @@ pub(crate) fn maybe_create_entry_wrapper( }; if main_def_id.is_local() { - let instance = Instance::mono(tcx, main_def_id).polymorphize(tcx); + let instance = Instance::mono(tcx, main_def_id); if module.get_name(tcx.symbol_name(instance).name).is_none() { return; } @@ -75,7 +75,7 @@ pub(crate) fn maybe_create_entry_wrapper( } }; - let instance = Instance::mono(tcx, rust_main_def_id).polymorphize(tcx); + let instance = Instance::mono(tcx, rust_main_def_id); let main_name = tcx.symbol_name(instance).name; let main_sig = get_function_sig(tcx, m.target_config().default_call_conv, instance); @@ -117,8 +117,7 @@ pub(crate) fn maybe_create_entry_wrapper( report.def_id, tcx.mk_args(&[GenericArg::from(main_ret_ty)]), DUMMY_SP, - ) - .polymorphize(tcx); + ); let report_name = tcx.symbol_name(report).name; let report_sig = get_function_sig(tcx, m.target_config().default_call_conv, report); @@ -143,8 +142,7 @@ pub(crate) fn maybe_create_entry_wrapper( start_def_id, tcx.mk_args(&[main_ret_ty.into()]), DUMMY_SP, - ) - .polymorphize(tcx); + ); let start_func_id = import_function(tcx, m, start_instance); let main_val = bcx.ins().func_addr(m.target_config().pointer_type(), main_func_ref); diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 6676e684ca0..c17d1f30fbe 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -1005,9 +1005,6 @@ pub(crate) fn assert_assignable<'tcx>( } } } - (ty::Param(_), _) | (_, ty::Param(_)) if fx.tcx.sess.opts.unstable_opts.polymorphize => { - // No way to check if it is correct or not with polymorphization enabled - } _ => { assert_eq!( from_ty, diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index ab4fdb78bb0..a1f9eab10e7 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -634,6 +634,9 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister { InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r", InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w", InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::preg) => { + unreachable!("clobber-only") + } InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => "r", InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => "f", InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => "r", @@ -653,9 +656,9 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister { InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => "r", InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b", InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => "v", InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr) - | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) - | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => { + | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => { unreachable!("clobber-only") } InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r", @@ -720,6 +723,9 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl cx.type_vector(cx.type_i64(), 2) } InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(), + InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::preg) => { + unreachable!("clobber-only") + } InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => cx.type_f32(), InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(), @@ -730,9 +736,11 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(), InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(), + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => { + cx.type_vector(cx.type_i32(), 4) + } InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr) - | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) - | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => { + | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => { unreachable!("clobber-only") } InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 3846d025537..f67dcf0cb11 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -544,7 +544,10 @@ impl<'gcc, 'tcx> HasWasmCAbiOpt for CodegenCx<'gcc, 'tcx> { impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> { fn x86_abi_opt(&self) -> X86Abi { - X86Abi { regparm: self.tcx.sess.opts.unstable_opts.regparm } + X86Abi { + regparm: self.tcx.sess.opts.unstable_opts.regparm, + reg_struct_return: self.tcx.sess.opts.unstable_opts.reg_struct_return, + } } } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 604678a9af4..79d1a06dd46 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -772,6 +772,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( sym::simd_flog => "log", sym::simd_floor => "floor", sym::simd_fma => "fma", + sym::simd_relaxed_fma => "fma", // FIXME: this should relax to non-fused multiply-add when necessary sym::simd_fpowi => "__builtin_powi", sym::simd_fpow => "pow", sym::simd_fsin => "sin", diff --git a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt index 56b51275a53..457072b1a5b 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt @@ -6,7 +6,6 @@ tests/ui/functions-closures/parallel-codegen-closures.rs tests/ui/linkage-attr/linkage1.rs tests/ui/lto/dylib-works.rs tests/ui/numbers-arithmetic/saturating-float-casts.rs -tests/ui/polymorphization/promoted-function.rs tests/ui/sepcomp/sepcomp-cci.rs tests/ui/sepcomp/sepcomp-extern.rs tests/ui/sepcomp/sepcomp-fns-backwards.rs diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 6ee80c08d4a..d1804cb49ad 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -645,6 +645,7 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> | Arm(ArmInlineAsmRegClass::qreg_low4) => "x", Arm(ArmInlineAsmRegClass::dreg) | Arm(ArmInlineAsmRegClass::qreg) => "w", Hexagon(HexagonInlineAsmRegClass::reg) => "r", + Hexagon(HexagonInlineAsmRegClass::preg) => unreachable!("clobber-only"), LoongArch(LoongArchInlineAsmRegClass::reg) => "r", LoongArch(LoongArchInlineAsmRegClass::freg) => "f", Mips(MipsInlineAsmRegClass::reg) => "r", @@ -655,9 +656,8 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> PowerPC(PowerPCInlineAsmRegClass::reg) => "r", PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b", PowerPC(PowerPCInlineAsmRegClass::freg) => "f", - PowerPC(PowerPCInlineAsmRegClass::cr) - | PowerPC(PowerPCInlineAsmRegClass::xer) - | PowerPC(PowerPCInlineAsmRegClass::vreg) => { + PowerPC(PowerPCInlineAsmRegClass::vreg) => "v", + PowerPC(PowerPCInlineAsmRegClass::cr) | PowerPC(PowerPCInlineAsmRegClass::xer) => { unreachable!("clobber-only") } RiscV(RiscVInlineAsmRegClass::reg) => "r", @@ -813,6 +813,7 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' | Arm(ArmInlineAsmRegClass::qreg_low8) | Arm(ArmInlineAsmRegClass::qreg_low4) => cx.type_vector(cx.type_i64(), 2), Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(), + Hexagon(HexagonInlineAsmRegClass::preg) => unreachable!("clobber-only"), LoongArch(LoongArchInlineAsmRegClass::reg) => cx.type_i32(), LoongArch(LoongArchInlineAsmRegClass::freg) => cx.type_f32(), Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(), @@ -823,9 +824,8 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' PowerPC(PowerPCInlineAsmRegClass::reg) => cx.type_i32(), PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(), PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(), - PowerPC(PowerPCInlineAsmRegClass::cr) - | PowerPC(PowerPCInlineAsmRegClass::xer) - | PowerPC(PowerPCInlineAsmRegClass::vreg) => { + PowerPC(PowerPCInlineAsmRegClass::vreg) => cx.type_vector(cx.type_i32(), 4), + PowerPC(PowerPCInlineAsmRegClass::cr) | PowerPC(PowerPCInlineAsmRegClass::xer) => { unreachable!("clobber-only") } RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), @@ -1040,6 +1040,26 @@ fn llvm_fixup_input<'ll, 'tcx>( let value = bx.or(value, bx.const_u32(0xFFFF_0000)); bx.bitcast(value, bx.type_f32()) } + (PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s)) + if s.primitive() == Primitive::Float(Float::F32) => + { + let value = bx.insert_element( + bx.const_undef(bx.type_vector(bx.type_f32(), 4)), + value, + bx.const_usize(0), + ); + bx.bitcast(value, bx.type_vector(bx.type_f32(), 4)) + } + (PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s)) + if s.primitive() == Primitive::Float(Float::F64) => + { + let value = bx.insert_element( + bx.const_undef(bx.type_vector(bx.type_f64(), 2)), + value, + bx.const_usize(0), + ); + bx.bitcast(value, bx.type_vector(bx.type_f64(), 2)) + } _ => value, } } @@ -1175,6 +1195,18 @@ fn llvm_fixup_output<'ll, 'tcx>( let value = bx.trunc(value, bx.type_i16()); bx.bitcast(value, bx.type_f16()) } + (PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s)) + if s.primitive() == Primitive::Float(Float::F32) => + { + let value = bx.bitcast(value, bx.type_vector(bx.type_f32(), 4)); + bx.extract_element(value, bx.const_usize(0)) + } + (PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s)) + if s.primitive() == Primitive::Float(Float::F64) => + { + let value = bx.bitcast(value, bx.type_vector(bx.type_f64(), 2)); + bx.extract_element(value, bx.const_usize(0)) + } _ => value, } } @@ -1299,6 +1331,16 @@ fn llvm_fixup_output_type<'ll, 'tcx>( { cx.type_f32() } + (PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s)) + if s.primitive() == Primitive::Float(Float::F32) => + { + cx.type_vector(cx.type_f32(), 4) + } + (PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s)) + if s.primitive() == Primitive::Float(Float::F64) => + { + cx.type_vector(cx.type_f64(), 2) + } _ => layout.llvm_type(cx), } } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 48beb9be2b2..f6d2ec24da6 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -148,7 +148,7 @@ fn prepare_lto( // __llvm_profile_counter_bias is pulled in at link time by an undefined reference to // __llvm_profile_runtime, therefore we won't know until link time if this symbol // should have default visibility. - symbols_below_threshold.push(CString::new("__llvm_profile_counter_bias").unwrap()); + symbols_below_threshold.push(c"__llvm_profile_counter_bias".to_owned()); Ok((symbols_below_threshold, upstream_modules)) } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 00f7b479fa7..bde6668929c 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -61,6 +61,7 @@ fn write_output_file<'ll>( dwo_output: Option<&Path>, file_type: llvm::FileType, self_profiler_ref: &SelfProfilerRef, + verify_llvm_ir: bool, ) -> Result<(), FatalError> { debug!("write_output_file output={:?} dwo_output={:?}", output, dwo_output); unsafe { @@ -79,6 +80,7 @@ fn write_output_file<'ll>( output_c.as_ptr(), dwo_output_ptr, file_type, + verify_llvm_ir, ); // Record artifact sizes for self-profiling @@ -507,7 +509,7 @@ fn get_pgo_sample_use_path(config: &ModuleConfig) -> Option<CString> { } fn get_instr_profile_output_path(config: &ModuleConfig) -> Option<CString> { - config.instrument_coverage.then(|| CString::new("default_%m_%p.profraw").unwrap()) + config.instrument_coverage.then(|| c"default_%m_%p.profraw".to_owned()) } pub(crate) unsafe fn llvm_optimize( @@ -840,6 +842,7 @@ pub(crate) unsafe fn codegen( None, llvm::FileType::AssemblyFile, &cgcx.prof, + config.verify_llvm_ir, ) })?; } @@ -877,6 +880,7 @@ pub(crate) unsafe fn codegen( dwo_out, llvm::FileType::ObjectFile, &cgcx.prof, + config.verify_llvm_ir, ) })?; } diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index e0a2de3366c..ec77f32caf4 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -104,7 +104,10 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t let is_hidden = if is_generic { // This is a monomorphization of a generic function. - if !cx.tcx.sess.opts.share_generics() { + if !(cx.tcx.sess.opts.share_generics() + || tcx.codegen_fn_attrs(instance_def_id).inline + == rustc_attr::InlineAttr::Never) + { // When not sharing generics, all instances are in the same // crate and have hidden visibility. true diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 8852dec7d9f..adfe8aeb5c5 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -302,10 +302,9 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { (value, AddressSpace::DATA) } } - GlobalAlloc::Function { instance, .. } => ( - self.get_fn_addr(instance.polymorphize(self.tcx)), - self.data_layout().instruction_address_space, - ), + GlobalAlloc::Function { instance, .. } => { + (self.get_fn_addr(instance), self.data_layout().instruction_address_space) + } GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 841c110b3c8..8218126ea29 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -82,8 +82,8 @@ pub(crate) struct CodegenCx<'ll, 'tcx> { pub isize_ty: &'ll Type, - /// Extra codegen state needed when coverage instrumentation is enabled. - pub coverage_cx: Option<coverageinfo::CrateCoverageContext<'ll, 'tcx>>, + /// Extra per-CGU codegen state needed when coverage instrumentation is enabled. + pub coverage_cx: Option<coverageinfo::CguCoverageContext<'ll, 'tcx>>, pub dbg_cx: Option<debuginfo::CodegenUnitDebugContext<'ll, 'tcx>>, eh_personality: Cell<Option<&'ll Value>>, @@ -525,7 +525,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { let (llcx, llmod) = (&*llvm_module.llcx, llvm_module.llmod()); let coverage_cx = - tcx.sess.instrument_coverage().then(coverageinfo::CrateCoverageContext::new); + tcx.sess.instrument_coverage().then(coverageinfo::CguCoverageContext::new); let dbg_cx = if tcx.sess.opts.debuginfo != DebugInfo::None { let dctx = debuginfo::CodegenUnitDebugContext::new(llmod); @@ -576,7 +576,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { /// Extra state that is only available when coverage instrumentation is enabled. #[inline] #[track_caller] - pub(crate) fn coverage_cx(&self) -> &coverageinfo::CrateCoverageContext<'ll, 'tcx> { + pub(crate) fn coverage_cx(&self) -> &coverageinfo::CguCoverageContext<'ll, 'tcx> { self.coverage_cx.as_ref().expect("only called when coverage instrumentation is enabled") } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs index e3c0df27883..0752c718c70 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs @@ -1,12 +1,13 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxIndexSet; use rustc_index::bit_set::BitSet; +use rustc_middle::mir::CoverageIdsInfo; use rustc_middle::mir::coverage::{ CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, MappingKind, Op, SourceRegion, }; use rustc_middle::ty::Instance; -use tracing::{debug, instrument}; +use tracing::debug; use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind}; @@ -16,17 +17,8 @@ use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind}; pub(crate) struct FunctionCoverageCollector<'tcx> { /// Coverage info that was attached to this function by the instrumentor. function_coverage_info: &'tcx FunctionCoverageInfo, + ids_info: &'tcx CoverageIdsInfo, is_used: bool, - - /// Tracks which counters have been seen, so that we can identify mappings - /// to counters that were optimized out, and set them to zero. - counters_seen: BitSet<CounterId>, - /// Contains all expression IDs that have been seen in an `ExpressionUsed` - /// coverage statement, plus all expression IDs that aren't directly used - /// by any mappings (and therefore do not have expression-used statements). - /// After MIR traversal is finished, we can conclude that any IDs missing - /// from this set must have had their statements deleted by MIR opts. - expressions_seen: BitSet<ExpressionId>, } impl<'tcx> FunctionCoverageCollector<'tcx> { @@ -34,21 +26,24 @@ impl<'tcx> FunctionCoverageCollector<'tcx> { pub(crate) fn new( instance: Instance<'tcx>, function_coverage_info: &'tcx FunctionCoverageInfo, + ids_info: &'tcx CoverageIdsInfo, ) -> Self { - Self::create(instance, function_coverage_info, true) + Self::create(instance, function_coverage_info, ids_info, true) } /// Creates a new set of coverage data for an unused (never called) function. pub(crate) fn unused( instance: Instance<'tcx>, function_coverage_info: &'tcx FunctionCoverageInfo, + ids_info: &'tcx CoverageIdsInfo, ) -> Self { - Self::create(instance, function_coverage_info, false) + Self::create(instance, function_coverage_info, ids_info, false) } fn create( instance: Instance<'tcx>, function_coverage_info: &'tcx FunctionCoverageInfo, + ids_info: &'tcx CoverageIdsInfo, is_used: bool, ) -> Self { let num_counters = function_coverage_info.num_counters; @@ -58,44 +53,7 @@ impl<'tcx> FunctionCoverageCollector<'tcx> { num_counters={num_counters}, num_expressions={num_expressions}, is_used={is_used}" ); - // Create a filled set of expression IDs, so that expressions not - // directly used by mappings will be treated as "seen". - // (If they end up being unused, LLVM will delete them for us.) - let mut expressions_seen = BitSet::new_filled(num_expressions); - // For each expression ID that is directly used by one or more mappings, - // mark it as not-yet-seen. This indicates that we expect to see a - // corresponding `ExpressionUsed` statement during MIR traversal. - for mapping in function_coverage_info.mappings.iter() { - // Currently we only worry about ordinary code mappings. - // For branch and MC/DC mappings, expressions might not correspond - // to any particular point in the control-flow graph. - // (Keep this in sync with the injection of `ExpressionUsed` - // statements in the `InstrumentCoverage` MIR pass.) - if let MappingKind::Code(term) = mapping.kind - && let CovTerm::Expression(id) = term - { - expressions_seen.remove(id); - } - } - - Self { - function_coverage_info, - is_used, - counters_seen: BitSet::new_empty(num_counters), - expressions_seen, - } - } - - /// Marks a counter ID as having been seen in a counter-increment statement. - #[instrument(level = "debug", skip(self))] - pub(crate) fn mark_counter_id_seen(&mut self, id: CounterId) { - self.counters_seen.insert(id); - } - - /// Marks an expression ID as having been seen in an expression-used statement. - #[instrument(level = "debug", skip(self))] - pub(crate) fn mark_expression_id_seen(&mut self, id: ExpressionId) { - self.expressions_seen.insert(id); + Self { function_coverage_info, ids_info, is_used } } /// Identify expressions that will always have a value of zero, and note @@ -117,7 +75,7 @@ impl<'tcx> FunctionCoverageCollector<'tcx> { // (By construction, expressions can only refer to other expressions // that have lower IDs, so one pass is sufficient.) for (id, expression) in self.function_coverage_info.expressions.iter_enumerated() { - if !self.expressions_seen.contains(id) { + if !self.is_used || !self.ids_info.expressions_seen.contains(id) { // If an expression was not seen, it must have been optimized away, // so any operand that refers to it can be replaced with zero. zero_expressions.insert(id); @@ -146,7 +104,7 @@ impl<'tcx> FunctionCoverageCollector<'tcx> { assert_operand_expression_is_lower(id); } - if is_zero_term(&self.counters_seen, &zero_expressions, *operand) { + if is_zero_term(&self.ids_info.counters_seen, &zero_expressions, *operand) { *operand = CovTerm::Zero; } }; @@ -172,17 +130,17 @@ impl<'tcx> FunctionCoverageCollector<'tcx> { pub(crate) fn into_finished(self) -> FunctionCoverage<'tcx> { let zero_expressions = self.identify_zero_expressions(); - let FunctionCoverageCollector { function_coverage_info, is_used, counters_seen, .. } = self; + let FunctionCoverageCollector { function_coverage_info, ids_info, is_used, .. } = self; - FunctionCoverage { function_coverage_info, is_used, counters_seen, zero_expressions } + FunctionCoverage { function_coverage_info, ids_info, is_used, zero_expressions } } } pub(crate) struct FunctionCoverage<'tcx> { pub(crate) function_coverage_info: &'tcx FunctionCoverageInfo, + ids_info: &'tcx CoverageIdsInfo, is_used: bool, - counters_seen: BitSet<CounterId>, zero_expressions: ZeroExpressions, } @@ -238,7 +196,7 @@ impl<'tcx> FunctionCoverage<'tcx> { } fn is_zero_term(&self, term: CovTerm) -> bool { - is_zero_term(&self.counters_seen, &self.zero_expressions, term) + !self.is_used || is_zero_term(&self.ids_info.counters_seen, &self.zero_expressions, term) } } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index b582dd967a7..8c24579fa7c 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -535,9 +535,12 @@ fn add_unused_function_coverage<'tcx>( }), ); - // An unused function's mappings will automatically be rewritten to map to - // zero, because none of its counters/expressions are marked as seen. - let function_coverage = FunctionCoverageCollector::unused(instance, function_coverage_info); + // An unused function's mappings will all be rewritten to map to zero. + let function_coverage = FunctionCoverageCollector::unused( + instance, + function_coverage_info, + tcx.coverage_ids_info(instance.def), + ); cx.coverage_cx().function_coverage_map.borrow_mut().insert(instance, function_coverage); } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index bf773cd2667..c2fcb33f98b 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -21,8 +21,8 @@ mod llvm_cov; pub(crate) mod map_data; mod mapgen; -/// A context object for maintaining all state needed by the coverageinfo module. -pub(crate) struct CrateCoverageContext<'ll, 'tcx> { +/// Extra per-CGU context/state needed for coverage instrumentation. +pub(crate) struct CguCoverageContext<'ll, 'tcx> { /// Coverage data for each instrumented function identified by DefId. pub(crate) function_coverage_map: RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>, @@ -32,7 +32,7 @@ pub(crate) struct CrateCoverageContext<'ll, 'tcx> { covfun_section_name: OnceCell<CString>, } -impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> { +impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> { pub(crate) fn new() -> Self { Self { function_coverage_map: Default::default(), @@ -143,6 +143,13 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { let bx = self; + // Due to LocalCopy instantiation or MIR inlining, coverage statements + // can end up in a crate that isn't doing coverage instrumentation. + // When that happens, we currently just discard those statements, so + // the corresponding code will be undercounted. + // FIXME(Zalathar): Find a better solution for mixed-coverage builds. + let Some(coverage_cx) = &bx.cx.coverage_cx else { return }; + let Some(function_coverage_info) = bx.tcx.instance_mir(instance.def).function_coverage_info.as_deref() else { @@ -150,32 +157,28 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { return; }; - // FIXME(#132395): Unwrapping `coverage_cx` here has led to ICEs in the - // wild, so keep this early-return until we understand why. - let mut coverage_map = match bx.coverage_cx { - Some(ref cx) => cx.function_coverage_map.borrow_mut(), - None => return, - }; - let func_coverage = coverage_map - .entry(instance) - .or_insert_with(|| FunctionCoverageCollector::new(instance, function_coverage_info)); + // Mark the instance as used in this CGU, for coverage purposes. + // This includes functions that were not partitioned into this CGU, + // but were MIR-inlined into one of this CGU's functions. + coverage_cx.function_coverage_map.borrow_mut().entry(instance).or_insert_with(|| { + FunctionCoverageCollector::new( + instance, + function_coverage_info, + bx.tcx.coverage_ids_info(instance.def), + ) + }); match *kind { CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!( "marker statement {kind:?} should have been removed by CleanupPostBorrowck" ), CoverageKind::CounterIncrement { id } => { - func_coverage.mark_counter_id_seen(id); - // We need to explicitly drop the `RefMut` before calling into - // `instrprof_increment`, as that needs an exclusive borrow. - drop(coverage_map); - // The number of counters passed to `llvm.instrprof.increment` might // be smaller than the number originally inserted by the instrumentor, // if some high-numbered counters were removed by MIR optimizations. // If so, LLVM's profiler runtime will use fewer physical counters. let num_counters = - bx.tcx().coverage_ids_info(instance.def).max_counter_id.as_u32() + 1; + bx.tcx().coverage_ids_info(instance.def).num_counters_after_mir_opts(); assert!( num_counters as usize <= function_coverage_info.num_counters, "num_counters disagreement: query says {num_counters} but function info only has {}", @@ -192,23 +195,23 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { ); bx.instrprof_increment(fn_name, hash, num_counters, index); } - CoverageKind::ExpressionUsed { id } => { - func_coverage.mark_expression_id_seen(id); + CoverageKind::ExpressionUsed { id: _ } => { + // Expression-used statements are markers that are handled by + // `coverage_ids_info`, so there's nothing to codegen here. } CoverageKind::CondBitmapUpdate { index, decision_depth } => { - drop(coverage_map); - let cond_bitmap = bx - .coverage_cx() + let cond_bitmap = coverage_cx .try_get_mcdc_condition_bitmap(&instance, decision_depth) .expect("mcdc cond bitmap should have been allocated for updating"); let cond_index = bx.const_i32(index as i32); bx.mcdc_condbitmap_update(cond_index, cond_bitmap); } CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth } => { - drop(coverage_map); - let cond_bitmap = bx.coverage_cx() - .try_get_mcdc_condition_bitmap(&instance, decision_depth) - .expect("mcdc cond bitmap should have been allocated for merging into the global bitmap"); + let cond_bitmap = + coverage_cx.try_get_mcdc_condition_bitmap(&instance, decision_depth).expect( + "mcdc cond bitmap should have been allocated for merging \ + into the global bitmap", + ); assert!( bitmap_idx as usize <= function_coverage_info.mcdc_bitmap_bits, "bitmap index of the decision out of range" diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index ef16e5bb459..59275254022 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -203,6 +203,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( Stub::Struct, unique_type_id, &ptr_type_debuginfo_name, + None, cx.size_and_align_of(ptr_type), NO_SCOPE_METADATA, DIFlags::FlagZero, @@ -259,6 +260,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( layout.fields.offset(abi::WIDE_PTR_ADDR), DIFlags::FlagZero, data_ptr_type_di_node, + None, ), build_field_di_node( cx, @@ -268,6 +270,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( layout.fields.offset(abi::WIDE_PTR_EXTRA), DIFlags::FlagZero, type_di_node(cx, extra_field.ty), + None, ), ] }, @@ -369,6 +372,7 @@ fn build_dyn_type_di_node<'ll, 'tcx>( Stub::Struct, unique_type_id, &type_name, + None, cx.size_and_align_of(dyn_type), NO_SCOPE_METADATA, DIFlags::FlagZero, @@ -467,8 +471,6 @@ pub(crate) fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id), }, ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id), - // Type parameters from polymorphized functions. - ty::Param(_) => build_param_type_di_node(cx, t), _ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t), }; @@ -722,6 +724,14 @@ fn build_cpp_f16_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> DINodeCreation // `f16`'s value to be displayed using a Natvis visualiser in `intrinsic.natvis`. let float_ty = cx.tcx.types.f16; let bits_ty = cx.tcx.types.u16; + let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + match float_ty.kind() { + ty::Adt(def, _) => Some(file_metadata_from_def_id(cx, Some(def.did()))), + _ => None, + } + } else { + None + }; type_map::build_type_with_children( cx, type_map::stub( @@ -729,12 +739,21 @@ fn build_cpp_f16_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> DINodeCreation Stub::Struct, UniqueTypeId::for_ty(cx.tcx, float_ty), "f16", + def_location, cx.size_and_align_of(float_ty), NO_SCOPE_METADATA, DIFlags::FlagZero, ), // Fields: |cx, float_di_node| { + let def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + match bits_ty.kind() { + ty::Adt(def, _) => Some(def.did()), + _ => None, + } + } else { + None + }; smallvec![build_field_di_node( cx, float_di_node, @@ -743,6 +762,7 @@ fn build_cpp_f16_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> DINodeCreation Size::ZERO, DIFlags::FlagZero, type_di_node(cx, bits_ty), + def_id, )] }, NO_GENERICS, @@ -839,6 +859,7 @@ fn build_foreign_type_di_node<'ll, 'tcx>( Stub::Struct, unique_type_id, &compute_debuginfo_type_name(cx.tcx, t, false), + None, cx.size_and_align_of(t), Some(get_namespace_for_item(cx, def_id)), DIFlags::FlagZero, @@ -848,26 +869,6 @@ fn build_foreign_type_di_node<'ll, 'tcx>( ) } -fn build_param_type_di_node<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - t: Ty<'tcx>, -) -> DINodeCreationResult<'ll> { - debug!("build_param_type_di_node: {:?}", t); - let name = format!("{t:?}"); - DINodeCreationResult { - di_node: unsafe { - llvm::LLVMRustDIBuilderCreateBasicType( - DIB(cx), - name.as_c_char_ptr(), - name.len(), - Size::ZERO.bits(), - DW_ATE_unsigned, - ) - }, - already_stored_in_typemap: false, - } -} - pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( tcx: TyCtxt<'tcx>, codegen_unit_name: &str, @@ -989,15 +990,22 @@ fn build_field_di_node<'ll, 'tcx>( offset: Size, flags: DIFlags, type_di_node: &'ll DIType, + def_id: Option<DefId>, ) -> &'ll DIType { + let (file_metadata, line_number) = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers + { + file_metadata_from_def_id(cx, def_id) + } else { + (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) + }; unsafe { llvm::LLVMRustDIBuilderCreateMemberType( DIB(cx), owner, name.as_c_char_ptr(), name.len(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, + file_metadata, + line_number, size_and_align.0.bits(), size_and_align.1.bits() as u32, offset.bits(), @@ -1041,6 +1049,11 @@ fn build_struct_type_di_node<'ll, 'tcx>( let containing_scope = get_namespace_for_item(cx, adt_def.did()); let struct_type_and_layout = cx.layout_of(struct_type); let variant_def = adt_def.non_enum_variant(); + let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(file_metadata_from_def_id(cx, Some(adt_def.did()))) + } else { + None + }; type_map::build_type_with_children( cx, @@ -1049,6 +1062,7 @@ fn build_struct_type_di_node<'ll, 'tcx>( Stub::Struct, unique_type_id, &compute_debuginfo_type_name(cx.tcx, struct_type, false), + def_location, size_and_align_of(struct_type_and_layout), Some(containing_scope), visibility_di_flags(cx, adt_def.did(), adt_def.did()), @@ -1068,6 +1082,11 @@ fn build_struct_type_di_node<'ll, 'tcx>( Cow::Borrowed(f.name.as_str()) }; let field_layout = struct_type_and_layout.field(cx, i); + let def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(f.did) + } else { + None + }; build_field_di_node( cx, owner, @@ -1076,6 +1095,7 @@ fn build_struct_type_di_node<'ll, 'tcx>( struct_type_and_layout.fields.offset(i), visibility_di_flags(cx, f.did, adt_def.did()), type_di_node(cx, field_layout.ty), + def_id, ) }) .collect() @@ -1125,6 +1145,7 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>( layout.fields.offset(index), DIFlags::FlagZero, type_di_node(cx, up_var_ty), + None, ) }) .collect() @@ -1150,6 +1171,7 @@ fn build_tuple_type_di_node<'ll, 'tcx>( Stub::Struct, unique_type_id, &type_name, + None, size_and_align_of(tuple_type_and_layout), NO_SCOPE_METADATA, DIFlags::FlagZero, @@ -1168,6 +1190,7 @@ fn build_tuple_type_di_node<'ll, 'tcx>( tuple_type_and_layout.fields.offset(index), DIFlags::FlagZero, type_di_node(cx, component_type), + None, ) }) .collect() @@ -1189,6 +1212,12 @@ fn build_closure_env_di_node<'ll, 'tcx>( let containing_scope = get_namespace_for_item(cx, def_id); let type_name = compute_debuginfo_type_name(cx.tcx, closure_env_type, false); + let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(file_metadata_from_def_id(cx, Some(def_id))) + } else { + None + }; + type_map::build_type_with_children( cx, type_map::stub( @@ -1196,6 +1225,7 @@ fn build_closure_env_di_node<'ll, 'tcx>( Stub::Struct, unique_type_id, &type_name, + def_location, cx.size_and_align_of(closure_env_type), Some(containing_scope), DIFlags::FlagZero, @@ -1219,6 +1249,11 @@ fn build_union_type_di_node<'ll, 'tcx>( let containing_scope = get_namespace_for_item(cx, union_def_id); let union_ty_and_layout = cx.layout_of(union_type); let type_name = compute_debuginfo_type_name(cx.tcx, union_type, false); + let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(file_metadata_from_def_id(cx, Some(union_def_id))) + } else { + None + }; type_map::build_type_with_children( cx, @@ -1227,6 +1262,7 @@ fn build_union_type_di_node<'ll, 'tcx>( Stub::Union, unique_type_id, &type_name, + def_location, size_and_align_of(union_ty_and_layout), Some(containing_scope), DIFlags::FlagZero, @@ -1239,6 +1275,11 @@ fn build_union_type_di_node<'ll, 'tcx>( .enumerate() .map(|(i, f)| { let field_layout = union_ty_and_layout.field(cx, i); + let def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(f.did) + } else { + None + }; build_field_di_node( cx, owner, @@ -1247,6 +1288,7 @@ fn build_union_type_di_node<'ll, 'tcx>( Size::ZERO, DIFlags::FlagZero, type_di_node(cx, field_layout.ty), + def_id, ) }) .collect() @@ -1321,14 +1363,7 @@ pub(crate) fn build_global_var_di_node<'ll>( // We may want to remove the namespace scope if we're in an extern block (see // https://github.com/rust-lang/rust/pull/46457#issuecomment-351750952). let var_scope = get_namespace_for_item(cx, def_id); - let span = hygiene::walk_chain_collapsed(tcx.def_span(def_id), DUMMY_SP); - - let (file_metadata, line_number) = if !span.is_dummy() { - let loc = cx.lookup_debug_loc(span.lo()); - (file_metadata(cx, &loc.file), loc.line) - } else { - (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) - }; + let (file_metadata, line_number) = file_metadata_from_def_id(cx, Some(def_id)); let is_local_to_unit = is_node_local_to_unit(cx, def_id); @@ -1418,6 +1453,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>( Stub::VTableTy { vtable_holder }, unique_type_id, &vtable_type_name, + None, (size, pointer_align), NO_SCOPE_METADATA, DIFlags::FlagArtificial, @@ -1455,6 +1491,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>( field_offset, DIFlags::FlagZero, field_type_di_node, + None, )) }) .collect() @@ -1606,3 +1643,20 @@ fn tuple_field_name(field_index: usize) -> Cow<'static, str> { .map(|s| Cow::from(*s)) .unwrap_or_else(|| Cow::from(format!("__{field_index}"))) } + +pub(crate) type DefinitionLocation<'ll> = (&'ll DIFile, c_uint); + +pub(crate) fn file_metadata_from_def_id<'ll>( + cx: &CodegenCx<'ll, '_>, + def_id: Option<DefId>, +) -> DefinitionLocation<'ll> { + if let Some(def_id) = def_id + && let span = hygiene::walk_chain_collapsed(cx.tcx.def_span(def_id), DUMMY_SP) + && !span.is_dummy() + { + let loc = cx.lookup_debug_loc(span.lo()); + (file_metadata(cx, &loc.file), loc.line) + } else { + (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) + } +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index 100b046cee2..d374767f187 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -4,7 +4,7 @@ use libc::c_uint; use rustc_abi::{Align, Endian, Size, TagEncoding, VariantIdx, Variants}; use rustc_codegen_ssa::debuginfo::type_names::compute_debuginfo_type_name; use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo}; -use rustc_codegen_ssa::traits::ConstCodegenMethods; +use rustc_codegen_ssa::traits::{ConstCodegenMethods, MiscCodegenMethods}; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; @@ -16,8 +16,8 @@ use crate::debuginfo::metadata::enums::DiscrResult; use crate::debuginfo::metadata::type_map::{self, Stub, UniqueTypeId}; use crate::debuginfo::metadata::{ DINodeCreationResult, NO_GENERICS, NO_SCOPE_METADATA, SmallVec, UNKNOWN_LINE_NUMBER, - build_field_di_node, file_metadata, size_and_align_of, type_di_node, unknown_file_metadata, - visibility_di_flags, + build_field_di_node, file_metadata, file_metadata_from_def_id, size_and_align_of, type_di_node, + unknown_file_metadata, visibility_di_flags, }; use crate::debuginfo::utils::DIB; use crate::llvm::debuginfo::{DIFile, DIFlags, DIType}; @@ -192,6 +192,12 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>( assert!(!wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout)); + let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(file_metadata_from_def_id(cx, Some(enum_adt_def.did()))) + } else { + None + }; + type_map::build_type_with_children( cx, type_map::stub( @@ -199,6 +205,7 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>( type_map::Stub::Union, unique_type_id, &enum_type_name, + def_location, cx.size_and_align_of(enum_type), NO_SCOPE_METADATA, visibility_di_flags(cx, enum_adt_def.did(), enum_adt_def.did()), @@ -262,6 +269,14 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>( unique_type_id: UniqueTypeId<'tcx>, ) -> DINodeCreationResult<'ll> { let coroutine_type = unique_type_id.expect_ty(); + let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + let &ty::Coroutine(coroutine_def_id, _) = coroutine_type.kind() else { + bug!("build_coroutine_di_node() called with non-coroutine type: `{:?}`", coroutine_type) + }; + Some(file_metadata_from_def_id(cx, Some(coroutine_def_id))) + } else { + None + }; let coroutine_type_and_layout = cx.layout_of(coroutine_type); let coroutine_type_name = compute_debuginfo_type_name(cx.tcx, coroutine_type, false); @@ -274,6 +289,7 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>( type_map::Stub::Union, unique_type_id, &coroutine_type_name, + def_location, size_and_align_of(coroutine_type_and_layout), NO_SCOPE_METADATA, DIFlags::FlagZero, @@ -321,6 +337,12 @@ fn build_single_variant_union_fields<'ll, 'tcx>( let tag_base_type_di_node = type_di_node(cx, tag_base_type); let tag_base_type_align = cx.align_of(tag_base_type); + let enum_adt_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(enum_adt_def.did()) + } else { + None + }; + let variant_names_type_di_node = build_variant_names_type_di_node( cx, enum_type_di_node, @@ -328,6 +350,7 @@ fn build_single_variant_union_fields<'ll, 'tcx>( variant_index, Cow::from(enum_adt_def.variant(variant_index).name.as_str()), )), + enum_adt_def_id, ); let variant_struct_type_wrapper_di_node = build_variant_struct_wrapper_type_di_node( @@ -341,6 +364,7 @@ fn build_single_variant_union_fields<'ll, 'tcx>( tag_base_type_di_node, tag_base_type, DiscrResult::NoDiscriminant, + None, ); smallvec![ @@ -354,6 +378,7 @@ fn build_single_variant_union_fields<'ll, 'tcx>( Size::ZERO, visibility_flags, variant_struct_type_wrapper_di_node, + None, ), unsafe { llvm::LLVMRustDIBuilderCreateStaticMemberType( @@ -383,6 +408,12 @@ fn build_union_fields_for_enum<'ll, 'tcx>( ) -> SmallVec<&'ll DIType> { let tag_base_type = tag_base_type(cx.tcx, enum_type_and_layout); + let enum_adt_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(enum_adt_def.did()) + } else { + None + }; + let variant_names_type_di_node = build_variant_names_type_di_node( cx, enum_type_di_node, @@ -390,6 +421,7 @@ fn build_union_fields_for_enum<'ll, 'tcx>( let variant_name = Cow::from(enum_adt_def.variant(variant_index).name.as_str()); (variant_index, variant_name) }), + enum_adt_def_id, ); let visibility_flags = visibility_di_flags(cx, enum_adt_def.did(), enum_adt_def.did()); @@ -447,6 +479,7 @@ fn build_variant_names_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, containing_scope: &'ll DIType, variants: impl Iterator<Item = (VariantIdx, Cow<'tcx, str>)>, + enum_def_id: Option<rustc_span::def_id::DefId>, ) -> &'ll DIType { // Create an enumerator for each variant. super::build_enumeration_type_di_node( @@ -454,6 +487,7 @@ fn build_variant_names_type_di_node<'ll, 'tcx>( "VariantNames", variant_names_enum_base_type(cx), variants.map(|(variant_index, variant_name)| (variant_name, variant_index.as_u32().into())), + enum_def_id, containing_scope, ) } @@ -469,6 +503,7 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>( tag_base_type_di_node: &'ll DIType, tag_base_type: Ty<'tcx>, discr: DiscrResult, + source_info: Option<(&'ll DIFile, c_uint)>, ) -> &'ll DIType { type_map::build_type_with_children( cx, @@ -481,6 +516,7 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>( variant_index, ), &variant_struct_wrapper_type_name(variant_index), + source_info, // NOTE: We use size and align of enum_type, not from variant_layout: size_and_align_of(enum_or_coroutine_type_and_layout), Some(enum_or_coroutine_type_di_node), @@ -530,6 +566,7 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>( Size::ZERO, DIFlags::FlagZero, variant_struct_type_di_node, + None, )); let build_assoc_const = @@ -684,6 +721,11 @@ fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>( variant_range .clone() .map(|variant_index| (variant_index, CoroutineArgs::variant_name(variant_index))), + if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(coroutine_def_id) + } else { + None + }, ); let discriminants: IndexVec<VariantIdx, DiscrResult> = { @@ -776,6 +818,11 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>( tag_base_type_di_node, tag_base_type, variant_member_info.discr, + if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + variant_member_info.source_info + } else { + None + }, ); // We use LLVMRustDIBuilderCreateMemberType() member type directly because @@ -831,6 +878,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>( lo_offset, di_flags, type_di_node, + None, )); unions_fields.push(build_field_di_node( @@ -841,6 +889,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>( hi_offset, DIFlags::FlagZero, type_di_node, + None, )); } else { unions_fields.push(build_field_di_node( @@ -851,6 +900,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>( enum_type_and_layout.fields.offset(tag_field), di_flags, tag_base_type_di_node, + None, )); } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index b3d4a6642a1..65ab22ad89e 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use rustc_abi::{FieldIdx, TagEncoding, VariantIdx, Variants}; use rustc_codegen_ssa::debuginfo::type_names::{compute_debuginfo_type_name, cpp_like_debuginfo}; use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo}; +use rustc_codegen_ssa::traits::MiscCodegenMethods; use rustc_hir::def::CtorKind; use rustc_index::IndexSlice; use rustc_middle::bug; @@ -16,8 +17,8 @@ use super::{SmallVec, size_and_align_of}; use crate::common::{AsCCharPtr, CodegenCx}; use crate::debuginfo::metadata::type_map::{self, Stub}; use crate::debuginfo::metadata::{ - UNKNOWN_LINE_NUMBER, build_field_di_node, build_generic_type_param_di_nodes, type_di_node, - unknown_file_metadata, + UNKNOWN_LINE_NUMBER, build_field_di_node, build_generic_type_param_di_nodes, + file_metadata_from_def_id, type_di_node, unknown_file_metadata, }; use crate::debuginfo::utils::{DIB, create_DIArray, get_namespace_for_item}; use crate::llvm::debuginfo::{DIFlags, DIType}; @@ -68,6 +69,11 @@ fn build_c_style_enum_di_node<'ll, 'tcx>( enum_type_and_layout: TyAndLayout<'tcx>, ) -> DINodeCreationResult<'ll> { let containing_scope = get_namespace_for_item(cx, enum_adt_def.did()); + let enum_adt_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(enum_adt_def.did()) + } else { + None + }; DINodeCreationResult { di_node: build_enumeration_type_di_node( cx, @@ -77,6 +83,7 @@ fn build_c_style_enum_di_node<'ll, 'tcx>( let name = Cow::from(enum_adt_def.variant(variant_index).name.as_str()); (name, discr.val) }), + enum_adt_def_id, containing_scope, ), already_stored_in_typemap: false, @@ -92,6 +99,7 @@ fn build_enumeration_type_di_node<'ll, 'tcx>( type_name: &str, base_type: Ty<'tcx>, enumerators: impl Iterator<Item = (Cow<'tcx, str>, u128)>, + def_id: Option<rustc_span::def_id::DefId>, containing_scope: &'ll DIType, ) -> &'ll DIType { let is_unsigned = match base_type.kind() { @@ -115,14 +123,21 @@ fn build_enumeration_type_di_node<'ll, 'tcx>( }) .collect(); + let (file_metadata, line_number) = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers + { + file_metadata_from_def_id(cx, def_id) + } else { + (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) + }; + unsafe { llvm::LLVMRustDIBuilderCreateEnumerationType( DIB(cx), containing_scope, type_name.as_c_char_ptr(), type_name.len(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, + file_metadata, + line_number, size.bits(), align.bits() as u32, create_DIArray(DIB(cx), &enumerator_di_nodes[..]), @@ -193,6 +208,12 @@ fn build_enum_variant_struct_type_di_node<'ll, 'tcx>( ) -> &'ll DIType { assert_eq!(variant_layout.ty, enum_type_and_layout.ty); + let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(file_metadata_from_def_id(cx, Some(variant_def.def_id))) + } else { + None + }; + type_map::build_type_with_children( cx, type_map::stub( @@ -204,6 +225,7 @@ fn build_enum_variant_struct_type_di_node<'ll, 'tcx>( variant_index, ), variant_def.name.as_str(), + def_location, // NOTE: We use size and align of enum_type, not from variant_layout: size_and_align_of(enum_type_and_layout), Some(enum_type_di_node), @@ -231,6 +253,7 @@ fn build_enum_variant_struct_type_di_node<'ll, 'tcx>( variant_layout.fields.offset(field_index), di_flags, type_di_node(cx, field_layout.ty), + None, ) }) .collect::<SmallVec<_>>() @@ -286,6 +309,7 @@ fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>( Stub::Struct, unique_type_id, &variant_name, + None, size_and_align_of(coroutine_type_and_layout), Some(coroutine_type_di_node), DIFlags::FlagZero, @@ -312,6 +336,7 @@ fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>( variant_layout.fields.offset(field_index), DIFlags::FlagZero, type_di_node(cx, field_type), + None, ) }) .collect(); @@ -331,6 +356,7 @@ fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>( coroutine_type_and_layout.fields.offset(index), DIFlags::FlagZero, type_di_node(cx, upvar_ty), + None, ) }) .collect(); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index d4006691d37..241bf167a81 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -4,7 +4,7 @@ use libc::c_uint; use rustc_abi::{Size, TagEncoding, VariantIdx, Variants}; use rustc_codegen_ssa::debuginfo::type_names::compute_debuginfo_type_name; use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo}; -use rustc_codegen_ssa::traits::ConstCodegenMethods; +use rustc_codegen_ssa::traits::{ConstCodegenMethods, MiscCodegenMethods}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self}; @@ -14,7 +14,8 @@ use crate::common::{AsCCharPtr, CodegenCx}; use crate::debuginfo::metadata::type_map::{self, Stub, StubInfo, UniqueTypeId}; use crate::debuginfo::metadata::{ DINodeCreationResult, NO_GENERICS, SmallVec, UNKNOWN_LINE_NUMBER, file_metadata, - size_and_align_of, type_di_node, unknown_file_metadata, visibility_di_flags, + file_metadata_from_def_id, size_and_align_of, type_di_node, unknown_file_metadata, + visibility_di_flags, }; use crate::debuginfo::utils::{DIB, create_DIArray, get_namespace_for_item}; use crate::llvm::debuginfo::{DIFile, DIFlags, DIType}; @@ -55,6 +56,12 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>( assert!(!wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout)); + let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(file_metadata_from_def_id(cx, Some(enum_adt_def.did()))) + } else { + None + }; + type_map::build_type_with_children( cx, type_map::stub( @@ -62,6 +69,7 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>( Stub::Struct, unique_type_id, &enum_type_name, + def_location, size_and_align_of(enum_type_and_layout), Some(containing_scope), visibility_flags, @@ -84,14 +92,27 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>( enum_type_and_layout.for_variant(cx, variant_index), visibility_flags, ), - source_info: None, + source_info: if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(file_metadata_from_def_id( + cx, + Some(enum_adt_def.variant(variant_index).def_id), + )) + } else { + None + }, }) .collect(); + let enum_adt_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(enum_adt_def.did()) + } else { + None + }; smallvec![build_enum_variant_part_di_node( cx, enum_type_and_layout, enum_type_di_node, + enum_adt_def_id, &variant_member_infos[..], )] }, @@ -134,6 +155,12 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>( let coroutine_type_name = compute_debuginfo_type_name(cx.tcx, coroutine_type, false); + let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(file_metadata_from_def_id(cx, Some(coroutine_def_id))) + } else { + None + }; + type_map::build_type_with_children( cx, type_map::stub( @@ -141,6 +168,7 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>( Stub::Struct, unique_type_id, &coroutine_type_name, + def_location, size_and_align_of(coroutine_type_and_layout), Some(containing_scope), DIFlags::FlagZero, @@ -197,10 +225,16 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>( }) .collect(); + let coroutine_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { + Some(coroutine_def_id) + } else { + None + }; smallvec![build_enum_variant_part_di_node( cx, coroutine_type_and_layout, coroutine_type_di_node, + coroutine_def_id, &variant_struct_type_di_nodes[..], )] }, @@ -228,6 +262,7 @@ fn build_enum_variant_part_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, enum_type_and_layout: TyAndLayout<'tcx>, enum_type_di_node: &'ll DIType, + enum_type_def_id: Option<rustc_span::def_id::DefId>, variant_member_infos: &[VariantMemberInfo<'_, 'll>], ) -> &'ll DIType { let tag_member_di_node = @@ -236,6 +271,13 @@ fn build_enum_variant_part_di_node<'ll, 'tcx>( let variant_part_unique_type_id = UniqueTypeId::for_enum_variant_part(cx.tcx, enum_type_and_layout.ty); + let (file_metadata, line_number) = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers + { + file_metadata_from_def_id(cx, enum_type_def_id) + } else { + (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) + }; + let stub = StubInfo::new( cx, variant_part_unique_type_id, @@ -246,8 +288,8 @@ fn build_enum_variant_part_di_node<'ll, 'tcx>( enum_type_di_node, variant_part_name.as_c_char_ptr(), variant_part_name.len(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, + file_metadata, + line_number, enum_type_and_layout.size.bits(), enum_type_and_layout.align.abi.bits() as u32, DIFlags::FlagZero, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index 4e461476040..a37e719d43f 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -8,7 +8,7 @@ use rustc_macros::HashStable; use rustc_middle::bug; use rustc_middle::ty::{self, PolyExistentialTraitRef, Ty, TyCtxt}; -use super::{SmallVec, UNKNOWN_LINE_NUMBER, unknown_file_metadata}; +use super::{DefinitionLocation, SmallVec, UNKNOWN_LINE_NUMBER, unknown_file_metadata}; use crate::common::{AsCCharPtr, CodegenCx}; use crate::debuginfo::utils::{DIB, create_DIArray, debug_context}; use crate::llvm::debuginfo::{DIFlags, DIScope, DIType}; @@ -186,6 +186,7 @@ pub(super) fn stub<'ll, 'tcx>( kind: Stub<'ll>, unique_type_id: UniqueTypeId<'tcx>, name: &str, + def_location: Option<DefinitionLocation<'ll>>, (size, align): (Size, Align), containing_scope: Option<&'ll DIScope>, flags: DIFlags, @@ -193,6 +194,12 @@ pub(super) fn stub<'ll, 'tcx>( let empty_array = create_DIArray(DIB(cx), &[]); let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx); + let (file_metadata, line_number) = if let Some(def_location) = def_location { + (def_location.0, def_location.1) + } else { + (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) + }; + let metadata = match kind { Stub::Struct | Stub::VTableTy { .. } => { let vtable_holder = match kind { @@ -205,8 +212,8 @@ pub(super) fn stub<'ll, 'tcx>( containing_scope, name.as_c_char_ptr(), name.len(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, + file_metadata, + line_number, size.bits(), align.bits() as u32, flags, @@ -225,8 +232,8 @@ pub(super) fn stub<'ll, 'tcx>( containing_scope, name.as_c_char_ptr(), name.len(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, + file_metadata, + line_number, size.bits(), align.bits() as u32, flags, diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index da7f94e8cf7..c38c5d4c644 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -352,84 +352,84 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { | sym::saturating_add | sym::saturating_sub => { let ty = arg_tys[0]; - match int_type_width_signed(ty, self) { - Some((width, signed)) => match name { - sym::ctlz | sym::cttz => { - let y = self.const_bool(false); - let ret = self.call_intrinsic(&format!("llvm.{name}.i{width}"), &[ - args[0].immediate(), - y, - ]); - - self.intcast(ret, llret_ty, false) - } - sym::ctlz_nonzero => { - let y = self.const_bool(true); - let llvm_name = &format!("llvm.ctlz.i{width}"); - let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); - self.intcast(ret, llret_ty, false) - } - sym::cttz_nonzero => { - let y = self.const_bool(true); - let llvm_name = &format!("llvm.cttz.i{width}"); - let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); - self.intcast(ret, llret_ty, false) - } - sym::ctpop => { - let ret = self.call_intrinsic(&format!("llvm.ctpop.i{width}"), &[args - [0] - .immediate()]); - self.intcast(ret, llret_ty, false) - } - sym::bswap => { - if width == 8 { - args[0].immediate() // byte swap a u8/i8 is just a no-op - } else { - self.call_intrinsic(&format!("llvm.bswap.i{width}"), &[ - args[0].immediate() - ]) - } - } - sym::bitreverse => self - .call_intrinsic(&format!("llvm.bitreverse.i{width}"), &[ + if !ty.is_integral() { + tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { + span, + name, + ty, + }); + return Ok(()); + } + let (size, signed) = ty.int_size_and_signed(self.tcx); + let width = size.bits(); + match name { + sym::ctlz | sym::cttz => { + let y = self.const_bool(false); + let ret = self.call_intrinsic(&format!("llvm.{name}.i{width}"), &[ + args[0].immediate(), + y, + ]); + + self.intcast(ret, llret_ty, false) + } + sym::ctlz_nonzero => { + let y = self.const_bool(true); + let llvm_name = &format!("llvm.ctlz.i{width}"); + let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); + self.intcast(ret, llret_ty, false) + } + sym::cttz_nonzero => { + let y = self.const_bool(true); + let llvm_name = &format!("llvm.cttz.i{width}"); + let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); + self.intcast(ret, llret_ty, false) + } + sym::ctpop => { + let ret = self.call_intrinsic(&format!("llvm.ctpop.i{width}"), &[ + args[0].immediate() + ]); + self.intcast(ret, llret_ty, false) + } + sym::bswap => { + if width == 8 { + args[0].immediate() // byte swap a u8/i8 is just a no-op + } else { + self.call_intrinsic(&format!("llvm.bswap.i{width}"), &[ args[0].immediate() - ]), - sym::rotate_left | sym::rotate_right => { - let is_left = name == sym::rotate_left; - let val = args[0].immediate(); - let raw_shift = args[1].immediate(); - // rotate = funnel shift with first two args the same - let llvm_name = - &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width); - - // llvm expects shift to be the same type as the values, but rust - // always uses `u32`. - let raw_shift = self.intcast(raw_shift, self.val_ty(val), false); - - self.call_intrinsic(llvm_name, &[val, val, raw_shift]) + ]) } - sym::saturating_add | sym::saturating_sub => { - let is_add = name == sym::saturating_add; - let lhs = args[0].immediate(); - let rhs = args[1].immediate(); - let llvm_name = &format!( - "llvm.{}{}.sat.i{}", - if signed { 's' } else { 'u' }, - if is_add { "add" } else { "sub" }, - width - ); - self.call_intrinsic(llvm_name, &[lhs, rhs]) - } - _ => bug!(), - }, - None => { - tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { - span, - name, - ty, - }); - return Ok(()); } + sym::bitreverse => self + .call_intrinsic(&format!("llvm.bitreverse.i{width}"), &[ + args[0].immediate() + ]), + sym::rotate_left | sym::rotate_right => { + let is_left = name == sym::rotate_left; + let val = args[0].immediate(); + let raw_shift = args[1].immediate(); + // rotate = funnel shift with first two args the same + let llvm_name = + &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width); + + // llvm expects shift to be the same type as the values, but rust + // always uses `u32`. + let raw_shift = self.intcast(raw_shift, self.val_ty(val), false); + + self.call_intrinsic(llvm_name, &[val, val, raw_shift]) + } + sym::saturating_add | sym::saturating_sub => { + let is_add = name == sym::saturating_add; + let lhs = args[0].immediate(); + let rhs = args[1].immediate(); + let llvm_name = &format!( + "llvm.{}{}.sat.i{}", + if signed { 's' } else { 'u' }, + if is_add { "add" } else { "sub" }, + width + ); + self.call_intrinsic(llvm_name, &[lhs, rhs]) + } + _ => bug!(), } } @@ -1534,6 +1534,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( sym::simd_flog => ("log", bx.type_func(&[vec_ty], vec_ty)), sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)), sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)), + sym::simd_relaxed_fma => ("fmuladd", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)), sym::simd_fpowi => ("powi", bx.type_func(&[vec_ty, bx.type_i32()], vec_ty)), sym::simd_fpow => ("pow", bx.type_func(&[vec_ty, vec_ty], vec_ty)), sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)), @@ -1572,6 +1573,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( | sym::simd_fpowi | sym::simd_fsin | sym::simd_fsqrt + | sym::simd_relaxed_fma | sym::simd_round | sym::simd_trunc ) { @@ -2529,19 +2531,3 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span_bug!(span, "unknown SIMD intrinsic"); } - -// Returns the width of an int Ty, and if it's signed or not -// Returns None if the type is not an integer -// FIXME: there’s multiple of this functions, investigate using some of the already existing -// stuffs. -fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_, '_>) -> Option<(u64, bool)> { - match ty.kind() { - ty::Int(t) => { - Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), true)) - } - ty::Uint(t) => { - Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), false)) - } - _ => None, - } -} diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 3dfb86d422d..5235891a18d 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -36,7 +36,7 @@ use rustc_codegen_ssa::back::write::{ use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen}; use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError}; +use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; @@ -370,19 +370,14 @@ impl CodegenBackend for LlvmCodegenBackend { (codegen_results, work_products) } - fn link( - &self, - sess: &Session, - codegen_results: CodegenResults, - outputs: &OutputFilenames, - ) -> Result<(), ErrorGuaranteed> { + fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) { use rustc_codegen_ssa::back::link::link_binary; use crate::back::archive::LlvmArchiveBuilderBuilder; // Run the linker on any artifacts that resulted from the LLVM run. // This should produce either a finished executable or library. - link_binary(sess, &LlvmArchiveBuilderBuilder, codegen_results, outputs) + link_binary(sess, &LlvmArchiveBuilderBuilder, codegen_results, outputs); } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 17b0ec4b936..b1ace0033ba 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2240,6 +2240,7 @@ unsafe extern "C" { Output: *const c_char, DwoOutput: *const c_char, FileType: FileType, + VerifyIR: bool, ) -> LLVMRustResult; pub fn LLVMRustOptimize<'a>( M: &'a Module, diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index e4c3e748cb5..8baa69cefe1 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -10,6 +10,15 @@ use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; +fn round_up_to_alignment<'ll>( + bx: &mut Builder<'_, 'll, '_>, + mut value: &'ll Value, + align: Align, +) -> &'ll Value { + value = bx.add(value, bx.cx().const_i32(align.bytes() as i32 - 1)); + return bx.and(value, bx.cx().const_i32(-(align.bytes() as i32))); +} + fn round_pointer_up_to_alignment<'ll>( bx: &mut Builder<'_, 'll, '_>, addr: &'ll Value, @@ -17,8 +26,7 @@ fn round_pointer_up_to_alignment<'ll>( ptr_ty: &'ll Type, ) -> &'ll Value { let mut ptr_as_int = bx.ptrtoint(addr, bx.cx().type_isize()); - ptr_as_int = bx.add(ptr_as_int, bx.cx().const_i32(align.bytes() as i32 - 1)); - ptr_as_int = bx.and(ptr_as_int, bx.cx().const_i32(-(align.bytes() as i32))); + ptr_as_int = round_up_to_alignment(bx, ptr_as_int, align); bx.inttoptr(ptr_as_int, ptr_ty) } @@ -270,6 +278,106 @@ fn emit_s390x_va_arg<'ll, 'tcx>( bx.load(val_type, val_addr, layout.align.abi) } +fn emit_xtensa_va_arg<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + list: OperandRef<'tcx, &'ll Value>, + target_ty: Ty<'tcx>, +) -> &'ll Value { + // Implementation of va_arg for Xtensa. There doesn't seem to be an authoritative source for + // this, other than "what GCC does". + // + // The va_list type has three fields: + // struct __va_list_tag { + // int32_t *va_stk; // Arguments passed on the stack + // int32_t *va_reg; // Arguments passed in registers, saved to memory by the prologue. + // int32_t va_ndx; // Offset into the arguments, in bytes + // }; + // + // The first 24 bytes (equivalent to 6 registers) come from va_reg, the rest from va_stk. + // Thus if va_ndx is less than 24, the next va_arg *may* read from va_reg, + // otherwise it must come from va_stk. + // + // Primitive arguments are never split between registers and the stack. For example, if loading an 8 byte + // primitive value and va_ndx = 20, we instead bump the offset and read everything from va_stk. + let va_list_addr = list.immediate(); + // FIXME: handle multi-field structs that split across regsave/stack? + let layout = bx.cx.layout_of(target_ty); + let from_stack = bx.append_sibling_block("va_arg.from_stack"); + let from_regsave = bx.append_sibling_block("va_arg.from_regsave"); + let end = bx.append_sibling_block("va_arg.end"); + + // (*va).va_ndx + let va_reg_offset = 4; + let va_ndx_offset = va_reg_offset + 4; + let offset_ptr = + bx.inbounds_gep(bx.type_i8(), va_list_addr, &[bx.cx.const_usize(va_ndx_offset)]); + + let offset = bx.load(bx.type_i32(), offset_ptr, bx.tcx().data_layout.i32_align.abi); + let offset = round_up_to_alignment(bx, offset, layout.align.abi); + + let slot_size = layout.size.align_to(Align::from_bytes(4).unwrap()).bytes() as i32; + + // Update the offset in va_list, by adding the slot's size. + let offset_next = bx.add(offset, bx.const_i32(slot_size)); + + // Figure out where to look for our value. We do that by checking the end of our slot (offset_next). + // If that is within the regsave area, then load from there. Otherwise load from the stack area. + let regsave_size = bx.const_i32(24); + let use_regsave = bx.icmp(IntPredicate::IntULE, offset_next, regsave_size); + bx.cond_br(use_regsave, from_regsave, from_stack); + + bx.switch_to_block(from_regsave); + // update va_ndx + bx.store(offset_next, offset_ptr, bx.tcx().data_layout.pointer_align.abi); + + // (*va).va_reg + let regsave_area_ptr = + bx.inbounds_gep(bx.type_i8(), va_list_addr, &[bx.cx.const_usize(va_reg_offset)]); + let regsave_area = + bx.load(bx.type_ptr(), regsave_area_ptr, bx.tcx().data_layout.pointer_align.abi); + let regsave_value_ptr = bx.inbounds_gep(bx.type_i8(), regsave_area, &[offset]); + bx.br(end); + + bx.switch_to_block(from_stack); + + // The first time we switch from regsave to stack we needs to adjust our offsets a bit. + // va_stk is set up such that the first stack argument is always at va_stk + 32. + // The corrected offset is written back into the va_list struct. + + // let offset_corrected = cmp::max(offset, 32); + let stack_offset_start = bx.const_i32(32); + let needs_correction = bx.icmp(IntPredicate::IntULE, offset, stack_offset_start); + let offset_corrected = bx.select(needs_correction, stack_offset_start, offset); + + // let offset_next_corrected = offset_corrected + slot_size; + // va_ndx = offset_next_corrected; + let offset_next_corrected = bx.add(offset_next, bx.const_i32(slot_size)); + // update va_ndx + bx.store(offset_next_corrected, offset_ptr, bx.tcx().data_layout.pointer_align.abi); + + // let stack_value_ptr = unsafe { (*va).va_stk.byte_add(offset_corrected) }; + let stack_area_ptr = bx.inbounds_gep(bx.type_i8(), va_list_addr, &[bx.cx.const_usize(0)]); + let stack_area = bx.load(bx.type_ptr(), stack_area_ptr, bx.tcx().data_layout.pointer_align.abi); + let stack_value_ptr = bx.inbounds_gep(bx.type_i8(), stack_area, &[offset_corrected]); + bx.br(end); + + bx.switch_to_block(end); + + // On big-endian, for values smaller than the slot size we'd have to align the read to the end + // of the slot rather than the start. While the ISA and GCC support big-endian, all the Xtensa + // targets supported by rustc are litte-endian so don't worry about it. + + // if from_regsave { + // unsafe { *regsave_value_ptr } + // } else { + // unsafe { *stack_value_ptr } + // } + assert!(bx.tcx().sess.target.endian == Endian::Little); + let value_ptr = + bx.phi(bx.type_ptr(), &[regsave_value_ptr, stack_value_ptr], &[from_regsave, from_stack]); + return bx.load(layout.llvm_type(bx), value_ptr, layout.align.abi); +} + pub(super) fn emit_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, addr: OperandRef<'tcx, &'ll Value>, @@ -302,6 +410,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( let indirect: bool = target_ty_size > 8 || !target_ty_size.is_power_of_two(); emit_ptr_va_arg(bx, addr, target_ty, indirect, Align::from_bytes(8).unwrap(), false) } + "xtensa" => emit_xtensa_va_arg(bx, addr, target_ty), // For all other architecture/OS combinations fall back to using // the LLVM va_arg instruction. // https://llvm.org/docs/LangRef.html#va-arg-instruction diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index b898cfec796..c81e36dfc8d 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -17,6 +17,7 @@ regex = "1.4" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } +rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -42,7 +43,7 @@ tempfile = "3.2" thin-vec = "0.2.12" thorin-dwp = "0.8" tracing = "0.1" -wasm-encoder = "0.216.0" +wasm-encoder = "0.219" # tidy-alphabetical-end [target.'cfg(unix)'.dependencies] diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 62db3d5a98c..56188714b44 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -201,6 +201,11 @@ codegen_ssa_missing_memory_ordering = Atomic intrinsic missing memory ordering codegen_ssa_missing_query_depgraph = found CGU-reuse attribute but `-Zquery-dep-graph` was not specified +codegen_ssa_mixed_export_name_and_no_mangle = `{$no_mangle_attr}` attribute may not be used in combination with `#[export_name]` + .label = `{$no_mangle_attr}` is ignored + .note = `#[export_name]` takes precedence + .suggestion = remove the `{$no_mangle_attr}` attribute + codegen_ssa_msvc_missing_linker = the msvc targets depend on the msvc linker but `link.exe` was not found codegen_ssa_multiple_external_func_decl = multiple declarations of external function `{$function}` from library `{$library_name}` have different calling conventions diff --git a/compiler/rustc_codegen_ssa/src/back/apple.rs b/compiler/rustc_codegen_ssa/src/back/apple.rs index 93d90cd16b2..d9c5c3e5af9 100644 --- a/compiler/rustc_codegen_ssa/src/back/apple.rs +++ b/compiler/rustc_codegen_ssa/src/back/apple.rs @@ -97,7 +97,7 @@ fn minimum_deployment_target(target: &Target) -> OSVersion { } /// Name of the environment variable used to fetch the deployment target on the given OS. -fn deployment_target_env_var(os: &str) -> &'static str { +pub fn deployment_target_env_var(os: &str) -> &'static str { match os { "macos" => "MACOSX_DEPLOYMENT_TARGET", "ios" => "IPHONEOS_DEPLOYMENT_TARGET", diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 651485a1876..f8b3ba79c0d 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -15,7 +15,7 @@ use rustc_ast::CRATE_NODE_ID; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; -use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError}; +use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; @@ -71,7 +71,7 @@ pub fn link_binary( archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: CodegenResults, outputs: &OutputFilenames, -) -> Result<(), ErrorGuaranteed> { +) { let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); let mut tempfiles_for_stdout_output: Vec<PathBuf> = Vec::new(); @@ -119,7 +119,7 @@ pub fn link_binary( &codegen_results, RlibFlavor::Normal, &path, - )? + ) .build(&out_filename); } CrateType::Staticlib => { @@ -129,7 +129,7 @@ pub fn link_binary( &codegen_results, &out_filename, &path, - )?; + ); } _ => { link_natively( @@ -139,7 +139,7 @@ pub fn link_binary( &out_filename, &codegen_results, path.as_ref(), - )?; + ); } } if sess.opts.json_artifact_notifications { @@ -225,8 +225,6 @@ pub fn link_binary( maybe_remove_temps_from_module(preserve_objects, preserve_dwarf_objects, module); } }); - - Ok(()) } // Crate type is not passed when calculating the dylibs to include for LTO. In that case all @@ -298,7 +296,7 @@ fn link_rlib<'a>( codegen_results: &CodegenResults, flavor: RlibFlavor, tmpdir: &MaybeTempDir, -) -> Result<Box<dyn ArchiveBuilder + 'a>, ErrorGuaranteed> { +) -> Box<dyn ArchiveBuilder + 'a> { let mut ab = archive_builder_builder.new_archive_builder(sess); let trailing_metadata = match flavor { @@ -374,7 +372,7 @@ fn link_rlib<'a>( { let path = find_native_static_library(filename.as_str(), true, sess); let src = read(path) - .map_err(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e }))?; + .unwrap_or_else(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e })); let (data, _) = create_wrapper_file(sess, ".bundled_lib".to_string(), &src); let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str()); packed_bundled_libs.push(wrapper_file); @@ -392,7 +390,7 @@ fn link_rlib<'a>( codegen_results.crate_info.used_libraries.iter(), tmpdir.as_ref(), true, - )? { + ) { ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| { sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: output_path, error }); }); @@ -433,7 +431,7 @@ fn link_rlib<'a>( ab.add_file(&lib) } - Ok(ab) + ab } /// Extract all symbols defined in raw-dylib libraries, collated by library name. @@ -445,7 +443,7 @@ fn link_rlib<'a>( fn collate_raw_dylibs<'a>( sess: &Session, used_libraries: impl IntoIterator<Item = &'a NativeLib>, -) -> Result<Vec<(String, Vec<DllImport>)>, ErrorGuaranteed> { +) -> Vec<(String, Vec<DllImport>)> { // Use index maps to preserve original order of imports and libraries. let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default(); @@ -469,15 +467,13 @@ fn collate_raw_dylibs<'a>( } } } - if let Some(guar) = sess.dcx().has_errors() { - return Err(guar); - } - Ok(dylib_table + sess.dcx().abort_if_errors(); + dylib_table .into_iter() .map(|(name, imports)| { (name, imports.into_iter().map(|(_, import)| import.clone()).collect()) }) - .collect()) + .collect() } fn create_dll_import_libs<'a>( @@ -486,8 +482,8 @@ fn create_dll_import_libs<'a>( used_libraries: impl IntoIterator<Item = &'a NativeLib>, tmpdir: &Path, is_direct_dependency: bool, -) -> Result<Vec<PathBuf>, ErrorGuaranteed> { - Ok(collate_raw_dylibs(sess, used_libraries)? +) -> Vec<PathBuf> { + collate_raw_dylibs(sess, used_libraries) .into_iter() .map(|(raw_dylib_name, raw_dylib_imports)| { let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; @@ -537,7 +533,7 @@ fn create_dll_import_libs<'a>( output_path }) - .collect()) + .collect() } /// Create a static archive. @@ -557,7 +553,7 @@ fn link_staticlib( codegen_results: &CodegenResults, out_filename: &Path, tempdir: &MaybeTempDir, -) -> Result<(), ErrorGuaranteed> { +) { info!("preparing staticlib to {:?}", out_filename); let mut ab = link_rlib( sess, @@ -565,7 +561,7 @@ fn link_staticlib( codegen_results, RlibFlavor::StaticlibBase, tempdir, - )?; + ); let mut all_native_libs = vec![]; let res = each_linked_rlib( @@ -656,8 +652,6 @@ fn link_staticlib( print_native_static_libs(sess, &print.out, &all_native_libs, &all_rust_dylibs); } } - - Ok(()) } /// Use `thorin` (rust implementation of a dwarf packaging utility) to link DWARF objects into a @@ -773,7 +767,7 @@ fn link_natively( out_filename: &Path, codegen_results: &CodegenResults, tmpdir: &Path, -) -> Result<(), ErrorGuaranteed> { +) { info!("preparing {:?} to {:?}", crate_type, out_filename); let (linker_path, flavor) = linker_and_flavor(sess); let self_contained_components = self_contained_components(sess, crate_type); @@ -797,7 +791,7 @@ fn link_natively( temp_filename, codegen_results, self_contained_components, - )?; + ); linker::disable_localization(&mut cmd); @@ -1177,8 +1171,6 @@ fn link_natively( ab.add_file(temp_filename); ab.build(out_filename); } - - Ok(()) } fn strip_symbols_with_external_utility( @@ -1386,7 +1378,7 @@ fn link_sanitizer_runtime( let filename = format!("rustc{channel}_rt.{name}"); let path = find_sanitizer_runtime(sess, &filename); let rpath = path.to_str().expect("non-utf8 component in path"); - linker.cc_args(&["-Wl,-rpath", "-Xlinker", rpath]); + linker.link_args(&["-rpath", rpath]); linker.link_dylib_by_name(&filename, false, true); } else if sess.target.is_like_msvc && flavor == LinkerFlavor::Msvc(Lld::No) && name == "asan" { // MSVC provides the `/INFERASANLIBS` argument to automatically find the @@ -2210,7 +2202,7 @@ fn add_rpath_args( is_like_osx: sess.target.is_like_osx, linker_is_gnu: sess.target.linker_flavor.is_gnu(), }; - cmd.cc_args(&rpath::get_rpath_flags(&rpath_config)); + cmd.link_args(&rpath::get_rpath_linker_args(&rpath_config)); } } @@ -2232,7 +2224,7 @@ fn linker_with_args( out_filename: &Path, codegen_results: &CodegenResults, self_contained_components: LinkSelfContainedComponents, -) -> Result<Command, ErrorGuaranteed> { +) -> Command { let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled(); let cmd = &mut *super::linker::get_linker( sess, @@ -2356,7 +2348,7 @@ fn linker_with_args( codegen_results.crate_info.used_libraries.iter(), tmpdir, true, - )? { + ) { cmd.add_object(&output_path); } // As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case @@ -2388,7 +2380,7 @@ fn linker_with_args( native_libraries_from_nonstatics, tmpdir, false, - )? { + ) { cmd.add_object(&output_path); } @@ -2435,7 +2427,7 @@ fn linker_with_args( // to it and remove the option. Currently the last holdout is wasm32-unknown-emscripten. add_post_link_args(cmd, sess, flavor); - Ok(cmd.take_cmd()) + cmd.take_cmd() } fn add_order_independent_options( diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 6ee599c9964..05dfbd40a0a 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -24,6 +24,9 @@ use super::command::Command; use super::symbol_export; use crate::errors; +#[cfg(test)] +mod tests; + /// Disables non-English messages from localized linkers. /// Such messages may cause issues with text encoding on Windows (#35785) /// and prevent inspection of linker output in case of errors, which we occasionally do. @@ -178,23 +181,42 @@ fn verbatim_args<L: Linker + ?Sized>( } l } +/// Add underlying linker arguments to C compiler command, by wrapping them in +/// `-Wl` or `-Xlinker`. +fn convert_link_args_to_cc_args(cmd: &mut Command, args: impl IntoIterator<Item: AsRef<OsStr>>) { + let mut combined_arg = OsString::from("-Wl"); + for arg in args { + // If the argument itself contains a comma, we need to emit it + // as `-Xlinker`, otherwise we can use `-Wl`. + if arg.as_ref().as_encoded_bytes().contains(&b',') { + // Emit current `-Wl` argument, if any has been built. + if combined_arg != OsStr::new("-Wl") { + cmd.arg(combined_arg); + // Begin next `-Wl` argument. + combined_arg = OsString::from("-Wl"); + } + + // Emit `-Xlinker` argument. + cmd.arg("-Xlinker"); + cmd.arg(arg); + } else { + // Append to `-Wl` argument. + combined_arg.push(","); + combined_arg.push(arg); + } + } + // Emit final `-Wl` argument. + if combined_arg != OsStr::new("-Wl") { + cmd.arg(combined_arg); + } +} /// Arguments for the underlying linker. /// Add options to pass them through cc wrapper if `Linker` is a cc wrapper. -fn link_args<L: Linker + ?Sized>( - l: &mut L, - args: impl IntoIterator<Item: AsRef<OsStr>, IntoIter: ExactSizeIterator>, -) -> &mut L { - let args = args.into_iter(); +fn link_args<L: Linker + ?Sized>(l: &mut L, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut L { if !l.is_cc() { verbatim_args(l, args); - } else if args.len() != 0 { - // FIXME: Support arguments with commas, see `rpaths_to_flags` for the example. - let mut combined_arg = OsString::from("-Wl"); - for arg in args { - combined_arg.push(","); - combined_arg.push(arg); - } - l.cmd().arg(combined_arg); + } else { + convert_link_args_to_cc_args(l.cmd(), args); } l } @@ -224,7 +246,7 @@ macro_rules! generate_arg_methods { verbatim_args(self, iter::once(arg)) } #[allow(unused)] - pub(crate) fn link_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>, IntoIter: ExactSizeIterator>) -> &mut Self { + pub(crate) fn link_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self { link_args(self, args) } #[allow(unused)] diff --git a/compiler/rustc_codegen_ssa/src/back/linker/tests.rs b/compiler/rustc_codegen_ssa/src/back/linker/tests.rs new file mode 100644 index 00000000000..bf3e8c90200 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/back/linker/tests.rs @@ -0,0 +1,32 @@ +use super::*; + +#[test] +fn test_rpaths_to_args() { + let mut cmd = Command::new("foo"); + convert_link_args_to_cc_args(&mut cmd, &["-rpath", "path1", "-rpath", "path2"]); + assert_eq!(cmd.get_args(), [OsStr::new("-Wl,-rpath,path1,-rpath,path2")]); +} + +#[test] +fn test_xlinker() { + let mut cmd = Command::new("foo"); + convert_link_args_to_cc_args(&mut cmd, &[ + "arg1", + "arg2", + "arg3,with,comma", + "arg4,with,comma", + "arg5", + "arg6,with,comma", + ]); + + assert_eq!(cmd.get_args(), [ + OsStr::new("-Wl,arg1,arg2"), + OsStr::new("-Xlinker"), + OsStr::new("arg3,with,comma"), + OsStr::new("-Xlinker"), + OsStr::new("arg4,with,comma"), + OsStr::new("-Wl,arg5"), + OsStr::new("-Xlinker"), + OsStr::new("arg6,with,comma"), + ]); +} diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs index 56a808df6b0..d633cc98ac8 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs @@ -13,39 +13,27 @@ pub(super) struct RPathConfig<'a> { pub linker_is_gnu: bool, } -pub(super) fn get_rpath_flags(config: &RPathConfig<'_>) -> Vec<OsString> { +pub(super) fn get_rpath_linker_args(config: &RPathConfig<'_>) -> Vec<OsString> { debug!("preparing the RPATH!"); let rpaths = get_rpaths(config); - let mut flags = rpaths_to_flags(rpaths); + let mut args = Vec::with_capacity(rpaths.len() * 2); // the minimum needed capacity + + for rpath in rpaths { + args.push("-rpath".into()); + args.push(rpath); + } if config.linker_is_gnu { // Use DT_RUNPATH instead of DT_RPATH if available - flags.push("-Wl,--enable-new-dtags".into()); + args.push("--enable-new-dtags".into()); // Set DF_ORIGIN for substitute $ORIGIN - flags.push("-Wl,-z,origin".into()); - } - - flags -} - -fn rpaths_to_flags(rpaths: Vec<OsString>) -> Vec<OsString> { - let mut ret = Vec::with_capacity(rpaths.len()); // the minimum needed capacity - - for rpath in rpaths { - if rpath.to_string_lossy().contains(',') { - ret.push("-Wl,-rpath".into()); - ret.push("-Xlinker".into()); - ret.push(rpath); - } else { - let mut single_arg = OsString::from("-Wl,-rpath,"); - single_arg.push(rpath); - ret.push(single_arg); - } + args.push("-z".into()); + args.push("origin".into()); } - ret + args } fn get_rpaths(config: &RPathConfig<'_>) -> Vec<OsString> { diff --git a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs index 39034842d10..f1a30105c59 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs @@ -1,13 +1,4 @@ -use std::ffi::OsString; -use std::path::{Path, PathBuf}; - -use super::{RPathConfig, get_rpath_relative_to_output, minimize_rpaths, rpaths_to_flags}; - -#[test] -fn test_rpaths_to_flags() { - let flags = rpaths_to_flags(vec!["path1".into(), "path2".into()]); - assert_eq!(flags, ["-Wl,-rpath,path1", "-Wl,-rpath,path2"]); -} +use super::*; #[test] fn test_minimize1() { @@ -69,15 +60,3 @@ fn test_rpath_relative_issue_119571() { // Should not panic when lib only contains filename. let _ = get_rpath_relative_to_output(config, Path::new("libstd.so")); } - -#[test] -fn test_xlinker() { - let args = rpaths_to_flags(vec!["a/normal/path".into(), "a,comma,path".into()]); - - assert_eq!(args, vec![ - OsString::from("-Wl,-rpath,a/normal/path"), - OsString::from("-Wl,-rpath"), - OsString::from("-Xlinker"), - OsString::from("a,comma,path") - ]); -} diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index d9152c5d080..788a8a13b3e 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -282,7 +282,7 @@ fn exported_symbols_provider_local( })); } - if tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics() { + if tcx.local_crate_exports_generics() { use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility}; use rustc_middle::ty::InstanceKind; @@ -310,6 +310,16 @@ fn exported_symbols_provider_local( continue; } + if !tcx.sess.opts.share_generics() { + if tcx.codegen_fn_attrs(mono_item.def_id()).inline == rustc_attr::InlineAttr::Never + { + // this is OK, we explicitly allow sharing inline(never) across crates even + // without share-generics. + } else { + continue; + } + } + match *mono_item { MonoItem::Fn(Instance { def: InstanceKind::Item(def), args }) => { if args.non_erasable_generics().next().is_some() { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 7c0e9cfd5a7..90d48d6ee7e 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1883,7 +1883,11 @@ impl Translate for SharedEmitter { } impl Emitter for SharedEmitter { - fn emit_diagnostic(&mut self, mut diag: rustc_errors::DiagInner) { + fn emit_diagnostic( + &mut self, + mut diag: rustc_errors::DiagInner, + _registry: &rustc_errors::registry::Registry, + ) { // Check that we aren't missing anything interesting when converting to // the cut-down local `DiagInner`. assert_eq!(diag.span, MultiSpan::new()); @@ -2028,8 +2032,6 @@ pub struct OngoingCodegen<B: ExtraBackendMethods> { impl<B: ExtraBackendMethods> OngoingCodegen<B> { pub fn join(self, sess: &Session) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) { - let _timer = sess.timer("finish_ongoing_codegen"); - self.shared_emitter_main.check(sess, true); let compiled_modules = sess.time("join_worker_thread", || match self.coordinator.join() { Ok(Ok(compiled_modules)) => compiled_modules, diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index a5acd8170ab..11744eabab0 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -3,11 +3,10 @@ use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr, list_contains_nam use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err}; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS; -use rustc_hir::{LangItem, lang_items}; +use rustc_hir::{self as hir, HirId, LangItem, lang_items}; use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, }; @@ -78,6 +77,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let mut inline_span = None; let mut link_ordinal_span = None; let mut no_sanitize_span = None; + let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default(); for attr in attrs.iter() { // In some cases, attribute are only valid on functions, but it's the `check_attr` @@ -116,7 +116,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, sym::no_mangle => { if tcx.opt_item_name(did.to_def_id()).is_some() { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + mixed_export_name_no_mangle_lint_state.track_no_mangle( + attr.span, + tcx.local_def_id_to_hir_id(did), + attr, + ); } else { tcx.dcx() .struct_span_err( @@ -240,6 +245,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { .emit(); } codegen_fn_attrs.export_name = Some(s); + mixed_export_name_no_mangle_lint_state.track_export_name(attr.span); } } sym::target_feature => { @@ -513,6 +519,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } + mixed_export_name_no_mangle_lint_state.lint_if_mixed(tcx); + codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| { if !attr.has_name(sym::inline) { return ia; @@ -779,6 +787,49 @@ fn check_link_name_xor_ordinal( } } +#[derive(Default)] +struct MixedExportNameAndNoMangleState<'a> { + export_name: Option<Span>, + hir_id: Option<HirId>, + no_mangle: Option<Span>, + no_mangle_attr: Option<&'a ast::Attribute>, +} + +impl<'a> MixedExportNameAndNoMangleState<'a> { + fn track_export_name(&mut self, span: Span) { + self.export_name = Some(span); + } + + fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a ast::Attribute) { + self.no_mangle = Some(span); + self.hir_id = Some(hir_id); + self.no_mangle_attr = Some(attr_name); + } + + /// Emit diagnostics if the lint condition is met. + fn lint_if_mixed(self, tcx: TyCtxt<'_>) { + if let Self { + export_name: Some(export_name), + no_mangle: Some(no_mangle), + hir_id: Some(hir_id), + no_mangle_attr: Some(no_mangle_attr), + } = self + { + tcx.emit_node_span_lint( + lint::builtin::UNUSED_ATTRIBUTES, + hir_id, + no_mangle, + errors::MixedExportNameAndNoMangle { + no_mangle, + no_mangle_attr: rustc_ast_pretty::pprust::attribute_to_string(no_mangle_attr), + export_name, + removal_span: no_mangle, + }, + ); + } + } +} + pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers }; } diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 6c4f6d37972..cf72c2ed742 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -432,11 +432,8 @@ fn push_debuginfo_type_name<'tcx>( push_closure_or_coroutine_name(tcx, def_id, args, qualified, output, visited); } } - // Type parameters from polymorphized functions. - ty::Param(_) => { - write!(output, "{t:?}").unwrap(); - } - ty::Error(_) + ty::Param(_) + | ty::Error(_) | ty::Infer(_) | ty::Placeholder(..) | ty::Alias(..) diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index f93cb52ea3e..00f8654e670 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -10,7 +10,7 @@ use rustc_errors::codes::*; use rustc_errors::{ Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, }; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutError; use rustc_span::{Span, Symbol}; @@ -1114,3 +1114,15 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_ #[derive(Diagnostic)] #[diag(codegen_ssa_aix_strip_not_used)] pub(crate) struct AixStripNotUsed; + +#[derive(LintDiagnostic)] +#[diag(codegen_ssa_mixed_export_name_and_no_mangle)] +pub(crate) struct MixedExportNameAndNoMangle { + #[label] + pub no_mangle: Span, + pub no_mangle_attr: String, + #[note] + pub export_name: Span, + #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] + pub removal_span: Span, +} diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index e3ed12b5ce6..b0a1dedd646 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -847,10 +847,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (instance, mut llfn) = match *callee.layout.ty.kind() { ty::FnDef(def_id, args) => ( - Some( - ty::Instance::expect_resolve(bx.tcx(), bx.typing_env(), def_id, args, fn_span) - .polymorphize(bx.tcx()), - ), + Some(ty::Instance::expect_resolve( + bx.tcx(), + bx.typing_env(), + def_id, + args, + fn_span, + )), None, ), ty::FnPtr(..) => (None, Some(callee.immediate())), diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index b8fa8c0351b..c38484109d2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -1,5 +1,6 @@ use rustc_abi::Primitive::{Int, Pointer}; -use rustc_abi::{Align, FieldsShape, Size, TagEncoding, VariantIdx, Variants}; +use rustc_abi::{Align, BackendRepr, FieldsShape, Size, TagEncoding, VariantIdx, Variants}; +use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; @@ -385,15 +386,22 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { if variant_index != untagged_variant { let niche = self.project_field(bx, tag_field); let niche_llty = bx.cx().immediate_backend_type(niche.layout); + let BackendRepr::Scalar(scalar) = niche.layout.backend_repr else { + bug!("expected a scalar placeref for the niche"); + }; + // We are supposed to compute `niche_value.wrapping_add(niche_start)` wrapping + // around the `niche`'s type. + // The easiest way to do that is to do wrapping arithmetic on `u128` and then + // masking off any extra bits that occur because we did the arithmetic with too many bits. let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); let niche_value = (niche_value as u128).wrapping_add(niche_start); - // FIXME(eddyb): check the actual primitive type here. - let niche_llval = if niche_value == 0 { - // HACK(eddyb): using `c_null` as it works on all types. - bx.cx().const_null(niche_llty) - } else { - bx.cx().const_uint_big(niche_llty, niche_value) - }; + let niche_value = niche_value & niche.layout.size.unsigned_int_max(); + + let niche_llval = bx.cx().scalar_to_backend( + Scalar::from_uint(niche_value, niche.layout.size), + scalar, + niche_llty, + ); OperandValue::Immediate(niche_llval).store(bx, niche); } } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index f63b2d139c5..cf537392234 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -478,8 +478,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { def_id, args, ) - .unwrap() - .polymorphize(bx.cx().tcx()); + .unwrap(); OperandValue::Immediate(bx.get_fn_addr(instance)) } _ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty), @@ -493,8 +492,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { def_id, args, ty::ClosureKind::FnOnce, - ) - .polymorphize(bx.cx().tcx()); + ); OperandValue::Immediate(bx.cx().get_fn_addr(instance)) } _ => bug!("{} cannot be cast to a fn ptr", operand.layout.ty), diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index cbf214763b4..5b4a51fc301 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -4,7 +4,6 @@ use std::hash::Hash; use rustc_ast::expand::allocator::AllocatorKind; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{DynSend, DynSync}; -use rustc_errors::ErrorGuaranteed; use rustc_metadata::EncodedMetadata; use rustc_metadata::creader::MetadataLoaderDyn; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; @@ -71,11 +70,11 @@ pub trait CodegenBackend { need_metadata_module: bool, ) -> Box<dyn Any>; - /// This is called on the returned `Box<dyn Any>` from `codegen_backend` + /// This is called on the returned `Box<dyn Any>` from [`codegen_crate`](Self::codegen_crate) /// /// # Panics /// - /// Panics when the passed `Box<dyn Any>` was not returned by `codegen_backend`. + /// Panics when the passed `Box<dyn Any>` was not returned by [`codegen_crate`](Self::codegen_crate). fn join_codegen( &self, ongoing_codegen: Box<dyn Any>, @@ -83,14 +82,9 @@ pub trait CodegenBackend { outputs: &OutputFilenames, ) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>); - /// This is called on the returned `CodegenResults` from `join_codegen` - fn link( - &self, - sess: &Session, - codegen_results: CodegenResults, - outputs: &OutputFilenames, - ) -> Result<(), ErrorGuaranteed> { - link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, outputs) + /// This is called on the returned [`CodegenResults`] from [`join_codegen`](Self::join_codegen). + fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) { + link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, outputs); } /// Returns `true` if this backend can be safely called from multiple threads. diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index f93f4d36e45..c31c94495d0 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -110,7 +110,7 @@ const_eval_extern_type_field = `extern type` field does not have a known offset const_eval_fn_ptr_call = function pointers need an RFC before allowed to be called in {const_eval_const_context}s const_eval_for_loop_into_iter_non_const = - cannot convert `{$ty}` into an iterator in {const_eval_const_context}s + cannot use `for` loop on `{$ty}` in {const_eval_const_context}s const_eval_frame_note = {$times -> [0] {const_eval_frame_note_inner} @@ -324,11 +324,11 @@ const_eval_ptr_as_bytes_1 = this code performed an operation that depends on the underlying bytes representing a pointer const_eval_ptr_as_bytes_2 = the absolute address of a pointer is not known at compile-time, so such operations are not supported -const_eval_question_branch_non_const = - `?` cannot determine the branch of `{$ty}` in {const_eval_const_context}s +const_eval_question_branch_non_const = + `?` is not allowed on `{$ty}` in {const_eval_const_context}s const_eval_question_from_residual_non_const = - `?` cannot convert from residual of `{$ty}` in {const_eval_const_context}s + `?` is not allowed on `{$ty}` in {const_eval_const_context}s const_eval_range = in the range {$lo}..={$hi} const_eval_range_lower = greater or equal to {$lo} diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 9f21c864487..916929b6c0b 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -18,8 +18,7 @@ use rustc_middle::span_bug; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_mir_dataflow::Analysis; -use rustc_mir_dataflow::impls::MaybeStorageLive; -use rustc_mir_dataflow::storage::always_storage_live_locals; +use rustc_mir_dataflow::impls::{MaybeStorageLive, always_storage_live_locals}; use rustc_span::{Span, Symbol, sym}; use rustc_trait_selection::traits::{ Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt, diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index ab81e60a33f..23f2aa4d029 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -140,7 +140,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { err, param_ty.name.as_str(), &constraint, - None, + Some(trait_ref.def_id), None, ); } @@ -180,8 +180,10 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { }; } - let mut err = match kind { - CallDesugaringKind::ForLoopIntoIter => { + // Don't point at the trait if this is a desugaring... + // FIXME(const_trait_impl): we could perhaps do this for `Iterator`. + match kind { + CallDesugaringKind::ForLoopIntoIter | CallDesugaringKind::ForLoopNext => { error!(NonConstForLoopIntoIter) } CallDesugaringKind::QuestionBranch => { @@ -196,10 +198,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { CallDesugaringKind::Await => { error!(NonConstAwait) } - }; - - diag_trait(&mut err, self_ty, kind.trait_def_id(tcx)); - err + } } CallKind::FnCall { fn_trait_id, self_ty } => { let note = match self_ty.kind() { diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs index e49d702127d..817acfcca74 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -168,9 +168,9 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine { }) } - fn expose_ptr( - _ecx: &mut InterpCx<'tcx, Self>, - _ptr: interpret::Pointer<Self::Provenance>, + fn expose_provenance( + _ecx: &InterpCx<'tcx, Self>, + _provenance: Self::Provenance, ) -> interpret::InterpResult<'tcx> { unimplemented!() } diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 1271d9d2d0d..3cb77d1dcb5 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -139,12 +139,14 @@ where match error { // Don't emit a new diagnostic for these errors, they are already reported elsewhere or // should remain silent. + err_inval!(AlreadyReported(info)) => ErrorHandled::Reported(info, span), err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { ErrorHandled::TooGeneric(span) } - err_inval!(AlreadyReported(guar)) => ErrorHandled::Reported(guar, span), err_inval!(Layout(LayoutError::ReferencesError(guar))) => { - ErrorHandled::Reported(ReportedErrorInfo::tainted_by_errors(guar), span) + // This can occur in infallible promoteds e.g. when a non-existent type or field is + // encountered. + ErrorHandled::Reported(ReportedErrorInfo::allowed_in_infallible(guar), span) } // Report remaining errors. _ => { @@ -152,7 +154,12 @@ where let span = span.substitute_dummy(our_span); let err = mk(span, frames); let mut err = tcx.dcx().create_err(err); - let can_be_spurious = matches!(error, InterpErrorKind::ResourceExhaustion(_)); + // We allow invalid programs in infallible promoteds since invalid layouts can occur + // anyway (e.g. due to size overflow). And we allow OOM as that can happen any time. + let allowed_in_infallible = matches!( + error, + InterpErrorKind::ResourceExhaustion(_) | InterpErrorKind::InvalidProgram(_) + ); let msg = error.diagnostic_message(); error.add_args(&mut err); @@ -160,8 +167,8 @@ where // Use *our* span to label the interp error err.span_label(our_span, msg); let g = err.emit(); - let reported = if can_be_spurious { - ReportedErrorInfo::spurious(g) + let reported = if allowed_in_infallible { + ReportedErrorInfo::allowed_in_infallible(g) } else { ReportedErrorInfo::from(g) }; diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index b27e3606f38..11e0fac51d8 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -21,9 +21,8 @@ use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ self, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, - InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, RangeSet, Scalar, compile_time_machine, - interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, - throw_unsup_format, + InterpCx, InterpResult, MPlaceTy, OpTy, RangeSet, Scalar, compile_time_machine, interp_ok, + throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -586,7 +585,10 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { } #[inline(always)] - fn expose_ptr(_ecx: &mut InterpCx<'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx> { + fn expose_provenance( + _ecx: &InterpCx<'tcx, Self>, + _provenance: Self::Provenance, + ) -> InterpResult<'tcx> { // This is only reachable with -Zunleash-the-miri-inside-of-you. throw_unsup_format!("exposing pointers is not possible at compile-time") } diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 8cbdcd68e13..34f795bda75 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -2,6 +2,7 @@ use rustc_abi::VariantIdx; use rustc_middle::query::{Key, TyCtxtAt}; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use tracing::instrument; @@ -85,5 +86,6 @@ pub fn tag_for_variant_provider<'tcx>( crate::const_eval::DummyMachine, ); - ecx.tag_for_variant(ty, variant_index).unwrap().map(|(tag, _tag_field)| tag) + let layout = ecx.layout_of(ty).unwrap(); + ecx.tag_for_variant(layout, variant_index).unwrap().map(|(tag, _tag_field)| tag) } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index c95e51f0a1f..ef3e96784ce 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -238,7 +238,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let scalar = src.to_scalar(); let ptr = scalar.to_pointer(self)?; match ptr.into_pointer_or_addr() { - Ok(ptr) => M::expose_ptr(self, ptr)?, + Ok(ptr) => M::expose_provenance(self, ptr.provenance)?, Err(_) => {} // Do nothing, exposing an invalid pointer (`None` provenance) is a NOP. }; interp_ok(ImmTy::from_scalar( diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index f94d0cbb42b..6faac1582ab 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -1,7 +1,7 @@ //! Functions for reading and writing discriminants of multi-variant layouts (enums and coroutines). use rustc_abi::{self as abi, TagEncoding, VariantIdx, Variants}; -use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt}; +use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout}; use rustc_middle::ty::{self, CoroutineArgsExt, ScalarInt, Ty}; use rustc_middle::{mir, span_bug}; use tracing::{instrument, trace}; @@ -21,17 +21,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { variant_index: VariantIdx, dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { - // Layout computation excludes uninhabited variants from consideration - // therefore there's no way to represent those variants in the given layout. - // Essentially, uninhabited variants do not have a tag that corresponds to their - // discriminant, so we cannot do anything here. - // When evaluating we will always error before even getting here, but ConstProp 'executes' - // dead code, so we cannot ICE here. - if dest.layout().for_variant(self, variant_index).is_uninhabited() { - throw_ub!(UninhabitedEnumVariantWritten(variant_index)) - } - - match self.tag_for_variant(dest.layout().ty, variant_index)? { + match self.tag_for_variant(dest.layout(), variant_index)? { Some((tag, tag_field)) => { // No need to validate that the discriminant here because the // `TyAndLayout::for_variant()` call earlier already checks the @@ -80,7 +70,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if ty.is_enum() { // Hilariously, `Single` is used even for 0-variant enums. // (See https://github.com/rust-lang/rust/issues/89765). - if matches!(ty.kind(), ty::Adt(def, ..) if def.variants().is_empty()) { + if ty.ty_adt_def().unwrap().variants().is_empty() { throw_ub!(UninhabitedEnumVariantRead(index)) } // For consistency with `write_discriminant`, and to make sure that @@ -188,6 +178,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let variants = ty.ty_adt_def().expect("tagged layout for non adt").variants(); assert!(variant_index < variants.next_index()); + if variant_index == untagged_variant { + // The untagged variant can be in the niche range, but even then it + // is not a valid encoding. + throw_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))) + } variant_index } else { untagged_variant @@ -236,10 +231,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// given field index. pub(crate) fn tag_for_variant( &self, - ty: Ty<'tcx>, + layout: TyAndLayout<'tcx>, variant_index: VariantIdx, ) -> InterpResult<'tcx, Option<(ScalarInt, usize)>> { - match self.layout_of(ty)?.variants { + // Layout computation excludes uninhabited variants from consideration. + // Therefore, there's no way to represent those variants in the given layout. + // Essentially, uninhabited variants do not have a tag that corresponds to their + // discriminant, so we have to bail out here. + if layout.for_variant(self, variant_index).is_uninhabited() { + throw_ub!(UninhabitedEnumVariantWritten(variant_index)) + } + + match layout.variants { abi::Variants::Single { .. } => { // The tag of a `Single` enum is like the tag of the niched // variant: there's no tag as the discriminant is encoded @@ -260,7 +263,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // raw discriminants for enums are isize or bigger during // their computation, but the in-memory tag is the smallest possible // representation - let discr = self.discriminant_for_variant(ty, variant_index)?; + let discr = self.discriminant_for_variant(layout.ty, variant_index)?; let discr_size = discr.layout.size; let discr_val = discr.to_scalar().to_bits(discr_size)?; let tag_size = tag_layout.size(self); @@ -286,11 +289,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .. } => { assert!(variant_index != untagged_variant); + // We checked that this variant is inhabited, so it must be in the niche range. + assert!( + niche_variants.contains(&variant_index), + "invalid variant index for this enum" + ); let variants_start = niche_variants.start().as_u32(); - let variant_index_relative = variant_index - .as_u32() - .checked_sub(variants_start) - .expect("overflow computing relative variant idx"); + let variant_index_relative = variant_index.as_u32().strict_sub(variants_start); // We need to use machine arithmetic when taking into account `niche_start`: // tag_val = variant_index_relative + niche_start_val let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index fe93a48c2f2..241be5e175c 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -268,7 +268,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; // do not continue if typeck errors occurred (can only occur in local crate) if let Some(err) = body.tainted_by_errors { - throw_inval!(AlreadyReported(ReportedErrorInfo::tainted_by_errors(err))); + throw_inval!(AlreadyReported(ReportedErrorInfo::from(err))); } interp_ok(body) } @@ -585,13 +585,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { match err { ErrorHandled::TooGeneric(..) => {}, ErrorHandled::Reported(reported, span) => { - if reported.is_tainted_by_errors() { - // const-eval will return "tainted" errors if e.g. the layout cannot - // be computed as the type references non-existing names. - // See <https://github.com/rust-lang/rust/issues/124348>. - } else if reported.can_be_spurious() { + if reported.is_allowed_in_infallible() { // These errors can just sometimes happen, even when the expression - // is nominally "infallible", e.g. when running out of memory. + // is nominally "infallible", e.g. when running out of memory + // or when some layout could not be computed. } else { // Looks like the const is not captured by `required_consts`, that's bad. span_bug!(span, "interpret const eval failure of {val:?} which is not in required_consts"); diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index dbe09d55b2d..a180d5da941 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -327,11 +327,11 @@ pub trait Machine<'tcx>: Sized { addr: u64, ) -> InterpResult<'tcx, Pointer<Option<Self::Provenance>>>; - /// Marks a pointer as exposed, allowing it's provenance + /// Marks a pointer as exposed, allowing its provenance /// to be recovered. "Pointer-to-int cast" - fn expose_ptr( - ecx: &mut InterpCx<'tcx, Self>, - ptr: Pointer<Self::Provenance>, + fn expose_provenance( + ecx: &InterpCx<'tcx, Self>, + provenance: Self::Provenance, ) -> InterpResult<'tcx>; /// Convert a pointer with provenance into an allocation-offset pair and extra provenance info. diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 277d293597a..027ba9644cb 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -944,6 +944,52 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(()) } + /// Handle the effect an FFI call might have on the state of allocations. + /// This overapproximates the modifications which external code might make to memory: + /// We set all reachable allocations as initialized, mark all provenances as exposed + /// and overwrite them with `Provenance::WILDCARD`. + pub fn prepare_for_native_call( + &mut self, + id: AllocId, + initial_prov: M::Provenance, + ) -> InterpResult<'tcx> { + // Expose provenance of the root allocation. + M::expose_provenance(self, initial_prov)?; + + let mut done = FxHashSet::default(); + let mut todo = vec![id]; + while let Some(id) = todo.pop() { + if !done.insert(id) { + // We already saw this allocation before, don't process it again. + continue; + } + let info = self.get_alloc_info(id); + + // If there is no data behind this pointer, skip this. + if !matches!(info.kind, AllocKind::LiveData) { + continue; + } + + // Expose all provenances in this allocation, and add them to `todo`. + let alloc = self.get_alloc_raw(id)?; + for prov in alloc.provenance().provenances() { + M::expose_provenance(self, prov)?; + if let Some(id) = prov.get_alloc_id() { + todo.push(id); + } + } + + // Prepare for possible write from native code if mutable. + if info.mutbl.is_mut() { + self.get_alloc_raw_mut(id)? + .0 + .prepare_for_native_write() + .map_err(|e| e.to_interp_error(id))?; + } + } + interp_ok(()) + } + /// Create a lazy debug printer that prints the given allocation and all allocations it points /// to, recursively. #[must_use] diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 2beec544fad..810e9356b26 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -5,8 +5,7 @@ use std::assert_matches::assert_matches; use either::{Either, Left, Right}; -use rustc_abi::{Align, BackendRepr, HasDataLayout, Size}; -use rustc_ast::Mutability; +use rustc_abi::{BackendRepr, HasDataLayout, Size}; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::{bug, mir, span_bug}; @@ -1018,29 +1017,39 @@ where self.allocate_dyn(layout, kind, MemPlaceMeta::None) } - /// Returns a wide MPlace of type `str` to a new 1-aligned allocation. - /// Immutable strings are deduplicated and stored in global memory. - pub fn allocate_str( + /// Allocates a sequence of bytes in the interpreter's memory with alignment 1. + /// This is allocated in immutable global memory and deduplicated. + pub fn allocate_bytes_dedup( + &mut self, + bytes: &[u8], + ) -> InterpResult<'tcx, Pointer<M::Provenance>> { + let salt = M::get_global_alloc_salt(self, None); + let id = self.tcx.allocate_bytes_dedup(bytes, salt); + + // Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation. + M::adjust_alloc_root_pointer( + &self, + Pointer::from(id), + M::GLOBAL_KIND.map(MemoryKind::Machine), + ) + } + + /// Allocates a string in the interpreter's memory, returning it as a (wide) place. + /// This is allocated in immutable global memory and deduplicated. + pub fn allocate_str_dedup( &mut self, str: &str, - kind: MemoryKind<M::MemoryKind>, - mutbl: Mutability, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - let tcx = self.tcx.tcx; + let bytes = str.as_bytes(); + let ptr = self.allocate_bytes_dedup(bytes)?; - // Use cache for immutable strings. - let ptr = if mutbl.is_not() { - // Use dedup'd allocation function. - let salt = M::get_global_alloc_salt(self, None); - let id = tcx.allocate_bytes_dedup(str.as_bytes(), salt); + // Create length metadata for the string. + let meta = Scalar::from_target_usize(u64::try_from(bytes.len()).unwrap(), self); - // Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation. - M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind))? - } else { - self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)? - }; - let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self); + // Get layout for Rust's str type. let layout = self.layout_of(self.tcx.types.str_).unwrap(); + + // Combine pointer and metadata into a wide pointer. interp_ok(self.ptr_with_meta_to_mplace( ptr.into(), MemPlaceMeta::Meta(meta), diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 8ce11c71b8b..a9ebf386617 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -10,7 +10,7 @@ use rustc_index::IndexVec; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; -use rustc_mir_dataflow::storage::always_storage_live_locals; +use rustc_mir_dataflow::impls::always_storage_live_locals; use rustc_span::Span; use tracing::{info_span, instrument, trace}; diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 8bb5f173a56..ecb7c3fc93c 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -14,10 +14,8 @@ use crate::const_eval::{CompileTimeInterpCx, CompileTimeMachine, InterpretationR /// Checks whether a type contains generic parameters which must be instantiated. /// -/// In case it does, returns a `TooGeneric` const eval error. Note that due to polymorphization -/// types may be "concrete enough" even though they still contain generic parameters in -/// case these parameters are unused. -pub(crate) fn ensure_monomorphic_enough<'tcx, T>(tcx: TyCtxt<'tcx>, ty: T) -> InterpResult<'tcx> +/// In case it does, returns a `TooGeneric` const eval error. +pub(crate) fn ensure_monomorphic_enough<'tcx, T>(_tcx: TyCtxt<'tcx>, ty: T) -> InterpResult<'tcx> where T: TypeVisitable<TyCtxt<'tcx>>, { @@ -27,11 +25,9 @@ where } struct FoundParam; - struct UsedParamsNeedInstantiationVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - } + struct UsedParamsNeedInstantiationVisitor {} - impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> { + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor { type Result = ControlFlow<FoundParam>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { @@ -41,25 +37,7 @@ where match *ty.kind() { ty::Param(_) => ControlFlow::Break(FoundParam), - ty::Closure(def_id, args) - | ty::CoroutineClosure(def_id, args, ..) - | ty::Coroutine(def_id, args, ..) - | ty::FnDef(def_id, args) => { - let instance = ty::InstanceKind::Item(def_id); - let unused_params = self.tcx.unused_generic_params(instance); - for (index, arg) in args.into_iter().enumerate() { - let index = index - .try_into() - .expect("more generic parameters than can fit into a `u32`"); - // Only recurse when generic parameters in fns, closures and coroutines - // are used and have to be instantiated. - // - // Just in case there are closures or coroutines within this arg, - // recurse. - if unused_params.is_used(index) && arg.has_param() { - return arg.visit_with(self); - } - } + ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) | ty::FnDef(..) => { ControlFlow::Continue(()) } _ => ty.super_visit_with(self), @@ -74,7 +52,7 @@ where } } - let mut vis = UsedParamsNeedInstantiationVisitor { tcx }; + let mut vis = UsedParamsNeedInstantiationVisitor {}; if matches!(ty.visit_with(&mut vis), ControlFlow::Break(FoundParam)) { throw_inval!(TooGeneric); } else { diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 2a7408f1c70..b5adf06b300 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -10,6 +10,7 @@ #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(slice_ptr_get)] +#![feature(strict_overflow_ops)] #![feature(trait_alias)] #![feature(try_blocks)] #![feature(unqualified_local_imports)] diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index 9bf16d4fe16..6593547cd23 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -1,7 +1,7 @@ use rustc_hir::LangItem; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, Mutability}; +use rustc_middle::ty::{self}; use rustc_middle::{bug, mir}; use rustc_span::symbol::Symbol; use tracing::trace; @@ -20,12 +20,9 @@ fn alloc_caller_location<'tcx>( // This can fail if rustc runs out of memory right here. Trying to emit an error would be // pointless, since that would require allocating more memory than these short strings. let file = if loc_details.file { - ecx.allocate_str(filename.as_str(), MemoryKind::CallerLocation, Mutability::Not).unwrap() + ecx.allocate_str_dedup(filename.as_str()).unwrap() } else { - // FIXME: This creates a new allocation each time. It might be preferable to - // perform this allocation only once, and re-use the `MPlaceTy`. - // See https://github.com/rust-lang/rust/pull/89920#discussion_r730012398 - ecx.allocate_str("<redacted>", MemoryKind::CallerLocation, Mutability::Not).unwrap() + ecx.allocate_str_dedup("<redacted>").unwrap() }; let file = file.map_provenance(CtfeProvenance::as_immutable); let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) }; diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index 34a2464972a..78d69a66edc 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -415,6 +415,10 @@ impl<O: ForestObligation> ObligationForest<O> { .collect() } + pub fn has_pending_obligations(&self) -> bool { + self.nodes.iter().any(|node| node.state.get() == NodeState::Pending) + } + fn insert_into_error_cache(&mut self, index: usize) { let node = &self.nodes[index]; self.error_cache diff --git a/compiler/rustc_driver_impl/src/args.rs b/compiler/rustc_driver_impl/src/args.rs index 28574457389..2fc767b3750 100644 --- a/compiler/rustc_driver_impl/src/args.rs +++ b/compiler/rustc_driver_impl/src/args.rs @@ -99,10 +99,7 @@ impl Expander { /// If this function is intended to be used with command line arguments, /// `argv[0]` must be removed prior to calling it manually. #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable -pub fn arg_expand_all( - early_dcx: &EarlyDiagCtxt, - at_args: &[String], -) -> Result<Vec<String>, ErrorGuaranteed> { +pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec<String> { let mut expander = Expander::default(); let mut result = Ok(()); for arg in at_args { @@ -110,7 +107,10 @@ pub fn arg_expand_all( result = Err(early_dcx.early_err(format!("failed to load argument file: {err}"))); } } - result.map(|()| expander.finish()) + if let Err(guar) = result { + guar.raise_fatal(); + } + expander.finish() } /// Gets the raw unprocessed command-line arguments as Unicode strings, without doing any further diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index c270ce16726..2e01c385a66 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -42,9 +42,7 @@ use rustc_data_structures::profiling::{ }; use rustc_errors::emitter::stderr_destination; use rustc_errors::registry::Registry; -use rustc_errors::{ - ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, FatalError, PResult, markdown, -}; +use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown}; use rustc_feature::find_gated_cfg; use rustc_interface::util::{self, get_codegen_backend}; use rustc_interface::{Linker, Queries, interface, passes}; @@ -160,6 +158,9 @@ pub trait Callbacks { /// Called after parsing the crate root. Submodules are not yet parsed when /// this callback is called. Return value instructs the compiler whether to /// continue the compilation afterwards (defaults to `Compilation::Continue`) + #[deprecated = "This callback will likely be removed or stop giving access \ + to the TyCtxt in the future. Use either the after_expansion \ + or the after_analysis callback instead."] fn after_crate_root_parsing<'tcx>( &mut self, _compiler: &interface::Compiler, @@ -181,7 +182,7 @@ pub trait Callbacks { fn after_analysis<'tcx>( &mut self, _compiler: &interface::Compiler, - _queries: &'tcx Queries<'tcx>, + _tcx: TyCtxt<'tcx>, ) -> Compilation { Compilation::Continue } @@ -268,14 +269,14 @@ impl<'a> RunCompiler<'a> { } /// Parse args and run the compiler. - pub fn run(self) -> interface::Result<()> { + pub fn run(self) { run_compiler( self.at_args, self.callbacks, self.file_loader, self.make_codegen_backend, self.using_internal_features, - ) + ); } } @@ -287,7 +288,7 @@ fn run_compiler( Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>, >, using_internal_features: Arc<std::sync::atomic::AtomicBool>, -) -> interface::Result<()> { +) { let mut default_early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); // Throw away the first argument, the name of the binary. @@ -300,9 +301,11 @@ fn run_compiler( // the compiler with @empty_file as argv[0] and no more arguments. let at_args = at_args.get(1..).unwrap_or_default(); - let args = args::arg_expand_all(&default_early_dcx, at_args)?; + let args = args::arg_expand_all(&default_early_dcx, at_args); - let Some(matches) = handle_options(&default_early_dcx, &args) else { return Ok(()) }; + let Some(matches) = handle_options(&default_early_dcx, &args) else { + return; + }; let sopts = config::build_session_options(&mut default_early_dcx, &matches); // fully initialize ice path static once unstable options are available as context @@ -310,7 +313,7 @@ fn run_compiler( if let Some(ref code) = matches.opt_str("explain") { handle_explain(&default_early_dcx, diagnostics_registry(), code, sopts.color); - return Ok(()); + return; } let (odir, ofile) = make_output(&matches); @@ -336,18 +339,11 @@ fn run_compiler( }; let has_input = match make_input(&default_early_dcx, &matches.free) { - Err(reported) => return Err(reported), - Ok(Some(input)) => { + Some(input) => { config.input = input; true // has input: normal compilation } - Ok(None) => match matches.free.as_slice() { - [] => false, // no input: we will exit early - [_] => panic!("make_input should have provided valid inputs"), - [fst, snd, ..] => default_early_dcx.early_fatal(format!( - "multiple input filenames provided (first two filenames are `{fst}` and `{snd}`)" - )), - }, + None => false, // no input: we will exit early }; drop(default_early_dcx); @@ -362,7 +358,7 @@ fn run_compiler( // printing some information without compiling, or exiting immediately // after parsing, etc. let early_exit = || { - if let Some(guar) = sess.dcx().has_errors() { Err(guar) } else { Ok(()) } + sess.dcx().abort_if_errors(); }; // This implements `-Whelp`. It should be handled very early, like @@ -393,26 +389,25 @@ fn run_compiler( } let linker = compiler.enter(|queries| { - let early_exit = || early_exit().map(|_| None); + let early_exit = || { + sess.dcx().abort_if_errors(); + None + }; // Parse the crate root source code (doesn't parse submodules yet) // Everything else is parsed during macro expansion. - queries.parse()?; + queries.parse(); // If pretty printing is requested: Figure out the representation, print it and exit if let Some(pp_mode) = sess.opts.pretty { if pp_mode.needs_ast_map() { - queries.global_ctxt()?.enter(|tcx| { + queries.global_ctxt().enter(|tcx| { tcx.ensure().early_lint_checks(()); pretty::print(sess, pp_mode, pretty::PrintExtra::NeedsAstMap { tcx }); - Ok(()) - })?; - - queries.global_ctxt()?.enter(|tcx| { passes::write_dep_info(tcx); }); } else { - let krate = queries.parse()?; + let krate = queries.parse(); pretty::print(sess, pp_mode, pretty::PrintExtra::AfterParsing { krate: &*krate.borrow(), }); @@ -421,62 +416,54 @@ fn run_compiler( return early_exit(); } + #[allow(deprecated)] if callbacks.after_crate_root_parsing(compiler, queries) == Compilation::Stop { return early_exit(); } - if sess.opts.unstable_opts.parse_only || sess.opts.unstable_opts.show_span.is_some() { + if sess.opts.unstable_opts.parse_crate_root_only { return early_exit(); } // Make sure name resolution and macro expansion is run. - queries.global_ctxt()?.enter(|tcx| tcx.resolver_for_lowering()); + 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)); + queries.global_ctxt().enter(|tcxt| dump_feature_usage_metrics(tcxt, metrics_dir)); } if callbacks.after_expansion(compiler, queries) == Compilation::Stop { return early_exit(); } - queries.global_ctxt()?.enter(|tcx| { + queries.global_ctxt().enter(|tcx| { passes::write_dep_info(tcx); - }); - if sess.opts.output_types.contains_key(&OutputType::DepInfo) - && sess.opts.output_types.len() == 1 - { - return early_exit(); - } + if sess.opts.output_types.contains_key(&OutputType::DepInfo) + && sess.opts.output_types.len() == 1 + { + return early_exit(); + } - if sess.opts.unstable_opts.no_analysis { - return early_exit(); - } + if sess.opts.unstable_opts.no_analysis { + return early_exit(); + } - queries.global_ctxt()?.enter(|tcx| tcx.analysis(()))?; + tcx.ensure().analysis(()); - if callbacks.after_analysis(compiler, queries) == Compilation::Stop { - return early_exit(); - } + if callbacks.after_analysis(compiler, tcx) == Compilation::Stop { + return early_exit(); + } - queries.global_ctxt()?.enter(|tcx| { - Ok(Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)?)) + Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)) }) - })?; + }); // Linking is done outside the `compiler.enter()` so that the // `GlobalCtxt` within `Queries` can be freed as early as possible. if let Some(linker) = linker { - let _timer = sess.timer("link"); - linker.link(sess, codegen_backend)? - } - - if let Some(fuel) = sess.opts.unstable_opts.print_fuel.as_deref() { - eprintln!("Fuel used by {}: {}", fuel, sess.print_fuel.load(Ordering::SeqCst)); + linker.link(sess, codegen_backend); } - - Ok(()) }) } @@ -509,41 +496,40 @@ fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<OutFileNa /// Extract input (string or file and optional path) from matches. /// This handles reading from stdin if `-` is provided. -fn make_input( - early_dcx: &EarlyDiagCtxt, - free_matches: &[String], -) -> Result<Option<Input>, ErrorGuaranteed> { - let [input_file] = free_matches else { return Ok(None) }; - - if input_file != "-" { - // Normal `Input::File` - return Ok(Some(Input::File(PathBuf::from(input_file)))); - } - - // read from stdin as `Input::Str` - let mut input = String::new(); - if io::stdin().read_to_string(&mut input).is_err() { - // Immediately stop compilation if there was an issue reading - // the input (for example if the input stream is not UTF-8). - let reported = - early_dcx.early_err("couldn't read from stdin, as it did not contain valid UTF-8"); - return Err(reported); - } +fn make_input(early_dcx: &EarlyDiagCtxt, free_matches: &[String]) -> Option<Input> { + match free_matches { + [] => None, // no input: we will exit early, + [ifile] if ifile == "-" => { + // read from stdin as `Input::Str` + let mut input = String::new(); + if io::stdin().read_to_string(&mut input).is_err() { + // Immediately stop compilation if there was an issue reading + // the input (for example if the input stream is not UTF-8). + early_dcx + .early_fatal("couldn't read from stdin, as it did not contain valid UTF-8"); + } - let name = match env::var("UNSTABLE_RUSTDOC_TEST_PATH") { - Ok(path) => { - let line = env::var("UNSTABLE_RUSTDOC_TEST_LINE").expect( - "when UNSTABLE_RUSTDOC_TEST_PATH is set \ + let name = match env::var("UNSTABLE_RUSTDOC_TEST_PATH") { + Ok(path) => { + let line = env::var("UNSTABLE_RUSTDOC_TEST_LINE").expect( + "when UNSTABLE_RUSTDOC_TEST_PATH is set \ UNSTABLE_RUSTDOC_TEST_LINE also needs to be set", - ); - let line = isize::from_str_radix(&line, 10) - .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number"); - FileName::doc_test_source_code(PathBuf::from(path), line) - } - Err(_) => FileName::anon_source_code(&input), - }; + ); + let line = isize::from_str_radix(&line, 10) + .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number"); + FileName::doc_test_source_code(PathBuf::from(path), line) + } + Err(_) => FileName::anon_source_code(&input), + }; - Ok(Some(Input::Str { name, input })) + Some(Input::Str { name, input }) + } + [ifile] => Some(Input::File(PathBuf::from(ifile))), + [ifile1, ifile2, ..] => early_dcx.early_fatal(format!( + "multiple input filenames provided (first two filenames are `{}` and `{}`)", + ifile1, ifile2 + )), + } } /// Whether to stop or continue compilation. @@ -673,9 +659,7 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { }; } }; - if compiler.codegen_backend.link(sess, codegen_results, &outputs).is_err() { - FatalError.raise(); - } + compiler.codegen_backend.link(sess, codegen_results, &outputs); } else { dcx.emit_fatal(RlinkNotAFile {}); } @@ -875,8 +859,9 @@ fn print_crate_info( DeploymentTarget => { if sess.target.is_like_osx { println_info!( - "deployment_target={}", - apple::pretty_version(apple::deployment_target(sess)) + "{}={}", + apple::deployment_target_env_var(&sess.target.os), + apple::pretty_version(apple::deployment_target(sess)), ) } else { #[allow(rustc::diagnostic_outside_of_impl)] @@ -1617,7 +1602,8 @@ pub fn main() -> ! { let exit_code = catch_with_exit_code(|| { RunCompiler::new(&args::raw_args(&early_dcx)?, &mut callbacks) .set_using_internal_features(using_internal_features) - .run() + .run(); + Ok(()) }); if let Some(format) = callbacks.time_passes { diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 0733b8c0b98..5df960be307 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -222,8 +222,8 @@ impl<'tcx> PrintExtra<'tcx> { } pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { - if ppm.needs_analysis() && ex.tcx().analysis(()).is_err() { - FatalError.raise(); + if ppm.needs_analysis() { + ex.tcx().ensure().analysis(()); } let (src, src_name) = get_source(sess); diff --git a/compiler/rustc_error_codes/src/error_codes/E0060.md b/compiler/rustc_error_codes/src/error_codes/E0060.md index 54b10c886cc..a6831c881e3 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0060.md +++ b/compiler/rustc_error_codes/src/error_codes/E0060.md @@ -15,7 +15,7 @@ unsafe { printf(); } // error! Using this declaration, it must be called with at least one argument, so simply calling `printf()` is invalid. But the following uses are allowed: -``` +```rust,edition2021 # use std::os::raw::{c_char, c_int}; # #[cfg_attr(all(windows, target_env = "msvc"), # link(name = "legacy_stdio_definitions", @@ -23,16 +23,11 @@ simply calling `printf()` is invalid. But the following uses are allowed: # extern "C" { fn printf(_: *const c_char, ...) -> c_int; } # fn main() { unsafe { - use std::ffi::CString; - - let fmt = CString::new("test\n").unwrap(); - printf(fmt.as_ptr()); + printf(c"test\n".as_ptr()); - let fmt = CString::new("number = %d\n").unwrap(); - printf(fmt.as_ptr(), 3); + printf(c"number = %d\n".as_ptr(), 3); - let fmt = CString::new("%d, %d\n").unwrap(); - printf(fmt.as_ptr(), 10, 5); + printf(c"%d, %d\n".as_ptr(), 10, 5); } # } ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0622.md b/compiler/rustc_error_codes/src/error_codes/E0622.md index 5d71ee9949d..4cb605b636d 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0622.md +++ b/compiler/rustc_error_codes/src/error_codes/E0622.md @@ -7,10 +7,11 @@ Erroneous code example: #![allow(internal_features)] extern "rust-intrinsic" { - pub static breakpoint: fn(); // error: intrinsic must be a function + pub static atomic_singlethreadfence_seqcst: fn(); + // error: intrinsic must be a function } -fn main() { unsafe { breakpoint(); } } +fn main() { unsafe { atomic_singlethreadfence_seqcst(); } } ``` An intrinsic is a function available for use in a given programming language @@ -22,8 +23,8 @@ error, just declare a function. Example: #![allow(internal_features)] extern "rust-intrinsic" { - pub fn breakpoint(); // ok! + pub fn atomic_singlethreadfence_seqcst(); // ok! } -fn main() { unsafe { breakpoint(); } } +fn main() { unsafe { atomic_singlethreadfence_seqcst(); } } ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0751.md b/compiler/rustc_error_codes/src/error_codes/E0751.md index 8794f7868f3..825809b229a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0751.md +++ b/compiler/rustc_error_codes/src/error_codes/E0751.md @@ -9,4 +9,4 @@ impl !MyTrait for i32 { } // error! ``` Negative implementations are a promise that the trait will never be implemented -for the given types. Therefore, both cannot exists at the same time. +for the given types. Therefore, both cannot exist at the same time. diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index b4a651b10b1..b337e279400 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -12,6 +12,7 @@ use rustc_span::SourceFile; use rustc_span::source_map::SourceMap; use crate::emitter::FileWithAnnotatedLines; +use crate::registry::Registry; use crate::snippet::Line; use crate::translation::{Translate, to_fluent_args}; use crate::{ @@ -45,7 +46,7 @@ impl Translate for AnnotateSnippetEmitter { impl Emitter for AnnotateSnippetEmitter { /// The entry point for the diagnostics generation - fn emit_diagnostic(&mut self, mut diag: DiagInner) { + fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { let fluent_args = to_fluent_args(diag.args.iter()); let mut suggestions = diag.suggestions.unwrap_tag(); diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 798668b8bc1..b4510371323 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -6,6 +6,7 @@ use std::path::{Path, PathBuf}; use std::process::ExitStatus; use rustc_abi::TargetDataLayoutErrors; +use rustc_ast::util::parser::ExprPrecedence; use rustc_ast_pretty::pprust; use rustc_macros::Subdiagnostic; use rustc_span::Span; @@ -298,6 +299,12 @@ impl IntoDiagArg for hir::def::Namespace { } } +impl IntoDiagArg for ExprPrecedence { + fn into_diag_arg(self) -> DiagArgValue { + DiagArgValue::Number(self as i32) + } +} + #[derive(Clone)] pub struct DiagSymbolList<S = Symbol>(Vec<S>); diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index a386129e814..1b6c6edcc61 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -27,6 +27,7 @@ use termcolor::{Buffer, BufferWriter, Color, ColorChoice, ColorSpec, StandardStr use tracing::{debug, instrument, trace, warn}; use crate::diagnostic::DiagLocation; +use crate::registry::Registry; use crate::snippet::{ Annotation, AnnotationColumn, AnnotationType, Line, MultilineAnnotation, Style, StyledString, }; @@ -181,7 +182,7 @@ pub type DynEmitter = dyn Emitter + DynSend; /// Emitter trait for emitting errors. pub trait Emitter: Translate { /// Emit a structured diagnostic. - fn emit_diagnostic(&mut self, diag: DiagInner); + fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry); /// Emit a notification that an artifact has been output. /// Currently only supported for the JSON format. @@ -189,7 +190,7 @@ pub trait Emitter: Translate { /// Emit a report about future breakage. /// Currently only supported for the JSON format. - fn emit_future_breakage_report(&mut self, _diags: Vec<DiagInner>) {} + fn emit_future_breakage_report(&mut self, _diags: Vec<DiagInner>, _registry: &Registry) {} /// Emit list of unused externs. /// Currently only supported for the JSON format. @@ -500,7 +501,7 @@ impl Emitter for HumanEmitter { self.sm.as_deref() } - fn emit_diagnostic(&mut self, mut diag: DiagInner) { + fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { let fluent_args = to_fluent_args(diag.args.iter()); let mut suggestions = diag.suggestions.unwrap_tag(); @@ -561,7 +562,7 @@ impl Emitter for SilentEmitter { None } - fn emit_diagnostic(&mut self, mut diag: DiagInner) { + fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { if self.emit_fatal_diagnostic && diag.level == Level::Fatal { if let Some(fatal_note) = &self.fatal_note { diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new()); diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index e3b6dcea892..97df7f9265a 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -44,7 +44,6 @@ mod tests; pub struct JsonEmitter { #[setters(skip)] dst: IntoDynSyncSend<Box<dyn Write + Send>>, - registry: Option<Registry>, #[setters(skip)] sm: Lrc<SourceMap>, fluent_bundle: Option<Lrc<FluentBundle>>, @@ -74,7 +73,6 @@ impl JsonEmitter { ) -> JsonEmitter { JsonEmitter { dst: IntoDynSyncSend(dst), - registry: None, sm, fluent_bundle: None, fallback_bundle, @@ -121,8 +119,8 @@ impl Translate for JsonEmitter { } impl Emitter for JsonEmitter { - fn emit_diagnostic(&mut self, diag: crate::DiagInner) { - let data = Diagnostic::from_errors_diagnostic(diag, self); + fn emit_diagnostic(&mut self, diag: crate::DiagInner, registry: &Registry) { + let data = Diagnostic::from_errors_diagnostic(diag, self, registry); let result = self.emit(EmitTyped::Diagnostic(data)); if let Err(e) = result { panic!("failed to print diagnostics: {e:?}"); @@ -137,7 +135,7 @@ impl Emitter for JsonEmitter { } } - fn emit_future_breakage_report(&mut self, diags: Vec<crate::DiagInner>) { + fn emit_future_breakage_report(&mut self, diags: Vec<crate::DiagInner>, registry: &Registry) { let data: Vec<FutureBreakageItem<'_>> = diags .into_iter() .map(|mut diag| { @@ -151,7 +149,7 @@ impl Emitter for JsonEmitter { } FutureBreakageItem { diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic( - diag, self, + diag, self, registry, )), } }) @@ -291,7 +289,11 @@ struct UnusedExterns<'a> { impl Diagnostic { /// Converts from `rustc_errors::DiagInner` to `Diagnostic`. - fn from_errors_diagnostic(diag: crate::DiagInner, je: &JsonEmitter) -> Diagnostic { + fn from_errors_diagnostic( + diag: crate::DiagInner, + je: &JsonEmitter, + registry: &Registry, + ) -> Diagnostic { let args = to_fluent_args(diag.args.iter()); let sugg_to_diag = |sugg: &CodeSuggestion| { let translated_message = @@ -344,7 +346,7 @@ impl Diagnostic { let code = if let Some(code) = diag.code { Some(DiagnosticCode { code: code.to_string(), - explanation: je.registry.as_ref().unwrap().try_find_description(code).ok(), + explanation: registry.try_find_description(code).ok(), }) } else if let Some(IsLint { name, .. }) = &diag.is_lint { Some(DiagnosticCode { code: name.to_string(), explanation: None }) @@ -382,7 +384,7 @@ impl Diagnostic { } else { OutputTheme::Ascii }) - .emit_diagnostic(diag); + .emit_diagnostic(diag, registry); let buf = Arc::try_unwrap(buf.0).unwrap().into_inner().unwrap(); let buf = String::from_utf8(buf).unwrap(); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 98200c367f9..6232c875ee8 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -55,7 +55,6 @@ pub use diagnostic_impls::{ }; pub use emitter::ColorConfig; use emitter::{DynEmitter, Emitter, is_case_difference, is_different}; -use registry::Registry; use rustc_data_structures::AtomicRef; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::{Hash128, StableHasher}; @@ -77,6 +76,8 @@ pub use snippet::Style; pub use termcolor::{Color, ColorSpec, WriteColor}; use tracing::debug; +use crate::registry::Registry; + pub mod annotate_snippet_emitter_writer; pub mod codes; mod diagnostic; @@ -483,6 +484,8 @@ impl<'a> std::ops::Deref for DiagCtxtHandle<'a> { struct DiagCtxtInner { flags: DiagCtxtFlags, + registry: Registry, + /// The error guarantees from all emitted errors. The length gives the error count. err_guars: Vec<ErrorGuaranteed>, /// The error guarantee from all emitted lint errors. The length gives the @@ -619,9 +622,7 @@ impl Drop for DiagCtxtInner { // Important: it is sound to produce an `ErrorGuaranteed` when emitting // delayed bugs because they are guaranteed to be emitted here if // necessary. - if self.err_guars.is_empty() { - self.flush_delayed() - } + self.flush_delayed(); // Sanity check: did we use some of the expensive `trimmed_def_paths` functions // unexpectedly, that is, without producing diagnostics? If so, for debugging purposes, we @@ -664,6 +665,11 @@ impl DiagCtxt { self } + pub fn with_registry(mut self, registry: Registry) -> Self { + self.inner.get_mut().registry = registry; + self + } + pub fn new(emitter: Box<DynEmitter>) -> Self { Self { inner: Lock::new(DiagCtxtInner::new(emitter)) } } @@ -694,7 +700,7 @@ impl DiagCtxt { struct FalseEmitter; impl Emitter for FalseEmitter { - fn emit_diagnostic(&mut self, _: DiagInner) { + fn emit_diagnostic(&mut self, _: DiagInner, _: &Registry) { unimplemented!("false emitter must only used during `wrap_emitter`") } @@ -759,6 +765,7 @@ impl DiagCtxt { let mut inner = self.inner.borrow_mut(); let DiagCtxtInner { flags: _, + registry: _, err_guars, lint_err_guars, delayed_bugs, @@ -964,7 +971,7 @@ impl<'a> DiagCtxtHandle<'a> { self.inner.borrow().has_errors_or_delayed_bugs() } - pub fn print_error_count(&self, registry: &Registry) { + pub fn print_error_count(&self) { let mut inner = self.inner.borrow_mut(); // Any stashed diagnostics should have been handled by @@ -1014,7 +1021,7 @@ impl<'a> DiagCtxtHandle<'a> { .emitted_diagnostic_codes .iter() .filter_map(|&code| { - if registry.try_find_description(code).is_ok() { + if inner.registry.try_find_description(code).is_ok() { Some(code.to_string()) } else { None @@ -1075,10 +1082,10 @@ impl<'a> DiagCtxtHandle<'a> { } pub fn emit_future_breakage_report(&self) { - let mut inner = self.inner.borrow_mut(); + let inner = &mut *self.inner.borrow_mut(); let diags = std::mem::take(&mut inner.future_breakage_diagnostics); if !diags.is_empty() { - inner.emitter.emit_future_breakage_report(diags); + inner.emitter.emit_future_breakage_report(diags, &inner.registry); } } @@ -1409,6 +1416,7 @@ impl DiagCtxtInner { fn new(emitter: Box<DynEmitter>) -> Self { Self { flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() }, + registry: Registry::new(&[]), err_guars: Vec::new(), lint_err_guars: Vec::new(), delayed_bugs: Vec::new(), @@ -1582,7 +1590,7 @@ impl DiagCtxtInner { } self.has_printed = true; - self.emitter.emit_diagnostic(diagnostic); + self.emitter.emit_diagnostic(diagnostic, &self.registry); } if is_error { @@ -1695,7 +1703,13 @@ impl DiagCtxtInner { // eventually happened. assert!(self.stashed_diagnostics.is_empty()); + if !self.err_guars.is_empty() { + // If an error happened already. We shouldn't expose delayed bugs. + return; + } + if self.delayed_bugs.is_empty() { + // Nothing to do. return; } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 19c2d466f7c..6a6496f9827 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -732,7 +732,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { _ => item.to_tokens(), }; let attr_item = attr.unwrap_normal_item(); - if let AttrArgs::Eq(..) = attr_item.args { + if let AttrArgs::Eq { .. } = attr_item.args { self.cx.dcx().emit_err(UnsupportedKeyValue { span }); } let inner_tokens = attr_item.args.inner_tokens(); @@ -990,7 +990,7 @@ pub fn parse_ast_fragment<'a>( } } AstFragmentKind::Ty => AstFragment::Ty(this.parse_ty()?), - AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_alt( + AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::Yes, diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index f044d964f13..9e459bd81a1 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -174,6 +174,7 @@ pub(crate) fn placeholder( vis, is_placeholder: true, safety: Safety::Default, + default: None, }]), AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant { attrs: Default::default(), diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index e910415c345..53362cb2529 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -193,6 +193,9 @@ declare_features! ( (accepted, expr_fragment_specifier_2024, "1.83.0", Some(123742)), /// Allows arbitrary expressions in key-value attributes at parse time. (accepted, extended_key_value_attributes, "1.54.0", Some(78835)), + /// Allows using `efiapi`, `aapcs`, `sysv64` and `win64` as calling + /// convention for functions with varargs. + (accepted, extended_varargs_abi_support, "CURRENT_RUSTC_VERSION", Some(100189)), /// Allows resolving absolute paths as paths from other crates. (accepted, extern_absolute_paths, "1.30.0", Some(44660)), /// Allows `extern crate foo as bar;`. This puts `bar` into extern prelude. @@ -225,7 +228,7 @@ declare_features! ( /// Allows the use of `if let` expressions. (accepted, if_let, "1.0.0", None), /// Rescoping temporaries in `if let` to align with Rust 2024. - (accepted, if_let_rescope, "CURRENT_RUSTC_VERSION", Some(124085)), + (accepted, if_let_rescope, "1.84.0", Some(124085)), /// Allows top level or-patterns (`p | q`) in `if let` and `while let`. (accepted, if_while_or_patterns, "1.33.0", Some(48215)), /// Allows lifetime elision in `impl` headers. For example: @@ -357,7 +360,7 @@ declare_features! ( (accepted, repr_transparent, "1.28.0", Some(43036)), /// Allows enums like Result<T, E> to be used across FFI, if T's niche value can /// be used to describe E or vice-versa. - (accepted, result_ffi_guarantees, "CURRENT_RUSTC_VERSION", Some(110503)), + (accepted, result_ffi_guarantees, "1.84.0", Some(110503)), /// Allows return-position `impl Trait` in traits. (accepted, return_position_impl_trait_in_trait, "1.75.0", Some(91611)), /// Allows code like `let x: &'static u32 = &42` to work (RFC 1414). @@ -367,7 +370,7 @@ declare_features! ( /// Allows `Self` struct constructor (RFC 2302). (accepted, self_struct_ctor, "1.32.0", Some(51994)), /// Shortern the tail expression lifetime - (accepted, shorter_tail_lifetimes, "CURRENT_RUSTC_VERSION", Some(123739)), + (accepted, shorter_tail_lifetimes, "1.84.0", Some(123739)), /// Allows using subslice patterns, `[a, .., b]` and `[a, xs @ .., b]`. (accepted, slice_patterns, "1.42.0", Some(62254)), /// Allows use of `&foo[a..b]` as a slicing syntax. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index fd08b35d242..3bf485c2eb6 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1104,10 +1104,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( - TEST, rustc_polymorphize_error, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes - ), - rustc_attr!( TEST, rustc_def_path, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No ), diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 6ff70044eed..8b4f441dafe 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -101,7 +101,7 @@ declare_features! ( /// Allows using `#[unsafe_destructor_blind_to_params]` (RFC 1238). (removed, dropck_parametricity, "1.38.0", Some(28498), None), /// Uses generic effect parameters for ~const bounds - (removed, effects, "CURRENT_RUSTC_VERSION", Some(102090), + (removed, effects, "1.84.0", Some(102090), Some("removed, redundant with `#![feature(const_trait_impl)]`")), /// Allows defining `existential type`s. (removed, existential_type, "1.38.0", Some(63063), @@ -119,6 +119,13 @@ declare_features! ( (removed, generator_clone, "1.65.0", Some(95360), Some("renamed to `coroutine_clone`")), /// Allows defining generators. (removed, generators, "1.21.0", Some(43122), Some("renamed to `coroutines`")), + /// An extension to the `generic_associated_types` feature, allowing incomplete features. + (removed, generic_associated_types_extended, "CURRENT_RUSTC_VERSION", Some(95451), + Some( + "feature needs overhaul and reimplementation pending \ + better implied higher-ranked implied bounds support" + ) + ), /// Allows `impl Trait` in bindings (`let`, `const`, `static`). (removed, impl_trait_in_bindings, "1.55.0", Some(63065), Some("the implementation was not maintainable, the feature may get reintroduced once the current refactorings are done")), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index aa2e451ef39..93a605e197c 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -338,7 +338,7 @@ declare_features! ( (unstable, riscv_target_feature, "1.45.0", Some(44839)), (unstable, rtm_target_feature, "1.35.0", Some(44839)), (unstable, s390x_target_feature, "1.82.0", Some(44839)), - (unstable, sparc_target_feature, "CURRENT_RUSTC_VERSION", Some(132783)), + (unstable, sparc_target_feature, "1.84.0", Some(132783)), (unstable, sse4a_target_feature, "1.27.0", Some(44839)), (unstable, tbm_target_feature, "1.27.0", Some(44839)), (unstable, wasm_target_feature, "1.30.0", Some(44839)), @@ -394,6 +394,8 @@ declare_features! ( (unstable, async_fn_track_caller, "1.73.0", Some(110011)), /// Allows `for await` loops. (unstable, async_for_loop, "1.77.0", Some(118898)), + /// Allows `async` trait bound modifier. + (unstable, async_trait_bounds, "CURRENT_RUSTC_VERSION", Some(62290)), /// Allows using C-variadics. (unstable, c_variadic, "1.34.0", Some(44930)), /// Allows the use of `#[cfg(<true/false>)]`. @@ -453,6 +455,9 @@ declare_features! ( (unstable, custom_test_frameworks, "1.30.0", Some(50297)), /// Allows declarative macros 2.0 (`macro`). (unstable, decl_macro, "1.17.0", Some(39412)), + /// Allows the use of default values on struct definitions and the construction of struct + /// literals with the functional update syntax without a base. + (unstable, default_field_values, "CURRENT_RUSTC_VERSION", Some(132162)), /// Allows using `#[deprecated_safe]` to deprecate the safeness of a function or trait (unstable, deprecated_safe, "1.61.0", Some(94978)), /// Allows having using `suggestion` in the `#[deprecated]` attribute. @@ -475,9 +480,6 @@ declare_features! ( (unstable, exhaustive_patterns, "1.13.0", Some(51085)), /// Allows explicit tail calls via `become` expression. (incomplete, explicit_tail_calls, "1.72.0", Some(112788)), - /// Allows using `efiapi`, `sysv64` and `win64` as calling convention - /// for functions with varargs. - (unstable, extended_varargs_abi_support, "1.65.0", Some(100189)), /// Allows defining `extern type`s. (unstable, extern_types, "1.23.0", Some(43467)), /// Allow using 128-bit (quad precision) floating point numbers. @@ -500,14 +502,14 @@ declare_features! ( (unstable, gen_blocks, "1.75.0", Some(117078)), /// Infer generic args for both consts and types. (unstable, generic_arg_infer, "1.55.0", Some(85077)), - /// An extension to the `generic_associated_types` feature, allowing incomplete features. - (incomplete, generic_associated_types_extended, "1.61.0", Some(95451)), /// Allows non-trivial generic constants which have to have wfness manually propagated to callers (incomplete, generic_const_exprs, "1.56.0", Some(76560)), /// Allows generic parameters and where-clauses on free & associated const items. (incomplete, generic_const_items, "1.73.0", Some(113521)), /// Allows registering static items globally, possibly across crates, to iterate over at runtime. (unstable, global_registration, "1.80.0", Some(125119)), + /// Allows using guards in patterns. + (incomplete, guard_patterns, "CURRENT_RUSTC_VERSION", Some(129967)), /// Allows using `..=X` as a patterns in slices. (unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)), /// Allows `if let` guard in match arms. @@ -538,7 +540,7 @@ declare_features! ( /// Allows `#[marker]` on certain traits allowing overlapping implementations. (unstable, marker_trait_attr, "1.30.0", Some(29864)), /// Enables the generic const args MVP (only bare paths, not arbitrary computation). - (incomplete, min_generic_const_args, "CURRENT_RUSTC_VERSION", Some(132980)), + (incomplete, min_generic_const_args, "1.84.0", Some(132980)), /// A minimal, sound subset of specialization intended to be used by the /// standard library until the soundness issues with specialization /// are fixed. diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 3276f516a52..0b7ffc4af45 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -109,7 +109,16 @@ pub enum DefKind { Use, /// An `extern` block. ForeignMod, - /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]` + /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]`. + /// + /// Not all anon-consts are actually still relevant in the HIR. We lower + /// trivial const-arguments directly to `hir::ConstArgKind::Path`, at which + /// point the definition for the anon-const ends up unused and incomplete. + /// + /// We do not provide any a `Span` for the definition and pretty much all other + /// queries also ICE when using this `DefId`. Given that the `DefId` of such + /// constants should only be reachable by iterating all definitions of a + /// given crate, you should not have to worry about this. AnonConst, /// An inline constant, e.g. `const { 1 + 2 }` InlineConst, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 16e53a27128..365e4cbb556 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,7 +1,7 @@ use std::fmt; use rustc_abi::ExternAbi; -use rustc_ast::util::parser::ExprPrecedence; +use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; use rustc_ast::{ self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitKind, TraitObjectSyntax, UintTy, @@ -261,8 +261,6 @@ pub struct ConstArg<'hir> { #[stable_hasher(ignore)] pub hir_id: HirId, pub kind: ConstArgKind<'hir>, - /// Indicates whether this comes from a `~const` desugaring. - pub is_desugared_from_effects: bool, } impl<'hir> ConstArg<'hir> { @@ -277,6 +275,7 @@ impl<'hir> ConstArg<'hir> { match self.kind { ConstArgKind::Path(path) => path.span(), ConstArgKind::Anon(anon) => anon.span, + ConstArgKind::Infer(span) => span, } } } @@ -291,6 +290,11 @@ pub enum ConstArgKind<'hir> { /// However, in the future, we'll be using it for all of those. Path(QPath<'hir>), Anon(&'hir AnonConst), + /// **Note:** Not all inferred consts are represented as + /// `ConstArgKind::Infer`. In cases where it is ambiguous whether + /// a generic arg is a type or a const, inference variables are + /// represented as `GenericArg::Infer` instead. + Infer(Span), } #[derive(Clone, Copy, Debug, HashStable_Generic)] @@ -310,6 +314,10 @@ pub enum GenericArg<'hir> { Lifetime(&'hir Lifetime), Type(&'hir Ty<'hir>), Const(&'hir ConstArg<'hir>), + /// **Note:** Inference variables are only represented as + /// `GenericArg::Infer` in cases where it is ambiguous whether + /// a generic arg is a type or a const. Otherwise, inference variables + /// are represented as `TyKind::Infer` or `ConstArgKind::Infer`. Infer(InferArg), } @@ -462,14 +470,7 @@ impl<'hir> GenericArgs<'hir> { /// This function returns the number of type and const generic params. /// It should only be used for diagnostics. pub fn num_generic_params(&self) -> usize { - self.args - .iter() - .filter(|arg| match arg { - GenericArg::Lifetime(_) - | GenericArg::Const(ConstArg { is_desugared_from_effects: true, .. }) => false, - _ => true, - }) - .count() + self.args.iter().filter(|arg| !matches!(arg, GenericArg::Lifetime(_))).count() } /// The span encompassing the arguments and constraints[^1] inside the surrounding brackets. @@ -690,8 +691,8 @@ impl<'hir> Generics<'hir> { if self.has_where_clause_predicates { self.predicates .iter() - .rfind(|&p| p.in_where_clause()) - .map_or(end, |p| p.span()) + .rfind(|&p| p.kind.in_where_clause()) + .map_or(end, |p| p.span) .shrink_to_hi() .to(end) } else { @@ -714,8 +715,10 @@ impl<'hir> Generics<'hir> { &self, param_def_id: LocalDefId, ) -> impl Iterator<Item = &WhereBoundPredicate<'hir>> { - self.predicates.iter().filter_map(move |pred| match pred { - WherePredicate::BoundPredicate(bp) if bp.is_param_bound(param_def_id.to_def_id()) => { + self.predicates.iter().filter_map(move |pred| match pred.kind { + WherePredicateKind::BoundPredicate(bp) + if bp.is_param_bound(param_def_id.to_def_id()) => + { Some(bp) } _ => None, @@ -726,8 +729,8 @@ impl<'hir> Generics<'hir> { &self, param_def_id: LocalDefId, ) -> impl Iterator<Item = &WhereRegionPredicate<'_>> { - self.predicates.iter().filter_map(move |pred| match pred { - WherePredicate::RegionPredicate(rp) if rp.is_param_bound(param_def_id) => Some(rp), + self.predicates.iter().filter_map(move |pred| match pred.kind { + WherePredicateKind::RegionPredicate(rp) if rp.is_param_bound(param_def_id) => Some(rp), _ => None, }) } @@ -779,9 +782,9 @@ impl<'hir> Generics<'hir> { pub fn span_for_predicate_removal(&self, pos: usize) -> Span { let predicate = &self.predicates[pos]; - let span = predicate.span(); + let span = predicate.span; - if !predicate.in_where_clause() { + if !predicate.kind.in_where_clause() { // <T: ?Sized, U> // ^^^^^^^^ return span; @@ -790,19 +793,19 @@ impl<'hir> Generics<'hir> { // We need to find out which comma to remove. if pos < self.predicates.len() - 1 { let next_pred = &self.predicates[pos + 1]; - if next_pred.in_where_clause() { + if next_pred.kind.in_where_clause() { // where T: ?Sized, Foo: Bar, // ^^^^^^^^^^^ - return span.until(next_pred.span()); + return span.until(next_pred.span); } } if pos > 0 { let prev_pred = &self.predicates[pos - 1]; - if prev_pred.in_where_clause() { + if prev_pred.kind.in_where_clause() { // where Foo: Bar, T: ?Sized, // ^^^^^^^^^^^ - return prev_pred.span().shrink_to_hi().to(span); + return prev_pred.span.shrink_to_hi().to(span); } } @@ -814,7 +817,7 @@ impl<'hir> Generics<'hir> { pub fn span_for_bound_removal(&self, predicate_pos: usize, bound_pos: usize) -> Span { let predicate = &self.predicates[predicate_pos]; - let bounds = predicate.bounds(); + let bounds = predicate.kind.bounds(); if bounds.len() == 1 { return self.span_for_predicate_removal(predicate_pos); @@ -841,7 +844,15 @@ impl<'hir> Generics<'hir> { /// A single predicate in a where-clause. #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub enum WherePredicate<'hir> { +pub struct WherePredicate<'hir> { + pub hir_id: HirId, + pub span: Span, + pub kind: &'hir WherePredicateKind<'hir>, +} + +/// The kind of a single predicate in a where-clause. +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub enum WherePredicateKind<'hir> { /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). BoundPredicate(WhereBoundPredicate<'hir>), /// A lifetime predicate (e.g., `'a: 'b + 'c`). @@ -850,28 +861,20 @@ pub enum WherePredicate<'hir> { EqPredicate(WhereEqPredicate<'hir>), } -impl<'hir> WherePredicate<'hir> { - pub fn span(&self) -> Span { - match self { - WherePredicate::BoundPredicate(p) => p.span, - WherePredicate::RegionPredicate(p) => p.span, - WherePredicate::EqPredicate(p) => p.span, - } - } - +impl<'hir> WherePredicateKind<'hir> { pub fn in_where_clause(&self) -> bool { match self { - WherePredicate::BoundPredicate(p) => p.origin == PredicateOrigin::WhereClause, - WherePredicate::RegionPredicate(p) => p.in_where_clause, - WherePredicate::EqPredicate(_) => false, + WherePredicateKind::BoundPredicate(p) => p.origin == PredicateOrigin::WhereClause, + WherePredicateKind::RegionPredicate(p) => p.in_where_clause, + WherePredicateKind::EqPredicate(_) => false, } } pub fn bounds(&self) -> GenericBounds<'hir> { match self { - WherePredicate::BoundPredicate(p) => p.bounds, - WherePredicate::RegionPredicate(p) => p.bounds, - WherePredicate::EqPredicate(_) => &[], + WherePredicateKind::BoundPredicate(p) => p.bounds, + WherePredicateKind::RegionPredicate(p) => p.bounds, + WherePredicateKind::EqPredicate(_) => &[], } } } @@ -886,8 +889,6 @@ pub enum PredicateOrigin { /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct WhereBoundPredicate<'hir> { - pub hir_id: HirId, - pub span: Span, /// Origin of the predicate. pub origin: PredicateOrigin, /// Any generics from a `for` binding. @@ -908,7 +909,6 @@ impl<'hir> WhereBoundPredicate<'hir> { /// A lifetime predicate (e.g., `'a: 'b + 'c`). #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct WhereRegionPredicate<'hir> { - pub span: Span, pub in_where_clause: bool, pub lifetime: &'hir Lifetime, pub bounds: GenericBounds<'hir>, @@ -924,7 +924,6 @@ impl<'hir> WhereRegionPredicate<'hir> { /// An equality predicate (e.g., `T = int`); currently unsupported. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct WhereEqPredicate<'hir> { - pub span: Span, pub lhs_ty: &'hir Ty<'hir>, pub rhs_ty: &'hir Ty<'hir>, } @@ -1656,29 +1655,6 @@ impl fmt::Display for ConstContext { /// A literal. pub type Lit = Spanned<LitKind>; -#[derive(Copy, Clone, Debug, HashStable_Generic)] -pub enum ArrayLen<'hir> { - Infer(InferArg), - Body(&'hir ConstArg<'hir>), -} - -impl ArrayLen<'_> { - pub fn span(self) -> Span { - match self { - ArrayLen::Infer(arg) => arg.span, - ArrayLen::Body(body) => body.span(), - } - } - - pub fn hir_id(self) -> HirId { - match self { - ArrayLen::Infer(InferArg { hir_id, .. }) | ArrayLen::Body(&ConstArg { hir_id, .. }) => { - hir_id - } - } - } -} - /// A constant (expression) that's not an item or associated item, /// but needs its own `DefId` for type-checking, const-eval, etc. /// These are usually found nested inside types (e.g., array lengths) @@ -1721,39 +1697,52 @@ pub struct Expr<'hir> { impl Expr<'_> { pub fn precedence(&self) -> ExprPrecedence { match self.kind { - ExprKind::ConstBlock(_) => ExprPrecedence::ConstBlock, - ExprKind::Array(_) => ExprPrecedence::Array, - ExprKind::Call(..) => ExprPrecedence::Call, - ExprKind::MethodCall(..) => ExprPrecedence::MethodCall, - ExprKind::Tup(_) => ExprPrecedence::Tup, - ExprKind::Binary(op, ..) => ExprPrecedence::Binary(op.node), - ExprKind::Unary(..) => ExprPrecedence::Unary, - ExprKind::Lit(_) => ExprPrecedence::Lit, + ExprKind::Closure { .. } => ExprPrecedence::Closure, + + ExprKind::Break(..) + | ExprKind::Continue(..) + | ExprKind::Ret(..) + | ExprKind::Yield(..) + | ExprKind::Become(..) => ExprPrecedence::Jump, + + // Binop-like expr kinds, handled by `AssocOp`. + ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(), ExprKind::Cast(..) => ExprPrecedence::Cast, + + ExprKind::Assign(..) | + ExprKind::AssignOp(..) => ExprPrecedence::Assign, + + // Unary, prefix + ExprKind::AddrOf(..) + // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`. + // However, this is not exactly right. When `let _ = a` is the LHS of a binop we + // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` + // but we need to print `(let _ = a) < b` as-is with parens. + | ExprKind::Let(..) + | ExprKind::Unary(..) => ExprPrecedence::Prefix, + + // Never need parens + ExprKind::Array(_) + | ExprKind::Block(..) + | ExprKind::Call(..) + | ExprKind::ConstBlock(_) + | ExprKind::Field(..) + | ExprKind::If(..) + | ExprKind::Index(..) + | ExprKind::InlineAsm(..) + | ExprKind::Lit(_) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::MethodCall(..) + | ExprKind::OffsetOf(..) + | ExprKind::Path(..) + | ExprKind::Repeat(..) + | ExprKind::Struct(..) + | ExprKind::Tup(_) + | ExprKind::Type(..) + | ExprKind::Err(_) => ExprPrecedence::Unambiguous, + ExprKind::DropTemps(ref expr, ..) => expr.precedence(), - ExprKind::If(..) => ExprPrecedence::If, - ExprKind::Let(..) => ExprPrecedence::Let, - ExprKind::Loop(..) => ExprPrecedence::Loop, - ExprKind::Match(..) => ExprPrecedence::Match, - ExprKind::Closure { .. } => ExprPrecedence::Closure, - ExprKind::Block(..) => ExprPrecedence::Block, - ExprKind::Assign(..) => ExprPrecedence::Assign, - ExprKind::AssignOp(..) => ExprPrecedence::AssignOp, - ExprKind::Field(..) => ExprPrecedence::Field, - ExprKind::Index(..) => ExprPrecedence::Index, - ExprKind::Path(..) => ExprPrecedence::Path, - ExprKind::AddrOf(..) => ExprPrecedence::AddrOf, - ExprKind::Break(..) => ExprPrecedence::Break, - ExprKind::Continue(..) => ExprPrecedence::Continue, - ExprKind::Ret(..) => ExprPrecedence::Ret, - ExprKind::Become(..) => ExprPrecedence::Become, - ExprKind::Struct(..) => ExprPrecedence::Struct, - ExprKind::Repeat(..) => ExprPrecedence::Repeat, - ExprKind::Yield(..) => ExprPrecedence::Yield, - ExprKind::Type(..) | ExprKind::InlineAsm(..) | ExprKind::OffsetOf(..) => { - ExprPrecedence::Mac - } - ExprKind::Err(_) => ExprPrecedence::Err, } } @@ -1868,7 +1857,12 @@ impl Expr<'_> { base.can_have_side_effects() } ExprKind::Struct(_, fields, init) => { - fields.iter().map(|field| field.expr).chain(init).any(|e| e.can_have_side_effects()) + let init_side_effects = match init { + StructTailExpr::Base(init) => init.can_have_side_effects(), + StructTailExpr::DefaultFields(_) | StructTailExpr::None => false, + }; + fields.iter().map(|field| field.expr).any(|e| e.can_have_side_effects()) + || init_side_effects } ExprKind::Array(args) @@ -1937,20 +1931,52 @@ impl Expr<'_> { ExprKind::Path(QPath::Resolved(None, path2)), ) => path1.res == path2.res, ( - ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val1], None), - ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val2], None), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeTo, _), + [val1], + StructTailExpr::None, + ), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeTo, _), + [val2], + StructTailExpr::None, + ), ) | ( - ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val1], None), - ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val2], None), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeToInclusive, _), + [val1], + StructTailExpr::None, + ), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeToInclusive, _), + [val2], + StructTailExpr::None, + ), ) | ( - ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val1], None), - ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val2], None), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeFrom, _), + [val1], + StructTailExpr::None, + ), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeFrom, _), + [val2], + StructTailExpr::None, + ), ) => val1.expr.equivalent_for_indexing(val2.expr), ( - ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val1, val3], None), - ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val2, val4], None), + ExprKind::Struct( + QPath::LangItem(LangItem::Range, _), + [val1, val3], + StructTailExpr::None, + ), + ExprKind::Struct( + QPath::LangItem(LangItem::Range, _), + [val2, val4], + StructTailExpr::None, + ), ) => { val1.expr.equivalent_for_indexing(val2.expr) && val3.expr.equivalent_for_indexing(val4.expr) @@ -2107,13 +2133,13 @@ pub enum ExprKind<'hir> { /// /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`, /// where `base` is the `Option<Expr>`. - Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], Option<&'hir Expr<'hir>>), + Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], StructTailExpr<'hir>), /// An array literal constructed from one repeated element. /// /// E.g., `[1; 5]`. The first expression is the element /// to be repeated; the second is the number of times to repeat it. - Repeat(&'hir Expr<'hir>, ArrayLen<'hir>), + Repeat(&'hir Expr<'hir>, &'hir ConstArg<'hir>), /// A suspension point for coroutines (i.e., `yield <expr>`). Yield(&'hir Expr<'hir>, YieldSource), @@ -2122,6 +2148,19 @@ pub enum ExprKind<'hir> { Err(rustc_span::ErrorGuaranteed), } +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub enum StructTailExpr<'hir> { + /// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`. + None, + /// A struct expression with a "base", an expression of the same type as the outer struct that + /// will be used to populate any fields not explicitly mentioned: `Foo { ..base }` + Base(&'hir Expr<'hir>), + /// A struct expression with a `..` tail but no "base" expression. The values from the struct + /// fields' default values will be used to populate any fields not explicitly mentioned: + /// `Foo { .. }`. + DefaultFields(Span), +} + /// Represents an optionally `Self`-qualified value/type path or associated extension. /// /// To resolve the path to a `DefId`, call [`qpath_res`]. @@ -2623,7 +2662,7 @@ impl<'hir> Ty<'hir> { TyKind::Infer => true, TyKind::Slice(ty) => ty.is_suggestable_infer_ty(), TyKind::Array(ty, length) => { - ty.is_suggestable_infer_ty() || matches!(length, ArrayLen::Infer(..)) + ty.is_suggestable_infer_ty() || matches!(length.kind, ConstArgKind::Infer(..)) } TyKind::Tup(tys) => tys.iter().any(Self::is_suggestable_infer_ty), TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => mut_ty.ty.is_suggestable_infer_ty(), @@ -2832,7 +2871,7 @@ pub enum TyKind<'hir> { /// A variable length slice (i.e., `[T]`). Slice(&'hir Ty<'hir>), /// A fixed length array (i.e., `[T; n]`). - Array(&'hir Ty<'hir>, ArrayLen<'hir>), + Array(&'hir Ty<'hir>, &'hir ConstArg<'hir>), /// A raw pointer (i.e., `*const T` or `*mut T`). Ptr(MutTy<'hir>), /// A reference (i.e., `&'a T` or `&'a mut T`). @@ -2859,6 +2898,11 @@ pub enum TyKind<'hir> { Typeof(&'hir AnonConst), /// `TyKind::Infer` means the type should be inferred instead of it having been /// specified. This can appear anywhere in a type. + /// + /// **Note:** Not all inferred types are represented as + /// `TyKind::Infer`. In cases where it is ambiguous whether + /// a generic arg is a type or a const, inference variables are + /// represented as `GenericArg::Infer` instead. Infer, /// Placeholder for a type that has failed to be defined. Err(rustc_span::ErrorGuaranteed), @@ -3178,6 +3222,7 @@ pub struct FieldDef<'hir> { pub def_id: LocalDefId, pub ty: &'hir Ty<'hir>, pub safety: Safety, + pub default: Option<&'hir AnonConst>, } impl FieldDef<'_> { @@ -3798,9 +3843,7 @@ pub enum Node<'hir> { GenericParam(&'hir GenericParam<'hir>), Crate(&'hir Mod<'hir>), Infer(&'hir InferArg), - WhereBoundPredicate(&'hir WhereBoundPredicate<'hir>), - // FIXME: Merge into `Node::Infer`. - ArrayLenInfer(&'hir InferArg), + WherePredicate(&'hir WherePredicate<'hir>), PreciseCapturingNonLifetimeArg(&'hir PreciseCapturingNonLifetimeArg), // Created by query feeding Synthetic, @@ -3853,8 +3896,7 @@ impl<'hir> Node<'hir> { | Node::TraitRef(..) | Node::OpaqueTy(..) | Node::Infer(..) - | Node::WhereBoundPredicate(..) - | Node::ArrayLenInfer(..) + | Node::WherePredicate(..) | Node::Synthetic | Node::Err(..) => None, } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index a453af3f7fd..9abb0870bf0 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -343,9 +343,6 @@ pub trait Visitor<'v>: Sized { fn visit_pat_field(&mut self, f: &'v PatField<'v>) -> Self::Result { walk_pat_field(self, f) } - fn visit_array_length(&mut self, len: &'v ArrayLen<'v>) -> Self::Result { - walk_array_len(self, len) - } fn visit_anon_const(&mut self, c: &'v AnonConst) -> Self::Result { walk_anon_const(self, c) } @@ -710,14 +707,6 @@ pub fn walk_pat_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v PatField<' visitor.visit_pat(field.pat) } -pub fn walk_array_len<'v, V: Visitor<'v>>(visitor: &mut V, len: &'v ArrayLen<'v>) -> V::Result { - match len { - // FIXME: Use `visit_infer` here. - ArrayLen::Infer(InferArg { hir_id, span: _ }) => visitor.visit_id(*hir_id), - ArrayLen::Body(c) => visitor.visit_const_arg(c), - } -} - pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonConst) -> V::Result { try_visit!(visitor.visit_id(constant.hir_id)); visitor.visit_nested_body(constant.body) @@ -739,6 +728,7 @@ pub fn walk_const_arg<'v, V: Visitor<'v>>( match &const_arg.kind { ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, const_arg.hir_id, qpath.span()), ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon), + ConstArgKind::Infer(..) => V::Result::output(), } } @@ -753,12 +743,15 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) } ExprKind::Repeat(ref element, ref count) => { try_visit!(visitor.visit_expr(element)); - try_visit!(visitor.visit_array_length(count)); + try_visit!(visitor.visit_const_arg(count)); } ExprKind::Struct(ref qpath, fields, ref optional_base) => { try_visit!(visitor.visit_qpath(qpath, expression.hir_id, expression.span)); walk_list!(visitor, visit_expr_field, fields); - visit_opt!(visitor, visit_expr, optional_base); + match optional_base { + StructTailExpr::Base(base) => try_visit!(visitor.visit_expr(base)), + StructTailExpr::None | StructTailExpr::DefaultFields(_) => {} + } } ExprKind::Tup(subexpressions) => { walk_list!(visitor, visit_expr, subexpressions); @@ -901,7 +894,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul } TyKind::Array(ref ty, ref length) => { try_visit!(visitor.visit_ty(ty)); - try_visit!(visitor.visit_array_length(length)); + try_visit!(visitor.visit_const_arg(length)); } TyKind::TraitObject(bounds, ref lifetime, _syntax) => { for bound in bounds { @@ -961,30 +954,28 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>( visitor: &mut V, predicate: &'v WherePredicate<'v>, ) -> V::Result { - match *predicate { - WherePredicate::BoundPredicate(WhereBoundPredicate { - hir_id, + let &WherePredicate { hir_id, kind, span: _ } = predicate; + try_visit!(visitor.visit_id(hir_id)); + match *kind { + WherePredicateKind::BoundPredicate(WhereBoundPredicate { ref bounded_ty, bounds, bound_generic_params, origin: _, - span: _, }) => { - try_visit!(visitor.visit_id(hir_id)); try_visit!(visitor.visit_ty(bounded_ty)); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_generic_param, bound_generic_params); } - WherePredicate::RegionPredicate(WhereRegionPredicate { + WherePredicateKind::RegionPredicate(WhereRegionPredicate { ref lifetime, bounds, - span: _, in_where_clause: _, }) => { try_visit!(visitor.visit_lifetime(lifetime)); walk_list!(visitor, visit_param_bound, bounds); } - WherePredicate::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty, span: _ }) => { + WherePredicateKind::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty }) => { try_visit!(visitor.visit_ty(lhs_ty)); try_visit!(visitor.visit_ty(rhs_ty)); } @@ -1202,10 +1193,14 @@ pub fn walk_struct_def<'v, V: Visitor<'v>>( V::Result::output() } -pub fn walk_field_def<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v FieldDef<'v>) -> V::Result { - try_visit!(visitor.visit_id(field.hir_id)); - try_visit!(visitor.visit_ident(field.ident)); - visitor.visit_ty(field.ty) +pub fn walk_field_def<'v, V: Visitor<'v>>( + visitor: &mut V, + FieldDef { hir_id, ident, ty, default, span: _, vis_span: _, def_id: _, safety: _ }: &'v FieldDef<'v>, +) -> V::Result { + try_visit!(visitor.visit_id(*hir_id)); + try_visit!(visitor.visit_ident(*ident)); + visit_opt!(visitor, visit_anon_const, default); + visitor.visit_ty(*ty) } pub fn walk_enum_def<'v, V: Visitor<'v>>( diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 49b4a1fabec..070d63b48b7 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -113,8 +113,6 @@ hir_analysis_const_param_ty_impl_on_unsized = the trait `ConstParamTy` may not be implemented for this type .label = type is not `Sized` -hir_analysis_const_specialize = cannot specialize on const impl with non-const impl - hir_analysis_copy_impl_on_non_adt = the trait `Copy` cannot be implemented for this type .label = type is not a structure or enumeration @@ -590,7 +588,7 @@ hir_analysis_value_of_associated_struct_already_specified = .label = re-bound here .previous_bound_label = `{$item_name}` bound here first -hir_analysis_variadic_function_compatible_convention = C-variadic function must have a compatible calling convention, like {$conventions} +hir_analysis_variadic_function_compatible_convention = C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi` .label = C-variadic function must have a compatible calling convention hir_analysis_variances_of = {$variances} diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index c66572a9377..022a6d457ec 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -17,7 +17,7 @@ use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; use rustc_middle::span_bug; use rustc_middle::ty::error::TypeErrorToStringExt; -use rustc_middle::ty::fold::BottomUpFolder; +use rustc_middle::ty::fold::{BottomUpFolder, fold_regions}; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt}; use rustc_middle::ty::{ @@ -33,7 +33,7 @@ use tracing::{debug, instrument}; use ty::TypingMode; use {rustc_attr as attr, rustc_hir as hir}; -use super::compare_impl_item::{check_type_bounds, compare_impl_method, compare_impl_ty}; +use super::compare_impl_item::check_type_bounds; use super::*; use crate::check::intrinsicck::InlineAsmCtxt; @@ -103,7 +103,7 @@ fn allowed_union_or_unsafe_field<'tcx>( // Fallback case: allow `ManuallyDrop` and things that are `Copy`, // also no need to report an error if the type is unresolved. ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop()) - || ty.is_copy_modulo_regions(tcx, typing_env) + || tcx.type_is_copy_modulo_regions(typing_env, ty) || ty.references_error() } }; @@ -322,8 +322,12 @@ fn check_opaque_meets_bounds<'tcx>( }; let param_env = tcx.param_env(defining_use_anchor); - // FIXME(#132279): This should eventually use the already defined hidden types. - let infcx = tcx.infer_ctxt().build(TypingMode::analysis_in_body(tcx, defining_use_anchor)); + // FIXME(#132279): Once `PostBorrowckAnalysis` is supported in the old solver, this branch should be removed. + let infcx = tcx.infer_ctxt().build(if tcx.next_trait_solver_globally() { + TypingMode::post_borrowck_analysis(tcx, defining_use_anchor) + } else { + TypingMode::analysis_in_body(tcx, defining_use_anchor) + }); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let args = match origin { @@ -346,7 +350,7 @@ fn check_opaque_meets_bounds<'tcx>( // FIXME: Consider wrapping the hidden type in an existential `Binder` and instantiating it // here rather than using ReErased. let hidden_ty = tcx.type_of(def_id.to_def_id()).instantiate(tcx, args); - let hidden_ty = tcx.fold_regions(hidden_ty, |re, _dbi| match re.kind() { + let hidden_ty = fold_regions(tcx, hidden_ty, |re, _dbi| match re.kind() { ty::ReErased => infcx.next_region_var(RegionVariableOrigin::MiscVariable(span)), _ => re, }); @@ -417,7 +421,11 @@ fn check_opaque_meets_bounds<'tcx>( let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?; - if let hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } = origin { + if infcx.next_trait_solver() { + Ok(()) + } else if let hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } = + origin + { // HACK: this should also fall through to the hidden type check below, but the original // implementation had a bug where equivalent lifetimes are not identical. This caused us // to reject existing stable code that is otherwise completely fine. The real fix is to @@ -1036,18 +1044,23 @@ fn check_impl_items_against_trait<'tcx>( tcx.dcx().span_delayed_bug(tcx.def_span(impl_item), "missing associated item in trait"); continue; }; - match ty_impl_item.kind { - ty::AssocKind::Const => { - tcx.ensure().compare_impl_const(( - impl_item.expect_local(), - ty_impl_item.trait_item_def_id.unwrap(), - )); - } - ty::AssocKind::Fn => { - compare_impl_method(tcx, ty_impl_item, ty_trait_item, trait_ref); - } - ty::AssocKind::Type => { - compare_impl_ty(tcx, ty_impl_item, ty_trait_item, trait_ref); + + let res = tcx.ensure().compare_impl_item(impl_item.expect_local()); + + if res.is_ok() { + match ty_impl_item.kind { + ty::AssocKind::Fn => { + compare_impl_item::refine::check_refining_return_position_impl_trait_in_trait( + tcx, + ty_impl_item, + ty_trait_item, + tcx.impl_trait_ref(ty_impl_item.container_id(tcx)) + .unwrap() + .instantiate_identity(), + ); + } + ty::AssocKind::Const => {} + ty::AssocKind::Type => {} } } 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 f86ca95a954..e176fc58999 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -33,7 +33,25 @@ use tracing::{debug, instrument}; use super::potentially_plural_count; use crate::errors::{LifetimesOrBoundsMismatchOnTrait, MethodShouldReturnFuture}; -mod refine; +pub(super) mod refine; + +/// Call the query `tcx.compare_impl_item()` directly instead. +pub(super) fn compare_impl_item( + tcx: TyCtxt<'_>, + impl_item_def_id: LocalDefId, +) -> Result<(), ErrorGuaranteed> { + let impl_item = tcx.associated_item(impl_item_def_id); + let trait_item = tcx.associated_item(impl_item.trait_item_def_id.unwrap()); + let impl_trait_ref = + tcx.impl_trait_ref(impl_item.container_id(tcx)).unwrap().instantiate_identity(); + debug!(?impl_trait_ref); + + match impl_item.kind { + ty::AssocKind::Fn => compare_impl_method(tcx, impl_item, trait_item, impl_trait_ref), + ty::AssocKind::Type => compare_impl_ty(tcx, impl_item, trait_item, impl_trait_ref), + ty::AssocKind::Const => compare_impl_const(tcx, impl_item, trait_item, impl_trait_ref), + } +} /// Checks that a method from an impl conforms to the signature of /// the same method as declared in the trait. @@ -44,22 +62,15 @@ mod refine; /// - `trait_m`: the method in the trait /// - `impl_trait_ref`: the TraitRef corresponding to the trait implementation #[instrument(level = "debug", skip(tcx))] -pub(super) fn compare_impl_method<'tcx>( +fn compare_impl_method<'tcx>( tcx: TyCtxt<'tcx>, impl_m: ty::AssocItem, trait_m: ty::AssocItem, impl_trait_ref: ty::TraitRef<'tcx>, -) { - let _: Result<_, ErrorGuaranteed> = try { - check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, false)?; - compare_method_predicate_entailment(tcx, impl_m, trait_m, impl_trait_ref)?; - refine::check_refining_return_position_impl_trait_in_trait( - tcx, - impl_m, - trait_m, - impl_trait_ref, - ); - }; +) -> Result<(), ErrorGuaranteed> { + check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, false)?; + compare_method_predicate_entailment(tcx, impl_m, trait_m, impl_trait_ref)?; + Ok(()) } /// Checks a bunch of different properties of the impl/trait methods for @@ -523,8 +534,9 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( let impl_sig = ocx.normalize( &misc_cause, param_env, - tcx.liberate_late_bound_regions( - impl_m.def_id, + infcx.instantiate_binder_with_fresh_vars( + return_span, + infer::HigherRankedType, tcx.fn_sig(impl_m.def_id).instantiate_identity(), ), ); @@ -536,10 +548,9 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // them with inference variables. // We will use these inference variables to collect the hidden types of RPITITs. let mut collector = ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_def_id); - let unnormalized_trait_sig = infcx - .instantiate_binder_with_fresh_vars( - return_span, - infer::HigherRankedType, + let unnormalized_trait_sig = tcx + .liberate_late_bound_regions( + impl_m.def_id, tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_to_impl_args), ) .fold_with(&mut collector); @@ -702,8 +713,8 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( let mut remapped_types = DefIdMap::default(); for (def_id, (ty, args)) in collected_types { - match infcx.fully_resolve((ty, args)) { - Ok((ty, args)) => { + match infcx.fully_resolve(ty) { + Ok(ty) => { // `ty` contains free regions that we created earlier while liberating the // trait fn signature. However, projection normalization expects `ty` to // contains `def_id`'s early-bound regions. @@ -883,33 +894,27 @@ impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> { self.tcx } - fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> { - if let ty::Alias(ty::Opaque, ty::AliasTy { args, def_id, .. }) = *t.kind() { - let mut mapped_args = Vec::with_capacity(args.len()); - for (arg, v) in std::iter::zip(args, self.tcx.variances_of(def_id)) { - mapped_args.push(match (arg.unpack(), v) { - // Skip uncaptured opaque args - (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => arg, - _ => arg.try_fold_with(self)?, - }); - } - Ok(Ty::new_opaque(self.tcx, def_id, self.tcx.mk_args(&mapped_args))) - } else { - t.try_super_fold_with(self) - } - } - fn try_fold_region( &mut self, region: ty::Region<'tcx>, ) -> Result<ty::Region<'tcx>, Self::Error> { match region.kind() { - // Remap late-bound regions from the function. + // Never remap bound regions or `'static` + ty::ReBound(..) | ty::ReStatic | ty::ReError(_) => return Ok(region), + // We always remap liberated late-bound regions from the function. ty::ReLateParam(_) => {} // Remap early-bound regions as long as they don't come from the `impl` itself, // in which case we don't really need to renumber them. - ty::ReEarlyParam(ebr) if ebr.index as usize >= self.num_impl_args => {} - _ => return Ok(region), + ty::ReEarlyParam(ebr) => { + if ebr.index as usize >= self.num_impl_args { + // Remap + } else { + return Ok(region); + } + } + ty::ReVar(_) | ty::RePlaceholder(_) | ty::ReErased => unreachable!( + "should not have leaked vars or placeholders into hidden type of RPITIT" + ), } let e = if let Some(id_region) = self.map.get(®ion) { @@ -1100,7 +1105,7 @@ fn check_region_bounds_on_impl_item<'tcx>( // FIXME: we could potentially look at the impl's bounds to not point at bounds that // *are* present in the impl. for p in trait_generics.predicates { - if let hir::WherePredicate::BoundPredicate(pred) = p { + if let hir::WherePredicateKind::BoundPredicate(pred) = p.kind { for b in pred.bounds { if let hir::GenericBound::Outlives(lt) = b { bounds_span.push(lt.ident.span); @@ -1113,7 +1118,7 @@ fn check_region_bounds_on_impl_item<'tcx>( { let mut impl_bounds = 0; for p in impl_generics.predicates { - if let hir::WherePredicate::BoundPredicate(pred) = p { + if let hir::WherePredicateKind::BoundPredicate(pred) = p.kind { for b in pred.bounds { if let hir::GenericBound::Outlives(_) = b { impl_bounds += 1; @@ -1727,17 +1732,12 @@ fn compare_generic_param_kinds<'tcx>( Ok(()) } -/// Use `tcx.compare_impl_const` instead -pub(super) fn compare_impl_const_raw( - tcx: TyCtxt<'_>, - (impl_const_item_def, trait_const_item_def): (LocalDefId, DefId), +fn compare_impl_const<'tcx>( + tcx: TyCtxt<'tcx>, + impl_const_item: ty::AssocItem, + trait_const_item: ty::AssocItem, + impl_trait_ref: ty::TraitRef<'tcx>, ) -> Result<(), ErrorGuaranteed> { - let impl_const_item = tcx.associated_item(impl_const_item_def); - let trait_const_item = tcx.associated_item(trait_const_item_def); - let impl_trait_ref = - tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap().instantiate_identity(); - debug!(?impl_trait_ref); - compare_number_of_generics(tcx, impl_const_item, trait_const_item, false)?; compare_generic_param_kinds(tcx, impl_const_item, trait_const_item, false)?; check_region_bounds_on_impl_item(tcx, impl_const_item, trait_const_item, false)?; @@ -1868,19 +1868,17 @@ fn compare_const_predicate_entailment<'tcx>( } #[instrument(level = "debug", skip(tcx))] -pub(super) fn compare_impl_ty<'tcx>( +fn compare_impl_ty<'tcx>( tcx: TyCtxt<'tcx>, impl_ty: ty::AssocItem, trait_ty: ty::AssocItem, impl_trait_ref: ty::TraitRef<'tcx>, -) { - let _: Result<(), ErrorGuaranteed> = try { - compare_number_of_generics(tcx, impl_ty, trait_ty, false)?; - compare_generic_param_kinds(tcx, impl_ty, trait_ty, false)?; - check_region_bounds_on_impl_item(tcx, impl_ty, trait_ty, false)?; - compare_type_predicate_entailment(tcx, impl_ty, trait_ty, impl_trait_ref)?; - check_type_bounds(tcx, trait_ty, impl_ty, impl_trait_ref)?; - }; +) -> Result<(), ErrorGuaranteed> { + compare_number_of_generics(tcx, impl_ty, trait_ty, false)?; + compare_generic_param_kinds(tcx, impl_ty, trait_ty, false)?; + check_region_bounds_on_impl_item(tcx, impl_ty, trait_ty, false)?; + compare_type_predicate_entailment(tcx, impl_ty, trait_ty, impl_trait_ref)?; + check_type_bounds(tcx, trait_ty, impl_ty, impl_trait_ref) } /// The equivalent of [compare_method_predicate_entailment], but for associated types diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index 67cbcc1566a..6eac4ac3baf 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -17,7 +17,7 @@ use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt; use rustc_trait_selection::traits::{ObligationCtxt, elaborate, normalize_param_env_or_error}; /// Check that an implementation does not refine an RPITIT from a trait method signature. -pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>( +pub(crate) fn check_refining_return_position_impl_trait_in_trait<'tcx>( tcx: TyCtxt<'tcx>, impl_m: ty::AssocItem, trait_m: ty::AssocItem, diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 3e33120901f..2e6b511412b 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -87,6 +87,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid + | sym::breakpoint | sym::size_of | sym::min_align_of | sym::needs_drop @@ -641,7 +642,9 @@ pub fn check_intrinsic_type( | sym::simd_round | sym::simd_trunc => (1, 0, vec![param(0)], param(0)), sym::simd_fpowi => (1, 0, vec![param(0), tcx.types.i32], param(0)), - sym::simd_fma => (1, 0, vec![param(0), param(0), param(0)], param(0)), + sym::simd_fma | sym::simd_relaxed_fma => { + (1, 0, vec![param(0), param(0), param(0)], param(0)) + } sym::simd_gather => (3, 0, vec![param(0), param(1), param(2)], param(0)), sym::simd_masked_load => (3, 0, vec![param(0), param(1), param(2)], param(2)), sym::simd_masked_store => (3, 0, vec![param(0), param(1), param(2)], tcx.types.unit), diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index b96469f503c..df4da03f0f5 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -178,7 +178,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { // Check that the type implements Copy. The only case where this can // possibly fail is for SIMD types which don't #[derive(Copy)]. - if !ty.is_copy_modulo_regions(self.tcx, self.typing_env) { + if !self.tcx.type_is_copy_modulo_regions(self.typing_env, ty) { let msg = "arguments for inline assembly must be copyable"; self.tcx .dcx() diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 375cbfd1c4f..61e203a1ff6 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -108,7 +108,7 @@ pub fn provide(providers: &mut Providers) { adt_async_destructor, region_scope_tree, collect_return_position_impl_trait_in_trait_tys, - compare_impl_const: compare_impl_item::compare_impl_const_raw, + compare_impl_item: compare_impl_item::compare_impl_item, check_coroutine_obligations: check::check_coroutine_obligations, ..*providers }; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 9a70555fede..c9773972d9a 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -13,6 +13,7 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_macros::LintDiagnostic; +use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::query::Providers; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::trait_def::TraitSpecializationKind; @@ -116,13 +117,12 @@ where } f(&mut wfcx)?; - let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?; - let errors = wfcx.select_all_or_error(); if !errors.is_empty() { return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } + let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?; debug!(?assumed_wf_types); let infcx_compat = infcx.fork(); @@ -1104,6 +1104,25 @@ fn check_type_defn<'tcx>( for variant in variants.iter() { // All field types must be well-formed. for field in &variant.fields { + if let Some(def_id) = field.value + && let Some(_ty) = tcx.type_of(def_id).no_bound_vars() + { + // FIXME(generic_const_exprs, default_field_values): this is a hack and needs to + // be refactored to check the instantiate-ability of the code better. + if let Some(def_id) = def_id.as_local() + && let hir::Node::AnonConst(anon) = tcx.hir_node_by_def_id(def_id) + && let expr = &tcx.hir().body(anon.body).value + && let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let Res::Def(DefKind::ConstParam, _def_id) = path.res + { + // Do not evaluate bare `const` params, as those would ICE and are only + // usable if `#![feature(generic_const_exprs)]` is enabled. + } else { + // Evaluate the constant proactively, to emit an error if the constant has + // an unconditional error. We only do so if the const has no type params. + let _ = tcx.const_eval_poly(def_id.into()); + } + } let field_id = field.did.expect_local(); let hir::FieldDef { ty: hir_ty, .. } = tcx.hir_node_by_def_id(field_id).expect_field(); @@ -1170,19 +1189,13 @@ fn check_type_defn<'tcx>( // Explicit `enum` discriminant values must const-evaluate successfully. if let ty::VariantDiscr::Explicit(discr_def_id) = variant.discr { - let cause = traits::ObligationCause::new( - tcx.def_span(discr_def_id), - wfcx.body_def_id, - ObligationCauseCode::Misc, - ); - wfcx.register_obligation(Obligation::new( - tcx, - cause, - wfcx.param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable( - ty::Const::from_anon_const(tcx, discr_def_id.expect_local()), - ))), - )); + match tcx.const_eval_poly(discr_def_id) { + Ok(_) => {} + Err(ErrorHandled::Reported(..)) => {} + Err(ErrorHandled::TooGeneric(sp)) => { + span_bug!(sp, "enum variant discr was too generic to eval") + } + } } } @@ -1921,8 +1934,8 @@ fn check_variances_for_type_defn<'tcx>( hir_generics .predicates .iter() - .filter_map(|predicate| match predicate { - hir::WherePredicate::BoundPredicate(predicate) => { + .filter_map(|predicate| match predicate.kind { + hir::WherePredicateKind::BoundPredicate(predicate) => { match icx.lower_ty(predicate.bounded_ty).kind() { ty::Param(data) => Some(Parameter(data.index)), _ => None, @@ -2202,8 +2215,8 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { span = predicates .iter() // There seems to be no better way to find out which predicate we are in - .find(|pred| pred.span().contains(obligation_span)) - .map(|pred| pred.span()) + .find(|pred| pred.span.contains(obligation_span)) + .map(|pred| pred.span) .unwrap_or(obligation_span); } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 93be3e06e5d..8f9b0626031 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -34,6 +34,7 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::hir::nested_filter; use rustc_middle::query::Providers; +use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, TypingMode}; use rustc_middle::{bug, span_bug}; @@ -150,11 +151,11 @@ impl<'v> Visitor<'v> for HirPlaceholderCollector { _ => {} } } - fn visit_array_length(&mut self, length: &'v hir::ArrayLen<'v>) { - if let hir::ArrayLen::Infer(inf) = length { - self.0.push(inf.span); + fn visit_const_arg(&mut self, const_arg: &'v hir::ConstArg<'v>) { + if let hir::ConstArgKind::Infer(span) = const_arg.kind { + self.0.push(span); } - intravisit::walk_array_len(self, length) + intravisit::walk_const_arg(self, const_arg) } } @@ -308,10 +309,10 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { self.tcx.ensure().type_of(param.def_id); if let Some(default) = default { // need to store default and type of default + self.tcx.ensure().const_param_default(param.def_id); if let hir::ConstArgKind::Anon(ac) = default.kind { self.tcx.ensure().type_of(ac.def_id); } - self.tcx.ensure().const_param_default(param.def_id); } } } @@ -1020,12 +1021,12 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> { } } -fn lower_variant( - tcx: TyCtxt<'_>, +fn lower_variant<'tcx>( + tcx: TyCtxt<'tcx>, variant_did: Option<LocalDefId>, ident: Ident, discr: ty::VariantDiscr, - def: &hir::VariantData<'_>, + def: &hir::VariantData<'tcx>, adt_kind: ty::AdtKind, parent_did: LocalDefId, ) -> ty::VariantDef { @@ -1041,6 +1042,7 @@ fn lower_variant( name: f.ident.name, vis: tcx.visibility(f.def_id), safety: f.safety, + value: f.default.map(|v| v.def_id.to_def_id()), }) .collect(); let recovered = match def { @@ -1415,7 +1417,7 @@ fn infer_return_ty_for_fn_sig<'tcx>( GenericParamKind::Lifetime { .. } => true, _ => false, }); - let fn_sig = tcx.fold_regions(fn_sig, |r, _| match *r { + let fn_sig = fold_regions(tcx, fn_sig, |r, _| match *r { ty::ReErased => { if has_region_params { ty::Region::new_error_with_message( @@ -1816,7 +1818,6 @@ fn const_param_default<'tcx>( ), }; let icx = ItemCtxt::new(tcx, def_id); - // FIXME(const_generics): investigate which places do and don't need const ty feeding - let ct = icx.lowerer().lower_const_arg(default_ct, FeedConstTy::No); + let ct = icx.lowerer().lower_const_arg(default_ct, FeedConstTy::Param(def_id.to_def_id())); ty::EarlyBinder::bind(ct) } diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index 8648a7d1e32..f1022d95753 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -1,6 +1,6 @@ use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_hir::intravisit; -use rustc_middle::hir::nested_filter::OnlyBodies; +use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::sym; @@ -42,7 +42,8 @@ pub(crate) fn predicates_and_item_bounds(tcx: TyCtxt<'_>) { } pub(crate) fn def_parents(tcx: TyCtxt<'_>) { - for did in tcx.hir().body_owners() { + for iid in tcx.hir().items() { + let did = iid.owner_id.def_id; if tcx.has_attr(did, sym::rustc_dump_def_parents) { struct AnonConstFinder<'tcx> { tcx: TyCtxt<'tcx>, @@ -50,7 +51,7 @@ pub(crate) fn def_parents(tcx: TyCtxt<'_>) { } impl<'tcx> intravisit::Visitor<'tcx> for AnonConstFinder<'tcx> { - type NestedFilter = OnlyBodies; + type NestedFilter = nested_filter::All; fn nested_visit_map(&mut self) -> Self::Map { self.tcx.hir() @@ -62,11 +63,11 @@ pub(crate) fn def_parents(tcx: TyCtxt<'_>) { } } - // Look for any anon consts inside of this body owner as there is no way to apply + // Look for any anon consts inside of this item as there is no way to apply // the `rustc_dump_def_parents` attribute to the anon const so it would not be possible // to see what its def parent is. let mut anon_ct_finder = AnonConstFinder { tcx, anon_consts: vec![] }; - intravisit::walk_expr(&mut anon_ct_finder, tcx.hir().body_owned_by(did).value); + intravisit::walk_item(&mut anon_ct_finder, tcx.hir().item(iid)); for did in [did].into_iter().chain(anon_ct_finder.anon_consts) { let span = tcx.def_span(did); diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index c31bff28fd3..1d9114b0ef3 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -181,7 +181,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // expressions' count (i.e. `N` in `[x; N]`), and explicit // `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`), // as they shouldn't be able to cause query cycle errors. - Node::Expr(Expr { kind: ExprKind::Repeat(_, ArrayLen::Body(ct)), .. }) + Node::Expr(Expr { kind: ExprKind::Repeat(_, ct), .. }) if ct.anon_const_hir_id() == Some(hir_id) => { Some(parent_did) @@ -419,7 +419,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { if let Node::ConstBlock(_) = node { own_params.push(ty::GenericParamDef { index: next_index(), - name: Symbol::intern("<const_ty>"), + name: rustc_span::sym::const_ty_placeholder, def_id: def_id.to_def_id(), pure_wrt_drop: false, kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false }, diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index b5dee5bd021..ca2597e79fd 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -1,12 +1,13 @@ use std::assert_matches::assert_matches; -use hir::{HirId, Node}; +use hir::Node; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_middle::ty::{self, GenericPredicates, ImplTraitInTraitData, Ty, TyCtxt, Upcast}; +use rustc_middle::ty::{ + self, GenericPredicates, ImplTraitInTraitData, Ty, TyCtxt, TypeVisitable, TypeVisitor, Upcast, +}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span}; @@ -238,11 +239,11 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen trace!(?predicates); // Add inline `<T: Foo>` bounds and bounds in the where clause. for predicate in hir_generics.predicates { - match predicate { - hir::WherePredicate::BoundPredicate(bound_pred) => { + match predicate.kind { + hir::WherePredicateKind::BoundPredicate(bound_pred) => { let ty = icx.lowerer().lower_ty_maybe_return_type_notation(bound_pred.bounded_ty); - let bound_vars = tcx.late_bound_vars(bound_pred.hir_id); + let bound_vars = tcx.late_bound_vars(predicate.hir_id); // Keep the type around in a dummy predicate, in case of no bounds. // That way, `where Ty:` is not a complete noop (see #53696) and `Ty` // is still checked for WF. @@ -275,7 +276,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen predicates.extend(bounds.clauses()); } - hir::WherePredicate::RegionPredicate(region_pred) => { + hir::WherePredicateKind::RegionPredicate(region_pred) => { let r1 = icx .lowerer() .lower_lifetime(region_pred.lifetime, RegionInferReason::RegionPredicate); @@ -298,14 +299,14 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen })) } - hir::WherePredicate::EqPredicate(..) => { + hir::WherePredicateKind::EqPredicate(..) => { // FIXME(#20041) } } } if tcx.features().generic_const_exprs() { - predicates.extend(const_evaluatable_predicates_of(tcx, def_id)); + predicates.extend(const_evaluatable_predicates_of(tcx, def_id, &predicates)); } let mut predicates: Vec<_> = predicates.into_iter().collect(); @@ -369,32 +370,48 @@ fn compute_bidirectional_outlives_predicates<'tcx>( } } -fn const_evaluatable_predicates_of( - tcx: TyCtxt<'_>, +#[instrument(level = "debug", skip(tcx, predicates), ret)] +fn const_evaluatable_predicates_of<'tcx>( + tcx: TyCtxt<'tcx>, def_id: LocalDefId, -) -> FxIndexSet<(ty::Clause<'_>, Span)> { + predicates: &FxIndexSet<(ty::Clause<'tcx>, Span)>, +) -> FxIndexSet<(ty::Clause<'tcx>, Span)> { struct ConstCollector<'tcx> { tcx: TyCtxt<'tcx>, preds: FxIndexSet<(ty::Clause<'tcx>, Span)>, } - impl<'tcx> intravisit::Visitor<'tcx> for ConstCollector<'tcx> { - fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) { - let ct = ty::Const::from_anon_const(self.tcx, c.def_id); - if let ty::ConstKind::Unevaluated(_) = ct.kind() { - let span = self.tcx.def_span(c.def_id); - self.preds.insert((ty::ClauseKind::ConstEvaluatable(ct).upcast(self.tcx), span)); - } - } + fn is_const_param_default(tcx: TyCtxt<'_>, def: LocalDefId) -> bool { + let hir_id = tcx.local_def_id_to_hir_id(def); + let (_, parent_node) = tcx + .hir() + .parent_iter(hir_id) + .skip_while(|(_, n)| matches!(n, Node::ConstArg(..))) + .next() + .unwrap(); + matches!( + parent_node, + Node::GenericParam(hir::GenericParam { kind: hir::GenericParamKind::Const { .. }, .. }) + ) + } - fn visit_const_param_default(&mut self, _param: HirId, _ct: &'tcx hir::ConstArg<'tcx>) { - // Do not look into const param defaults, - // these get checked when they are actually instantiated. - // - // We do not want the following to error: - // - // struct Foo<const N: usize, const M: usize = { N + 1 }>; - // struct Bar<const N: usize>(Foo<N, 3>); + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ConstCollector<'tcx> { + fn visit_const(&mut self, c: ty::Const<'tcx>) { + if let ty::ConstKind::Unevaluated(uv) = c.kind() { + if is_const_param_default(self.tcx, uv.def.expect_local()) { + // Do not look into const param defaults, + // these get checked when they are actually instantiated. + // + // We do not want the following to error: + // + // struct Foo<const N: usize, const M: usize = { N + 1 }>; + // struct Bar<const N: usize>(Foo<N, 3>); + return; + } + + let span = self.tcx.def_span(uv.def); + self.preds.insert((ty::ClauseKind::ConstEvaluatable(c).upcast(self.tcx), span)); + } } } @@ -402,28 +419,31 @@ fn const_evaluatable_predicates_of( let node = tcx.hir_node(hir_id); let mut collector = ConstCollector { tcx, preds: FxIndexSet::default() }; + + for (clause, _sp) in predicates { + clause.visit_with(&mut collector); + } + if let hir::Node::Item(item) = node - && let hir::ItemKind::Impl(impl_) = item.kind + && let hir::ItemKind::Impl(_) = item.kind { - if let Some(of_trait) = &impl_.of_trait { - debug!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id); - collector.visit_trait_ref(of_trait); + if let Some(of_trait) = tcx.impl_trait_ref(def_id) { + debug!("visit impl trait_ref"); + of_trait.instantiate_identity().visit_with(&mut collector); } - debug!("const_evaluatable_predicates_of({:?}): visit_self_ty", def_id); - collector.visit_ty(impl_.self_ty); - } - - if let Some(generics) = node.generics() { - debug!("const_evaluatable_predicates_of({:?}): visit_generics", def_id); - collector.visit_generics(generics); + debug!("visit self_ty"); + let self_ty = tcx.type_of(def_id); + self_ty.instantiate_identity().visit_with(&mut collector); } - if let Some(fn_sig) = tcx.hir().fn_sig_by_hir_id(hir_id) { - debug!("const_evaluatable_predicates_of({:?}): visit_fn_decl", def_id); - collector.visit_fn_decl(fn_sig.decl); + if let Some(_) = tcx.hir().fn_sig_by_hir_id(hir_id) { + debug!("visit fn sig"); + let fn_sig = tcx.fn_sig(def_id); + let fn_sig = fn_sig.instantiate_identity(); + debug!(?fn_sig); + fn_sig.visit_with(&mut collector); } - debug!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.preds); collector.preds } @@ -881,7 +901,8 @@ impl<'tcx> ItemCtxt<'tcx> { let mut bounds = Bounds::default(); for predicate in hir_generics.predicates { - let hir::WherePredicate::BoundPredicate(predicate) = predicate else { + let hir_id = predicate.hir_id; + let hir::WherePredicateKind::BoundPredicate(predicate) = predicate.kind else { continue; }; @@ -901,7 +922,7 @@ impl<'tcx> ItemCtxt<'tcx> { let bound_ty = self.lowerer().lower_ty_maybe_return_type_notation(predicate.bounded_ty); - let bound_vars = self.tcx.late_bound_vars(predicate.hir_id); + let bound_vars = self.tcx.late_bound_vars(hir_id); self.lowerer().lower_bounds( bound_ty, predicate.bounds, @@ -974,10 +995,10 @@ pub(super) fn const_conditions<'tcx>( let mut bounds = Bounds::default(); for pred in generics.predicates { - match pred { - hir::WherePredicate::BoundPredicate(bound_pred) => { + match pred.kind { + hir::WherePredicateKind::BoundPredicate(bound_pred) => { let ty = icx.lowerer().lower_ty_maybe_return_type_notation(bound_pred.bounded_ty); - let bound_vars = tcx.late_bound_vars(bound_pred.hir_id); + let bound_vars = tcx.late_bound_vars(pred.hir_id); icx.lowerer().lower_bounds( ty, bound_pred.bounds.iter(), diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index b4b3ef31f97..74f381d2661 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -936,9 +936,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } fn visit_where_predicate(&mut self, predicate: &'tcx hir::WherePredicate<'tcx>) { - match predicate { - &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - hir_id, + let hir_id = predicate.hir_id; + match predicate.kind { + &hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate { bounded_ty, bounds, bound_generic_params, @@ -979,7 +979,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { walk_list!(this, visit_param_bound, bounds); }) } - &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { + &hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate { lifetime, bounds, .. @@ -987,7 +987,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { self.visit_lifetime(lifetime); walk_list!(self, visit_param_bound, bounds); } - &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { + &hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate { + lhs_ty, rhs_ty, .. + }) => { self.visit_ty(lhs_ty); self.visit_ty(rhs_ty); } @@ -2058,47 +2060,37 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { }; match path.res { Res::Def(DefKind::TyParam, _) | Res::SelfTyParam { trait_: _ } => { - // Get the generics of this type's hir owner. This is *different* - // from the generics of the parameter's definition, since we want - // to be able to resolve an RTN path on a nested body (e.g. method - // inside an impl) using the where clauses on the method. - // FIXME(return_type_notation): Think of some better way of doing this. - let Some(generics) = self.tcx.hir_owner_node(hir_id.owner).generics() - else { - return; - }; - - // Look for the first bound that contains an associated type that - // matches the segment that we're looking for. We ignore any subsequent - // bounds since we'll be emitting a hard error in HIR lowering, so this - // is purely speculative. - let one_bound = generics.predicates.iter().find_map(|predicate| { - let hir::WherePredicate::BoundPredicate(predicate) = predicate else { - return None; - }; - let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) = - predicate.bounded_ty.kind - else { - return None; - }; - if bounded_path.res != path.res { - return None; - } - predicate.bounds.iter().find_map(|bound| { - let hir::GenericBound::Trait(trait_) = bound else { - return None; - }; + let mut bounds = + self.for_each_trait_bound_on_res(path.res).filter_map(|trait_def_id| { BoundVarContext::supertrait_hrtb_vars( self.tcx, - trait_.trait_ref.trait_def_id()?, + trait_def_id, item_segment.ident, ty::AssocKind::Fn, ) - }) - }); - let Some((bound_vars, assoc_item)) = one_bound else { + }); + + let Some((bound_vars, assoc_item)) = bounds.next() else { + // This will error in HIR lowering. + self.tcx + .dcx() + .span_delayed_bug(path.span, "no resolution for RTN path"); return; }; + + // Don't bail if we have identical bounds, which may be collected from + // something like `T: Bound + Bound`, or via elaborating supertraits. + for (second_vars, second_assoc_item) in bounds { + if second_vars != bound_vars || second_assoc_item != assoc_item { + // This will error in HIR lowering. + self.tcx.dcx().span_delayed_bug( + path.span, + "ambiguous resolution for RTN path", + ); + return; + } + } + (bound_vars, assoc_item.def_id, item_segment) } // If we have a self type alias (in an impl), try to resolve an @@ -2164,6 +2156,92 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { existing_bound_vars.extend(bound_vars); self.record_late_bound_vars(item_segment.hir_id, existing_bound_vars_saved); } + + /// Walk the generics of the item for a trait bound whose self type + /// corresponds to the expected res, and return the trait def id. + fn for_each_trait_bound_on_res( + &self, + expected_res: Res, + ) -> impl Iterator<Item = DefId> + use<'tcx, '_> { + std::iter::from_coroutine( + #[coroutine] + move || { + let mut scope = self.scope; + loop { + let hir_id = match *scope { + Scope::Binder { hir_id, .. } => Some(hir_id), + Scope::Root { opt_parent_item: Some(parent_def_id) } => { + Some(self.tcx.local_def_id_to_hir_id(parent_def_id)) + } + Scope::Body { .. } + | Scope::ObjectLifetimeDefault { .. } + | Scope::Supertrait { .. } + | Scope::TraitRefBoundary { .. } + | Scope::LateBoundary { .. } + | Scope::Opaque { .. } + | Scope::Root { opt_parent_item: None } => None, + }; + + if let Some(hir_id) = hir_id { + let node = self.tcx.hir_node(hir_id); + // If this is a `Self` bound in a trait, yield the trait itself. + // Specifically, we don't need to look at any supertraits since + // we already do that in `BoundVarContext::supertrait_hrtb_vars`. + if let Res::SelfTyParam { trait_: _ } = expected_res + && let hir::Node::Item(item) = node + && let hir::ItemKind::Trait(..) = item.kind + { + // Yield the trait's def id. Supertraits will be + // elaborated from that. + yield item.owner_id.def_id.to_def_id(); + } else if let Some(generics) = node.generics() { + for pred in generics.predicates { + let hir::WherePredicateKind::BoundPredicate(pred) = pred.kind + else { + continue; + }; + let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) = + pred.bounded_ty.kind + else { + continue; + }; + // Match the expected res. + if bounded_path.res != expected_res { + continue; + } + for pred in pred.bounds { + match pred { + hir::GenericBound::Trait(poly_trait_ref) => { + if let Some(def_id) = + poly_trait_ref.trait_ref.trait_def_id() + { + yield def_id; + } + } + hir::GenericBound::Outlives(_) + | hir::GenericBound::Use(_, _) => {} + } + } + } + } + } + + match *scope { + Scope::Binder { s, .. } + | Scope::Body { s, .. } + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s } + | Scope::LateBoundary { s, .. } + | Scope::Opaque { s, .. } => { + scope = s; + } + Scope::Root { .. } => break, + } + } + }, + ) + } } /// Detects late-bound lifetimes and inserts them into diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 816761fd00f..5595504c3d9 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -5,13 +5,13 @@ use rustc_hir as hir; use rustc_hir::HirId; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::query::plumbing::CyclePlaceholder; +use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, Article, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span}; -use tracing::debug; use super::{ItemCtxt, bad_placeholder}; use crate::errors::TypeofReservedKeywordUsed; @@ -113,7 +113,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { // so no need for ConstArg. Node::Ty(&hir::Ty { kind: TyKind::Typeof(ref e), span, .. }) if e.hir_id == hir_id => { let ty = tcx.typeck(def_id).node_type(tcx.local_def_id_to_hir_id(def_id)); - let ty = tcx.fold_regions(ty, |r, _| { + let ty = fold_regions(tcx, ty, |r, _| { if r.is_erased() { ty::Region::new_error_misc(tcx) } else { r } }); let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false, None) { @@ -125,6 +125,12 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { return ty; } + Node::Field(&hir::FieldDef { default: Some(c), def_id: field_def_id, .. }) + if c.hir_id == hir_id => + { + tcx.type_of(field_def_id).instantiate_identity() + } + _ => Ty::new_error_with_message( tcx, span, @@ -137,252 +143,26 @@ fn const_arg_anon_type_of<'tcx>(tcx: TyCtxt<'tcx>, arg_hir_id: HirId, span: Span use hir::*; use rustc_middle::ty::Ty; - let parent_node_id = tcx.parent_hir_id(arg_hir_id); - let parent_node = tcx.hir_node(parent_node_id); - - let (generics, arg_idx) = match parent_node { - // Easy case: arrays repeat expressions. + match tcx.parent_hir_node(arg_hir_id) { + // Array length const arguments do not have `type_of` fed as there is never a corresponding + // generic parameter definition. Node::Ty(&hir::Ty { kind: TyKind::Array(_, ref constant), .. }) | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) - if constant.hir_id() == arg_hir_id => + if constant.hir_id == arg_hir_id => { return tcx.types.usize; } - Node::GenericParam(&GenericParam { - def_id: param_def_id, - kind: GenericParamKind::Const { default: Some(ct), .. }, - .. - }) if ct.hir_id == arg_hir_id => { - return tcx - .type_of(param_def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"); - } - - // This match arm is for when the def_id appears in a GAT whose - // path can't be resolved without typechecking e.g. - // - // trait Foo { - // type Assoc<const N: usize>; - // fn foo() -> Self::Assoc<3>; - // } - // - // In the above code we would call this query with the def_id of 3 and - // the parent_node we match on would be the hir node for Self::Assoc<3> - // - // `Self::Assoc<3>` cant be resolved without typechecking here as we - // didnt write <Self as Foo>::Assoc<3>. If we did then another match - // arm would handle this. - // - // I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU - Node::Ty(hir_ty @ hir::Ty { kind: TyKind::Path(QPath::TypeRelative(ty, segment)), .. }) => { - // Find the Item containing the associated type so we can create an ItemCtxt. - // Using the ItemCtxt lower the HIR for the unresolved assoc type into a - // ty which is a fully resolved projection. - // For the code example above, this would mean lowering `Self::Assoc<3>` - // to a ty::Alias(ty::Projection, `<Self as Foo>::Assoc<3>`). - let item_def_id = tcx.hir().get_parent_item(ty.hir_id).def_id; - let ty = ItemCtxt::new(tcx, item_def_id).lower_ty(hir_ty); - - // Iterate through the generics of the projection to find the one that corresponds to - // the def_id that this query was called with. We filter to only type and const args here - // as a precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't - // but it can't hurt to be safe ^^ - if let ty::Alias(ty::Projection | ty::Inherent, projection) = ty.kind() { - let generics = tcx.generics_of(projection.def_id); - - let arg_index = segment - .args - .and_then(|args| { - args.args - .iter() - .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.hir_id() == arg_hir_id) - }) - .unwrap_or_else(|| { - bug!("no arg matching AnonConst in segment"); - }); - - (generics, arg_index) - } else { - // I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU - return Ty::new_error_with_message( - tcx, - span, - "unexpected non-GAT usage of an anon const", - ); - } - } - Node::Expr(&Expr { - kind: - ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)), - .. - }) => { - let body_owner = tcx.hir().enclosing_body_owner(arg_hir_id); - let tables = tcx.typeck(body_owner); - // This may fail in case the method/path does not actually exist. - // As there is no relevant param for `def_id`, we simply return - // `None` here. - let Some(type_dependent_def) = tables.type_dependent_def_id(parent_node_id) else { - return Ty::new_error_with_message( - tcx, - span, - format!("unable to find type-dependent def for {parent_node_id:?}"), - ); - }; - let idx = segment - .args - .and_then(|args| { - args.args - .iter() - .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.hir_id() == arg_hir_id) - }) - .unwrap_or_else(|| { - bug!("no arg matching ConstArg in segment"); - }); - - (tcx.generics_of(type_dependent_def), idx) - } - - Node::Ty(&hir::Ty { kind: TyKind::Path(_), .. }) - | Node::Expr(&Expr { kind: ExprKind::Path(_) | ExprKind::Struct(..), .. }) - | Node::TraitRef(..) - | Node::Pat(_) => { - let path = match parent_node { - Node::Ty(&hir::Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. }) - | Node::TraitRef(&TraitRef { path, .. }) => &*path, - Node::Expr(&Expr { - kind: - ExprKind::Path(QPath::Resolved(_, path)) - | ExprKind::Struct(&QPath::Resolved(_, path), ..), - .. - }) => { - let body_owner = tcx.hir().enclosing_body_owner(arg_hir_id); - let _tables = tcx.typeck(body_owner); - &*path - } - Node::Pat(pat) => { - if let Some(path) = get_path_containing_arg_in_pat(pat, arg_hir_id) { - path - } else { - return Ty::new_error_with_message( - tcx, - span, - format!("unable to find const parent for {arg_hir_id} in pat {pat:?}"), - ); - } - } - _ => { - return Ty::new_error_with_message( - tcx, - span, - format!("unexpected const parent path {parent_node:?}"), - ); - } - }; - - // We've encountered an `AnonConst` in some path, so we need to - // figure out which generic parameter it corresponds to and return - // the relevant type. - let Some((arg_index, segment)) = path.segments.iter().find_map(|seg| { - let args = seg.args?; - args.args - .iter() - .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.hir_id() == arg_hir_id) - .map(|index| (index, seg)) - .or_else(|| { - args.constraints - .iter() - .copied() - .filter_map(AssocItemConstraint::ct) - .position(|ct| ct.hir_id == arg_hir_id) - .map(|idx| (idx, seg)) - }) - }) else { - return Ty::new_error_with_message(tcx, span, "no arg matching AnonConst in path"); - }; - - let generics = match tcx.res_generics_def_id(segment.res) { - Some(def_id) => tcx.generics_of(def_id), - None => { - return Ty::new_error_with_message( - tcx, - span, - format!("unexpected anon const res {:?} in path: {:?}", segment.res, path), - ); - } - }; - - (generics, arg_index) - } - _ => { - return Ty::new_error_with_message( - tcx, - span, - format!("unexpected const arg parent in type_of(): {parent_node:?}"), - ); - } - }; - - debug!(?parent_node); - debug!(?generics, ?arg_idx); - if let Some(param_def_id) = generics - .own_params - .iter() - .filter(|param| param.kind.is_ty_or_const()) - .nth(match generics.has_self && generics.parent.is_none() { - true => arg_idx + 1, - false => arg_idx, - }) - .and_then(|param| match param.kind { - ty::GenericParamDefKind::Const { .. } => { - debug!(?param); - Some(param.def_id) - } - _ => None, - }) - { - tcx.type_of(param_def_id).no_bound_vars().expect("const parameter types cannot be generic") - } else { - return Ty::new_error_with_message( + // This is not a `bug!` as const arguments in path segments that did not resolve to anything + // will result in `type_of` never being fed. + _ => Ty::new_error_with_message( tcx, span, - format!("const generic parameter not found in {generics:?} at position {arg_idx:?}"), - ); + "`type_of` called on const argument's anon const before the const argument was lowered", + ), } } -fn get_path_containing_arg_in_pat<'hir>( - pat: &'hir hir::Pat<'hir>, - arg_id: HirId, -) -> Option<&'hir hir::Path<'hir>> { - use hir::*; - - let is_arg_in_path = |p: &hir::Path<'_>| { - p.segments - .iter() - .filter_map(|seg| seg.args) - .flat_map(|args| args.args) - .any(|arg| arg.hir_id() == arg_id) - }; - let mut arg_path = None; - pat.walk(|pat| match pat.kind { - PatKind::Struct(QPath::Resolved(_, path), _, _) - | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) - | PatKind::Path(QPath::Resolved(_, path)) - if is_arg_in_path(path) => - { - arg_path = Some(path); - false - } - _ => true, - }); - arg_path -} - pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, Ty<'_>> { use rustc_hir::*; use rustc_middle::ty::Ty; @@ -578,8 +358,6 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ x => bug!("unexpected non-type Node::GenericParam: {:?}", x), }, - Node::ArrayLenInfer(_) => tcx.types.usize, - x => { bug!("unexpected sort of node in type_of(): {:?}", x); } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index fcea0052546..66255829dcf 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -226,13 +226,18 @@ impl TaitConstraintLocator<'_> { constrained = true; if !opaque_types_defined_by.contains(&self.def_id) { - self.tcx.dcx().emit_err(TaitForwardCompat { + let guar = self.tcx.dcx().emit_err(TaitForwardCompat { span: hidden_type.span, item_span: self .tcx .def_ident_span(item_def_id) .unwrap_or_else(|| self.tcx.def_span(item_def_id)), }); + // Avoid "opaque type not constrained" errors on the opaque itself. + self.found = Some(ty::OpaqueHiddenType { + span: DUMMY_SP, + ty: Ty::new_error(self.tcx, guar), + }); } let concrete_type = self.tcx.erase_regions(hidden_type.remap_generic_params_to_declaration_params( @@ -248,7 +253,7 @@ impl TaitConstraintLocator<'_> { if !constrained { debug!("no constraints in typeck results"); if opaque_types_defined_by.contains(&self.def_id) { - self.tcx.dcx().emit_err(TaitForwardCompat2 { + let guar = self.tcx.dcx().emit_err(TaitForwardCompat2 { span: self .tcx .def_ident_span(item_def_id) @@ -256,6 +261,11 @@ impl TaitConstraintLocator<'_> { opaque_type_span: self.tcx.def_span(self.def_id), opaque_type: self.tcx.def_path_str(self.def_id), }); + // Avoid "opaque type not constrained" errors on the opaque itself. + self.found = Some(ty::OpaqueHiddenType { + span: DUMMY_SP, + ty: Ty::new_error(self.tcx, guar), + }); } return; }; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 0b2e9ed6052..4142dcff226 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -672,11 +672,10 @@ pub(crate) struct MainFunctionGenericParameters { #[derive(Diagnostic)] #[diag(hir_analysis_variadic_function_compatible_convention, code = E0045)] -pub(crate) struct VariadicFunctionCompatibleConvention<'a> { +pub(crate) struct VariadicFunctionCompatibleConvention { #[primary_span] #[label] pub span: Span, - pub conventions: &'a str, } #[derive(Diagnostic)] @@ -1081,13 +1080,6 @@ pub(crate) struct EmptySpecialization { } #[derive(Diagnostic)] -#[diag(hir_analysis_const_specialize)] -pub(crate) struct ConstSpecialize { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(hir_analysis_static_specialize)] pub(crate) struct StaticSpecialize { #[primary_span] diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 6ebe1cedcaf..0404e38a293 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -69,7 +69,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { search_bounds(hir_bounds); if let Some((self_ty, where_clause)) = self_ty_where_predicates { for clause in where_clause { - if let hir::WherePredicate::BoundPredicate(pred) = clause + if let hir::WherePredicateKind::BoundPredicate(pred) = clause.kind && pred.is_param_bound(self_ty.to_def_id()) { search_bounds(pred.bounds); @@ -703,7 +703,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// /// It might actually be possible that we can already support early-bound generic params /// in such types if we just lifted some more checks in other places, too, for example -/// inside [`ty::Const::from_anon_const`]. However, even if that were the case, we should +/// inside `HirTyLowerer::lower_anon_const`. However, even if that were the case, we should /// probably gate this behind another feature flag. /// /// [^1]: <https://github.com/rust-lang/project-const-generics/issues/28>. diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index 5e27ace4cbe..321a8aba7f7 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -8,7 +8,8 @@ use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; use rustc_middle::span_bug; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::{ - self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, Upcast, + self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, + TypeVisitableExt, Upcast, }; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; @@ -92,11 +93,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); + + // We don't support >1 principal if regular_traits.len() > 1 { - let _ = self.report_trait_object_addition_traits_error(®ular_traits); - } else if regular_traits.is_empty() && auto_traits.is_empty() { - let reported = self.report_trait_object_with_no_traits_error(span, &trait_bounds); - return Ty::new_error(tcx, reported); + let guar = self.report_trait_object_addition_traits_error(®ular_traits); + return Ty::new_error(tcx, guar); + } + // We don't support empty trait objects. + if regular_traits.is_empty() && auto_traits.is_empty() { + let guar = self.report_trait_object_with_no_traits_error(span, &trait_bounds); + return Ty::new_error(tcx, guar); + } + // Don't create a dyn trait if we have errors in the principal. + if let Err(guar) = trait_bounds.error_reported() { + return Ty::new_error(tcx, guar); } // Check that there are no gross dyn-compatibility violations; @@ -126,7 +136,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { for (base_trait_ref, original_span) in regular_traits_refs_spans { let base_pred: ty::Predicate<'tcx> = base_trait_ref.upcast(tcx); - for ClauseWithSupertraitSpan { pred, original_span, supertrait_span } in + for ClauseWithSupertraitSpan { pred, supertrait_span } in traits::elaborate(tcx, [ClauseWithSupertraitSpan::new(base_pred, original_span)]) .filter_only_self() { @@ -194,7 +204,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { for def_ids in associated_types.values_mut() { for (projection_bound, span) in &projection_bounds { let def_id = projection_bound.projection_def_id(); - // FIXME(#120456) - is `swap_remove` correct? def_ids.swap_remove(&def_id); if tcx.generics_require_sized_self(def_id) { tcx.emit_node_span_lint( @@ -210,11 +219,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { def_ids.retain(|def_id| !tcx.generics_require_sized_self(def_id)); } - self.complain_about_missing_assoc_tys( + if let Err(guar) = self.check_for_required_assoc_tys( associated_types, potential_assoc_types, hir_trait_bounds, - ); + ) { + return Ty::new_error(tcx, guar); + } // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as // `dyn Trait + Send`. 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 4e5a8271e9e..2e227ead14a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -279,7 +279,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } else { let mut err = self.dcx().create_err(err); if suggest_constraining_type_param( - tcx, generics, &mut err, &qself_str, &trait_ref, None, None, + tcx, + generics, + &mut err, + &qself_str, + &trait_ref, + Some(best_trait), + None, ) && !identically_named { // We suggested constraining a type parameter, but the associated item on it @@ -714,20 +720,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// reasonable suggestion on how to write it. For the case of multiple associated types in the /// same trait bound have the same name (as they come from different supertraits), we instead /// emit a generic note suggesting using a `where` clause to constraint instead. - pub(crate) fn complain_about_missing_assoc_tys( + pub(crate) fn check_for_required_assoc_tys( &self, associated_types: FxIndexMap<Span, FxIndexSet<DefId>>, potential_assoc_types: Vec<usize>, trait_bounds: &[hir::PolyTraitRef<'_>], - ) { + ) -> Result<(), ErrorGuaranteed> { if associated_types.values().all(|v| v.is_empty()) { - return; + return Ok(()); } let tcx = self.tcx(); // FIXME: Marked `mut` so that we can replace the spans further below with a more // appropriate one, but this should be handled earlier in the span assignment. - let mut associated_types: FxIndexMap<Span, Vec<_>> = associated_types + let associated_types: FxIndexMap<Span, Vec<_>> = associated_types .into_iter() .map(|(span, def_ids)| { (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect()) @@ -739,7 +745,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and // `issue-22560.rs`. let mut trait_bound_spans: Vec<Span> = vec![]; - let mut dyn_compatibility_violations = false; + let mut dyn_compatibility_violations = Ok(()); for (span, items) in &associated_types { if !items.is_empty() { trait_bound_spans.push(*span); @@ -752,13 +758,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let violations = dyn_compatibility_violations_for_assoc_item(tcx, trait_def_id, *assoc_item); if !violations.is_empty() { - report_dyn_incompatibility(tcx, *span, None, trait_def_id, &violations).emit(); - dyn_compatibility_violations = true; + dyn_compatibility_violations = Err(report_dyn_incompatibility( + tcx, + *span, + None, + trait_def_id, + &violations, + ) + .emit()); } } } - if dyn_compatibility_violations { - return; + + if let Err(guar) = dyn_compatibility_violations { + return Err(guar); } // related to issue #91997, turbofishes added only when in an expr or pat @@ -769,39 +782,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::Node::Expr(_) | hir::Node::Pat(_) => true, _ => false, }; - match bound.trait_ref.path.segments { - // FIXME: `trait_ref.path.span` can point to a full path with multiple - // segments, even though `trait_ref.path.segments` is of length `1`. Work - // around that bug here, even though it should be fixed elsewhere. - // This would otherwise cause an invalid suggestion. For an example, look at - // `tests/ui/issues/issue-28344.rs` where instead of the following: - // - // error[E0191]: the value of the associated type `Output` - // (from trait `std::ops::BitXor`) must be specified - // --> $DIR/issue-28344.rs:4:17 - // | - // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); - // | ^^^^^^ help: specify the associated type: - // | `BitXor<Output = Type>` - // - // we would output: - // - // error[E0191]: the value of the associated type `Output` - // (from trait `std::ops::BitXor`) must be specified - // --> $DIR/issue-28344.rs:4:17 - // | - // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); - // | ^^^^^^^^^^^^^ help: specify the associated type: - // | `BitXor::bitor<Output = Type>` - [segment] if segment.args.is_none() => { - trait_bound_spans = vec![segment.ident.span]; - associated_types = associated_types - .into_values() - .map(|items| (segment.ident.span, items)) - .collect(); - } - _ => {} - } } // We get all the associated items that _are_ set, @@ -998,7 +978,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - err.emit(); + Err(err.emit()) } /// On ambiguous associated type, look for an associated function whose name matches the diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index ae1279d428c..c3f4fc8699a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -104,7 +104,7 @@ fn generic_arg_mismatch_err( GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), GenericParamDefKind::Const { .. }, ) if tcx.type_of(param.def_id).skip_binder() == tcx.types.usize => { - let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id())); + let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id)); if let Ok(snippet) = snippet { err.span_suggestion( arg.span(), diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 01276abec22..3313339abb3 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -29,14 +29,14 @@ use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err, }; -use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{GenericArg, GenericArgs, HirId}; +use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::print::PrintPolyTraitRefExt as _; use rustc_middle::ty::{ self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt, @@ -998,6 +998,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.lower_const_arg(ct, FeedConstTy::No).into() } }; + if term.references_error() { + continue; + } // FIXME(#97583): This isn't syntactically well-formed! where_bounds.push(format!( " T: {trait}::{assoc_name} = {term}", @@ -1569,7 +1572,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { infcx.fresh_args_for_item(DUMMY_SP, impl_def_id), ); - let value = tcx.fold_regions(qself_ty, |_, _| tcx.lifetimes.re_erased); + let value = fold_regions(tcx, qself_ty, |_, _| tcx.lifetimes.re_erased); // FIXME: Don't bother dealing with non-lifetime binders here... if value.has_escaping_bound_vars() { return false; @@ -2088,7 +2091,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { qpath.span(), format!("Const::lower_const_arg: invalid qpath {qpath:?}"), ), - hir::ConstArgKind::Anon(anon) => Const::from_anon_const(tcx, anon.def_id), + hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon), + hir::ConstArgKind::Infer(span) => self.ct_infer(None, span), } } @@ -2175,6 +2179,87 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + /// Literals and const generic parameters are eagerly converted to a constant, everything else + /// becomes `Unevaluated`. + #[instrument(skip(self), level = "debug")] + fn lower_anon_const(&self, anon: &AnonConst) -> Const<'tcx> { + let tcx = self.tcx(); + + let expr = &tcx.hir().body(anon.body).value; + debug!(?expr); + + let ty = tcx + .type_of(anon.def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"); + + match self.try_lower_anon_const_lit(ty, expr) { + Some(v) => v, + None => ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst { + def: anon.def_id.to_def_id(), + args: ty::GenericArgs::identity_for_item(tcx, anon.def_id.to_def_id()), + }), + } + } + + #[instrument(skip(self), level = "debug")] + fn try_lower_anon_const_lit( + &self, + ty: Ty<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Option<Const<'tcx>> { + let tcx = self.tcx(); + + // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments + // currently have to be wrapped in curly brackets, so it's necessary to special-case. + let expr = match &expr.kind { + hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => { + block.expr.as_ref().unwrap() + } + _ => expr, + }; + + if let hir::ExprKind::Path(hir::QPath::Resolved( + _, + &hir::Path { res: Res::Def(DefKind::ConstParam, _), .. }, + )) = expr.kind + { + span_bug!( + expr.span, + "try_lower_anon_const_lit: received const param which shouldn't be possible" + ); + }; + + let lit_input = match expr.kind { + hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), + hir::ExprKind::Unary(hir::UnOp::Neg, expr) => match expr.kind { + hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: true }), + _ => None, + }, + _ => None, + }; + + if let Some(lit_input) = lit_input { + // If an error occurred, ignore that it's a literal and leave reporting the error up to + // mir. + match tcx.at(expr.span).lit_to_const(lit_input) { + Ok(c) => return Some(c), + Err(_) if lit_input.ty.has_aliases() => { + // allow the `ty` to be an alias type, though we cannot handle it here + return None; + } + Err(e) => { + tcx.dcx().span_delayed_bug( + expr.span, + format!("try_lower_anon_const_lit: couldn't lit_to_const {e:?}"), + ); + } + } + } + + None + } + fn lower_delegation_ty(&self, idx: hir::InferDelegationKind) -> Ty<'tcx> { let delegation_sig = self.tcx().inherit_sig_for_delegation_item(self.item_def_id()); match idx { @@ -2309,13 +2394,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx.at(span).type_of(def_id).instantiate(tcx, args) } hir::TyKind::Array(ty, length) => { - let length = match length { - hir::ArrayLen::Infer(inf) => self.ct_infer(None, inf.span), - hir::ArrayLen::Body(constant) => { - self.lower_const_arg(constant, FeedConstTy::No) - } - }; - + let length = self.lower_const_arg(length, FeedConstTy::No); Ty::new_array_with_const_len(tcx, self.lower_ty(ty), length) } hir::TyKind::Typeof(e) => tcx.type_of(e.def_id).instantiate_identity(), diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index 5b0165bf993..c0fb94d2cb2 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -5,6 +5,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ObligationCause, WellFormedLoc}; use rustc_middle::bug; use rustc_middle::query::Providers; +use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{self, TyCtxt, TypingMode}; use rustc_span::def_id::LocalDefId; use rustc_trait_selection::traits::{self, ObligationCtxt}; @@ -75,7 +76,7 @@ fn diagnostic_hir_wf_check<'tcx>( // This visitor can walk into binders, resulting in the `tcx_ty` to // potentially reference escaping bound variables. We simply erase // those here. - let tcx_ty = self.tcx.fold_regions(tcx_ty, |r, _| { + let tcx_ty = fold_regions(self.tcx, tcx_ty, |r, _| { if r.is_bound() { self.tcx.lifetimes.re_erased } else { r } }); let cause = traits::ObligationCause::new( diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 246643d8074..ee55e1bc21a 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -66,7 +66,6 @@ //! on traits with methods can. use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::outlives::env::OutlivesEnvironment; @@ -134,7 +133,6 @@ fn check_always_applicable( unconstrained_parent_impl_args(tcx, impl2_def_id, impl2_args) }; - res = res.and(check_constness(tcx, impl1_def_id, impl2_node, span)); res = res.and(check_static_lifetimes(tcx, &parent_args, span)); res = res.and(check_duplicate_params(tcx, impl1_args, parent_args, span)); res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span)); @@ -157,30 +155,6 @@ fn check_has_items( Ok(()) } -/// Check that the specializing impl `impl1` is at least as const as the base -/// impl `impl2` -fn check_constness( - tcx: TyCtxt<'_>, - impl1_def_id: LocalDefId, - impl2_node: Node, - span: Span, -) -> Result<(), ErrorGuaranteed> { - if impl2_node.is_from_trait() { - // This isn't a specialization - return Ok(()); - } - - let impl1_constness = tcx.constness(impl1_def_id.to_def_id()); - let impl2_constness = tcx.constness(impl2_node.def_id()); - - if let hir::Constness::Const = impl2_constness { - if let hir::Constness::NotConst = impl1_constness { - return Err(tcx.dcx().emit_err(errors::ConstSpecialize { span })); - } - } - Ok(()) -} - /// Given a specializing impl `impl1`, and the base impl `impl2`, returns two /// generic parameters `(S1, S2)` that equate their trait references. /// The returned types are expressed in terms of the generics of `impl1`. diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 0a26b6776bb..87fd4de26a5 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -62,7 +62,9 @@ This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] +#![feature(coroutines)] #![feature(if_let_guard)] +#![feature(iter_from_coroutine)] #![feature(iter_intersperse)] #![feature(let_chains)] #![feature(never_type)] @@ -98,9 +100,7 @@ use rustc_middle::middle; use rustc_middle::mir::interpret::GlobalId; use rustc_middle::query::Providers; use rustc_middle::ty::{self, Const, Ty, TyCtxt}; -use rustc_session::parse::feature_err; use rustc_span::Span; -use rustc_span::symbol::sym; use rustc_trait_selection::traits; use self::hir_ty_lowering::{FeedConstTy, HirTyLowerer}; @@ -113,34 +113,9 @@ fn require_c_abi_if_c_variadic( abi: ExternAbi, span: Span, ) { - const CONVENTIONS_UNSTABLE: &str = - "`C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi`"; - const CONVENTIONS_STABLE: &str = "`C` or `cdecl`"; - const UNSTABLE_EXPLAIN: &str = - "using calling conventions other than `C` or `cdecl` for varargs functions is unstable"; - - if !decl.c_variadic || matches!(abi, ExternAbi::C { .. } | ExternAbi::Cdecl { .. }) { - return; + if decl.c_variadic && !abi.supports_varargs() { + tcx.dcx().emit_err(errors::VariadicFunctionCompatibleConvention { span }); } - - let extended_abi_support = tcx.features().extended_varargs_abi_support(); - let conventions = match (extended_abi_support, abi.supports_varargs()) { - // User enabled additional ABI support for varargs and function ABI matches those ones. - (true, true) => return, - - // Using this ABI would be ok, if the feature for additional ABI support was enabled. - // Return CONVENTIONS_STABLE, because we want the other error to look the same. - (false, true) => { - feature_err(&tcx.sess, sym::extended_varargs_abi_support, span, UNSTABLE_EXPLAIN) - .emit(); - CONVENTIONS_STABLE - } - - (false, false) => CONVENTIONS_STABLE, - (true, false) => CONVENTIONS_UNSTABLE, - }; - - tcx.dcx().emit_err(errors::VariadicFunctionCompatibleConvention { span, conventions }); } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 0a3aa8fec90..a74e36c45c6 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -10,7 +10,7 @@ use std::cell::Cell; use std::vec; use rustc_abi::ExternAbi; -use rustc_ast::util::parser::{self, AssocOp, Fixity}; +use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity}; use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::{self, Breaks}; use rustc_ast_pretty::pprust::{Comments, PrintState}; @@ -117,12 +117,7 @@ impl<'a> State<'a> { Node::Ctor(..) => panic!("cannot print isolated Ctor"), Node::LetStmt(a) => self.print_local_decl(a), Node::Crate(..) => panic!("cannot print Crate"), - Node::WhereBoundPredicate(pred) => { - self.print_formal_generic_params(pred.bound_generic_params); - self.print_type(pred.bounded_ty); - self.print_bounds(":", pred.bounds); - } - Node::ArrayLenInfer(_) => self.word("_"), + Node::WherePredicate(pred) => self.print_where_predicate(pred), Node::Synthetic => unreachable!(), Node::Err(_) => self.word("/*ERROR*/"), } @@ -319,7 +314,7 @@ impl<'a> State<'a> { self.word("["); self.print_type(ty); self.word("; "); - self.print_array_length(length); + self.print_const_arg(length); self.word("]"); } hir::TyKind::Typeof(ref e) => { @@ -990,13 +985,6 @@ impl<'a> State<'a> { self.print_else(elseopt) } - fn print_array_length(&mut self, len: &hir::ArrayLen<'_>) { - match len { - hir::ArrayLen::Infer(..) => self.word("_"), - hir::ArrayLen::Body(ct) => self.print_const_arg(ct), - } - } - fn print_anon_const(&mut self, constant: &hir::AnonConst) { self.ann.nested(self, Nested::Body(constant.body)) } @@ -1005,6 +993,7 @@ impl<'a> State<'a> { match &const_arg.kind { ConstArgKind::Path(qpath) => self.print_qpath(qpath, true), ConstArgKind::Anon(anon) => self.print_anon_const(anon), + ConstArgKind::Infer(..) => self.word("_"), } } @@ -1014,10 +1003,6 @@ impl<'a> State<'a> { self.pclose() } - fn print_expr_maybe_paren(&mut self, expr: &hir::Expr<'_>, prec: i8) { - self.print_expr_cond_paren(expr, expr.precedence().order() < prec) - } - /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in /// `if cond { ... }`. fn print_expr_as_cond(&mut self, expr: &hir::Expr<'_>) { @@ -1049,7 +1034,7 @@ impl<'a> State<'a> { } self.space(); self.word_space("="); - let npals = || parser::needs_par_as_let_scrutinee(init.precedence().order()); + let npals = || parser::needs_par_as_let_scrutinee(init.precedence()); self.print_expr_cond_paren(init, Self::cond_needs_par(init) || npals()) } @@ -1081,12 +1066,12 @@ impl<'a> State<'a> { self.end() } - fn print_expr_repeat(&mut self, element: &hir::Expr<'_>, count: &hir::ArrayLen<'_>) { + fn print_expr_repeat(&mut self, element: &hir::Expr<'_>, count: &hir::ConstArg<'_>) { self.ibox(INDENT_UNIT); self.word("["); self.print_expr(element); self.word_space(";"); - self.print_array_length(count); + self.print_const_arg(count); self.word("]"); self.end() } @@ -1095,22 +1080,36 @@ impl<'a> State<'a> { &mut self, qpath: &hir::QPath<'_>, fields: &[hir::ExprField<'_>], - wth: Option<&hir::Expr<'_>>, + wth: hir::StructTailExpr<'_>, ) { self.print_qpath(qpath, true); self.word("{"); self.commasep_cmnt(Consistent, fields, |s, field| s.print_expr_field(field), |f| f.span); - if let Some(expr) = wth { - self.ibox(INDENT_UNIT); - if !fields.is_empty() { - self.word(","); - self.space(); + match wth { + hir::StructTailExpr::Base(expr) => { + self.ibox(INDENT_UNIT); + if !fields.is_empty() { + self.word(","); + self.space(); + } + self.word(".."); + self.print_expr(expr); + self.end(); + } + hir::StructTailExpr::DefaultFields(_) => { + self.ibox(INDENT_UNIT); + if !fields.is_empty() { + self.word(","); + self.space(); + } + self.word(".."); + self.end(); + } + hir::StructTailExpr::None => { + if !fields.is_empty() { + self.word(","); + } } - self.word(".."); - self.print_expr(expr); - self.end(); - } else if !fields.is_empty() { - self.word(","); } self.word("}"); @@ -1140,12 +1139,12 @@ impl<'a> State<'a> { } fn print_expr_call(&mut self, func: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let prec = match func.kind { - hir::ExprKind::Field(..) => parser::PREC_FORCE_PAREN, - _ => parser::PREC_UNAMBIGUOUS, + let needs_paren = match func.kind { + hir::ExprKind::Field(..) => true, + _ => func.precedence() < ExprPrecedence::Unambiguous, }; - self.print_expr_maybe_paren(func, prec); + self.print_expr_cond_paren(func, needs_paren); self.print_call_post(args) } @@ -1156,7 +1155,7 @@ impl<'a> State<'a> { args: &[hir::Expr<'_>], ) { let base_args = args; - self.print_expr_maybe_paren(receiver, parser::PREC_UNAMBIGUOUS); + self.print_expr_cond_paren(receiver, receiver.precedence() < ExprPrecedence::Unambiguous); self.word("."); self.print_ident(segment.ident); @@ -1170,37 +1169,38 @@ impl<'a> State<'a> { fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { let assoc_op = AssocOp::from_ast_binop(op.node); - let prec = assoc_op.precedence() as i8; - let fixity = assoc_op.fixity(); - - let (left_prec, right_prec) = match fixity { - Fixity::Left => (prec, prec + 1), - Fixity::Right => (prec + 1, prec), - Fixity::None => (prec + 1, prec + 1), + let binop_prec = assoc_op.precedence(); + let left_prec = lhs.precedence(); + let right_prec = rhs.precedence(); + + let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() { + Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec), + Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec), + Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec), }; - let left_prec = match (&lhs.kind, op.node) { + match (&lhs.kind, op.node) { // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead // of `(x as i32) < ...`. We need to convince it _not_ to do that. (&hir::ExprKind::Cast { .. }, hir::BinOpKind::Lt | hir::BinOpKind::Shl) => { - parser::PREC_FORCE_PAREN + left_needs_paren = true; } - (&hir::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => { - parser::PREC_FORCE_PAREN + (&hir::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(binop_prec) => { + left_needs_paren = true; } - _ => left_prec, - }; + _ => {} + } - self.print_expr_maybe_paren(lhs, left_prec); + self.print_expr_cond_paren(lhs, left_needs_paren); self.space(); self.word_space(op.node.as_str()); - self.print_expr_maybe_paren(rhs, right_prec) + self.print_expr_cond_paren(rhs, right_needs_paren); } fn print_expr_unary(&mut self, op: hir::UnOp, expr: &hir::Expr<'_>) { self.word(op.as_str()); - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Prefix); } fn print_expr_addr_of( @@ -1217,7 +1217,7 @@ impl<'a> State<'a> { self.print_mutability(mutability, true); } } - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Prefix); } fn print_literal(&mut self, lit: &hir::Lit) { @@ -1355,8 +1355,7 @@ impl<'a> State<'a> { self.print_literal(lit); } hir::ExprKind::Cast(expr, ty) => { - let prec = AssocOp::As.precedence() as i8; - self.print_expr_maybe_paren(expr, prec); + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Cast); self.space(); self.word_space("as"); self.print_type(ty); @@ -1457,27 +1456,25 @@ impl<'a> State<'a> { self.print_block(blk); } hir::ExprKind::Assign(lhs, rhs, _) => { - let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren(lhs, prec + 1); + self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign); self.space(); self.word_space("="); - self.print_expr_maybe_paren(rhs, prec); + self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign); } hir::ExprKind::AssignOp(op, lhs, rhs) => { - let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren(lhs, prec + 1); + self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign); self.space(); self.word(op.node.as_str()); self.word_space("="); - self.print_expr_maybe_paren(rhs, prec); + self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign); } hir::ExprKind::Field(expr, ident) => { - self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS); + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Unambiguous); self.word("."); self.print_ident(ident); } hir::ExprKind::Index(expr, index, _) => { - self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS); + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Unambiguous); self.word("["); self.print_expr(index); self.word("]"); @@ -1491,7 +1488,7 @@ impl<'a> State<'a> { } if let Some(expr) = opt_expr { self.space(); - self.print_expr_maybe_paren(expr, parser::PREC_JUMP); + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump); } } hir::ExprKind::Continue(destination) => { @@ -1505,13 +1502,13 @@ impl<'a> State<'a> { self.word("return"); if let Some(expr) = result { self.word(" "); - self.print_expr_maybe_paren(expr, parser::PREC_JUMP); + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump); } } hir::ExprKind::Become(result) => { self.word("become"); self.word(" "); - self.print_expr_maybe_paren(result, parser::PREC_JUMP); + self.print_expr_cond_paren(result, result.precedence() < ExprPrecedence::Jump); } hir::ExprKind::InlineAsm(asm) => { self.word("asm!"); @@ -1536,7 +1533,7 @@ impl<'a> State<'a> { } hir::ExprKind::Yield(expr, _) => { self.word_space("yield"); - self.print_expr_maybe_paren(expr, parser::PREC_JUMP); + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump); } hir::ExprKind::Err(_) => { self.popen(); @@ -2160,47 +2157,50 @@ impl<'a> State<'a> { if i != 0 { self.word_space(","); } + self.print_where_predicate(predicate); + } + } - match *predicate { - hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - bound_generic_params, - bounded_ty, - bounds, - .. - }) => { - self.print_formal_generic_params(bound_generic_params); - self.print_type(bounded_ty); - self.print_bounds(":", bounds); - } - hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { - lifetime, - bounds, - .. - }) => { - self.print_lifetime(lifetime); - self.word(":"); - - for (i, bound) in bounds.iter().enumerate() { - match bound { - GenericBound::Outlives(lt) => { - self.print_lifetime(lt); - } - _ => panic!("unexpected bound on lifetime param: {bound:?}"), - } + fn print_where_predicate(&mut self, predicate: &hir::WherePredicate<'_>) { + match *predicate.kind { + hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate { + bound_generic_params, + bounded_ty, + bounds, + .. + }) => { + self.print_formal_generic_params(bound_generic_params); + self.print_type(bounded_ty); + self.print_bounds(":", bounds); + } + hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate { + lifetime, + bounds, + .. + }) => { + self.print_lifetime(lifetime); + self.word(":"); - if i != 0 { - self.word(":"); + for (i, bound) in bounds.iter().enumerate() { + match bound { + GenericBound::Outlives(lt) => { + self.print_lifetime(lt); } + _ => panic!("unexpected bound on lifetime param: {bound:?}"), + } + + if i != 0 { + self.word(":"); } } - hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { - lhs_ty, rhs_ty, .. - }) => { - self.print_type(lhs_ty); - self.space(); - self.word_space("="); - self.print_type(rhs_ty); - } + } + hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate { + lhs_ty, rhs_ty, .. + }) => { + self.print_type(lhs_ty); + self.space(); + self.word_space("="); + self.print_type(rhs_ty); } } } diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 3669100ed91..a93da52b270 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -10,6 +10,12 @@ hir_typeck_address_of_temporary_taken = cannot take address of a temporary hir_typeck_arg_mismatch_indeterminate = argument type mismatch was detected, but rustc had trouble determining where .note = we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new +hir_typeck_base_expression_double_dot = base expression required after `..` +hir_typeck_base_expression_double_dot_add_expr = add a base expression here +hir_typeck_base_expression_double_dot_enable_default_field_values = + add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields +hir_typeck_base_expression_double_dot_remove = remove the `..` as all the fields are already present + hir_typeck_candidate_trait_note = `{$trait_name}` defines an item `{$item_name}`{$action_or_ty -> [NONE] {""} [implement] , perhaps you need to implement it @@ -79,6 +85,11 @@ hir_typeck_field_multiply_specified_in_initializer = .label = used more than once .previous_use_label = first use of `{$ident}` +hir_typeck_fn_item_to_variadic_function = can't pass a function item to a variadic function + .suggestion = use a function pointer instead + .help = a function item is zero-sized and needs to be cast into a function pointer to be used in FFI + .note = for more information on function items, visit https://doc.rust-lang.org/reference/types/function-item.html + hir_typeck_fru_expr = this expression does not end in a comma... hir_typeck_fru_expr2 = ... so this is interpreted as a `..` range expression, instead of functional record update syntax hir_typeck_fru_note = this expression may have been misinterpreted as a `..` range expression @@ -141,7 +152,6 @@ hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value in hir_typeck_pass_to_variadic_function = can't pass `{$ty}` to variadic function .suggestion = cast the value to `{$cast_ty}` - .help = cast the value to `{$cast_ty}` .teach_help = certain types, like `{$ty}`, must be casted before passing them to a variadic function, because of arcane ABI rules dictated by the C standard hir_typeck_ptr_cast_add_auto_to_object = adding {$traits_len -> diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index bfdf764d299..243313ee876 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -57,7 +57,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // type in that case) let mut all_arms_diverge = Diverges::WarnedAlways; - let expected = orig_expected.adjust_for_branches(self); + let expected = + orig_expected.try_structurally_resolve_and_adjust_for_branches(self, expr.span); debug!(?expected); let mut coercion = { diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index e6e0f62b54d..b430f48965a 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -1,10 +1,10 @@ use std::iter; -use rustc_ast::util::parser::PREC_UNAMBIGUOUS; +use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey}; use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem}; +use rustc_hir::{self as hir, HirId, LangItem}; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer; use rustc_infer::traits::{self, Obligation, ObligationCause, ObligationCauseCode}; @@ -428,7 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let (fn_sig, def_id) = match *callee_ty.kind() { ty::FnDef(def_id, args) => { - self.enforce_context_effects(call_expr.span, def_id, args); + self.enforce_context_effects(Some(call_expr.hir_id), call_expr.span, def_id, args); let fn_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, args); // Unit testing: function items annotated with @@ -606,7 +606,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Ok(rest_snippet) = rest_snippet { - let sugg = if callee_expr.precedence().order() >= PREC_UNAMBIGUOUS { + let sugg = if callee_expr.precedence() >= ExprPrecedence::Unambiguous { vec![ (up_to_rcvr_span, "".to_string()), (rest_span, format!(".{}({rest_snippet}", segment.ident)), @@ -837,6 +837,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[tracing::instrument(level = "debug", skip(self, span))] pub(super) fn enforce_context_effects( &self, + call_hir_id: Option<HirId>, span: Span, callee_did: DefId, callee_args: GenericArgsRef<'tcx>, @@ -867,10 +868,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.tcx.is_conditionally_const(callee_did) { let q = self.tcx.const_conditions(callee_did); // FIXME(const_trait_impl): Use this span with a better cause code. - for (cond, _) in q.instantiate(self.tcx, callee_args) { + for (idx, (cond, pred_span)) in + q.instantiate(self.tcx, callee_args).into_iter().enumerate() + { + let cause = self.cause( + span, + if let Some(hir_id) = call_hir_id { + ObligationCauseCode::HostEffectInExpr(callee_did, pred_span, hir_id, idx) + } else { + ObligationCauseCode::WhereClause(callee_did, pred_span) + }, + ); self.register_predicate(Obligation::new( self.tcx, - self.misc(span), + cause, self.param_env, cond.to_host_effect_clause(self.tcx, host), )); diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 483a8d1d9a9..80b91c21598 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -28,6 +28,7 @@ //! expression, `e as U2` is not necessarily so (in fact it will only be valid if //! `U1` coerces to `U2`). +use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxHashSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; @@ -1107,8 +1108,8 @@ impl<'a, 'tcx> CastCheck<'tcx> { } fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) { - let expr_prec = self.expr.precedence().order(); - let needs_parens = expr_prec < rustc_ast::util::parser::PREC_UNAMBIGUOUS; + let expr_prec = self.expr.precedence(); + let needs_parens = expr_prec < ExprPrecedence::Unambiguous; let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize)); let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span); diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 11e03229265..bf41dcbe4a3 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -215,7 +215,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } - // Examine the supertype and consider auto-borrowing. + // Examine the supertype and consider type-specific coercions, such + // as auto-borrowing, coercing pointer mutability, a `dyn*` coercion, + // or pin-ergonomics. match *b.kind() { ty::RawPtr(_, b_mutbl) => { return self.coerce_unsafe_ptr(a, b, b_mutbl); @@ -230,7 +232,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { if self.tcx.features().pin_ergonomics() && self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) => { - return self.coerce_pin(a, b); + let pin_coerce = self.commit_if_ok(|_| self.coerce_pin_ref(a, b)); + if pin_coerce.is_ok() { + return pin_coerce; + } } _ => {} } @@ -797,7 +802,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { /// - `Pin<Box<T>>` as `Pin<&T>` /// - `Pin<Box<T>>` as `Pin<&mut T>` #[instrument(skip(self), level = "trace")] - fn coerce_pin(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + fn coerce_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { // We need to make sure the two types are compatible for coercion. // Then we will build a ReborrowPin adjustment and return that as an InferOk. diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 4f579b05d83..7746a5a7132 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -16,6 +16,47 @@ use rustc_span::{Span, Symbol}; use crate::fluent_generated as fluent; #[derive(Diagnostic)] +#[diag(hir_typeck_base_expression_double_dot, code = E0797)] +pub(crate) struct BaseExpressionDoubleDot { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub default_field_values: Option<BaseExpressionDoubleDotEnableDefaultFieldValues>, + #[subdiagnostic] + pub add_expr: Option<BaseExpressionDoubleDotAddExpr>, + #[subdiagnostic] + pub remove_dots: Option<BaseExpressionDoubleDotRemove>, +} + +#[derive(Subdiagnostic)] +#[suggestion( + hir_typeck_base_expression_double_dot_remove, + code = "", + applicability = "machine-applicable", + style = "verbose" +)] +pub(crate) struct BaseExpressionDoubleDotRemove { + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion( + hir_typeck_base_expression_double_dot_add_expr, + code = "/* expr */", + applicability = "has-placeholders", + style = "verbose" +)] +pub(crate) struct BaseExpressionDoubleDotAddExpr { + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] +#[help(hir_typeck_base_expression_double_dot_enable_default_field_values)] +pub(crate) struct BaseExpressionDoubleDotEnableDefaultFieldValues; + +#[derive(Diagnostic)] #[diag(hir_typeck_field_multiply_specified_in_initializer, code = E0062)] pub(crate) struct FieldMultiplySpecifiedInInitializer { #[primary_span] @@ -789,11 +830,20 @@ pub(crate) struct PassToVariadicFunction<'a, 'tcx> { pub span: Span, pub ty: Ty<'tcx>, pub cast_ty: &'a str, - #[suggestion(code = "{replace}", applicability = "machine-applicable")] - pub sugg_span: Option<Span>, - pub replace: String, - #[help] - pub help: bool, + #[suggestion(code = " as {cast_ty}", applicability = "machine-applicable", style = "verbose")] + pub sugg_span: Span, #[note(hir_typeck_teach_help)] pub(crate) teach: bool, } + +#[derive(Diagnostic)] +#[diag(hir_typeck_fn_item_to_variadic_function, code = E0617)] +#[help] +#[note] +pub(crate) struct PassFnItemToVariadicFunction { + #[primary_span] + pub span: Span, + #[suggestion(code = " as {replace}", applicability = "machine-applicable", style = "verbose")] + pub sugg_span: Span, + pub replace: String, +} diff --git a/compiler/rustc_hir_typeck/src/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs index 4653458b5dd..6d95b6917e2 100644 --- a/compiler/rustc_hir_typeck/src/expectation.rs +++ b/compiler/rustc_hir_typeck/src/expectation.rs @@ -39,10 +39,14 @@ impl<'a, 'tcx> Expectation<'tcx> { // an expected type. Otherwise, we might write parts of the type // when checking the 'then' block which are incompatible with the // 'else' branch. - pub(super) fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { + pub(super) fn try_structurally_resolve_and_adjust_for_branches( + &self, + fcx: &FnCtxt<'a, 'tcx>, + span: Span, + ) -> Expectation<'tcx> { match *self { ExpectHasType(ety) => { - let ety = fcx.shallow_resolve(ety); + let ety = fcx.try_structurally_resolve_type(span, ety); if !ety.is_ty_var() { ExpectHasType(ety) } else { NoExpectation } } ExpectRvalueLikeUnsized(ety) => ExpectRvalueLikeUnsized(ety), diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 1610848958e..0e079b03769 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -19,7 +19,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, HirId, QPath}; -use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _; +use rustc_hir_analysis::hir_ty_lowering::{FeedConstTy, HirTyLowerer as _}; use rustc_infer::infer; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_infer::traits::ObligationCause; @@ -44,10 +44,11 @@ use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectatio use crate::TupleArgumentsFlag::DontTupleArguments; use crate::coercion::{CoerceMany, DynamicCoerceMany}; use crate::errors::{ - AddressOfTemporaryTaken, FieldMultiplySpecifiedInInitializer, - FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, ReturnLikeStatementKind, - ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, TypeMismatchFruTypo, - YieldExprOutsideOfCoroutine, + AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr, + BaseExpressionDoubleDotEnableDefaultFieldValues, BaseExpressionDoubleDotRemove, + FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, + ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, + TypeMismatchFruTypo, YieldExprOutsideOfCoroutine, }; use crate::{ BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, Needs, cast, fatally_break_rust, @@ -68,7 +69,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // While we don't allow *arbitrary* coercions here, we *do* allow // coercions from ! to `expected`. - if ty.is_never() && self.expr_guaranteed_to_constitute_read_for_never(expr) { + if self.try_structurally_resolve_type(expr.span, ty).is_never() + && self.expr_guaranteed_to_constitute_read_for_never(expr) + { if let Some(_) = self.typeck_results.borrow().adjustments().get(expr.hir_id) { let reported = self.dcx().span_delayed_bug( expr.span, @@ -274,7 +277,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // unless it's a place expression that isn't being read from, in which case // diverging would be unsound since we may never actually read the `!`. // e.g. `let _ = *never_ptr;` with `never_ptr: *const !`. - if ty.is_never() && self.expr_guaranteed_to_constitute_read_for_never(expr) { + if self.try_structurally_resolve_type(expr.span, ty).is_never() + && self.expr_guaranteed_to_constitute_read_for_never(expr) + { self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); } @@ -420,8 +425,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | hir::Node::GenericParam(_) | hir::Node::Crate(_) | hir::Node::Infer(_) - | hir::Node::WhereBoundPredicate(_) - | hir::Node::ArrayLenInfer(_) + | hir::Node::WherePredicate(_) | hir::Node::PreciseCapturingNonLifetimeArg(_) | hir::Node::OpaqueTy(_) => { unreachable!("no sub-expr expected for {parent_node:?}") @@ -625,7 +629,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| { - match ty.kind() { + match self.try_structurally_resolve_type(expr.span, ty).kind() { ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => { if oprnd.is_syntactic_place_expr() { // Places may legitimately have unsized types. @@ -720,7 +724,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_assoc_method_call(segs); let e = self.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted"); - self.set_tainted_by_errors(e); Ty::new_error(tcx, e) } Res::Def(DefKind::Variant, _) => { @@ -1290,7 +1293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let cond_diverges = self.diverges.get(); self.diverges.set(Diverges::Maybe); - let expected = orig_expected.adjust_for_branches(self); + let expected = orig_expected.try_structurally_resolve_and_adjust_for_branches(self, sp); let then_ty = self.check_expr_with_expectation(then_expr, expected); let then_diverges = self.diverges.get(); self.diverges.set(Diverges::Maybe); @@ -1351,8 +1354,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs: &'tcx hir::Expr<'tcx>, span: Span, ) -> Ty<'tcx> { - let expected_ty = expected.coercion_target_type(self, expr.span); - if expected_ty == self.tcx.types.bool { + let expected_ty = expected.only_has_type(self); + if expected_ty == Some(self.tcx.types.bool) { let guar = self.expr_assign_expected_bool_error(expr, lhs, rhs, span); return Ty::new_error(self.tcx, guar); } @@ -1636,7 +1639,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let element_ty = if !args.is_empty() { let coerce_to = expected .to_option(self) - .and_then(|uty| match *uty.kind() { + .and_then(|uty| match *self.try_structurally_resolve_type(expr.span, uty).kind() { ty::Array(ty, _) | ty::Slice(ty) => Some(ty), _ => None, }) @@ -1669,9 +1672,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { else { return; }; - if let hir::TyKind::Array(_, length) = ty.peel_refs().kind - && let hir::ArrayLen::Body(ct) = length - { + if let hir::TyKind::Array(_, ct) = ty.peel_refs().kind { let span = ct.span(); self.dcx().try_steal_modify_and_emit_err( span, @@ -1709,13 +1710,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_expr_repeat( &self, element: &'tcx hir::Expr<'tcx>, - count: &'tcx hir::ArrayLen<'tcx>, + count: &'tcx hir::ConstArg<'tcx>, expected: Expectation<'tcx>, expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; let count_span = count.span(); - let count = self.try_structurally_resolve_const(count_span, self.lower_array_length(count)); + let count = self.try_structurally_resolve_const( + count_span, + self.normalize(count_span, self.lower_const_arg(count, FeedConstTy::No)), + ); if let Some(count) = count.try_to_target_usize(tcx) { self.suggest_array_len(expr, count); @@ -1851,11 +1855,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_expr_struct( &self, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, expected: Expectation<'tcx>, - qpath: &QPath<'tcx>, + qpath: &'tcx QPath<'tcx>, fields: &'tcx [hir::ExprField<'tcx>], - base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, + base_expr: &'tcx hir::StructTailExpr<'tcx>, ) -> Ty<'tcx> { // Find the relevant variant let (variant, adt_ty) = match self.check_struct_path(qpath, expr.hir_id) { @@ -1895,7 +1899,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, variant: &'tcx ty::VariantDef, hir_fields: &'tcx [hir::ExprField<'tcx>], - base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, + base_expr: &'tcx hir::StructTailExpr<'tcx>, ) { let tcx = self.tcx; @@ -2019,13 +2023,90 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the fields with the base_expr. This could cause us to hit errors later // when certain fields are assumed to exist that in fact do not. if error_happened { - if let Some(base_expr) = base_expr { + if let hir::StructTailExpr::Base(base_expr) = base_expr { self.check_expr(base_expr); } return; } - if let Some(base_expr) = base_expr { + if let hir::StructTailExpr::DefaultFields(span) = *base_expr { + let mut missing_mandatory_fields = Vec::new(); + let mut missing_optional_fields = Vec::new(); + for f in &variant.fields { + let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id); + if let Some(_) = remaining_fields.remove(&ident) { + if f.value.is_none() { + missing_mandatory_fields.push(ident); + } else { + missing_optional_fields.push(ident); + } + } + } + if !self.tcx.features().default_field_values() { + self.dcx().emit_err(BaseExpressionDoubleDot { + span: span.shrink_to_hi(), + // We only mention enabling the feature if this is a nightly rustc *and* the + // expression would make sense with the feature enabled. + default_field_values: if self.tcx.sess.is_nightly_build() + && missing_mandatory_fields.is_empty() + && !missing_optional_fields.is_empty() + { + Some(BaseExpressionDoubleDotEnableDefaultFieldValues) + } else { + None + }, + add_expr: if !missing_mandatory_fields.is_empty() + || !missing_optional_fields.is_empty() + { + Some(BaseExpressionDoubleDotAddExpr { span: span.shrink_to_hi() }) + } else { + None + }, + remove_dots: if missing_mandatory_fields.is_empty() + && missing_optional_fields.is_empty() + { + Some(BaseExpressionDoubleDotRemove { span }) + } else { + None + }, + }); + return; + } + if !missing_mandatory_fields.is_empty() { + let s = pluralize!(missing_mandatory_fields.len()); + let fields: Vec<_> = + missing_mandatory_fields.iter().map(|f| format!("`{f}`")).collect(); + let fields = match &fields[..] { + [] => unreachable!(), + [only] => only.to_string(), + [start @ .., last] => format!("{} and {last}", start.join(", ")), + }; + self.dcx() + .struct_span_err( + span.shrink_to_hi(), + format!("missing mandatory field{s} {fields}"), + ) + .emit(); + return; + } + let fru_tys = match adt_ty.kind() { + ty::Adt(adt, args) if adt.is_struct() => variant + .fields + .iter() + .map(|f| self.normalize(span, f.ty(self.tcx, args))) + .collect(), + ty::Adt(adt, args) if adt.is_enum() => variant + .fields + .iter() + .map(|f| self.normalize(span, f.ty(self.tcx, args))) + .collect(), + _ => { + self.dcx().emit_err(FunctionalRecordUpdateOnNonStruct { span }); + return; + } + }; + self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys); + } else if let hir::StructTailExpr::Base(base_expr) = base_expr { // FIXME: We are currently creating two branches here in order to maintain // consistency. But they should be merged as much as possible. let fru_tys = if self.tcx.features().type_changing_struct_update() { @@ -2157,12 +2238,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_struct_fields_on_error( &self, fields: &'tcx [hir::ExprField<'tcx>], - base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, + base_expr: &'tcx hir::StructTailExpr<'tcx>, ) { for field in fields { self.check_expr(field.expr); } - if let Some(base) = *base_expr { + if let hir::StructTailExpr::Base(base) = *base_expr { self.check_expr(base); } } diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 2a00530c434..27ec2e9e0d4 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -228,7 +228,7 @@ impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) { } fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool { - ty.is_copy_modulo_regions(self.0.tcx, self.0.typing_env()) + self.0.type_is_copy_modulo_regions(ty) } fn body_owner_def_id(&self) -> LocalDefId { @@ -686,7 +686,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx fn walk_struct_expr<'hir>( &self, fields: &[hir::ExprField<'_>], - opt_with: &Option<&'hir hir::Expr<'_>>, + opt_with: &hir::StructTailExpr<'hir>, ) -> Result<(), Cx::Error> { // Consume the expressions supplying values for each field. for field in fields { @@ -702,8 +702,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } let with_expr = match *opt_with { - Some(w) => &*w, - None => { + hir::StructTailExpr::Base(w) => &*w, + hir::StructTailExpr::DefaultFields(_) | hir::StructTailExpr::None => { return Ok(()); } }; diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 7719facccc7..ddd146fe785 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -705,7 +705,8 @@ impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> { fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) -> Self::Result { // For a local, try suggest annotating the type if it's missing. - if let None = local.ty + if let hir::LocalSource::Normal = local.source + && let None = local.ty && let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(local.hir_id) && let Some(vid) = self.fcx.root_vid(ty) && self.reachable_vids.contains(&vid) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 47af8c681da..863be3bdcb2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -185,7 +185,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, method: MethodCallee<'tcx>, ) { - self.enforce_context_effects(span, method.def_id, method.args); + self.enforce_context_effects(Some(hir_id), span, method.def_id, method.args); self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id))); self.write_args(hir_id, method.args); } @@ -263,6 +263,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Adjust::Deref(Some(overloaded_deref)) => { self.enforce_context_effects( + None, expr.span, overloaded_deref.method_call(self.tcx), self.tcx.mk_args(&[a.target.into()]), @@ -486,24 +487,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub(crate) fn lower_array_length(&self, length: &hir::ArrayLen<'tcx>) -> ty::Const<'tcx> { - match length { - hir::ArrayLen::Infer(inf) => self.ct_infer(None, inf.span), - hir::ArrayLen::Body(const_arg) => { - let span = const_arg.span(); - let c = self.lowerer().lower_const_arg(const_arg, FeedConstTy::No); - self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None)); - self.normalize(span, c) - } - } - } - pub(crate) fn lower_const_arg( &self, const_arg: &'tcx hir::ConstArg<'tcx>, - param_def_id: DefId, + feed: FeedConstTy, ) -> ty::Const<'tcx> { - let ct = self.lowerer().lower_const_arg(const_arg, FeedConstTy::Param(param_def_id)); + let ct = self.lowerer().lower_const_arg(const_arg, feed); self.register_wf_obligation( ct.into(), self.tcx.hir().span(const_arg.hir_id), @@ -1278,7 +1267,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.fcx.lower_ty(ty).raw.into() } (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.fcx.lower_const_arg(ct, param.def_id).into() + self.fcx.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into() } (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { self.fcx.ty_infer(Some(param), inf.span).into() diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index b09d78614c3..6eaba641888 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -11,26 +11,56 @@ use rustc_trait_selection::traits; use crate::FnCtxt; +enum ClauseFlavor { + /// Predicate comes from `predicates_of`. + Where, + /// Predicate comes from `const_conditions`. + Const, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn adjust_fulfillment_error_for_expr_obligation( &self, error: &mut traits::FulfillmentError<'tcx>, ) -> bool { - let ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) = - *error.obligation.cause.code().peel_derives() - else { - return false; + let (def_id, hir_id, idx, flavor) = match *error.obligation.cause.code().peel_derives() { + ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) => { + (def_id, hir_id, idx, ClauseFlavor::Where) + } + ObligationCauseCode::HostEffectInExpr(def_id, _, hir_id, idx) => { + (def_id, hir_id, idx, ClauseFlavor::Const) + } + _ => return false, }; - let Some(uninstantiated_pred) = self - .tcx - .predicates_of(def_id) - .instantiate_identity(self.tcx) - .predicates - .into_iter() - .nth(idx) - else { - return false; + let uninstantiated_pred = match flavor { + ClauseFlavor::Where => { + if let Some(pred) = self + .tcx + .predicates_of(def_id) + .instantiate_identity(self.tcx) + .predicates + .into_iter() + .nth(idx) + { + pred + } else { + return false; + } + } + ClauseFlavor::Const => { + if let Some((pred, _)) = self + .tcx + .const_conditions(def_id) + .instantiate_identity(self.tcx) + .into_iter() + .nth(idx) + { + pred.to_host_effect_clause(self.tcx, ty::BoundConstness::Maybe) + } else { + return false; + } + } }; let generics = self.tcx.generics_of(def_id); @@ -39,6 +69,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::ClauseKind::Trait(pred) => { (pred.trait_ref.args.to_vec(), Some(pred.self_ty().into())) } + ty::ClauseKind::HostEffect(pred) => { + (pred.trait_ref.args.to_vec(), Some(pred.self_ty().into())) + } ty::ClauseKind::Projection(pred) => (pred.projection_term.args.to_vec(), None), ty::ClauseKind::ConstArgHasType(arg, ty) => (vec![ty.into(), arg.into()], None), ty::ClauseKind::ConstEvaluatable(e) => (vec![e.into()], None), @@ -94,39 +127,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.find_ambiguous_parameter_in(def_id, error.root_obligation.predicate); } - let (expr, qpath) = match self.tcx.hir_node(hir_id) { - hir::Node::Expr(expr) => { - if self.closure_span_overlaps_error(error, expr.span) { - return false; + match self.tcx.hir_node(hir_id) { + hir::Node::Expr(expr) => self.point_at_expr_if_possible( + error, + def_id, + expr, + predicate_self_type_to_point_at, + param_to_point_at, + fallback_param_to_point_at, + self_param_to_point_at, + ), + + hir::Node::Ty(hir::Ty { kind: hir::TyKind::Path(qpath), .. }) => { + for param in [ + predicate_self_type_to_point_at, + param_to_point_at, + fallback_param_to_point_at, + self_param_to_point_at, + ] + .into_iter() + .flatten() + { + if self.point_at_path_if_possible(error, def_id, param, &qpath) { + return true; + } } - let qpath = - if let hir::ExprKind::Path(qpath) = expr.kind { Some(qpath) } else { None }; - (Some(&expr.kind), qpath) + false } - hir::Node::Ty(hir::Ty { kind: hir::TyKind::Path(qpath), .. }) => (None, Some(*qpath)), - _ => return false, - }; - if let Some(qpath) = qpath { - // Prefer pointing at the turbofished arg that corresponds to the - // self type of the failing predicate over anything else. - if let Some(param) = predicate_self_type_to_point_at - && self.point_at_path_if_possible(error, def_id, param, &qpath) - { - return true; - } + _ => false, + } + } - if let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Call(callee, args), - hir_id: call_hir_id, - span: call_span, - .. - }) = self.tcx.parent_hir_node(hir_id) - && callee.hir_id == hir_id - { - if self.closure_span_overlaps_error(error, *call_span) { - return false; + fn point_at_expr_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + callee_def_id: DefId, + expr: &'tcx hir::Expr<'tcx>, + predicate_self_type_to_point_at: Option<ty::GenericArg<'tcx>>, + param_to_point_at: Option<ty::GenericArg<'tcx>>, + fallback_param_to_point_at: Option<ty::GenericArg<'tcx>>, + self_param_to_point_at: Option<ty::GenericArg<'tcx>>, + ) -> bool { + if self.closure_span_overlaps_error(error, expr.span) { + return false; + } + + match expr.kind { + hir::ExprKind::Call( + hir::Expr { kind: hir::ExprKind::Path(qpath), span: callee_span, .. }, + args, + ) => { + if let Some(param) = predicate_self_type_to_point_at + && self.point_at_path_if_possible(error, callee_def_id, param, &qpath) + { + return true; } for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] @@ -135,32 +191,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { if self.blame_specific_arg_if_possible( error, - def_id, + callee_def_id, param, - *call_hir_id, - callee.span, + expr.hir_id, + *callee_span, None, args, ) { return true; } } + + for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + .into_iter() + .flatten() + { + if self.point_at_path_if_possible(error, callee_def_id, param, &qpath) { + return true; + } + } } + hir::ExprKind::Path(qpath) => { + // If the parent is an call, then process this as a call. + // + // This is because the `WhereClauseInExpr` obligations come from + // the well-formedness of the *path* expression, but we care to + // point at the call expression (namely, its args). + if let hir::Node::Expr( + call_expr @ hir::Expr { kind: hir::ExprKind::Call(callee, ..), .. }, + ) = self.tcx.parent_hir_node(expr.hir_id) + && callee.hir_id == expr.hir_id + { + return self.point_at_expr_if_possible( + error, + callee_def_id, + call_expr, + predicate_self_type_to_point_at, + param_to_point_at, + fallback_param_to_point_at, + self_param_to_point_at, + ); + } - for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] - .into_iter() - .flatten() - { - if self.point_at_path_if_possible(error, def_id, param, &qpath) { + // Otherwise, just try to point at path components. + + if let Some(param) = predicate_self_type_to_point_at + && self.point_at_path_if_possible(error, callee_def_id, param, &qpath) + { return true; } - } - } - match expr { - Some(hir::ExprKind::MethodCall(segment, receiver, args, ..)) => { + for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + .into_iter() + .flatten() + { + if self.point_at_path_if_possible(error, callee_def_id, param, &qpath) { + return true; + } + } + } + hir::ExprKind::MethodCall(segment, receiver, args, ..) => { if let Some(param) = predicate_self_type_to_point_at - && self.point_at_generic_if_possible(error, def_id, param, segment) + && self.point_at_generic_if_possible(error, callee_def_id, param, segment) { // HACK: This is not correct, since `predicate_self_type_to_point_at` might // not actually correspond to the receiver of the method call. But we @@ -170,7 +262,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error.obligation.cause.map_code(|parent_code| { ObligationCauseCode::FunctionArg { arg_hir_id: receiver.hir_id, - call_hir_id: hir_id, + call_hir_id: expr.hir_id, parent_code, } }); @@ -183,9 +275,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { if self.blame_specific_arg_if_possible( error, - def_id, + callee_def_id, param, - hir_id, + expr.hir_id, segment.ident.span, Some(receiver), args, @@ -194,7 +286,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } if let Some(param_to_point_at) = param_to_point_at - && self.point_at_generic_if_possible(error, def_id, param_to_point_at, segment) + && self.point_at_generic_if_possible( + error, + callee_def_id, + param_to_point_at, + segment, + ) { return true; } @@ -208,17 +305,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return true; } } - Some(hir::ExprKind::Struct(qpath, fields, ..)) => { + hir::ExprKind::Struct(qpath, fields, ..) => { if let Res::Def(DefKind::Struct | DefKind::Variant, variant_def_id) = - self.typeck_results.borrow().qpath_res(qpath, hir_id) + self.typeck_results.borrow().qpath_res(qpath, expr.hir_id) { for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] .into_iter() .flatten() { - let refined_expr = - self.point_at_field_if_possible(def_id, param, variant_def_id, fields); + let refined_expr = self.point_at_field_if_possible( + callee_def_id, + param, + variant_def_id, + fields, + ); match refined_expr { None => {} @@ -242,7 +343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .into_iter() .flatten() { - if self.point_at_path_if_possible(error, def_id, param, qpath) { + if self.point_at_path_if_possible(error, callee_def_id, param, qpath) { return true; } } @@ -525,7 +626,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, ) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> { match obligation_cause_code { - traits::ObligationCauseCode::WhereClauseInExpr(_, _, _, _) => { + traits::ObligationCauseCode::WhereClauseInExpr(_, _, _, _) + | ObligationCauseCode::HostEffectInExpr(..) => { // This is the "root"; we assume that the `expr` is already pointing here. // Therefore, we return `Ok` so that this `expr` can be refined further. Ok(expr) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 9c18dbd422d..6b1cceefbee 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -440,20 +440,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty: Ty<'tcx>, cast_ty: &str, ) { - let (sugg_span, replace, help) = - if let Ok(snippet) = sess.source_map().span_to_snippet(span) { - (Some(span), format!("{snippet} as {cast_ty}"), false) - } else { - (None, "".to_string(), true) - }; - sess.dcx().emit_err(errors::PassToVariadicFunction { span, ty, cast_ty, - help, - replace, - sugg_span, + sugg_span: span.shrink_to_hi(), teach: sess.teach(E0617), }); } @@ -472,9 +463,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variadic_error(tcx.sess, arg.span, arg_ty, "c_uint"); } ty::FnDef(..) => { - let ptr_ty = Ty::new_fn_ptr(self.tcx, arg_ty.fn_sig(self.tcx)); - let ptr_ty = self.resolve_vars_if_possible(ptr_ty); - variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string()); + let fn_ptr = Ty::new_fn_ptr(self.tcx, arg_ty.fn_sig(self.tcx)); + let fn_ptr = self.resolve_vars_if_possible(fn_ptr).to_string(); + + let fn_item_spa = arg.span; + tcx.sess.dcx().emit_err(errors::PassFnItemToVariadicFunction { + span: fn_item_spa, + sugg_span: fn_item_spa.shrink_to_hi(), + replace: fn_ptr, + }); } _ => {} } @@ -906,6 +903,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + let detect_dotdot = |err: &mut Diag<'_>, ty: Ty<'_>, expr: &hir::Expr<'_>| { + if let ty::Adt(adt, _) = ty.kind() + && self.tcx().lang_items().get(hir::LangItem::RangeFull) == Some(adt.did()) + && let hir::ExprKind::Struct( + hir::QPath::LangItem(hir::LangItem::RangeFull, _), + [], + _, + ) = expr.kind + { + // We have `Foo(a, .., c)`, where the user might be trying to use the "rest" syntax + // from default field values, which is not supported on tuples. + let explanation = if self.tcx.features().default_field_values() { + "this is only supported on non-tuple struct literals" + } else if self.tcx.sess.is_nightly_build() { + "this is only supported on non-tuple struct literals when \ + `#![feature(default_field_values)]` is enabled" + } else { + "this is not supported" + }; + let msg = format!( + "you might have meant to use `..` to skip providing a value for \ + expected fields, but {explanation}; it is instead interpreted as a \ + `std::ops::RangeFull` literal", + ); + err.span_help(expr.span, msg); + } + }; + let mut reported = None; errors.retain(|error| { let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = @@ -939,18 +964,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // can be collated pretty easily if needed. // Next special case: if there is only one "Incompatible" error, just emit that - if let [ + if let &[ Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))), ] = &errors[..] { - let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx]; - let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx]; + let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; + let (provided_ty, provided_arg_span) = provided_arg_tys[provided_idx]; let trace = mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty); - let mut err = - self.err_ctxt().report_and_explain_type_error(trace, self.param_env, *err); + let mut err = self.err_ctxt().report_and_explain_type_error(trace, self.param_env, err); self.emit_coerce_suggestions( &mut err, - provided_args[*provided_idx], + provided_args[provided_idx], provided_ty, Expectation::rvalue_hint(self, expected_ty) .only_has_type(self) @@ -985,7 +1009,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_ptr_null_mut( expected_ty, provided_ty, - provided_args[*provided_idx], + provided_args[provided_idx], &mut err, ); @@ -995,7 +1019,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_ident, expected_ty, provided_ty, - provided_args[*provided_idx], + provided_args[provided_idx], is_method, ); @@ -1013,6 +1037,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments, ); suggest_confusable(&mut err); + detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]); return err.emit(); } @@ -1137,6 +1162,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None, None, ); + detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]); } Error::Extra(arg_idx) => { let (provided_ty, provided_span) = provided_arg_tys[arg_idx]; @@ -1220,6 +1246,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; prev_extra_idx = Some(arg_idx.index()) } + detect_dotdot(&mut err, provided_ty, provided_args[arg_idx]); } Error::Missing(expected_idx) => { // If there are multiple missing arguments adjacent to each other, @@ -2347,9 +2374,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let check_for_matched_generics = || { if matched_inputs.iter().any(|x| x.is_some()) - && params_with_generics.iter().any(|x| x.0.is_some()) + && params_with_generics.iter().any(|x| x.1.is_some()) { - for (idx, (generic, _)) in params_with_generics.iter().enumerate() { + for &(idx, generic, _) in ¶ms_with_generics { // Param has to have a generic and be matched to be relevant if matched_inputs[idx.into()].is_none() { continue; @@ -2362,7 +2389,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for unmatching_idx in idx + 1..params_with_generics.len() { if matched_inputs[unmatching_idx.into()].is_none() && let Some(unmatched_idx_param_generic) = - params_with_generics[unmatching_idx].0 + params_with_generics[unmatching_idx].1 && unmatched_idx_param_generic.name.ident() == generic.name.ident() { @@ -2377,8 +2404,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let check_for_matched_generics = check_for_matched_generics(); - for (idx, (generic_param, param)) in - params_with_generics.iter().enumerate().filter(|(idx, _)| { + for &(idx, generic_param, param) in + params_with_generics.iter().filter(|&(idx, _, _)| { check_for_matched_generics || expected_idx.is_none_or(|expected_idx| expected_idx == *idx) }) @@ -2390,8 +2417,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let other_params_matched: Vec<(usize, &hir::Param<'_>)> = params_with_generics .iter() - .enumerate() - .filter(|(other_idx, (other_generic_param, _))| { + .filter(|(other_idx, other_generic_param, _)| { if *other_idx == idx { return false; } @@ -2410,18 +2436,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } other_generic_param.name.ident() == generic_param.name.ident() }) - .map(|(other_idx, (_, other_param))| (other_idx, *other_param)) + .map(|&(other_idx, _, other_param)| (other_idx, other_param)) .collect(); if !other_params_matched.is_empty() { let other_param_matched_names: Vec<String> = other_params_matched .iter() - .map(|(_, other_param)| { + .map(|(idx, other_param)| { if let hir::PatKind::Binding(_, _, ident, _) = other_param.pat.kind { format!("`{ident}`") } else { - "{unknown}".to_string() + format!("parameter #{}", idx + 1) } }) .collect(); @@ -2478,18 +2504,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let param_idents_matching: Vec<String> = params_with_generics .iter() - .filter(|(generic, _)| { + .filter(|(_, generic, _)| { if let Some(generic) = generic { generic.name.ident() == generic_param.name.ident() } else { false } }) - .map(|(_, param)| { + .map(|(idx, _, param)| { if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind { format!("`{ident}`") } else { - "{unknown}".to_string() + format!("parameter #{}", idx + 1) } }) .collect(); @@ -2498,8 +2524,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { spans.push_span_label( generic_param.span, format!( - "{} all reference this parameter {}", + "{} {} reference this parameter `{}`", display_list_with_comma_and(¶m_idents_matching), + if param_idents_matching.len() == 2 { "both" } else { "all" }, generic_param.name.ident().name, ), ); @@ -2580,7 +2607,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(params_with_generics) = self.get_hir_params_with_generics(def_id, is_method) { debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); - for (idx, (generic_param, _)) in params_with_generics.iter().enumerate() { + for &(idx, generic_param, _) in ¶ms_with_generics { if matched_inputs[idx.into()].is_none() { continue; } @@ -2594,20 +2621,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut idxs_matched: Vec<usize> = vec![]; - for (other_idx, (_, _)) in params_with_generics.iter().enumerate().filter( - |(other_idx, (other_generic_param, _))| { - if *other_idx == idx { + for &(other_idx, _, _) in + params_with_generics.iter().filter(|&&(other_idx, other_generic_param, _)| { + if other_idx == idx { return false; } let Some(other_generic_param) = other_generic_param else { return false; }; - if matched_inputs[(*other_idx).into()].is_some() { + if matched_inputs[other_idx.into()].is_some() { return false; } other_generic_param.name.ident() == generic_param.name.ident() - }, - ) { + }) + { idxs_matched.push(other_idx); } @@ -2642,7 +2669,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, def_id: DefId, is_method: bool, - ) -> Option<Vec<(Option<&hir::GenericParam<'_>>, &hir::Param<'_>)>> { + ) -> Option<Vec<(usize, Option<&hir::GenericParam<'_>>, &hir::Param<'_>)>> { let fn_node = self.tcx.hir().get_if_local(def_id)?; let fn_decl = fn_node.fn_decl()?; @@ -2685,7 +2712,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } debug_assert_eq!(params.len(), generic_params.len()); - Some(generic_params.into_iter().zip(params).collect()) + Some( + generic_params + .into_iter() + .zip(params) + .enumerate() + .map(|(a, (b, c))| (a, b, c)) + .collect(), + ) } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 3940d138deb..aacdcf027b6 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -307,7 +307,11 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { ty::Alias(ty::Projection | ty::Inherent | ty::Weak, _) if !ty.has_escaping_bound_vars() => { - self.normalize(span, ty).ty_adt_def() + if self.next_trait_solver() { + self.try_structurally_resolve_type(span, ty).ty_adt_def() + } else { + self.normalize(span, ty).ty_adt_def() + } } _ => None, } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index c4c4c2f200b..ddcd90a2a9d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2,7 +2,7 @@ use core::cmp::min; use core::iter; use hir::def_id::LocalDefId; -use rustc_ast::util::parser::{ExprPrecedence, PREC_UNAMBIGUOUS}; +use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::packed::Pu128; use rustc_errors::{Applicability, Diag, MultiSpan}; use rustc_hir as hir; @@ -10,7 +10,7 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, - Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, + Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicateKind, }; use rustc_hir_analysis::collect::suggest_impl_trait; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; @@ -398,7 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // so we remove the user's `clone` call. { vec![(receiver_method.ident.span, conversion_method.name.to_string())] - } else if expr.precedence().order() < ExprPrecedence::MethodCall.order() { + } else if expr.precedence() < ExprPrecedence::Unambiguous { vec![ (expr.span.shrink_to_lo(), "(".to_string()), (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), @@ -1004,8 +1004,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // get all where BoundPredicates here, because they are used in two cases below let where_predicates = predicates .iter() - .filter_map(|p| match p { - WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + .filter_map(|p| match p.kind { + WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate { bounds, bounded_ty, .. @@ -1376,7 +1376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let span = expr.span.find_oldest_ancestor_in_same_ctxt(); - let mut sugg = if expr.precedence().order() >= PREC_UNAMBIGUOUS { + let mut sugg = if expr.precedence() >= ExprPrecedence::Unambiguous { vec![(span.shrink_to_hi(), ".into()".to_owned())] } else { vec![ @@ -3000,7 +3000,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`", ); - let close_paren = if expr.precedence().order() < PREC_UNAMBIGUOUS { + let close_paren = if expr.precedence() < ExprPrecedence::Unambiguous { sugg.push((expr.span.shrink_to_lo(), "(".to_string())); ")" } else { @@ -3025,7 +3025,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let len = src.trim_end_matches(&checked_ty.to_string()).len(); expr.span.with_lo(expr.span.lo() + BytePos(len as u32)) }, - if expr.precedence().order() < PREC_UNAMBIGUOUS { + if expr.precedence() < ExprPrecedence::Unambiguous { // Readd `)` format!("{expected_ty})") } else { @@ -3394,7 +3394,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else { return; }; - if self.can_eq(self.param_env, expected_ty, ty) { + if self.can_eq(self.param_env, expected_ty, ty) + // FIXME: this happens with macro calls. Need to figure out why the stmt + // `println!();` doesn't include the `;` in its `Span`. (#133845) + // We filter these out to avoid ICEs with debug assertions on caused by + // empty suggestions. + && stmt.span.hi() != tail_expr.span.hi() + { err.span_suggestion_short( stmt.span.with_lo(tail_expr.span.hi()), "remove this semicolon", diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 3754fd02428..b31ee1a55d6 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -7,7 +7,7 @@ use rustc_hir_analysis::hir_ty_lowering::generics::{ check_generic_arg_count_for_call, lower_generic_args, }; use rustc_hir_analysis::hir_ty_lowering::{ - GenericArgsLowerer, HirTyLowerer, IsMethodCall, RegionInferReason, + FeedConstTy, GenericArgsLowerer, HirTyLowerer, IsMethodCall, RegionInferReason, }; use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk}; use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; @@ -429,7 +429,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.cfcx.lower_ty(ty).raw.into() } (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.cfcx.lower_const_arg(ct, param.def_id).into() + self.cfcx.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into() } (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { self.cfcx.ty_infer(Some(param), inf.span).into() diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 640729576fc..039c117c099 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1574,7 +1574,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // Thus we need to prevent them from trying to match the `&_` autoref // candidates that get created for `&self` trait methods. ty::Alias(ty::Opaque, alias_ty) - if self.infcx.can_define_opaque_ty(alias_ty.def_id) + if !self.next_trait_solver() + && self.infcx.can_define_opaque_ty(alias_ty.def_id) && !xform_self_ty.is_ty_var() => { return ProbeResult::NoMatch; @@ -1642,6 +1643,28 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + // FIXME(-Znext-solver): See the linked issue below. + // <https://github.com/rust-lang/trait-system-refactor-initiative/issues/134> + // + // In the new solver, check the well-formedness of the return type. + // This emulates, in a way, the predicates that fall out of + // normalizing the return type in the old solver. + // + // We alternatively could check the predicates of the method itself hold, + // but we intentionally do not do this in the old solver b/c of cycles, + // and doing it in the new solver would be stronger. This should be fixed + // in the future, since it likely leads to much better method winnowing. + if let Some(xform_ret_ty) = xform_ret_ty + && self.infcx.next_trait_solver() + { + ocx.register_obligation(traits::Obligation::new( + self.tcx, + cause.clone(), + self.param_env, + ty::ClauseKind::WellFormed(xform_ret_ty.into()), + )); + } + // Evaluate those obligations to see if they might possibly hold. for error in ocx.select_where_possible() { result = ProbeResult::NoMatch; diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index cff2aa68993..6b1a288510a 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -664,7 +664,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let is_write = sugg_span.ctxt().outer_expn_data().macro_def_id.is_some_and(|def_id| { tcx.is_diagnostic_item(sym::write_macro, def_id) || tcx.is_diagnostic_item(sym::writeln_macro, def_id) - }) && item_name.name == Symbol::intern("write_fmt"); + }) && item_name.name == sym::write_fmt; let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source { self.suggest_missing_writer(rcvr_ty, rcvr_expr) } else { diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 3d401cef76f..7c667511aa9 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -296,7 +296,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); }; *deref = OverloadedDeref { mutbl, span: deref.span }; - self.enforce_context_effects(expr.span, method.def_id, method.args); + self.enforce_context_effects(None, expr.span, method.def_id, method.args); // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514). // This helps avoid accidental drops. if inside_union diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index d50eff0deb0..b0c020dd7cb 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -1802,7 +1802,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut is_mutbl = bm.1; for pointer_ty in place.deref_tys() { - match pointer_ty.kind() { + match self.structurally_resolve_type(self.tcx.hir().span(var_hir_id), pointer_ty).kind() + { // We don't capture derefs of raw ptrs ty::RawPtr(_, _) => unreachable!(), @@ -1816,7 +1817,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Dereferencing a box doesn't change mutability ty::Adt(def, ..) if def.is_box() => {} - unexpected_ty => bug!("deref of unexpected pointer type {:?}", unexpected_ty), + unexpected_ty => span_bug!( + self.tcx.hir().span(var_hir_id), + "deref of unexpected pointer type {:?}", + unexpected_ty + ), } } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 2f436ce77a4..e17a68c8692 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -12,7 +12,7 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::span_bug; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; -use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, fold_regions}; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperFoldable}; use rustc_span::Span; @@ -827,7 +827,10 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { // no reason to keep regions around. They will be repopulated during MIR // borrowck, and specifically region constraints will be populated during // MIR typeck which is run on the new body. - value = tcx.fold_regions(value, |_, _| tcx.lifetimes.re_erased); + // + // We're not using `tcx.erase_regions` as that also anonymizes bound variables, + // regressing borrowck diagnostics. + value = fold_regions(tcx, value, |_, _| tcx.lifetimes.re_erased); // Normalize consts in writeback, because GCE doesn't normalize eagerly. if tcx.features().generic_const_exprs() { diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index e8735cba9bd..6dab6468870 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -16,8 +16,15 @@ mod persist; pub use persist::{ LoadResult, copy_cgu_workproduct_to_incr_comp_cache_dir, finalize_session_directory, - in_incr_comp_dir, in_incr_comp_dir_sess, load_query_result_cache, save_dep_graph, - save_work_product_index, setup_dep_graph, + in_incr_comp_dir, in_incr_comp_dir_sess, load_query_result_cache, save_work_product_index, + setup_dep_graph, }; +use rustc_middle::util::Providers; + +#[allow(missing_docs)] +pub fn provide(providers: &mut Providers) { + providers.hooks.save_dep_graph = + |tcx| tcx.sess.time("serialize_dep_graph", || persist::save_dep_graph(tcx.tcx)); +} rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index f6c3e8ebbcb..4ea171ab4a9 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -114,7 +114,6 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::svh::Svh; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_data_structures::{base_n, flock}; -use rustc_errors::ErrorGuaranteed; use rustc_fs_util::{LinkOrCopy, link_or_copy, try_canonicalize}; use rustc_middle::bug; use rustc_session::config::CrateType; @@ -212,9 +211,9 @@ pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBu /// The garbage collection will take care of it. /// /// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph -pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuaranteed> { +pub(crate) fn prepare_session_directory(sess: &Session) { if sess.opts.incremental.is_none() { - return Ok(()); + return; } let _timer = sess.timer("incr_comp_prepare_session_directory"); @@ -224,7 +223,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara // {incr-comp-dir}/{crate-name-and-disambiguator} let crate_dir = crate_path(sess); debug!("crate-dir: {}", crate_dir.display()); - create_dir(sess, &crate_dir, "crate")?; + create_dir(sess, &crate_dir, "crate"); // Hack: canonicalize the path *after creating the directory* // because, on windows, long paths can cause problems; @@ -233,7 +232,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara let crate_dir = match try_canonicalize(&crate_dir) { Ok(v) => v, Err(err) => { - return Err(sess.dcx().emit_err(errors::CanonicalizePath { path: crate_dir, err })); + sess.dcx().emit_fatal(errors::CanonicalizePath { path: crate_dir, err }); } }; @@ -248,11 +247,11 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara // Lock the new session directory. If this fails, return an // error without retrying - let (directory_lock, lock_file_path) = lock_directory(sess, &session_dir)?; + let (directory_lock, lock_file_path) = lock_directory(sess, &session_dir); // Now that we have the lock, we can actually create the session // directory - create_dir(sess, &session_dir, "session")?; + create_dir(sess, &session_dir, "session"); // Find a suitable source directory to copy from. Ignore those that we // have already tried before. @@ -266,7 +265,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara ); sess.init_incr_comp_session(session_dir, directory_lock); - return Ok(()); + return; }; debug!("attempting to copy data from source: {}", source_directory.display()); @@ -280,7 +279,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara } sess.init_incr_comp_session(session_dir, directory_lock); - return Ok(()); + return; } else { debug!("copying failed - trying next directory"); @@ -459,21 +458,17 @@ fn generate_session_dir_path(crate_dir: &Path) -> PathBuf { directory_path } -fn create_dir(sess: &Session, path: &Path, dir_tag: &str) -> Result<(), ErrorGuaranteed> { +fn create_dir(sess: &Session, path: &Path, dir_tag: &str) { match std_fs::create_dir_all(path) { Ok(()) => { debug!("{} directory created successfully", dir_tag); - Ok(()) } - Err(err) => Err(sess.dcx().emit_err(errors::CreateIncrCompDir { tag: dir_tag, path, err })), + Err(err) => sess.dcx().emit_fatal(errors::CreateIncrCompDir { tag: dir_tag, path, err }), } } /// Allocate the lock-file and lock it. -fn lock_directory( - sess: &Session, - session_dir: &Path, -) -> Result<(flock::Lock, PathBuf), ErrorGuaranteed> { +fn lock_directory(sess: &Session, session_dir: &Path) -> (flock::Lock, PathBuf) { let lock_file_path = lock_file_path(session_dir); debug!("lock_directory() - lock_file: {}", lock_file_path.display()); @@ -484,15 +479,15 @@ fn lock_directory( true, ) { // the lock should be exclusive - Ok(lock) => Ok((lock, lock_file_path)), + Ok(lock) => (lock, lock_file_path), Err(lock_err) => { let is_unsupported_lock = flock::Lock::error_unsupported(&lock_err); - Err(sess.dcx().emit_err(errors::CreateLock { + sess.dcx().emit_fatal(errors::CreateLock { lock_err, session_dir, is_unsupported_lock, is_cargo: rustc_session::utils::was_invoked_from_cargo(), - })) + }); } } } diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index c74804cc798..48df84f3d09 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -11,7 +11,6 @@ use rustc_serialize::Decodable; use rustc_serialize::opaque::MemDecoder; use rustc_session::Session; use rustc_session::config::IncrementalStateAssertion; -use rustc_span::ErrorGuaranteed; use tracing::{debug, warn}; use super::data::*; @@ -182,7 +181,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc<SerializedDepGraph>, WorkPr /// If we are not in incremental compilation mode, returns `None`. /// Otherwise, tries to load the query result cache from disk, /// creating an empty cache if it could not be loaded. -pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache<'_>> { +pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache> { if sess.opts.incremental.is_none() { return None; } @@ -194,19 +193,19 @@ pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache<'_>> { LoadResult::Ok { data: (bytes, start_pos) } => { let cache = OnDiskCache::new(sess, bytes, start_pos).unwrap_or_else(|()| { sess.dcx().emit_warn(errors::CorruptFile { path: &path }); - OnDiskCache::new_empty(sess.source_map()) + OnDiskCache::new_empty() }); Some(cache) } - _ => Some(OnDiskCache::new_empty(sess.source_map())), + _ => Some(OnDiskCache::new_empty()), } } /// Setups the dependency graph by loading an existing graph from disk and set up streaming of a /// new graph to an incremental session directory. -pub fn setup_dep_graph(sess: &Session) -> Result<DepGraph, ErrorGuaranteed> { +pub fn setup_dep_graph(sess: &Session) -> DepGraph { // `load_dep_graph` can only be called after `prepare_session_directory`. - prepare_session_directory(sess)?; + prepare_session_directory(sess); let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess)); @@ -222,10 +221,9 @@ pub fn setup_dep_graph(sess: &Session) -> Result<DepGraph, ErrorGuaranteed> { }); } - Ok(res - .and_then(|result| { - let (prev_graph, prev_work_products) = result.open(sess); - build_dep_graph(sess, prev_graph, prev_work_products) - }) - .unwrap_or_else(DepGraph::new_disabled)) + res.and_then(|result| { + let (prev_graph, prev_work_products) = result.open(sess); + build_dep_graph(sess, prev_graph, prev_work_products) + }) + .unwrap_or_else(DepGraph::new_disabled) } diff --git a/compiler/rustc_incremental/src/persist/mod.rs b/compiler/rustc_incremental/src/persist/mod.rs index 186db0a60a3..f5d5167e0e2 100644 --- a/compiler/rustc_incremental/src/persist/mod.rs +++ b/compiler/rustc_incremental/src/persist/mod.rs @@ -12,5 +12,6 @@ mod work_product; pub use fs::{finalize_session_directory, in_incr_comp_dir, in_incr_comp_dir_sess}; pub use load::{LoadResult, load_query_result_cache, setup_dep_graph}; -pub use save::{save_dep_graph, save_work_product_index}; +pub(crate) use save::save_dep_graph; +pub use save::save_work_product_index; pub use work_product::copy_cgu_workproduct_to_incr_comp_cache_dir; diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 53726df4597..8fc50ca1b43 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -25,7 +25,7 @@ use crate::errors; /// /// This function should only run after all queries have completed. /// Trying to execute a query afterwards would attempt to read the result cache we just dropped. -pub fn save_dep_graph(tcx: TyCtxt<'_>) { +pub(crate) fn save_dep_graph(tcx: TyCtxt<'_>) { debug!("save_dep_graph()"); tcx.dep_graph.with_ignore(|| { let sess = tcx.sess; diff --git a/compiler/rustc_index/Cargo.toml b/compiler/rustc_index/Cargo.toml index f7d18f84e34..33e8e2824c7 100644 --- a/compiler/rustc_index/Cargo.toml +++ b/compiler/rustc_index/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -arrayvec = { version = "0.7", default-features = false } rustc_index_macros = { path = "../rustc_index_macros", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index a9239489222..aba1e938296 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -4,7 +4,6 @@ use std::rc::Rc; use std::{fmt, iter, mem, slice}; use Chunk::*; -use arrayvec::ArrayVec; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_Generic, Encodable_Generic}; use smallvec::{SmallVec, smallvec}; @@ -240,45 +239,6 @@ impl<T: Idx> BitSet<T> { BitIter::new(&self.words) } - /// Set `self = self | other`. In contrast to `union` returns `true` if the set contains at - /// least one bit that is not in `other` (i.e. `other` is not a superset of `self`). - /// - /// This is an optimization for union of a hybrid bitset. - fn reverse_union_sparse(&mut self, sparse: &SparseBitSet<T>) -> bool { - assert!(sparse.domain_size == self.domain_size); - self.clear_excess_bits(); - - let mut not_already = false; - // Index of the current word not yet merged. - let mut current_index = 0; - // Mask of bits that came from the sparse set in the current word. - let mut new_bit_mask = 0; - for (word_index, mask) in sparse.iter().map(|x| word_index_and_mask(*x)) { - // Next bit is in a word not inspected yet. - if word_index > current_index { - self.words[current_index] |= new_bit_mask; - // Were there any bits in the old word that did not occur in the sparse set? - not_already |= (self.words[current_index] ^ new_bit_mask) != 0; - // Check all words we skipped for any set bit. - not_already |= self.words[current_index + 1..word_index].iter().any(|&x| x != 0); - // Update next word. - current_index = word_index; - // Reset bit mask, no bits have been merged yet. - new_bit_mask = 0; - } - // Add bit and mark it as coming from the sparse set. - // self.words[word_index] |= mask; - new_bit_mask |= mask; - } - self.words[current_index] |= new_bit_mask; - // Any bits in the last inspected word that were not in the sparse set? - not_already |= (self.words[current_index] ^ new_bit_mask) != 0; - // Any bits in the tail? Note `clear_excess_bits` before. - not_already |= self.words[current_index + 1..].iter().any(|&x| x != 0); - - not_already - } - pub fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> { let (start, end) = inclusive_start_end(range, self.domain_size)?; let (start_word_index, _) = word_index_and_mask(start); @@ -336,6 +296,111 @@ impl<T: Idx> From<GrowableBitSet<T>> for BitSet<T> { } } +impl<T> Clone for BitSet<T> { + fn clone(&self) -> Self { + BitSet { domain_size: self.domain_size, words: self.words.clone(), marker: PhantomData } + } + + fn clone_from(&mut self, from: &Self) { + self.domain_size = from.domain_size; + self.words.clone_from(&from.words); + } +} + +impl<T: Idx> fmt::Debug for BitSet<T> { + fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { + w.debug_list().entries(self.iter()).finish() + } +} + +impl<T: Idx> ToString for BitSet<T> { + fn to_string(&self) -> String { + let mut result = String::new(); + let mut sep = '['; + + // Note: this is a little endian printout of bytes. + + // i tracks how many bits we have printed so far. + let mut i = 0; + for word in &self.words { + let mut word = *word; + for _ in 0..WORD_BYTES { + // for each byte in `word`: + let remain = self.domain_size - i; + // If less than a byte remains, then mask just that many bits. + let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF }; + assert!(mask <= 0xFF); + let byte = word & mask; + + result.push_str(&format!("{sep}{byte:02x}")); + + if remain <= 8 { + break; + } + word >>= 8; + i += 8; + sep = '-'; + } + sep = '|'; + } + result.push(']'); + + result + } +} + +pub struct BitIter<'a, T: Idx> { + /// A copy of the current word, but with any already-visited bits cleared. + /// (This lets us use `trailing_zeros()` to find the next set bit.) When it + /// is reduced to 0, we move onto the next word. + word: Word, + + /// The offset (measured in bits) of the current word. + offset: usize, + + /// Underlying iterator over the words. + iter: slice::Iter<'a, Word>, + + marker: PhantomData<T>, +} + +impl<'a, T: Idx> BitIter<'a, T> { + #[inline] + fn new(words: &'a [Word]) -> BitIter<'a, T> { + // We initialize `word` and `offset` to degenerate values. On the first + // call to `next()` we will fall through to getting the first word from + // `iter`, which sets `word` to the first word (if there is one) and + // `offset` to 0. Doing it this way saves us from having to maintain + // additional state about whether we have started. + BitIter { + word: 0, + offset: usize::MAX - (WORD_BITS - 1), + iter: words.iter(), + marker: PhantomData, + } + } +} + +impl<'a, T: Idx> Iterator for BitIter<'a, T> { + type Item = T; + fn next(&mut self) -> Option<T> { + loop { + if self.word != 0 { + // Get the position of the next set bit in the current word, + // then clear the bit. + let bit_pos = self.word.trailing_zeros() as usize; + self.word ^= 1 << bit_pos; + return Some(T::new(bit_pos + self.offset)); + } + + // Move onto the next word. `wrapping_add()` is needed to handle + // the degenerate initial value given to `offset` in `new()`. + self.word = *self.iter.next()?; + self.offset = self.offset.wrapping_add(WORD_BITS); + } + } +} + /// A fixed-size bitset type with a partially dense, partially sparse /// representation. The bitset is broken into chunks, and chunks that are all /// zeros or all ones are represented and handled very efficiently. @@ -345,6 +410,9 @@ impl<T: Idx> From<GrowableBitSet<T>> for BitSet<T> { /// some stretches with lots of 0s and 1s mixed in a way that causes trouble /// for `IntervalSet`. /// +/// Best used via `MixedBitSet`, rather than directly, because `MixedBitSet` +/// has better performance for small bitsets. +/// /// `T` is an index type, typically a newtyped `usize` wrapper, but it can also /// just be `usize`. /// @@ -369,9 +437,11 @@ pub struct ChunkedBitSet<T> { #[derive(Clone, Debug, PartialEq, Eq)] enum Chunk { /// A chunk that is all zeros; we don't represent the zeros explicitly. + /// The `ChunkSize` is always non-zero. Zeros(ChunkSize), /// A chunk that is all ones; we don't represent the ones explicitly. + /// `ChunkSize` is always non-zero. Ones(ChunkSize), /// A chunk that has a mix of zeros and ones, which are represented @@ -381,11 +451,12 @@ enum Chunk { /// turns out to be both simpler and have better performance than /// allocating the minimum number of words, largely because we avoid having /// to store the length, which would make this type larger. These excess - /// words are always be zero, as are any excess bits in the final in-use - /// word. + /// words are always zero, as are any excess bits in the final in-use word. + /// + /// The first `ChunkSize` field is always non-zero. /// - /// The second field is the count of 1s set in the chunk, and must satisfy - /// `0 < count < chunk_domain_size`. + /// The second `ChunkSize` field is the count of 1s set in the chunk, and + /// must satisfy `0 < count < chunk_domain_size`. /// /// The words are within an `Rc` because it's surprisingly common to /// duplicate an entire chunk, e.g. in `ChunkedBitSet::clone_from()`, or @@ -450,6 +521,11 @@ impl<T: Idx> ChunkedBitSet<T> { ChunkedBitSet::new(domain_size, /* is_empty */ false) } + pub fn clear(&mut self) { + let domain_size = self.domain_size(); + *self = ChunkedBitSet::new_empty(domain_size); + } + #[cfg(test)] fn chunks(&self) -> &[Chunk] { &self.chunks @@ -461,7 +537,7 @@ impl<T: Idx> ChunkedBitSet<T> { } pub fn is_empty(&self) -> bool { - self.chunks.iter().all(|chunk| matches!(chunk, Chunk::Zeros(..) | Chunk::Ones(0))) + self.chunks.iter().all(|chunk| matches!(chunk, Zeros(..))) } /// Returns `true` if `self` contains `elem`. @@ -610,6 +686,18 @@ impl<T: Idx> ChunkedBitSet<T> { } } + fn chunk_iter(&self, chunk_index: usize) -> ChunkIter<'_> { + match self.chunks.get(chunk_index) { + Some(Zeros(_chunk_domain_size)) => ChunkIter::Zeros, + Some(Ones(chunk_domain_size)) => ChunkIter::Ones(0..*chunk_domain_size as usize), + Some(Mixed(chunk_domain_size, _, ref words)) => { + let num_words = num_words(*chunk_domain_size as usize); + ChunkIter::Mixed(BitIter::new(&words[0..num_words])) + } + None => ChunkIter::Finished, + } + } + bit_relations_inherent_impls! {} } @@ -809,30 +897,6 @@ impl<T: Idx> BitRelations<ChunkedBitSet<T>> for ChunkedBitSet<T> { } } -impl<T: Idx> BitRelations<HybridBitSet<T>> for ChunkedBitSet<T> { - fn union(&mut self, other: &HybridBitSet<T>) -> bool { - // FIXME: This is slow if `other` is dense, but it hasn't been a problem - // in practice so far. - // If a faster implementation of this operation is required, consider - // reopening https://github.com/rust-lang/rust/pull/94625 - assert_eq!(self.domain_size, other.domain_size()); - sequential_update(|elem| self.insert(elem), other.iter()) - } - - fn subtract(&mut self, other: &HybridBitSet<T>) -> bool { - // FIXME: This is slow if `other` is dense, but it hasn't been a problem - // in practice so far. - // If a faster implementation of this operation is required, consider - // reopening https://github.com/rust-lang/rust/pull/94625 - assert_eq!(self.domain_size, other.domain_size()); - sequential_update(|elem| self.remove(elem), other.iter()) - } - - fn intersect(&mut self, _other: &HybridBitSet<T>) -> bool { - unimplemented!("implement if/when necessary"); - } -} - impl<T: Idx> BitRelations<ChunkedBitSet<T>> for BitSet<T> { fn union(&mut self, other: &ChunkedBitSet<T>) -> bool { sequential_update(|elem| self.insert(elem), other.iter()) @@ -851,7 +915,7 @@ impl<T: Idx> BitRelations<ChunkedBitSet<T>> for BitSet<T> { words = &mut words[..CHUNK_WORDS]; } match chunk { - Chunk::Zeros(..) => { + Zeros(..) => { for word in words { if *word != 0 { changed = true; @@ -859,8 +923,8 @@ impl<T: Idx> BitRelations<ChunkedBitSet<T>> for BitSet<T> { } } } - Chunk::Ones(..) => (), - Chunk::Mixed(_, _, data) => { + Ones(..) => (), + Mixed(_, _, data) => { for (i, word) in words.iter_mut().enumerate() { let new_val = *word & data[i]; if new_val != *word { @@ -897,78 +961,44 @@ impl<T> Clone for ChunkedBitSet<T> { } pub struct ChunkedBitIter<'a, T: Idx> { - index: usize, - bitset: &'a ChunkedBitSet<T>, + bit_set: &'a ChunkedBitSet<T>, + + // The index of the current chunk. + chunk_index: usize, + + // The sub-iterator for the current chunk. + chunk_iter: ChunkIter<'a>, } impl<'a, T: Idx> ChunkedBitIter<'a, T> { #[inline] - fn new(bitset: &'a ChunkedBitSet<T>) -> ChunkedBitIter<'a, T> { - ChunkedBitIter { index: 0, bitset } + fn new(bit_set: &'a ChunkedBitSet<T>) -> ChunkedBitIter<'a, T> { + ChunkedBitIter { bit_set, chunk_index: 0, chunk_iter: bit_set.chunk_iter(0) } } } impl<'a, T: Idx> Iterator for ChunkedBitIter<'a, T> { type Item = T; - fn next(&mut self) -> Option<T> { - while self.index < self.bitset.domain_size() { - let elem = T::new(self.index); - let chunk = &self.bitset.chunks[chunk_index(elem)]; - match &chunk { - Zeros(chunk_domain_size) => { - self.index += *chunk_domain_size as usize; - } - Ones(_chunk_domain_size) => { - self.index += 1; - return Some(elem); - } - Mixed(_chunk_domain_size, _, words) => loop { - let elem = T::new(self.index); - self.index += 1; - let (word_index, mask) = chunk_word_index_and_mask(elem); - if (words[word_index] & mask) != 0 { - return Some(elem); - } - if self.index % CHUNK_BITS == 0 { - break; - } - }, - } - } - None - } - fn fold<B, F>(mut self, mut init: B, mut f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - // If `next` has already been called, we may not be at the start of a chunk, so we first - // advance the iterator to the start of the next chunk, before proceeding in chunk sized - // steps. - while self.index % CHUNK_BITS != 0 { - let Some(item) = self.next() else { return init }; - init = f(init, item); - } - let start_chunk = self.index / CHUNK_BITS; - let chunks = &self.bitset.chunks[start_chunk..]; - for (i, chunk) in chunks.iter().enumerate() { - let base = (start_chunk + i) * CHUNK_BITS; - match chunk { - Chunk::Zeros(_) => (), - Chunk::Ones(limit) => { - for j in 0..(*limit as usize) { - init = f(init, T::new(base + j)); + fn next(&mut self) -> Option<T> { + loop { + match &mut self.chunk_iter { + ChunkIter::Zeros => {} + ChunkIter::Ones(iter) => { + if let Some(next) = iter.next() { + return Some(T::new(next + self.chunk_index * CHUNK_BITS)); } } - Chunk::Mixed(_, _, words) => { - init = BitIter::new(&**words).fold(init, |val, mut item: T| { - item.increment_by(base); - f(val, item) - }); + ChunkIter::Mixed(iter) => { + if let Some(next) = iter.next() { + return Some(T::new(next + self.chunk_index * CHUNK_BITS)); + } } + ChunkIter::Finished => return None, } + self.chunk_index += 1; + self.chunk_iter = self.bit_set.chunk_iter(self.chunk_index); } - init } } @@ -1005,7 +1035,7 @@ impl Chunk { } fn new(chunk_domain_size: usize, is_empty: bool) -> Self { - debug_assert!(chunk_domain_size <= CHUNK_BITS); + debug_assert!(0 < chunk_domain_size && chunk_domain_size <= CHUNK_BITS); let chunk_domain_size = chunk_domain_size as ChunkSize; if is_empty { Zeros(chunk_domain_size) } else { Ones(chunk_domain_size) } } @@ -1020,6 +1050,13 @@ impl Chunk { } } +enum ChunkIter<'a> { + Zeros, + Ones(Range<usize>), + Mixed(BitIter<'a, usize>), + Finished, +} + // Applies a function to mutate a bitset, and returns true if any // of the applications return true fn sequential_update<T: Idx>( @@ -1029,289 +1066,12 @@ fn sequential_update<T: Idx>( it.fold(false, |changed, elem| self_update(elem) | changed) } -// Optimization of intersection for SparseBitSet that's generic -// over the RHS -fn sparse_intersect<T: Idx>( - set: &mut SparseBitSet<T>, - other_contains: impl Fn(&T) -> bool, -) -> bool { - let size = set.elems.len(); - set.elems.retain(|elem| other_contains(elem)); - set.elems.len() != size -} - -// Optimization of dense/sparse intersection. The resulting set is -// guaranteed to be at most the size of the sparse set, and hence can be -// represented as a sparse set. Therefore the sparse set is copied and filtered, -// then returned as the new set. -fn dense_sparse_intersect<T: Idx>( - dense: &BitSet<T>, - sparse: &SparseBitSet<T>, -) -> (SparseBitSet<T>, bool) { - let mut sparse_copy = sparse.clone(); - sparse_intersect(&mut sparse_copy, |el| dense.contains(*el)); - let n = sparse_copy.len(); - (sparse_copy, n != dense.count()) -} - -// hybrid REL dense -impl<T: Idx> BitRelations<BitSet<T>> for HybridBitSet<T> { - fn union(&mut self, other: &BitSet<T>) -> bool { - assert_eq!(self.domain_size(), other.domain_size); - match self { - HybridBitSet::Sparse(sparse) => { - // `self` is sparse and `other` is dense. To - // merge them, we have two available strategies: - // * Densify `self` then merge other - // * Clone other then integrate bits from `self` - // The second strategy requires dedicated method - // since the usual `union` returns the wrong - // result. In the dedicated case the computation - // is slightly faster if the bits of the sparse - // bitset map to only few words of the dense - // representation, i.e. indices are near each - // other. - // - // Benchmarking seems to suggest that the second - // option is worth it. - let mut new_dense = other.clone(); - let changed = new_dense.reverse_union_sparse(sparse); - *self = HybridBitSet::Dense(new_dense); - changed - } - - HybridBitSet::Dense(dense) => dense.union(other), - } - } - - fn subtract(&mut self, other: &BitSet<T>) -> bool { - assert_eq!(self.domain_size(), other.domain_size); - match self { - HybridBitSet::Sparse(sparse) => { - sequential_update(|elem| sparse.remove(elem), other.iter()) - } - HybridBitSet::Dense(dense) => dense.subtract(other), - } - } - - fn intersect(&mut self, other: &BitSet<T>) -> bool { - assert_eq!(self.domain_size(), other.domain_size); - match self { - HybridBitSet::Sparse(sparse) => sparse_intersect(sparse, |elem| other.contains(*elem)), - HybridBitSet::Dense(dense) => dense.intersect(other), - } - } -} - -// dense REL hybrid -impl<T: Idx> BitRelations<HybridBitSet<T>> for BitSet<T> { - fn union(&mut self, other: &HybridBitSet<T>) -> bool { - assert_eq!(self.domain_size, other.domain_size()); - match other { - HybridBitSet::Sparse(sparse) => { - sequential_update(|elem| self.insert(elem), sparse.iter().cloned()) - } - HybridBitSet::Dense(dense) => self.union(dense), - } - } - - fn subtract(&mut self, other: &HybridBitSet<T>) -> bool { - assert_eq!(self.domain_size, other.domain_size()); - match other { - HybridBitSet::Sparse(sparse) => { - sequential_update(|elem| self.remove(elem), sparse.iter().cloned()) - } - HybridBitSet::Dense(dense) => self.subtract(dense), - } - } - - fn intersect(&mut self, other: &HybridBitSet<T>) -> bool { - assert_eq!(self.domain_size, other.domain_size()); - match other { - HybridBitSet::Sparse(sparse) => { - let (updated, changed) = dense_sparse_intersect(self, sparse); - - // We can't directly assign the SparseBitSet to the BitSet, and - // doing `*self = updated.to_dense()` would cause a drop / reallocation. Instead, - // the BitSet is cleared and `updated` is copied into `self`. - self.clear(); - for elem in updated.iter() { - self.insert(*elem); - } - changed - } - HybridBitSet::Dense(dense) => self.intersect(dense), - } - } -} - -// hybrid REL hybrid -impl<T: Idx> BitRelations<HybridBitSet<T>> for HybridBitSet<T> { - fn union(&mut self, other: &HybridBitSet<T>) -> bool { - assert_eq!(self.domain_size(), other.domain_size()); - match self { - HybridBitSet::Sparse(_) => { - match other { - HybridBitSet::Sparse(other_sparse) => { - // Both sets are sparse. Add the elements in - // `other_sparse` to `self` one at a time. This - // may or may not cause `self` to be densified. - let mut changed = false; - for elem in other_sparse.iter() { - changed |= self.insert(*elem); - } - changed - } - - HybridBitSet::Dense(other_dense) => self.union(other_dense), - } - } - - HybridBitSet::Dense(self_dense) => self_dense.union(other), - } - } - - fn subtract(&mut self, other: &HybridBitSet<T>) -> bool { - assert_eq!(self.domain_size(), other.domain_size()); - match self { - HybridBitSet::Sparse(self_sparse) => { - sequential_update(|elem| self_sparse.remove(elem), other.iter()) - } - HybridBitSet::Dense(self_dense) => self_dense.subtract(other), - } - } - - fn intersect(&mut self, other: &HybridBitSet<T>) -> bool { - assert_eq!(self.domain_size(), other.domain_size()); - match self { - HybridBitSet::Sparse(self_sparse) => { - sparse_intersect(self_sparse, |elem| other.contains(*elem)) - } - HybridBitSet::Dense(self_dense) => match other { - HybridBitSet::Sparse(other_sparse) => { - let (updated, changed) = dense_sparse_intersect(self_dense, other_sparse); - *self = HybridBitSet::Sparse(updated); - changed - } - HybridBitSet::Dense(other_dense) => self_dense.intersect(other_dense), - }, - } - } -} - -impl<T> Clone for BitSet<T> { - fn clone(&self) -> Self { - BitSet { domain_size: self.domain_size, words: self.words.clone(), marker: PhantomData } - } - - fn clone_from(&mut self, from: &Self) { - self.domain_size = from.domain_size; - self.words.clone_from(&from.words); - } -} - -impl<T: Idx> fmt::Debug for BitSet<T> { - fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { - w.debug_list().entries(self.iter()).finish() - } -} - impl<T: Idx> fmt::Debug for ChunkedBitSet<T> { fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { w.debug_list().entries(self.iter()).finish() } } -impl<T: Idx> ToString for BitSet<T> { - fn to_string(&self) -> String { - let mut result = String::new(); - let mut sep = '['; - - // Note: this is a little endian printout of bytes. - - // i tracks how many bits we have printed so far. - let mut i = 0; - for word in &self.words { - let mut word = *word; - for _ in 0..WORD_BYTES { - // for each byte in `word`: - let remain = self.domain_size - i; - // If less than a byte remains, then mask just that many bits. - let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF }; - assert!(mask <= 0xFF); - let byte = word & mask; - - result.push_str(&format!("{sep}{byte:02x}")); - - if remain <= 8 { - break; - } - word >>= 8; - i += 8; - sep = '-'; - } - sep = '|'; - } - result.push(']'); - - result - } -} - -pub struct BitIter<'a, T: Idx> { - /// A copy of the current word, but with any already-visited bits cleared. - /// (This lets us use `trailing_zeros()` to find the next set bit.) When it - /// is reduced to 0, we move onto the next word. - word: Word, - - /// The offset (measured in bits) of the current word. - offset: usize, - - /// Underlying iterator over the words. - iter: slice::Iter<'a, Word>, - - marker: PhantomData<T>, -} - -impl<'a, T: Idx> BitIter<'a, T> { - #[inline] - fn new(words: &'a [Word]) -> BitIter<'a, T> { - // We initialize `word` and `offset` to degenerate values. On the first - // call to `next()` we will fall through to getting the first word from - // `iter`, which sets `word` to the first word (if there is one) and - // `offset` to 0. Doing it this way saves us from having to maintain - // additional state about whether we have started. - BitIter { - word: 0, - offset: usize::MAX - (WORD_BITS - 1), - iter: words.iter(), - marker: PhantomData, - } - } -} - -impl<'a, T: Idx> Iterator for BitIter<'a, T> { - type Item = T; - fn next(&mut self) -> Option<T> { - loop { - if self.word != 0 { - // Get the position of the next set bit in the current word, - // then clear the bit. - let bit_pos = self.word.trailing_zeros() as usize; - let bit = 1 << bit_pos; - self.word ^= bit; - return Some(T::new(bit_pos + self.offset)); - } - - // Move onto the next word. `wrapping_add()` is needed to handle - // the degenerate initial value given to `offset` in `new()`. - let word = self.iter.next()?; - self.word = *word; - self.offset = self.offset.wrapping_add(WORD_BITS); - } - } -} - #[inline] fn bitwise<Op>(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool where @@ -1349,100 +1109,10 @@ where false } -const SPARSE_MAX: usize = 8; - -/// A fixed-size bitset type with a sparse representation and a maximum of -/// `SPARSE_MAX` elements. The elements are stored as a sorted `ArrayVec` with -/// no duplicates. -/// -/// This type is used by `HybridBitSet`; do not use directly. -#[derive(Clone, Debug)] -pub struct SparseBitSet<T> { - domain_size: usize, - elems: ArrayVec<T, SPARSE_MAX>, -} - -impl<T: Idx> SparseBitSet<T> { - fn new_empty(domain_size: usize) -> Self { - SparseBitSet { domain_size, elems: ArrayVec::new() } - } - - fn len(&self) -> usize { - self.elems.len() - } - - fn is_empty(&self) -> bool { - self.elems.len() == 0 - } - - fn contains(&self, elem: T) -> bool { - assert!(elem.index() < self.domain_size); - self.elems.contains(&elem) - } - - fn insert(&mut self, elem: T) -> bool { - assert!(elem.index() < self.domain_size); - let changed = if let Some(i) = self.elems.iter().position(|&e| e.index() >= elem.index()) { - if self.elems[i] == elem { - // `elem` is already in the set. - false - } else { - // `elem` is smaller than one or more existing elements. - self.elems.insert(i, elem); - true - } - } else { - // `elem` is larger than all existing elements. - self.elems.push(elem); - true - }; - assert!(self.len() <= SPARSE_MAX); - changed - } - - fn remove(&mut self, elem: T) -> bool { - assert!(elem.index() < self.domain_size); - if let Some(i) = self.elems.iter().position(|&e| e == elem) { - self.elems.remove(i); - true - } else { - false - } - } - - fn to_dense(&self) -> BitSet<T> { - let mut dense = BitSet::new_empty(self.domain_size); - for elem in self.elems.iter() { - dense.insert(*elem); - } - dense - } - - fn iter(&self) -> slice::Iter<'_, T> { - self.elems.iter() - } - - bit_relations_inherent_impls! {} -} - -impl<T: Idx + Ord> SparseBitSet<T> { - pub fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> { - let mut last_leq = None; - for e in self.iter() { - if range.contains(e) { - last_leq = Some(*e); - } - } - last_leq - } -} - -/// A fixed-size bitset type with a hybrid representation: sparse when there -/// are up to a `SPARSE_MAX` elements in the set, but dense when there are more -/// than `SPARSE_MAX`. -/// -/// This type is especially efficient for sets that typically have a small -/// number of elements, but a large `domain_size`, and are cleared frequently. +/// A bitset with a mixed representation, using `BitSet` for small and medium +/// bitsets, and `ChunkedBitSet` for large bitsets, i.e. those with enough bits +/// for at least two chunks. This is a good choice for many bitsets that can +/// have large domain sizes (e.g. 5000+). /// /// `T` is an index type, typically a newtyped `usize` wrapper, but it can also /// just be `usize`. @@ -1450,181 +1120,143 @@ impl<T: Idx + Ord> SparseBitSet<T> { /// All operations that involve an element will panic if the element is equal /// to or greater than the domain size. All operations that involve two bitsets /// will panic if the bitsets have differing domain sizes. -#[derive(Clone)] -pub enum HybridBitSet<T> { - Sparse(SparseBitSet<T>), - Dense(BitSet<T>), +#[derive(PartialEq, Eq)] +pub enum MixedBitSet<T> { + Small(BitSet<T>), + Large(ChunkedBitSet<T>), } -impl<T: Idx> fmt::Debug for HybridBitSet<T> { - fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { +impl<T> MixedBitSet<T> { + pub fn domain_size(&self) -> usize { match self { - Self::Sparse(b) => b.fmt(w), - Self::Dense(b) => b.fmt(w), + MixedBitSet::Small(set) => set.domain_size(), + MixedBitSet::Large(set) => set.domain_size(), } } } -impl<T: Idx> HybridBitSet<T> { - pub fn new_empty(domain_size: usize) -> Self { - HybridBitSet::Sparse(SparseBitSet::new_empty(domain_size)) +impl<T: Idx> MixedBitSet<T> { + #[inline] + pub fn new_empty(domain_size: usize) -> MixedBitSet<T> { + if domain_size <= CHUNK_BITS { + MixedBitSet::Small(BitSet::new_empty(domain_size)) + } else { + MixedBitSet::Large(ChunkedBitSet::new_empty(domain_size)) + } } - pub fn domain_size(&self) -> usize { + #[inline] + pub fn is_empty(&self) -> bool { match self { - HybridBitSet::Sparse(sparse) => sparse.domain_size, - HybridBitSet::Dense(dense) => dense.domain_size, + MixedBitSet::Small(set) => set.is_empty(), + MixedBitSet::Large(set) => set.is_empty(), } } - pub fn clear(&mut self) { - let domain_size = self.domain_size(); - *self = HybridBitSet::new_empty(domain_size); - } - + #[inline] pub fn contains(&self, elem: T) -> bool { match self { - HybridBitSet::Sparse(sparse) => sparse.contains(elem), - HybridBitSet::Dense(dense) => dense.contains(elem), + MixedBitSet::Small(set) => set.contains(elem), + MixedBitSet::Large(set) => set.contains(elem), } } - pub fn superset(&self, other: &HybridBitSet<T>) -> bool { - match (self, other) { - (HybridBitSet::Dense(self_dense), HybridBitSet::Dense(other_dense)) => { - self_dense.superset(other_dense) - } - _ => { - assert!(self.domain_size() == other.domain_size()); - other.iter().all(|elem| self.contains(elem)) - } + #[inline] + pub fn insert(&mut self, elem: T) -> bool { + match self { + MixedBitSet::Small(set) => set.insert(elem), + MixedBitSet::Large(set) => set.insert(elem), } } - pub fn is_empty(&self) -> bool { + pub fn insert_all(&mut self) { match self { - HybridBitSet::Sparse(sparse) => sparse.is_empty(), - HybridBitSet::Dense(dense) => dense.is_empty(), + MixedBitSet::Small(set) => set.insert_all(), + MixedBitSet::Large(set) => set.insert_all(), } } - /// Returns the previous element present in the bitset from `elem`, - /// inclusively of elem. That is, will return `Some(elem)` if elem is in the - /// bitset. - pub fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> - where - T: Ord, - { + #[inline] + pub fn remove(&mut self, elem: T) -> bool { match self { - HybridBitSet::Sparse(sparse) => sparse.last_set_in(range), - HybridBitSet::Dense(dense) => dense.last_set_in(range), + MixedBitSet::Small(set) => set.remove(elem), + MixedBitSet::Large(set) => set.remove(elem), } } - pub fn insert(&mut self, elem: T) -> bool { - // No need to check `elem` against `self.domain_size` here because all - // the match cases check it, one way or another. + pub fn iter(&self) -> MixedBitIter<'_, T> { match self { - HybridBitSet::Sparse(sparse) if sparse.len() < SPARSE_MAX => { - // The set is sparse and has space for `elem`. - sparse.insert(elem) - } - HybridBitSet::Sparse(sparse) if sparse.contains(elem) => { - // The set is sparse and does not have space for `elem`, but - // that doesn't matter because `elem` is already present. - false - } - HybridBitSet::Sparse(sparse) => { - // The set is sparse and full. Convert to a dense set. - let mut dense = sparse.to_dense(); - let changed = dense.insert(elem); - assert!(changed); - *self = HybridBitSet::Dense(dense); - changed - } - HybridBitSet::Dense(dense) => dense.insert(elem), + MixedBitSet::Small(set) => MixedBitIter::Small(set.iter()), + MixedBitSet::Large(set) => MixedBitIter::Large(set.iter()), } } - pub fn insert_range(&mut self, elems: impl RangeBounds<T>) { - // No need to check `elem` against `self.domain_size` here because all - // the match cases check it, one way or another. - let start = match elems.start_bound().cloned() { - Bound::Included(start) => start.index(), - Bound::Excluded(start) => start.index() + 1, - Bound::Unbounded => 0, - }; - let end = match elems.end_bound().cloned() { - Bound::Included(end) => end.index() + 1, - Bound::Excluded(end) => end.index(), - Bound::Unbounded => self.domain_size() - 1, - }; - let Some(len) = end.checked_sub(start) else { return }; + bit_relations_inherent_impls! {} +} + +impl<T> Clone for MixedBitSet<T> { + fn clone(&self) -> Self { match self { - HybridBitSet::Sparse(sparse) if sparse.len() + len < SPARSE_MAX => { - // The set is sparse and has space for `elems`. - for elem in start..end { - sparse.insert(T::new(elem)); - } - } - HybridBitSet::Sparse(sparse) => { - // The set is sparse and full. Convert to a dense set. - let mut dense = sparse.to_dense(); - dense.insert_range(elems); - *self = HybridBitSet::Dense(dense); - } - HybridBitSet::Dense(dense) => dense.insert_range(elems), + MixedBitSet::Small(set) => MixedBitSet::Small(set.clone()), + MixedBitSet::Large(set) => MixedBitSet::Large(set.clone()), } } - pub fn insert_all(&mut self) { - let domain_size = self.domain_size(); - match self { - HybridBitSet::Sparse(_) => { - *self = HybridBitSet::Dense(BitSet::new_filled(domain_size)); - } - HybridBitSet::Dense(dense) => dense.insert_all(), + /// WARNING: this implementation of clone_from may panic if the two + /// bitsets have different domain sizes. This constraint is not inherent to + /// `clone_from`, but it works with the existing call sites and allows a + /// faster implementation, which is important because this function is hot. + fn clone_from(&mut self, from: &Self) { + match (self, from) { + (MixedBitSet::Small(set), MixedBitSet::Small(from)) => set.clone_from(from), + (MixedBitSet::Large(set), MixedBitSet::Large(from)) => set.clone_from(from), + _ => panic!("MixedBitSet size mismatch"), } } +} - pub fn remove(&mut self, elem: T) -> bool { - // Note: we currently don't bother going from Dense back to Sparse. - match self { - HybridBitSet::Sparse(sparse) => sparse.remove(elem), - HybridBitSet::Dense(dense) => dense.remove(elem), +impl<T: Idx> BitRelations<MixedBitSet<T>> for MixedBitSet<T> { + fn union(&mut self, other: &MixedBitSet<T>) -> bool { + match (self, other) { + (MixedBitSet::Small(set), MixedBitSet::Small(other)) => set.union(other), + (MixedBitSet::Large(set), MixedBitSet::Large(other)) => set.union(other), + _ => panic!("MixedBitSet size mismatch"), } } - /// Converts to a dense set, consuming itself in the process. - pub fn to_dense(self) -> BitSet<T> { - match self { - HybridBitSet::Sparse(sparse) => sparse.to_dense(), - HybridBitSet::Dense(dense) => dense, + fn subtract(&mut self, other: &MixedBitSet<T>) -> bool { + match (self, other) { + (MixedBitSet::Small(set), MixedBitSet::Small(other)) => set.subtract(other), + (MixedBitSet::Large(set), MixedBitSet::Large(other)) => set.subtract(other), + _ => panic!("MixedBitSet size mismatch"), } } - pub fn iter(&self) -> HybridIter<'_, T> { + fn intersect(&mut self, _other: &MixedBitSet<T>) -> bool { + unimplemented!("implement if/when necessary"); + } +} + +impl<T: Idx> fmt::Debug for MixedBitSet<T> { + fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - HybridBitSet::Sparse(sparse) => HybridIter::Sparse(sparse.iter()), - HybridBitSet::Dense(dense) => HybridIter::Dense(dense.iter()), + MixedBitSet::Small(set) => set.fmt(w), + MixedBitSet::Large(set) => set.fmt(w), } } - - bit_relations_inherent_impls! {} } -pub enum HybridIter<'a, T: Idx> { - Sparse(slice::Iter<'a, T>), - Dense(BitIter<'a, T>), +pub enum MixedBitIter<'a, T: Idx> { + Small(BitIter<'a, T>), + Large(ChunkedBitIter<'a, T>), } -impl<'a, T: Idx> Iterator for HybridIter<'a, T> { +impl<'a, T: Idx> Iterator for MixedBitIter<'a, T> { type Item = T; - fn next(&mut self) -> Option<T> { match self { - HybridIter::Sparse(sparse) => sparse.next().copied(), - HybridIter::Dense(dense) => dense.next(), + MixedBitIter::Small(iter) => iter.next(), + MixedBitIter::Large(iter) => iter.next(), } } } @@ -1897,7 +1529,7 @@ impl<R: Idx, C: Idx> fmt::Debug for BitMatrix<R, C> { /// sparse representation. /// /// Initially, every row has no explicit representation. If any bit within a -/// row is set, the entire row is instantiated as `Some(<HybridBitSet>)`. +/// row is set, the entire row is instantiated as `Some(<BitSet>)`. /// Furthermore, any previously uninstantiated rows prior to it will be /// instantiated as `None`. Those prior rows may themselves become fully /// instantiated later on if any of their bits are set. @@ -1911,7 +1543,7 @@ where C: Idx, { num_columns: usize, - rows: IndexVec<R, Option<HybridBitSet<C>>>, + rows: IndexVec<R, Option<BitSet<C>>>, } impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { @@ -1920,10 +1552,10 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { Self { num_columns, rows: IndexVec::new() } } - fn ensure_row(&mut self, row: R) -> &mut HybridBitSet<C> { - // Instantiate any missing rows up to and including row `row` with an empty HybridBitSet. - // Then replace row `row` with a full HybridBitSet if necessary. - self.rows.get_or_insert_with(row, || HybridBitSet::new_empty(self.num_columns)) + fn ensure_row(&mut self, row: R) -> &mut BitSet<C> { + // Instantiate any missing rows up to and including row `row` with an empty `BitSet`. + // Then replace row `row` with a full `BitSet` if necessary. + self.rows.get_or_insert_with(row, || BitSet::new_empty(self.num_columns)) } /// Sets the cell at `(row, column)` to true. Put another way, insert @@ -1997,17 +1629,17 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { self.row(row).into_iter().flat_map(|r| r.iter()) } - pub fn row(&self, row: R) -> Option<&HybridBitSet<C>> { + pub fn row(&self, row: R) -> Option<&BitSet<C>> { self.rows.get(row)?.as_ref() } /// Intersects `row` with `set`. `set` can be either `BitSet` or - /// `HybridBitSet`. Has no effect if `row` does not exist. + /// `ChunkedBitSet`. Has no effect if `row` does not exist. /// /// Returns true if the row was changed. pub fn intersect_row<Set>(&mut self, row: R, set: &Set) -> bool where - HybridBitSet<C>: BitRelations<Set>, + BitSet<C>: BitRelations<Set>, { match self.rows.get_mut(row) { Some(Some(row)) => row.intersect(set), @@ -2016,12 +1648,12 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { } /// Subtracts `set` from `row`. `set` can be either `BitSet` or - /// `HybridBitSet`. Has no effect if `row` does not exist. + /// `ChunkedBitSet`. Has no effect if `row` does not exist. /// /// Returns true if the row was changed. pub fn subtract_row<Set>(&mut self, row: R, set: &Set) -> bool where - HybridBitSet<C>: BitRelations<Set>, + BitSet<C>: BitRelations<Set>, { match self.rows.get_mut(row) { Some(Some(row)) => row.subtract(set), @@ -2030,12 +1662,12 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { } /// Unions `row` with `set`. `set` can be either `BitSet` or - /// `HybridBitSet`. + /// `ChunkedBitSet`. /// /// Returns true if the row was changed. pub fn union_row<Set>(&mut self, row: R, set: &Set) -> bool where - HybridBitSet<C>: BitRelations<Set>, + BitSet<C>: BitRelations<Set>, { self.ensure_row(row).union(set) } diff --git a/compiler/rustc_index/src/bit_set/tests.rs b/compiler/rustc_index/src/bit_set/tests.rs index 21e681d63f6..f6142323979 100644 --- a/compiler/rustc_index/src/bit_set/tests.rs +++ b/compiler/rustc_index/src/bit_set/tests.rs @@ -76,96 +76,6 @@ fn union_two_sets() { } #[test] -fn hybrid_bitset() { - let mut sparse038: HybridBitSet<usize> = HybridBitSet::new_empty(256); - assert!(sparse038.is_empty()); - assert!(sparse038.insert(0)); - assert!(sparse038.insert(1)); - assert!(sparse038.insert(8)); - assert!(sparse038.insert(3)); - assert!(!sparse038.insert(3)); - assert!(sparse038.remove(1)); - assert!(!sparse038.is_empty()); - assert_eq!(sparse038.iter().collect::<Vec<_>>(), [0, 3, 8]); - - for i in 0..256 { - if i == 0 || i == 3 || i == 8 { - assert!(sparse038.contains(i)); - } else { - assert!(!sparse038.contains(i)); - } - } - - let mut sparse01358 = sparse038.clone(); - assert!(sparse01358.insert(1)); - assert!(sparse01358.insert(5)); - assert_eq!(sparse01358.iter().collect::<Vec<_>>(), [0, 1, 3, 5, 8]); - - let mut dense10 = HybridBitSet::new_empty(256); - for i in 0..10 { - assert!(dense10.insert(i)); - } - assert!(!dense10.is_empty()); - assert_eq!(dense10.iter().collect::<Vec<_>>(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - - let mut dense256 = HybridBitSet::new_empty(256); - assert!(dense256.is_empty()); - dense256.insert_all(); - assert!(!dense256.is_empty()); - for i in 0..256 { - assert!(dense256.contains(i)); - } - - assert!(sparse038.superset(&sparse038)); // sparse + sparse (self) - assert!(sparse01358.superset(&sparse038)); // sparse + sparse - assert!(dense10.superset(&sparse038)); // dense + sparse - assert!(dense10.superset(&dense10)); // dense + dense (self) - assert!(dense256.superset(&dense10)); // dense + dense - - let mut hybrid = sparse038.clone(); - assert!(!sparse01358.union(&hybrid)); // no change - assert!(hybrid.union(&sparse01358)); - assert!(hybrid.superset(&sparse01358) && sparse01358.superset(&hybrid)); - assert!(!dense256.union(&dense10)); - - // dense / sparse where dense superset sparse - assert!(!dense10.clone().union(&sparse01358)); - assert!(sparse01358.clone().union(&dense10)); - assert!(dense10.clone().intersect(&sparse01358)); - assert!(!sparse01358.clone().intersect(&dense10)); - assert!(dense10.clone().subtract(&sparse01358)); - assert!(sparse01358.clone().subtract(&dense10)); - - // dense / sparse where sparse superset dense - let dense038 = sparse038.to_dense(); - assert!(!sparse01358.clone().union(&dense038)); - assert!(dense038.clone().union(&sparse01358)); - assert!(sparse01358.clone().intersect(&dense038)); - assert!(!dense038.clone().intersect(&sparse01358)); - assert!(sparse01358.clone().subtract(&dense038)); - assert!(dense038.clone().subtract(&sparse01358)); - - let mut dense = dense10.clone(); - assert!(dense.union(&dense256)); - assert!(dense.superset(&dense256) && dense256.superset(&dense)); - assert!(hybrid.union(&dense256)); - assert!(hybrid.superset(&dense256) && dense256.superset(&hybrid)); - - assert!(!dense10.clone().intersect(&dense256)); - assert!(dense256.clone().intersect(&dense10)); - assert!(dense10.clone().subtract(&dense256)); - assert!(dense256.clone().subtract(&dense10)); - - assert_eq!(dense256.iter().count(), 256); - let mut dense0 = dense256; - for i in 0..256 { - assert!(dense0.remove(i)); - } - assert!(!dense0.remove(0)); - assert!(dense0.is_empty()); -} - -#[test] fn chunked_bitset() { let mut b0 = ChunkedBitSet::<usize>::new_empty(0); let b0b = b0.clone(); @@ -593,15 +503,15 @@ fn sparse_matrix_operations() { matrix.insert(2, 99); matrix.insert(4, 0); - let mut disjoint: HybridBitSet<usize> = HybridBitSet::new_empty(100); + let mut disjoint: BitSet<usize> = BitSet::new_empty(100); disjoint.insert(33); - let mut superset = HybridBitSet::new_empty(100); + let mut superset = BitSet::new_empty(100); superset.insert(22); superset.insert(75); superset.insert(33); - let mut subset = HybridBitSet::new_empty(100); + let mut subset = BitSet::new_empty(100); subset.insert(22); // SparseBitMatrix::remove @@ -746,90 +656,6 @@ fn dense_last_set_before() { } } -/// Merge dense hybrid set into empty sparse hybrid set. -#[bench] -fn union_hybrid_sparse_empty_to_dense(b: &mut Bencher) { - let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(256); - for i in 0..10 { - assert!(pre_dense.insert(i)); - } - let pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(256); - b.iter(|| { - let dense = pre_dense.clone(); - let mut sparse = pre_sparse.clone(); - sparse.union(&dense); - }) -} - -/// Merge dense hybrid set into full hybrid set with same indices. -#[bench] -fn union_hybrid_sparse_full_to_dense(b: &mut Bencher) { - let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(256); - for i in 0..10 { - assert!(pre_dense.insert(i)); - } - let mut pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(256); - for i in 0..SPARSE_MAX { - assert!(pre_sparse.insert(i)); - } - b.iter(|| { - let dense = pre_dense.clone(); - let mut sparse = pre_sparse.clone(); - sparse.union(&dense); - }) -} - -/// Merge dense hybrid set into full hybrid set with indices over the whole domain. -#[bench] -fn union_hybrid_sparse_domain_to_dense(b: &mut Bencher) { - let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX * 64); - for i in 0..10 { - assert!(pre_dense.insert(i)); - } - let mut pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX * 64); - for i in 0..SPARSE_MAX { - assert!(pre_sparse.insert(i * 64)); - } - b.iter(|| { - let dense = pre_dense.clone(); - let mut sparse = pre_sparse.clone(); - sparse.union(&dense); - }) -} - -/// Merge dense hybrid set into empty hybrid set where the domain is very small. -#[bench] -fn union_hybrid_sparse_empty_small_domain(b: &mut Bencher) { - let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX); - for i in 0..SPARSE_MAX { - assert!(pre_dense.insert(i)); - } - let pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX); - b.iter(|| { - let dense = pre_dense.clone(); - let mut sparse = pre_sparse.clone(); - sparse.union(&dense); - }) -} - -/// Merge dense hybrid set into full hybrid set where the domain is very small. -#[bench] -fn union_hybrid_sparse_full_small_domain(b: &mut Bencher) { - let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX); - for i in 0..SPARSE_MAX { - assert!(pre_dense.insert(i)); - } - let mut pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX); - for i in 0..SPARSE_MAX { - assert!(pre_sparse.insert(i)); - } - b.iter(|| { - let dense = pre_dense.clone(); - let mut sparse = pre_sparse.clone(); - sparse.union(&dense); - }) -} - #[bench] fn bench_insert(b: &mut Bencher) { let mut bs = BitSet::new_filled(99999usize); diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 1968ed34752..5fc9b679c8a 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -7,7 +7,7 @@ use rustc_middle::ty::relate::combine::PredicateEmittingRelation; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::{DUMMY_SP, ErrorGuaranteed}; -use super::{BoundRegionConversionTime, InferCtxt, SubregionOrigin}; +use super::{BoundRegionConversionTime, InferCtxt, RegionVariableOrigin, SubregionOrigin}; impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { type Interner = TyCtxt<'tcx>; @@ -87,6 +87,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.inner.borrow_mut().unwrap_region_constraints().opportunistic_resolve_var(self.tcx, vid) } + fn next_region_infer(&self) -> ty::Region<'tcx> { + self.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP)) + } + fn next_ty_infer(&self) -> Ty<'tcx> { self.next_ty_var(DUMMY_SP) } diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 6af49accc84..e454a88e847 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -9,7 +9,7 @@ use rustc_data_structures::graph::implementation::{ use rustc_data_structures::intern::Interned; use rustc_data_structures::unord::UnordSet; use rustc_index::{IndexSlice, IndexVec}; -use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::fold::{TypeFoldable, fold_regions}; use rustc_middle::ty::{ self, ReBound, ReEarlyParam, ReErased, ReError, ReLateParam, RePlaceholder, ReStatic, ReVar, Region, RegionVid, Ty, TyCtxt, @@ -974,7 +974,7 @@ impl<'tcx> LexicalRegionResolutions<'tcx> { where T: TypeFoldable<TyCtxt<'tcx>>, { - tcx.fold_regions(value, |r, _db| self.resolve_region(tcx, r)) + fold_regions(tcx, value, |r, _db| self.resolve_region(tcx, r)) } fn value(&self, rid: RegionVid) -> &VarValue<'tcx> { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 555c1022a8a..544f941dda5 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -33,7 +33,7 @@ use rustc_middle::traits::select; pub use rustc_middle::ty::IntVarValue; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{ - BoundVarReplacerDelegate, TypeFoldable, TypeFolder, TypeSuperFoldable, + BoundVarReplacerDelegate, TypeFoldable, TypeFolder, TypeSuperFoldable, fold_regions, }; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{ @@ -990,11 +990,17 @@ impl<'tcx> InferCtxt<'tcx> { #[inline(always)] pub fn can_define_opaque_ty(&self, id: impl Into<DefId>) -> bool { + debug_assert!(!self.next_trait_solver()); match self.typing_mode() { TypingMode::Analysis { defining_opaque_types } => { id.into().as_local().is_some_and(|def_id| defining_opaque_types.contains(&def_id)) } - TypingMode::Coherence | TypingMode::PostAnalysis => false, + // FIXME(#132279): This function is quite weird in post-analysis + // and post-borrowck analysis mode. We may need to modify its uses + // to support PostBorrowckAnalysis in the old solver as well. + TypingMode::Coherence + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => false, } } @@ -1165,7 +1171,7 @@ impl<'tcx> InferCtxt<'tcx> { } if value.has_infer_regions() { let guar = self.dcx().delayed_bug(format!("`{value:?}` is not fully resolved")); - Ok(self.tcx.fold_regions(value, |re, _| { + Ok(fold_regions(self.tcx, value, |re, _| { if re.is_var() { ty::Region::new_error(self.tcx, guar) } else { re } })) } else { @@ -1276,7 +1282,6 @@ impl<'tcx> InferCtxt<'tcx> { /// using canonicalization or carrying this inference context around. pub fn typing_env(&self, param_env: ty::ParamEnv<'tcx>) -> ty::TypingEnv<'tcx> { let typing_mode = match self.typing_mode() { - ty::TypingMode::Coherence => ty::TypingMode::Coherence, // FIXME(#132279): This erases the `defining_opaque_types` as it isn't possible // to handle them without proper canonicalization. This means we may cause cycle // errors and fail to reveal opaques while inside of bodies. We should rename this @@ -1284,7 +1289,9 @@ impl<'tcx> InferCtxt<'tcx> { ty::TypingMode::Analysis { defining_opaque_types: _ } => { TypingMode::non_body_analysis() } - ty::TypingMode::PostAnalysis => ty::TypingMode::PostAnalysis, + mode @ (ty::TypingMode::Coherence + | ty::TypingMode::PostBorrowckAnalysis { .. } + | ty::TypingMode::PostAnalysis) => mode, }; ty::TypingEnv { typing_mode, param_env } } diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index a608ea1ad57..b64686afd23 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -98,6 +98,7 @@ impl<'tcx> InferCtxt<'tcx> { span: Span, param_env: ty::ParamEnv<'tcx>, ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, TypeError<'tcx>> { + debug_assert!(!self.next_trait_solver()); let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() { ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) if def_id.is_local() => { let def_id = def_id.expect_local(); @@ -546,7 +547,9 @@ impl<'tcx> InferCtxt<'tcx> { ); } } - ty::TypingMode::PostAnalysis => bug!("insert hidden type post-analysis"), + mode @ (ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => { + bug!("insert hidden type in {mode:?}") + } } Ok(()) diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index ba1516655b0..51282b900ed 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -84,6 +84,8 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { self.collect_remaining_errors(infcx) } + fn has_pending_obligations(&self) -> bool; + fn pending_obligations(&self) -> PredicateObligations<'tcx>; /// Among all pending obligations, collect those are stalled on a inference variable which has diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 3920d3077d3..07ae24ee6d3 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -5,9 +5,9 @@ use std::sync::Arc; use rustc_ast::{LitKind, MetaItemKind, token}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::jobserver; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::Lrc; -use rustc_data_structures::{defer, jobserver}; use rustc_errors::registry::Registry; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; use rustc_lint::LintStore; @@ -441,7 +441,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se temps_dir, }, bundle, - config.registry.clone(), + config.registry, locale_resources, config.lint_caps, target, @@ -492,32 +492,34 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se // There are two paths out of `f`. // - Normal exit. - // - Panic, e.g. triggered by `abort_if_errors`. + // - Panic, e.g. triggered by `abort_if_errors` or a fatal error. // // We must run `finish_diagnostics` in both cases. - let res = { - // If `f` panics, `finish_diagnostics` will run during - // unwinding because of the `defer`. - let sess_abort_guard = defer(|| { - compiler.sess.finish_diagnostics(&config.registry); - }); - - let res = f(&compiler); - - // If `f` doesn't panic, `finish_diagnostics` will run - // normally when `sess_abort_guard` is dropped. - drop(sess_abort_guard); - - // If error diagnostics have been emitted, we can't return an - // error directly, because the return type of this function - // is `R`, not `Result<R, E>`. But we need to communicate the - // errors' existence to the caller, otherwise the caller might - // mistakenly think that no errors occurred and return a zero - // exit code. So we abort (panic) instead, similar to if `f` - // had panicked. + let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(&compiler))); + + compiler.sess.finish_diagnostics(); + + // If error diagnostics have been emitted, we can't return an + // error directly, because the return type of this function + // is `R`, not `Result<R, E>`. But we need to communicate the + // errors' existence to the caller, otherwise the caller might + // mistakenly think that no errors occurred and return a zero + // exit code. So we abort (panic) instead, similar to if `f` + // had panicked. + if res.is_ok() { compiler.sess.dcx().abort_if_errors(); + } + + // Also make sure to flush delayed bugs as if we panicked, the + // bugs would be flushed by the Drop impl of DiagCtxt while + // unwinding, which would result in an abort with + // "panic in a destructor during cleanup". + compiler.sess.dcx().flush_delayed(); - res + let res = match res { + Ok(res) => res, + // Resume unwinding if a panic happened. + Err(err) => std::panic::resume_unwind(err), }; let prof = compiler.sess.prof.clone(); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 43a98782016..62f35333015 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -33,15 +33,15 @@ use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_ use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; use rustc_span::symbol::{Symbol, sym}; -use rustc_span::{FileName, SourceFileHash, SourceFileHashAlgorithm}; +use rustc_span::{ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm}; use rustc_target::spec::PanicStrategy; use rustc_trait_selection::traits; use tracing::{info, instrument}; -use crate::interface::{Compiler, Result}; +use crate::interface::Compiler; use crate::{errors, proc_macro_decls, util}; -pub(crate) fn parse<'a>(sess: &'a Session) -> Result<ast::Crate> { +pub(crate) fn parse<'a>(sess: &'a Session) -> ast::Crate { let krate = sess .time("parse_crate", || { let mut parser = unwrap_or_emit_fatal(match &sess.io.input { @@ -52,17 +52,16 @@ pub(crate) fn parse<'a>(sess: &'a Session) -> Result<ast::Crate> { }); parser.parse_crate_mod() }) - .map_err(|parse_error| parse_error.emit())?; - - if let Some(ref s) = sess.opts.unstable_opts.show_span { - rustc_ast_passes::show_span::run(sess.dcx(), s, &krate); - } + .unwrap_or_else(|parse_error| { + let guar: ErrorGuaranteed = parse_error.emit(); + guar.raise_fatal(); + }); if sess.opts.unstable_opts.input_stats { input_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS", "ast-stats-1"); } - Ok(krate) + krate } fn pre_expansion_lint<'a>( @@ -689,10 +688,12 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| { rustc_const_eval::provide(providers); rustc_middle::hir::provide(providers); rustc_borrowck::provide(providers); + rustc_incremental::provide(providers); rustc_mir_build::provide(providers); rustc_mir_transform::provide(providers); rustc_monomorphize::provide(providers); rustc_privacy::provide(providers); + rustc_query_impl::provide(providers); rustc_resolve::provide(providers); rustc_hir_analysis::provide(providers); rustc_hir_typeck::provide(providers); @@ -714,7 +715,7 @@ pub(crate) fn create_global_ctxt<'tcx>( gcx_cell: &'tcx OnceLock<GlobalCtxt<'tcx>>, arena: &'tcx WorkerLocal<Arena<'tcx>>, hir_arena: &'tcx WorkerLocal<rustc_hir::Arena<'tcx>>, -) -> Result<&'tcx GlobalCtxt<'tcx>> { +) -> &'tcx GlobalCtxt<'tcx> { let sess = &compiler.sess; rustc_builtin_macros::cmdline_attrs::inject( @@ -735,7 +736,7 @@ pub(crate) fn create_global_ctxt<'tcx>( sess.cfg_version, ); let outputs = util::build_output_filenames(&pre_configured_attrs, sess); - let dep_graph = setup_dep_graph(sess)?; + let dep_graph = setup_dep_graph(sess); let cstore = FreezeLock::new(Box::new(CStore::new(compiler.codegen_backend.metadata_loader())) as _); @@ -798,7 +799,7 @@ pub(crate) fn create_global_ctxt<'tcx>( feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs)))); feed.output_filenames(Arc::new(outputs)); }); - Ok(qcx) + qcx }) } @@ -828,7 +829,6 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { tcx.ensure().check_mod_attrs(module); tcx.ensure().check_mod_naked_functions(module); tcx.ensure().check_mod_unstable_api_usage(module); - tcx.ensure().check_mod_const_bodies(module); }); }, { @@ -879,7 +879,6 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { || tcx.hir().body_const_context(def_id).is_some() { tcx.ensure().mir_drops_elaborated_and_const_checked(def_id); - tcx.ensure().unused_generic_params(ty::InstanceKind::Item(def_id.to_def_id())); } } }); @@ -898,8 +897,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { // If `-Zvalidate-mir` is set, we also want to compute the final MIR for each item // (either its `mir_for_ctfe` or `optimized_mir`) since that helps uncover any bugs // in MIR optimizations that may only be reachable through codegen, or other codepaths - // that requires the optimized/ctfe MIR, such as polymorphization, coroutine bodies, - // or evaluating consts. + // that requires the optimized/ctfe MIR, coroutine bodies, or evaluating consts. if tcx.sess.opts.unstable_opts.validate_mir { sess.time("ensuring_final_MIR_is_computable", || { tcx.hir().par_body_owners(|def_id| { @@ -911,7 +909,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { /// Runs the type-checking, region checking and other miscellaneous analysis /// passes on the crate. -fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { +fn analysis(tcx: TyCtxt<'_>, (): ()) { run_required_analyses(tcx); let sess = tcx.sess; @@ -925,7 +923,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { // But we exclude lint errors from this, because lint errors are typically // less serious and we're more likely to want to continue (#87337). if let Some(guar) = sess.dcx().has_errors_excluding_lint_errors() { - return Err(guar); + guar.raise_fatal(); } sess.time("misc_checking_3", || { @@ -1053,8 +1051,6 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { }) } } - - Ok(()) } /// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used @@ -1096,12 +1092,12 @@ fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) { pub(crate) fn start_codegen<'tcx>( codegen_backend: &dyn CodegenBackend, tcx: TyCtxt<'tcx>, -) -> Result<Box<dyn Any>> { +) -> Box<dyn Any> { // Don't do code generation if there were any errors. Likewise if // there were any delayed bugs, because codegen will likely cause // more ICEs, obscuring the original problem. if let Some(guar) = tcx.sess.dcx().has_errors_or_delayed_bugs() { - return Err(guar); + guar.raise_fatal(); } // Hook for UI tests. @@ -1129,7 +1125,7 @@ pub(crate) fn start_codegen<'tcx>( } } - Ok(codegen) + codegen } fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit { diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index b6837ec764f..7e3a1332630 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -12,13 +12,12 @@ use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepGraph; use rustc_middle::ty::{GlobalCtxt, TyCtxt}; -use rustc_serialize::opaque::FileEncodeResult; use rustc_session::Session; use rustc_session::config::{self, OutputFilenames, OutputType}; use crate::errors::FailedWritingFile; -use crate::interface::{Compiler, Result}; -use crate::{errors, passes}; +use crate::interface::Compiler; +use crate::passes; /// Represent the result of a query. /// @@ -28,19 +27,17 @@ use crate::{errors, passes}; /// [`compute`]: Self::compute pub struct Query<T> { /// `None` means no value has been computed yet. - result: RefCell<Option<Result<Steal<T>>>>, + result: RefCell<Option<Steal<T>>>, } impl<T> Query<T> { - fn compute<F: FnOnce() -> Result<T>>(&self, f: F) -> Result<QueryResult<'_, T>> { - RefMut::filter_map( + fn compute<F: FnOnce() -> T>(&self, f: F) -> QueryResult<'_, T> { + QueryResult(RefMut::map( self.result.borrow_mut(), - |r: &mut Option<Result<Steal<T>>>| -> Option<&mut Steal<T>> { - r.get_or_insert_with(|| f().map(Steal::new)).as_mut().ok() + |r: &mut Option<Steal<T>>| -> &mut Steal<T> { + r.get_or_insert_with(|| Steal::new(f())) }, - ) - .map_err(|r| *r.as_ref().unwrap().as_ref().map(|_| ()).unwrap_err()) - .map(QueryResult) + )) } } @@ -62,7 +59,7 @@ impl<'a, T> std::ops::DerefMut for QueryResult<'a, T> { impl<'a, 'tcx> QueryResult<'a, &'tcx GlobalCtxt<'tcx>> { pub fn enter<T>(&mut self, f: impl FnOnce(TyCtxt<'tcx>) -> T) -> T { - (*self.0).get_mut().enter(f) + (*self.0).borrow().enter(f) } } @@ -90,17 +87,19 @@ impl<'tcx> Queries<'tcx> { } } - pub fn finish(&self) -> FileEncodeResult { - if let Some(gcx) = self.gcx_cell.get() { gcx.finish() } else { Ok(0) } + pub fn finish(&'tcx self) { + if let Some(gcx) = self.gcx_cell.get() { + gcx.finish(); + } } - pub fn parse(&self) -> Result<QueryResult<'_, ast::Crate>> { + pub fn parse(&self) -> QueryResult<'_, ast::Crate> { self.parse.compute(|| passes::parse(&self.compiler.sess)) } - pub fn global_ctxt(&'tcx self) -> Result<QueryResult<'tcx, &'tcx GlobalCtxt<'tcx>>> { + pub fn global_ctxt(&'tcx self) -> QueryResult<'tcx, &'tcx GlobalCtxt<'tcx>> { self.gcx.compute(|| { - let krate = self.parse()?.steal(); + let krate = self.parse().steal(); passes::create_global_ctxt( self.compiler, @@ -125,8 +124,8 @@ impl Linker { pub fn codegen_and_build_linker( tcx: TyCtxt<'_>, codegen_backend: &dyn CodegenBackend, - ) -> Result<Linker> { - let ongoing_codegen = passes::start_codegen(codegen_backend, tcx)?; + ) -> Linker { + let ongoing_codegen = passes::start_codegen(codegen_backend, tcx); // This must run after monomorphization so that all generic types // have been instantiated. @@ -140,7 +139,7 @@ impl Linker { tcx.sess.code_stats.print_vtable_sizes(crate_name); } - Ok(Linker { + Linker { dep_graph: tcx.dep_graph.clone(), output_filenames: Arc::clone(tcx.output_filenames(())), crate_hash: if tcx.needs_crate_hash() { @@ -149,16 +148,17 @@ impl Linker { None }, ongoing_codegen, - }) + } } - pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) -> Result<()> { - let (codegen_results, work_products) = - codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames); + pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) { + let (codegen_results, work_products) = sess.time("finish_ongoing_codegen", || { + codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames) + }); - if let Some(guar) = sess.dcx().has_errors() { - return Err(guar); - } + sess.dcx().abort_if_errors(); + + let _timer = sess.timer("link"); sess.time("serialize_work_products", || { rustc_incremental::save_work_product_index(sess, &self.dep_graph, work_products) @@ -177,7 +177,7 @@ impl Linker { .keys() .any(|&i| i == OutputType::Exe || i == OutputType::Metadata) { - return Ok(()); + return; } if sess.opts.unstable_opts.no_link { @@ -188,10 +188,10 @@ impl Linker { &codegen_results, &*self.output_filenames, ) - .map_err(|error| { + .unwrap_or_else(|error| { sess.dcx().emit_fatal(FailedWritingFile { path: &rlink_file, error }) - })?; - return Ok(()); + }); + return; } let _timer = sess.prof.verbose_generic_activity("link_crate"); @@ -209,29 +209,10 @@ impl Compiler { let queries = Queries::new(self); let ret = f(&queries); - // NOTE: intentionally does not compute the global context if it hasn't been built yet, - // since that likely means there was a parse error. - if let Some(Ok(gcx)) = &mut *queries.gcx.result.borrow_mut() { - let gcx = gcx.get_mut(); - // We assume that no queries are run past here. If there are new queries - // after this point, they'll show up as "<unknown>" in self-profiling data. - { - let _prof_timer = - queries.compiler.sess.prof.generic_activity("self_profile_alloc_query_strings"); - gcx.enter(rustc_query_impl::alloc_self_profile_query_strings); - } - - self.sess.time("serialize_dep_graph", || gcx.enter(rustc_incremental::save_dep_graph)); - - gcx.enter(rustc_query_impl::query_key_hash_verify_all); - } - // The timer's lifetime spans the dropping of `queries`, which contains // the global context. _timer = self.sess.timer("free_global_ctxt"); - if let Err((path, error)) = queries.finish() { - self.sess.dcx().emit_fatal(errors::FailedWritingFile { path: &path, error }); - } + queries.finish(); ret } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index e48c4d46b59..e76e9ca9f85 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -712,7 +712,7 @@ fn test_unstable_options_tracking_hash() { untracked!(no_analysis, true); untracked!(no_leak_check, true); untracked!(no_parallel_backend, true); - untracked!(parse_only, true); + untracked!(parse_crate_root_only, true); // `pre_link_arg` is omitted because it just forwards to `pre_link_args`. untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]); untracked!(print_codegen_stats, true); @@ -771,6 +771,7 @@ fn test_unstable_options_tracking_hash() { tracked!(crate_attr, vec!["abc".to_string()]); tracked!(cross_crate_inline_threshold, InliningThreshold::Always); tracked!(debug_info_for_profiling, true); + tracked!(debug_info_type_line_numbers, true); tracked!(default_visibility, Some(rustc_target::spec::SymbolVisibility::Hidden)); tracked!(dep_info_omit_d_target, true); tracked!(direct_access_external_data, Some(true)); @@ -784,7 +785,6 @@ fn test_unstable_options_tracking_hash() { tracked!(flatten_format_args, false); tracked!(fmt_debug, FmtDebug::Shallow); tracked!(force_unstable_if_unmarked, true); - tracked!(fuel, Some(("abc".to_string(), 99))); tracked!(function_return, FunctionReturn::ThunkExtern); tracked!(function_sections, Some(false)); tracked!(human_readable_cgu_names, true); @@ -830,9 +830,9 @@ fn test_unstable_options_tracking_hash() { tracked!(plt, Some(true)); tracked!(polonius, Polonius::Legacy); tracked!(precise_enum_drop_elaboration, false); - tracked!(print_fuel, Some("abc".to_string())); tracked!(profile_sample_use, Some(PathBuf::from("abc"))); tracked!(profiler_runtime, "abc".to_string()); + tracked!(reg_struct_return, true); tracked!(regparm, Some(3)); tracked!(relax_elf_relocations, Some(true)); tracked!(remap_cwd_prefix, Some(PathBuf::from("abc"))); @@ -845,7 +845,6 @@ fn test_unstable_options_tracking_hash() { tracked!(sanitizer_recover, SanitizerSet::ADDRESS); tracked!(saturating_float_casts, Some(true)); tracked!(share_generics, Some(true)); - tracked!(show_span, Some(String::from("abc"))); tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc"))); tracked!(small_data_threshold, Some(16)); tracked!(split_lto_unit, Some(true)); diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index bcb103957ba..aa4abf678b9 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -33,6 +33,7 @@ pub mod unescape; mod tests; use unicode_properties::UnicodeEmoji; +pub use unicode_xid::UNICODE_VERSION as UNICODE_XID_VERSION; use self::LiteralKind::*; use self::TokenKind::*; @@ -566,19 +567,19 @@ impl Cursor<'_> { fn c_or_byte_string( &mut self, - mk_kind: impl FnOnce(bool) -> LiteralKind, - mk_kind_raw: impl FnOnce(Option<u8>) -> LiteralKind, + mk_kind: fn(bool) -> LiteralKind, + mk_kind_raw: fn(Option<u8>) -> LiteralKind, single_quoted: Option<fn(bool) -> LiteralKind>, ) -> TokenKind { match (self.first(), self.second(), single_quoted) { - ('\'', _, Some(mk_kind)) => { + ('\'', _, Some(single_quoted)) => { self.bump(); let terminated = self.single_quoted_string(); let suffix_start = self.pos_within_token(); if terminated { self.eat_literal_suffix(); } - let kind = mk_kind(terminated); + let kind = single_quoted(terminated); Literal { kind, suffix_start } } ('"', _, _) => { @@ -707,17 +708,7 @@ impl Cursor<'_> { self.bump(); self.bump(); self.eat_while(is_id_continue); - match self.first() { - '\'' => { - // Check if after skipping literal contents we've met a closing - // single quote (which means that user attempted to create a - // string with single quotes). - self.bump(); - let kind = Char { terminated: true }; - return Literal { kind, suffix_start: self.pos_within_token() }; - } - _ => return RawLifetime, - } + return RawLifetime; } // Either a lifetime or a character literal with diff --git a/compiler/rustc_lexer/src/tests.rs b/compiler/rustc_lexer/src/tests.rs index 556bbf1f518..db7225fc2a8 100644 --- a/compiler/rustc_lexer/src/tests.rs +++ b/compiler/rustc_lexer/src/tests.rs @@ -77,61 +77,51 @@ fn test_too_many_hashes() { check_raw_str(&s2, Err(RawStrError::TooManyDelimiters { found: u32::from(max_count) + 1 })); } +// https://github.com/rust-lang/rust/issues/70528 #[test] fn test_valid_shebang() { - // https://github.com/rust-lang/rust/issues/70528 - let input = "#!/usr/bin/rustrun\nlet x = 5;"; - assert_eq!(strip_shebang(input), Some(18)); -} + let input = "#!/bin/bash"; + assert_eq!(strip_shebang(input), Some(input.len())); -#[test] -fn test_invalid_shebang_valid_rust_syntax() { - // https://github.com/rust-lang/rust/issues/70528 - let input = "#! [bad_attribute]"; + let input = "#![attribute]"; assert_eq!(strip_shebang(input), None); -} -#[test] -fn test_shebang_second_line() { - // Because shebangs are interpreted by the kernel, they must be on the first line - let input = "\n#!/bin/bash"; + let input = "#! /bin/bash"; + assert_eq!(strip_shebang(input), Some(input.len())); + + let input = "#! [attribute]"; assert_eq!(strip_shebang(input), None); -} -#[test] -fn test_shebang_space() { - let input = "#! /bin/bash"; + let input = "#! /* blah */ /bin/bash"; assert_eq!(strip_shebang(input), Some(input.len())); -} -#[test] -fn test_shebang_empty_shebang() { - let input = "#! \n[attribute(foo)]"; + let input = "#! /* blah */ [attribute]"; assert_eq!(strip_shebang(input), None); -} -#[test] -fn test_invalid_shebang_comment() { - let input = "#!//bin/ami/a/comment\n["; - assert_eq!(strip_shebang(input), None) -} + let input = "#! // blah\n/bin/bash"; + assert_eq!(strip_shebang(input), Some(10)); // strip up to the newline -#[test] -fn test_invalid_shebang_another_comment() { - let input = "#!/*bin/ami/a/comment*/\n[attribute"; - assert_eq!(strip_shebang(input), None) -} + let input = "#! // blah\n[attribute]"; + assert_eq!(strip_shebang(input), None); -#[test] -fn test_shebang_valid_rust_after() { - let input = "#!/*bin/ami/a/comment*/\npub fn main() {}"; - assert_eq!(strip_shebang(input), Some(23)) -} + let input = "#! /* blah\nblah\nblah */ /bin/bash"; + assert_eq!(strip_shebang(input), Some(10)); -#[test] -fn test_shebang_followed_by_attrib() { - let input = "#!/bin/rust-scripts\n#![allow_unused(true)]"; - assert_eq!(strip_shebang(input), Some(19)); + let input = "#! /* blah\nblah\nblah */ [attribute]"; + assert_eq!(strip_shebang(input), None); + + let input = "#!\n/bin/sh"; + assert_eq!(strip_shebang(input), Some(2)); + + let input = "#!\n[attribute]"; + assert_eq!(strip_shebang(input), None); + + // Because shebangs are interpreted by the kernel, they must be on the first line + let input = "\n#!/bin/bash"; + assert_eq!(strip_shebang(input), None); + + let input = "\n#![attribute]"; + assert_eq!(strip_shebang(input), None); } fn check_lexing(src: &str, expect: Expect) { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 69fd7f2d8b2..49e6b763590 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -450,15 +450,15 @@ lint_invalid_nan_comparisons_eq_ne = incorrect NaN comparison, NaN cannot be dir lint_invalid_nan_comparisons_lt_le_gt_ge = incorrect NaN comparison, NaN is not orderable lint_invalid_reference_casting_assign_to_ref = assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - .label = casting happend here + .label = casting happened here lint_invalid_reference_casting_bigger_layout = casting references to a bigger memory layout than the backing allocation is undefined behavior, even if the reference is unused - .label = casting happend here + .label = casting happened here .alloc = backing allocation comes from here .layout = casting from `{$from_ty}` ({$from_size} bytes) to `{$to_ty}` ({$to_size} bytes) lint_invalid_reference_casting_borrow_as_mut = casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - .label = casting happend here + .label = casting happened here lint_invalid_reference_casting_note_book = for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html> @@ -733,6 +733,9 @@ lint_renamed_lint = lint `{$name}` has been renamed to `{$replace}` lint_requested_level = requested on the command line with `{$level} {$lint_name}` +lint_reserved_multihash = reserved token in Rust 2024 + .suggestion = insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 + lint_reserved_prefix = prefix `{$prefix}` is unknown .label = unknown prefix .suggestion = insert whitespace here to avoid this being parsed as a prefix in Rust 2021 @@ -772,6 +775,9 @@ 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_symbol_intern_string_literal = using `Symbol::intern` on a string literal + .help = consider adding the symbol to `compiler/rustc_span/src/symbol.rs` + 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}` @@ -879,6 +885,12 @@ lint_unnameable_test_items = cannot test inner items lint_unnecessary_qualification = unnecessary qualification .suggestion = remove the unnecessary path segments +lint_unpredictable_fn_pointer_comparisons = function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique + .note_duplicated_fn = the address of the same function can vary between different codegen units + .note_deduplicated_fn = furthermore, different functions could have the same address after being merged together + .note_visit_fn_addr_eq = for more information visit <https://doc.rust-lang.org/nightly/core/ptr/fn.fn_addr_eq.html> + .fn_addr_eq_suggestion = refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint + lint_unqualified_local_imports = `use` of a local item without leading `self::`, `super::`, or `crate::` lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index bda982a3675..ec085198922 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -586,7 +586,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { return; } } - if ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()) { + if cx.type_is_copy_modulo_regions(ty) { return; } if type_implements_negative_copy_modulo_regions(cx.tcx, ty, cx.typing_env()) { @@ -1407,7 +1407,7 @@ declare_lint_pass!(TypeAliasBounds => [TYPE_ALIAS_BOUNDS]); impl TypeAliasBounds { pub(crate) fn affects_object_lifetime_defaults(pred: &hir::WherePredicate<'_>) -> bool { // Bounds of the form `T: 'a` with `T` type param affect object lifetime defaults. - if let hir::WherePredicate::BoundPredicate(pred) = pred + if let hir::WherePredicateKind::BoundPredicate(pred) = pred.kind && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_))) && pred.bound_generic_params.is_empty() // indeed, even if absent from the RHS && pred.bounded_ty.as_generic_param().is_some() @@ -1451,11 +1451,11 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { let mut inline_sugg = Vec::new(); for p in generics.predicates { - let span = p.span(); - if p.in_where_clause() { + let span = p.span; + if p.kind.in_where_clause() { where_spans.push(span); } else { - for b in p.bounds() { + for b in p.kind.bounds() { inline_spans.push(b.span()); } inline_sugg.push((span, String::new())); @@ -2071,7 +2071,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { let num_where_predicates = hir_generics .predicates .iter() - .filter(|predicate| predicate.in_where_clause()) + .filter(|predicate| predicate.kind.in_where_clause()) .count(); let mut bound_count = 0; @@ -2080,8 +2080,8 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { let mut dropped_where_predicate_count = 0; for (i, where_predicate) in hir_generics.predicates.iter().enumerate() { let (relevant_lifetimes, bounds, predicate_span, in_where_clause) = - match where_predicate { - hir::WherePredicate::RegionPredicate(predicate) => { + match where_predicate.kind { + hir::WherePredicateKind::RegionPredicate(predicate) => { if let Some(ResolvedArg::EarlyBound(region_def_id)) = cx.tcx.named_bound_var(predicate.lifetime.hir_id) { @@ -2090,21 +2090,21 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { cx.tcx, // don't warn if the inferred span actually came from the predicate we're looking at // this happens if the type is recursively defined - inferred_outlives - .iter() - .filter(|(_, span)| !predicate.span.contains(*span)), + inferred_outlives.iter().filter(|(_, span)| { + !where_predicate.span.contains(*span) + }), item.owner_id.def_id, region_def_id, ), &predicate.bounds, - predicate.span, + where_predicate.span, predicate.in_where_clause, ) } else { continue; } } - hir::WherePredicate::BoundPredicate(predicate) => { + hir::WherePredicateKind::BoundPredicate(predicate) => { // FIXME we can also infer bounds on associated types, // and should check for them here. match predicate.bounded_ty.kind { @@ -2118,12 +2118,12 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { // don't warn if the inferred span actually came from the predicate we're looking at // this happens if the type is recursively defined inferred_outlives.iter().filter(|(_, span)| { - !predicate.span.contains(*span) + !where_predicate.span.contains(*span) }), index, ), &predicate.bounds, - predicate.span, + where_predicate.span, predicate.origin == PredicateOrigin::WhereClause, ) } @@ -2161,7 +2161,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { } else if i + 1 < num_where_predicates { // If all the bounds on a predicate were inferable and there are // further predicates, we want to eat the trailing comma. - let next_predicate_span = hir_generics.predicates[i + 1].span(); + let next_predicate_span = hir_generics.predicates[i + 1].span; if next_predicate_span.from_expansion() { where_lint_spans.push(predicate_span); } else { diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 7248b192763..44c7888a530 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -710,6 +710,10 @@ impl<'tcx> LateContext<'tcx> { TypingEnv { typing_mode: self.typing_mode(), param_env: self.param_env } } + pub fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool { + self.tcx.type_is_copy_modulo_regions(self.typing_env(), ty) + } + /// Gets the type-checking results for the current body, /// or `None` if outside a body. pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> { diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 565c3c04252..a3731e31c2b 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -176,8 +176,12 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: & lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() } .decorate_lint(diag); } - BuiltinLintDiag::ReservedString(suggestion) => { - lints::ReservedString { suggestion }.decorate_lint(diag); + BuiltinLintDiag::ReservedString { is_string, suggestion } => { + if is_string { + lints::ReservedString { suggestion }.decorate_lint(diag); + } else { + lints::ReservedMultihash { suggestion }.decorate_lint(diag); + } } BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => { lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag); diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs index e3e51ba263d..7e298a9a63c 100644 --- a/compiler/rustc_lint/src/dangling.rs +++ b/compiler/rustc_lint/src/dangling.rs @@ -130,11 +130,11 @@ impl DanglingPointerSearcher<'_, '_> { fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::MethodCall(method, receiver, _args, _span) = expr.kind - && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && cx.tcx.has_attr(fn_id, sym::rustc_as_ptr) && is_temporary_rvalue(receiver) && let ty = cx.typeck_results().expr_ty(receiver) && owns_allocation(cx.tcx, ty) + && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && cx.tcx.has_attr(fn_id, sym::rustc_as_ptr) { // FIXME: use `emit_node_lint` when `#[primary_span]` is added. cx.tcx.emit_node_span_lint( diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs index 8fe86738658..ce23892508b 100644 --- a/compiler/rustc_lint/src/drop_forget_useless.rs +++ b/compiler/rustc_lint/src/drop_forget_useless.rs @@ -144,7 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id) { let arg_ty = cx.typeck_results().expr_ty(arg); - let is_copy = arg_ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()); + let is_copy = cx.type_is_copy_modulo_regions(arg_ty); let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); let let_underscore_ignore_sugg = || { if let Some((_, node)) = cx.tcx.hir().parent_iter(expr.hir_id).nth(0) diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 4f3184f1d7c..a68a2a7f983 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -245,6 +245,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_lifetime(&mut self, lt: &'a ast::Lifetime, _: ast_visit::LifetimeCtxt) { self.check_id(lt.id); + ast_visit::walk_lifetime(self, lt); } fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) { @@ -259,6 +260,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_attribute(&mut self, attr: &'a ast::Attribute) { lint_callback!(self, check_attribute, attr); + ast_visit::walk_attribute(self, attr); } fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) { diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs index 025fd452040..28368e1ab46 100644 --- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs +++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs @@ -9,6 +9,7 @@ use crate::lints::{ use crate::{EarlyContext, EarlyLintPass, LintContext}; declare_lint! { + #[allow(text_direction_codepoint_in_literal)] /// The `text_direction_codepoint_in_literal` lint detects Unicode codepoints that change the /// visual representation of text on screen in a way that does not correspond to their on /// memory representation. diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 0e874669043..2db229ed133 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -103,8 +103,11 @@ fn expr_parent_is_else(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { } fn expr_parent_is_stmt(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { - let Some((_, hir::Node::Stmt(stmt))) = tcx.hir().parent_iter(hir_id).next() else { - return false; + let mut parents = tcx.hir().parent_iter(hir_id); + let stmt = match parents.next() { + Some((_, hir::Node::Stmt(stmt))) => stmt, + Some((_, hir::Node::Block(_) | hir::Node::Arm(_))) => return true, + _ => return false, }; let (hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr)) = stmt.kind else { return false }; expr.hir_id == hir_id diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 38c38b59bc5..482650e04e8 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -17,8 +17,9 @@ use tracing::debug; use crate::lints::{ BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistentDocKeyword, - NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag, TyQualified, - TykindDiag, TykindKind, TypeIrInherentUsage, UntranslatableDiag, + NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag, + SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrInherentUsage, + UntranslatableDiag, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; @@ -242,17 +243,10 @@ fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &Path<'_>) -> Option<String> { } // Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait. Res::SelfTyAlias { alias_to: did, is_trait_impl: false, .. } => { - if let ty::Adt(adt, args) = cx.tcx.type_of(did).instantiate_identity().kind() { - if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(adt.did()) - { - // NOTE: This path is currently unreachable as `Ty<'tcx>` is - // defined as a type alias meaning that `impl<'tcx> Ty<'tcx>` - // is not actually allowed. - // - // I(@lcnr) still kept this branch in so we don't miss this - // if we ever change it in the future. - return Some(format!("{}<{}>", name, args[0])); - } + if let ty::Adt(adt, args) = cx.tcx.type_of(did).instantiate_identity().kind() + && let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(adt.did()) + { + return Some(format!("{}<{}>", name, args[0])); } } _ => (), @@ -657,3 +651,33 @@ fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => false, } } + +declare_tool_lint! { + /// The `symbol_intern_string_literal` detects `Symbol::intern` being called on a string literal + pub rustc::SYMBOL_INTERN_STRING_LITERAL, + // rustc_driver crates out of the compiler can't/shouldn't add preinterned symbols; + // bootstrap will deny this manually + Allow, + "Forbid uses of string literals in `Symbol::intern`, suggesting preinterning instead", + report_in_external_macro: true +} + +declare_lint_pass!(SymbolInternStringLiteral => [SYMBOL_INTERN_STRING_LITERAL]); + +impl<'tcx> LateLintPass<'tcx> for SymbolInternStringLiteral { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + if let ExprKind::Call(path, [arg]) = expr.kind + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::SymbolIntern, def_id) + && let ExprKind::Lit(kind) = arg.kind + && let rustc_ast::LitKind::Str(_, _) = kind.node + { + cx.emit_span_lint( + SYMBOL_INTERN_STRING_LITERAL, + kind.span, + SymbolInternStringLiteralDiag, + ); + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 4cf5c7b4ff9..a99c94592b3 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -614,6 +614,8 @@ fn register_internals(store: &mut LintStore) { store.register_late_mod_pass(|_| Box::new(PassByValue)); store.register_lints(&SpanUseEqCtxt::lint_vec()); store.register_late_mod_pass(|_| Box::new(SpanUseEqCtxt)); + store.register_lints(&SymbolInternStringLiteral::lint_vec()); + store.register_late_mod_pass(|_| Box::new(SymbolInternStringLiteral)); // FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and // `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and // these lints will trigger all of the time - change this once migration to diagnostic structs diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 352155729e5..20822f23bf1 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -300,7 +300,7 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_> { let affect_object_lifetime_defaults = self .preds .iter() - .filter(|pred| pred.in_where_clause() == self.in_where_clause) + .filter(|pred| pred.kind.in_where_clause() == self.in_where_clause) .any(|pred| TypeAliasBounds::affects_object_lifetime_defaults(pred)); // If there are any shorthand assoc tys, then the bounds can't be removed automatically. @@ -908,6 +908,11 @@ pub(crate) struct QueryUntracked { pub(crate) struct SpanUseEqCtxtDiag; #[derive(LintDiagnostic)] +#[diag(lint_symbol_intern_string_literal)] +#[help] +pub(crate) struct SymbolInternStringLiteralDiag; + +#[derive(LintDiagnostic)] #[diag(lint_tykind_kind)] pub(crate) struct TykindKind { #[suggestion(code = "ty", applicability = "maybe-incorrect")] @@ -1810,6 +1815,42 @@ pub(crate) enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> { }, } +#[derive(LintDiagnostic)] +pub(crate) enum UnpredictableFunctionPointerComparisons<'a> { + #[diag(lint_unpredictable_fn_pointer_comparisons)] + #[note(lint_note_duplicated_fn)] + #[note(lint_note_deduplicated_fn)] + #[note(lint_note_visit_fn_addr_eq)] + Suggestion { + #[subdiagnostic] + sugg: UnpredictableFunctionPointerComparisonsSuggestion<'a>, + }, + #[diag(lint_unpredictable_fn_pointer_comparisons)] + #[note(lint_note_duplicated_fn)] + #[note(lint_note_deduplicated_fn)] + #[note(lint_note_visit_fn_addr_eq)] + Warn, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + lint_fn_addr_eq_suggestion, + style = "verbose", + applicability = "maybe-incorrect" +)] +pub(crate) struct UnpredictableFunctionPointerComparisonsSuggestion<'a> { + pub ne: &'a str, + pub cast_right: String, + pub deref_left: &'a str, + pub deref_right: &'a str, + #[suggestion_part(code = "{ne}std::ptr::fn_addr_eq({deref_left}")] + pub left: Span, + #[suggestion_part(code = ", {deref_right}")] + pub middle: Span, + #[suggestion_part(code = "{cast_right})")] + pub right: Span, +} + pub(crate) struct ImproperCTypes<'a> { pub ty: Ty<'a>, pub desc: &'a str, @@ -3059,3 +3100,10 @@ pub(crate) struct ReservedString { #[suggestion(code = " ", applicability = "machine-applicable")] pub suggestion: Span, } + +#[derive(LintDiagnostic)] +#[diag(lint_reserved_multihash)] +pub(crate) struct ReservedMultihash { + #[suggestion(code = " ", applicability = "machine-applicable")] + pub suggestion: Span, +} diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index b1d7d4ab689..33650be056d 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -23,7 +23,9 @@ use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons, - InvalidNanComparisonsSuggestion, UnusedComparisons, VariantSizeDifferencesDiag, + InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons, + UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, + VariantSizeDifferencesDiag, }; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; @@ -166,6 +168,35 @@ declare_lint! { "detects ambiguous wide pointer comparisons" } +declare_lint! { + /// The `unpredictable_function_pointer_comparisons` lint checks comparison + /// of function pointer as the operands. + /// + /// ### Example + /// + /// ```rust + /// fn a() {} + /// fn b() {} + /// + /// let f: fn() = a; + /// let g: fn() = b; + /// + /// let _ = f == g; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Function pointers comparisons do not produce meaningful result since + /// they are never guaranteed to be unique and could vary between different + /// code generation units. Furthermore, different functions could have the + /// same address after being merged together. + UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS, + Warn, + "detects unpredictable function pointer comparisons" +} + #[derive(Copy, Clone, Default)] pub(crate) struct TypeLimits { /// Id of the last visited negated expression @@ -178,7 +209,8 @@ impl_lint_pass!(TypeLimits => [ UNUSED_COMPARISONS, OVERFLOWING_LITERALS, INVALID_NAN_COMPARISONS, - AMBIGUOUS_WIDE_POINTER_COMPARISONS + AMBIGUOUS_WIDE_POINTER_COMPARISONS, + UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS ]); impl TypeLimits { @@ -255,7 +287,7 @@ fn lint_nan<'tcx>( cx.emit_span_lint(INVALID_NAN_COMPARISONS, e.span, lint); } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] enum ComparisonOp { BinOp(hir::BinOpKind), Other, @@ -383,6 +415,100 @@ fn lint_wide_pointer<'tcx>( ); } +fn lint_fn_pointer<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx hir::Expr<'tcx>, + cmpop: ComparisonOp, + l: &'tcx hir::Expr<'tcx>, + r: &'tcx hir::Expr<'tcx>, +) { + let peel_refs = |mut ty: Ty<'tcx>| -> (Ty<'tcx>, usize) { + let mut refs = 0; + + while let ty::Ref(_, inner_ty, _) = ty.kind() { + ty = *inner_ty; + refs += 1; + } + + (ty, refs) + }; + + // Left and right operands can have borrows, remove them + let l = l.peel_borrows(); + let r = r.peel_borrows(); + + let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else { return }; + let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else { return }; + + // Remove any references as `==` will deref through them (and count the + // number of references removed, for latter). + let (l_ty, l_ty_refs) = peel_refs(l_ty); + let (r_ty, r_ty_refs) = peel_refs(r_ty); + + if !l_ty.is_fn() || !r_ty.is_fn() { + return; + } + + // Let's try to suggest `ptr::fn_addr_eq` if/when possible. + + let is_eq_ne = matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne)); + + if !is_eq_ne { + // Neither `==` nor `!=`, we can't suggest `ptr::fn_addr_eq`, just show the warning. + return cx.emit_span_lint( + UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS, + e.span, + UnpredictableFunctionPointerComparisons::Warn, + ); + } + + let (Some(l_span), Some(r_span)) = + (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span)) + else { + // No appropriate spans for the left and right operands, just show the warning. + return cx.emit_span_lint( + UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS, + e.span, + UnpredictableFunctionPointerComparisons::Warn, + ); + }; + + let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" }; + + // `ptr::fn_addr_eq` only works with raw pointer, deref any references. + let deref_left = &*"*".repeat(l_ty_refs); + let deref_right = &*"*".repeat(r_ty_refs); + + let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo()); + let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo()); + let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi()); + + // We only check for a right cast as `FnDef` == `FnPtr` is not possible, + // only `FnPtr == FnDef` is possible. + let cast_right = if !r_ty.is_fn_ptr() { + let fn_sig = r_ty.fn_sig(cx.tcx); + format!(" as {fn_sig}") + } else { + String::new() + }; + + cx.emit_span_lint( + UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS, + e.span, + UnpredictableFunctionPointerComparisons::Suggestion { + sugg: UnpredictableFunctionPointerComparisonsSuggestion { + ne, + deref_left, + deref_right, + left, + middle, + right, + cast_right, + }, + }, + ); +} + impl<'tcx> LateLintPass<'tcx> for TypeLimits { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) { match e.kind { @@ -399,7 +525,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons); } else { lint_nan(cx, e, binop, l, r); - lint_wide_pointer(cx, e, ComparisonOp::BinOp(binop.node), l, r); + let cmpop = ComparisonOp::BinOp(binop.node); + lint_wide_pointer(cx, e, cmpop, l, r); + lint_fn_pointer(cx, e, cmpop, l, r); } } } @@ -411,6 +539,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { && let Some(cmpop) = diag_item_cmpop(diag_item) => { lint_wide_pointer(cx, e, cmpop, l, r); + lint_fn_pointer(cx, e, cmpop, l, r); } hir::ExprKind::MethodCall(_, l, [r], _) if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) @@ -418,6 +547,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { && let Some(cmpop) = diag_item_cmpop(diag_item) => { lint_wide_pointer(cx, e, cmpop, l, r); + lint_fn_pointer(cx, e, cmpop, l, r); } _ => {} }; diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 5ec920d39f4..9cad5d98562 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1235,7 +1235,7 @@ impl EarlyLintPass for UnusedParens { self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space); }, // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106. - Ident(.., Some(p)) | Box(p) | Deref(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), + Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space), // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342. // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106. Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), @@ -1304,12 +1304,12 @@ impl EarlyLintPass for UnusedParens { } fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) { - use rustc_ast::{WhereBoundPredicate, WherePredicate}; - if let WherePredicate::BoundPredicate(WhereBoundPredicate { + use rustc_ast::{WhereBoundPredicate, WherePredicateKind}; + if let WherePredicateKind::BoundPredicate(WhereBoundPredicate { bounded_ty, bound_generic_params, .. - }) = pred + }) = &pred.kind && let ast::TyKind::Paren(_) = &bounded_ty.kind && bound_generic_params.is_empty() { @@ -1563,7 +1563,7 @@ impl UnusedImportBraces { } rename.unwrap_or(orig_ident).name } - ast::UseTreeKind::Glob => Symbol::intern("*"), + ast::UseTreeKind::Glob => sym::asterisk, ast::UseTreeKind::Nested { .. } => return, }; diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 3b406c7e161..54e927df3c4 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -179,7 +179,7 @@ declare_lint! { Warn, "applying forbid to lint-groups", @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, reference: "issue #81670 <https://github.com/rust-lang/rust/issues/81670>", }; } @@ -3929,6 +3929,7 @@ declare_lint! { } declare_lint! { + #[allow(text_direction_codepoint_in_literal)] /// The `text_direction_codepoint_in_comment` lint detects Unicode codepoints in comments that /// change the visual representation of text on screen in a way that does not correspond to /// their on memory representation. diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index c74fceeedba..eb761bd6475 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -663,8 +663,11 @@ pub enum BuiltinLintDiag { ReservedPrefix(Span, String), /// `'r#` in edition < 2021. RawPrefix(Span), - /// `##` or `#"` is edition < 2024. - ReservedString(Span), + /// `##` or `#"` in edition < 2024. + ReservedString { + is_string: bool, + suggestion: Span, + }, TrailingMacro(bool, Ident), BreakWithLabelAndLoop(Span), UnicodeTextFlow(Span, String), diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 489c911d7ee..20859b167bc 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -552,7 +552,7 @@ static CodeGenFileType fromRust(LLVMRustFileType Type) { extern "C" LLVMRustResult LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, LLVMModuleRef M, const char *Path, const char *DwoPath, - LLVMRustFileType RustFileType) { + LLVMRustFileType RustFileType, bool VerifyIR) { llvm::legacy::PassManager *PM = unwrap<llvm::legacy::PassManager>(PMR); auto FileType = fromRust(RustFileType); @@ -577,10 +577,10 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, return LLVMRustResult::Failure; } auto DBOS = buffer_ostream(DOS); - unwrap(Target)->addPassesToEmitFile(*PM, BOS, &DBOS, FileType, false); + unwrap(Target)->addPassesToEmitFile(*PM, BOS, &DBOS, FileType, !VerifyIR); PM->run(*unwrap(M)); } else { - unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, false); + unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, !VerifyIR); PM->run(*unwrap(M)); } diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index d80aa0cc4f4..6d7d88fa8d7 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -233,9 +233,6 @@ metadata_prev_alloc_error_handler = metadata_prev_global_alloc = previous global allocator defined here -metadata_profiler_builtins_needs_core = - `profiler_builtins` crate (required by compiler options) is not compatible with crate attribute `#![no_core]` - metadata_raw_dylib_no_nul = link name must not contain NUL characters if link kind is `raw-dylib` diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index ebae968d005..29dba2bca61 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -799,20 +799,16 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { self.inject_dependency_if(cnum, "a panic runtime", &|data| data.needs_panic_runtime()); } - fn inject_profiler_runtime(&mut self, krate: &ast::Crate) { - if self.sess.opts.unstable_opts.no_profiler_runtime - || !(self.sess.instrument_coverage() || self.sess.opts.cg.profile_generate.enabled()) - { + fn inject_profiler_runtime(&mut self) { + let needs_profiler_runtime = + self.sess.instrument_coverage() || self.sess.opts.cg.profile_generate.enabled(); + if !needs_profiler_runtime || self.sess.opts.unstable_opts.no_profiler_runtime { return; } info!("loading profiler"); let name = Symbol::intern(&self.sess.opts.unstable_opts.profiler_runtime); - if name == sym::profiler_builtins && attr::contains_name(&krate.attrs, sym::no_core) { - self.dcx().emit_err(errors::ProfilerBuiltinsNeedsCore); - } - let Some(cnum) = self.resolve_crate(name, DUMMY_SP, CrateDepKind::Implicit) else { return; }; @@ -865,8 +861,10 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // First up we check for global allocators. Look at the crate graph here // and see what's a global allocator, including if we ourselves are a // global allocator. - let mut global_allocator = - self.cstore.has_global_allocator.then(|| Symbol::intern("this crate")); + #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + let this_crate = Symbol::intern("this crate"); + + let mut global_allocator = self.cstore.has_global_allocator.then_some(this_crate); for (_, data) in self.cstore.iter_crate_data() { if data.has_global_allocator() { match global_allocator { @@ -880,8 +878,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { } } } - let mut alloc_error_handler = - self.cstore.has_alloc_error_handler.then(|| Symbol::intern("this crate")); + let mut alloc_error_handler = self.cstore.has_alloc_error_handler.then_some(this_crate); for (_, data) in self.cstore.iter_crate_data() { if data.has_alloc_error_handler() { match alloc_error_handler { @@ -1046,7 +1043,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { pub fn postprocess(&mut self, krate: &ast::Crate) { self.inject_forced_externs(); - self.inject_profiler_runtime(krate); + self.inject_profiler_runtime(); self.inject_allocator_crate(krate); self.inject_panic_runtime(krate); @@ -1172,7 +1169,7 @@ fn attempt_load_dylib(path: &Path) -> Result<libloading::Library, libloading::Er // the expected format is lib<name>.a(libname.so) for the actual // dynamic library let library_name = path.file_stem().expect("expect a library name"); - let mut archive_member = OsString::from("a("); + let mut archive_member = std::ffi::OsString::from("a("); archive_member.push(library_name); archive_member.push(".so)"); let new_path = path.with_extension(archive_member); diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index 16684ae6f26..b6c5619ec18 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -340,10 +340,6 @@ pub struct NoPanicStrategy { } #[derive(Diagnostic)] -#[diag(metadata_profiler_builtins_needs_core)] -pub struct ProfilerBuiltinsNeedsCore; - -#[derive(Diagnostic)] #[diag(metadata_not_profiler_runtime)] pub struct NotProfilerRuntime { pub crate_name: Symbol, diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index ace46891f83..8bd2281981b 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -54,9 +54,13 @@ pub fn walk_native_lib_search_dirs<R>( // The targets here should be in sync with `copy_third_party_objects` in bootstrap. // FIXME: implement `-Clink-self-contained=+/-unwind,+/-sanitizers`, move the shipped libunwind // and sanitizers to self-contained directory, and stop adding this search path. + // FIXME: On AIX this also has the side-effect of making the list of library search paths + // non-empty, which is needed or the linker may decide to record the LIBPATH env, if + // defined, as the search path instead of appending the default search paths. if sess.target.vendor == "fortanix" || sess.target.os == "linux" || sess.target.os == "fuchsia" + || sess.target.is_like_aix || sess.target.is_like_osx && !sess.opts.unstable_opts.sanitizer.is_empty() { f(&sess.target_tlib_path.dir, false)?; diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 56beff5aa64..b9586338655 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -872,6 +872,7 @@ impl MetadataBlob { let def_kind = root.tables.def_kind.get(blob, item).unwrap(); let def_key = root.tables.def_keys.get(blob, item).unwrap().decode(blob); + #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] let def_name = if item == CRATE_DEF_INDEX { rustc_span::symbol::kw::Crate } else { @@ -1103,6 +1104,7 @@ impl<'a> CrateMetadataRef<'a> { name: self.item_name(did.index), vis: self.get_visibility(did.index), safety: self.get_safety(did.index), + value: self.get_default_field(did.index), }) .collect(), adt_kind, @@ -1168,6 +1170,10 @@ impl<'a> CrateMetadataRef<'a> { self.root.tables.safety.get(self, id).unwrap_or_else(|| self.missing("safety", id)) } + fn get_default_field(self, id: DefIndex) -> Option<DefId> { + self.root.tables.default_fields.get(self, id).map(|d| d.decode(self)) + } + fn get_trait_item_def_id(self, id: DefIndex) -> Option<DefId> { self.root.tables.trait_item_def_id.get(self, id).map(|d| d.decode_from_cdata(self)) } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index a89096beb8c..f01ad31d0bb 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -269,7 +269,6 @@ provide! { tcx, def_id, other, cdata, lookup_default_body_stability => { table } lookup_deprecation_entry => { table } params_in_repr => { table } - unused_generic_params => { table_direct } def_kind => { cdata.def_kind(def_id.index) } impl_parent => { table } defaultness => { table_direct } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index dd149af5205..5c80d24f502 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1383,6 +1383,31 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let def_id = local_id.to_def_id(); let def_kind = tcx.def_kind(local_id); self.tables.def_kind.set_some(def_id.index, def_kind); + + // The `DefCollector` will sometimes create unnecessary `DefId`s + // for trivial const arguments which are directly lowered to + // `ConstArgKind::Path`. We never actually access this `DefId` + // anywhere so we don't need to encode it for other crates. + if def_kind == DefKind::AnonConst + && match tcx.hir_node_by_def_id(local_id) { + hir::Node::ConstArg(hir::ConstArg { kind, .. }) => match kind { + // Skip encoding defs for these as they should not have had a `DefId` created + hir::ConstArgKind::Path(..) | hir::ConstArgKind::Infer(..) => true, + hir::ConstArgKind::Anon(..) => false, + }, + _ => false, + } + { + continue; + } + + if def_kind == DefKind::Field + && let hir::Node::Field(field) = tcx.hir_node_by_def_id(local_id) + && let Some(anon) = field.default + { + record!(self.tables.default_fields[def_id] <- anon.def_id.to_def_id()); + } + if should_encode_span(def_kind) { let def_span = tcx.def_span(local_id); record!(self.tables.def_span[def_id] <- def_span); @@ -1749,10 +1774,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { { record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); } - - let instance = ty::InstanceKind::Item(def_id.to_def_id()); - let unused = tcx.unused_generic_params(instance); - self.tables.unused_generic_params.set(def_id.local_def_index, unused); } // Encode all the deduced parameter attributes for everything that has MIR, even for items diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index a486ad42482..4961464833a 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -395,7 +395,6 @@ define_tables! { 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>>>, - unused_generic_params: Table<DefIndex, UnusedGenericParams>, // Reexported names are not associated with individual `DefId`s, // e.g. a glob import can introduce a lot of names, all with the same `DefId`. // That's why the encoded list needs to contain `ModChild` structures describing all the names @@ -451,6 +450,7 @@ define_tables! { trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>, trait_item_def_id: Table<DefIndex, RawDefId>, expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>, + default_fields: Table<DefIndex, LazyValue<DefId>>, params_in_repr: Table<DefIndex, LazyValue<BitSet<u32>>>, repr_options: Table<DefIndex, LazyValue<ReprOptions>>, // `def_keys` and `def_path_hashes` represent a lazy version of a diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 9438350ca09..28f406fbc96 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -1,6 +1,5 @@ use rustc_hir::def::CtorOf; use rustc_index::Idx; -use tracing::trace; use crate::rmeta::*; @@ -530,8 +529,6 @@ where { /// Given the metadata, extract out the value at a particular index (if any). pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(&self, metadata: M, i: I) -> T::Value<'tcx> { - trace!("LazyTable::lookup: index={:?} len={:?}", i, self.len); - // Access past the end of the table returns a Default if i.index() >= self.len { return Default::default(); diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 52c3212ab80..5dd85978f00 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -73,6 +73,9 @@ middle_drop_check_overflow = middle_erroneous_constant = erroneous constant encountered +middle_failed_writing_file = + failed to write file {$path}: {$error}" + middle_layout_references_error = the type has an unknown layout diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index 5c2aa0005d4..6300d856393 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -1,5 +1,5 @@ -use std::fmt; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use std::{fmt, io}; use rustc_errors::codes::*; use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage}; @@ -19,6 +19,13 @@ pub struct DropCheckOverflow<'tcx> { } #[derive(Diagnostic)] +#[diag(middle_failed_writing_file)] +pub struct FailedWritingFile<'a> { + pub path: &'a Path, + pub error: io::Error, +} + +#[derive(Diagnostic)] #[diag(middle_opaque_hidden_type_mismatch)] pub struct OpaqueHiddenTypeMismatch<'tcx> { pub self_ty: Ty<'tcx>, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 007e6f46006..0c701c834f2 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -947,8 +947,7 @@ impl<'hir> Map<'hir> { Node::Infer(i) => i.span, Node::LetStmt(local) => local.span, Node::Crate(item) => item.spans.inner_span, - Node::WhereBoundPredicate(pred) => pred.span, - Node::ArrayLenInfer(inf) => inf.span, + Node::WherePredicate(pred) => pred.span, Node::PreciseCapturingNonLifetimeArg(param) => param.ident.span, Node::Synthetic => unreachable!(), Node::Err(span) => span, @@ -1225,8 +1224,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { format!("{id} (generic_param {})", path_str(param.def_id)) } Node::Crate(..) => String::from("(root_crate)"), - Node::WhereBoundPredicate(_) => node_str("where bound predicate"), - Node::ArrayLenInfer(_) => node_str("array len infer"), + Node::WherePredicate(_) => node_str("where predicate"), Node::Synthetic => unreachable!(), Node::Err(_) => node_str("error"), Node::PreciseCapturingNonLifetimeArg(_param) => node_str("parameter"), diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index 742797fdeef..92fa64b0987 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -108,6 +108,19 @@ declare_hooks! { /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we /// can just link to the upstream crate and therefore don't need a mono item. hook should_codegen_locally(instance: crate::ty::Instance<'tcx>) -> bool; + + hook alloc_self_profile_query_strings() -> (); + + /// Saves and writes the DepGraph to the file system. + /// + /// This function saves both the dep-graph and the query result cache, + /// and drops the result cache. + /// + /// This function should only run after all queries have completed. + /// Trying to execute a query afterwards would attempt to read the result cache we just dropped. + hook save_dep_graph() -> (); + + hook query_key_hash_verify_all() -> (); } #[cold] diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 11a4e7f89a7..b7410ca5f18 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -28,7 +28,6 @@ rustc_index::newtype_index! { #[derive(HashStable)] #[encodable] #[orderable] - #[max = 0xFFFF_FFFF] #[debug_format = "CounterId({})"] pub struct CounterId {} } @@ -46,7 +45,6 @@ rustc_index::newtype_index! { #[derive(HashStable)] #[encodable] #[orderable] - #[max = 0xFFFF_FFFF] #[debug_format = "ExpressionId({})"] pub struct ExpressionId {} } diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 509f2667b35..d6f8fed755f 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -643,6 +643,28 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> Ok(()) } + /// Initialize all previously uninitialized bytes in the entire allocation, and set + /// provenance of everything to `Wildcard`. Before calling this, make sure all + /// provenance in this allocation is exposed! + pub fn prepare_for_native_write(&mut self) -> AllocResult { + let full_range = AllocRange { start: Size::ZERO, size: Size::from_bytes(self.len()) }; + // Overwrite uninitialized bytes with 0, to ensure we don't leak whatever their value happens to be. + for chunk in self.init_mask.range_as_init_chunks(full_range) { + if !chunk.is_init() { + let uninit_bytes = &mut self.bytes + [chunk.range().start.bytes_usize()..chunk.range().end.bytes_usize()]; + uninit_bytes.fill(0); + } + } + // Mark everything as initialized now. + self.mark_init(full_range, true); + + // Set provenance of all bytes to wildcard. + self.provenance.write_wildcards(self.len()); + + Ok(()) + } + /// Remove all provenance in the given memory range. pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult { self.provenance.clear(range, cx)?; diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 5c47fc6a399..3a83b184d83 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -195,6 +195,25 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { Ok(()) } + + /// Overwrites all provenance in the allocation with wildcard provenance. + /// + /// Provided for usage in Miri and panics otherwise. + pub fn write_wildcards(&mut self, alloc_size: usize) { + assert!( + Prov::OFFSET_IS_ADDR, + "writing wildcard provenance is not supported when `OFFSET_IS_ADDR` is false" + ); + let wildcard = Prov::WILDCARD.unwrap(); + + // Remove all pointer provenances, then write wildcards into the whole byte range. + self.ptrs.clear(); + let last = Size::from_bytes(alloc_size); + let bytes = self.bytes.get_or_insert_with(Box::default); + for offset in Size::ZERO..last { + bytes.insert(offset, wildcard); + } + } } /// A partial, owned list of provenance to transfer into another allocation. diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 08afa33c6b4..ad5d678178d 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -46,7 +46,7 @@ impl ErrorHandled { pub fn emit_note(&self, tcx: TyCtxt<'_>) { match self { &ErrorHandled::Reported(err, span) => { - if !err.is_tainted_by_errors && !span.is_dummy() { + if !err.allowed_in_infallible && !span.is_dummy() { tcx.dcx().emit_note(error::ErroneousConstant { span }); } } @@ -58,34 +58,26 @@ impl ErrorHandled { #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] pub struct ReportedErrorInfo { error: ErrorGuaranteed, - is_tainted_by_errors: bool, - /// Whether this is the kind of error that can sometimes occur, and sometimes not. - /// Used for resource exhaustion errors. - can_be_spurious: bool, + /// Whether this error is allowed to show up even in otherwise "infallible" promoteds. + /// This is for things like overflows during size computation or resource exhaustion. + allowed_in_infallible: bool, } impl ReportedErrorInfo { #[inline] - pub fn tainted_by_errors(error: ErrorGuaranteed) -> ReportedErrorInfo { - ReportedErrorInfo { is_tainted_by_errors: true, can_be_spurious: false, error } - } - #[inline] - pub fn spurious(error: ErrorGuaranteed) -> ReportedErrorInfo { - ReportedErrorInfo { can_be_spurious: true, is_tainted_by_errors: false, error } + pub fn allowed_in_infallible(error: ErrorGuaranteed) -> ReportedErrorInfo { + ReportedErrorInfo { allowed_in_infallible: true, error } } - pub fn is_tainted_by_errors(&self) -> bool { - self.is_tainted_by_errors - } - pub fn can_be_spurious(&self) -> bool { - self.can_be_spurious + pub fn is_allowed_in_infallible(&self) -> bool { + self.allowed_in_infallible } } impl From<ErrorGuaranteed> for ReportedErrorInfo { #[inline] fn from(error: ErrorGuaranteed) -> ReportedErrorInfo { - ReportedErrorInfo { is_tainted_by_errors: false, can_be_spurious: false, error } + ReportedErrorInfo { allowed_in_infallible: false, error } } } diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index 1d5afe22573..25c7c26ddd9 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -66,6 +66,9 @@ pub trait Provenance: Copy + fmt::Debug + 'static { /// pointer, and implement ptr-to-int transmutation by stripping provenance. const OFFSET_IS_ADDR: bool; + /// If wildcard provenance is implemented, contains the unique, general wildcard provenance variant. + const WILDCARD: Option<Self>; + /// Determines how a pointer should be printed. fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result; @@ -168,6 +171,9 @@ impl Provenance for CtfeProvenance { // so ptr-to-int casts are not possible (since we do not know the global physical offset). const OFFSET_IS_ADDR: bool = false; + // `CtfeProvenance` does not implement wildcard provenance. + const WILDCARD: Option<Self> = None; + fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Print AllocId. fmt::Debug::fmt(&ptr.provenance.alloc_id(), f)?; // propagates `alternate` flag @@ -197,6 +203,9 @@ impl Provenance for AllocId { // so ptr-to-int casts are not possible (since we do not know the global physical offset). const OFFSET_IS_ADDR: bool = false; + // `AllocId` does not implement wildcard provenance. + const WILDCARD: Option<Self> = None; + fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Forward `alternate` flag to `alloc_id` printing. if f.alternate() { diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 244d22d082f..161716610fe 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -111,6 +111,13 @@ impl<'tcx> MonoItem<'tcx> { return InstantiationMode::GloballyShared { may_conflict: false }; } + if let InlineAttr::Never = tcx.codegen_fn_attrs(instance.def_id()).inline + && self.is_generic_fn() + { + // Upgrade inline(never) to a globally shared instance. + return InstantiationMode::GloballyShared { may_conflict: true }; + } + // At this point we don't have explicit linkage and we're an // inlined function. If we're inlining into all CGUs then we'll // be creating a local copy per CGU. diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 86abeb50382..80dfcbf2e69 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -8,7 +8,7 @@ use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::LocalDefId; -use rustc_index::bit_set::BitMatrix; +use rustc_index::bit_set::{BitMatrix, BitSet}; use rustc_index::{Idx, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::Span; @@ -17,6 +17,7 @@ use smallvec::SmallVec; use super::{ConstValue, SourceInfo}; use crate::mir; +use crate::ty::fold::fold_regions; use crate::ty::{self, CoroutineArgsExt, OpaqueHiddenType, Ty, TyCtxt}; rustc_index::newtype_index! { @@ -315,7 +316,7 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> { /// All regions of `ty` must be of kind `ReVar` and must represent /// universal regions *external* to the closure. pub fn bind(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self { - let inner = tcx.fold_regions(ty, |r, depth| match r.kind() { + let inner = fold_regions(tcx, ty, |r, depth| match r.kind() { ty::ReVar(vid) => { let br = ty::BoundRegion { var: ty::BoundVar::new(vid.index()), @@ -334,7 +335,7 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> { tcx: TyCtxt<'tcx>, mut map: impl FnMut(ty::RegionVid) -> ty::Region<'tcx>, ) -> Ty<'tcx> { - tcx.fold_regions(self.inner, |r, depth| match r.kind() { + fold_regions(tcx, self.inner, |r, depth| match r.kind() { ty::ReBound(debruijn, br) => { debug_assert_eq!(debruijn, depth); map(ty::RegionVid::new(br.var.index())) @@ -358,12 +359,22 @@ pub struct DestructuredConstant<'tcx> { /// Used by the `coverage_ids_info` query. #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)] pub struct CoverageIdsInfo { - /// Coverage codegen needs to know the highest counter ID that is ever + pub counters_seen: BitSet<mir::coverage::CounterId>, + pub expressions_seen: BitSet<mir::coverage::ExpressionId>, +} + +impl CoverageIdsInfo { + /// Coverage codegen needs to know how many coverage counters are ever /// incremented within a function, so that it can set the `num-counters` /// argument of the `llvm.instrprof.increment` intrinsic. /// /// This may be less than the highest counter ID emitted by the /// InstrumentCoverage MIR pass, if the highest-numbered counter increments /// were removed by MIR optimizations. - pub max_counter_id: mir::coverage::CounterId, + pub fn num_counters_after_mir_opts(&self) -> u32 { + // FIXME(Zalathar): Currently this treats an unused counter as "used" + // if its ID is less than that of the highest counter that really is + // used. Fixing this would require adding a renumbering step somewhere. + self.counters_seen.last_set_in(..).map_or(0, |max| max.as_u32() + 1) + } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0f2a6d598a0..a3976c3dda1 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -82,7 +82,7 @@ use crate::ty::print::{PrintTraitRefExt, describe_as_module}; use crate::ty::util::AlwaysRequiresDrop; use crate::ty::{ self, CrateInherentImpls, GenericArg, GenericArgsRef, PseudoCanonicalInput, Ty, TyCtxt, - TyCtxtFeed, UnusedGenericParams, + TyCtxtFeed, }; use crate::{dep_graph, mir, thir}; @@ -276,7 +276,7 @@ rustc_queries! { } /// The root query triggering all analysis passes like typeck or borrowck. - query analysis(key: ()) -> Result<(), ErrorGuaranteed> { + query analysis(key: ()) { eval_always desc { "running analysis passes on this crate" } } @@ -916,6 +916,12 @@ rustc_queries! { cache_on_disk_if { true } } + /// Checks well-formedness of tail calls (`become f()`). + query check_tail_calls(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> { + desc { |tcx| "tail-call-checking `{}`", tcx.def_path_str(key) } + cache_on_disk_if { true } + } + /// Returns the types assumed to be well formed while "inside" of the given item. /// /// Note that we've liberated the late bound regions of function signatures, so @@ -958,11 +964,6 @@ rustc_queries! { desc { |tcx| "checking for unstable API usage in {}", describe_as_module(key, tcx) } } - /// Checks the const bodies in the module for illegal operations (e.g. `if` or `loop`). - query check_mod_const_bodies(key: LocalModDefId) { - desc { |tcx| "checking consts in {}", describe_as_module(key, tcx) } - } - /// Checks the loops in the module. query check_mod_loops(key: LocalModDefId) { desc { |tcx| "checking loops in {}", describe_as_module(key, tcx) } @@ -1086,6 +1087,8 @@ rustc_queries! { } /// Computes the tag (if any) for a given type and variant. + /// `None` means that the variant doesn't need a tag (because it is niched). + /// Will panic for uninhabited variants. query tag_for_variant( key: (Ty<'tcx>, abi::VariantIdx) ) -> Option<ty::ScalarInt> { @@ -2045,15 +2048,6 @@ rustc_queries! { desc { "getting codegen unit `{sym}`" } } - query unused_generic_params(key: ty::InstanceKind<'tcx>) -> UnusedGenericParams { - cache_on_disk_if { key.def_id().is_local() } - desc { - |tcx| "determining which generic parameters are unused by `{}`", - tcx.def_path_str(key.def_id()) - } - separate_provide_extern - } - query backend_optimization_level(_: ()) -> OptLevel { desc { "optimization level used by backend" } } @@ -2311,10 +2305,13 @@ rustc_queries! { desc { "checking validity requirement for `{}`: {}", key.1.value, key.0 } } - query compare_impl_const( - key: (LocalDefId, DefId) - ) -> Result<(), ErrorGuaranteed> { - desc { |tcx| "checking assoc const `{}` has the same type as trait item", tcx.def_path_str(key.0) } + /// This takes the def-id of an associated item from a impl of a trait, + /// and checks its validity against the trait item it corresponds to. + /// + /// Any other def id will ICE. + query compare_impl_item(key: LocalDefId) -> Result<(), ErrorGuaranteed> { + desc { |tcx| "checking assoc item `{}` is compatible with trait definition", tcx.def_path_str(key) } + ensure_forwards_result_if_red } query deduced_param_attrs(def_id: DefId) -> &'tcx [ty::DeducedParamAttrs] { diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 3849cb72668..119a99e1bf7 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -23,7 +23,7 @@ use rustc_session::Session; use rustc_span::hygiene::{ ExpnId, HygieneDecodeContext, HygieneEncodeContext, SyntaxContext, SyntaxContextData, }; -use rustc_span::source_map::{SourceMap, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::{ BytePos, CachingSourceMapView, ExpnData, ExpnHash, Pos, RelativeBytePos, SourceFile, Span, SpanDecoder, SpanEncoder, StableSourceFileId, Symbol, @@ -49,7 +49,7 @@ const SYMBOL_PREINTERNED: u8 = 2; /// previous compilation session. This data will eventually include the results /// of a few selected queries (like `typeck` and `mir_optimized`) and /// any side effects that have been emitted during a query. -pub struct OnDiskCache<'sess> { +pub struct OnDiskCache { // The complete cache data in serialized form. serialized_data: RwLock<Option<Mmap>>, @@ -57,7 +57,6 @@ pub struct OnDiskCache<'sess> { // session. current_side_effects: Lock<FxHashMap<DepNodeIndex, QuerySideEffects>>, - source_map: &'sess SourceMap, file_index_to_stable_id: FxHashMap<SourceFileIndex, EncodedSourceFileId>, // Caches that are populated lazily during decoding. @@ -151,12 +150,12 @@ impl EncodedSourceFileId { } } -impl<'sess> OnDiskCache<'sess> { +impl OnDiskCache { /// Creates a new `OnDiskCache` instance from the serialized data in `data`. /// /// The serialized cache has some basic integrity checks, if those checks indicate that the /// on-disk data is corrupt, an error is returned. - pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Result<Self, ()> { + pub fn new(sess: &Session, data: Mmap, start_pos: usize) -> Result<Self, ()> { assert!(sess.opts.incremental.is_some()); let mut decoder = MemDecoder::new(&data, start_pos)?; @@ -175,7 +174,6 @@ impl<'sess> OnDiskCache<'sess> { serialized_data: RwLock::new(Some(data)), file_index_to_stable_id: footer.file_index_to_stable_id, file_index_to_file: Default::default(), - source_map: sess.source_map(), current_side_effects: Default::default(), query_result_index: footer.query_result_index.into_iter().collect(), prev_side_effects_index: footer.side_effects_index.into_iter().collect(), @@ -187,12 +185,11 @@ impl<'sess> OnDiskCache<'sess> { }) } - pub fn new_empty(source_map: &'sess SourceMap) -> Self { + pub fn new_empty() -> Self { Self { serialized_data: RwLock::new(None), file_index_to_stable_id: Default::default(), file_index_to_file: Default::default(), - source_map, current_side_effects: Default::default(), query_result_index: Default::default(), prev_side_effects_index: Default::default(), @@ -423,7 +420,7 @@ impl<'sess> OnDiskCache<'sess> { } fn with_decoder<'a, 'tcx, T, F: for<'s> FnOnce(&mut CacheDecoder<'s, 'tcx>) -> T>( - &'sess self, + &self, tcx: TyCtxt<'tcx>, pos: AbsoluteBytePos, f: F, @@ -436,7 +433,6 @@ impl<'sess> OnDiskCache<'sess> { tcx, opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize()) .unwrap(), - source_map: self.source_map, file_index_to_file: &self.file_index_to_file, file_index_to_stable_id: &self.file_index_to_stable_id, alloc_decoding_session: self.alloc_decoding_state.new_decoding_session(), @@ -457,7 +453,6 @@ impl<'sess> OnDiskCache<'sess> { pub struct CacheDecoder<'a, 'tcx> { tcx: TyCtxt<'tcx>, opaque: MemDecoder<'a>, - source_map: &'a SourceMap, file_index_to_file: &'a Lock<FxHashMap<SourceFileIndex, Lrc<SourceFile>>>, file_index_to_stable_id: &'a FxHashMap<SourceFileIndex, EncodedSourceFileId>, alloc_decoding_session: AllocDecodingSession<'a>, @@ -470,8 +465,7 @@ pub struct CacheDecoder<'a, 'tcx> { impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { #[inline] fn file_index_to_file(&self, index: SourceFileIndex) -> Lrc<SourceFile> { - let CacheDecoder { tcx, file_index_to_file, file_index_to_stable_id, source_map, .. } = - *self; + let CacheDecoder { tcx, file_index_to_file, file_index_to_stable_id, .. } = *self; Lrc::clone(file_index_to_file.borrow_mut().entry(index).or_insert_with(|| { let source_file_id = &file_index_to_stable_id[&index]; @@ -490,7 +484,8 @@ impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { self.tcx.import_source_files(source_file_cnum); } - source_map + tcx.sess + .source_map() .source_file_by_stable_id(source_file_id.stable_source_file_id) .expect("failed to lookup `SourceFile` in new context") })) diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 20ba1b27c0e..c8675660e0f 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -66,7 +66,7 @@ pub struct QuerySystem<'tcx> { /// Do not access this directly. It is only meant to be used by /// `DepGraph::try_mark_green()` and the query infrastructure. /// This is `None` if we are not incremental compilation mode - pub on_disk_cache: Option<OnDiskCache<'tcx>>, + pub on_disk_cache: Option<OnDiskCache>, pub fns: QuerySystemFns<'tcx>, diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 9cf6bc1b777..86014c34b45 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -158,8 +158,21 @@ pub struct AdtExpr<'tcx> { pub user_ty: UserTy<'tcx>, pub fields: Box<[FieldExpr]>, - /// The base, e.g. `Foo {x: 1, .. base}`. - pub base: Option<FruInfo<'tcx>>, + /// The base, e.g. `Foo {x: 1, ..base}`. + pub base: AdtExprBase<'tcx>, +} + +#[derive(Clone, Debug, HashStable)] +pub enum AdtExprBase<'tcx> { + /// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`. + None, + /// A struct expression with a "base", an expression of the same type as the outer struct that + /// will be used to populate any fields not explicitly mentioned: `Foo { ..base }` + Base(FruInfo<'tcx>), + /// A struct expression with a `..` tail but no "base" expression. The values from the struct + /// fields' default values will be used to populate any fields not explicitly mentioned: + /// `Foo { .. }`. + DefaultFields(Box<[Ty<'tcx>]>), } #[derive(Clone, Debug, HashStable)] diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 81202a6eaad..64bac12b266 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -2,6 +2,7 @@ use super::{ AdtExpr, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand, Pat, PatKind, Stmt, StmtKind, Thir, }; +use crate::thir::AdtExprBase; pub trait Visitor<'thir, 'tcx: 'thir>: Sized { fn thir(&self) -> &'thir Thir<'tcx>; @@ -127,7 +128,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( for field in &**fields { visitor.visit_expr(&visitor.thir()[field.expr]); } - if let Some(base) = base { + if let AdtExprBase::Base(base) = base { visitor.visit_expr(&visitor.thir()[base.base]); } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index d61ef7641ee..0a77c3bc42f 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -204,6 +204,10 @@ pub enum ObligationCauseCode<'tcx> { /// list of the item. WhereClauseInExpr(DefId, Span, HirId, usize), + /// Like `WhereClauseinExpr`, but indexes into the `const_conditions` + /// rather than the `predicates_of`. + HostEffectInExpr(DefId, Span, HirId, usize), + /// A type like `&'a T` is WF only if `T: 'a`. ReferenceOutlivesReferent(Ty<'tcx>), diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index c4d86c3210e..31055276422 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -2,15 +2,11 @@ use std::borrow::Cow; use rustc_data_structures::intern::Interned; use rustc_error_messages::MultiSpan; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::{self as hir}; use rustc_macros::HashStable; use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo}; -use tracing::{debug, instrument}; -use crate::mir::interpret::{LitToConstInput, Scalar}; -use crate::ty::{self, GenericArgs, Ty, TyCtxt, TypeVisitableExt}; +use crate::mir::interpret::Scalar; +use crate::ty::{self, Ty, TyCtxt}; mod int; mod kind; @@ -151,10 +147,6 @@ impl<'tcx> Const<'tcx> { } impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> { - fn try_to_target_usize(self, interner: TyCtxt<'tcx>) -> Option<u64> { - self.try_to_target_usize(interner) - } - fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferConst) -> Self { Const::new_infer(tcx, infer) } @@ -185,82 +177,6 @@ impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> { } impl<'tcx> Const<'tcx> { - // FIXME: move this and try_from_lit to hir_ty_lowering like lower_const_arg/from_const_arg - /// Literals and const generic parameters are eagerly converted to a constant, everything else - /// becomes `Unevaluated`. - #[instrument(skip(tcx), level = "debug")] - pub fn from_anon_const(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Self { - let body_id = match tcx.hir_node_by_def_id(def) { - hir::Node::AnonConst(ac) => ac.body, - node => span_bug!( - tcx.def_span(def.to_def_id()), - "from_anon_const can only process anonymous constants, not {node:?}" - ), - }; - - let expr = &tcx.hir().body(body_id).value; - debug!(?expr); - - let ty = tcx.type_of(def).no_bound_vars().expect("const parameter types cannot be generic"); - - match Self::try_from_lit(tcx, ty, expr) { - Some(v) => v, - None => ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst { - def: def.to_def_id(), - args: GenericArgs::identity_for_item(tcx, def.to_def_id()), - }), - } - } - - #[instrument(skip(tcx), level = "debug")] - fn try_from_lit(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, expr: &'tcx hir::Expr<'tcx>) -> Option<Self> { - // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments - // currently have to be wrapped in curly brackets, so it's necessary to special-case. - let expr = match &expr.kind { - hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => { - block.expr.as_ref().unwrap() - } - _ => expr, - }; - - if let hir::ExprKind::Path(hir::QPath::Resolved( - _, - &hir::Path { res: Res::Def(DefKind::ConstParam, _), .. }, - )) = expr.kind - { - span_bug!(expr.span, "try_from_lit: received const param which shouldn't be possible"); - }; - - let lit_input = match expr.kind { - hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), - hir::ExprKind::Unary(hir::UnOp::Neg, expr) => match expr.kind { - hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: true }), - _ => None, - }, - _ => None, - }; - - if let Some(lit_input) = lit_input { - // If an error occurred, ignore that it's a literal and leave reporting the error up to - // mir. - match tcx.at(expr.span).lit_to_const(lit_input) { - Ok(c) => return Some(c), - Err(_) if lit_input.ty.has_aliases() => { - // allow the `ty` to be an alias type, though we cannot handle it here - return None; - } - Err(e) => { - tcx.dcx().span_delayed_bug( - expr.span, - format!("Const::try_from_lit: couldn't lit_to_const {e:?}"), - ); - } - } - } - - None - } - /// Creates a constant with the given integer value and interns it. #[inline] pub fn from_bits( diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index d982122e2aa..2841470d248 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -585,6 +585,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.trait_def(trait_def_id).implement_via_object } + fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool { + self.trait_def(trait_def_id).safety == hir::Safety::Unsafe + } + fn is_impl_trait_in_trait(self, def_id: DefId) -> bool { self.is_impl_trait_in_trait(def_id) } @@ -1371,8 +1375,17 @@ impl<'tcx> GlobalCtxt<'tcx> { tls::enter_context(&icx, || f(icx.tcx)) } - pub fn finish(&self) -> FileEncodeResult { - self.dep_graph.finish_encoding() + pub fn finish(&'tcx self) { + // We assume that no queries are run past here. If there are new queries + // after this point, they'll show up as "<unknown>" in self-profiling data. + self.enter(|tcx| tcx.alloc_self_profile_query_strings()); + + self.enter(|tcx| tcx.save_dep_graph()); + self.enter(|tcx| tcx.query_key_hash_verify_all()); + + if let Err((path, error)) = self.dep_graph.finish_encoding() { + self.sess.dcx().emit_fatal(crate::error::FailedWritingFile { path: &path, error }); + } } } @@ -1466,7 +1479,7 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_adt_def_from_data(ty::AdtDefData::new(self, did, kind, variants, repr)) } - /// Allocates a read-only byte or string literal for `mir::interpret`. + /// Allocates a read-only byte or string literal for `mir::interpret` with alignment 1. /// Returns the same `AllocId` if called again with the same bytes. pub fn allocate_bytes_dedup(self, bytes: &[u8], salt: usize) -> interpret::AllocId { // Create an allocation that just contains these bytes. @@ -1566,10 +1579,6 @@ impl<'tcx> TyCtxt<'tcx> { } } - pub fn consider_optimizing<T: Fn() -> String>(self, msg: T) -> bool { - self.sess.consider_optimizing(|| self.crate_name(LOCAL_CRATE), msg) - } - /// Obtain all lang items of this crate and all dependencies (recursively) pub fn lang_items(self) -> &'tcx rustc_hir::lang_items::LanguageItems { self.get_lang_items(()) @@ -1950,8 +1959,6 @@ impl<'tcx> TyCtxt<'tcx> { #[inline] pub fn local_crate_exports_generics(self) -> bool { - debug_assert!(self.sess.opts.share_generics()); - self.crate_types().iter().any(|crate_type| { match crate_type { CrateType::Executable diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 84f52bfe48f..604f1da26c6 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -1,14 +1,15 @@ //! Diagnostics related methods for `Ty`. -use std::borrow::Cow; use std::fmt::Write; use std::ops::ControlFlow; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display}; +use rustc_errors::{ + Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display, pluralize, +}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicate}; +use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicateKind}; use rustc_span::{BytePos, Span}; use rustc_type_ir::TyKind::*; @@ -161,7 +162,7 @@ pub fn suggest_arbitrary_trait_bound<'tcx>( true } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] enum SuggestChangingConstraintsMessage<'a> { RestrictBoundFurther, RestrictType { ty: &'a str }, @@ -172,7 +173,7 @@ enum SuggestChangingConstraintsMessage<'a> { fn suggest_changing_unsized_bound( generics: &hir::Generics<'_>, - suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>, + suggestions: &mut Vec<(Span, String, String, SuggestChangingConstraintsMessage<'_>)>, param: &hir::GenericParam<'_>, def_id: Option<DefId>, ) { @@ -180,7 +181,7 @@ fn suggest_changing_unsized_bound( // First look at the `where` clause because we can have `where T: ?Sized`, // then look at params. for (where_pos, predicate) in generics.predicates.iter().enumerate() { - let WherePredicate::BoundPredicate(predicate) = predicate else { + let WherePredicateKind::BoundPredicate(predicate) = predicate.kind else { continue; }; if !predicate.is_param_bound(param.def_id.to_def_id()) { @@ -207,7 +208,8 @@ fn suggest_changing_unsized_bound( continue; } - let mut push_suggestion = |sp, msg| suggestions.push((sp, String::new(), msg)); + let mut push_suggestion = + |sp, msg| suggestions.push((sp, "Sized".to_string(), String::new(), msg)); if predicate.bounds.len() == unsized_bounds.len() { // All the bounds are unsized bounds, e.g. @@ -278,8 +280,25 @@ pub fn suggest_constraining_type_params<'a>( span_to_replace: Option<Span>, ) -> bool { let mut grouped = FxHashMap::default(); + let mut unstable_suggestion = false; param_names_and_constraints.for_each(|(param_name, constraint, def_id)| { - grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id)) + let stable = match def_id { + Some(def_id) => match tcx.lookup_stability(def_id) { + Some(s) => s.level.is_stable(), + None => true, + }, + None => true, + }; + if stable || tcx.sess.is_nightly_build() { + grouped.entry(param_name).or_insert(Vec::new()).push(( + constraint, + def_id, + if stable { "" } else { "unstable " }, + )); + if !stable { + unstable_suggestion = true; + } + } }); let mut applicability = Applicability::MachineApplicable; @@ -290,16 +309,21 @@ pub fn suggest_constraining_type_params<'a>( let Some(param) = param else { return false }; { - let mut sized_constraints = constraints.extract_if(|(_, def_id)| { + let mut sized_constraints = constraints.extract_if(|(_, def_id, _)| { def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized)) }); - if let Some((_, def_id)) = sized_constraints.next() { + if let Some((_, def_id, _)) = sized_constraints.next() { applicability = Applicability::MaybeIncorrect; err.span_label(param.span, "this type parameter needs to be `Sized`"); suggest_changing_unsized_bound(generics, &mut suggestions, param, def_id); } } + let bound_message = if constraints.iter().any(|(_, def_id, _)| def_id.is_none()) { + SuggestChangingConstraintsMessage::RestrictBoundFurther + } else { + SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name } + }; // in the scenario like impl has stricter requirements than trait, // we should not suggest restrict bound on the impl, here we double check @@ -312,15 +336,54 @@ pub fn suggest_constraining_type_params<'a>( .collect(); constraints - .retain(|(_, def_id)| def_id.map_or(true, |def| !bound_trait_defs.contains(&def))); + .retain(|(_, def_id, _)| def_id.map_or(true, |def| !bound_trait_defs.contains(&def))); if constraints.is_empty() { continue; } - let mut constraint = constraints.iter().map(|&(c, _)| c).collect::<Vec<_>>(); + let mut constraint = constraints.iter().map(|&(c, _, _)| c).collect::<Vec<_>>(); constraint.sort(); constraint.dedup(); + let all_known = constraints.iter().all(|&(_, def_id, _)| def_id.is_some()); + let all_stable = constraints.iter().all(|&(_, _, stable)| stable.is_empty()); + let all_unstable = constraints.iter().all(|&(_, _, stable)| !stable.is_empty()); + let post = if all_stable || all_unstable { + // Don't redundantly say "trait `X`, trait `Y`", instead "traits `X` and `Y`" + let mut trait_names = constraints + .iter() + .map(|&(c, def_id, _)| match def_id { + None => format!("`{c}`"), + Some(def_id) => format!("`{}`", tcx.item_name(def_id)), + }) + .collect::<Vec<_>>(); + trait_names.sort(); + trait_names.dedup(); + let n = trait_names.len(); + let stable = if all_stable { "" } else { "unstable " }; + let trait_ = if all_known { format!("trait{}", pluralize!(n)) } else { String::new() }; + format!("{stable}{trait_}{}", match &trait_names[..] { + [t] => format!(" {t}"), + [ts @ .., last] => format!(" {} and {last}", ts.join(", ")), + [] => return false, + },) + } else { + // We're more explicit when there's a mix of stable and unstable traits. + let mut trait_names = constraints + .iter() + .map(|&(c, def_id, stable)| match def_id { + None => format!("`{c}`"), + Some(def_id) => format!("{stable}trait `{}`", tcx.item_name(def_id)), + }) + .collect::<Vec<_>>(); + trait_names.sort(); + trait_names.dedup(); + match &trait_names[..] { + [t] => t.to_string(), + [ts @ .., last] => format!("{} and {last}", ts.join(", ")), + [] => return false, + } + }; let constraint = constraint.join(" + "); let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| { let suggestion = if span_to_replace.is_some() { @@ -333,13 +396,11 @@ pub fn suggest_constraining_type_params<'a>( format!(" {constraint}") }; - use SuggestChangingConstraintsMessage::RestrictBoundFurther; - if let Some(open_paren_sp) = open_paren_sp { - suggestions.push((open_paren_sp, "(".to_string(), RestrictBoundFurther)); - suggestions.push((span, format!("){suggestion}"), RestrictBoundFurther)); + suggestions.push((open_paren_sp, post.clone(), "(".to_string(), bound_message)); + suggestions.push((span, post.clone(), format!("){suggestion}"), bound_message)); } else { - suggestions.push((span, suggestion, RestrictBoundFurther)); + suggestions.push((span, post.clone(), suggestion, bound_message)); } }; @@ -397,7 +458,8 @@ pub fn suggest_constraining_type_params<'a>( // - insert: `, X: Bar` suggestions.push(( generics.tail_span_for_predicate_suggestion(), - constraints.iter().fold(String::new(), |mut string, &(constraint, _)| { + post, + constraints.iter().fold(String::new(), |mut string, &(constraint, _, _)| { write!(string, ", {param_name}: {constraint}").unwrap(); string }), @@ -426,6 +488,7 @@ pub fn suggest_constraining_type_params<'a>( // default (`<T=Foo>`), so we suggest adding `where T: Bar`. suggestions.push(( generics.tail_span_for_predicate_suggestion(), + post, format!("{where_prefix} {param_name}: {constraint}"), SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }, )); @@ -439,6 +502,7 @@ pub fn suggest_constraining_type_params<'a>( if let Some(colon_span) = param.colon_span { suggestions.push(( colon_span.shrink_to_hi(), + post, format!(" {constraint}"), SuggestChangingConstraintsMessage::RestrictType { ty: param_name }, )); @@ -451,6 +515,7 @@ pub fn suggest_constraining_type_params<'a>( // - help: consider restricting this type parameter with `T: Foo` suggestions.push(( param.span.shrink_to_hi(), + post, format!(": {constraint}"), SuggestChangingConstraintsMessage::RestrictType { ty: param_name }, )); @@ -459,39 +524,46 @@ pub fn suggest_constraining_type_params<'a>( // FIXME: remove the suggestions that are from derive, as the span is not correct suggestions = suggestions .into_iter() - .filter(|(span, _, _)| !span.in_derive_expansion()) + .filter(|(span, _, _, _)| !span.in_derive_expansion()) .collect::<Vec<_>>(); - + let suggested = !suggestions.is_empty(); if suggestions.len() == 1 { - let (span, suggestion, msg) = suggestions.pop().unwrap(); + let (span, post, suggestion, msg) = suggestions.pop().unwrap(); let msg = match msg { SuggestChangingConstraintsMessage::RestrictBoundFurther => { - Cow::from("consider further restricting this bound") + format!("consider further restricting this bound") + } + SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } + | SuggestChangingConstraintsMessage::RestrictType { ty } + if ty.starts_with("impl ") => + { + format!("consider restricting opaque type `{ty}` with {post}") } SuggestChangingConstraintsMessage::RestrictType { ty } => { - Cow::from(format!("consider restricting type parameter `{ty}`")) + format!("consider restricting type parameter `{ty}` with {post}") } SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => { - Cow::from(format!("consider further restricting type parameter `{ty}`")) + format!("consider further restricting type parameter `{ty}` with {post}") } SuggestChangingConstraintsMessage::RemoveMaybeUnsized => { - Cow::from("consider removing the `?Sized` bound to make the type parameter `Sized`") + format!("consider removing the `?Sized` bound to make the type parameter `Sized`") } SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized => { - Cow::from("consider replacing `?Sized` with `Sized`") + format!("consider replacing `?Sized` with `Sized`") } }; err.span_suggestion_verbose(span, msg, suggestion, applicability); } else if suggestions.len() > 1 { + let post = if unstable_suggestion { " (some of them are unstable traits)" } else { "" }; err.multipart_suggestion_verbose( - "consider restricting type parameters", - suggestions.into_iter().map(|(span, suggestion, _)| (span, suggestion)).collect(), + format!("consider restricting type parameters{post}"), + suggestions.into_iter().map(|(span, _, suggestion, _)| (span, suggestion)).collect(), applicability, ); } - true + suggested } /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for. diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 43d243b0584..4a82af32559 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -58,12 +58,9 @@ impl<'tcx> TypeError<'tcx> { pluralize!(values.found) ) .into(), - TypeError::FixedArraySize(values) => format!( - "expected an array with a fixed size of {} element{}, found one with {} element{}", - values.expected, - pluralize!(values.expected), - values.found, - pluralize!(values.found) + TypeError::ArraySize(values) => format!( + "expected an array with a size of {}, found one with a size of {}", + values.expected, values.found, ) .into(), TypeError::ArgCount => "incorrect number of function parameters".into(), diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 7adbd556141..1b073d3c466 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -2,9 +2,9 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_hir::def_id::DefId; use rustc_type_ir::data_structures::DelayedMap; pub use rustc_type_ir::fold::{ - FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable, shift_region, shift_vars, + FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable, fold_regions, shift_region, + shift_vars, }; -use tracing::{debug, instrument}; use crate::ty::{self, Binder, BoundTy, Ty, TyCtxt, TypeVisitableExt}; @@ -51,85 +51,6 @@ where } /////////////////////////////////////////////////////////////////////////// -// Region folder - -impl<'tcx> TyCtxt<'tcx> { - /// Folds the escaping and free regions in `value` using `f`. - pub fn fold_regions<T>( - self, - value: T, - mut f: impl FnMut(ty::Region<'tcx>, ty::DebruijnIndex) -> ty::Region<'tcx>, - ) -> T - where - T: TypeFoldable<TyCtxt<'tcx>>, - { - value.fold_with(&mut RegionFolder::new(self, &mut f)) - } -} - -/// Folds over the substructure of a type, visiting its component -/// types and all regions that occur *free* within it. -/// -/// That is, function pointer types and trait object can introduce -/// new bound regions which are not visited by this visitors as -/// they are not free; only regions that occur free will be -/// visited by `fld_r`. -pub struct RegionFolder<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - - /// Stores the index of a binder *just outside* the stuff we have - /// visited. So this begins as INNERMOST; when we pass through a - /// binder, it is incremented (via `shift_in`). - current_index: ty::DebruijnIndex, - - /// Callback invokes for each free region. The `DebruijnIndex` - /// points to the binder *just outside* the ones we have passed - /// through. - fold_region_fn: - &'a mut (dyn FnMut(ty::Region<'tcx>, ty::DebruijnIndex) -> ty::Region<'tcx> + 'a), -} - -impl<'a, 'tcx> RegionFolder<'a, 'tcx> { - #[inline] - pub fn new( - tcx: TyCtxt<'tcx>, - fold_region_fn: &'a mut dyn FnMut(ty::Region<'tcx>, ty::DebruijnIndex) -> ty::Region<'tcx>, - ) -> RegionFolder<'a, 'tcx> { - RegionFolder { tcx, current_index: ty::INNERMOST, fold_region_fn } - } -} - -impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for RegionFolder<'a, 'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( - &mut self, - t: ty::Binder<'tcx, T>, - ) -> ty::Binder<'tcx, T> { - self.current_index.shift_in(1); - let t = t.super_fold_with(self); - self.current_index.shift_out(1); - t - } - - #[instrument(skip(self), level = "debug", ret)] - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - ty::ReBound(debruijn, _) if debruijn < self.current_index => { - debug!(?self.current_index, "skipped bound region"); - r - } - _ => { - debug!(?self.current_index, "folding free region"); - (self.fold_region_fn)(r, self.current_index) - } - } - } -} - -/////////////////////////////////////////////////////////////////////////// // Bound vars replacer /// A delegate used when instantiating bound vars. diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 73fd8aa5b6c..65c909e70f6 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -19,8 +19,8 @@ use crate::error; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::ty::print::{FmtPrinter, Printer, shrunk_instance_name}; use crate::ty::{ - self, EarlyBinder, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, - TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + self, EarlyBinder, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, }; /// An `InstanceKind` along with the args that are needed to substitute the instance. @@ -190,19 +190,23 @@ impl<'tcx> Instance<'tcx> { /// This method already takes into account the global `-Zshare-generics` /// setting, always returning `None` if `share-generics` is off. pub fn upstream_monomorphization(&self, tcx: TyCtxt<'tcx>) -> Option<CrateNum> { - // If we are not in share generics mode, we don't link to upstream - // monomorphizations but always instantiate our own internal versions - // instead. - if !tcx.sess.opts.share_generics() { - return None; - } - // If this is an item that is defined in the local crate, no upstream // crate can know about it/provide a monomorphization. if self.def_id().is_local() { return None; } + // If we are not in share generics mode, we don't link to upstream + // monomorphizations but always instantiate our own internal versions + // instead. + if !tcx.sess.opts.share_generics() + // However, if the def_id is marked inline(never), then it's fine to just reuse the + // upstream monomorphization. + && tcx.codegen_fn_attrs(self.def_id()).inline != rustc_attr::InlineAttr::Never + { + return None; + } + // If this a non-generic instance, it cannot be a shared monomorphization. self.args.non_erasable_generics().next()?; @@ -913,116 +917,6 @@ impl<'tcx> Instance<'tcx> { tcx.try_normalize_erasing_regions(typing_env, v.instantiate_identity()) } } - - /// Returns a new `Instance` where generic parameters in `instance.args` are replaced by - /// identity parameters if they are determined to be unused in `instance.def`. - pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self { - debug!("polymorphize: running polymorphization analysis"); - if !tcx.sess.opts.unstable_opts.polymorphize { - return self; - } - - let polymorphized_args = polymorphize(tcx, self.def, self.args); - debug!("polymorphize: self={:?} polymorphized_args={:?}", self, polymorphized_args); - Self { def: self.def, args: polymorphized_args } - } -} - -fn polymorphize<'tcx>( - tcx: TyCtxt<'tcx>, - instance: ty::InstanceKind<'tcx>, - args: GenericArgsRef<'tcx>, -) -> GenericArgsRef<'tcx> { - debug!("polymorphize({:?}, {:?})", instance, args); - let unused = tcx.unused_generic_params(instance); - debug!("polymorphize: unused={:?}", unused); - - // If this is a closure or coroutine then we need to handle the case where another closure - // from the function is captured as an upvar and hasn't been polymorphized. In this case, - // the unpolymorphized upvar closure would result in a polymorphized closure producing - // multiple mono items (and eventually symbol clashes). - let def_id = instance.def_id(); - let upvars_ty = match tcx.type_of(def_id).skip_binder().kind() { - ty::Closure(..) => Some(args.as_closure().tupled_upvars_ty()), - ty::Coroutine(..) => { - assert_eq!( - args.as_coroutine().kind_ty(), - tcx.types.unit, - "polymorphization does not support coroutines from async closures" - ); - Some(args.as_coroutine().tupled_upvars_ty()) - } - _ => None, - }; - let has_upvars = upvars_ty.is_some_and(|ty| !ty.tuple_fields().is_empty()); - debug!("polymorphize: upvars_ty={:?} has_upvars={:?}", upvars_ty, has_upvars); - - struct PolymorphizationFolder<'tcx> { - tcx: TyCtxt<'tcx>, - } - - impl<'tcx> ty::TypeFolder<TyCtxt<'tcx>> for PolymorphizationFolder<'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - debug!("fold_ty: ty={:?}", ty); - match *ty.kind() { - ty::Closure(def_id, args) => { - let polymorphized_args = - polymorphize(self.tcx, ty::InstanceKind::Item(def_id), args); - if args == polymorphized_args { - ty - } else { - Ty::new_closure(self.tcx, def_id, polymorphized_args) - } - } - ty::Coroutine(def_id, args) => { - let polymorphized_args = - polymorphize(self.tcx, ty::InstanceKind::Item(def_id), args); - if args == polymorphized_args { - ty - } else { - Ty::new_coroutine(self.tcx, def_id, polymorphized_args) - } - } - _ => ty.super_fold_with(self), - } - } - } - - GenericArgs::for_item(tcx, def_id, |param, _| { - let is_unused = unused.is_unused(param.index); - debug!("polymorphize: param={:?} is_unused={:?}", param, is_unused); - match param.kind { - // Upvar case: If parameter is a type parameter.. - ty::GenericParamDefKind::Type { .. } if - // ..and has upvars.. - has_upvars && - // ..and this param has the same type as the tupled upvars.. - upvars_ty == Some(args[param.index as usize].expect_ty()) => { - // ..then double-check that polymorphization marked it used.. - debug_assert!(!is_unused); - // ..and polymorphize any closures/coroutines captured as upvars. - let upvars_ty = upvars_ty.unwrap(); - let polymorphized_upvars_ty = upvars_ty.fold_with( - &mut PolymorphizationFolder { tcx }); - debug!("polymorphize: polymorphized_upvars_ty={:?}", polymorphized_upvars_ty); - ty::GenericArg::from(polymorphized_upvars_ty) - }, - - // Simple case: If parameter is a const or type parameter.. - ty::GenericParamDefKind::Const { .. } | ty::GenericParamDefKind::Type { .. } if - // ..and is within range and unused.. - unused.is_unused(param.index) => - // ..then use the identity for this parameter. - tcx.mk_param_from_def(param), - - // Otherwise, use the parameter as before. - _ => args[param.index as usize], - } - }) } fn needs_fn_once_adapter_shim( diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 01ad76aedc3..07573a79260 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -552,7 +552,10 @@ impl<'tcx> HasWasmCAbiOpt for TyCtxt<'tcx> { impl<'tcx> HasX86AbiOpt for TyCtxt<'tcx> { fn x86_abi_opt(&self) -> X86Abi { - X86Abi { regparm: self.sess.opts.unstable_opts.regparm } + X86Abi { + regparm: self.sess.opts.unstable_opts.regparm, + reg_struct_return: self.sess.opts.unstable_opts.reg_struct_return, + } } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 2b532701904..70e0568b202 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1364,6 +1364,7 @@ pub struct FieldDef { pub name: Symbol, pub vis: Visibility<DefId>, pub safety: hir::Safety, + pub value: Option<DefId>, } impl PartialEq for FieldDef { @@ -1376,9 +1377,9 @@ impl PartialEq for FieldDef { // of `FieldDef` changes, a compile-error will be produced, reminding // us to revisit this assumption. - let Self { did: lhs_did, name: _, vis: _, safety: _ } = &self; + let Self { did: lhs_did, name: _, vis: _, safety: _, value: _ } = &self; - let Self { did: rhs_did, name: _, vis: _, safety: _ } = other; + let Self { did: rhs_did, name: _, vis: _, safety: _, value: _ } = other; let res = lhs_did == rhs_did; @@ -1405,7 +1406,7 @@ impl Hash for FieldDef { // of `FieldDef` changes, a compile-error will be produced, reminding // us to revisit this assumption. - let Self { did, name: _, vis: _, safety: _ } = &self; + let Self { did, name: _, vis: _, safety: _, value: _ } = &self; did.hash(s) } @@ -1558,10 +1559,7 @@ impl<'tcx> TyCtxt<'tcx> { let is_box = self.is_lang_item(did.to_def_id(), LangItem::OwnedBox); // This is here instead of layout because the choice must make it into metadata. - if is_box - || !self - .consider_optimizing(|| format!("Reorder fields of {:?}", self.def_path_str(did))) - { + if is_box { flags.insert(ReprFlags::IS_LINEAR); } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index b06687490d2..afdec7a86d4 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -3,7 +3,6 @@ use std::iter; pub use rustc_type_ir::relate::*; use crate::ty::error::{ExpectedFound, TypeError}; -use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{self as ty, Ty, TyCtxt}; pub type RelateResult<'tcx, T> = rustc_type_ir::relate::RelateResult<TyCtxt<'tcx>, T>; @@ -86,10 +85,7 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate< // in `a`. let mut a_v: Vec<_> = a.into_iter().collect(); let mut b_v: Vec<_> = b.into_iter().collect(); - // `skip_binder` here is okay because `stable_cmp` doesn't look at binders - a_v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); a_v.dedup(); - b_v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); b_v.dedup(); if a_v.len() != b_v.len() { return Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))); diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 142db8a17f0..474062218c9 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -978,6 +978,14 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> { fn async_destructor_ty(self, interner: TyCtxt<'tcx>) -> Ty<'tcx> { self.async_destructor_ty(interner) } + + fn has_unsafe_fields(self) -> bool { + if let ty::Adt(adt_def, ..) = self.kind() { + adt_def.all_fields().any(|x| x.safety == hir::Safety::Unsafe) + } else { + false + } + } } /// Type utilities diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 55a7a837b6d..57054bd1a0b 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -21,6 +21,7 @@ use tracing::{debug, instrument}; use super::TypingEnv; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::query::Providers; +use crate::ty::fold::fold_regions; use crate::ty::layout::{FloatExt, IntegerExt}; use crate::ty::{ self, Asyncness, FallibleTypeFolder, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeFoldable, @@ -171,6 +172,24 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Checks whether `ty: Copy` holds while ignoring region constraints. + /// + /// This impacts whether values of `ty` are *moved* or *copied* + /// when referenced. This means that we may generate MIR which + /// does copies even when the type actually doesn't satisfy the + /// full requirements for the `Copy` trait (cc #29149) -- this + /// winds up being reported as an error during NLL borrow check. + /// + /// This function should not be used if there is an `InferCtxt` available. + /// Use `InferCtxt::type_is_copy_modulo_regions` instead. + pub fn type_is_copy_modulo_regions( + self, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, + ) -> bool { + ty.is_trivially_pure_clone_copy() || self.is_copy_raw(typing_env.as_query_input(ty)) + } + /// Returns the deeply last field of nested structures, or the same type if /// not a structure at all. Corresponds to the only possible unsized field, /// and its type can be used to determine unsizing strategy. @@ -735,7 +754,7 @@ impl<'tcx> TyCtxt<'tcx> { .filter(|decl| !decl.ignore_for_traits) .map(move |decl| { let mut vars = vec![]; - let ty = self.fold_regions(decl.ty, |re, debruijn| { + let ty = fold_regions(self, decl.ty, |re, debruijn| { assert_eq!(re, self.lifetimes.re_erased); let var = ty::BoundVar::from_usize(vars.len()); vars.push(ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon)); @@ -1173,21 +1192,6 @@ impl<'tcx> Ty<'tcx> { .map(|(min, _)| ty::Const::from_bits(tcx, min, typing_env, self)) } - /// Checks whether values of this type `T` are *moved* or *copied* - /// when referenced -- this amounts to a check for whether `T: - /// Copy`, but note that we **don't** consider lifetimes when - /// doing this check. This means that we may generate MIR which - /// does copies even when the type actually doesn't satisfy the - /// full requirements for the `Copy` trait (cc #29149) -- this - /// winds up being reported as an error during NLL borrow check. - pub fn is_copy_modulo_regions( - self, - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ) -> bool { - self.is_trivially_pure_clone_copy() || tcx.is_copy_raw(typing_env.as_query_input(self)) - } - /// Checks whether values of this type `T` have a size known at /// compile time (i.e., whether `T: Sized`). Lifetimes are ignored /// for the purposes of this check, so it can be an diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index e5eeb23be25..09a05104e49 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -131,8 +131,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( VtblEntry::Vacant => continue, VtblEntry::Method(instance) => { // Prepare the fn ptr we write into the vtable. - let instance = instance.polymorphize(tcx); - let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT); + let fn_alloc_id = tcx.reserve_and_set_fn_alloc(*instance, CTFE_ALLOC_SALT); let fn_ptr = Pointer::from(fn_alloc_id); Scalar::from_pointer(fn_ptr, &tcx) } diff --git a/compiler/rustc_middle/src/util/call_kind.rs b/compiler/rustc_middle/src/util/call_kind.rs index acfb78b3f6e..df5b73ac1bd 100644 --- a/compiler/rustc_middle/src/util/call_kind.rs +++ b/compiler/rustc_middle/src/util/call_kind.rs @@ -14,6 +14,8 @@ use crate::ty::{AssocItemContainer, GenericArgsRef, Instance, Ty, TyCtxt, Typing pub enum CallDesugaringKind { /// for _ in x {} calls x.into_iter() ForLoopIntoIter, + /// for _ in x {} calls iter.next() + ForLoopNext, /// x? calls x.branch() QuestionBranch, /// x? calls type_of(x)::from_residual() @@ -28,6 +30,7 @@ impl CallDesugaringKind { pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId { match self { Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(), + Self::ForLoopNext => tcx.require_lang_item(LangItem::Iterator, None), Self::QuestionBranch | Self::TryBlockFromOutput => { tcx.require_lang_item(LangItem::Try, None) } @@ -121,6 +124,10 @@ pub fn call_kind<'tcx>( && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop) { Some((CallDesugaringKind::ForLoopIntoIter, method_args.type_at(0))) + } else if tcx.is_lang_item(method_did, LangItem::IteratorNext) + && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop) + { + Some((CallDesugaringKind::ForLoopNext, method_args.type_at(0))) } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) { if tcx.is_lang_item(method_did, LangItem::TryTraitBranch) { Some((CallDesugaringKind::QuestionBranch, method_args.type_at(0))) diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index f28cf84fa69..f647486f62a 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -84,12 +84,17 @@ mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable -mir_build_const_param_in_pattern = const parameters cannot be referenced in patterns +mir_build_const_defined_here = constant defined here -mir_build_const_pattern_depends_on_generic_parameter = - constant pattern depends on a generic parameter +mir_build_const_param_in_pattern = constant parameters cannot be referenced in patterns + .label = can't be used in patterns +mir_build_const_param_in_pattern_def = constant defined here + +mir_build_const_pattern_depends_on_generic_parameter = constant pattern cannot depend on generic parameters + .label = `const` depends on a generic parameter mir_build_could_not_eval_const_pattern = could not evaluate constant pattern + .label = could not evaluate constant mir_build_deref_raw_pointer_requires_unsafe = dereference of raw pointer is unsafe and requires unsafe block @@ -147,7 +152,8 @@ mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = mir_build_interpreted_as_const = introduce a variable instead -mir_build_invalid_pattern = `{$non_sm_ty}` cannot be used in patterns +mir_build_invalid_pattern = {$prefix} `{$non_sm_ty}` cannot be used in patterns + .label = {$prefix} can't be used in patterns mir_build_irrefutable_let_patterns_if_let = irrefutable `if let` {$count -> [one] pattern @@ -244,10 +250,12 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa .label = mutation of layout constrained field mir_build_nan_pattern = cannot use NaN in patterns + .label = evaluates to `NaN`, which is not allowed in patterns .note = NaNs compare inequal to everything, even themselves, so this pattern would never match .help = try using the `is_nan` method instead mir_build_non_const_path = runtime values cannot be referenced in patterns + .label = references a runtime value mir_build_non_empty_never_pattern = mismatched types @@ -265,13 +273,15 @@ mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type .suggestion = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown .help = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern -mir_build_non_partial_eq_match = - to use a constant of type `{$non_peq_ty}` in a pattern, the type must implement `PartialEq` +mir_build_non_partial_eq_match = constant of non-structural type `{$ty}` in a pattern + .label = constant of non-structural type mir_build_pattern_not_covered = refutable pattern in {$origin} .pattern_ty = the matched value is of type `{$pattern_ty}` -mir_build_pointer_pattern = function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. +mir_build_pointer_pattern = function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon + .label = can't be used in patterns + .note = see https://github.com/rust-lang/rust/issues/70861 for details mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future @@ -283,6 +293,8 @@ mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly .missing_box = `#[rustc_box]` requires the `owned_box` lang item mir_build_static_in_pattern = statics cannot be referenced in patterns + .label = can't be used in patterns +mir_build_static_in_pattern_def = `static` defined here mir_build_suggest_attempted_int_lit = alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits @@ -310,12 +322,12 @@ mir_build_trailing_irrefutable_let_patterns = trailing irrefutable {$count -> *[other] them } into the body -mir_build_type_not_structural = - to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq)]` - +mir_build_type_not_structural = constant of non-structural type `{$ty}` in a pattern + .label = constant of non-structural type +mir_build_type_not_structural_def = `{$ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns mir_build_type_not_structural_more_info = see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details - -mir_build_type_not_structural_tip = the traits must be derived, manual `impl`s are not sufficient +mir_build_type_not_structural_tip = + the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details mir_build_unconditional_recursion = function cannot return without recursing .label = cannot return without recursing @@ -334,6 +346,7 @@ mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = .label = access to union field mir_build_union_pattern = cannot use unions in constant patterns + .label = can't use a `union` here mir_build_unreachable_making_this_unreachable = collectively making this unreachable 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 c3e9bd302de..67114efdff5 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -283,7 +283,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { fields.iter().map(|e| self.parse_operand(*e)).collect::<Result<_, _>>()? )) }, - ExprKind::Adt(box AdtExpr{ adt_def, variant_index, args, fields, .. }) => { + ExprKind::Adt(box AdtExpr { adt_def, variant_index, args, fields, .. }) => { let is_union = adt_def.is_union(); let active_field_index = is_union.then(|| fields[0].name); 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 0cab853196b..777ff9e68f0 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs @@ -176,7 +176,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let ty = expr.ty; if !ty.is_sized(tcx, this.typing_env()) { // !sized means !copy, so this is an unsized move - assert!(!ty.is_copy_modulo_regions(tcx, this.typing_env())); + assert!(!tcx.type_is_copy_modulo_regions(this.typing_env(), ty)); // As described above, detect the case where we are passing a value of unsized // type, and that value is coming from the deref of a box. 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 3f89e337781..c66af118453 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -231,7 +231,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if range.start <= range.end { BinOp::BitAnd } else { BinOp::BitOr }; let mut comparer = |range: u128, bin_op: BinOp| -> Place<'tcx> { - // We can use `ty::TypingEnv::fully_monomorphized()`` here + // We can use `ty::TypingEnv::fully_monomorphized()` here // as we only need it to compute the layout of a primitive. let range_val = Const::from_bits( this.tcx, diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index bebb44faba6..a3d5376dcd4 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -1,7 +1,5 @@ //! See docs in build/expr/mod.rs -use std::iter; - use rustc_ast::{AsmMacro, InlineAsmOptions}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -344,25 +342,51 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }) .collect(); - let field_names = adt_def.variant(variant_index).fields.indices(); - - let fields = if let Some(FruInfo { base, field_types }) = base { - let place_builder = unpack!(block = this.as_place_builder(block, *base)); + let variant = adt_def.variant(variant_index); + let field_names = variant.fields.indices(); - // MIR does not natively support FRU, so for each - // base-supplied field, generate an operand that - // reads it from the base. - iter::zip(field_names, &**field_types) - .map(|(n, ty)| match fields_map.get(&n) { - Some(v) => v.clone(), - None => { - let place = place_builder.clone_project(PlaceElem::Field(n, *ty)); - this.consume_by_copy_or_move(place.to_place(this)) - } - }) - .collect() - } else { - field_names.filter_map(|n| fields_map.get(&n).cloned()).collect() + let fields = match base { + AdtExprBase::None => { + field_names.filter_map(|n| fields_map.get(&n).cloned()).collect() + } + AdtExprBase::Base(FruInfo { base, field_types }) => { + let place_builder = unpack!(block = this.as_place_builder(block, *base)); + + // We desugar FRU as we lower to MIR, so for each + // base-supplied field, generate an operand that + // reads it from the base. + itertools::zip_eq(field_names, &**field_types) + .map(|(n, ty)| match fields_map.get(&n) { + Some(v) => v.clone(), + None => { + let place = + place_builder.clone_project(PlaceElem::Field(n, *ty)); + this.consume_by_copy_or_move(place.to_place(this)) + } + }) + .collect() + } + AdtExprBase::DefaultFields(field_types) => { + itertools::zip_eq(field_names, &**field_types) + .map(|(n, ty)| match fields_map.get(&n) { + Some(v) => v.clone(), + None => match variant.fields[n].value { + Some(def) => { + let value = Const::from_unevaluated(this.tcx, def) + .instantiate(this.tcx, args); + this.literal_operand(expr_span, value) + } + None => { + let name = variant.fields[n].name; + span_bug!( + expr_span, + "missing mandatory field `{name}` of type `{ty}`", + ); + } + }, + }) + .collect() + } }; let inferred_ty = expr.ty; diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 3317f3b7f8a..f43c29d8f5d 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -50,6 +50,10 @@ pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx return construct_error(tcx, def, e); } + if let Err(err) = tcx.check_tail_calls(def) { + return construct_error(tcx, def, err); + } + let body = match tcx.thir_body(def) { Err(error_reported) => construct_error(tcx, def, error_reported), Ok((thir, expr)) => { diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs new file mode 100644 index 00000000000..b1f46d37d50 --- /dev/null +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -0,0 +1,386 @@ +use rustc_abi::ExternAbi; +use rustc_errors::Applicability; +use rustc_hir::LangItem; +use rustc_hir::def::DefKind; +use rustc_middle::span_bug; +use rustc_middle::thir::visit::{self, Visitor}; +use rustc_middle::thir::{BodyTy, Expr, ExprId, ExprKind, Thir}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; + +pub(crate) fn check_tail_calls(tcx: TyCtxt<'_>, def: LocalDefId) -> Result<(), ErrorGuaranteed> { + let (thir, expr) = tcx.thir_body(def)?; + let thir = &thir.borrow(); + + // If `thir` is empty, a type error occurred, skip this body. + if thir.exprs.is_empty() { + return Ok(()); + } + + let is_closure = matches!(tcx.def_kind(def), DefKind::Closure); + let caller_ty = tcx.type_of(def).skip_binder(); + + let mut visitor = TailCallCkVisitor { + tcx, + thir, + found_errors: Ok(()), + // FIXME(#132279): we're clearly in a body here. + typing_env: ty::TypingEnv::non_body_analysis(tcx, def), + is_closure, + caller_ty, + }; + + visitor.visit_expr(&thir[expr]); + + visitor.found_errors +} + +struct TailCallCkVisitor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + thir: &'a Thir<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + /// Whatever the currently checked body is one of a closure + is_closure: bool, + /// The result of the checks, `Err(_)` if there was a problem with some + /// tail call, `Ok(())` if all of them were fine. + found_errors: Result<(), ErrorGuaranteed>, + /// Type of the caller function. + caller_ty: Ty<'tcx>, +} + +impl<'tcx> TailCallCkVisitor<'_, 'tcx> { + fn check_tail_call(&mut self, call: &Expr<'_>, expr: &Expr<'_>) { + if self.is_closure { + self.report_in_closure(expr); + return; + } + + let BodyTy::Fn(caller_sig) = self.thir.body_type else { + span_bug!( + call.span, + "`become` outside of functions should have been disallowed by hit_typeck" + ) + }; + + let ExprKind::Scope { value, .. } = call.kind else { + span_bug!(call.span, "expected scope, found: {call:?}") + }; + let value = &self.thir[value]; + + if matches!( + value.kind, + ExprKind::Binary { .. } + | ExprKind::Unary { .. } + | ExprKind::AssignOp { .. } + | ExprKind::Index { .. } + ) { + self.report_builtin_op(call, expr); + return; + } + + let ExprKind::Call { ty, fun, ref args, from_hir_call, fn_span } = value.kind else { + self.report_non_call(value, expr); + return; + }; + + if !from_hir_call { + self.report_op(ty, args, fn_span, expr); + } + + // Closures in thir look something akin to + // `for<'a> extern "rust-call" fn(&'a [closure@...], ()) -> <[closure@...] as FnOnce<()>>::Output {<[closure@...] as Fn<()>>::call}` + // So we have to check for them in this weird way... + if let &ty::FnDef(did, args) = ty.kind() { + let parent = self.tcx.parent(did); + if self.tcx.fn_trait_kind_from_def_id(parent).is_some() + && args.first().and_then(|arg| arg.as_type()).is_some_and(Ty::is_closure) + { + self.report_calling_closure(&self.thir[fun], args[1].as_type().unwrap(), expr); + + // Tail calling is likely to cause unrelated errors (ABI, argument mismatches), + // skip them, producing an error about calling a closure is enough. + return; + }; + } + + // Erase regions since tail calls don't care about lifetimes + let callee_sig = + self.tcx.normalize_erasing_late_bound_regions(self.typing_env, ty.fn_sig(self.tcx)); + + if caller_sig.abi != callee_sig.abi { + self.report_abi_mismatch(expr.span, caller_sig.abi, callee_sig.abi); + } + + if caller_sig.inputs_and_output != callee_sig.inputs_and_output { + if caller_sig.inputs() != callee_sig.inputs() { + self.report_arguments_mismatch(expr.span, caller_sig, callee_sig); + } + + // FIXME(explicit_tail_calls): this currenly fails for cases where opaques are used. + // e.g. + // ``` + // fn a() -> impl Sized { become b() } // ICE + // fn b() -> u8 { 0 } + // ``` + // we should think what is the expected behavior here. + // (we should probably just accept this by revealing opaques?) + if caller_sig.output() != callee_sig.output() { + span_bug!(expr.span, "hir typeck should have checked the return type already"); + } + } + + { + let caller_needs_location = self.needs_location(self.caller_ty); + let callee_needs_location = self.needs_location(ty); + + if caller_needs_location != callee_needs_location { + self.report_track_caller_mismatch(expr.span, caller_needs_location); + } + } + + if caller_sig.c_variadic { + self.report_c_variadic_caller(expr.span); + } + + if callee_sig.c_variadic { + self.report_c_variadic_callee(expr.span); + } + } + + /// Returns true if function of type `ty` needs location argument + /// (i.e. if a function is marked as `#[track_caller]`) + fn needs_location(&self, ty: Ty<'tcx>) -> bool { + if let &ty::FnDef(did, substs) = ty.kind() { + let instance = + ty::Instance::expect_resolve(self.tcx, self.typing_env, did, substs, DUMMY_SP); + + instance.def.requires_caller_location(self.tcx) + } else { + false + } + } + + fn report_in_closure(&mut self, expr: &Expr<'_>) { + let err = self.tcx.dcx().span_err(expr.span, "`become` is not allowed in closures"); + self.found_errors = Err(err); + } + + fn report_builtin_op(&mut self, value: &Expr<'_>, expr: &Expr<'_>) { + let err = self + .tcx + .dcx() + .struct_span_err(value.span, "`become` does not support operators") + .with_note("using `become` on a builtin operator is not useful") + .with_span_suggestion( + value.span.until(expr.span), + "try using `return` instead", + "return ", + Applicability::MachineApplicable, + ) + .emit(); + self.found_errors = Err(err); + } + + fn report_op(&mut self, fun_ty: Ty<'_>, args: &[ExprId], fn_span: Span, expr: &Expr<'_>) { + let mut err = + self.tcx.dcx().struct_span_err(fn_span, "`become` does not support operators"); + + if let &ty::FnDef(did, _substs) = fun_ty.kind() + && let parent = self.tcx.parent(did) + && matches!(self.tcx.def_kind(parent), DefKind::Trait) + && let Some(method) = op_trait_as_method_name(self.tcx, parent) + { + match args { + &[arg] => { + let arg = &self.thir[arg]; + + err.multipart_suggestion( + "try using the method directly", + vec![ + (fn_span.shrink_to_lo().until(arg.span), "(".to_owned()), + (arg.span.shrink_to_hi(), format!(").{method}()")), + ], + Applicability::MaybeIncorrect, + ); + } + &[lhs, rhs] => { + let lhs = &self.thir[lhs]; + let rhs = &self.thir[rhs]; + + err.multipart_suggestion( + "try using the method directly", + vec![ + (lhs.span.shrink_to_lo(), format!("(")), + (lhs.span.between(rhs.span), format!(").{method}(")), + (rhs.span.between(expr.span.shrink_to_hi()), ")".to_owned()), + ], + Applicability::MaybeIncorrect, + ); + } + _ => span_bug!(expr.span, "operator with more than 2 args? {args:?}"), + } + } + + self.found_errors = Err(err.emit()); + } + + fn report_non_call(&mut self, value: &Expr<'_>, expr: &Expr<'_>) { + let err = self + .tcx + .dcx() + .struct_span_err(value.span, "`become` requires a function call") + .with_span_note(value.span, "not a function call") + .with_span_suggestion( + value.span.until(expr.span), + "try using `return` instead", + "return ", + Applicability::MaybeIncorrect, + ) + .emit(); + self.found_errors = Err(err); + } + + fn report_calling_closure(&mut self, fun: &Expr<'_>, tupled_args: Ty<'_>, expr: &Expr<'_>) { + let underscored_args = match tupled_args.kind() { + ty::Tuple(tys) if tys.is_empty() => "".to_owned(), + ty::Tuple(tys) => std::iter::repeat("_, ").take(tys.len() - 1).chain(["_"]).collect(), + _ => "_".to_owned(), + }; + + let err = self + .tcx + .dcx() + .struct_span_err(expr.span, "tail calling closures directly is not allowed") + .with_multipart_suggestion( + "try casting the closure to a function pointer type", + vec![ + (fun.span.shrink_to_lo(), "(".to_owned()), + (fun.span.shrink_to_hi(), format!(" as fn({underscored_args}) -> _)")), + ], + Applicability::MaybeIncorrect, + ) + .emit(); + self.found_errors = Err(err); + } + + fn report_abi_mismatch(&mut self, sp: Span, caller_abi: ExternAbi, callee_abi: ExternAbi) { + let err = self + .tcx + .dcx() + .struct_span_err(sp, "mismatched function ABIs") + .with_note("`become` requires caller and callee to have the same ABI") + .with_note(format!("caller ABI is `{caller_abi}`, while callee ABI is `{callee_abi}`")) + .emit(); + self.found_errors = Err(err); + } + + fn report_arguments_mismatch( + &mut self, + sp: Span, + caller_sig: ty::FnSig<'_>, + callee_sig: ty::FnSig<'_>, + ) { + let err = self + .tcx + .dcx() + .struct_span_err(sp, "mismatched signatures") + .with_note("`become` requires caller and callee to have matching signatures") + .with_note(format!("caller signature: `{caller_sig}`")) + .with_note(format!("callee signature: `{callee_sig}`")) + .emit(); + self.found_errors = Err(err); + } + + fn report_track_caller_mismatch(&mut self, sp: Span, caller_needs_location: bool) { + let err = match caller_needs_location { + true => self + .tcx + .dcx() + .struct_span_err( + sp, + "a function marked with `#[track_caller]` cannot tail-call one that is not", + ) + .emit(), + false => self + .tcx + .dcx() + .struct_span_err( + sp, + "a function mot marked with `#[track_caller]` cannot tail-call one that is", + ) + .emit(), + }; + + self.found_errors = Err(err); + } + + fn report_c_variadic_caller(&mut self, sp: Span) { + let err = self + .tcx + .dcx() + // FIXME(explicit_tail_calls): highlight the `...` + .struct_span_err(sp, "tail-calls are not allowed in c-variadic functions") + .emit(); + + self.found_errors = Err(err); + } + + fn report_c_variadic_callee(&mut self, sp: Span) { + let err = self + .tcx + .dcx() + // FIXME(explicit_tail_calls): highlight the function or something... + .struct_span_err(sp, "c-variadic functions can't be tail-called") + .emit(); + + self.found_errors = Err(err); + } +} + +impl<'a, 'tcx> Visitor<'a, 'tcx> for TailCallCkVisitor<'a, 'tcx> { + fn thir(&self) -> &'a Thir<'tcx> { + &self.thir + } + + fn visit_expr(&mut self, expr: &'a Expr<'tcx>) { + if let ExprKind::Become { value } = expr.kind { + let call = &self.thir[value]; + self.check_tail_call(call, expr); + } + + visit::walk_expr(self, expr); + } +} + +fn op_trait_as_method_name(tcx: TyCtxt<'_>, trait_did: DefId) -> Option<&'static str> { + let m = match tcx.as_lang_item(trait_did)? { + LangItem::Add => "add", + LangItem::Sub => "sub", + LangItem::Mul => "mul", + LangItem::Div => "div", + LangItem::Rem => "rem", + LangItem::Neg => "neg", + LangItem::Not => "not", + LangItem::BitXor => "bitxor", + LangItem::BitAnd => "bitand", + LangItem::BitOr => "bitor", + LangItem::Shl => "shl", + LangItem::Shr => "shr", + LangItem::AddAssign => "add_assign", + LangItem::SubAssign => "sub_assign", + LangItem::MulAssign => "mul_assign", + LangItem::DivAssign => "div_assign", + LangItem::RemAssign => "rem_assign", + LangItem::BitXorAssign => "bitxor_assign", + LangItem::BitAndAssign => "bitand_assign", + LangItem::BitOrAssign => "bitor_assign", + LangItem::ShlAssign => "shl_assign", + LangItem::ShrAssign => "shr_assign", + LangItem::Index => "index", + LangItem::IndexMut => "index_mut", + _ => return None, + }; + + Some(m) +} diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 58487a48184..3632da943e1 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -631,20 +631,27 @@ pub(crate) struct NonExhaustiveMatchAllArmsGuarded; #[diag(mir_build_static_in_pattern, code = E0158)] pub(crate) struct StaticInPattern { #[primary_span] + #[label] pub(crate) span: Span, + #[label(mir_build_static_in_pattern_def)] + pub(crate) static_span: Span, } #[derive(Diagnostic)] #[diag(mir_build_const_param_in_pattern, code = E0158)] pub(crate) struct ConstParamInPattern { #[primary_span] + #[label] pub(crate) span: Span, + #[label(mir_build_const_param_in_pattern_def)] + pub(crate) const_span: Span, } #[derive(Diagnostic)] #[diag(mir_build_non_const_path, code = E0080)] pub(crate) struct NonConstPath { #[primary_span] + #[label] pub(crate) span: Span, } @@ -695,6 +702,7 @@ pub(crate) struct WantedConstant { #[diag(mir_build_const_pattern_depends_on_generic_parameter, code = E0158)] pub(crate) struct ConstPatternDependsOnGenericParameter { #[primary_span] + #[label] pub(crate) span: Span, } @@ -702,6 +710,7 @@ pub(crate) struct ConstPatternDependsOnGenericParameter { #[diag(mir_build_could_not_eval_const_pattern)] pub(crate) struct CouldNotEvalConstPattern { #[primary_span] + #[label] pub(crate) span: Span, } @@ -867,33 +876,43 @@ pub(crate) enum Conflict { #[diag(mir_build_union_pattern)] pub(crate) struct UnionPattern { #[primary_span] + #[label] pub(crate) span: Span, } #[derive(Diagnostic)] #[diag(mir_build_type_not_structural)] -#[note(mir_build_type_not_structural_tip)] -#[note(mir_build_type_not_structural_more_info)] pub(crate) struct TypeNotStructural<'tcx> { #[primary_span] + #[label] pub(crate) span: Span, - pub(crate) non_sm_ty: Ty<'tcx>, + #[label(mir_build_type_not_structural_def)] + pub(crate) ty_def_span: Span, + pub(crate) ty: Ty<'tcx>, + #[note(mir_build_type_not_structural_tip)] + pub(crate) manual_partialeq_impl_span: Option<Span>, + #[note(mir_build_type_not_structural_more_info)] + pub(crate) manual_partialeq_impl_note: bool, } #[derive(Diagnostic)] #[diag(mir_build_non_partial_eq_match)] +#[note(mir_build_type_not_structural_more_info)] pub(crate) struct TypeNotPartialEq<'tcx> { #[primary_span] + #[label] pub(crate) span: Span, - pub(crate) non_peq_ty: Ty<'tcx>, + pub(crate) ty: Ty<'tcx>, } #[derive(Diagnostic)] #[diag(mir_build_invalid_pattern)] pub(crate) struct InvalidPattern<'tcx> { #[primary_span] + #[label] pub(crate) span: Span, pub(crate) non_sm_ty: Ty<'tcx>, + pub(crate) prefix: String, } #[derive(Diagnostic)] @@ -910,13 +929,16 @@ pub(crate) struct UnsizedPattern<'tcx> { #[help] pub(crate) struct NaNPattern { #[primary_span] + #[label] pub(crate) span: Span, } #[derive(Diagnostic)] #[diag(mir_build_pointer_pattern)] +#[note] pub(crate) struct PointerPattern { #[primary_span] + #[label] pub(crate) span: Span, } diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 3dbb552cdbb..833e5019865 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -12,6 +12,7 @@ // tidy-alphabetical-end mod build; +mod check_tail_calls; mod check_unsafety; mod errors; pub mod lints; @@ -28,6 +29,7 @@ pub fn provide(providers: &mut Providers) { providers.closure_saved_names_of_captured_variables = build::closure_saved_names_of_captured_variables; providers.check_unsafety = check_unsafety::check_unsafety; + providers.check_tail_calls = check_tail_calls::check_tail_calls; providers.thir_body = thir::cx::thir_body; providers.hooks.thir_tree = thir::print::thir_tree; providers.hooks.thir_flat = thir::print::thir_flat; diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index 3fa0e4def82..30b6718683b 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -2,7 +2,7 @@ use rustc_ast as ast; use rustc_hir::LangItem; use rustc_middle::bug; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; -use rustc_middle::ty::{self, ScalarInt, TyCtxt}; +use rustc_middle::ty::{self, ScalarInt, TyCtxt, TypeVisitableExt as _}; use tracing::trace; use crate::build::parse_float_into_scalar; @@ -13,6 +13,10 @@ pub(crate) fn lit_to_const<'tcx>( ) -> Result<ty::Const<'tcx>, LitToConstError> { let LitToConstInput { lit, ty, neg } = lit_input; + if let Err(guar) = ty.error_reported() { + return Ok(ty::Const::new_error(tcx, guar)); + } + let trunc = |n| { let width = match tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)) { Ok(layout) => layout.size, diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index ee9bcce104e..d75f01dfba0 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -222,7 +222,7 @@ impl<'tcx> Cx<'tcx> { args, fields: Box::new([FieldExpr { name: FieldIdx::from(0u32), expr }]), user_ty: None, - base: None, + base: AdtExprBase::None, })); debug!(?kind); @@ -464,7 +464,7 @@ impl<'tcx> Cx<'tcx> { variant_index: index, fields: field_refs, user_ty, - base: None, + base: AdtExprBase::None, })) } else { ExprKind::Call { @@ -594,20 +594,36 @@ impl<'tcx> Cx<'tcx> { args, user_ty, fields: self.field_refs(fields), - base: base.map(|base| FruInfo { - base: self.mirror_expr(base), - field_types: self.typeck_results().fru_field_types()[expr.hir_id] - .iter() - .copied() - .collect(), - }), + base: match base { + hir::StructTailExpr::Base(base) => AdtExprBase::Base(FruInfo { + base: self.mirror_expr(base), + field_types: self.typeck_results().fru_field_types() + [expr.hir_id] + .iter() + .copied() + .collect(), + }), + hir::StructTailExpr::DefaultFields(_) => { + AdtExprBase::DefaultFields( + self.typeck_results().fru_field_types()[expr.hir_id] + .iter() + .copied() + .collect(), + ) + } + hir::StructTailExpr::None => AdtExprBase::None, + }, })) } AdtKind::Enum => { let res = self.typeck_results().qpath_res(qpath, expr.hir_id); match res { Res::Def(DefKind::Variant, variant_id) => { - assert!(base.is_none()); + assert!(matches!( + base, + hir::StructTailExpr::None + | hir::StructTailExpr::DefaultFields(_) + )); let index = adt.variant_index_with_id(variant_id); let user_provided_types = @@ -621,7 +637,21 @@ impl<'tcx> Cx<'tcx> { args, user_ty, fields: self.field_refs(fields), - base: None, + base: match base { + hir::StructTailExpr::DefaultFields(_) => { + AdtExprBase::DefaultFields( + self.typeck_results().fru_field_types() + [expr.hir_id] + .iter() + .copied() + .collect(), + ) + } + hir::StructTailExpr::Base(base) => { + span_bug!(base.span, "unexpected res: {:?}", res); + } + hir::StructTailExpr::None => AdtExprBase::None, + }, })) } _ => { @@ -1029,7 +1059,7 @@ impl<'tcx> Cx<'tcx> { args, user_ty, fields: Box::new([]), - base: None, + base: AdtExprBase::None, })), _ => bug!("unexpected ty: {:?}", ty), } 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 e55cd35e243..a13b00e1921 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -780,7 +780,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: return; }; - let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.typing_env); + let is_binding_by_move = |ty: Ty<'tcx>| !cx.tcx.type_is_copy_modulo_regions(cx.typing_env, ty); let sess = cx.tcx.sess; diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 5db08f01fdb..aed00aecefc 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,14 +1,17 @@ use rustc_abi::{FieldIdx, VariantIdx}; use rustc_apfloat::Float; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Diag; use rustc_hir as hir; use rustc_index::Idx; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::thir::{FieldPat, Pat, PatKind}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, ValTree}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeVisitor, ValTree}; use rustc_middle::{mir, span_bug}; -use rustc_span::Span; +use rustc_span::def_id::DefId; +use rustc_span::{Span, sym}; use rustc_trait_selection::traits::ObligationCause; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use tracing::{debug, instrument, trace}; @@ -35,7 +38,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { id: hir::HirId, span: Span, ) -> Box<Pat<'tcx>> { - let mut convert = ConstToPat::new(self, id, span); + let mut convert = ConstToPat::new(self, id, span, c); match c.kind() { ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty), @@ -49,21 +52,26 @@ struct ConstToPat<'tcx> { tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, span: Span, + id: hir::HirId, treat_byte_string_as_slice: bool, + + c: ty::Const<'tcx>, } impl<'tcx> ConstToPat<'tcx> { - fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span) -> Self { + fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self { trace!(?pat_ctxt.typeck_results.hir_owner); ConstToPat { tcx: pat_ctxt.tcx, typing_env: pat_ctxt.typing_env, span, + id, treat_byte_string_as_slice: pat_ctxt .typeck_results .treat_byte_string_as_slice .contains(&id.local_id), + c, } } @@ -71,13 +79,32 @@ impl<'tcx> ConstToPat<'tcx> { ty.is_structural_eq_shallow(self.tcx) } + /// We errored. Signal that in the pattern, so that follow up errors can be silenced. + fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> { + if let ty::ConstKind::Unevaluated(uv) = self.c.kind() { + let def_kind = self.tcx.def_kind(uv.def); + if let hir::def::DefKind::AssocConst = def_kind + && let Some(def_id) = uv.def.as_local() + { + // Include the container item in the output. + err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), ""); + } + if let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = def_kind { + err.span_label( + self.tcx.def_span(uv.def), + crate::fluent_generated::mir_build_const_defined_here, + ); + } + } + Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()) }) + } + fn unevaluated_to_pat( &mut self, uv: ty::UnevaluatedConst<'tcx>, ty: Ty<'tcx>, ) -> Box<Pat<'tcx>> { trace!(self.treat_byte_string_as_slice); - let pat_from_kind = |kind| Box::new(Pat { span: self.span, ty, kind }); // It's not *technically* correct to be revealing opaque types here as borrowcheck has // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even @@ -96,32 +123,60 @@ impl<'tcx> ConstToPat<'tcx> { Ok(Ok(c)) => c, Err(ErrorHandled::Reported(_, _)) => { // Let's tell the use where this failing const occurs. - let e = self.tcx.dcx().emit_err(CouldNotEvalConstPattern { span: self.span }); - return pat_from_kind(PatKind::Error(e)); + let mut err = + self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span }); + // We've emitted an error on the original const, it would be redundant to complain + // on its use as well. + if let ty::ConstKind::Unevaluated(uv) = self.c.kind() + && let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = + self.tcx.def_kind(uv.def) + { + err.downgrade_to_delayed_bug(); + } + return self.mk_err(err, ty); } Err(ErrorHandled::TooGeneric(_)) => { - let e = self + let mut e = self .tcx .dcx() - .emit_err(ConstPatternDependsOnGenericParameter { span: self.span }); - return pat_from_kind(PatKind::Error(e)); + .create_err(ConstPatternDependsOnGenericParameter { span: self.span }); + for arg in uv.args { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Param(param_ty) = ty.kind() + { + let def_id = self.tcx.hir().enclosing_body_owner(self.id); + let generics = self.tcx.generics_of(def_id); + let param = generics.type_param(*param_ty, self.tcx); + let span = self.tcx.def_span(param.def_id); + e.span_label(span, "constant depends on this generic parameter"); + if let Some(ident) = self.tcx.def_ident_span(def_id) + && self.tcx.sess.source_map().is_multiline(ident.between(span)) + { + // Display the `fn` name as well in the diagnostic, as the generic isn't + // in the same line and it could be confusing otherwise. + e.span_label(ident, ""); + } + } + } + return self.mk_err(e, ty); } Ok(Err(bad_ty)) => { // The pattern cannot be turned into a valtree. let e = match bad_ty.kind() { ty::Adt(def, ..) => { assert!(def.is_union()); - self.tcx.dcx().emit_err(UnionPattern { span: self.span }) + self.tcx.dcx().create_err(UnionPattern { span: self.span }) } ty::FnPtr(..) | ty::RawPtr(..) => { - self.tcx.dcx().emit_err(PointerPattern { span: self.span }) + self.tcx.dcx().create_err(PointerPattern { span: self.span }) } - _ => self - .tcx - .dcx() - .emit_err(InvalidPattern { span: self.span, non_sm_ty: bad_ty }), + _ => self.tcx.dcx().create_err(InvalidPattern { + span: self.span, + non_sm_ty: bad_ty, + prefix: bad_ty.prefix_string(self.tcx).to_string(), + }), }; - return pat_from_kind(PatKind::Error(e)); + return self.mk_err(e, ty); } }; @@ -130,42 +185,16 @@ impl<'tcx> ConstToPat<'tcx> { if !inlined_const_as_pat.references_error() { // Always check for `PartialEq` if we had no other errors yet. - if !self.type_has_partial_eq_impl(ty) { - let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty }; - let e = self.tcx.dcx().emit_err(err); - return pat_from_kind(PatKind::Error(e)); + if !type_has_partial_eq_impl(self.tcx, typing_env, ty).0 { + let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty }); + extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err); + return self.mk_err(err, ty); } } inlined_const_as_pat } - #[instrument(level = "trace", skip(self), ret)] - fn type_has_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool { - let (infcx, param_env) = self.tcx.infer_ctxt().build_with_typing_env(self.typing_env); - // double-check there even *is* a semantic `PartialEq` to dispatch to. - // - // (If there isn't, then we can safely issue a hard - // error, because that's never worked, due to compiler - // using `PartialEq::eq` in this scenario in the past.) - let partial_eq_trait_id = - self.tcx.require_lang_item(hir::LangItem::PartialEq, Some(self.span)); - let partial_eq_obligation = Obligation::new( - self.tcx, - ObligationCause::dummy(), - param_env, - ty::TraitRef::new(self.tcx, partial_eq_trait_id, [ty, ty]), - ); - - // This *could* accept a type that isn't actually `PartialEq`, because region bounds get - // ignored. However that should be pretty much impossible since consts that do not depend on - // generics can only mention the `'static` lifetime, and how would one have a type that's - // `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem - // we'll need to leave some sort of trace of this requirement in the MIR so that borrowck - // can ensure that the type really implements `PartialEq`. - infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation) - } - fn field_pats( &self, vals: impl Iterator<Item = (ValTree<'tcx>, Ty<'tcx>)>, @@ -190,10 +219,25 @@ impl<'tcx> ConstToPat<'tcx> { // Extremely important check for all ADTs! Make sure they opted-in to be used in // patterns. debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty); - let err = TypeNotStructural { span, non_sm_ty: ty }; - let e = tcx.dcx().emit_err(err); - // We errored. Signal that in the pattern, so that follow up errors can be silenced. - PatKind::Error(e) + let (_impls_partial_eq, derived, structural, impl_def_id) = + type_has_partial_eq_impl(self.tcx, self.typing_env, ty); + let (manual_partialeq_impl_span, manual_partialeq_impl_note) = + match (structural, impl_def_id) { + (true, _) => (None, false), + (_, Some(def_id)) if def_id.is_local() && !derived => { + (Some(tcx.def_span(def_id)), false) + } + _ => (None, true), + }; + let ty_def_span = tcx.def_span(adt_def.did()); + let err = TypeNotStructural { + span, + ty, + ty_def_span, + manual_partialeq_impl_span, + manual_partialeq_impl_note, + }; + return self.mk_err(tcx.dcx().create_err(err), ty); } ty::Adt(adt_def, args) if adt_def.is_enum() => { let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap(); @@ -207,7 +251,7 @@ impl<'tcx> ConstToPat<'tcx> { adt_def.variants()[variant_index] .fields .iter() - .map(|field| field.ty(self.tcx, args)), + .map(|field| field.ty(tcx, args)), ), ), } @@ -216,7 +260,7 @@ impl<'tcx> ConstToPat<'tcx> { assert!(!def.is_union()); // Valtree construction would never succeed for unions. PatKind::Leaf { subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip( - def.non_enum_variant().fields.iter().map(|field| field.ty(self.tcx, args)), + def.non_enum_variant().fields.iter().map(|field| field.ty(tcx, args)), )), } } @@ -252,10 +296,10 @@ impl<'tcx> ConstToPat<'tcx> { // deref pattern. _ => { if !pointee_ty.is_sized(tcx, self.typing_env) && !pointee_ty.is_slice() { - let err = UnsizedPattern { span, non_sm_ty: *pointee_ty }; - let e = tcx.dcx().emit_err(err); - // We errored. Signal that in the pattern, so that follow up errors can be silenced. - PatKind::Error(e) + return self.mk_err( + tcx.dcx().create_err(UnsizedPattern { span, non_sm_ty: *pointee_ty }), + ty, + ); } else { // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when // matching against references, you can only use byte string literals. @@ -286,8 +330,7 @@ impl<'tcx> ConstToPat<'tcx> { if is_nan { // NaNs are not ever equal to anything so they make no sense as patterns. // Also see <https://github.com/rust-lang/rfcs/pull/3535>. - let e = tcx.dcx().emit_err(NaNPattern { span }); - PatKind::Error(e) + return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty); } else { PatKind::Constant { value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)), @@ -305,13 +348,153 @@ impl<'tcx> ConstToPat<'tcx> { ) } _ => { - let err = InvalidPattern { span, non_sm_ty: ty }; - let e = tcx.dcx().emit_err(err); - // We errored. Signal that in the pattern, so that follow up errors can be silenced. - PatKind::Error(e) + let err = InvalidPattern { + span, + non_sm_ty: ty, + prefix: ty.prefix_string(tcx).to_string(), + }; + return self.mk_err(tcx.dcx().create_err(err), ty); } }; Box::new(Pat { span, ty, kind }) } } + +/// Given a type with type parameters, visit every ADT looking for types that need to +/// `#[derive(PartialEq)]` for it to be a structural type. +fn extend_type_not_partial_eq<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, + err: &mut Diag<'_>, +) { + /// Collect all types that need to be `StructuralPartialEq`. + struct UsedParamsNeedInstantiationVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + /// The user has written `impl PartialEq for Ty` which means it's non-structual. + adts_with_manual_partialeq: FxHashSet<Span>, + /// The type has no `PartialEq` implementation, neither manual or derived. + adts_without_partialeq: FxHashSet<Span>, + /// The user has written `impl PartialEq for Ty` which means it's non-structual, + /// but we don't have a span to point at, so we'll just add them as a `note`. + manual: Vec<Ty<'tcx>>, + /// The type has no `PartialEq` implementation, neither manual or derived, but + /// we don't have a span to point at, so we'll just add them as a `note`. + without: Vec<Ty<'tcx>>, + } + + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if let ty::Adt(def, _args) = ty.kind() { + let ty_def_id = def.did(); + let ty_def_span = self.tcx.def_span(ty_def_id); + let (impls_partial_eq, derived, structural, impl_def_id) = + type_has_partial_eq_impl(self.tcx, self.typing_env, ty); + match (impls_partial_eq, derived, structural, impl_def_id) { + (_, _, true, _) => {} + (true, false, _, Some(def_id)) if def_id.is_local() => { + self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id)); + } + (true, false, _, _) if ty_def_id.is_local() => { + self.adts_with_manual_partialeq.insert(ty_def_span); + } + (false, _, _, _) if ty_def_id.is_local() => { + self.adts_without_partialeq.insert(ty_def_span); + } + (true, false, _, _) => { + self.manual.push(ty); + } + (false, _, _, _) => { + self.without.push(ty); + } + _ => {} + }; + } + use rustc_middle::ty::TypeSuperVisitable; + ty.super_visit_with(self) + } + } + let mut v = UsedParamsNeedInstantiationVisitor { + tcx, + typing_env, + adts_with_manual_partialeq: FxHashSet::default(), + adts_without_partialeq: FxHashSet::default(), + manual: vec![], + without: vec![], + }; + v.visit_ty(ty); + #[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering + for span in v.adts_with_manual_partialeq { + err.span_note(span, "the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details"); + } + #[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering + for span in v.adts_without_partialeq { + err.span_label( + span, + "must be annotated with `#[derive(PartialEq)]` to be usable in patterns", + ); + } + for ty in v.manual { + err.note(format!( + "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details" + )); + } + for ty in v.without { + err.note(format!( + "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns" + )); + } +} + +#[instrument(level = "trace", skip(tcx), ret)] +fn type_has_partial_eq_impl<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, +) -> ( + /* has impl */ bool, + /* is derived */ bool, + /* structural partial eq */ bool, + /* non-blanket impl */ Option<DefId>, +) { + let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); + // double-check there even *is* a semantic `PartialEq` to dispatch to. + // + // (If there isn't, then we can safely issue a hard + // error, because that's never worked, due to compiler + // using `PartialEq::eq` in this scenario in the past.) + let partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::PartialEq, None); + let structural_partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::StructuralPeq, None); + + let partial_eq_obligation = Obligation::new( + tcx, + ObligationCause::dummy(), + param_env, + ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]), + ); + + let mut automatically_derived = false; + let mut structural_peq = false; + let mut impl_def_id = None; + for def_id in tcx.non_blanket_impls_for_ty(partial_eq_trait_id, ty) { + automatically_derived = tcx.has_attr(def_id, sym::automatically_derived); + impl_def_id = Some(def_id); + } + for _ in tcx.non_blanket_impls_for_ty(structural_partial_eq_trait_id, ty) { + structural_peq = true; + } + // This *could* accept a type that isn't actually `PartialEq`, because region bounds get + // ignored. However that should be pretty much impossible since consts that do not depend on + // generics can only mention the `'static` lifetime, and how would one have a type that's + // `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem + // we'll need to leave some sort of trace of this requirement in the MIR so that borrowck + // can ensure that the type really implements `PartialEq`. + ( + infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation), + automatically_derived, + structural_peq, + impl_def_id, + ) +} diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 08c6b4abd3b..2dbc8b7b573 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -528,11 +528,17 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { | Res::SelfCtor(..) => PatKind::Leaf { subpatterns }, _ => { let e = match res { - Res::Def(DefKind::ConstParam, _) => { - self.tcx.dcx().emit_err(ConstParamInPattern { span }) + Res::Def(DefKind::ConstParam, def_id) => { + self.tcx.dcx().emit_err(ConstParamInPattern { + span, + const_span: self.tcx().def_span(def_id), + }) } - Res::Def(DefKind::Static { .. }, _) => { - self.tcx.dcx().emit_err(StaticInPattern { span }) + Res::Def(DefKind::Static { .. }, def_id) => { + self.tcx.dcx().emit_err(StaticInPattern { + span, + static_span: self.tcx().def_span(def_id), + }) } _ => self.tcx.dcx().emit_err(NonConstPath { span }), }; @@ -620,32 +626,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { ) -> PatKind<'tcx> { let tcx = self.tcx; let def_id = block.def_id; - let body_id = block.body; - let expr = &tcx.hir().body(body_id).value; let ty = tcx.typeck(def_id).node_type(block.hir_id); - // Special case inline consts that are just literals. This is solely - // a performance optimization, as we could also just go through the regular - // const eval path below. - // FIXME: investigate the performance impact of removing this. - let lit_input = match expr.kind { - hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), - hir::ExprKind::Unary(hir::UnOp::Neg, expr) => match expr.kind { - hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: true }), - _ => None, - }, - _ => None, - }; - if let Some(lit_input) = lit_input { - match tcx.at(expr.span).lit_to_const(lit_input) { - Ok(c) => return self.const_to_pat(c, ty, id, span).kind, - // If an error occurred, ignore that it's a literal - // and leave reporting the error up to const eval of - // the unevaluated constant below. - Err(_) => {} - } - } - let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()); let parent_args = tcx.erase_regions(ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id)); diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 6be0ed5fb31..2bcdb67c58a 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -566,11 +566,17 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_expr(field_expr.expr, depth_lvl + 2); } - if let Some(ref base) = adt_expr.base { - print_indented!(self, "base:", depth_lvl + 1); - self.print_fru_info(base, depth_lvl + 2); - } else { - print_indented!(self, "base: None", depth_lvl + 1); + match adt_expr.base { + AdtExprBase::Base(ref base) => { + print_indented!(self, "base:", depth_lvl + 1); + self.print_fru_info(base, depth_lvl + 2); + } + AdtExprBase::DefaultFields(_) => { + print_indented!(self, "base: {{ defaulted fields }}", depth_lvl + 1); + } + AdtExprBase::None => { + print_indented!(self, "base: None", depth_lvl + 1); + } } } diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index 5ebb343f4e1..4a9bcdaddb3 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -1,6 +1,7 @@ //! Random access inspection of the results of a dataflow analysis. use std::cmp::Ordering; +use std::ops::{Deref, DerefMut}; #[cfg(debug_assertions)] use rustc_index::bit_set::BitSet; @@ -8,18 +9,58 @@ use rustc_middle::mir::{self, BasicBlock, Location}; use super::{Analysis, Direction, Effect, EffectIndex, Results}; -/// Allows random access inspection of the results of a dataflow analysis. +/// Some `ResultsCursor`s want to own a `Results`, and some want to borrow a `Results`, either +/// mutable or immutably. This type allows all of the above. It's similar to `Cow`. +pub enum ResultsHandle<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + BorrowedMut(&'a mut Results<'tcx, A>), + Owned(Results<'tcx, A>), +} + +impl<'tcx, A> Deref for ResultsHandle<'_, 'tcx, A> +where + A: Analysis<'tcx>, +{ + type Target = Results<'tcx, A>; + + fn deref(&self) -> &Results<'tcx, A> { + match self { + ResultsHandle::BorrowedMut(borrowed) => borrowed, + ResultsHandle::Owned(owned) => owned, + } + } +} + +impl<'tcx, A> DerefMut for ResultsHandle<'_, 'tcx, A> +where + A: Analysis<'tcx>, +{ + fn deref_mut(&mut self) -> &mut Results<'tcx, A> { + match self { + ResultsHandle::BorrowedMut(borrowed) => borrowed, + ResultsHandle::Owned(owned) => owned, + } + } +} + +/// Allows random access inspection of the results of a dataflow analysis. Use this when you want +/// to inspect domain values only in certain locations; use `ResultsVisitor` if you want to inspect +/// domain values in many or all locations. /// -/// This cursor only has linear performance within a basic block when its statements are visited in -/// the same order as the `DIRECTION` of the analysis. In the worst case—when statements are -/// visited in *reverse* order—performance will be quadratic in the number of statements in the -/// block. The order in which basic blocks are inspected has no impact on performance. +/// Because `Results` only has domain values for the entry of each basic block, these inspections +/// involve some amount of domain value recomputations. This cursor only has linear performance +/// within a basic block when its statements are visited in the same order as the `DIRECTION` of +/// the analysis. In the worst case—when statements are visited in *reverse* order—performance will +/// be quadratic in the number of statements in the block. The order in which basic blocks are +/// inspected has no impact on performance. pub struct ResultsCursor<'mir, 'tcx, A> where A: Analysis<'tcx>, { body: &'mir mir::Body<'tcx>, - results: Results<'tcx, A>, + results: ResultsHandle<'mir, 'tcx, A>, state: A::Domain, pos: CursorPosition, @@ -47,13 +88,8 @@ where self.body } - /// Unwraps this cursor, returning the underlying `Results`. - pub fn into_results(self) -> Results<'tcx, A> { - self.results - } - /// Returns a new cursor that can inspect `results`. - pub fn new(body: &'mir mir::Body<'tcx>, results: Results<'tcx, A>) -> Self { + pub fn new(body: &'mir mir::Body<'tcx>, results: ResultsHandle<'mir, 'tcx, A>) -> Self { let bottom_value = results.analysis.bottom_value(body); ResultsCursor { body, diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 9a5cf9d4e84..566a6b09b2b 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -9,30 +9,35 @@ use super::{Analysis, Effect, EffectIndex, Results, SwitchIntTarget}; pub trait Direction { const IS_FORWARD: bool; - const IS_BACKWARD: bool = !Self::IS_FORWARD; - /// Applies all effects between the given `EffectIndex`s. - /// - /// `effects.start()` must precede or equal `effects.end()` in this direction. - fn apply_effects_in_range<'tcx, A>( + /// Called by `iterate_to_fixpoint` during initial analysis computation. + fn apply_effects_in_block<'mir, 'tcx, A>( analysis: &mut A, + body: &mir::Body<'tcx>, state: &mut A::Domain, block: BasicBlock, - block_data: &mir::BasicBlockData<'tcx>, - effects: RangeInclusive<EffectIndex>, + block_data: &'mir mir::BasicBlockData<'tcx>, + propagate: impl FnMut(BasicBlock, &A::Domain), ) where A: Analysis<'tcx>; - fn apply_effects_in_block<'mir, 'tcx, A>( + /// Called by `ResultsCursor` to recompute the domain value for a location + /// in a basic block. Applies all effects between the given `EffectIndex`s. + /// + /// `effects.start()` must precede or equal `effects.end()` in this direction. + fn apply_effects_in_range<'tcx, A>( analysis: &mut A, state: &mut A::Domain, block: BasicBlock, - block_data: &'mir mir::BasicBlockData<'tcx>, - ) -> TerminatorEdges<'mir, 'tcx> - where + block_data: &mir::BasicBlockData<'tcx>, + effects: RangeInclusive<EffectIndex>, + ) where A: Analysis<'tcx>; + /// Called by `ResultsVisitor` to recompute the analysis domain values for + /// all locations in a basic block (starting from the entry value stored + /// in `Results`) and to visit them with `vis`. fn visit_results_in_block<'mir, 'tcx, A>( state: &mut A::Domain, block: BasicBlock, @@ -41,16 +46,6 @@ pub trait Direction { vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, ) where A: Analysis<'tcx>; - - fn join_state_into_successors_of<'tcx, A>( - analysis: &mut A, - body: &mir::Body<'tcx>, - exit_state: &mut A::Domain, - block: BasicBlock, - edges: TerminatorEdges<'_, 'tcx>, - propagate: impl FnMut(BasicBlock, &A::Domain), - ) where - A: Analysis<'tcx>; } /// Dataflow that runs from the exit of a block (terminator), to its entry (the first statement). @@ -61,23 +56,84 @@ impl Direction for Backward { fn apply_effects_in_block<'mir, 'tcx, A>( analysis: &mut A, + body: &mir::Body<'tcx>, state: &mut A::Domain, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - ) -> TerminatorEdges<'mir, 'tcx> - where + mut propagate: impl FnMut(BasicBlock, &A::Domain), + ) where A: Analysis<'tcx>, { let terminator = block_data.terminator(); let location = Location { block, statement_index: block_data.statements.len() }; analysis.apply_before_terminator_effect(state, terminator, location); - let edges = analysis.apply_terminator_effect(state, terminator, location); + analysis.apply_terminator_effect(state, terminator, location); for (statement_index, statement) in block_data.statements.iter().enumerate().rev() { let location = Location { block, statement_index }; analysis.apply_before_statement_effect(state, statement, location); analysis.apply_statement_effect(state, statement, location); } - edges + + let exit_state = state; + for pred in body.basic_blocks.predecessors()[block].iter().copied() { + match body[pred].terminator().kind { + // Apply terminator-specific edge effects. + // + // FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally. + mir::TerminatorKind::Call { destination, target: Some(dest), .. } + if dest == block => + { + let mut tmp = exit_state.clone(); + analysis.apply_call_return_effect( + &mut tmp, + pred, + CallReturnPlaces::Call(destination), + ); + propagate(pred, &tmp); + } + + mir::TerminatorKind::InlineAsm { ref targets, ref operands, .. } + if targets.contains(&block) => + { + let mut tmp = exit_state.clone(); + analysis.apply_call_return_effect( + &mut tmp, + pred, + CallReturnPlaces::InlineAsm(operands), + ); + propagate(pred, &tmp); + } + + mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == block => { + let mut tmp = exit_state.clone(); + analysis.apply_call_return_effect( + &mut tmp, + resume, + CallReturnPlaces::Yield(resume_arg), + ); + propagate(pred, &tmp); + } + + mir::TerminatorKind::SwitchInt { targets: _, ref discr } => { + let mut applier = BackwardSwitchIntEdgeEffectsApplier { + body, + pred, + exit_state, + block, + propagate: &mut propagate, + effects_applied: false, + }; + + analysis.apply_switch_int_edge_effects(pred, discr, &mut applier); + + if !applier.effects_applied { + propagate(pred, exit_state) + } + } + + _ => propagate(pred, exit_state), + } + } } fn apply_effects_in_range<'tcx, A>( @@ -170,7 +226,6 @@ impl Direction for Backward { vis.visit_block_end(state); - // Terminator let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); results.analysis.apply_before_terminator_effect(state, term, loc); @@ -188,82 +243,13 @@ impl Direction for Backward { vis.visit_block_start(state); } - - fn join_state_into_successors_of<'tcx, A>( - analysis: &mut A, - body: &mir::Body<'tcx>, - exit_state: &mut A::Domain, - bb: BasicBlock, - _edges: TerminatorEdges<'_, 'tcx>, - mut propagate: impl FnMut(BasicBlock, &A::Domain), - ) where - A: Analysis<'tcx>, - { - for pred in body.basic_blocks.predecessors()[bb].iter().copied() { - match body[pred].terminator().kind { - // Apply terminator-specific edge effects. - // - // FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally. - mir::TerminatorKind::Call { destination, target: Some(dest), .. } if dest == bb => { - let mut tmp = exit_state.clone(); - analysis.apply_call_return_effect( - &mut tmp, - pred, - CallReturnPlaces::Call(destination), - ); - propagate(pred, &tmp); - } - - mir::TerminatorKind::InlineAsm { ref targets, ref operands, .. } - if targets.contains(&bb) => - { - let mut tmp = exit_state.clone(); - analysis.apply_call_return_effect( - &mut tmp, - pred, - CallReturnPlaces::InlineAsm(operands), - ); - propagate(pred, &tmp); - } - - mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == bb => { - let mut tmp = exit_state.clone(); - analysis.apply_call_return_effect( - &mut tmp, - resume, - CallReturnPlaces::Yield(resume_arg), - ); - propagate(pred, &tmp); - } - - mir::TerminatorKind::SwitchInt { targets: _, ref discr } => { - let mut applier = BackwardSwitchIntEdgeEffectsApplier { - body, - pred, - exit_state, - bb, - propagate: &mut propagate, - effects_applied: false, - }; - - analysis.apply_switch_int_edge_effects(pred, discr, &mut applier); - - if !applier.effects_applied { - propagate(pred, exit_state) - } - } - - _ => propagate(pred, exit_state), - } - } - } } struct BackwardSwitchIntEdgeEffectsApplier<'mir, 'tcx, D, F> { body: &'mir mir::Body<'tcx>, pred: BasicBlock, exit_state: &'mir mut D, - bb: BasicBlock, + block: BasicBlock, propagate: &'mir mut F, effects_applied: bool, } @@ -276,8 +262,8 @@ where fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) { assert!(!self.effects_applied); - let values = &self.body.basic_blocks.switch_sources()[&(self.bb, self.pred)]; - let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.bb }); + let values = &self.body.basic_blocks.switch_sources()[&(self.block, self.pred)]; + let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.block }); let mut tmp = None; for target in targets { @@ -298,11 +284,12 @@ impl Direction for Forward { fn apply_effects_in_block<'mir, 'tcx, A>( analysis: &mut A, + _body: &mir::Body<'tcx>, state: &mut A::Domain, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - ) -> TerminatorEdges<'mir, 'tcx> - where + mut propagate: impl FnMut(BasicBlock, &A::Domain), + ) where A: Analysis<'tcx>, { for (statement_index, statement) in block_data.statements.iter().enumerate() { @@ -313,7 +300,53 @@ impl Direction for Forward { let terminator = block_data.terminator(); let location = Location { block, statement_index: block_data.statements.len() }; analysis.apply_before_terminator_effect(state, terminator, location); - analysis.apply_terminator_effect(state, terminator, location) + let edges = analysis.apply_terminator_effect(state, terminator, location); + + let exit_state = state; + match edges { + TerminatorEdges::None => {} + TerminatorEdges::Single(target) => propagate(target, exit_state), + TerminatorEdges::Double(target, unwind) => { + propagate(target, exit_state); + propagate(unwind, exit_state); + } + TerminatorEdges::AssignOnReturn { return_, cleanup, place } => { + // This must be done *first*, otherwise the unwind path will see the assignments. + if let Some(cleanup) = cleanup { + propagate(cleanup, exit_state); + } + + if !return_.is_empty() { + analysis.apply_call_return_effect(exit_state, block, place); + for &target in return_ { + propagate(target, exit_state); + } + } + } + TerminatorEdges::SwitchInt { targets, discr } => { + let mut applier = ForwardSwitchIntEdgeEffectsApplier { + exit_state, + targets, + propagate, + effects_applied: false, + }; + + analysis.apply_switch_int_edge_effects(block, discr, &mut applier); + + let ForwardSwitchIntEdgeEffectsApplier { + exit_state, + mut propagate, + effects_applied, + .. + } = applier; + + if !effects_applied { + for target in targets.all_targets() { + propagate(*target, exit_state); + } + } + } + } } fn apply_effects_in_range<'tcx, A>( @@ -351,7 +384,8 @@ impl Direction for Forward { let statement = &block_data.statements[from.statement_index]; analysis.apply_statement_effect(state, statement, location); - // If we only needed to apply the after effect of the statement at `idx`, we are done. + // If we only needed to apply the after effect of the statement at `idx`, we are + // done. if from == to { return; } @@ -419,62 +453,6 @@ impl Direction for Forward { vis.visit_block_end(state); } - - fn join_state_into_successors_of<'tcx, A>( - analysis: &mut A, - _body: &mir::Body<'tcx>, - exit_state: &mut A::Domain, - bb: BasicBlock, - edges: TerminatorEdges<'_, 'tcx>, - mut propagate: impl FnMut(BasicBlock, &A::Domain), - ) where - A: Analysis<'tcx>, - { - match edges { - TerminatorEdges::None => {} - TerminatorEdges::Single(target) => propagate(target, exit_state), - TerminatorEdges::Double(target, unwind) => { - propagate(target, exit_state); - propagate(unwind, exit_state); - } - TerminatorEdges::AssignOnReturn { return_, cleanup, place } => { - // This must be done *first*, otherwise the unwind path will see the assignments. - if let Some(cleanup) = cleanup { - propagate(cleanup, exit_state); - } - - if !return_.is_empty() { - analysis.apply_call_return_effect(exit_state, bb, place); - for &target in return_ { - propagate(target, exit_state); - } - } - } - TerminatorEdges::SwitchInt { targets, discr } => { - let mut applier = ForwardSwitchIntEdgeEffectsApplier { - exit_state, - targets, - propagate, - effects_applied: false, - }; - - analysis.apply_switch_int_edge_effects(bb, discr, &mut applier); - - let ForwardSwitchIntEdgeEffectsApplier { - exit_state, - mut propagate, - effects_applied, - .. - } = applier; - - if !effects_applied { - for target in targets.all_targets() { - propagate(*target, exit_state); - } - } - } - } - } } struct ForwardSwitchIntEdgeEffectsApplier<'mir, D, F> { diff --git a/compiler/rustc_mir_dataflow/src/framework/fmt.rs b/compiler/rustc_mir_dataflow/src/framework/fmt.rs index ed540cd148c..faf2c411dde 100644 --- a/compiler/rustc_mir_dataflow/src/framework/fmt.rs +++ b/compiler/rustc_mir_dataflow/src/framework/fmt.rs @@ -4,7 +4,7 @@ use std::fmt; use rustc_index::Idx; -use rustc_index::bit_set::{BitSet, ChunkedBitSet, HybridBitSet}; +use rustc_index::bit_set::{BitSet, ChunkedBitSet, MixedBitSet}; use super::lattice::MaybeReachable; @@ -85,8 +85,8 @@ where let size = self.domain_size(); assert_eq!(size, old.domain_size()); - let mut set_in_self = HybridBitSet::new_empty(size); - let mut cleared_in_self = HybridBitSet::new_empty(size); + let mut set_in_self = MixedBitSet::new_empty(size); + let mut cleared_in_self = MixedBitSet::new_empty(size); for i in (0..size).map(T::new) { match (self.contains(i), old.contains(i)) { @@ -112,8 +112,8 @@ where let size = self.domain_size(); assert_eq!(size, old.domain_size()); - let mut set_in_self = HybridBitSet::new_empty(size); - let mut cleared_in_self = HybridBitSet::new_empty(size); + let mut set_in_self = MixedBitSet::new_empty(size); + let mut cleared_in_self = MixedBitSet::new_empty(size); for i in (0..size).map(T::new) { match (self.contains(i), old.contains(i)) { @@ -127,6 +127,26 @@ where } } +impl<T, C> DebugWithContext<C> for MixedBitSet<T> +where + T: Idx + DebugWithContext<C>, +{ + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MixedBitSet::Small(set) => set.fmt_with(ctxt, f), + MixedBitSet::Large(set) => set.fmt_with(ctxt, f), + } + } + + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match (self, old) { + (MixedBitSet::Small(set), MixedBitSet::Small(old)) => set.fmt_diff_with(old, ctxt, f), + (MixedBitSet::Large(set), MixedBitSet::Large(old)) => set.fmt_diff_with(old, ctxt, f), + _ => panic!("MixedBitSet size mismatch"), + } + } +} + impl<S, C> DebugWithContext<C> for MaybeReachable<S> where S: DebugWithContext<C>, @@ -159,8 +179,8 @@ where } fn fmt_diff<T, C>( - inserted: &HybridBitSet<T>, - removed: &HybridBitSet<T>, + inserted: &MixedBitSet<T>, + removed: &MixedBitSet<T>, ctxt: &C, f: &mut fmt::Formatter<'_>, ) -> fmt::Result @@ -230,16 +250,3 @@ where write!(f, "{}", ctxt.move_data().move_paths[*self]) } } - -impl<T, C> DebugWithContext<C> for crate::lattice::Dual<T> -where - T: DebugWithContext<C>, -{ - fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (self.0).fmt_with(ctxt, f) - } - - fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (self.0).fmt_diff_with(&old.0, ctxt, f) - } -} diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 98a4f58cb5d..561229cf725 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -47,20 +47,16 @@ where { pub(crate) fn new( body: &'mir Body<'tcx>, - results: Results<'tcx, A>, + results: &'mir mut Results<'tcx, A>, style: OutputStyle, ) -> Self { let reachable = mir::traversal::reachable_as_bitset(body); - Formatter { cursor: results.into_results_cursor(body).into(), style, reachable } + Formatter { cursor: results.as_results_cursor(body).into(), style, reachable } } fn body(&self) -> &'mir Body<'tcx> { self.cursor.borrow().body() } - - pub(crate) fn into_results(self) -> Results<'tcx, A> { - self.cursor.into_inner().into_results() - } } /// A pair of a basic block and an index into that basic blocks `successors`. diff --git a/compiler/rustc_mir_dataflow/src/framework/lattice.rs b/compiler/rustc_mir_dataflow/src/framework/lattice.rs index 4d03ee53b7c..e063eaf74bd 100644 --- a/compiler/rustc_mir_dataflow/src/framework/lattice.rs +++ b/compiler/rustc_mir_dataflow/src/framework/lattice.rs @@ -25,8 +25,8 @@ //! //! ## `PartialOrd` //! -//! Given that they represent partially ordered sets, you may be surprised that [`JoinSemiLattice`] -//! and [`MeetSemiLattice`] do not have [`PartialOrd`] as a supertrait. This +//! Given that it represents a partially ordered set, you may be surprised that [`JoinSemiLattice`] +//! does not have [`PartialOrd`] as a supertrait. This //! is because most standard library types use lexicographic ordering instead of set inclusion for //! their `PartialOrd` impl. Since we do not actually need to compare lattice elements to run a //! dataflow analysis, there's no need for a newtype wrapper with a custom `PartialOrd` impl. The @@ -40,7 +40,7 @@ use std::iter; -use rustc_index::bit_set::{BitSet, ChunkedBitSet, HybridBitSet}; +use rustc_index::bit_set::{BitSet, MixedBitSet}; use rustc_index::{Idx, IndexVec}; use crate::framework::BitSetExt; @@ -58,23 +58,6 @@ pub trait JoinSemiLattice: Eq { fn join(&mut self, other: &Self) -> bool; } -/// A [partially ordered set][poset] that has a [greatest lower bound][glb] for any pair of -/// elements in the set. -/// -/// Dataflow analyses only require that their domains implement [`JoinSemiLattice`], not -/// `MeetSemiLattice`. However, types that will be used as dataflow domains should implement both -/// so that they can be used with [`Dual`]. -/// -/// [glb]: https://en.wikipedia.org/wiki/Infimum_and_supremum -/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set -pub trait MeetSemiLattice: Eq { - /// Computes the greatest lower bound of two elements, storing the result in `self` and - /// returning `true` if `self` has changed. - /// - /// The lattice meet operator is abbreviated as `∧`. - fn meet(&mut self, other: &Self) -> bool; -} - /// A set that has a "bottom" element, which is less than or equal to any other element. pub trait HasBottom { const BOTTOM: Self; @@ -105,17 +88,6 @@ impl JoinSemiLattice for bool { } } -impl MeetSemiLattice for bool { - fn meet(&mut self, other: &Self) -> bool { - if let (true, false) = (*self, *other) { - *self = false; - return true; - } - - false - } -} - impl HasBottom for bool { const BOTTOM: Self = false; @@ -145,18 +117,6 @@ impl<I: Idx, T: JoinSemiLattice> JoinSemiLattice for IndexVec<I, T> { } } -impl<I: Idx, T: MeetSemiLattice> MeetSemiLattice for IndexVec<I, T> { - fn meet(&mut self, other: &Self) -> bool { - assert_eq!(self.len(), other.len()); - - let mut changed = false; - for (a, b) in iter::zip(self, other) { - changed |= a.meet(b); - } - changed - } -} - /// A `BitSet` represents the lattice formed by the powerset of all possible values of /// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices, /// one for each possible value of `T`. @@ -166,60 +126,12 @@ impl<T: Idx> JoinSemiLattice for BitSet<T> { } } -impl<T: Idx> MeetSemiLattice for BitSet<T> { - fn meet(&mut self, other: &Self) -> bool { - self.intersect(other) - } -} - -impl<T: Idx> JoinSemiLattice for ChunkedBitSet<T> { +impl<T: Idx> JoinSemiLattice for MixedBitSet<T> { fn join(&mut self, other: &Self) -> bool { self.union(other) } } -impl<T: Idx> MeetSemiLattice for ChunkedBitSet<T> { - fn meet(&mut self, other: &Self) -> bool { - self.intersect(other) - } -} - -/// The counterpart of a given semilattice `T` using the [inverse order]. -/// -/// The dual of a join-semilattice is a meet-semilattice and vice versa. For example, the dual of a -/// powerset has the empty set as its top element and the full set as its bottom element and uses -/// set *intersection* as its join operator. -/// -/// [inverse order]: https://en.wikipedia.org/wiki/Duality_(order_theory) -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Dual<T>(pub T); - -impl<T: Idx> BitSetExt<T> for Dual<BitSet<T>> { - fn contains(&self, elem: T) -> bool { - self.0.contains(elem) - } - - fn union(&mut self, other: &HybridBitSet<T>) { - self.0.union(other); - } - - fn subtract(&mut self, other: &HybridBitSet<T>) { - self.0.subtract(other); - } -} - -impl<T: MeetSemiLattice> JoinSemiLattice for Dual<T> { - fn join(&mut self, other: &Self) -> bool { - self.0.meet(&other.0) - } -} - -impl<T: JoinSemiLattice> MeetSemiLattice for Dual<T> { - fn meet(&mut self, other: &Self) -> bool { - self.0.join(&other.0) - } -} - /// Extends a type `T` with top and bottom elements to make it a partially ordered set in which no /// value of `T` is comparable with any other. /// @@ -257,22 +169,6 @@ impl<T: Clone + Eq> JoinSemiLattice for FlatSet<T> { } } -impl<T: Clone + Eq> MeetSemiLattice for FlatSet<T> { - fn meet(&mut self, other: &Self) -> bool { - let result = match (&*self, other) { - (Self::Bottom, _) | (_, Self::Top) => return false, - (Self::Elem(ref a), Self::Elem(ref b)) if a == b => return false, - - (Self::Top, Self::Elem(ref x)) => Self::Elem(x.clone()), - - _ => Self::Bottom, - }; - - *self = result; - true - } -} - impl<T> HasBottom for FlatSet<T> { const BOTTOM: Self = Self::Bottom; @@ -331,20 +227,6 @@ impl<T, S: BitSetExt<T>> BitSetExt<T> for MaybeReachable<S> { fn contains(&self, elem: T) -> bool { self.contains(elem) } - - fn union(&mut self, other: &HybridBitSet<T>) { - match self { - MaybeReachable::Unreachable => {} - MaybeReachable::Reachable(set) => set.union(other), - } - } - - fn subtract(&mut self, other: &HybridBitSet<T>) { - match self { - MaybeReachable::Unreachable => {} - MaybeReachable::Reachable(set) => set.subtract(other), - } - } } impl<V: Clone> Clone for MaybeReachable<V> { diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index 244dfe26ad3..caff2a81ff3 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -8,8 +8,9 @@ //! The `impls` module contains several examples of dataflow analyses. //! //! Then call `iterate_to_fixpoint` on your type that impls `Analysis` to get a `Results`. From -//! there, you can use a `ResultsCursor` to inspect the fixpoint solution to your dataflow problem, -//! or implement the `ResultsVisitor` interface and use `visit_results`. The following example uses +//! there, you can use a `ResultsCursor` to inspect the fixpoint solution to your dataflow problem +//! (good for inspecting a small number of locations), or implement the `ResultsVisitor` interface +//! and use `visit_results` (good for inspecting many or all locations). The following example uses //! the `ResultsCursor` approach. //! //! ```ignore (cross-crate-imports) @@ -34,7 +35,7 @@ use std::cmp::Ordering; use rustc_data_structures::work_queue::WorkQueue; -use rustc_index::bit_set::{BitSet, ChunkedBitSet, HybridBitSet}; +use rustc_index::bit_set::{BitSet, MixedBitSet}; use rustc_index::{Idx, IndexVec}; use rustc_middle::bug; use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges, traversal}; @@ -62,36 +63,18 @@ pub use self::visitor::{ResultsVisitor, visit_results}; /// operations needed by all of them. pub trait BitSetExt<T> { fn contains(&self, elem: T) -> bool; - fn union(&mut self, other: &HybridBitSet<T>); - fn subtract(&mut self, other: &HybridBitSet<T>); } impl<T: Idx> BitSetExt<T> for BitSet<T> { fn contains(&self, elem: T) -> bool { self.contains(elem) } - - fn union(&mut self, other: &HybridBitSet<T>) { - self.union(other); - } - - fn subtract(&mut self, other: &HybridBitSet<T>) { - self.subtract(other); - } } -impl<T: Idx> BitSetExt<T> for ChunkedBitSet<T> { +impl<T: Idx> BitSetExt<T> for MixedBitSet<T> { fn contains(&self, elem: T) -> bool { self.contains(elem) } - - fn union(&mut self, other: &HybridBitSet<T>) { - self.union(other); - } - - fn subtract(&mut self, other: &HybridBitSet<T>) { - self.subtract(other); - } } /// A dataflow problem with an arbitrarily complex transfer function. @@ -278,22 +261,17 @@ pub trait Analysis<'tcx> { // every iteration. let mut state = self.bottom_value(body); while let Some(bb) = dirty_queue.pop() { - let bb_data = &body[bb]; - // Set the state to the entry state of the block. // This is equivalent to `state = entry_sets[bb].clone()`, // but it saves an allocation, thus improving compile times. state.clone_from(&entry_sets[bb]); - // Apply the block transfer function, using the cached one if it exists. - let edges = Self::Direction::apply_effects_in_block(&mut self, &mut state, bb, bb_data); - - Self::Direction::join_state_into_successors_of( + Self::Direction::apply_effects_in_block( &mut self, body, &mut state, bb, - edges, + &body[bb], |target: BasicBlock, state: &Self::Domain| { let set_changed = entry_sets[target].join(state); if set_changed { @@ -303,17 +281,16 @@ pub trait Analysis<'tcx> { ); } - let results = Results { analysis: self, entry_sets }; + let mut results = Results { analysis: self, entry_sets }; if tcx.sess.opts.unstable_opts.dump_mir_dataflow { - let (res, results) = write_graphviz_results(tcx, body, results, pass_name); + let res = write_graphviz_results(tcx, body, &mut results, pass_name); if let Err(e) = res { error!("Failed to write graphviz dataflow results: {}", e); } - results - } else { - results } + + results } } @@ -350,7 +327,7 @@ impl<T: Idx> GenKill<T> for BitSet<T> { } } -impl<T: Idx> GenKill<T> for ChunkedBitSet<T> { +impl<T: Idx> GenKill<T> for MixedBitSet<T> { fn gen_(&mut self, elem: T) { self.insert(elem); } @@ -378,16 +355,6 @@ impl<T, S: GenKill<T>> GenKill<T> for MaybeReachable<S> { } } -impl<T: Idx> GenKill<T> for lattice::Dual<BitSet<T>> { - fn gen_(&mut self, elem: T) { - self.0.insert(elem); - } - - fn kill(&mut self, elem: T) { - self.0.remove(elem); - } -} - // NOTE: DO NOT CHANGE VARIANT ORDER. The derived `Ord` impls rely on the current order. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] enum Effect { diff --git a/compiler/rustc_mir_dataflow/src/framework/results.rs b/compiler/rustc_mir_dataflow/src/framework/results.rs index ff6cafbfbae..c4321c454e6 100644 --- a/compiler/rustc_mir_dataflow/src/framework/results.rs +++ b/compiler/rustc_mir_dataflow/src/framework/results.rs @@ -17,10 +17,13 @@ use super::{Analysis, ResultsCursor, ResultsVisitor, graphviz, visit_results}; use crate::errors::{ DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter, }; +use crate::framework::cursor::ResultsHandle; pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>; -/// A dataflow analysis that has converged to fixpoint. +/// A dataflow analysis that has converged to fixpoint. It only holds the domain values at the +/// entry of each basic block. Domain values in other parts of the block are recomputed on the fly +/// by visitors (i.e. `ResultsCursor`, or `ResultsVisitor` impls). #[derive(Clone)] pub struct Results<'tcx, A> where @@ -34,12 +37,21 @@ impl<'tcx, A> Results<'tcx, A> where A: Analysis<'tcx>, { - /// Creates a `ResultsCursor` that can inspect these `Results`. + /// Creates a `ResultsCursor` that mutably borrows the `Results`, which is appropriate when the + /// `Results` is also used outside the cursor. + pub fn as_results_cursor<'mir>( + &'mir mut self, + body: &'mir mir::Body<'tcx>, + ) -> ResultsCursor<'mir, 'tcx, A> { + ResultsCursor::new(body, ResultsHandle::BorrowedMut(self)) + } + + /// Creates a `ResultsCursor` that takes ownership of the `Results`. pub fn into_results_cursor<'mir>( self, body: &'mir mir::Body<'tcx>, ) -> ResultsCursor<'mir, 'tcx, A> { - ResultsCursor::new(body, self) + ResultsCursor::new(body, ResultsHandle::Owned(self)) } /// Gets the dataflow state for the given block. @@ -74,9 +86,9 @@ where pub(super) fn write_graphviz_results<'tcx, A>( tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, - results: Results<'tcx, A>, + results: &mut Results<'tcx, A>, pass_name: Option<&'static str>, -) -> (std::io::Result<()>, Results<'tcx, A>) +) -> std::io::Result<()> where A: Analysis<'tcx>, A::Domain: DebugWithContext<A>, @@ -87,7 +99,7 @@ where let def_id = body.source.def_id(); let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else { // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse` - return (Ok(()), results); + return Ok(()); }; let file = try { @@ -104,12 +116,12 @@ where create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? } - _ => return (Ok(()), results), + _ => return Ok(()), } }; let mut file = match file { Ok(f) => f, - Err(e) => return (Err(e), results), + Err(e) => return Err(e), }; let style = match attrs.formatter { @@ -132,7 +144,7 @@ where file.write_all(&buf)?; }; - (lhs, graphviz.into_results()) + lhs } #[derive(Default)] diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index 5c7539eed4d..bde41974d47 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -26,7 +26,9 @@ pub fn visit_results<'mir, 'tcx, A>( } } -/// A visitor over the results of an `Analysis`. +/// A visitor over the results of an `Analysis`. Use this when you want to inspect domain values in +/// many or all locations; use `ResultsCursor` if you want to inspect domain values only in certain +/// locations. pub trait ResultsVisitor<'mir, 'tcx, A> where A: Analysis<'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 859019fd1f6..cec654cac72 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -15,7 +15,7 @@ use crate::{Analysis, GenKill}; pub struct MaybeBorrowedLocals; impl MaybeBorrowedLocals { - pub(super) fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T> { + pub(super) fn transfer_function<'a, T>(trans: &'a mut T) -> TransferFunction<'a, T> { TransferFunction { trans } } } @@ -39,7 +39,7 @@ impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals { statement: &Statement<'tcx>, location: Location, ) { - self.transfer_function(trans).visit_statement(statement, location); + Self::transfer_function(trans).visit_statement(statement, location); } fn apply_terminator_effect<'mir>( @@ -48,7 +48,7 @@ impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals { terminator: &'mir Terminator<'tcx>, location: Location, ) -> TerminatorEdges<'mir, 'tcx> { - self.transfer_function(trans).visit_terminator(terminator, location); + Self::transfer_function(trans).visit_terminator(terminator, location); terminator.edges() } } diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 9bb50d1e056..91677657602 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -1,7 +1,7 @@ use std::assert_matches::assert_matches; use rustc_index::Idx; -use rustc_index::bit_set::{BitSet, ChunkedBitSet}; +use rustc_index::bit_set::{BitSet, MixedBitSet}; use rustc_middle::bug; use rustc_middle::mir::{self, Body, CallReturnPlaces, Location, TerminatorEdges}; use rustc_middle::ty::{self, TyCtxt}; @@ -12,7 +12,7 @@ use crate::framework::SwitchIntEdgeEffects; use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex}; use crate::{ Analysis, GenKill, MaybeReachable, drop_flag_effects, drop_flag_effects_for_function_entry, - drop_flag_effects_for_location, lattice, on_all_children_bits, on_lookup_result_bits, + drop_flag_effects_for_location, on_all_children_bits, on_lookup_result_bits, }; /// `MaybeInitializedPlaces` tracks all places that might be @@ -42,10 +42,10 @@ use crate::{ /// } /// ``` /// -/// To determine whether a place *must* be initialized at a -/// particular control-flow point, one can take the set-difference -/// between this data and the data from `MaybeUninitializedPlaces` at the -/// corresponding control-flow point. +/// To determine whether a place is *definitely* initialized at a +/// particular control-flow point, one can take the set-complement +/// of the data from `MaybeUninitializedPlaces` at the corresponding +/// control-flow point. /// /// Similarly, at a given `drop` statement, the set-intersection /// between this data and `MaybeUninitializedPlaces` yields the set of @@ -70,7 +70,7 @@ impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { pub fn is_unwind_dead( &self, place: mir::Place<'tcx>, - state: &MaybeReachable<ChunkedBitSet<MovePathIndex>>, + state: &MaybeReachable<MixedBitSet<MovePathIndex>>, ) -> bool { if let LookupResult::Exact(path) = self.move_data().rev_lookup.find(place.as_ref()) { let mut maybe_live = false; @@ -117,10 +117,10 @@ impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> { /// } /// ``` /// -/// To determine whether a place *must* be uninitialized at a -/// particular control-flow point, one can take the set-difference -/// between this data and the data from `MaybeInitializedPlaces` at the -/// corresponding control-flow point. +/// To determine whether a place is *definitely* uninitialized at a +/// particular control-flow point, one can take the set-complement +/// of the data from `MaybeInitializedPlaces` at the corresponding +/// control-flow point. /// /// Similarly, at a given `drop` statement, the set-intersection /// between this data and `MaybeInitializedPlaces` yields the set of @@ -170,57 +170,6 @@ impl<'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } } -/// `DefinitelyInitializedPlaces` tracks all places that are definitely -/// initialized upon reaching a particular point in the control flow -/// for a function. -/// -/// For example, in code like the following, we have corresponding -/// dataflow information shown in the right-hand comments. -/// -/// ```rust -/// struct S; -/// fn foo(pred: bool) { // definite-init: -/// // { } -/// let a = S; let mut b = S; let c; let d; // {a, b } -/// -/// if pred { -/// drop(a); // { b, } -/// b = S; // { b, } -/// -/// } else { -/// drop(b); // {a, } -/// d = S; // {a, d} -/// -/// } // { } -/// -/// c = S; // { c } -/// } -/// ``` -/// -/// To determine whether a place *may* be uninitialized at a -/// particular control-flow point, one can take the set-complement -/// of this data. -/// -/// Similarly, at a given `drop` statement, the set-difference between -/// this data and `MaybeInitializedPlaces` yields the set of places -/// that would require a dynamic drop-flag at that statement. -pub struct DefinitelyInitializedPlaces<'a, 'tcx> { - body: &'a Body<'tcx>, - move_data: &'a MoveData<'tcx>, -} - -impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> { - pub fn new(body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self { - DefinitelyInitializedPlaces { body, move_data } - } -} - -impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { - fn move_data(&self) -> &MoveData<'tcx> { - self.move_data - } -} - /// `EverInitializedPlaces` tracks all places that might have ever been /// initialized upon reaching a particular point in the control flow /// for a function, without an intervening `StorageDead`. @@ -293,23 +242,10 @@ impl<'tcx> MaybeUninitializedPlaces<'_, 'tcx> { } } -impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> { - fn update_bits( - trans: &mut <Self as Analysis<'tcx>>::Domain, - path: MovePathIndex, - state: DropFlagState, - ) { - match state { - DropFlagState::Absent => trans.kill(path), - DropFlagState::Present => trans.gen_(path), - } - } -} - impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { /// There can be many more `MovePathIndex` than there are locals in a MIR body. - /// We use a chunked bitset to avoid paying too high a memory footprint. - type Domain = MaybeReachable<ChunkedBitSet<MovePathIndex>>; + /// We use a mixed bitset to avoid paying too high a memory footprint. + type Domain = MaybeReachable<MixedBitSet<MovePathIndex>>; const NAME: &'static str = "maybe_init"; @@ -320,7 +256,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { *state = - MaybeReachable::Reachable(ChunkedBitSet::new_empty(self.move_data().move_paths.len())); + MaybeReachable::Reachable(MixedBitSet::new_empty(self.move_data().move_paths.len())); drop_flag_effects_for_function_entry(self.body, self.move_data, |path, s| { assert!(s == DropFlagState::Present); state.gen_(path); @@ -435,14 +371,14 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { /// There can be many more `MovePathIndex` than there are locals in a MIR body. - /// We use a chunked bitset to avoid paying too high a memory footprint. - type Domain = ChunkedBitSet<MovePathIndex>; + /// We use a mixed bitset to avoid paying too high a memory footprint. + type Domain = MixedBitSet<MovePathIndex>; const NAME: &'static str = "maybe_uninit"; fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { // bottom = initialized (start_block_effect counters this at outset) - ChunkedBitSet::new_empty(self.move_data().move_paths.len()) + MixedBitSet::new_empty(self.move_data().move_paths.len()) } // sets on_entry bits for Arg places @@ -554,80 +490,16 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } } -impl<'a, 'tcx> Analysis<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { - /// Use set intersection as the join operator. - type Domain = lattice::Dual<BitSet<MovePathIndex>>; - - const NAME: &'static str = "definite_init"; - - fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { - // bottom = initialized (start_block_effect counters this at outset) - lattice::Dual(BitSet::new_filled(self.move_data().move_paths.len())) - } - - // sets on_entry bits for Arg places - fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { - state.0.clear(); - - drop_flag_effects_for_function_entry(self.body, self.move_data, |path, s| { - assert!(s == DropFlagState::Present); - state.0.insert(path); - }); - } - - fn apply_statement_effect( - &mut self, - trans: &mut Self::Domain, - _statement: &mir::Statement<'tcx>, - location: Location, - ) { - drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| { - Self::update_bits(trans, path, s) - }) - } - - fn apply_terminator_effect<'mir>( - &mut self, - trans: &mut Self::Domain, - terminator: &'mir mir::Terminator<'tcx>, - location: Location, - ) -> TerminatorEdges<'mir, 'tcx> { - drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| { - Self::update_bits(trans, path, s) - }); - terminator.edges() - } - - fn apply_call_return_effect( - &mut self, - trans: &mut Self::Domain, - _block: mir::BasicBlock, - return_places: CallReturnPlaces<'_, 'tcx>, - ) { - return_places.for_each(|place| { - // when a call returns successfully, that means we need to set - // the bits for that dest_place to 1 (initialized). - on_lookup_result_bits( - self.move_data(), - self.move_data().rev_lookup.find(place.as_ref()), - |mpi| { - trans.gen_(mpi); - }, - ); - }); - } -} - impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { /// There can be many more `InitIndex` than there are locals in a MIR body. - /// We use a chunked bitset to avoid paying too high a memory footprint. - type Domain = ChunkedBitSet<InitIndex>; + /// We use a mixed bitset to avoid paying too high a memory footprint. + type Domain = MixedBitSet<InitIndex>; const NAME: &'static str = "ever_init"; fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { // bottom = no initialized variables by default - ChunkedBitSet::new_empty(self.move_data().inits.len()) + MixedBitSet::new_empty(self.move_data().inits.len()) } fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index 9b5bfa9bfa0..b9e194b00c5 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -9,10 +9,11 @@ mod storage_liveness; pub use self::borrowed_locals::{MaybeBorrowedLocals, borrowed_locals}; pub use self::initialized::{ - DefinitelyInitializedPlaces, EverInitializedPlaces, MaybeInitializedPlaces, - MaybeUninitializedPlaces, + EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces, }; pub use self::liveness::{ MaybeLiveLocals, MaybeTransitiveLiveLocals, TransferFunction as LivenessTransferFunction, }; -pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive}; +pub use self::storage_liveness::{ + MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive, always_storage_live_locals, +}; diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 576289e228a..1aae06d79d3 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -7,6 +7,23 @@ use rustc_middle::mir::*; use super::MaybeBorrowedLocals; use crate::{Analysis, GenKill, ResultsCursor}; +/// The set of locals in a MIR body that do not have `StorageLive`/`StorageDead` annotations. +/// +/// These locals have fixed storage for the duration of the body. +pub fn always_storage_live_locals(body: &Body<'_>) -> BitSet<Local> { + let mut always_live_locals = BitSet::new_filled(body.local_decls.len()); + + for block in &*body.basic_blocks { + for statement in &block.statements { + if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = statement.kind { + always_live_locals.remove(l); + } + } + } + + always_live_locals +} + pub struct MaybeStorageLive<'a> { always_live_locals: Cow<'a, BitSet<Local>>, } @@ -28,10 +45,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageLive<'a> { } fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) { - assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size()); - for local in self.always_live_locals.iter() { - on_entry.insert(local); - } + on_entry.union(&*self.always_live_locals); for arg in body.args_iter() { on_entry.insert(arg); @@ -135,7 +149,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { loc: Location, ) { // If a place is borrowed in a statement, it needs storage for that statement. - self.borrowed_locals.mut_analysis().apply_statement_effect(trans, stmt, loc); + MaybeBorrowedLocals::transfer_function(trans).visit_statement(stmt, loc); match &stmt.kind { StatementKind::StorageDead(l) => trans.kill(*l), @@ -180,10 +194,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { loc: Location, ) { // If a place is borrowed in a terminator, it needs storage for that terminator. - self.borrowed_locals - .mut_analysis() - .transfer_function(trans) - .visit_terminator(terminator, loc); + MaybeBorrowedLocals::transfer_function(trans).visit_terminator(terminator, loc); match &terminator.kind { TerminatorKind::Call { destination, .. } => { diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 7ef7c541173..2248972cecc 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -25,7 +25,7 @@ pub use self::framework::{ use self::move_paths::MoveData; pub mod debuginfo; -pub mod drop_flag_effects; +mod drop_flag_effects; pub mod elaborate_drops; mod errors; mod framework; @@ -33,8 +33,7 @@ pub mod impls; pub mod move_paths; pub mod points; pub mod rustc_peek; -pub mod storage; -pub mod un_derefer; +mod un_derefer; pub mod value_analysis; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 99d0ccde105..34ef8afdde3 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -12,9 +12,7 @@ use crate::errors::{ PeekMustBePlaceOrRefPlace, StopAfterDataFlowEndedCompilation, }; use crate::framework::BitSetExt; -use crate::impls::{ - DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPlaces, -}; +use crate::impls::{MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPlaces}; use crate::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex}; use crate::{Analysis, JoinSemiLattice, ResultsCursor}; @@ -56,13 +54,6 @@ pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { sanity_check_via_rustc_peek(tcx, flow_uninits.into_results_cursor(body)); } - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_definite_init).is_some() { - let flow_def_inits = - DefinitelyInitializedPlaces::new(body, &move_data).iterate_to_fixpoint(tcx, body, None); - - sanity_check_via_rustc_peek(tcx, flow_def_inits.into_results_cursor(body)); - } - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() { let flow_liveness = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None); diff --git a/compiler/rustc_mir_dataflow/src/storage.rs b/compiler/rustc_mir_dataflow/src/storage.rs deleted file mode 100644 index e5a0e1d312e..00000000000 --- a/compiler/rustc_mir_dataflow/src/storage.rs +++ /dev/null @@ -1,20 +0,0 @@ -use rustc_index::bit_set::BitSet; -use rustc_middle::mir::{self, Local}; - -/// The set of locals in a MIR body that do not have `StorageLive`/`StorageDead` annotations. -/// -/// These locals have fixed storage for the duration of the body. -pub fn always_storage_live_locals(body: &mir::Body<'_>) -> BitSet<Local> { - let mut always_live_locals = BitSet::new_filled(body.local_decls.len()); - - for block in &*body.basic_blocks { - for statement in &block.statements { - use mir::StatementKind::{StorageDead, StorageLive}; - if let StorageLive(l) | StorageDead(l) = statement.kind { - always_live_locals.remove(l); - } - } - } - - always_live_locals -} diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 8295a806d71..858752a3f01 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -70,8 +70,8 @@ use rustc_middle::ty::{ use rustc_middle::{bug, span_bug}; use rustc_mir_dataflow::impls::{ MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive, + always_storage_live_locals, }; -use rustc_mir_dataflow::storage::always_storage_live_locals; use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor}; use rustc_span::Span; use rustc_span::def_id::{DefId, LocalDefId}; @@ -676,12 +676,11 @@ fn locals_live_across_suspend_points<'tcx>( let mut borrowed_locals_cursor = borrowed_locals_results.clone().into_results_cursor(body); - // Calculate the MIR locals that we actually need to keep storage around - // for. - let mut requires_storage_cursor = + // Calculate the MIR locals that we need to keep storage around for. + let mut requires_storage_results = MaybeRequiresStorage::new(borrowed_locals_results.into_results_cursor(body)) - .iterate_to_fixpoint(tcx, body, None) - .into_results_cursor(body); + .iterate_to_fixpoint(tcx, body, None); + let mut requires_storage_cursor = requires_storage_results.as_results_cursor(body); // Calculate the liveness of MIR locals ignoring borrows. let mut liveness = @@ -697,8 +696,7 @@ fn locals_live_across_suspend_points<'tcx>( let loc = Location { block, statement_index: data.statements.len() }; liveness.seek_to_block_end(block); - let mut live_locals: BitSet<_> = BitSet::new_empty(body.local_decls.len()); - live_locals.union(liveness.get()); + let mut live_locals = liveness.get().clone(); if !movable { // The `liveness` variable contains the liveness of MIR locals ignoring borrows. @@ -754,7 +752,7 @@ fn locals_live_across_suspend_points<'tcx>( body, &saved_locals, always_live_locals.clone(), - requires_storage_cursor.into_results(), + requires_storage_results, ); LivenessInfo { diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index cf9f6981c5a..46efdd16ee8 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -1,3 +1,4 @@ +use std::cmp::Ordering; use std::fmt::{self, Debug}; use rustc_data_structures::captures::Captures; @@ -10,9 +11,12 @@ use tracing::{debug, debug_span, instrument}; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, TraverseCoverageGraphWithLoops}; +#[cfg(test)] +mod tests; + /// The coverage counter or counter expression associated with a particular /// BCB node or BCB edge. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] enum BcbCounter { Counter { id: CounterId }, Expression { id: ExpressionId }, @@ -43,8 +47,9 @@ struct BcbExpression { rhs: BcbCounter, } -#[derive(Debug)] -pub(super) enum CounterIncrementSite { +/// Enum representing either a node or an edge in the coverage graph. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(super) enum Site { Node { bcb: BasicCoverageBlock }, Edge { from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock }, } @@ -54,16 +59,10 @@ pub(super) enum CounterIncrementSite { pub(super) struct CoverageCounters { /// List of places where a counter-increment statement should be injected /// into MIR, each with its corresponding counter ID. - counter_increment_sites: IndexVec<CounterId, CounterIncrementSite>, + counter_increment_sites: IndexVec<CounterId, Site>, /// Coverage counters/expressions that are associated with individual BCBs. node_counters: IndexVec<BasicCoverageBlock, Option<BcbCounter>>, - /// Coverage counters/expressions that are associated with the control-flow - /// edge between two BCBs. - /// - /// We currently don't iterate over this map, but if we do in the future, - /// switch it back to `FxIndexMap` to avoid query stability hazards. - edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>, /// Table of expression data, associating each expression ID with its /// corresponding operator (+ or -) and its LHS/RHS operands. @@ -83,93 +82,30 @@ impl CoverageCounters { let mut builder = CountersBuilder::new(graph, bcb_needs_counter); builder.make_bcb_counters(); - builder.counters + builder.into_coverage_counters() } fn with_num_bcbs(num_bcbs: usize) -> Self { Self { counter_increment_sites: IndexVec::new(), node_counters: IndexVec::from_elem_n(None, num_bcbs), - edge_counters: FxHashMap::default(), expressions: IndexVec::new(), expressions_memo: FxHashMap::default(), } } - /// Shared helper used by [`Self::make_phys_node_counter`] and - /// [`Self::make_phys_edge_counter`]. Don't call this directly. - fn make_counter_inner(&mut self, site: CounterIncrementSite) -> BcbCounter { + /// Creates a new physical counter for a BCB node or edge. + fn make_phys_counter(&mut self, site: Site) -> BcbCounter { let id = self.counter_increment_sites.push(site); BcbCounter::Counter { id } } - /// Creates a new physical counter for a BCB node. - fn make_phys_node_counter(&mut self, bcb: BasicCoverageBlock) -> BcbCounter { - self.make_counter_inner(CounterIncrementSite::Node { bcb }) - } - - /// Creates a new physical counter for a BCB edge. - fn make_phys_edge_counter( - &mut self, - from_bcb: BasicCoverageBlock, - to_bcb: BasicCoverageBlock, - ) -> BcbCounter { - self.make_counter_inner(CounterIncrementSite::Edge { from_bcb, to_bcb }) - } - fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter { let new_expr = BcbExpression { lhs, op, rhs }; - *self - .expressions_memo - .entry(new_expr) - .or_insert_with(|| Self::make_expression_inner(&mut self.expressions, new_expr)) - } - - /// This is an associated function so that we can call it while borrowing - /// `&mut self.expressions_memo`. - fn make_expression_inner( - expressions: &mut IndexVec<ExpressionId, BcbExpression>, - new_expr: BcbExpression, - ) -> BcbCounter { - // Simplify expressions using basic algebra. - // - // Some of these cases might not actually occur in practice, depending - // on the details of how the instrumentor builds expressions. - let BcbExpression { lhs, op, rhs } = new_expr; - - if let BcbCounter::Expression { id } = lhs { - let lhs_expr = &expressions[id]; - - // Simplify `(a - b) + b` to `a`. - if lhs_expr.op == Op::Subtract && op == Op::Add && lhs_expr.rhs == rhs { - return lhs_expr.lhs; - } - // Simplify `(a + b) - b` to `a`. - if lhs_expr.op == Op::Add && op == Op::Subtract && lhs_expr.rhs == rhs { - return lhs_expr.lhs; - } - // Simplify `(a + b) - a` to `b`. - if lhs_expr.op == Op::Add && op == Op::Subtract && lhs_expr.lhs == rhs { - return lhs_expr.rhs; - } - } - - if let BcbCounter::Expression { id } = rhs { - let rhs_expr = &expressions[id]; - - // Simplify `a + (b - a)` to `b`. - if op == Op::Add && rhs_expr.op == Op::Subtract && lhs == rhs_expr.rhs { - return rhs_expr.lhs; - } - // Simplify `a - (a - b)` to `b`. - if op == Op::Subtract && rhs_expr.op == Op::Subtract && lhs == rhs_expr.lhs { - return rhs_expr.rhs; - } - } - - // Simplification failed, so actually create the new expression. - let id = expressions.push(new_expr); - BcbCounter::Expression { id } + *self.expressions_memo.entry(new_expr).or_insert_with(|| { + let id = self.expressions.push(new_expr); + BcbCounter::Expression { id } + }) } /// Creates a counter that is the sum of the given counters. @@ -182,6 +118,12 @@ impl CoverageCounters { .reduce(|accum, counter| self.make_expression(accum, Op::Add, counter)) } + /// Creates a counter whose value is `lhs - SUM(rhs)`. + fn make_subtracted_sum(&mut self, lhs: BcbCounter, rhs: &[BcbCounter]) -> BcbCounter { + let Some(rhs_sum) = self.make_sum(rhs) else { return lhs }; + self.make_expression(lhs, Op::Subtract, rhs_sum) + } + pub(super) fn num_counters(&self) -> usize { self.counter_increment_sites.len() } @@ -195,20 +137,6 @@ impl CoverageCounters { counter } - fn set_edge_counter( - &mut self, - from_bcb: BasicCoverageBlock, - to_bcb: BasicCoverageBlock, - counter: BcbCounter, - ) -> BcbCounter { - let existing = self.edge_counters.insert((from_bcb, to_bcb), counter); - assert!( - existing.is_none(), - "edge ({from_bcb:?} -> {to_bcb:?}) already has a counter: {existing:?} => {counter:?}" - ); - counter - } - pub(super) fn term_for_bcb(&self, bcb: BasicCoverageBlock) -> Option<CovTerm> { self.node_counters[bcb].map(|counter| counter.as_term()) } @@ -218,8 +146,8 @@ impl CoverageCounters { /// each site's corresponding counter ID. pub(super) fn counter_increment_sites( &self, - ) -> impl Iterator<Item = (CounterId, &CounterIncrementSite)> { - self.counter_increment_sites.iter_enumerated() + ) -> impl Iterator<Item = (CounterId, Site)> + Captures<'_> { + self.counter_increment_sites.iter_enumerated().map(|(id, &site)| (id, site)) } /// Returns an iterator over the subset of BCB nodes that have been associated @@ -254,22 +182,53 @@ impl CoverageCounters { } } +/// Symbolic representation of the coverage counter to be used for a particular +/// node or edge in the coverage graph. The same site counter can be used for +/// multiple sites, if they have been determined to have the same count. +#[derive(Clone, Copy, Debug)] +enum SiteCounter { + /// A physical counter at some node/edge. + Phys { site: Site }, + /// A counter expression for a node that takes the sum of all its in-edge + /// counters. + NodeSumExpr { bcb: BasicCoverageBlock }, + /// A counter expression for an edge that takes the counter of its source + /// node, and subtracts the counters of all its sibling out-edges. + EdgeDiffExpr { from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock }, +} + +/// Yields the graph successors of `from_bcb` that aren't `to_bcb`. This is +/// used when creating a counter expression for [`SiteCounter::EdgeDiffExpr`]. +/// +/// For example, in this diagram the sibling out-edge targets of edge `AC` are +/// the nodes `B` and `D`. +/// +/// ```text +/// A +/// / | \ +/// B C D +/// ``` +fn sibling_out_edge_targets( + graph: &CoverageGraph, + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, +) -> impl Iterator<Item = BasicCoverageBlock> + Captures<'_> { + graph.successors[from_bcb].iter().copied().filter(move |&t| t != to_bcb) +} + /// Helper struct that allows counter creation to inspect the BCB graph, and /// the set of nodes that need counters. struct CountersBuilder<'a> { - counters: CoverageCounters, graph: &'a CoverageGraph, bcb_needs_counter: &'a BitSet<BasicCoverageBlock>, + + site_counters: FxHashMap<Site, SiteCounter>, } impl<'a> CountersBuilder<'a> { fn new(graph: &'a CoverageGraph, bcb_needs_counter: &'a BitSet<BasicCoverageBlock>) -> Self { assert_eq!(graph.num_nodes(), bcb_needs_counter.domain_size()); - Self { - counters: CoverageCounters::with_num_bcbs(graph.num_nodes()), - graph, - bcb_needs_counter, - } + Self { graph, bcb_needs_counter, site_counters: FxHashMap::default() } } fn make_bcb_counters(&mut self) { @@ -302,9 +261,7 @@ impl<'a> CountersBuilder<'a> { fn make_node_counter_and_out_edge_counters(&mut self, from_bcb: BasicCoverageBlock) { // First, ensure that this node has a counter of some kind. // We might also use that counter to compute one of the out-edge counters. - let node_counter = self.get_or_make_node_counter(from_bcb); - - let successors = self.graph.successors[from_bcb].as_slice(); + self.get_or_make_node_counter(from_bcb); // If this node's out-edges won't sum to the node's counter, // then there's no reason to create edge counters here. @@ -315,11 +272,11 @@ impl<'a> CountersBuilder<'a> { // When choosing which out-edge should be given a counter expression, ignore edges that // already have counters, or could use the existing counter of their target node. let out_edge_has_counter = |to_bcb| { - if self.counters.edge_counters.contains_key(&(from_bcb, to_bcb)) { + if self.site_counters.contains_key(&Site::Edge { from_bcb, to_bcb }) { return true; } self.graph.sole_predecessor(to_bcb) == Some(from_bcb) - && self.counters.node_counters[to_bcb].is_some() + && self.site_counters.contains_key(&Site::Node { bcb: to_bcb }) }; // Determine the set of out-edges that could benefit from being given an expression. @@ -332,51 +289,41 @@ impl<'a> CountersBuilder<'a> { // If there are out-edges without counters, choose one to be given an expression // (computed from this node and the other out-edges) instead of a physical counter. - let Some(target_bcb) = self.choose_out_edge_for_expression(from_bcb, &candidate_successors) + let Some(to_bcb) = self.choose_out_edge_for_expression(from_bcb, &candidate_successors) else { return; }; // For each out-edge other than the one that was chosen to get an expression, - // ensure that it has a counter (existing counter/expression or a new counter), - // and accumulate the corresponding counters into a single sum expression. - let other_out_edge_counters = successors - .iter() - .copied() - // Skip the chosen edge, since we'll calculate its count from this sum. - .filter(|&edge_target_bcb| edge_target_bcb != target_bcb) - .map(|to_bcb| self.get_or_make_edge_counter(from_bcb, to_bcb)) - .collect::<Vec<_>>(); - let Some(sum_of_all_other_out_edges) = self.counters.make_sum(&other_out_edge_counters) - else { - return; - }; + // ensure that it has a counter (existing counter/expression or a new counter). + for target in sibling_out_edge_targets(self.graph, from_bcb, to_bcb) { + self.get_or_make_edge_counter(from_bcb, target); + } // Now create an expression for the chosen edge, by taking the counter // for its source node and subtracting the sum of its sibling out-edges. - let expression = - self.counters.make_expression(node_counter, Op::Subtract, sum_of_all_other_out_edges); - - debug!("{target_bcb:?} gets an expression: {expression:?}"); - self.counters.set_edge_counter(from_bcb, target_bcb, expression); + let counter = SiteCounter::EdgeDiffExpr { from_bcb, to_bcb }; + self.site_counters.insert(Site::Edge { from_bcb, to_bcb }, counter); } #[instrument(level = "debug", skip(self))] - fn get_or_make_node_counter(&mut self, bcb: BasicCoverageBlock) -> BcbCounter { + fn get_or_make_node_counter(&mut self, bcb: BasicCoverageBlock) -> SiteCounter { // If the BCB already has a counter, return it. - if let Some(counter) = self.counters.node_counters[bcb] { + if let Some(&counter) = self.site_counters.get(&Site::Node { bcb }) { debug!("{bcb:?} already has a counter: {counter:?}"); return counter; } let counter = self.make_node_counter_inner(bcb); - self.counters.set_node_counter(bcb, counter) + self.site_counters.insert(Site::Node { bcb }, counter); + counter } - fn make_node_counter_inner(&mut self, bcb: BasicCoverageBlock) -> BcbCounter { + fn make_node_counter_inner(&mut self, bcb: BasicCoverageBlock) -> SiteCounter { // If the node's sole in-edge already has a counter, use that. if let Some(sole_pred) = self.graph.sole_predecessor(bcb) - && let Some(&edge_counter) = self.counters.edge_counters.get(&(sole_pred, bcb)) + && let Some(&edge_counter) = + self.site_counters.get(&Site::Edge { from_bcb: sole_pred, to_bcb: bcb }) { return edge_counter; } @@ -390,20 +337,17 @@ impl<'a> CountersBuilder<'a> { // leading to infinite recursion. if predecessors.len() <= 1 || predecessors.contains(&bcb) { debug!(?bcb, ?predecessors, "node has <=1 predecessors or is its own predecessor"); - let counter = self.counters.make_phys_node_counter(bcb); + let counter = SiteCounter::Phys { site: Site::Node { bcb } }; debug!(?bcb, ?counter, "node gets a physical counter"); return counter; } // A BCB with multiple incoming edges can compute its count by ensuring that counters // exist for each of those edges, and then adding them up to get a total count. - let in_edge_counters = predecessors - .iter() - .copied() - .map(|from_bcb| self.get_or_make_edge_counter(from_bcb, bcb)) - .collect::<Vec<_>>(); - let sum_of_in_edges: BcbCounter = - self.counters.make_sum(&in_edge_counters).expect("there must be at least one in-edge"); + for &from_bcb in predecessors { + self.get_or_make_edge_counter(from_bcb, bcb); + } + let sum_of_in_edges = SiteCounter::NodeSumExpr { bcb }; debug!("{bcb:?} gets a new counter (sum of predecessor counters): {sum_of_in_edges:?}"); sum_of_in_edges @@ -414,22 +358,23 @@ impl<'a> CountersBuilder<'a> { &mut self, from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, - ) -> BcbCounter { + ) -> SiteCounter { // If the edge already has a counter, return it. - if let Some(&counter) = self.counters.edge_counters.get(&(from_bcb, to_bcb)) { + if let Some(&counter) = self.site_counters.get(&Site::Edge { from_bcb, to_bcb }) { debug!("Edge {from_bcb:?}->{to_bcb:?} already has a counter: {counter:?}"); return counter; } let counter = self.make_edge_counter_inner(from_bcb, to_bcb); - self.counters.set_edge_counter(from_bcb, to_bcb, counter) + self.site_counters.insert(Site::Edge { from_bcb, to_bcb }, counter); + counter } fn make_edge_counter_inner( &mut self, from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, - ) -> BcbCounter { + ) -> SiteCounter { // If the target node has exactly one in-edge (i.e. this one), then just // use the node's counter, since it will have the same value. if let Some(sole_pred) = self.graph.sole_predecessor(to_bcb) { @@ -447,7 +392,7 @@ impl<'a> CountersBuilder<'a> { } // Make a new counter to count this edge. - let counter = self.counters.make_phys_edge_counter(from_bcb, to_bcb); + let counter = SiteCounter::Phys { site: Site::Edge { from_bcb, to_bcb } }; debug!(?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter"); counter } @@ -510,4 +455,145 @@ impl<'a> CountersBuilder<'a> { None } + + fn into_coverage_counters(self) -> CoverageCounters { + Transcriber::new(&self).transcribe_counters() + } +} + +/// Helper struct for converting `CountersBuilder` into a final `CoverageCounters`. +struct Transcriber<'a> { + old: &'a CountersBuilder<'a>, + new: CoverageCounters, + phys_counter_for_site: FxHashMap<Site, BcbCounter>, +} + +impl<'a> Transcriber<'a> { + fn new(old: &'a CountersBuilder<'a>) -> Self { + Self { + old, + new: CoverageCounters::with_num_bcbs(old.graph.num_nodes()), + phys_counter_for_site: FxHashMap::default(), + } + } + + fn transcribe_counters(mut self) -> CoverageCounters { + for bcb in self.old.bcb_needs_counter.iter() { + let site = Site::Node { bcb }; + let site_counter = self.site_counter(site); + + // Resolve the site counter into flat lists of nodes/edges whose + // physical counts contribute to the counter for this node. + // Distinguish between counts that will be added vs subtracted. + let mut pos = vec![]; + let mut neg = vec![]; + self.push_resolved_sites(site_counter, &mut pos, &mut neg); + + // Simplify by cancelling out sites that appear on both sides. + let (mut pos, mut neg) = sort_and_cancel(pos, neg); + + if pos.is_empty() { + // If we somehow end up with no positive terms after cancellation, + // fall back to creating a physical counter. There's no known way + // for this to happen, but it's hard to confidently rule it out. + debug_assert!(false, "{site:?} has no positive counter terms"); + pos = vec![Some(site)]; + neg = vec![]; + } + + let mut new_counters_for_sites = |sites: Vec<Option<Site>>| { + sites + .into_iter() + .filter_map(|id| try { self.ensure_phys_counter(id?) }) + .collect::<Vec<_>>() + }; + let mut pos = new_counters_for_sites(pos); + let mut neg = new_counters_for_sites(neg); + + pos.sort(); + neg.sort(); + + let pos_counter = self.new.make_sum(&pos).expect("`pos` should not be empty"); + let new_counter = self.new.make_subtracted_sum(pos_counter, &neg); + self.new.set_node_counter(bcb, new_counter); + } + + self.new + } + + fn site_counter(&self, site: Site) -> SiteCounter { + self.old.site_counters.get(&site).copied().unwrap_or_else(|| { + // We should have already created all necessary site counters. + // But if we somehow didn't, avoid crashing in release builds, + // and just use an extra physical counter instead. + debug_assert!(false, "{site:?} should have a counter"); + SiteCounter::Phys { site } + }) + } + + fn ensure_phys_counter(&mut self, site: Site) -> BcbCounter { + *self.phys_counter_for_site.entry(site).or_insert_with(|| self.new.make_phys_counter(site)) + } + + /// Resolves the given counter into flat lists of nodes/edges, whose counters + /// will then be added and subtracted to form a counter expression. + fn push_resolved_sites(&self, counter: SiteCounter, pos: &mut Vec<Site>, neg: &mut Vec<Site>) { + match counter { + SiteCounter::Phys { site } => pos.push(site), + SiteCounter::NodeSumExpr { bcb } => { + for &from_bcb in &self.old.graph.predecessors[bcb] { + let edge_counter = self.site_counter(Site::Edge { from_bcb, to_bcb: bcb }); + self.push_resolved_sites(edge_counter, pos, neg); + } + } + SiteCounter::EdgeDiffExpr { from_bcb, to_bcb } => { + // First, add the count for `from_bcb`. + let node_counter = self.site_counter(Site::Node { bcb: from_bcb }); + self.push_resolved_sites(node_counter, pos, neg); + + // Then subtract the counts for the other out-edges. + for target in sibling_out_edge_targets(self.old.graph, from_bcb, to_bcb) { + let edge_counter = self.site_counter(Site::Edge { from_bcb, to_bcb: target }); + // Swap `neg` and `pos` so that the counter is subtracted. + self.push_resolved_sites(edge_counter, neg, pos); + } + } + } + } +} + +/// Given two lists: +/// - Sorts each list. +/// - Converts each list to `Vec<Option<T>>`. +/// - Scans for values that appear in both lists, and cancels them out by +/// replacing matching pairs of values with `None`. +fn sort_and_cancel<T: Ord>(mut pos: Vec<T>, mut neg: Vec<T>) -> (Vec<Option<T>>, Vec<Option<T>>) { + pos.sort(); + neg.sort(); + + // Convert to `Vec<Option<T>>`. If `T` has a niche, this should be zero-cost. + let mut pos = pos.into_iter().map(Some).collect::<Vec<_>>(); + let mut neg = neg.into_iter().map(Some).collect::<Vec<_>>(); + + // Scan through the lists using two cursors. When either cursor reaches the + // end of its list, there can be no more equal pairs, so stop. + let mut p = 0; + let mut n = 0; + while p < pos.len() && n < neg.len() { + // If the values are equal, remove them and advance both cursors. + // Otherwise, advance whichever cursor points to the lesser value. + // (Choosing which cursor to advance relies on both lists being sorted.) + match pos[p].cmp(&neg[n]) { + Ordering::Less => p += 1, + Ordering::Equal => { + pos[p] = None; + neg[n] = None; + p += 1; + n += 1; + } + Ordering::Greater => n += 1, + } + } + + (pos, neg) } diff --git a/compiler/rustc_mir_transform/src/coverage/counters/tests.rs b/compiler/rustc_mir_transform/src/coverage/counters/tests.rs new file mode 100644 index 00000000000..794d4358f82 --- /dev/null +++ b/compiler/rustc_mir_transform/src/coverage/counters/tests.rs @@ -0,0 +1,41 @@ +use std::fmt::Debug; + +use super::sort_and_cancel; + +fn flatten<T>(input: Vec<Option<T>>) -> Vec<T> { + input.into_iter().flatten().collect() +} + +fn sort_and_cancel_and_flatten<T: Clone + Ord>(pos: Vec<T>, neg: Vec<T>) -> (Vec<T>, Vec<T>) { + let (pos_actual, neg_actual) = sort_and_cancel(pos, neg); + (flatten(pos_actual), flatten(neg_actual)) +} + +#[track_caller] +fn check_test_case<T: Clone + Debug + Ord>( + pos: Vec<T>, + neg: Vec<T>, + pos_expected: Vec<T>, + neg_expected: Vec<T>, +) { + eprintln!("pos = {pos:?}; neg = {neg:?}"); + let output = sort_and_cancel_and_flatten(pos, neg); + assert_eq!(output, (pos_expected, neg_expected)); +} + +#[test] +fn cancellation() { + let cases: &[(Vec<u32>, Vec<u32>, Vec<u32>, Vec<u32>)] = &[ + (vec![], vec![], vec![], vec![]), + (vec![4, 2, 1, 5, 3], vec![], vec![1, 2, 3, 4, 5], vec![]), + (vec![5, 5, 5, 5, 5], vec![5], vec![5, 5, 5, 5], vec![]), + (vec![1, 1, 2, 2, 3, 3], vec![1, 2, 3], vec![1, 2, 3], vec![]), + (vec![1, 1, 2, 2, 3, 3], vec![2, 4, 2], vec![1, 1, 3, 3], vec![4]), + ]; + + for (pos, neg, pos_expected, neg_expected) in cases { + check_test_case(pos.to_vec(), neg.to_vec(), pos_expected.to_vec(), neg_expected.to_vec()); + // Same test case, but with its inputs flipped and its outputs flipped. + check_test_case(neg.to_vec(), pos.to_vec(), neg_expected.to_vec(), pos_expected.to_vec()); + } +} diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index e8b3d80be02..241c3c79114 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -25,7 +25,7 @@ use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, Pos, SourceFile, Span}; use tracing::{debug, debug_span, trace}; -use crate::coverage::counters::{CounterIncrementSite, CoverageCounters}; +use crate::coverage::counters::{CoverageCounters, Site}; use crate::coverage::graph::CoverageGraph; use crate::coverage::mappings::ExtractedMappings; @@ -265,13 +265,13 @@ fn inject_coverage_statements<'tcx>( coverage_counters: &CoverageCounters, ) { // Inject counter-increment statements into MIR. - for (id, counter_increment_site) in coverage_counters.counter_increment_sites() { + for (id, site) in coverage_counters.counter_increment_sites() { // Determine the block to inject a counter-increment statement into. // For BCB nodes this is just their first block, but for edges we need // to create a new block between the two BCBs, and inject into that. - let target_bb = match *counter_increment_site { - CounterIncrementSite::Node { bcb } => basic_coverage_blocks[bcb].leader_bb(), - CounterIncrementSite::Edge { from_bcb, to_bcb } => { + let target_bb = match site { + Site::Node { bcb } => basic_coverage_blocks[bcb].leader_bb(), + Site::Edge { from_bcb, to_bcb } => { // Create a new block between the last block of `from_bcb` and // the first block of `to_bcb`. let from_bb = basic_coverage_blocks[from_bcb].last_bb(); diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index df151f8cca3..0090f6f3040 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,6 +1,7 @@ use rustc_data_structures::captures::Captures; +use rustc_index::bit_set::BitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::mir::coverage::{CounterId, CoverageKind}; +use rustc_middle::mir::coverage::{CovTerm, CoverageKind, MappingKind}; use rustc_middle::mir::{Body, CoverageIdsInfo, Statement, StatementKind}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::{self, TyCtxt}; @@ -86,15 +87,43 @@ fn coverage_ids_info<'tcx>( ) -> CoverageIdsInfo { let mir_body = tcx.instance_mir(instance_def); - let max_counter_id = all_coverage_in_mir_body(mir_body) - .filter_map(|kind| match *kind { - CoverageKind::CounterIncrement { id } => Some(id), - _ => None, - }) - .max() - .unwrap_or(CounterId::ZERO); + let Some(fn_cov_info) = mir_body.function_coverage_info.as_ref() else { + return CoverageIdsInfo { + counters_seen: BitSet::new_empty(0), + expressions_seen: BitSet::new_empty(0), + }; + }; + + let mut counters_seen = BitSet::new_empty(fn_cov_info.num_counters); + let mut expressions_seen = BitSet::new_filled(fn_cov_info.expressions.len()); + + // For each expression ID that is directly used by one or more mappings, + // mark it as not-yet-seen. This indicates that we expect to see a + // corresponding `ExpressionUsed` statement during MIR traversal. + for mapping in fn_cov_info.mappings.iter() { + // Currently we only worry about ordinary code mappings. + // For branch and MC/DC mappings, expressions might not correspond + // to any particular point in the control-flow graph. + // (Keep this in sync with the injection of `ExpressionUsed` + // statements in the `InstrumentCoverage` MIR pass.) + if let MappingKind::Code(CovTerm::Expression(id)) = mapping.kind { + expressions_seen.remove(id); + } + } + + for kind in all_coverage_in_mir_body(mir_body) { + match *kind { + CoverageKind::CounterIncrement { id } => { + counters_seen.insert(id); + } + CoverageKind::ExpressionUsed { id } => { + expressions_seen.insert(id); + } + _ => {} + } + } - CoverageIdsInfo { max_counter_id } + CoverageIdsInfo { counters_seen, expressions_seen } } fn all_coverage_in_mir_body<'a, 'tcx>( diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index d017202f48b..b94c925b1db 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -534,8 +534,13 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { // This allows the set of visited edges to grow monotonically with the lattice. FlatSet::Bottom => TerminatorEdges::None, FlatSet::Elem(scalar) => { - let choice = scalar.assert_scalar_int().to_bits_unchecked(); - TerminatorEdges::Single(targets.target_for_value(choice)) + if let Ok(scalar_int) = scalar.try_to_scalar_int() { + TerminatorEdges::Single( + targets.target_for_value(scalar_int.to_bits_unchecked()), + ) + } else { + TerminatorEdges::SwitchInt { discr, targets } + } } FlatSet::Top => TerminatorEdges::SwitchInt { discr, targets }, } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 9c74b2f0839..8f977d2979e 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -217,11 +217,6 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { else { continue; }; - if !tcx.consider_optimizing(|| { - format!("{} round {}", tcx.def_path_str(def_id), round_count) - }) { - break; - } // Replace `src` by `dest` everywhere. merges.insert(*src, *dest); diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 704ed508b22..17c8348140a 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -108,10 +108,6 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { let parent = BasicBlock::from_usize(i); let Some(opt_data) = evaluate_candidate(tcx, body, parent) else { continue }; - if !tcx.consider_optimizing(|| format!("EarlyOtherwiseBranch {opt_data:?}")) { - break; - } - trace!("SUCCESS: found optimization possibility to apply: {opt_data:?}"); should_cleanup = true; diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 976f4a8e919..d5a813ec8ec 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -638,7 +638,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let proj = match proj { ProjectionElem::Deref => { let ty = place.ty(self.local_decls, self.tcx).ty; - if let Some(Mutability::Not) = ty.ref_mutability() + // unsound: https://github.com/rust-lang/rust/issues/130853 + if self.tcx.sess.opts.unstable_opts.unsound_mir_opts + && let Some(Mutability::Not) = ty.ref_mutability() && let Some(pointee_ty) = ty.builtin_deref(true) && pointee_ty.is_freeze(self.tcx, self.typing_env()) { diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 00f6c3845d4..79c38b50459 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -210,12 +210,6 @@ impl<'tcx> Inliner<'tcx> { let callee_body = try_instance_mir(self.tcx, callsite.callee.def)?; self.check_mir_body(callsite, callee_body, callee_attrs, cross_crate_inlinable)?; - if !self.tcx.consider_optimizing(|| { - format!("Inline {:?} into {:?}", callsite.callee, caller_body.source) - }) { - return Err("optimization fuel exhausted"); - } - let Ok(callee_body) = callsite.callee.try_instantiate_mir_and_normalize_erasing_regions( self.tcx, self.typing_env, @@ -226,15 +220,7 @@ impl<'tcx> Inliner<'tcx> { // Normally, this shouldn't be required, but trait normalization failure can create a // validation ICE. - if !validate_types( - self.tcx, - MirPhase::Runtime(RuntimePhase::Optimized), - self.typing_env, - &callee_body, - &caller_body, - ) - .is_empty() - { + if !validate_types(self.tcx, self.typing_env, &callee_body, &caller_body).is_empty() { return Err("failed to validate callee body"); } diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 3352d583f2c..a6ba2f32d32 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -7,8 +7,8 @@ use rustc_middle::bug; use rustc_middle::mir::*; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, layout}; -use rustc_span::sym; use rustc_span::symbol::Symbol; +use rustc_span::{DUMMY_SP, sym}; use crate::simplify::simplify_duplicate_switch_targets; use crate::take_array; @@ -43,12 +43,12 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { match statement.kind { StatementKind::Assign(box (_place, ref mut rvalue)) => { if !preserve_ub_checks { - ctx.simplify_ub_check(&statement.source_info, rvalue); + ctx.simplify_ub_check(rvalue); } - ctx.simplify_bool_cmp(&statement.source_info, rvalue); - ctx.simplify_ref_deref(&statement.source_info, rvalue); - ctx.simplify_len(&statement.source_info, rvalue); - ctx.simplify_ptr_aggregate(&statement.source_info, rvalue); + ctx.simplify_bool_cmp(rvalue); + ctx.simplify_ref_deref(rvalue); + ctx.simplify_len(rvalue); + ctx.simplify_ptr_aggregate(rvalue); ctx.simplify_cast(rvalue); } _ => {} @@ -70,23 +70,8 @@ struct InstSimplifyContext<'a, 'tcx> { } impl<'tcx> InstSimplifyContext<'_, 'tcx> { - fn should_simplify(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool { - self.should_simplify_custom(source_info, "Rvalue", rvalue) - } - - fn should_simplify_custom( - &self, - source_info: &SourceInfo, - label: &str, - value: impl std::fmt::Debug, - ) -> bool { - self.tcx.consider_optimizing(|| { - format!("InstSimplify - {label}: {value:?} SourceInfo: {source_info:?}") - }) - } - /// Transform boolean comparisons into logical operations. - fn simplify_bool_cmp(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { + fn simplify_bool_cmp(&self, rvalue: &mut Rvalue<'tcx>) { match rvalue { Rvalue::BinaryOp(op @ (BinOp::Eq | BinOp::Ne), box (a, b)) => { let new = match (op, self.try_eval_bool(a), self.try_eval_bool(b)) { @@ -117,9 +102,7 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { _ => None, }; - if let Some(new) = new - && self.should_simplify(source_info, rvalue) - { + if let Some(new) = new { *rvalue = new; } } @@ -134,17 +117,13 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { } /// Transform `&(*a)` ==> `a`. - fn simplify_ref_deref(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { + fn simplify_ref_deref(&self, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) = rvalue { if let Some((base, ProjectionElem::Deref)) = place.as_ref().last_projection() { if rvalue.ty(self.local_decls, self.tcx) != base.ty(self.local_decls, self.tcx).ty { return; } - if !self.should_simplify(source_info, rvalue) { - return; - } - *rvalue = Rvalue::Use(Operand::Copy(Place { local: base.local, projection: self.tcx.mk_place_elems(base.projection), @@ -154,36 +133,24 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { } /// Transform `Len([_; N])` ==> `N`. - fn simplify_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { + fn simplify_len(&self, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Len(ref place) = *rvalue { let place_ty = place.ty(self.local_decls, self.tcx).ty; if let ty::Array(_, len) = *place_ty.kind() { - if !self.should_simplify(source_info, rvalue) { - return; - } - let const_ = Const::from_ty_const(len, self.tcx.types.usize, self.tcx); - let constant = ConstOperand { span: source_info.span, const_, user_ty: None }; + let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None }; *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant))); } } } /// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`. - fn simplify_ptr_aggregate(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { + fn simplify_ptr_aggregate(&self, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue { let meta_ty = fields.raw[1].ty(self.local_decls, self.tcx); if meta_ty.is_unit() { // The mutable borrows we're holding prevent printing `rvalue` here - if !self.should_simplify_custom( - source_info, - "Aggregate::RawPtr", - (&pointee_ty, *mutability, &fields), - ) { - return; - } - let mut fields = std::mem::take(fields); let _meta = fields.pop().unwrap(); let data = fields.pop().unwrap(); @@ -193,10 +160,10 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { } } - fn simplify_ub_check(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { + fn simplify_ub_check(&self, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue { let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks()); - let constant = ConstOperand { span: source_info.span, const_, user_ty: None }; + let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None }; *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant))); } } @@ -284,16 +251,6 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { return; } - if !self.tcx.consider_optimizing(|| { - format!( - "InstSimplify - Call: {:?} SourceInfo: {:?}", - (fn_def_id, fn_args), - terminator.source_info - ) - }) { - return; - } - let Ok([arg]) = take_array(args) else { return }; let Some(arg_place) = arg.node.place() else { return }; diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs index d8ff1cfc90b..29e762af8de 100644 --- a/compiler/rustc_mir_transform/src/lint.rs +++ b/compiler/rustc_mir_transform/src/lint.rs @@ -9,8 +9,7 @@ use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -use rustc_mir_dataflow::impls::{MaybeStorageDead, MaybeStorageLive}; -use rustc_mir_dataflow::storage::always_storage_live_locals; +use rustc_mir_dataflow::impls::{MaybeStorageDead, MaybeStorageLive, always_storage_live_locals}; use rustc_mir_dataflow::{Analysis, ResultsCursor}; pub(super) fn lint_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, when: String) { 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 index 732a9cd9890..7fb421dea0c 100644 --- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs +++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs @@ -7,7 +7,7 @@ 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::bit_set::MixedBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::bug; @@ -49,24 +49,24 @@ struct DropsReachable<'a, 'mir, 'tcx> { 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>>>>, + collected_drops: &'a mut MixedBitSet<MovePathIndex>, + visited: FxHashMap<BasicBlock, Rc<RefCell<MixedBitSet<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 make_new_path_set = || Rc::new(RefCell::new(MixedBitSet::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. + // 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`. + // 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); @@ -76,23 +76,24 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> { && 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. + // 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. + // 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 + // 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 { @@ -139,8 +140,9 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> { // 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. + // 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 @@ -394,10 +396,10 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body< 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 all_locals_dropped = MixedBitSet::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()); + let mut collected_drops = MixedBitSet::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", @@ -425,8 +427,9 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body< // 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. + let mut to_exclude = MixedBitSet::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; diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index ff027680c49..20e2a65b311 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -18,16 +18,11 @@ impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let def_id = body.source.def_id(); let typing_env = body.typing_env(tcx); let mut should_cleanup = false; for i in 0..body.basic_blocks.len() { let bbs = &*body.basic_blocks; let bb_idx = BasicBlock::from_usize(i); - if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {def_id:?} ")) { - continue; - } - match bbs[bb_idx].terminator().kind { TerminatorKind::SwitchInt { discr: ref _discr @ (Operand::Copy(_) | Operand::Move(_)), diff --git a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs index b6d6ef5de1d..a9227524ce5 100644 --- a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs +++ b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs @@ -14,10 +14,9 @@ impl<'tcx> crate::MirPass<'tcx> for MultipleReturnTerminators { sess.mir_opt_level() >= 4 } - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // find basic blocks with no statement and a return terminator let mut bbs_simple_returns = BitSet::new_empty(body.basic_blocks.len()); - let def_id = body.source.def_id(); let bbs = body.basic_blocks_mut(); for idx in bbs.indices() { if bbs[idx].statements.is_empty() @@ -28,10 +27,6 @@ impl<'tcx> crate::MirPass<'tcx> for MultipleReturnTerminators { } for bb in bbs { - if !tcx.consider_optimizing(|| format!("MultipleReturnTerminators {def_id:?} ")) { - break; - } - if let TerminatorKind::Goto { target } = bb.terminator().kind { if bbs_simple_returns.contains(target) { bb.terminator_mut().kind = TerminatorKind::Return; diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs index 98fa149e2bc..cd026ed6806 100644 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ b/compiler/rustc_mir_transform/src/nrvo.rs @@ -45,10 +45,6 @@ impl<'tcx> crate::MirPass<'tcx> for RenameReturnPlace { return; }; - if !tcx.consider_optimizing(|| format!("RenameReturnPlace {def_id:?}")) { - return; - } - debug!( "`{:?}` was eligible for NRVO, making {:?} the return place", def_id, returned_local diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 779e7f22101..8a45ce0762d 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -292,7 +292,7 @@ fn run_passes_inner<'tcx>( } pub(super) fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when: String) { - validate::Validator { when, mir_phase: body.phase }.run_pass(tcx, body); + validate::Validator { when }.run_pass(tcx, body); } fn dump_mir_for_pass<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, pass_name: &str, is_after: bool) { diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index af438ac2177..96bcdfa6fac 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -8,8 +8,7 @@ use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::Analysis; -use rustc_mir_dataflow::impls::MaybeStorageDead; -use rustc_mir_dataflow::storage::always_storage_live_locals; +use rustc_mir_dataflow::impls::{MaybeStorageDead, always_storage_live_locals}; use tracing::{debug, instrument}; use crate::ssa::{SsaLocals, StorageLiveLocals}; diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index f786c676e9e..e955d8277a4 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -1,5 +1,5 @@ use rustc_abi::FieldIdx; -use rustc_index::bit_set::ChunkedBitSet; +use rustc_index::bit_set::MixedBitSet; use rustc_middle::mir::{Body, TerminatorKind}; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, VariantDef}; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; @@ -67,7 +67,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUninitDrops { fn is_needs_drop_and_init<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - maybe_inits: &ChunkedBitSet<MovePathIndex>, + maybe_inits: &MixedBitSet<MovePathIndex>, move_data: &MoveData<'tcx>, ty: Ty<'tcx>, mpi: MovePathIndex, diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs index a535be798cd..e335051d656 100644 --- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs @@ -26,11 +26,6 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUnneededDrops { if ty.ty.needs_drop(tcx, typing_env) { continue; } - if !tcx.consider_optimizing(|| { - format!("RemoveUnneededDrops {:?}", body.source.def_id()) - }) { - continue; - } debug!("SUCCESS: replacing `drop` with goto({:?})", target); terminator.kind = TerminatorKind::Goto { target }; should_simplify = true; diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 6fd70fbe9b0..64e183bcbc0 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -17,10 +17,6 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveZsts { return; } - if !tcx.consider_optimizing(|| format!("RemoveZsts - {:?}", body.source.def_id())) { - return; - } - let typing_env = body.typing_env(tcx); let local_decls = &body.local_decls; let mut replacer = Replacer { tcx, typing_env, local_decls }; @@ -94,16 +90,12 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { } } - fn visit_operand(&mut self, operand: &mut Operand<'tcx>, loc: Location) { + fn visit_operand(&mut self, operand: &mut Operand<'tcx>, _: Location) { if let Operand::Constant(_) = operand { return; } let op_ty = operand.ty(self.local_decls, self.tcx); - if self.known_to_be_zst(op_ty) - && self.tcx.consider_optimizing(|| { - format!("RemoveZsts - Operand: {operand:?} Location: {loc:?}") - }) - { + if self.known_to_be_zst(op_ty) { *operand = Operand::Constant(Box::new(self.make_zst(op_ty))) } } diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 809357ec110..b8383e734e2 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -747,8 +747,8 @@ fn build_call_shim<'tcx>( sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); } - // FIXME(eddyb) avoid having this snippet both here and in - // `Instance::fn_sig` (introduce `InstanceKind::fn_sig`?). + // FIXME: Avoid having to adjust the signature both here and in + // `fn_sig_for_fn_abi`. if let ty::InstanceKind::VTableShim(..) = instance { // Modify fn(self, ...) to fn(self: *mut Self, ...) let mut inputs_and_output = sig.inputs_and_output.to_vec(); diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index 9cd32459c7b..734703ec78b 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -43,12 +43,6 @@ impl crate::MirPass<'_> for UnreachablePropagation { } } - if !tcx - .consider_optimizing(|| format!("UnreachablePropagation {:?} ", body.source.def_id())) - { - return; - } - patch.apply(body); // We do want do keep some unreachable blocks, but make them empty. diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 51e5c49cea1..404fbb6b839 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -29,12 +29,6 @@ enum EdgeKind { pub(super) struct Validator { /// Describes at which point in the pipeline this validation is happening. pub when: String, - /// The phase for which we are upholding the dialect. If the given phase forbids a specific - /// element, this validator will now emit errors if that specific element is encountered. - /// Note that phases that change the dialect cause all *following* phases to check the - /// invariants of the new dialect. A phase that changes dialects never checks the new invariants - /// itself. - pub mir_phase: MirPhase, } impl<'tcx> crate::MirPass<'tcx> for Validator { @@ -46,11 +40,9 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { if matches!(body.source.instance, InstanceKind::Intrinsic(..) | InstanceKind::Virtual(..)) { return; } - debug_assert_eq!(self.mir_phase, body.phase); let def_id = body.source.def_id(); - let mir_phase = body.phase; let typing_env = body.typing_env(tcx); - let can_unwind = if mir_phase <= MirPhase::Runtime(RuntimePhase::Initial) { + let can_unwind = if body.phase <= MirPhase::Runtime(RuntimePhase::Initial) { // In this case `AbortUnwindingCalls` haven't yet been executed. true } else if !tcx.def_kind(def_id).is_fn_like() { @@ -64,9 +56,7 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { ty::Coroutine(..) => ExternAbi::Rust, // No need to do MIR validation on error bodies ty::Error(_) => return, - _ => { - span_bug!(body.span, "unexpected body ty: {:?} phase {:?}", body_ty, mir_phase) - } + _ => span_bug!(body.span, "unexpected body ty: {body_ty:?}"), }; ty::layout::fn_can_unwind(tcx, Some(def_id), body_abi) @@ -76,7 +66,6 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { when: &self.when, body, tcx, - mir_phase, unwind_edge_count: 0, reachable_blocks: traversal::reachable_as_bitset(body), value_cache: FxHashSet::default(), @@ -86,7 +75,7 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { cfg_checker.check_cleanup_control_flow(); // Also run the TypeChecker. - for (location, msg) in validate_types(tcx, self.mir_phase, typing_env, body, body) { + for (location, msg) in validate_types(tcx, typing_env, body, body) { cfg_checker.fail(location, msg); } @@ -107,7 +96,6 @@ struct CfgChecker<'a, 'tcx> { when: &'a str, body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, - mir_phase: MirPhase, unwind_edge_count: usize, reachable_blocks: BitSet<BasicBlock>, value_cache: FxHashSet<u128>, @@ -294,7 +282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { match &statement.kind { StatementKind::AscribeUserType(..) => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`AscribeUserType` should have been removed after drop lowering phase", @@ -302,7 +290,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { } } StatementKind::FakeRead(..) => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`FakeRead` should have been removed after drop lowering phase", @@ -310,17 +298,17 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { } } StatementKind::SetDiscriminant { .. } => { - if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`SetDiscriminant`is not allowed until deaggregation"); } } StatementKind::Deinit(..) => { - if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`Deinit`is not allowed until deaggregation"); } } StatementKind::Retag(kind, _) => { - // FIXME(JakobDegen) The validator should check that `self.mir_phase < + // FIXME(JakobDegen) The validator should check that `self.body.phase < // DropsLowered`. However, this causes ICEs with generation of drop shims, which // seem to fail to set their `MirPhase` correctly. if matches!(kind, RetagKind::TwoPhase) { @@ -328,7 +316,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { } } StatementKind::Coverage(kind) => { - if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) + if self.body.phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) && let CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. } = kind { self.fail( @@ -391,7 +379,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { // the return edge from the call. FIXME(tmiasko): Since this is a strictly code // generation concern, the code generation should be responsible for handling // it. - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized) + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Optimized) && self.is_critical_call_edge(target, unwind) { self.fail( @@ -440,7 +428,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { if self.body.coroutine.is_none() { self.fail(location, "`Yield` cannot appear outside coroutine bodies"); } - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`Yield` should have been replaced by coroutine lowering"); } self.check_edge(location, *resume, EdgeKind::Normal); @@ -449,7 +437,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { } } TerminatorKind::FalseEdge { real_target, imaginary_target } => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`FalseEdge` should have been removed after drop elaboration", @@ -459,7 +447,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { self.check_edge(location, *imaginary_target, EdgeKind::Normal); } TerminatorKind::FalseUnwind { real_target, unwind } => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`FalseUnwind` should have been removed after drop elaboration", @@ -478,7 +466,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { if self.body.coroutine.is_none() { self.fail(location, "`CoroutineDrop` cannot appear outside coroutine bodies"); } - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`CoroutineDrop` should have been replaced by coroutine lowering", @@ -532,13 +520,11 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { /// `optimized_mir` is available. pub(super) fn validate_types<'tcx>( tcx: TyCtxt<'tcx>, - mir_phase: MirPhase, typing_env: ty::TypingEnv<'tcx>, body: &Body<'tcx>, caller_body: &Body<'tcx>, ) -> Vec<(Location, String)> { - let mut type_checker = - TypeChecker { body, caller_body, tcx, typing_env, mir_phase, failures: Vec::new() }; + let mut type_checker = TypeChecker { body, caller_body, tcx, typing_env, failures: Vec::new() }; type_checker.visit_body(body); type_checker.failures } @@ -548,7 +534,6 @@ struct TypeChecker<'a, 'tcx> { caller_body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - mir_phase: MirPhase, failures: Vec<(Location, String)>, } @@ -577,7 +562,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // After borrowck subtyping should be fully explicit via // `Subtype` projections. - let variance = if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + let variance = if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { Variance::Invariant } else { Variance::Covariant @@ -618,13 +603,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { // This check is somewhat expensive, so only run it when -Zvalidate-mir is passed. if self.tcx.sess.opts.unstable_opts.validate_mir - && self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) + && self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { // `Operand::Copy` is only supposed to be used with `Copy` types. if let Operand::Copy(place) = operand { let ty = place.ty(&self.body.local_decls, self.tcx).ty; - if !ty.is_copy_modulo_regions(self.tcx, self.typing_env) { + if !self.tcx.type_is_copy_modulo_regions(self.typing_env, ty) { self.fail(location, format!("`Operand::Copy` with non-`Copy` type {ty}")); } } @@ -642,7 +627,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ) { match elem { ProjectionElem::OpaqueCast(ty) - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) => + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) => { self.fail( location, @@ -656,7 +641,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } ProjectionElem::Deref - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::PostCleanup) => + if self.body.phase >= MirPhase::Runtime(RuntimePhase::PostCleanup) => { let base_ty = place_ref.ty(&self.body.local_decls, self.tcx).ty; @@ -856,7 +841,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // Set off any `bug!`s in the type computation code let _ = place.ty(&self.body.local_decls, self.tcx); - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) && place.projection.len() > 1 && cntxt != PlaceContext::NonUse(NonUseContext::VarDebugInfo) && place.projection[1..].contains(&ProjectionElem::Deref) @@ -974,7 +959,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } AggregateKind::RawPtr(pointee_ty, mutability) => { - if !matches!(self.mir_phase, MirPhase::Runtime(_)) { + if !matches!(self.body.phase, MirPhase::Runtime(_)) { // It would probably be fine to support this in earlier phases, but at the // time of writing it's only ever introduced from intrinsic lowering, so // earlier things just `bug!` on it. @@ -1016,7 +1001,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } }, Rvalue::Ref(_, BorrowKind::Fake(_), _) => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`Assign` statement with a `Fake` borrow should have been removed in runtime MIR", @@ -1130,7 +1115,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } UnOp::PtrMetadata => { - if !matches!(self.mir_phase, MirPhase::Runtime(_)) { + if !matches!(self.body.phase, MirPhase::Runtime(_)) { // It would probably be fine to support this in earlier phases, but at // the time of writing it's only ever introduced from intrinsic // lowering or other runtime-phase optimization passes, so earlier @@ -1206,7 +1191,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { "CastKind::{kind:?} output must be a raw const pointer, not {:?}", ty::RawPtr(_, Mutability::Not) ); - if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) { + if self.body.phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) { self.fail(location, format!("After borrowck, MIR disallows {kind:?}")); } } @@ -1222,7 +1207,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { "CastKind::{kind:?} output must be a raw pointer, not {:?}", ty::RawPtr(..) ); - if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) { + if self.body.phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) { self.fail(location, format!("After borrowck, MIR disallows {kind:?}")); } } @@ -1288,7 +1273,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } CastKind::Transmute => { - if let MirPhase::Runtime(..) = self.mir_phase { + if let MirPhase::Runtime(..) = self.body.phase { // Unlike `mem::transmute`, a MIR `Transmute` is well-formed // for any two `Sized` types, just potentially UB to run. @@ -1317,7 +1302,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { location, format!( "Transmute is not supported in non-runtime phase {:?}.", - self.mir_phase + self.body.phase ), ); } @@ -1404,7 +1389,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::AscribeUserType(..) => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`AscribeUserType` should have been removed after drop lowering phase", @@ -1412,7 +1397,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::FakeRead(..) => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`FakeRead` should have been removed after drop lowering phase", @@ -1463,7 +1448,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::SetDiscriminant { place, .. } => { - if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`SetDiscriminant`is not allowed until deaggregation"); } let pty = place.ty(&self.body.local_decls, self.tcx).ty.kind(); @@ -1477,12 +1462,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::Deinit(..) => { - if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`Deinit`is not allowed until deaggregation"); } } StatementKind::Retag(kind, _) => { - // FIXME(JakobDegen) The validator should check that `self.mir_phase < + // FIXME(JakobDegen) The validator should check that `self.body.phase < // DropsLowered`. However, this causes ICEs with generation of drop shims, which // seem to fail to set their `MirPhase` correctly. if matches!(kind, RetagKind::TwoPhase) { diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index 6c881fd7e06..e18441ea7fc 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } +rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl index 8528a2e68c0..540ce7cc4ce 100644 --- a/compiler/rustc_monomorphize/messages.ftl +++ b/compiler/rustc_monomorphize/messages.ftl @@ -41,6 +41,4 @@ monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined monomorphize_unknown_cgu_collection_mode = unknown codegen-item collection mode '{$mode}', falling back to 'lazy' mode -monomorphize_unused_generic_params = item has unused generic parameters - monomorphize_written_to_path = the full type name has been written to '{$path}' diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 5efe7ffc7b9..480d82c1a38 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -965,9 +965,7 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxtAt<'tcx>, instance: Instance<'tcx>) - return true; } - if tcx.is_reachable_non_generic(def_id) - || instance.polymorphize(*tcx).upstream_monomorphization(*tcx).is_some() - { + if tcx.is_reachable_non_generic(def_id) || instance.upstream_monomorphization(*tcx).is_some() { // We can link to the item in question, no instance needed in this crate. return false; } @@ -1114,7 +1112,7 @@ fn create_fn_mono_item<'tcx>( crate::util::dump_closure_profile(tcx, instance); } - respan(source, MonoItem::Fn(instance.polymorphize(tcx))) + respan(source, MonoItem::Fn(instance)) } /// Creates a `MonoItem` for each method that is referenced by the vtable for diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index 02865cad302..fc8d63b5888 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -1,11 +1,8 @@ use std::path::PathBuf; -use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; use rustc_macros::{Diagnostic, LintDiagnostic}; use rustc_span::{Span, Symbol}; -use crate::fluent_generated as fluent; - #[derive(Diagnostic)] #[diag(monomorphize_recursion_limit)] pub(crate) struct RecursionLimit { @@ -28,28 +25,6 @@ pub(crate) struct NoOptimizedMir { pub crate_name: Symbol, } -pub(crate) struct UnusedGenericParamsHint { - pub span: Span, - pub param_spans: Vec<Span>, - pub param_names: Vec<String>, -} - -impl<G: EmissionGuarantee> Diagnostic<'_, G> for UnusedGenericParamsHint { - #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { - let mut diag = Diag::new(dcx, level, fluent::monomorphize_unused_generic_params); - diag.span(self.span); - for (span, name) in self.param_spans.into_iter().zip(self.param_names) { - // FIXME: I can figure out how to do a label with a fluent string with a fixed message, - // or a label with a dynamic value in a hard-coded string, but I haven't figured out - // how to combine the two. 😢 - #[allow(rustc::untranslatable_diagnostic)] - diag.span_label(span, format!("generic parameter `{name}` is unused")); - } - diag - } -} - #[derive(LintDiagnostic)] #[diag(monomorphize_large_assignments)] #[note] diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 0f08930fb4c..caae54cd559 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -19,7 +19,6 @@ mod collector; mod errors; mod mono_checks; mod partitioning; -mod polymorphize; mod util; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -50,6 +49,5 @@ fn custom_coerce_unsize_info<'tcx>( pub fn provide(providers: &mut Providers) { partitioning::provide(providers); - polymorphize::provide(providers); mono_checks::provide(providers); } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 7240cfce0f7..dabce72650a 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -113,7 +113,6 @@ use rustc_middle::mir::mono::{ Visibility, }; use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths}; -use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, InstanceKind, TyCtxt}; use rustc_middle::util::Providers; use rustc_session::CodegenUnits; @@ -208,8 +207,8 @@ where // available to downstream crates. This depends on whether we are in // share-generics mode and whether the current crate can even have // downstream crates. - let export_generics = - cx.tcx.sess.opts.share_generics() && cx.tcx.local_crate_exports_generics(); + let can_export_generics = cx.tcx.local_crate_exports_generics(); + let always_export_generics = can_export_generics && cx.tcx.sess.opts.share_generics(); let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx); let cgu_name_cache = &mut UnordMap::default(); @@ -249,7 +248,8 @@ where cx.tcx, &mono_item, &mut can_be_internalized, - export_generics, + can_export_generics, + always_export_generics, ); if visibility == Visibility::Hidden && can_be_internalized { internalization_candidates.insert(mono_item); @@ -660,18 +660,14 @@ fn characteristic_def_id_of_mono_item<'tcx>( return None; } - // When polymorphization is enabled, methods which do not depend on their generic - // parameters, but the self-type of their impl block do will fail to normalize. - if !tcx.sess.opts.unstable_opts.polymorphize || !instance.has_param() { - // This is a method within an impl, find out what the self-type is: - let impl_self_ty = tcx.instantiate_and_normalize_erasing_regions( - instance.args, - ty::TypingEnv::fully_monomorphized(), - tcx.type_of(impl_def_id), - ); - if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) { - return Some(def_id); - } + // This is a method within an impl, find out what the self-type is: + let impl_self_ty = tcx.instantiate_and_normalize_erasing_regions( + instance.args, + ty::TypingEnv::fully_monomorphized(), + tcx.type_of(impl_def_id), + ); + if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) { + return Some(def_id); } } @@ -739,12 +735,19 @@ fn mono_item_linkage_and_visibility<'tcx>( tcx: TyCtxt<'tcx>, mono_item: &MonoItem<'tcx>, can_be_internalized: &mut bool, - export_generics: bool, + can_export_generics: bool, + always_export_generics: bool, ) -> (Linkage, Visibility) { if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) { return (explicit_linkage, Visibility::Default); } - let vis = mono_item_visibility(tcx, mono_item, can_be_internalized, export_generics); + let vis = mono_item_visibility( + tcx, + mono_item, + can_be_internalized, + can_export_generics, + always_export_generics, + ); (Linkage::External, vis) } @@ -767,7 +770,8 @@ fn mono_item_visibility<'tcx>( tcx: TyCtxt<'tcx>, mono_item: &MonoItem<'tcx>, can_be_internalized: &mut bool, - export_generics: bool, + can_export_generics: bool, + always_export_generics: bool, ) -> Visibility { let instance = match mono_item { // This is pretty complicated; see below. @@ -826,7 +830,11 @@ fn mono_item_visibility<'tcx>( // Upstream `DefId` instances get different handling than local ones. let Some(def_id) = def_id.as_local() else { - return if export_generics && is_generic { + return if is_generic + && (always_export_generics + || (can_export_generics + && tcx.codegen_fn_attrs(def_id).inline == rustc_attr::InlineAttr::Never)) + { // If it is an upstream monomorphization and we export generics, we must make // it available to downstream crates. *can_be_internalized = false; @@ -837,7 +845,10 @@ fn mono_item_visibility<'tcx>( }; if is_generic { - if export_generics { + if always_export_generics + || (can_export_generics + && tcx.codegen_fn_attrs(def_id).inline == rustc_attr::InlineAttr::Never) + { if tcx.is_unreachable_local_definition(def_id) { // This instance cannot be used from another crate. Visibility::Hidden diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs deleted file mode 100644 index e049fe99664..00000000000 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ /dev/null @@ -1,334 +0,0 @@ -//! Polymorphization Analysis -//! ========================= -//! -//! This module implements an analysis of functions, methods and closures to determine which -//! generic parameters are unused (and eventually, in what ways generic parameters are used - only -//! for their size, offset of a field, etc.). - -use rustc_hir::ConstContext; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; -use rustc_middle::mir::visit::{TyContext, Visitor}; -use rustc_middle::mir::{self, Local, LocalDecl, Location}; -use rustc_middle::query::Providers; -use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, UnusedGenericParams}; -use rustc_span::symbol::sym; -use tracing::{debug, instrument}; - -use crate::errors::UnusedGenericParamsHint; - -/// Provide implementations of queries relating to polymorphization analysis. -pub(crate) fn provide(providers: &mut Providers) { - providers.unused_generic_params = unused_generic_params; -} - -/// Determine which generic parameters are used by the instance. -/// -/// Returns a bitset where bits representing unused parameters are set (`is_empty` indicates all -/// parameters are used). -fn unused_generic_params<'tcx>( - tcx: TyCtxt<'tcx>, - instance: ty::InstanceKind<'tcx>, -) -> UnusedGenericParams { - assert!(instance.def_id().is_local()); - - if !tcx.sess.opts.unstable_opts.polymorphize { - // If polymorphization disabled, then all parameters are used. - return UnusedGenericParams::new_all_used(); - } - - let def_id = instance.def_id(); - // Exit early if this instance should not be polymorphized. - if !should_polymorphize(tcx, def_id, instance) { - return UnusedGenericParams::new_all_used(); - } - - let generics = tcx.generics_of(def_id); - debug!(?generics); - - // Exit early when there are no parameters to be unused. - if generics.is_empty() { - return UnusedGenericParams::new_all_used(); - } - - // Create a bitset with N rightmost ones for each parameter. - let generics_count: u32 = - generics.count().try_into().expect("more generic parameters than can fit into a `u32`"); - let mut unused_parameters = UnusedGenericParams::new_all_unused(generics_count); - debug!(?unused_parameters, "(start)"); - - mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters); - debug!(?unused_parameters, "(after default)"); - - // Visit MIR and accumulate used generic parameters. - let body = match tcx.hir().body_const_context(def_id.expect_local()) { - // Const functions are actually called and should thus be considered for polymorphization - // via their runtime MIR. - Some(ConstContext::ConstFn) | None => tcx.optimized_mir(def_id), - Some(_) => tcx.mir_for_ctfe(def_id), - }; - let mut vis = MarkUsedGenericParams { tcx, def_id, unused_parameters: &mut unused_parameters }; - vis.visit_body(body); - debug!(?unused_parameters, "(end)"); - - // Emit errors for debugging and testing if enabled. - if !unused_parameters.all_used() { - emit_unused_generic_params_error(tcx, def_id, generics, &unused_parameters); - } - - unused_parameters -} - -/// Returns `true` if the instance should be polymorphized. -fn should_polymorphize<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - instance: ty::InstanceKind<'tcx>, -) -> bool { - // If an instance's MIR body is not polymorphic then the modified generic parameters that are - // derived from polymorphization's result won't make any difference. - if !instance.has_polymorphic_mir_body() { - return false; - } - - // Don't polymorphize intrinsics or virtual calls - calling `instance_mir` will panic. - if matches!(instance, ty::InstanceKind::Intrinsic(..) | ty::InstanceKind::Virtual(..)) { - return false; - } - - // Foreign items have no bodies to analyze. - if tcx.is_foreign_item(def_id) { - return false; - } - - // Make sure there is MIR available. - match tcx.hir().body_const_context(def_id.expect_local()) { - Some(ConstContext::ConstFn) | None if !tcx.is_mir_available(def_id) => { - debug!("no mir available"); - return false; - } - Some(_) if !tcx.is_ctfe_mir_available(def_id) => { - debug!("no ctfe mir available"); - return false; - } - _ => true, - } -} - -/// Some parameters are considered used-by-default, such as non-generic parameters and the dummy -/// generic parameters from closures, this function marks them as used. `leaf_is_closure` should -/// be `true` if the item that `unused_generic_params` was invoked on is a closure. -#[instrument(level = "debug", skip(tcx, def_id, generics, unused_parameters))] -fn mark_used_by_default_parameters<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - generics: &'tcx ty::Generics, - unused_parameters: &mut UnusedGenericParams, -) { - match tcx.def_kind(def_id) { - DefKind::Closure | DefKind::SyntheticCoroutineBody => { - for param in &generics.own_params { - debug!(?param, "(closure/gen)"); - unused_parameters.mark_used(param.index); - } - } - DefKind::Mod - | DefKind::Struct - | DefKind::Union - | DefKind::Enum - | DefKind::Variant - | DefKind::Trait - | DefKind::TyAlias - | DefKind::ForeignTy - | DefKind::TraitAlias - | DefKind::AssocTy - | DefKind::TyParam - | DefKind::Fn - | DefKind::Const - | DefKind::ConstParam - | DefKind::Static { .. } - | DefKind::Ctor(_, _) - | DefKind::AssocFn - | DefKind::AssocConst - | DefKind::Macro(_) - | DefKind::ExternCrate - | DefKind::Use - | DefKind::ForeignMod - | DefKind::AnonConst - | DefKind::InlineConst - | DefKind::OpaqueTy - | DefKind::Field - | DefKind::LifetimeParam - | DefKind::GlobalAsm - | DefKind::Impl { .. } => { - for param in &generics.own_params { - debug!(?param, "(other)"); - if let ty::GenericParamDefKind::Lifetime = param.kind { - unused_parameters.mark_used(param.index); - } - } - } - } - - if let Some(parent) = generics.parent { - mark_used_by_default_parameters(tcx, parent, tcx.generics_of(parent), unused_parameters); - } -} - -/// Emit errors for the function annotated by `#[rustc_polymorphize_error]`, labelling each generic -/// parameter which was unused. -#[instrument(level = "debug", skip(tcx, generics))] -fn emit_unused_generic_params_error<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - generics: &'tcx ty::Generics, - unused_parameters: &UnusedGenericParams, -) { - let base_def_id = tcx.typeck_root_def_id(def_id); - if !tcx.has_attr(base_def_id, sym::rustc_polymorphize_error) { - return; - } - - let fn_span = match tcx.opt_item_ident(def_id) { - Some(ident) => ident.span, - _ => tcx.def_span(def_id), - }; - - let mut param_spans = Vec::new(); - let mut param_names = Vec::new(); - let mut next_generics = Some(generics); - while let Some(generics) = next_generics { - for param in &generics.own_params { - if unused_parameters.is_unused(param.index) { - debug!(?param); - let def_span = tcx.def_span(param.def_id); - param_spans.push(def_span); - param_names.push(param.name.to_string()); - } - } - - next_generics = generics.parent.map(|did| tcx.generics_of(did)); - } - - tcx.dcx().emit_err(UnusedGenericParamsHint { span: fn_span, param_spans, param_names }); -} - -/// Visitor used to aggregate generic parameter uses. -struct MarkUsedGenericParams<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - def_id: DefId, - unused_parameters: &'a mut UnusedGenericParams, -} - -impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> { - /// Invoke `unused_generic_params` on a body contained within the current item (e.g. - /// a closure, coroutine or constant). - #[instrument(level = "debug", skip(self, def_id, args))] - fn visit_child_body(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) { - let instance = ty::InstanceKind::Item(def_id); - let unused = self.tcx.unused_generic_params(instance); - debug!(?self.unused_parameters, ?unused); - for (i, arg) in args.iter().enumerate() { - let i = i.try_into().unwrap(); - if unused.is_used(i) { - arg.visit_with(self); - } - } - debug!(?self.unused_parameters); - } -} - -impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { - #[instrument(level = "debug", skip(self, local))] - fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { - if local == Local::from_usize(1) { - let def_kind = self.tcx.def_kind(self.def_id); - if matches!(def_kind, DefKind::Closure) { - // Skip visiting the closure/coroutine that is currently being processed. This only - // happens because the first argument to the closure is a reference to itself and - // that will call `visit_args`, resulting in each generic parameter captured being - // considered used by default. - debug!("skipping closure args"); - return; - } - } - - self.super_local_decl(local, local_decl); - } - - fn visit_const_operand(&mut self, ct: &mir::ConstOperand<'tcx>, location: Location) { - match ct.const_ { - mir::Const::Ty(_, c) => { - c.visit_with(self); - } - mir::Const::Unevaluated(mir::UnevaluatedConst { def, args: _, promoted }, ty) => { - // Avoid considering `T` unused when constants are of the form: - // `<Self as Foo<T>>::foo::promoted[p]` - if let Some(p) = promoted { - if self.def_id == def && !self.tcx.generics_of(def).has_self { - // If there is a promoted, don't look at the args - since it will always contain - // the generic parameters, instead, traverse the promoted MIR. - let promoted = self.tcx.promoted_mir(def); - self.visit_body(&promoted[p]); - } - } - - Visitor::visit_ty(self, ty, TyContext::Location(location)); - } - mir::Const::Val(_, ty) => Visitor::visit_ty(self, ty, TyContext::Location(location)), - } - } - - fn visit_ty(&mut self, ty: Ty<'tcx>, _: TyContext) { - ty.visit_with(self); - } -} - -impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for MarkUsedGenericParams<'a, 'tcx> { - #[instrument(level = "debug", skip(self))] - fn visit_const(&mut self, c: ty::Const<'tcx>) { - if !c.has_non_region_param() { - return; - } - - match c.kind() { - ty::ConstKind::Param(param) => { - debug!(?param); - self.unused_parameters.mark_used(param.index); - } - ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) - if matches!(self.tcx.def_kind(def), DefKind::AnonConst) => - { - self.visit_child_body(def, args); - } - _ => c.super_visit_with(self), - } - } - - #[instrument(level = "debug", skip(self))] - fn visit_ty(&mut self, ty: Ty<'tcx>) { - if !ty.has_non_region_param() { - return; - } - - match *ty.kind() { - ty::Closure(def_id, args) | ty::Coroutine(def_id, args, ..) => { - debug!(?def_id); - // Avoid cycle errors with coroutines. - if def_id == self.def_id { - return; - } - - // Consider any generic parameters used by any closures/coroutines as used in the - // parent. - self.visit_child_body(def_id, args); - } - ty::Param(param) => { - debug!(?param); - self.unused_parameters.mark_used(param.index); - } - _ => ty.super_visit_with(self), - } - } -} 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 adac35b57cd..198ccb000f3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -339,7 +339,9 @@ where match self.typing_mode() { TypingMode::Coherence => {} - TypingMode::Analysis { .. } | TypingMode::PostAnalysis => { + TypingMode::Analysis { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => { self.discard_impls_shadowed_by_env(goal, &mut candidates); } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 40d1576256e..70ceb22bfea 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -644,6 +644,12 @@ where } } + pub(super) fn next_region_var(&mut self) -> I::Region { + let region = self.delegate.next_region_infer(); + self.inspect.add_var_value(region); + region + } + pub(super) fn next_ty_infer(&mut self) -> I::Ty { let ty = self.delegate.next_ty_infer(); self.inspect.add_var_value(ty); diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 5c54656cc59..ebf1013db1e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -23,7 +23,7 @@ mod trait_goals; use rustc_type_ir::inherent::*; pub use rustc_type_ir::solve::*; -use rustc_type_ir::{self as ty, Interner}; +use rustc_type_ir::{self as ty, Interner, TypingMode}; use tracing::instrument; pub use self::eval_ctxt::{EvalCtxt, GenerateProofTree, SolverDelegateEvalExt}; @@ -321,6 +321,19 @@ where Ok(ct) } } + + fn opaque_type_is_rigid(&self, def_id: I::DefId) -> bool { + match self.typing_mode() { + // Opaques are never rigid outside of analysis mode. + TypingMode::Coherence | TypingMode::PostAnalysis => false, + // During analysis, opaques are rigid unless they may be defined by + // the current body. + TypingMode::Analysis { defining_opaque_types: non_rigid_opaques } + | TypingMode::PostBorrowckAnalysis { defined_opaque_types: non_rigid_opaques } => { + !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id)) + } + } + } } fn response_no_constraints_raw<I: Interner>( 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 33bd1cf2f56..f5b1b23b8e9 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 @@ -6,7 +6,7 @@ mod weak_types; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; -use rustc_type_ir::{self as ty, Interner, NormalizesTo, TypingMode, Upcast as _}; +use rustc_type_ir::{self as ty, Interner, NormalizesTo, Upcast as _}; use tracing::instrument; use crate::delegate::SolverDelegate; @@ -71,21 +71,10 @@ where Ok(()) } ty::AliasTermKind::OpaqueTy => { - match self.typing_mode() { - // Opaques are never rigid outside of analysis mode. - TypingMode::Coherence | TypingMode::PostAnalysis => Err(NoSolution), - // During analysis, opaques are only rigid if we may not define it. - TypingMode::Analysis { defining_opaque_types } => { - if rigid_alias - .def_id - .as_local() - .is_some_and(|def_id| defining_opaque_types.contains(&def_id)) - { - Err(NoSolution) - } else { - Ok(()) - } - } + if self.opaque_type_is_rigid(rigid_alias.def_id) { + Ok(()) + } else { + Err(NoSolution) } } // FIXME(generic_const_exprs): we would need to support generic consts here diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index 336bcb9df33..26a8a22d77e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -2,6 +2,7 @@ //! behaves differently depending on the current `TypingMode`. use rustc_index::bit_set::GrowableBitSet; +use rustc_type_ir::fold::fold_regions; use rustc_type_ir::inherent::*; use rustc_type_ir::{self as ty, Interner, TypingMode}; @@ -95,6 +96,26 @@ where ); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + TypingMode::PostBorrowckAnalysis { defined_opaque_types } => { + let Some(def_id) = opaque_ty.def_id.as_local() else { + return Err(NoSolution); + }; + + if !defined_opaque_types.contains(&def_id) { + return Err(NoSolution); + } + + let actual = cx.type_of(opaque_ty.def_id).instantiate(cx, opaque_ty.args); + // FIXME: Actually use a proper binder here instead of relying on `ReErased`. + // + // This is also probably unsound or sth :shrug: + let actual = fold_regions(cx, actual, |re, _dbi| match re.kind() { + ty::ReErased => self.next_region_var(), + _ => re, + }); + self.eq(goal.param_env, expected, actual)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } TypingMode::PostAnalysis => { // FIXME: Add an assertion that opaque type storage is empty. let actual = cx.type_of(opaque_ty.def_id).instantiate(cx, opaque_ty.args); 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 096dc32ccc9..12df35d30b8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -69,7 +69,9 @@ where // it's not a real impl. (ty::ImplPolarity::Reservation, _) => match ecx.typing_mode() { TypingMode::Coherence => Certainty::AMBIGUOUS, - TypingMode::Analysis { .. } | TypingMode::PostAnalysis => return Err(NoSolution), + TypingMode::Analysis { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => return Err(NoSolution), }, // Impl matches polarity @@ -167,6 +169,14 @@ where return result; } + // Only consider auto impls of unsafe traits when there are no unsafe + // fields. + if ecx.cx().trait_is_unsafe(goal.predicate.def_id()) + && goal.predicate.self_ty().has_unsafe_fields() + { + return Err(NoSolution); + } + // We only look into opaque types during analysis for opaque types // outside of their defining scope. Doing so for opaques in the // defining scope may require calling `typeck` on the same item we're @@ -174,20 +184,7 @@ where // ideally we want to avoid, since we can make progress on this goal // via an alias bound or a locally-inferred hidden type instead. if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() { - match ecx.typing_mode() { - TypingMode::Coherence | TypingMode::PostAnalysis => { - unreachable!("rigid opaque outside of analysis: {goal:?}"); - } - TypingMode::Analysis { defining_opaque_types } => { - if opaque_ty - .def_id - .as_local() - .is_some_and(|def_id| defining_opaque_types.contains(&def_id)) - { - return Err(NoSolution); - } - } - } + debug_assert!(ecx.opaque_type_is_rigid(opaque_ty.def_id)); } ecx.probe_and_evaluate_goal_for_constituent_tys( diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 8c4f669c332..d50bd18a1d7 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -169,9 +169,6 @@ parse_enum_struct_mutually_exclusive = `enum` and `struct` are mutually exclusiv parse_eq_field_init = expected `:`, found `=` .suggestion = replace equals symbol with a colon -parse_equals_struct_default = default values on `struct` fields aren't supported - .suggestion = remove this unsupported default value - parse_escape_only_char = {$byte -> [true] byte *[false] character @@ -343,6 +340,9 @@ parse_incorrect_semicolon = .suggestion = remove this semicolon .help = {$name} declarations are not followed by a semicolon +parse_incorrect_type_on_self = type not allowed for shorthand `self` parameter + .suggestion = move the modifiers on `self` to the type + parse_incorrect_use_of_await = incorrect use of `await` .parentheses_suggestion = `await` is not a method call, remove the parentheses @@ -716,6 +716,10 @@ parse_require_colon_after_labeled_expression = labeled expression must be follow .label = the label .suggestion = add `:` after the label +parse_reserved_multihash = reserved multi-hash token is forbidden + .note = sequences of two or more # are reserved for future use since Rust 2024 + .suggestion_whitespace = consider inserting whitespace here + parse_reserved_string = invalid string literal .note = unprefixed guarded string literals are reserved for future use since Rust 2024 .suggestion_whitespace = consider inserting whitespace here diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 9bdb99dc000..4c4e03cdfa3 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use rustc_ast::token::Token; +use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::{Path, Visibility}; use rustc_errors::codes::*; use rustc_errors::{ @@ -2152,6 +2153,15 @@ pub(crate) enum UnknownPrefixSugg { } #[derive(Diagnostic)] +#[diag(parse_reserved_multihash)] +#[note] +pub(crate) struct ReservedMultihash { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: Option<GuardedStringSugg>, +} +#[derive(Diagnostic)] #[diag(parse_reserved_string)] #[note] pub(crate) struct ReservedString { @@ -2611,8 +2621,9 @@ pub(crate) enum InvalidMutInPattern { #[diag(parse_repeated_mut_in_pattern)] pub(crate) struct RepeatedMutInPattern { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] pub span: Span, + #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] + pub suggestion: Span, } #[derive(Diagnostic)] @@ -2676,7 +2687,7 @@ pub(crate) struct UnexpectedExpressionInPattern { /// Was a `RangePatternBound` expected? pub is_bound: bool, /// The unexpected expr's precedence (used in match arm guard suggestions). - pub expr_precedence: i8, + pub expr_precedence: ExprPrecedence, } #[derive(Subdiagnostic)] @@ -3056,14 +3067,6 @@ pub(crate) struct SingleColonStructType { } #[derive(Diagnostic)] -#[diag(parse_equals_struct_default)] -pub(crate) struct EqualsStructDefault { - #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(parse_macro_rules_missing_bang)] pub(crate) struct MacroRulesMissingBang { #[primary_span] @@ -3398,3 +3401,22 @@ pub(crate) struct PolarityAndModifiers { pub polarity: &'static str, pub modifiers_concatenated: String, } + +#[derive(Diagnostic)] +#[diag(parse_incorrect_type_on_self)] +pub(crate) struct IncorrectTypeOnSelf { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub move_self_modifier: MoveSelfModifier, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +pub(crate) struct MoveSelfModifier { + #[suggestion_part(code = "")] + pub removal_span: Span, + #[suggestion_part(code = "{modifier}")] + pub insertion_span: Span, + pub modifier: String, +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 5023e83bd67..2426eb81678 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -18,6 +18,7 @@ use rustc_span::symbol::Symbol; use rustc_span::{BytePos, Pos, Span}; use tracing::debug; +use crate::lexer::diagnostics::TokenTreeDiagInfo; use crate::lexer::unicode_chars::UNICODE_ARRAY; use crate::{errors, make_unclosed_delims_error}; @@ -56,7 +57,7 @@ pub(crate) fn lex_token_trees<'psess, 'src>( } let cursor = Cursor::new(src); - let string_reader = StringReader { + let mut lexer = Lexer { psess, start_pos, pos: start_pos, @@ -65,34 +66,31 @@ pub(crate) fn lex_token_trees<'psess, 'src>( override_span, nbsp_is_whitespace: false, last_lifetime: None, + token: Token::dummy(), + diag_info: TokenTreeDiagInfo::default(), }; - let (stream, res, unmatched_delims) = - tokentrees::TokenTreesReader::lex_all_token_trees(string_reader); - match res { - Ok(()) if unmatched_delims.is_empty() => Ok(stream), - _ => { - // Return error if there are unmatched delimiters or unclosed delimiters. - // We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch - // because the delimiter mismatch is more likely to be the root cause of error - - let mut buffer = Vec::with_capacity(1); - for unmatched in unmatched_delims { - if let Some(err) = make_unclosed_delims_error(unmatched, psess) { - buffer.push(err); - } - } - if let Err(errs) = res { - // Add unclosing delimiter or diff marker errors - for err in errs { - buffer.push(err); - } - } - Err(buffer) + let (_open_spacing, stream, res) = lexer.lex_token_trees(/* is_delimited */ false); + let unmatched_delims = lexer.diag_info.unmatched_delims; + + if res.is_ok() && unmatched_delims.is_empty() { + Ok(stream) + } else { + // Return error if there are unmatched delimiters or unclosed delimiters. + // We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch + // because the delimiter mismatch is more likely to be the root cause of error + let mut buffer: Vec<_> = unmatched_delims + .into_iter() + .filter_map(|unmatched_delim| make_unclosed_delims_error(unmatched_delim, psess)) + .collect(); + if let Err(errs) = res { + // Add unclosing delimiter or diff marker errors + buffer.extend(errs); } + Err(buffer) } } -struct StringReader<'psess, 'src> { +struct Lexer<'psess, 'src> { psess: &'psess ParseSess, /// Initial position, read-only. start_pos: BytePos, @@ -111,9 +109,14 @@ struct StringReader<'psess, 'src> { /// Track the `Span` for the leading `'` of the last lifetime. Used for /// diagnostics to detect possible typo where `"` was meant. last_lifetime: Option<Span>, + + /// The current token. + token: Token, + + diag_info: TokenTreeDiagInfo, } -impl<'psess, 'src> StringReader<'psess, 'src> { +impl<'psess, 'src> Lexer<'psess, 'src> { fn dcx(&self) -> DiagCtxtHandle<'psess> { self.psess.dcx() } @@ -124,7 +127,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { /// Returns the next token, paired with a bool indicating if the token was /// preceded by whitespace. - fn next_token(&mut self) -> (Token, bool) { + fn next_token_from_cursor(&mut self) -> (Token, bool) { let mut preceded_by_whitespace = false; let mut swallow_next_invalid = 0; // Skip trivial (whitespace & comments) tokens @@ -231,7 +234,8 @@ impl<'psess, 'src> StringReader<'psess, 'src> { .push(span); token::Ident(sym, IdentIsRaw::No) } - // split up (raw) c string literals to an ident and a string literal when edition < 2021. + // split up (raw) c string literals to an ident and a string literal when edition < + // 2021. rustc_lexer::TokenKind::Literal { kind: kind @ (LiteralKind::CStr { .. } | LiteralKind::RawCStr { .. }), suffix_start: _, @@ -252,7 +256,9 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let prefix_span = self.mk_sp(start, lit_start); return (Token::new(self.ident(start), prefix_span), preceded_by_whitespace); } - rustc_lexer::TokenKind::GuardedStrPrefix => self.maybe_report_guarded_str(start, str_before), + rustc_lexer::TokenKind::GuardedStrPrefix => { + self.maybe_report_guarded_str(start, str_before) + } rustc_lexer::TokenKind::Literal { kind, suffix_start } => { let suffix_start = start + BytePos(suffix_start); let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind); @@ -294,15 +300,42 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let prefix_span = self.mk_sp(start, ident_start); if prefix_span.at_least_rust_2021() { + // If the raw lifetime is followed by \' then treat it a normal + // lifetime followed by a \', which is to interpret it as a character + // literal. In this case, it's always an invalid character literal + // since the literal must necessarily have >3 characters (r#...) inside + // of it, which is invalid. + if self.cursor.as_str().starts_with('\'') { + let lit_span = self.mk_sp(start, self.pos + BytePos(1)); + let contents = self.str_from_to(start + BytePos(1), self.pos); + emit_unescape_error( + self.dcx(), + contents, + lit_span, + lit_span, + Mode::Char, + 0..contents.len(), + EscapeError::MoreThanOneChar, + ) + .expect("expected error"); + } + let span = self.mk_sp(start, self.pos); - let lifetime_name_without_tick = Symbol::intern(&self.str_from(ident_start)); + let lifetime_name_without_tick = + Symbol::intern(&self.str_from(ident_start)); if !lifetime_name_without_tick.can_be_raw() { - self.dcx().emit_err(errors::CannotBeRawLifetime { span, ident: lifetime_name_without_tick }); + self.dcx().emit_err( + errors::CannotBeRawLifetime { + span, + ident: lifetime_name_without_tick + } + ); } // Put the `'` back onto the lifetime name. - let mut lifetime_name = String::with_capacity(lifetime_name_without_tick.as_str().len() + 1); + let mut lifetime_name = + String::with_capacity(lifetime_name_without_tick.as_str().len() + 1); lifetime_name.push('\''); lifetime_name += lifetime_name_without_tick.as_str(); let sym = Symbol::intern(&lifetime_name); @@ -358,8 +391,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { rustc_lexer::TokenKind::Caret => token::BinOp(token::Caret), rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent), - rustc_lexer::TokenKind::Unknown - | rustc_lexer::TokenKind::InvalidIdent => { + rustc_lexer::TokenKind::Unknown | rustc_lexer::TokenKind::InvalidIdent => { // Don't emit diagnostics for sequences of the same invalid token if swallow_next_invalid > 0 { swallow_next_invalid -= 1; @@ -790,7 +822,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { /// Detect guarded string literal syntax /// - /// RFC 3598 reserved this syntax for future use. As of Rust 2024, + /// RFC 3593 reserved this syntax for future use. As of Rust 2024, /// using this syntax produces an error. In earlier editions, however, it /// only results in an (allowed by default) lint, and is treated as /// separate tokens. @@ -803,7 +835,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let mut cursor = Cursor::new(str_before); - let (span, unterminated) = match cursor.guarded_double_quoted_string() { + let (is_string, span, unterminated) = match cursor.guarded_double_quoted_string() { Some(rustc_lexer::GuardedStr { n_hashes, terminated, token_len }) => { let end = start + BytePos(token_len); let span = self.mk_sp(start, end); @@ -816,13 +848,13 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let unterminated = if terminated { None } else { Some(str_start) }; - (span, unterminated) + (true, span, unterminated) } - _ => { + None => { // We should only get here in the `##+` case. debug_assert_eq!(self.str_from_to(start, start + BytePos(2)), "##"); - (span, None) + (false, span, None) } }; if edition2024 { @@ -844,7 +876,11 @@ impl<'psess, 'src> StringReader<'psess, 'src> { }; // In Edition 2024 and later, emit a hard error. - let err = self.dcx().emit_err(errors::ReservedString { span, sugg }); + let err = if is_string { + self.dcx().emit_err(errors::ReservedString { span, sugg }) + } else { + self.dcx().emit_err(errors::ReservedMultihash { span, sugg }) + }; token::Literal(token::Lit { kind: token::Err(err), @@ -857,7 +893,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX, span, ast::CRATE_NODE_ID, - BuiltinLintDiag::ReservedString(space_span), + BuiltinLintDiag::ReservedString { is_string, suggestion: space_span }, ); // For backwards compatibility, roll back to after just the first `#` diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index 7b21ffacc84..c6c9eb3b0b2 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -4,41 +4,19 @@ use rustc_ast_pretty::pprust::token_to_string; use rustc_errors::{Applicability, PErr}; use rustc_span::symbol::kw; -use super::diagnostics::{ - TokenTreeDiagInfo, report_suspicious_mismatch_block, same_indentation_level, -}; -use super::{StringReader, UnmatchedDelim}; +use super::diagnostics::{report_suspicious_mismatch_block, same_indentation_level}; +use super::{Lexer, UnmatchedDelim}; use crate::Parser; -pub(super) struct TokenTreesReader<'psess, 'src> { - string_reader: StringReader<'psess, 'src>, - /// The "next" token, which has been obtained from the `StringReader` but - /// not yet handled by the `TokenTreesReader`. - token: Token, - diag_info: TokenTreeDiagInfo, -} - -impl<'psess, 'src> TokenTreesReader<'psess, 'src> { - pub(super) fn lex_all_token_trees( - string_reader: StringReader<'psess, 'src>, - ) -> (TokenStream, Result<(), Vec<PErr<'psess>>>, Vec<UnmatchedDelim>) { - let mut tt_reader = TokenTreesReader { - string_reader, - token: Token::dummy(), - diag_info: TokenTreeDiagInfo::default(), - }; - let (_open_spacing, stream, res) = tt_reader.lex_token_trees(/* is_delimited */ false); - (stream, res, tt_reader.diag_info.unmatched_delims) - } - +impl<'psess, 'src> Lexer<'psess, 'src> { // Lex into a token stream. The `Spacing` in the result is that of the // opening delimiter. - fn lex_token_trees( + pub(super) fn lex_token_trees( &mut self, is_delimited: bool, ) -> (Spacing, TokenStream, Result<(), Vec<PErr<'psess>>>) { // Move past the opening delimiter. - let (_, open_spacing) = self.bump(false); + let open_spacing = self.bump_minimal(); let mut buf = Vec::new(); loop { @@ -71,7 +49,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { } _ => { // Get the next normal token. - let (this_tok, this_spacing) = self.bump(true); + let (this_tok, this_spacing) = self.bump(); buf.push(TokenTree::Token(this_tok, this_spacing)); } } @@ -80,7 +58,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { fn eof_err(&mut self) -> PErr<'psess> { let msg = "this file contains an unclosed delimiter"; - let mut err = self.string_reader.dcx().struct_span_err(self.token.span, msg); + let mut err = self.dcx().struct_span_err(self.token.span, msg); let unclosed_delimiter_show_limit = 5; let len = usize::min(unclosed_delimiter_show_limit, self.diag_info.open_braces.len()); @@ -110,7 +88,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { report_suspicious_mismatch_block( &mut err, &self.diag_info, - self.string_reader.psess.source_map(), + self.psess.source_map(), *delim, ) } @@ -136,7 +114,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { // Expand to cover the entire delimited token tree. let delim_span = DelimSpan::from_pair(pre_span, self.token.span); - let sm = self.string_reader.psess.source_map(); + let sm = self.psess.source_map(); let close_spacing = match self.token.kind { // Correct delimiter. @@ -160,7 +138,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { } // Move past the closing delimiter. - self.bump(false).1 + self.bump_minimal() } // Incorrect delimiter. token::CloseDelim(close_delim) => { @@ -203,7 +181,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { // bar(baz( // } // Incorrect delimiter but matches the earlier `{` if !self.diag_info.open_braces.iter().any(|&(b, _)| b == close_delim) { - self.bump(false).1 + self.bump_minimal() } else { // The choice of value here doesn't matter. Spacing::Alone @@ -225,14 +203,14 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { } // Move on to the next token, returning the current token and its spacing. - // Will glue adjacent single-char tokens together if `glue` is set. - fn bump(&mut self, glue: bool) -> (Token, Spacing) { + // Will glue adjacent single-char tokens together. + fn bump(&mut self) -> (Token, Spacing) { let (this_spacing, next_tok) = loop { - let (next_tok, is_next_tok_preceded_by_whitespace) = self.string_reader.next_token(); + let (next_tok, is_next_tok_preceded_by_whitespace) = self.next_token_from_cursor(); if is_next_tok_preceded_by_whitespace { break (Spacing::Alone, next_tok); - } else if glue && let Some(glued) = self.token.glue(&next_tok) { + } else if let Some(glued) = self.token.glue(&next_tok) { self.token = glued; } else { let this_spacing = if next_tok.is_punct() { @@ -249,6 +227,26 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { (this_tok, this_spacing) } + // Cut-down version of `bump` used when the token kind is known in advance. + fn bump_minimal(&mut self) -> Spacing { + let (next_tok, is_next_tok_preceded_by_whitespace) = self.next_token_from_cursor(); + + let this_spacing = if is_next_tok_preceded_by_whitespace { + Spacing::Alone + } else { + if next_tok.is_punct() { + Spacing::Joint + } else if next_tok == token::Eof { + Spacing::Alone + } else { + Spacing::JointHidden + } + }; + + self.token = next_tok; + this_spacing + } + fn unclosed_delim_err( &mut self, tts: TokenStream, @@ -256,7 +254,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { ) -> Vec<PErr<'psess>> { // If there are unclosed delims, see if there are diff markers and if so, point them // out instead of complaining about the unclosed delims. - let mut parser = Parser::new(self.string_reader.psess, tts, None); + let mut parser = Parser::new(self.psess, tts, None); let mut diff_errs = vec![]; // Suggest removing a `{` we think appears in an `if`/`while` condition. // We want to suggest removing a `{` only if we think we're in an `if`/`while` condition, @@ -314,14 +312,9 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { // An unexpected closing delimiter (i.e., there is no matching opening delimiter). let token_str = token_to_string(&self.token); let msg = format!("unexpected closing delimiter: `{token_str}`"); - let mut err = self.string_reader.dcx().struct_span_err(self.token.span, msg); + let mut err = self.dcx().struct_span_err(self.token.span, msg); - report_suspicious_mismatch_block( - &mut err, - &self.diag_info, - self.string_reader.psess.source_map(), - delim, - ); + report_suspicious_mismatch_block(&mut err, &self.diag_info, self.psess.source_map(), delim); err.span_label(self.token.span, "unexpected closing delimiter"); err } diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs index d78b3664b1e..42eef27803e 100644 --- a/compiler/rustc_parse/src/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs @@ -4,7 +4,7 @@ use rustc_span::symbol::kw; use rustc_span::{BytePos, Pos, Span}; -use super::StringReader; +use super::Lexer; use crate::errors::TokenSubstitution; use crate::token::{self, Delimiter}; @@ -338,7 +338,7 @@ const ASCII_ARRAY: &[(&str, &str, Option<token::TokenKind>)] = &[ ]; pub(super) fn check_for_substitution( - reader: &StringReader<'_, '_>, + lexer: &Lexer<'_, '_>, pos: BytePos, ch: char, count: usize, @@ -351,11 +351,11 @@ pub(super) fn check_for_substitution( let Some((_, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(s, _, _)| s == ascii_str) else { let msg = format!("substitution character not found for '{ch}'"); - reader.dcx().span_bug(span, msg); + lexer.dcx().span_bug(span, msg); }; // special help suggestion for "directed" double quotes - let sugg = if let Some(s) = peek_delimited(&reader.src[reader.src_index(pos)..], '“', '”') { + let sugg = if let Some(s) = peek_delimited(&lexer.src[lexer.src_index(pos)..], '“', '”') { let span = Span::with_root_ctxt( pos, pos + Pos::from_usize('“'.len_utf8() + s.len() + '”'.len_utf8()), diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 2792050a0b3..f963a424a7f 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -24,6 +24,7 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::{Diag, FatalError, PResult}; use rustc_session::parse::ParseSess; use rustc_span::{FileName, SourceFile, Span}; +pub use unicode_normalization::UNICODE_VERSION as UNICODE_NORMALIZATION_VERSION; pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index a1fe5508970..34131e3af6e 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1990,7 +1990,6 @@ impl<'a> Parser<'a> { /// `await? <expr>`, `await(<expr>)`, and `await { <expr> }`. pub(super) fn recover_incorrect_await_syntax( &mut self, - lo: Span, await_sp: Span, ) -> PResult<'a, P<Expr>> { let (hi, expr, is_question) = if self.token == token::Not { @@ -1999,8 +1998,8 @@ impl<'a> Parser<'a> { } else { self.recover_await_prefix(await_sp)? }; - let (sp, guar) = self.error_on_incorrect_await(lo, hi, &expr, is_question); - let expr = self.mk_expr_err(lo.to(sp), guar); + let (sp, guar) = self.error_on_incorrect_await(await_sp, hi, &expr, is_question); + let expr = self.mk_expr_err(await_sp.to(sp), guar); self.maybe_recover_from_bad_qpath(expr) } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index aa5e9586daf..eeb83a85e59 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,7 +1,7 @@ // ignore-tidy-filelength use core::mem; -use core::ops::ControlFlow; +use core::ops::{Bound, ControlFlow}; use ast::mut_visit::{self, MutVisitor}; use ast::token::IdentIsRaw; @@ -10,7 +10,7 @@ use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::util::case::Case; use rustc_ast::util::classify; -use rustc_ast::util::parser::{AssocOp, Fixity, prec_let_scrutinee_needs_par}; +use rustc_ast::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutinee_needs_par}; use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::{ self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy, @@ -120,7 +120,7 @@ impl<'a> Parser<'a> { r: Restrictions, attrs: AttrWrapper, ) -> PResult<'a, (P<Expr>, bool)> { - self.with_res(r, |this| this.parse_expr_assoc_with(0, attrs)) + self.with_res(r, |this| this.parse_expr_assoc_with(Bound::Unbounded, attrs)) } /// Parses an associative expression with operators of at least `min_prec` precedence. @@ -128,7 +128,7 @@ impl<'a> Parser<'a> { /// followed by a subexpression (e.g. `1 + 2`). pub(super) fn parse_expr_assoc_with( &mut self, - min_prec: usize, + min_prec: Bound<ExprPrecedence>, attrs: AttrWrapper, ) -> PResult<'a, (P<Expr>, bool)> { let lhs = if self.token.is_range_separator() { @@ -144,7 +144,7 @@ impl<'a> Parser<'a> { /// was actually parsed. pub(super) fn parse_expr_assoc_rest_with( &mut self, - min_prec: usize, + min_prec: Bound<ExprPrecedence>, starts_stmt: bool, mut lhs: P<Expr>, ) -> PResult<'a, (P<Expr>, bool)> { @@ -163,7 +163,11 @@ impl<'a> Parser<'a> { self.restrictions }; let prec = op.node.precedence(); - if prec < min_prec { + if match min_prec { + Bound::Included(min_prec) => prec < min_prec, + Bound::Excluded(min_prec) => prec <= min_prec, + Bound::Unbounded => false, + } { break; } // Check for deprecated `...` syntax @@ -276,16 +280,16 @@ impl<'a> Parser<'a> { } let fixity = op.fixity(); - let prec_adjustment = match fixity { - Fixity::Right => 0, - Fixity::Left => 1, + let min_prec = match fixity { + Fixity::Right => Bound::Included(prec), + Fixity::Left => Bound::Excluded(prec), // We currently have no non-associative operators that are not handled above by // the special cases. The code is here only for future convenience. - Fixity::None => 1, + Fixity::None => Bound::Excluded(prec), }; let (rhs, _) = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| { let attrs = this.parse_outer_attributes()?; - this.parse_expr_assoc_with(prec + prec_adjustment, attrs) + this.parse_expr_assoc_with(min_prec, attrs) })?; let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); @@ -451,7 +455,7 @@ impl<'a> Parser<'a> { /// The other two variants are handled in `parse_prefix_range_expr` below. fn parse_expr_range( &mut self, - prec: usize, + prec: ExprPrecedence, lhs: P<Expr>, op: AssocOp, cur_op_span: Span, @@ -460,7 +464,7 @@ impl<'a> Parser<'a> { let maybe_lt = self.token.clone(); let attrs = self.parse_outer_attributes()?; Some( - self.parse_expr_assoc_with(prec + 1, attrs) + self.parse_expr_assoc_with(Bound::Excluded(prec), attrs) .map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))? .0, ) @@ -518,7 +522,7 @@ impl<'a> Parser<'a> { let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() { // RHS must be parsed with more associativity than the dots. let attrs = this.parse_outer_attributes()?; - this.parse_expr_assoc_with(op.unwrap().precedence() + 1, attrs) + this.parse_expr_assoc_with(Bound::Excluded(op.unwrap().precedence()), attrs) .map(|(x, _)| (lo.to(x.span), Some(x))) .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))? } else { @@ -1446,34 +1450,31 @@ impl<'a> Parser<'a> { this.parse_expr_closure() } else { assert!(this.eat_keyword(kw::For)); - this.parse_expr_for(None, this.prev_token.span) + this.parse_expr_for(None, lo) } } else if this.eat_keyword(kw::While) { - this.parse_expr_while(None, this.prev_token.span) + this.parse_expr_while(None, lo) } else if let Some(label) = this.eat_label() { this.parse_expr_labeled(label, true) } else if this.eat_keyword(kw::Loop) { - let sp = this.prev_token.span; - this.parse_expr_loop(None, this.prev_token.span).map_err(|mut err| { - err.span_label(sp, "while parsing this `loop` expression"); + this.parse_expr_loop(None, lo).map_err(|mut err| { + err.span_label(lo, "while parsing this `loop` expression"); err }) } else if this.eat_keyword(kw::Match) { - let match_sp = this.prev_token.span; this.parse_expr_match().map_err(|mut err| { - err.span_label(match_sp, "while parsing this `match` expression"); + err.span_label(lo, "while parsing this `match` expression"); err }) } else if this.eat_keyword(kw::Unsafe) { - let sp = this.prev_token.span; this.parse_expr_block(None, lo, BlockCheckMode::Unsafe(ast::UserProvided)).map_err( |mut err| { - err.span_label(sp, "while parsing this `unsafe` expression"); + err.span_label(lo, "while parsing this `unsafe` expression"); err }, ) } else if this.check_inline_const(0) { - this.parse_const_block(lo.to(this.token.span), false) + this.parse_const_block(lo, false) } else if this.may_recover() && this.is_do_catch_block() { this.recover_do_catch() } else if this.is_try_block() { @@ -1514,7 +1515,7 @@ impl<'a> Parser<'a> { this.parse_expr_closure() } } else if this.eat_keyword_noexpect(kw::Await) { - this.recover_incorrect_await_syntax(lo, this.prev_token.span) + this.recover_incorrect_await_syntax(lo) } else { this.parse_expr_lit() } @@ -2630,7 +2631,7 @@ impl<'a> Parser<'a> { }; self.bump(); // Eat `let` token let lo = self.prev_token.span; - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -2646,7 +2647,8 @@ impl<'a> Parser<'a> { self.expect(&token::Eq)?; } let attrs = self.parse_outer_attributes()?; - let (expr, _) = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), attrs)?; + let (expr, _) = + self.parse_expr_assoc_with(Bound::Excluded(prec_let_scrutinee_needs_par()), attrs)?; let span = lo.to(expr.span); Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered))) } @@ -2776,7 +2778,7 @@ impl<'a> Parser<'a> { }; // Try to parse the pattern `for ($PAT) in $EXPR`. let pat = match ( - self.parse_pat_allow_top_alt( + self.parse_pat_allow_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3239,7 +3241,7 @@ impl<'a> Parser<'a> { // then we should recover. let mut snapshot = this.create_snapshot_for_diagnostic(); let pattern_follows = snapshot - .parse_pat_allow_top_alt( + .parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3313,43 +3315,37 @@ impl<'a> Parser<'a> { fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P<Pat>, Option<P<Expr>>)> { if self.token == token::OpenDelim(Delimiter::Parenthesis) { - // Detect and recover from `($pat if $cond) => $arm`. let left = self.token.span; - match self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, CommaRecoveryMode::EitherTupleOrPipe, - ) { - Ok(pat) => Ok((pat, self.parse_match_arm_guard()?)), - Err(err) - if let prev_sp = self.prev_token.span - && let true = self.eat_keyword(kw::If) => - { - // We know for certain we've found `($pat if` so far. - let mut cond = match self.parse_match_guard_condition() { - Ok(cond) => cond, - Err(cond_err) => { - cond_err.cancel(); - return Err(err); - } - }; - err.cancel(); - CondChecker::new(self).visit_expr(&mut cond); - self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]); - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; - let right = self.prev_token.span; - self.dcx().emit_err(errors::ParenthesesInMatchPat { - span: vec![left, right], - sugg: errors::ParenthesesInMatchPatSugg { left, right }, - }); - Ok((self.mk_pat(left.to(prev_sp), ast::PatKind::Wild), Some(cond))) - } - Err(err) => Err(err), + )?; + if let ast::PatKind::Paren(subpat) = &pat.kind + && let ast::PatKind::Guard(..) = &subpat.kind + { + // Detect and recover from `($pat if $cond) => $arm`. + // FIXME(guard_patterns): convert this to a normal guard instead + let span = pat.span; + let ast::PatKind::Paren(subpat) = pat.into_inner().kind else { unreachable!() }; + let ast::PatKind::Guard(_, mut cond) = subpat.into_inner().kind else { + unreachable!() + }; + self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span); + CondChecker::new(self).visit_expr(&mut cond); + let right = self.prev_token.span; + self.dcx().emit_err(errors::ParenthesesInMatchPat { + span: vec![left, right], + sugg: errors::ParenthesesInMatchPatSugg { left, right }, + }); + Ok((self.mk_pat(span, ast::PatKind::Wild), Some(cond))) + } else { + Ok((pat, self.parse_match_arm_guard()?)) } } else { // Regular parser flow: - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3537,7 +3533,7 @@ impl<'a> Parser<'a> { let exp_span = self.prev_token.span; // We permit `.. }` on the left-hand side of a destructuring assignment. if self.check(&token::CloseDelim(close_delim)) { - base = ast::StructRest::Rest(self.prev_token.span.shrink_to_hi()); + base = ast::StructRest::Rest(self.prev_token.span); break; } match self.parse_expr() { diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 5aebe716b0a..76ecb77d750 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -1,6 +1,7 @@ use ast::token::Delimiter; use rustc_ast::{ - self as ast, AttrVec, GenericBounds, GenericParam, GenericParamKind, TyKind, WhereClause, token, + self as ast, AttrVec, DUMMY_NODE_ID, GenericBounds, GenericParam, GenericParamKind, TyKind, + WhereClause, token, }; use rustc_errors::{Applicability, PResult}; use rustc_span::Span; @@ -14,8 +15,8 @@ use crate::errors::{ WhereClauseBeforeTupleStructBodySugg, }; -enum PredicateOrStructBody { - Predicate(ast::WherePredicate), +enum PredicateKindOrStructBody { + PredicateKind(ast::WherePredicateKind), StructBody(ThinVec<ast::FieldDef>), } @@ -218,10 +219,11 @@ impl<'a> Parser<'a> { } else if this.token.can_begin_type() { // Trying to write an associated type bound? (#26271) let snapshot = this.create_snapshot_for_diagnostic(); - match this.parse_ty_where_predicate() { - Ok(where_predicate) => { + let lo = this.token.span; + match this.parse_ty_where_predicate_kind() { + Ok(_) => { this.dcx().emit_err(errors::BadAssocTypeBounds { - span: where_predicate.span(), + span: lo.to(this.prev_token.span), }); // FIXME - try to continue parsing other generics? } @@ -340,31 +342,33 @@ impl<'a> Parser<'a> { loop { let where_sp = where_lo.to(self.prev_token.span); let pred_lo = self.token.span; - if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { + let kind = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { let lifetime = self.expect_lifetime(); // Bounds starting with a colon are mandatory, but possibly empty. self.expect(&token::Colon)?; let bounds = self.parse_lt_param_bounds(); - where_clause.predicates.push(ast::WherePredicate::RegionPredicate( - ast::WhereRegionPredicate { - span: pred_lo.to(self.prev_token.span), - lifetime, - bounds, - }, - )); + ast::WherePredicateKind::RegionPredicate(ast::WhereRegionPredicate { + lifetime, + bounds, + }) } else if self.check_type() { - match self.parse_ty_where_predicate_or_recover_tuple_struct_body( + match self.parse_ty_where_predicate_kind_or_recover_tuple_struct_body( struct_, pred_lo, where_sp, )? { - PredicateOrStructBody::Predicate(pred) => where_clause.predicates.push(pred), - PredicateOrStructBody::StructBody(body) => { + PredicateKindOrStructBody::PredicateKind(kind) => kind, + PredicateKindOrStructBody::StructBody(body) => { tuple_struct_body = Some(body); break; } } } else { break; - } + }; + where_clause.predicates.push(ast::WherePredicate { + kind, + id: DUMMY_NODE_ID, + span: pred_lo.to(self.prev_token.span), + }); let prev_token = self.prev_token.span; let ate_comma = self.eat(&token::Comma); @@ -384,12 +388,12 @@ impl<'a> Parser<'a> { Ok((where_clause, tuple_struct_body)) } - fn parse_ty_where_predicate_or_recover_tuple_struct_body( + fn parse_ty_where_predicate_kind_or_recover_tuple_struct_body( &mut self, struct_: Option<(Ident, Span)>, pred_lo: Span, where_sp: Span, - ) -> PResult<'a, PredicateOrStructBody> { + ) -> PResult<'a, PredicateKindOrStructBody> { let mut snapshot = None; if let Some(struct_) = struct_ @@ -399,8 +403,8 @@ impl<'a> Parser<'a> { snapshot = Some((struct_, self.create_snapshot_for_diagnostic())); }; - match self.parse_ty_where_predicate() { - Ok(pred) => Ok(PredicateOrStructBody::Predicate(pred)), + match self.parse_ty_where_predicate_kind() { + Ok(pred) => Ok(PredicateKindOrStructBody::PredicateKind(pred)), Err(type_err) => { let Some(((struct_name, body_insertion_point), mut snapshot)) = snapshot else { return Err(type_err); @@ -436,7 +440,7 @@ impl<'a> Parser<'a> { }); self.restore_snapshot(snapshot); - Ok(PredicateOrStructBody::StructBody(body)) + Ok(PredicateKindOrStructBody::StructBody(body)) } Ok(_) => Err(type_err), Err(body_err) => { @@ -448,8 +452,7 @@ impl<'a> Parser<'a> { } } - fn parse_ty_where_predicate(&mut self) -> PResult<'a, ast::WherePredicate> { - let lo = self.token.span; + fn parse_ty_where_predicate_kind(&mut self) -> PResult<'a, ast::WherePredicateKind> { // Parse optional `for<'a, 'b>`. // This `for` is parsed greedily and applies to the whole predicate, // the bounded type can have its own `for` applying only to it. @@ -464,8 +467,7 @@ impl<'a> Parser<'a> { let ty = self.parse_ty_for_where_clause()?; if self.eat(&token::Colon) { let bounds = self.parse_generic_bounds()?; - Ok(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { - span: lo.to(self.prev_token.span), + Ok(ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate { bound_generic_params: lifetime_defs, bounded_ty: ty, bounds, @@ -474,11 +476,7 @@ impl<'a> Parser<'a> { // FIXME: We are just dropping the binders in lifetime_defs on the floor here. } else if self.eat(&token::Eq) || self.eat(&token::EqEq) { let rhs_ty = self.parse_ty()?; - Ok(ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { - span: lo.to(self.prev_token.span), - lhs_ty: ty, - rhs_ty, - })) + Ok(ast::WherePredicateKind::EqPredicate(ast::WhereEqPredicate { lhs_ty: ty, rhs_ty })) } else { self.maybe_recover_bounds_doubled_colon(&ty)?; self.unexpected_any() diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 26e81b7676b..58b4bf8980b 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1836,6 +1836,22 @@ impl<'a> Parser<'a> { return Err(err); } }; + let mut default = None; + if p.token == token::Eq { + let mut snapshot = p.create_snapshot_for_diagnostic(); + snapshot.bump(); + match snapshot.parse_expr_anon_const() { + Ok(const_expr) => { + let sp = ty.span.shrink_to_hi().to(const_expr.value.span); + p.psess.gated_spans.gate(sym::default_field_values, sp); + p.restore_snapshot(snapshot); + default = Some(const_expr); + } + Err(err) => { + err.cancel(); + } + } + } Ok(( FieldDef { @@ -1845,6 +1861,7 @@ impl<'a> Parser<'a> { ident: None, id: DUMMY_NODE_ID, ty, + default, attrs, is_placeholder: false, }, @@ -2024,12 +2041,15 @@ impl<'a> Parser<'a> { if self.token == token::Colon && self.look_ahead(1, |t| *t != token::Colon) { self.dcx().emit_err(errors::SingleColonStructType { span: self.token.span }); } - if self.token == token::Eq { + let default = if self.token == token::Eq { self.bump(); let const_expr = self.parse_expr_anon_const()?; let sp = ty.span.shrink_to_hi().to(const_expr.value.span); - self.dcx().emit_err(errors::EqualsStructDefault { span: sp }); - } + self.psess.gated_spans.gate(sym::default_field_values, sp); + Some(const_expr) + } else { + None + }; Ok(FieldDef { span: lo.to(self.prev_token.span), ident: Some(name), @@ -2037,6 +2057,7 @@ impl<'a> Parser<'a> { safety, id: DUMMY_NODE_ID, ty, + default, attrs, is_placeholder: false, }) @@ -2941,6 +2962,32 @@ impl<'a> Parser<'a> { }; Ok((eself, eself_ident, eself_hi)) }; + let expect_self_ident_not_typed = + |this: &mut Self, modifier: &SelfKind, modifier_span: Span| { + let eself_ident = expect_self_ident(this); + + // Recover `: Type` after a qualified self + if this.may_recover() && this.eat_noexpect(&token::Colon) { + let snap = this.create_snapshot_for_diagnostic(); + match this.parse_ty() { + Ok(ty) => { + this.dcx().emit_err(errors::IncorrectTypeOnSelf { + span: ty.span, + move_self_modifier: errors::MoveSelfModifier { + removal_span: modifier_span, + insertion_span: ty.span.shrink_to_lo(), + modifier: modifier.to_ref_suggestion(), + }, + }); + } + Err(diag) => { + diag.cancel(); + this.restore_snapshot(snap); + } + } + } + eself_ident + }; // Recover for the grammar `*self`, `*const self`, and `*mut self`. let recover_self_ptr = |this: &mut Self| { this.dcx().emit_err(errors::SelfArgumentPointer { span: this.token.span }); @@ -2978,7 +3025,9 @@ impl<'a> Parser<'a> { // `¬_self` return Ok(None); }; - (eself, expect_self_ident(self), self.prev_token.span) + let hi = self.token.span; + let self_ident = expect_self_ident_not_typed(self, &eself, eself_lo.until(hi)); + (eself, self_ident, hi) } // `*self` token::BinOp(token::Star) if is_isolated_self(self, 1) => { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 0ed8d152d2d..37556c064d8 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1376,7 +1376,7 @@ impl<'a> Parser<'a> { AttrArgs::Delimited(args) } else if self.eat(&token::Eq) { let eq_span = self.prev_token.span; - AttrArgs::Eq(eq_span, AttrArgsEq::Ast(self.parse_expr_force_collect()?)) + AttrArgs::Eq { eq_span, value: AttrArgsEq::Ast(self.parse_expr_force_collect()?) } } else { AttrArgs::Empty }) diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 8fb6f85d0dd..752a52b382b 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -174,7 +174,7 @@ impl<'a> Parser<'a> { NonterminalKind::Pat(pat_kind) => { NtPat(self.collect_tokens_no_attrs(|this| match pat_kind { PatParam { .. } => this.parse_pat_no_top_alt(None, None), - PatWithOr => this.parse_pat_allow_top_alt( + PatWithOr => this.parse_pat_no_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index c4326427f67..4cda887a02b 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1,11 +1,13 @@ +use std::ops::Bound; + use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::ptr::P; use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token}; +use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{ - self as ast, Arm, AttrVec, BinOpKind, BindingMode, ByRef, Expr, ExprKind, ExprPrecedence, - LocalKind, MacCall, Mutability, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, - RangeSyntax, Stmt, StmtKind, + self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, Mutability, + Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, StmtKind, }; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey}; @@ -97,9 +99,34 @@ pub enum PatternLocation { impl<'a> Parser<'a> { /// Parses a pattern. /// - /// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns - /// at the top level. Used when parsing the parameters of lambda expressions, - /// functions, function pointers, and `pat` macro fragments. + /// Corresponds to `Pattern` in RFC 3637 and admits guard patterns at the top level. + /// Used when parsing patterns in all cases where neither `PatternNoTopGuard` nor + /// `PatternNoTopAlt` (see below) are used. + pub fn parse_pat_allow_top_guard( + &mut self, + expected: Option<Expected>, + rc: RecoverComma, + ra: RecoverColon, + rt: CommaRecoveryMode, + ) -> PResult<'a, P<Pat>> { + let pat = self.parse_pat_no_top_guard(expected, rc, ra, rt)?; + + if self.eat_keyword(kw::If) { + let cond = self.parse_expr()?; + // Feature-gate guard patterns + self.psess.gated_spans.gate(sym::guard_patterns, cond.span); + let span = pat.span.to(cond.span); + Ok(self.mk_pat(span, PatKind::Guard(pat, cond))) + } else { + Ok(pat) + } + } + + /// Parses a pattern. + /// + /// Corresponds to `PatternNoTopAlt` in RFC 3637 and does not admit or-patterns + /// or guard patterns at the top level. Used when parsing the parameters of lambda + /// expressions, functions, function pointers, and `pat_param` macro fragments. pub fn parse_pat_no_top_alt( &mut self, expected: Option<Expected>, @@ -110,25 +137,26 @@ impl<'a> Parser<'a> { /// Parses a pattern. /// - /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level. - /// Used for parsing patterns in all cases when `pat<no_top_alt>` is not used. + /// Corresponds to `PatternNoTopGuard` in RFC 3637 and allows or-patterns, but not + /// guard patterns, at the top level. Used for parsing patterns in `pat` fragments (until + /// the next edition) and `let`, `if let`, and `while let` expressions. /// /// Note that after the FCP in <https://github.com/rust-lang/rust/issues/81415>, /// a leading vert is allowed in nested or-patterns, too. This allows us to /// simplify the grammar somewhat. - pub fn parse_pat_allow_top_alt( + pub fn parse_pat_no_top_guard( &mut self, expected: Option<Expected>, rc: RecoverComma, ra: RecoverColon, rt: CommaRecoveryMode, ) -> PResult<'a, P<Pat>> { - self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) + self.parse_pat_no_top_guard_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) } /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true = /// recovered). - fn parse_pat_allow_top_alt_inner( + fn parse_pat_no_top_guard_inner( &mut self, expected: Option<Expected>, rc: RecoverComma, @@ -229,7 +257,7 @@ impl<'a> Parser<'a> { // We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level // or-patterns so that we can detect when a user tries to use it. This allows us to print a // better error message. - let (pat, trailing_vert) = self.parse_pat_allow_top_alt_inner( + let (pat, trailing_vert) = self.parse_pat_no_top_guard_inner( expected, rc, RecoverColon::No, @@ -434,8 +462,9 @@ impl<'a> Parser<'a> { // Parse an associative expression such as `+ expr`, `% expr`, ... // Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`]. - let Ok((expr, _)) = - snapshot.parse_expr_assoc_rest_with(0, false, expr).map_err(|err| err.cancel()) + let Ok((expr, _)) = snapshot + .parse_expr_assoc_rest_with(Bound::Unbounded, false, expr) + .map_err(|err| err.cancel()) else { // We got a trailing method/operator, but that wasn't an expression. return None; @@ -458,7 +487,7 @@ impl<'a> Parser<'a> { .create_err(UnexpectedExpressionInPattern { span, is_bound, - expr_precedence: expr.precedence().order(), + expr_precedence: expr.precedence(), }) .stash(span, StashKey::ExprInPat) .unwrap(), @@ -544,9 +573,7 @@ impl<'a> Parser<'a> { // HACK: a neater way would be preferable. let expr = match &err.args["expr_precedence"] { DiagArgValue::Number(expr_precedence) => { - if *expr_precedence - <= ExprPrecedence::Binary(BinOpKind::Eq).order() as i32 - { + if *expr_precedence <= ExprPrecedence::Compare as i32 { format!("({expr})") } else { format!("{expr}") @@ -568,8 +595,7 @@ impl<'a> Parser<'a> { } Some(guard) => { // Are parentheses required around the old guard? - let wrap_guard = guard.precedence().order() - <= ExprPrecedence::Binary(BinOpKind::And).order(); + let wrap_guard = guard.precedence() <= ExprPrecedence::LAnd; err.subdiagnostic( UnexpectedExpressionInPatternSugg::UpdateGuard { @@ -696,7 +722,7 @@ impl<'a> Parser<'a> { } else if self.check(&token::OpenDelim(Delimiter::Bracket)) { // Parse `[pat, pat,...]` as a slice pattern. let (pats, _) = self.parse_delim_comma_seq(Delimiter::Bracket, |p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -944,7 +970,7 @@ impl<'a> Parser<'a> { let open_paren = self.token.span; let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -1086,7 +1112,9 @@ impl<'a> Parser<'a> { return; } - self.dcx().emit_err(RepeatedMutInPattern { span: lo.to(self.prev_token.span) }); + let span = lo.to(self.prev_token.span); + let suggestion = span.with_hi(self.token.span.lo()); + self.dcx().emit_err(RepeatedMutInPattern { span, suggestion }); } /// Parse macro invocation @@ -1359,7 +1387,7 @@ impl<'a> Parser<'a> { path: Path, ) -> PResult<'a, PatKind> { let (fields, _) = self.parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -1394,7 +1422,7 @@ impl<'a> Parser<'a> { self.parse_builtin(|self_, _lo, ident| { Ok(match ident.name { // builtin#deref(PAT) - sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_alt( + sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -1669,7 +1697,7 @@ impl<'a> Parser<'a> { // Parsing a pattern of the form `fieldname: pat`. let fieldname = self.parse_field_name()?; self.bump(); - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 2f19a9b6b20..6a7029a8f1c 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -469,7 +469,7 @@ impl<'a> Parser<'a> { PathStyle::Pat if let Ok(_) = self .parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 190cd9ed061..5fa2e01fc86 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::mem; +use std::ops::Bound; use ast::Label; use rustc_ast as ast; @@ -207,7 +208,7 @@ impl<'a> Parser<'a> { // Perform this outside of the `collect_tokens` closure, since our // outer attributes do not apply to this part of the expression. let (expr, _) = self.with_res(Restrictions::STMT_EXPR, |this| { - this.parse_expr_assoc_rest_with(0, true, expr) + this.parse_expr_assoc_rest_with(Bound::Unbounded, true, expr) })?; Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr))) } else { @@ -240,7 +241,7 @@ impl<'a> Parser<'a> { let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac)); let e = self.maybe_recover_from_bad_qpath(e)?; let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?; - let (e, _) = self.parse_expr_assoc_rest_with(0, false, e)?; + let (e, _) = self.parse_expr_assoc_rest_with(Bound::Unbounded, false, e)?; StmtKind::Expr(e) }; Ok(self.mk_stmt(lo.to(hi), kind)) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index c561ea3823d..8cff23c2e32 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -9,7 +9,7 @@ use rustc_ast::{ }; use rustc_errors::{Applicability, PResult}; use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Span}; use thin_vec::{ThinVec, thin_vec}; use super::{Parser, PathStyle, SeqSep, TokenType, Trailing}; @@ -274,7 +274,6 @@ impl<'a> Parser<'a> { // Function pointer type self.parse_ty_bare_fn(lo, ThinVec::new(), None, recover_return_sign)? } else if self.check_keyword(kw::For) { - let for_span = self.token.span; // Function pointer type or bound list (trait object type) starting with a poly-trait. // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` @@ -302,7 +301,7 @@ impl<'a> Parser<'a> { kw: kw.name.as_str(), sugg: errors::TransposeDynOrImplSugg { removal_span, - insertion_span: for_span.shrink_to_lo(), + insertion_span: lo.shrink_to_lo(), kw: kw.name.as_str(), }, }); @@ -345,16 +344,14 @@ impl<'a> Parser<'a> { // FIXME(c_variadic): Should we just allow `...` syntactically // anywhere in a type and use semantic restrictions instead? // NOTE: This may regress certain MBE calls if done incorrectly. - let guar = self - .dcx() - .emit_err(NestedCVariadicType { span: lo.to(self.prev_token.span) }); + let guar = self.dcx().emit_err(NestedCVariadicType { span: lo }); TyKind::Err(guar) } } } else { let msg = format!("expected type, found {}", super::token_descr(&self.token)); - let mut err = self.dcx().struct_span_err(self.token.span, msg); - err.span_label(self.token.span, "expected type"); + let mut err = self.dcx().struct_span_err(lo, msg); + err.span_label(lo, "expected type"); return Err(err); }; @@ -943,7 +940,7 @@ impl<'a> Parser<'a> { let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() && self.eat_keyword(kw::Async) { - self.psess.gated_spans.gate(sym::async_closure, self.prev_token.span); + self.psess.gated_spans.gate(sym::async_trait_bounds, self.prev_token.span); BoundAsyncness::Async(self.prev_token.span) } else if self.may_recover() && self.token.uninterpolated_span().is_rust_2015() @@ -954,7 +951,7 @@ impl<'a> Parser<'a> { span: self.prev_token.span, help: HelpUseLatestEdition::new(), }); - self.psess.gated_spans.gate(sym::async_closure, self.prev_token.span); + self.psess.gated_spans.gate(sym::async_trait_bounds, self.prev_token.span); BoundAsyncness::Async(self.prev_token.span) } else { BoundAsyncness::Normal @@ -1139,7 +1136,7 @@ impl<'a> Parser<'a> { Some(ast::Path { span: fn_token_span.to(self.prev_token.span), segments: thin_vec![ast::PathSegment { - ident: Ident::new(Symbol::intern("Fn"), fn_token_span), + ident: Ident::new(sym::Fn, fn_token_span), id: DUMMY_NODE_ID, args: Some(P(ast::GenericArgs::Parenthesized(ast::ParenthesizedArgs { span: args_lo.to(self.prev_token.span), diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index f3174e7dea2..aab3f10bc66 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -43,7 +43,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) { } } _ => { - if let AttrArgs::Eq(..) = attr_item.args { + if let AttrArgs::Eq { .. } = attr_item.args { // All key-value attributes are restricted to meta-item syntax. match parse_meta(psess, attr) { Ok(_) => {} @@ -70,7 +70,7 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met parse_in(psess, tokens.clone(), "meta list", |p| p.parse_meta_seq_top())?; MetaItemKind::List(nmis) } - AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => { + AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => { if let ast::ExprKind::Lit(token_lit) = expr.kind { let res = ast::MetaItemLit::from_token_lit(token_lit, expr.span); let res = match res { @@ -116,7 +116,9 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met return Err(err); } } - AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => MetaItemKind::NameValue(lit.clone()), + AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => { + MetaItemKind::NameValue(lit.clone()) + } }, }) } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 0712af422ad..7a0a518bb51 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -709,8 +709,6 @@ passes_should_be_applied_to_trait = attribute should be applied to a trait .label = not a trait -passes_skipping_const_checks = skipping const checks - passes_stability_promotable = attribute cannot be applied to an expression diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs deleted file mode 100644 index f5ece513956..00000000000 --- a/compiler/rustc_passes/src/check_const.rs +++ /dev/null @@ -1,236 +0,0 @@ -//! This pass checks HIR bodies that may be evaluated at compile-time (e.g., `const`, `static`, -//! `const fn`) for structured control flow (e.g. `if`, `while`), which is forbidden in a const -//! context. -//! -//! By the time the MIR const-checker runs, these high-level constructs have been lowered to -//! control-flow primitives (e.g., `Goto`, `SwitchInt`), making it tough to properly attribute -//! errors. We still look for those primitives in the MIR const-checker to ensure nothing slips -//! through, but errors for structured control flow in a `const` should be emitted here. - -use rustc_hir::def_id::{LocalDefId, LocalModDefId}; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_middle::hir::nested_filter; -use rustc_middle::query::Providers; -use rustc_middle::span_bug; -use rustc_middle::ty::TyCtxt; -use rustc_session::parse::feature_err; -use rustc_span::{Span, Symbol, sym}; -use {rustc_attr as attr, rustc_hir as hir}; - -use crate::errors::SkippingConstChecks; - -/// An expression that is not *always* legal in a const context. -#[derive(Clone, Copy)] -enum NonConstExpr { - Loop(hir::LoopSource), - Match(hir::MatchSource), -} - -impl NonConstExpr { - fn name(self) -> String { - match self { - Self::Loop(src) => format!("`{}`", src.name()), - Self::Match(src) => format!("`{}`", src.name()), - } - } - - fn required_feature_gates(self) -> Option<&'static [Symbol]> { - use hir::LoopSource::*; - use hir::MatchSource::*; - - let gates: &[_] = match self { - Self::Match(AwaitDesugar) => { - return None; - } - - Self::Loop(ForLoop) | Self::Match(ForLoopDesugar) => &[sym::const_for], - - Self::Match(TryDesugar(_)) => &[sym::const_try], - - // All other expressions are allowed. - Self::Loop(Loop | While) | Self::Match(Normal | Postfix | FormatArgs) => &[], - }; - - Some(gates) - } -} - -fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { - let mut vis = CheckConstVisitor::new(tcx); - tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis); -} - -pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { check_mod_const_bodies, ..*providers }; -} - -#[derive(Copy, Clone)] -struct CheckConstVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - const_kind: Option<hir::ConstContext>, - def_id: Option<LocalDefId>, -} - -impl<'tcx> CheckConstVisitor<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> Self { - CheckConstVisitor { tcx, const_kind: None, def_id: None } - } - - /// Emits an error when an unsupported expression is found in a const context. - #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable - fn const_check_violated(&self, expr: NonConstExpr, span: Span) { - let Self { tcx, def_id, const_kind } = *self; - - let features = tcx.features(); - let required_gates = expr.required_feature_gates(); - - let is_feature_allowed = |feature_gate| { - // All features require that the corresponding gate be enabled, - // even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`. - if !tcx.features().enabled(feature_gate) { - return false; - } - - // If `def_id` is `None`, we don't need to consider stability attributes. - let def_id = match def_id { - Some(x) => x, - None => return true, - }; - - // If the function belongs to a trait, then it must enable the const_trait_impl - // feature to use that trait function (with a const default body). - if tcx.trait_of_item(def_id.to_def_id()).is_some() { - return true; - } - - // If this crate is not using stability attributes, or this function is not claiming to be a - // stable `const fn`, that is all that is required. - if !tcx.features().staged_api() || tcx.has_attr(def_id, sym::rustc_const_unstable) { - return true; - } - - // However, we cannot allow stable `const fn`s to use unstable features without an explicit - // opt-in via `rustc_allow_const_fn_unstable`. - let attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(def_id)); - attr::rustc_allow_const_fn_unstable(tcx.sess, attrs).any(|name| name == feature_gate) - }; - - match required_gates { - // Don't emit an error if the user has enabled the requisite feature gates. - Some(gates) if gates.iter().copied().all(is_feature_allowed) => return, - - // `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a - // corresponding feature gate. This encourages nightly users to use feature gates when - // possible. - None if tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you => { - tcx.dcx().emit_warn(SkippingConstChecks { span }); - return; - } - - _ => {} - } - - let const_kind = - const_kind.expect("`const_check_violated` may only be called inside a const context"); - - let required_gates = required_gates.unwrap_or(&[]); - let missing_gates: Vec<_> = - required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect(); - - match missing_gates.as_slice() { - [] => { - span_bug!( - span, - "we should not have reached this point, since `.await` is denied earlier" - ); - } - - [missing_primary, ref missing_secondary @ ..] => { - let msg = - format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name()); - let mut err = feature_err(&tcx.sess, *missing_primary, span, msg); - - // If multiple feature gates would be required to enable this expression, include - // them as help messages. Don't emit a separate error for each missing feature gate. - // - // FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This - // is a pretty narrow case, however. - tcx.disabled_nightly_features( - &mut err, - def_id.map(|id| tcx.local_def_id_to_hir_id(id)), - missing_secondary.into_iter().map(|gate| (String::new(), *gate)), - ); - - err.emit(); - } - } - } - - /// Saves the parent `const_kind` before calling `f` and restores it afterwards. - fn recurse_into( - &mut self, - kind: Option<hir::ConstContext>, - def_id: Option<LocalDefId>, - f: impl FnOnce(&mut Self), - ) { - let parent_def_id = self.def_id; - let parent_kind = self.const_kind; - self.def_id = def_id; - self.const_kind = kind; - f(self); - self.def_id = parent_def_id; - self.const_kind = parent_kind; - } -} - -impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) { - let kind = Some(hir::ConstContext::Const { inline: false }); - self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon)); - } - - fn visit_inline_const(&mut self, block: &'tcx hir::ConstBlock) { - let kind = Some(hir::ConstContext::Const { inline: true }); - self.recurse_into(kind, None, |this| intravisit::walk_inline_const(this, block)); - } - - fn visit_body(&mut self, body: &hir::Body<'tcx>) { - let owner = self.tcx.hir().body_owner_def_id(body.id()); - let kind = self.tcx.hir().body_const_context(owner); - self.recurse_into(kind, Some(owner), |this| intravisit::walk_body(this, body)); - } - - fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { - match &e.kind { - // Skip the following checks if we are not currently in a const context. - _ if self.const_kind.is_none() => {} - - hir::ExprKind::Loop(_, _, source, _) => { - self.const_check_violated(NonConstExpr::Loop(*source), e.span); - } - - hir::ExprKind::Match(_, _, source) => { - let non_const_expr = match source { - // These are handled by `ExprKind::Loop` above. - hir::MatchSource::ForLoopDesugar => None, - - _ => Some(NonConstExpr::Match(*source)), - }; - - if let Some(expr) = non_const_expr { - self.const_check_violated(expr, e.span); - } - } - - _ => {} - } - - intravisit::walk_expr(self, e); - } -} diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index fbf25cb476a..fdc7e1bba2f 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1670,13 +1670,6 @@ pub(crate) struct ProcMacroBadSig { pub kind: ProcMacroKind, } -#[derive(Diagnostic)] -#[diag(passes_skipping_const_checks)] -pub(crate) struct SkippingConstChecks { - #[primary_span] - pub span: Span, -} - #[derive(LintDiagnostic)] #[diag(passes_unreachable_due_to_uninhabited)] pub(crate) struct UnreachableDueToUninhabited<'desc, 'tcx> { diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index db34189be2a..f2a37307cee 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -360,11 +360,10 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } fn visit_where_predicate(&mut self, p: &'v hir::WherePredicate<'v>) { - record_variants!((self, p, p, None, hir, WherePredicate, WherePredicate), [ - BoundPredicate, - RegionPredicate, - EqPredicate - ]); + record_variants!( + (self, p, p.kind, Some(p.hir_id), hir, WherePredicate, WherePredicateKind), + [BoundPredicate, RegionPredicate, EqPredicate] + ); hir_visit::walk_where_predicate(self, p) } @@ -556,6 +555,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Slice, Rest, Never, + Guard, Paren, MacCall, Err @@ -611,7 +611,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { } fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) { - record_variants!((self, p, p, None, ast, WherePredicate, WherePredicate), [ + record_variants!((self, p, &p.kind, None, ast, WherePredicate, WherePredicateKind), [ BoundPredicate, RegionPredicate, EqPredicate diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 8f53b71319d..1aa077ad2bb 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -19,7 +19,6 @@ use rustc_middle::query::Providers; pub mod abi_test; mod check_attr; -mod check_const; pub mod dead; mod debugger_visualizer; mod diagnostic_items; @@ -43,7 +42,6 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { check_attr::provide(providers); - check_const::provide(providers); dead::provide(providers); debugger_visualizer::provide(providers); diagnostic_items::provide(providers); diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 9cd95a0b02d..09cbb648f9b 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1007,7 +1007,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { hir::ExprKind::Array(exprs) => self.propagate_through_exprs(exprs, succ), hir::ExprKind::Struct(_, fields, ref with_expr) => { - let succ = self.propagate_through_opt_expr(with_expr.as_deref(), succ); + let succ = match with_expr { + hir::StructTailExpr::Base(base) => { + self.propagate_through_opt_expr(Some(base), succ) + } + hir::StructTailExpr::None | hir::StructTailExpr::DefaultFields(_) => succ, + }; fields .iter() .rev() diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index c845d60ac07..3057f13e3a7 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -947,6 +947,25 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { }); } } + + fn check_expanded_fields( + &mut self, + adt: ty::AdtDef<'tcx>, + variant: &'tcx ty::VariantDef, + fields: &[hir::ExprField<'tcx>], + hir_id: hir::HirId, + span: Span, + ) { + for (vf_index, variant_field) in variant.fields.iter_enumerated() { + let field = + fields.iter().find(|f| self.typeck_results().field_index(f.hir_id) == vf_index); + let (hir_id, use_ctxt, span) = match field { + Some(field) => (field.hir_id, field.ident.span, field.span), + None => (hir_id, span, span), + }; + self.check_field(hir_id, use_ctxt, span, adt, variant_field, true); + } + } } impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { @@ -966,25 +985,29 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { let res = self.typeck_results().qpath_res(qpath, expr.hir_id); let adt = self.typeck_results().expr_ty(expr).ty_adt_def().unwrap(); let variant = adt.variant_of_res(res); - if let Some(base) = *base { - // If the expression uses FRU we need to make sure all the unmentioned fields - // are checked for privacy (RFC 736). Rather than computing the set of - // unmentioned fields, just check them all. - for (vf_index, variant_field) in variant.fields.iter_enumerated() { - let field = fields - .iter() - .find(|f| self.typeck_results().field_index(f.hir_id) == vf_index); - let (hir_id, use_ctxt, span) = match field { - Some(field) => (field.hir_id, field.ident.span, field.span), - None => (base.hir_id, base.span, base.span), - }; - self.check_field(hir_id, use_ctxt, span, adt, variant_field, true); + match *base { + hir::StructTailExpr::Base(base) => { + // If the expression uses FRU we need to make sure all the unmentioned fields + // are checked for privacy (RFC 736). Rather than computing the set of + // unmentioned fields, just check them all. + self.check_expanded_fields(adt, variant, fields, base.hir_id, base.span); } - } else { - for field in fields { - let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span); - let index = self.typeck_results().field_index(field.hir_id); - self.check_field(hir_id, use_ctxt, span, adt, &variant.fields[index], false); + hir::StructTailExpr::DefaultFields(span) => { + self.check_expanded_fields(adt, variant, fields, expr.hir_id, span); + } + hir::StructTailExpr::None => { + for field in fields { + let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span); + let index = self.typeck_results().field_index(field.hir_id); + self.check_field( + hir_id, + use_ctxt, + span, + adt, + &variant.fields[index], + false, + ); + } } } } diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index df898e0587f..d2bb0b3f277 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -198,12 +198,12 @@ trait QueryConfigRestored<'tcx> { -> Self::RestoredValue; } -pub fn query_system<'tcx>( +pub fn query_system<'a>( local_providers: Providers, extern_providers: ExternProviders, - on_disk_cache: Option<OnDiskCache<'tcx>>, + on_disk_cache: Option<OnDiskCache>, incremental: bool, -) -> QuerySystem<'tcx> { +) -> QuerySystem<'a> { QuerySystem { states: Default::default(), arenas: Default::default(), @@ -222,3 +222,9 @@ pub fn query_system<'tcx>( } rustc_middle::rustc_query_append! { define_queries! } + +pub fn provide(providers: &mut rustc_middle::util::Providers) { + providers.hooks.alloc_self_profile_query_strings = + |tcx| alloc_self_profile_query_strings(tcx.tcx); + providers.hooks.query_key_hash_verify_all = |tcx| query_key_hash_verify_all(tcx.tcx); +} diff --git a/compiler/rustc_query_impl/src/profiling_support.rs b/compiler/rustc_query_impl/src/profiling_support.rs index 5f989b264aa..7d80e54bf87 100644 --- a/compiler/rustc_query_impl/src/profiling_support.rs +++ b/compiler/rustc_query_impl/src/profiling_support.rs @@ -252,6 +252,8 @@ pub fn alloc_self_profile_query_strings(tcx: TyCtxt<'_>) { return; } + let _prof_timer = tcx.sess.prof.generic_activity("self_profile_alloc_query_strings"); + let mut string_cache = QueryKeyStringCache::new(); for alloc in super::ALLOC_SELF_PROFILE_QUERY_STRINGS.iter() { diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 9b26a5bbcc6..4b47ce8389c 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -302,7 +302,11 @@ impl<D: Deps> DepGraph<D> { OP: FnOnce() -> R, { match self.data() { - Some(data) => data.with_anon_task(cx, dep_kind, op), + Some(data) => { + let (result, index) = data.with_anon_task_inner(cx, dep_kind, op); + self.read_index(index); + (result, index) + } None => (op(), self.next_virtual_depnode_index()), } } @@ -397,7 +401,16 @@ impl<D: Deps> DepGraphData<D> { /// Executes something within an "anonymous" task, that is, a task the /// `DepNode` of which is determined by the list of inputs it read from. - pub(crate) fn with_anon_task<Tcx: DepContext<Deps = D>, OP, R>( + /// + /// NOTE: this does not actually count as a read of the DepNode here. + /// Using the result of this task without reading the DepNode will result + /// in untracked dependencies which may lead to ICEs as nodes are + /// incorrectly marked green. + /// + /// FIXME: This could perhaps return a `WithDepNode` to ensure that the + /// user of this function actually performs the read; we'll have to see + /// how to make that work with `anon` in `execute_job_incr`, though. + pub(crate) fn with_anon_task_inner<Tcx: DepContext<Deps = D>, OP, R>( &self, cx: Tcx, dep_kind: DepKind, diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index aac8ab87c64..6fb5e37d2d0 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -520,9 +520,11 @@ where let (result, dep_node_index) = qcx.start_query(job_id, query.depth_limit(), Some(&diagnostics), || { if query.anon() { - return dep_graph_data.with_anon_task(*qcx.dep_context(), query.dep_kind(), || { - query.compute(qcx, key) - }); + return dep_graph_data.with_anon_task_inner( + *qcx.dep_context(), + query.dep_kind(), + || query.compute(qcx, key), + ); } // `to_dep_node` is expensive for some `DepKind`s. diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index bf27b767a49..66492842581 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -12,23 +12,16 @@ use rustc_span::hygiene::LocalExpnId; use rustc_span::symbol::{Symbol, kw, sym}; use tracing::debug; -use crate::{ImplTraitContext, InvocationParent, PendingAnonConstInfo, Resolver}; +use crate::{ImplTraitContext, InvocationParent, Resolver}; pub(crate) fn collect_definitions( resolver: &mut Resolver<'_, '_>, fragment: &AstFragment, expansion: LocalExpnId, ) { - let InvocationParent { parent_def, pending_anon_const_info, impl_trait_context, in_attr } = + let InvocationParent { parent_def, impl_trait_context, in_attr } = resolver.invocation_parents[&expansion]; - let mut visitor = DefCollector { - resolver, - parent_def, - pending_anon_const_info, - expansion, - impl_trait_context, - in_attr, - }; + let mut visitor = DefCollector { resolver, parent_def, expansion, impl_trait_context, in_attr }; fragment.visit_with(&mut visitor); } @@ -36,13 +29,6 @@ pub(crate) fn collect_definitions( struct DefCollector<'a, 'ra, 'tcx> { resolver: &'a mut Resolver<'ra, 'tcx>, parent_def: LocalDefId, - /// If we have an anon const that consists of a macro invocation, e.g. `Foo<{ m!() }>`, - /// we need to wait until we know what the macro expands to before we create the def for - /// the anon const. That's because we lower some anon consts into `hir::ConstArgKind::Path`, - /// which don't have defs. - /// - /// See `Self::visit_anon_const()`. - pending_anon_const_info: Option<PendingAnonConstInfo>, impl_trait_context: ImplTraitContext, in_attr: bool, expansion: LocalExpnId, @@ -110,61 +96,13 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { fn visit_macro_invoc(&mut self, id: NodeId) { let id = id.placeholder_to_expn_id(); - let pending_anon_const_info = self.pending_anon_const_info.take(); let old_parent = self.resolver.invocation_parents.insert(id, InvocationParent { parent_def: self.parent_def, - pending_anon_const_info, impl_trait_context: self.impl_trait_context, in_attr: self.in_attr, }); assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation"); } - - /// Determines whether the const argument `AnonConst` is a simple macro call, optionally - /// surrounded with braces. - /// - /// If this const argument *is* a trivial macro call then the id for the macro call is - /// returned along with the information required to build the anon const's def if - /// the macro call expands to a non-trivial expression. - fn is_const_arg_trivial_macro_expansion( - &self, - anon_const: &'a AnonConst, - ) -> Option<(PendingAnonConstInfo, NodeId)> { - anon_const.value.optionally_braced_mac_call(false).map(|(block_was_stripped, id)| { - ( - PendingAnonConstInfo { - id: anon_const.id, - span: anon_const.value.span, - block_was_stripped, - }, - id, - ) - }) - } - - /// Determines whether the expression `const_arg_sub_expr` is a simple macro call, sometimes - /// surrounded with braces if a set of braces has not already been entered. This is required - /// as `{ N }` is treated as equivalent to a bare parameter `N` whereas `{{ N }}` is treated as - /// a real block expression and is lowered to an anonymous constant which is not allowed to use - /// generic parameters. - /// - /// If this expression is a trivial macro call then the id for the macro call is - /// returned along with the information required to build the anon const's def if - /// the macro call expands to a non-trivial expression. - fn is_const_arg_sub_expr_trivial_macro_expansion( - &self, - const_arg_sub_expr: &'a Expr, - ) -> Option<(PendingAnonConstInfo, NodeId)> { - let pending_anon = self.pending_anon_const_info.unwrap_or_else(|| - panic!("Checking expr is trivial macro call without having entered anon const: `{const_arg_sub_expr:?}`"), - ); - - const_arg_sub_expr.optionally_braced_mac_call(pending_anon.block_was_stripped).map( - |(block_was_stripped, id)| { - (PendingAnonConstInfo { block_was_stripped, ..pending_anon }, id) - }, - ) - } } impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { @@ -191,7 +129,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { ItemKind::Const(..) => DefKind::Const, ItemKind::Fn(..) | ItemKind::Delegation(..) => DefKind::Fn, ItemKind::MacroDef(def) => { - let edition = self.resolver.tcx.sess.edition(); + let edition = i.span.edition(); let macro_data = self.resolver.compile_macro(def, i.ident, &i.attrs, i.span, i.id, edition); let macro_kind = macro_data.ext.macro_kind(); @@ -376,78 +314,34 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } fn visit_anon_const(&mut self, constant: &'a AnonConst) { - // HACK(min_generic_const_args): don't create defs for anon consts if we think they will - // later be turned into ConstArgKind::Path's. because this is before resolve is done, we - // may accidentally identify a construction of a unit struct as a param and not create a - // def. we'll then create a def later in ast lowering in this case. the parent of nested - // items will be messed up, but that's ok because there can't be any if we're just looking - // for bare idents. - - if let Some((pending_anon, macro_invoc)) = - self.is_const_arg_trivial_macro_expansion(constant) - { - self.pending_anon_const_info = Some(pending_anon); - return self.visit_macro_invoc(macro_invoc); - } else if constant.value.is_potential_trivial_const_arg(true) { - return visit::walk_anon_const(self, constant); - } - - let def = self.create_def(constant.id, kw::Empty, DefKind::AnonConst, constant.value.span); - self.with_parent(def, |this| visit::walk_anon_const(this, constant)); + let parent = + self.create_def(constant.id, kw::Empty, DefKind::AnonConst, constant.value.span); + self.with_parent(parent, |this| visit::walk_anon_const(this, constant)); } fn visit_expr(&mut self, expr: &'a Expr) { - // If we're visiting the expression of a const argument that was a macro call then - // check if it is *still* unknown whether it is a trivial const arg or not. If so - // recurse into the macro call and delay creating the anon const def until expansion. - if self.pending_anon_const_info.is_some() - && let Some((pending_anon, macro_invoc)) = - self.is_const_arg_sub_expr_trivial_macro_expansion(expr) - { - self.pending_anon_const_info = Some(pending_anon); - return self.visit_macro_invoc(macro_invoc); - } - - // See self.pending_anon_const_info for explanation - let parent_def = self - .pending_anon_const_info - .take() - // If we already stripped away a set of braces then do not do it again when determining - // if the macro expanded to a trivial const arg. This arises in cases such as: - // `Foo<{ bar!() }>` where `bar!()` expands to `{ N }`. This should not be considered a - // trivial const argument even though `{ N }` by itself *is*. - .filter(|pending_anon| { - !expr.is_potential_trivial_const_arg(!pending_anon.block_was_stripped) - }) - .map(|pending_anon| { - self.create_def(pending_anon.id, kw::Empty, DefKind::AnonConst, pending_anon.span) - }) - .unwrap_or(self.parent_def); - - self.with_parent(parent_def, |this| { - let parent_def = match expr.kind { - ExprKind::MacCall(..) => return this.visit_macro_invoc(expr.id), - ExprKind::Closure(..) | ExprKind::Gen(..) => { - this.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span) - } - ExprKind::ConstBlock(ref constant) => { - for attr in &expr.attrs { - visit::walk_attribute(this, attr); - } - let def = this.create_def( - constant.id, - kw::Empty, - DefKind::InlineConst, - constant.value.span, - ); - this.with_parent(def, |this| visit::walk_anon_const(this, constant)); - return; + let parent_def = match expr.kind { + ExprKind::MacCall(..) => return self.visit_macro_invoc(expr.id), + ExprKind::Closure(..) | ExprKind::Gen(..) => { + self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span) + } + ExprKind::ConstBlock(ref constant) => { + for attr in &expr.attrs { + visit::walk_attribute(self, attr); } - _ => this.parent_def, - }; + let def = self.create_def( + constant.id, + kw::Empty, + DefKind::InlineConst, + constant.value.span, + ); + self.with_parent(def, |this| visit::walk_anon_const(this, constant)); + return; + } + _ => self.parent_def, + }; - this.with_parent(parent_def, |this| visit::walk_expr(this, expr)) - }) + self.with_parent(parent_def, |this| visit::walk_expr(this, expr)) } fn visit_ty(&mut self, ty: &'a Ty) { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 235434326aa..789d74876f7 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -13,7 +13,9 @@ use std::collections::hash_map::Entry; use std::mem::{replace, swap, take}; use rustc_ast::ptr::P; -use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, visit_opt, walk_list}; +use rustc_ast::visit::{ + AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, try_visit, visit_opt, walk_list, +}; use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::codes::*; @@ -749,8 +751,8 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r self.resolve_block(block); self.parent_scope.macro_rules = old_macro_rules; } - fn visit_anon_const(&mut self, _constant: &'ast AnonConst) { - bug!("encountered anon const without a manual call to `resolve_anon_const`"); + fn visit_anon_const(&mut self, constant: &'ast AnonConst) { + bug!("encountered anon const without a manual call to `resolve_anon_const`: {constant:#?}"); } fn visit_expr(&mut self, expr: &'ast Expr) { self.resolve_expr(expr, None); @@ -1268,15 +1270,14 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r debug!("visit_where_predicate {:?}", p); let previous_value = replace(&mut self.diag_metadata.current_where_predicate, Some(p)); self.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { - if let WherePredicate::BoundPredicate(WhereBoundPredicate { - ref bounded_ty, - ref bounds, - ref bound_generic_params, - span: predicate_span, + if let WherePredicateKind::BoundPredicate(WhereBoundPredicate { + bounded_ty, + bounds, + bound_generic_params, .. - }) = p + }) = &p.kind { - let span = predicate_span.shrink_to_lo().to(bounded_ty.span.shrink_to_lo()); + let span = p.span.shrink_to_lo().to(bounded_ty.span.shrink_to_lo()); this.with_generic_param_rib( bound_generic_params, RibKind::Normal, @@ -1347,7 +1348,24 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r fn visit_field_def(&mut self, f: &'ast FieldDef) { self.resolve_doc_links(&f.attrs, MaybeExported::Ok(f.id)); - visit::walk_field_def(self, f) + let FieldDef { + attrs, + id: _, + span: _, + vis, + ident, + ty, + is_placeholder: _, + default, + safety: _, + } = f; + walk_list!(self, visit_attribute, attrs); + try_visit!(self.visit_vis(vis)); + visit_opt!(self, visit_ident, ident); + try_visit!(self.visit_ty(ty)); + if let Some(v) = &default { + self.resolve_anon_const(v, AnonConstKind::ConstArg(IsRepeatExpr::No)); + } } } @@ -4522,7 +4540,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ); self.resolve_anon_const_manual( - constant.value.is_potential_trivial_const_arg(true), + constant.value.is_potential_trivial_const_arg(), anon_const_kind, |this| this.resolve_expr(&constant.value, None), ) @@ -4686,7 +4704,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // that is how they will be later lowered to HIR. if const_args.contains(&idx) { self.resolve_anon_const_manual( - argument.is_potential_trivial_const_arg(true), + argument.is_potential_trivial_const_arg(), AnonConstKind::ConstArg(IsRepeatExpr::No), |this| this.resolve_expr(argument, None), ); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 4a157049964..09f3e848766 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1317,21 +1317,24 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { /// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`. fn restrict_assoc_type_in_where_clause(&mut self, span: Span, err: &mut Diag<'_>) -> bool { // Detect that we are actually in a `where` predicate. - let (bounded_ty, bounds, where_span) = - if let Some(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { - bounded_ty, - bound_generic_params, - bounds, - span, - })) = self.diag_metadata.current_where_predicate - { - if !bound_generic_params.is_empty() { - return false; - } - (bounded_ty, bounds, span) - } else { + let (bounded_ty, bounds, where_span) = if let Some(ast::WherePredicate { + kind: + ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate { + bounded_ty, + bound_generic_params, + bounds, + }), + span, + .. + }) = self.diag_metadata.current_where_predicate + { + if !bound_generic_params.is_empty() { return false; - }; + } + (bounded_ty, bounds, span) + } else { + return false; + }; // Confirm that the target is an associated type. let (ty, _, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind { @@ -2840,9 +2843,10 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // for<'a, 'b> T: Trait<T> + 'b // ^^^^^^^^^^^ suggest outer binder `for<'a, 'b>` if let LifetimeBinderKind::WhereBound = kind - && let Some(ast::WherePredicate::BoundPredicate( + && let Some(predicate) = self.diag_metadata.current_where_predicate + && let ast::WherePredicateKind::BoundPredicate( ast::WhereBoundPredicate { bounded_ty, bounds, .. }, - )) = self.diag_metadata.current_where_predicate + ) = &predicate.kind && bounded_ty.id == binder { for bound in bounds { @@ -3109,6 +3113,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } + #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] let existing_name = match &in_scope_lifetimes[..] { [] => Symbol::intern("'a"), [(existing, _)] => existing.name, @@ -3473,7 +3478,6 @@ fn mk_where_bound_predicate( }; let new_where_bound_predicate = ast::WhereBoundPredicate { - span: DUMMY_SP, bound_generic_params: ThinVec::new(), bounded_ty: ast::ptr::P(ty.clone()), bounds: vec![ast::GenericBound::Trait(ast::PolyTraitRef { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index e382295b8f6..ca4adce37ce 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -174,7 +174,6 @@ impl<'ra> ParentScope<'ra> { #[derive(Copy, Debug, Clone)] struct InvocationParent { parent_def: LocalDefId, - pending_anon_const_info: Option<PendingAnonConstInfo>, impl_trait_context: ImplTraitContext, in_attr: bool, } @@ -182,24 +181,12 @@ struct InvocationParent { impl InvocationParent { const ROOT: Self = Self { parent_def: CRATE_DEF_ID, - pending_anon_const_info: None, impl_trait_context: ImplTraitContext::Existential, in_attr: false, }; } #[derive(Copy, Debug, Clone)] -struct PendingAnonConstInfo { - // A const arg is only a "trivial" const arg if it has at *most* one set of braces - // around the argument. We track whether we have stripped an outter brace so that - // if a macro expands to a braced expression *and* the macro was itself inside of - // some braces then we can consider it to be a non-trivial const argument. - block_was_stripped: bool, - id: NodeId, - span: Span, -} - -#[derive(Copy, Debug, Clone)] enum ImplTraitContext { Existential, Universal, @@ -2270,7 +2257,7 @@ fn module_to_string(module: Module<'_>) -> Option<String> { collect_mod(names, parent); } } else { - names.push(Symbol::intern("<opaque>")); + names.push(sym::opaque_module_name_placeholder); collect_mod(names, module.parent.unwrap()); } } diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 02e255d7726..65128ceb866 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -220,9 +220,9 @@ pub fn attrs_to_doc_fragments<'a>( fn span_for_value(attr: &ast::Attribute) -> Span { if let ast::AttrKind::Normal(normal) = &attr.kind - && let ast::AttrArgs::Eq(_, ast::AttrArgsEq::Hir(meta)) = &normal.item.args + && let ast::AttrArgs::Eq { value, .. } = &normal.item.args { - meta.span.with_ctxt(attr.span.ctxt()) + value.span().with_ctxt(attr.span.ctxt()) } else { attr.span } diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 893c532f1fb..eb14b78a003 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -84,8 +84,6 @@ session_not_supported = not supported session_octal_float_literal_not_supported = octal float literal is not supported -session_optimization_fuel_exhausted = optimization-fuel-exhausted: {$msg} - session_profile_sample_use_file_does_not_exist = file `{$path}` passed to `-C profile-sample-use` does not exist session_profile_use_file_does_not_exist = file `{$path}` passed to `-C profile-use` does not exist @@ -137,5 +135,6 @@ session_unsupported_crate_type_for_target = session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is greater than 5 +session_unsupported_reg_struct_return_arch = `-Zreg-struct-return` is only supported on x86 session_unsupported_regparm = `-Zregparm={$regparm}` is unsupported (valid values 0-3) session_unsupported_regparm_arch = `-Zregparm=N` is only supported on x86 diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index d60c56fee75..cb6d539cdf9 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1208,7 +1208,7 @@ impl Options { /// Returns `true` if there will be an output file generated. pub fn will_create_output_file(&self) -> bool { - !self.unstable_opts.parse_only && // The file is just being parsed + !self.unstable_opts.parse_crate_root_only && // The file is just being parsed self.unstable_opts.ls.is_empty() // The file is just being queried } @@ -1864,7 +1864,7 @@ fn parse_output_types( matches: &getopts::Matches, ) -> OutputTypes { let mut output_types = BTreeMap::new(); - if !unstable_opts.parse_only { + if !unstable_opts.parse_crate_root_only { for list in matches.opt_strs("emit") { for output_type in list.split(',') { let (shorthand, path) = split_out_file_name(output_type); @@ -2356,14 +2356,6 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M early_dcx.early_warn(format!("number of threads was capped at {}", parse::MAX_THREADS_CAP)); } - let fuel = unstable_opts.fuel.is_some() || unstable_opts.print_fuel.is_some(); - if fuel && unstable_opts.threads > 1 { - early_dcx.early_fatal("optimization fuel is incompatible with multiple threads"); - } - if fuel && cg.incremental.is_some() { - early_dcx.early_fatal("optimization fuel is incompatible with incremental compilation"); - } - let incremental = cg.incremental.as_ref().map(PathBuf::from); let assert_incr_state = parse_assert_incr_state(early_dcx, &unstable_opts.assert_incr_state); diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 33f84f10447..6c26a781487 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -464,12 +464,6 @@ pub fn report_lit_error( } #[derive(Diagnostic)] -#[diag(session_optimization_fuel_exhausted)] -pub(crate) struct OptimisationFuelExhausted { - pub(crate) msg: String, -} - -#[derive(Diagnostic)] #[diag(session_incompatible_linker_flavor)] #[note] pub(crate) struct IncompatibleLinkerFlavor { @@ -496,6 +490,10 @@ pub(crate) struct UnsupportedRegparm { pub(crate) struct UnsupportedRegparmArch; #[derive(Diagnostic)] +#[diag(session_unsupported_reg_struct_return_arch)] +pub(crate) struct UnsupportedRegStructReturnArch; + +#[derive(Diagnostic)] #[diag(session_failed_to_create_profiler)] pub(crate) struct FailedToCreateProfiler { pub(crate) err: String, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index edee7b4468c..b01d9e5e8e3 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -394,7 +394,6 @@ mod desc { pub(crate) const parse_collapse_macro_debuginfo: &str = "one of `no`, `external`, or `yes`"; pub(crate) const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`"; pub(crate) const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of(); - pub(crate) const parse_optimization_fuel: &str = "crate=integer"; pub(crate) const parse_dump_mono_stats: &str = "`markdown` (default) or `json`"; pub(crate) const parse_instrument_coverage: &str = parse_bool; pub(crate) const parse_coverage_options: &str = @@ -948,21 +947,6 @@ pub mod parse { true } - pub(crate) fn parse_optimization_fuel( - slot: &mut Option<(String, u64)>, - v: Option<&str>, - ) -> bool { - match v { - None => false, - Some(s) => { - let [crate_name, fuel] = *s.split('=').collect::<Vec<_>>() else { return false }; - let Ok(fuel) = fuel.parse::<u64>() else { return false }; - *slot = Some((crate_name.to_string(), fuel)); - true - } - } - } - pub(crate) fn parse_unpretty(slot: &mut Option<String>, v: Option<&str>) -> bool { match v { None => false, @@ -1718,6 +1702,8 @@ options! { "threshold to allow cross crate inlining of functions"), debug_info_for_profiling: bool = (false, parse_bool, [TRACKED], "emit discriminators and other data necessary for AutoFDO"), + debug_info_type_line_numbers: bool = (false, parse_bool, [TRACKED], + "emit type and line information for additional data types (default: no)"), debuginfo_compression: DebugInfoCompression = (DebugInfoCompression::None, parse_debuginfo_compression, [TRACKED], "compress debug info sections (none, zlib, zstd, default: none)"), deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED], @@ -1794,8 +1780,6 @@ options! { `shallow` prints only type names, `none` prints nothing and disables `{:?}`. (default: `full`)"), force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED], "force all crates to be `rustc_private` unstable (default: no)"), - fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], - "set the optimization fuel quota for a crate"), function_return: FunctionReturn = (FunctionReturn::default(), parse_function_return, [TRACKED], "replace returns with jumps to `__x86_return_thunk` (default: `keep`)"), function_sections: Option<bool> = (None, parse_opt_bool, [TRACKED], @@ -1955,8 +1939,9 @@ options! { "support compiling tests with panic=abort (default: no)"), panic_in_drop: PanicStrategy = (PanicStrategy::Unwind, parse_panic_strategy, [TRACKED], "panic strategy for panics in drops"), - parse_only: bool = (false, parse_bool, [UNTRACKED], - "parse only; do not compile, assemble, or link (default: no)"), + parse_crate_root_only: bool = (false, parse_bool, [UNTRACKED], + "parse the crate root file only; do not parse other files, compile, assemble, or link \ + (default: no)"), patchable_function_entry: PatchableFunctionEntry = (PatchableFunctionEntry::default(), parse_patchable_function_entry, [TRACKED], "nop padding at function entry"), plt: Option<bool> = (None, parse_opt_bool, [TRACKED], @@ -1965,8 +1950,6 @@ options! { (default: PLT is disabled if full relro is enabled on x86_64)"), polonius: Polonius = (Polonius::default(), parse_polonius, [TRACKED], "enable polonius-based borrow-checker (default: no)"), - polymorphize: bool = (false, parse_bool, [TRACKED], - "perform polymorphization analysis"), pre_link_arg: (/* redirected to pre_link_args */) = ((), parse_string_push, [UNTRACKED], "a single extra argument to prepend the linker invocation (can be used several times)"), pre_link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED], @@ -1978,8 +1961,6 @@ options! { #[rustc_lint_opt_deny_field_access("use `Session::print_codegen_stats` instead of this field")] print_codegen_stats: bool = (false, parse_bool, [UNTRACKED], "print codegen statistics (default: no)"), - print_fuel: Option<String> = (None, parse_opt_string, [TRACKED], - "make rustc print the total optimization fuel used by a crate"), print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "print the LLVM optimization passes being run (default: no)"), print_mono_items: Option<String> = (None, parse_opt_string, [UNTRACKED], @@ -2005,6 +1986,9 @@ options! { "enable queries of the dependency graph for regression testing (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], "randomize the layout of types (default: no)"), + reg_struct_return: bool = (false, parse_bool, [TRACKED], + "On x86-32 targets, it overrides the default ABI to return small structs in registers. + It is UNSOUND to link together crates that use different values for this flag!"), regparm: Option<u32> = (None, parse_opt_number, [TRACKED], "On x86-32 targets, setting this to N causes the compiler to pass N arguments \ in registers EAX, EDX, and ECX instead of on the stack for\ @@ -2055,8 +2039,6 @@ written to standard error output)"), "make the current crate share its generic instantiations"), shell_argfiles: bool = (false, parse_bool, [UNTRACKED], "allow argument files to be specified with POSIX \"shell-style\" argument quoting"), - show_span: Option<String> = (None, parse_opt_string, [TRACKED], - "show spans for compiler debugging (expr|pat|ty)"), simulate_remapped_rust_src_base: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], "simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \ to rust's source base directory. only meant for testing purposes"), diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index 357d746c184..2b2ba50d3fb 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -87,7 +87,7 @@ pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute]) -> Symbol { } } - Symbol::intern("rust_out") + sym::rust_out } pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) { diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 29fabdd1deb..993d111466b 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -4,7 +4,6 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Arc; use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering::SeqCst; use std::{env, fmt, io}; use rustc_data_structures::flock; @@ -12,7 +11,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::jobserver::{self, Client}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; use rustc_data_structures::sync::{ - AtomicU64, DynSend, DynSync, Lock, Lrc, MappedReadGuard, ReadGuard, RwLock, + DynSend, DynSync, Lock, Lrc, MappedReadGuard, ReadGuard, RwLock, }; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; use rustc_errors::codes::*; @@ -20,7 +19,6 @@ use rustc_errors::emitter::{ DynEmitter, HumanEmitter, HumanReadableErrorType, OutputTheme, stderr_destination, }; use rustc_errors::json::JsonEmitter; -use rustc_errors::registry::Registry; use rustc_errors::{ Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort, FluentBundle, LazyFallbackBundle, TerminalUrl, fallback_fluent_bundle, @@ -49,13 +47,6 @@ use crate::parse::{ParseSess, add_feature_diagnostics}; use crate::search_paths::SearchPath; use crate::{errors, filesearch, lint}; -struct OptimizationFuel { - /// If `-zfuel=crate=n` is specified, initially set to `n`, otherwise `0`. - remaining: u64, - /// We're rejecting all further optimizations. - out_of_fuel: bool, -} - /// The behavior of the CTFE engine when an error occurs with regards to backtraces. #[derive(Clone, Copy)] pub enum CtfeBacktrace { @@ -163,12 +154,6 @@ pub struct Session { /// Data about code being compiled, gathered during compilation. pub code_stats: CodeStats, - /// Tracks fuel info if `-zfuel=crate=n` is specified. - optimization_fuel: Lock<OptimizationFuel>, - - /// Always set to zero and incremented so that we can print fuel expended by a crate. - pub print_fuel: AtomicU64, - /// Loaded up early on in the initialization of this `Session` to avoid /// false positives about a job server in our environment. pub jobserver: Client, @@ -290,11 +275,11 @@ impl Session { } /// Invoked all the way at the end to finish off diagnostics printing. - pub fn finish_diagnostics(&self, registry: &Registry) -> Option<ErrorGuaranteed> { + pub fn finish_diagnostics(&self) -> Option<ErrorGuaranteed> { let mut guar = None; guar = guar.or(self.check_miri_unleashed_features()); guar = guar.or(self.dcx().emit_stashed_diagnostics()); - self.dcx().print_error_count(registry); + self.dcx().print_error_count(); if self.opts.json_future_incompat { self.dcx().emit_future_breakage_report(); } @@ -532,41 +517,6 @@ impl Session { self.opts.incremental.as_ref().map(|_| self.incr_comp_session_dir()) } - /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. - /// This expends fuel if applicable, and records fuel if applicable. - pub fn consider_optimizing( - &self, - get_crate_name: impl Fn() -> Symbol, - msg: impl Fn() -> String, - ) -> bool { - let mut ret = true; - if let Some((ref c, _)) = self.opts.unstable_opts.fuel { - if c == get_crate_name().as_str() { - assert_eq!(self.threads(), 1); - let mut fuel = self.optimization_fuel.lock(); - ret = fuel.remaining != 0; - if fuel.remaining == 0 && !fuel.out_of_fuel { - if self.dcx().can_emit_warnings() { - // We only call `msg` in case we can actually emit warnings. - // Otherwise, this could cause a `must_produce_diag` ICE - // (issue #79546). - self.dcx().emit_warn(errors::OptimisationFuelExhausted { msg: msg() }); - } - fuel.out_of_fuel = true; - } else if fuel.remaining > 0 { - fuel.remaining -= 1; - } - } - } - if let Some(ref c) = self.opts.unstable_opts.print_fuel { - if c == get_crate_name().as_str() { - assert_eq!(self.threads(), 1); - self.print_fuel.fetch_add(1, SeqCst); - } - } - ret - } - /// Is this edition 2015? pub fn is_rust_2015(&self) -> bool { self.edition().is_rust_2015() @@ -929,7 +879,6 @@ impl Session { #[allow(rustc::bad_opt_access)] fn default_emitter( sopts: &config::Options, - registry: rustc_errors::registry::Registry, source_map: Lrc<SourceMap>, bundle: Option<Lrc<FluentBundle>>, fallback_bundle: LazyFallbackBundle, @@ -992,7 +941,6 @@ fn default_emitter( json_rendered, color_config, ) - .registry(Some(registry)) .fluent_bundle(bundle) .ui_testing(sopts.unstable_opts.ui_testing) .ignored_directories_in_source_blocks( @@ -1048,11 +996,11 @@ pub fn build_session( sopts.unstable_opts.translate_directionality_markers, ); let source_map = rustc_span::source_map::get_source_map().unwrap(); - let emitter = - default_emitter(&sopts, registry, Lrc::clone(&source_map), bundle, fallback_bundle); + let emitter = default_emitter(&sopts, Lrc::clone(&source_map), bundle, fallback_bundle); - let mut dcx = - DiagCtxt::new(emitter).with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings)); + let mut dcx = DiagCtxt::new(emitter) + .with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings)) + .with_registry(registry); if let Some(ice_file) = ice_file { dcx = dcx.with_ice_file(ice_file); } @@ -1097,12 +1045,6 @@ pub fn build_session( Lrc::new(SearchPath::from_sysroot_and_triple(&sysroot, target_triple)) }; - let optimization_fuel = Lock::new(OptimizationFuel { - remaining: sopts.unstable_opts.fuel.as_ref().map_or(0, |&(_, i)| i), - out_of_fuel: false, - }); - let print_fuel = AtomicU64::new(0); - let prof = SelfProfilerRef::new( self_profiler, sopts.unstable_opts.time_passes.then(|| sopts.unstable_opts.time_passes_format), @@ -1130,8 +1072,6 @@ pub fn build_session( incr_comp_session: RwLock::new(IncrCompSession::NotInitialized), prof, code_stats: Default::default(), - optimization_fuel, - print_fuel, jobserver: jobserver::client(), lint_store: None, registered_lints: false, @@ -1362,6 +1302,11 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::UnsupportedRegparmArch); } } + if sess.opts.unstable_opts.reg_struct_return { + if sess.target.arch != "x86" { + sess.dcx().emit_err(errors::UnsupportedRegStructReturnArch); + } + } // The code model check applies to `thunk` and `thunk-extern`, but not `thunk-inline`, so it is // kept as a `match` to force a change if new ones are added, even if we currently only support @@ -1415,6 +1360,12 @@ pub struct EarlyDiagCtxt { dcx: DiagCtxt, } +impl Default for EarlyDiagCtxt { + fn default() -> Self { + Self::new(ErrorOutputType::default()) + } +} + impl EarlyDiagCtxt { pub fn new(output: ErrorOutputType) -> Self { let emitter = mk_emitter(output); diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index a326e8583ea..614c9169d66 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -313,6 +313,7 @@ macro_rules! optional { macro_rules! run_driver { ($args:expr, $callback:expr $(, $with_tcx:ident)?) => {{ use rustc_driver::{Callbacks, Compilation, RunCompiler}; + use rustc_middle::ty::TyCtxt; use rustc_interface::{interface, Queries}; use stable_mir::CompilerError; use std::ops::ControlFlow; @@ -341,8 +342,9 @@ macro_rules! run_driver { /// Runs the compiler against given target and tests it with `test_function` pub fn run(&mut self) -> Result<C, CompilerError<B>> { - let compiler_result = rustc_driver::catch_fatal_errors(|| { - RunCompiler::new(&self.args.clone(), self).run() + let compiler_result = rustc_driver::catch_fatal_errors(|| -> interface::Result::<()> { + RunCompiler::new(&self.args.clone(), self).run(); + Ok(()) }); match (compiler_result, self.result.take()) { (Ok(Ok(())), Some(ControlFlow::Continue(value))) => Ok(value), @@ -373,23 +375,21 @@ macro_rules! run_driver { fn after_analysis<'tcx>( &mut self, _compiler: &interface::Compiler, - queries: &'tcx Queries<'tcx>, + tcx: TyCtxt<'tcx>, ) -> Compilation { - queries.global_ctxt().unwrap().enter(|tcx| { - if let Some(callback) = self.callback.take() { - rustc_internal::run(tcx, || { - self.result = Some(callback($(optional!($with_tcx tcx))?)); - }) - .unwrap(); - if self.result.as_ref().is_some_and(|val| val.is_continue()) { - Compilation::Continue - } else { - Compilation::Stop - } - } else { + if let Some(callback) = self.callback.take() { + rustc_internal::run(tcx, || { + self.result = Some(callback($(optional!($with_tcx tcx))?)); + }) + .unwrap(); + if self.result.as_ref().is_some_and(|val| val.is_continue()) { Compilation::Continue + } else { + Compilation::Stop } - }) + } else { + Compilation::Continue + } } } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index a9ca5dbe509..a5826137181 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -26,6 +26,7 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; +use std::collections::hash_set::Entry as SetEntry; use std::fmt; use std::hash::Hash; @@ -1270,7 +1271,7 @@ pub struct HygieneDecodeContext { inner: Lock<HygieneDecodeContextInner>, /// A set of serialized `SyntaxContext` ids that are currently being decoded on each thread. - local_in_progress: WorkerLocal<RefCell<FxHashMap<u32, ()>>>, + local_in_progress: WorkerLocal<RefCell<FxHashSet<u32>>>, } /// Register an expansion which has been decoded from the on-disk-cache for the local crate. @@ -1364,14 +1365,14 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext match inner.decoding.entry(raw_id) { Entry::Occupied(ctxt_entry) => { match context.local_in_progress.borrow_mut().entry(raw_id) { - Entry::Occupied(..) => { + SetEntry::Occupied(..) => { // We're decoding this already on the current thread. Return here // and let the function higher up the stack finish decoding to handle // recursive cases. return *ctxt_entry.get(); } - Entry::Vacant(entry) => { - entry.insert(()); + SetEntry::Vacant(entry) => { + entry.insert(); // Some other thread is current decoding this. Race with it. *ctxt_entry.get() @@ -1380,7 +1381,7 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext } Entry::Vacant(entry) => { // We are the first thread to start decoding. Mark the current thread as being progress. - context.local_in_progress.borrow_mut().insert(raw_id, ()); + context.local_in_progress.borrow_mut().insert(raw_id); // Allocate and store SyntaxContext id *before* calling the decoder function, // as the SyntaxContextData may reference itself. diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 1481e1cbd91..958d4f0c251 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -22,6 +22,7 @@ #![feature(array_windows)] #![feature(cfg_match)] #![feature(core_io_borrowed_buf)] +#![feature(hash_set_entry)] #![feature(if_let_guard)] #![feature(let_chains)] #![feature(min_specialization)] @@ -50,6 +51,7 @@ pub mod source_map; use source_map::{SourceMap, SourceMapInputs}; pub use self::caching_source_map_view::CachingSourceMapView; +use crate::fatal_error::FatalError; pub mod edition; use edition::Edition; @@ -2613,6 +2615,10 @@ impl ErrorGuaranteed { pub fn unchecked_error_guaranteed() -> Self { ErrorGuaranteed(()) } + + pub fn raise_fatal(self) -> ! { + FatalError.raise() + } } impl<E: rustc_serialize::Encoder> Encodable<E> for ErrorGuaranteed { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 46e245fb71f..d30b17c9cd8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -182,6 +182,7 @@ symbols! { ConstParamTy_, Context, Continue, + ControlFlow, Copy, Cow, Debug, @@ -297,6 +298,7 @@ symbols! { Return, Right, Rust, + RustaceansAreAwesome, RustcDecodable, RustcEncodable, RwLock, @@ -315,6 +317,7 @@ symbols! { StructuralPartialEq, SubdiagMessage, Subdiagnostic, + SymbolIntern, Sync, SyncUnsafeCell, T, @@ -376,6 +379,7 @@ symbols! { adt_const_params, advanced_slice_patterns, adx_target_feature, + aes, aggregate_raw_ptr, alias, align, @@ -392,6 +396,7 @@ symbols! { allow_fail, allow_internal_unsafe, allow_internal_unstable, + altivec, alu32, always, and, @@ -438,6 +443,7 @@ symbols! { associated_types, assume, assume_init, + asterisk: "*", async_await, async_call, async_call_mut, @@ -466,6 +472,7 @@ symbols! { async_for_loop, async_iterator, async_iterator_poll_next, + async_trait_bounds, atomic, atomic_mod, atomics, @@ -518,6 +525,7 @@ symbols! { btreeset_iter, builtin_syntax, c, + c_dash_variadic, c_str, c_str_literals, c_unwind, @@ -648,6 +656,7 @@ symbols! { const_trait_bound_opt_out, const_trait_impl, const_try, + const_ty_placeholder: "<const_ty>", constant, constructor, convert_identity, @@ -719,6 +728,7 @@ symbols! { declare_lint_pass, decode, default_alloc_error_handler, + default_field_values, default_fn, default_lib_allocator, default_method_body_is_const, @@ -777,6 +787,7 @@ symbols! { drop_types_in_const, dropck_eyepatch, dropck_parametricity, + dummy_cgu_name, dylib, dyn_compatible_for_dispatch, dyn_metadata, @@ -920,6 +931,7 @@ symbols! { fmuladdf32, fmuladdf64, fn_align, + fn_body, fn_delegation, fn_must_use, fn_mut, @@ -960,6 +972,7 @@ symbols! { fs_create_dir, fsub_algebraic, fsub_fast, + fsxr, full, fundamental, fused_iterator, @@ -987,6 +1000,7 @@ symbols! { global_registration, globs, gt, + guard_patterns, half_open_range_patterns, half_open_range_patterns_in_slices, hash, @@ -1383,6 +1397,7 @@ symbols! { on, on_unimplemented, opaque, + opaque_module_name_placeholder: "<opaque>", open_options_new, ops, opt_out_copy, @@ -1611,6 +1626,7 @@ symbols! { repr_simd, repr_transparent, require, + reserve_x18: "reserve-x18", residual, result, result_ffi_guarantees, @@ -1651,6 +1667,7 @@ symbols! { rust_eh_catch_typeinfo, rust_eh_personality, rust_logo, + rust_out, rustc, rustc_abi, rustc_allocator, @@ -1728,11 +1745,9 @@ symbols! { rustc_partition_reused, rustc_pass_by_value, rustc_peek, - rustc_peek_definite_init, rustc_peek_liveness, rustc_peek_maybe_init, rustc_peek_maybe_uninit, - rustc_polymorphize_error, rustc_preserve_ub_checks, rustc_private, rustc_proc_macro_decls, @@ -1774,6 +1789,8 @@ symbols! { self_in_typedefs, self_struct_ctor, semitransparent, + sha2, + sha3, sha512_sm_x86, shadow_call_stack, shallow, @@ -1842,6 +1859,7 @@ symbols! { simd_reduce_mul_unordered, simd_reduce_or, simd_reduce_xor, + simd_relaxed_fma, simd_rem, simd_round, simd_saturating_add, @@ -1887,6 +1905,7 @@ symbols! { sreg, sreg_low16, sse, + sse2, sse4a_target_feature, stable, staged_api, @@ -2154,6 +2173,7 @@ symbols! { volatile_store, vreg, vreg_low16, + vsx, vtable_align, vtable_size, warn, @@ -2173,6 +2193,7 @@ symbols! { wrapping_sub, wreg, write_bytes, + write_fmt, write_macro, write_str, write_via_move, @@ -2402,6 +2423,7 @@ impl Symbol { } /// Maps a string to its interned representation. + #[rustc_diagnostic_item = "SymbolIntern"] pub fn intern(string: &str) -> Self { with_session_globals(|session_globals| session_globals.symbol_interner.intern(string)) } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index dcbc5f0f76d..1a10ab8829c 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -256,7 +256,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { }); // Encode impl generic params if the generic parameters contain non-region parameters - // (implying polymorphization is enabled) and this isn't an inherent impl. + // and this isn't an inherent impl. if impl_trait_ref.is_some() && args.iter().any(|a| a.has_non_region_param()) { self.path_generic_args( |this| { @@ -330,9 +330,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { ty::Float(FloatTy::F128) => "C4f128", ty::Never => "z", - // Should only be encountered with polymorphization, - // or within the identity-substituted impl header of an - // item nested within an impl item. + // Should only be encountered within the identity-substituted + // impl header of an item nested within an impl item. ty::Param(_) => "p", ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => bug!(), @@ -558,9 +557,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { let (ct_ty, valtree) = match ct.kind() { ty::ConstKind::Value(ty, val) => (ty, val), - // Should only be encountered with polymorphization, - // or within the identity-substituted impl header of an - // item nested within an impl item. + // Should only be encountered within the identity-substituted + // impl header of an item nested within an impl item. ty::ConstKind::Param(_) => { // Never cached (single-character). self.push("p"); diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index b82d327a409..cdccb3e5d72 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -1,7 +1,7 @@ use std::fmt; use rustc_data_structures::fx::FxIndexSet; -use rustc_span::Symbol; +use rustc_span::{Symbol, sym}; use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; @@ -71,18 +71,26 @@ impl AArch64InlineAsmRegClass { } } -pub(crate) fn target_reserves_x18(target: &Target) -> bool { - target.os == "android" || target.os == "fuchsia" || target.is_like_osx || target.is_like_windows +pub(crate) fn target_reserves_x18(target: &Target, target_features: &FxIndexSet<Symbol>) -> bool { + // See isX18ReservedByDefault in LLVM for targets reserve x18 by default: + // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/TargetParser/AArch64TargetParser.cpp#L102-L105 + // Note that +reserve-x18 is currently not set for the above targets. + target.os == "android" + || target.os == "fuchsia" + || target.env == "ohos" + || target.is_like_osx + || target.is_like_windows + || target_features.contains(&sym::reserve_x18) } fn reserved_x18( _arch: InlineAsmArch, _reloc_model: RelocModel, - _target_features: &FxIndexSet<Symbol>, + target_features: &FxIndexSet<Symbol>, target: &Target, _is_clobber: bool, ) -> Result<(), &'static str> { - if target_reserves_x18(target) { + if target_reserves_x18(target, target_features) { Err("x18 is a reserved register on this target") } else { Ok(()) diff --git a/compiler/rustc_target/src/asm/avr.rs b/compiler/rustc_target/src/asm/avr.rs index 276f376b297..55d393c81d3 100644 --- a/compiler/rustc_target/src/asm/avr.rs +++ b/compiler/rustc_target/src/asm/avr.rs @@ -105,7 +105,12 @@ def_regs! { #error = ["SP", "SPL", "SPH"] => "the stack pointer cannot be used as an operand for inline asm", #error = ["r0", "r1", "r1r0"] => - "r0 and r1 are not available due to an issue in LLVM", + "LLVM reserves r0 (scratch register) and r1 (zero register)", + // If this changes within LLVM, the compiler might use the registers + // in the future. This must be reflected in the set of clobbered + // registers, else the clobber ABI implementation is *unsound*, as + // this generates invalid code (register is not marked as clobbered + // but may change the register content). } } diff --git a/compiler/rustc_target/src/asm/hexagon.rs b/compiler/rustc_target/src/asm/hexagon.rs index f7e726c3376..aa14ca3f337 100644 --- a/compiler/rustc_target/src/asm/hexagon.rs +++ b/compiler/rustc_target/src/asm/hexagon.rs @@ -7,6 +7,7 @@ use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; def_reg_class! { Hexagon HexagonInlineAsmRegClass { reg, + preg, } } @@ -37,6 +38,7 @@ impl HexagonInlineAsmRegClass { ) -> &'static [(InlineAsmType, Option<Symbol>)] { match self { Self::reg => types! { _: I8, I16, I32, F32; }, + Self::preg => &[], } } } @@ -71,6 +73,10 @@ def_regs! { r26: reg = ["r26"], r27: reg = ["r27"], r28: reg = ["r28"], + p0: preg = ["p0"], + p1: preg = ["p1"], + p2: preg = ["p2"], + p3: preg = ["p3"], #error = ["r19"] => "r19 is used internally by LLVM and cannot be used as an operand for inline asm", #error = ["r29", "sp"] => diff --git a/compiler/rustc_target/src/asm/loongarch.rs b/compiler/rustc_target/src/asm/loongarch.rs index b1c01d27cad..b4ea6fc592a 100644 --- a/compiler/rustc_target/src/asm/loongarch.rs +++ b/compiler/rustc_target/src/asm/loongarch.rs @@ -38,7 +38,7 @@ impl LoongArchInlineAsmRegClass { ) -> &'static [(InlineAsmType, Option<Symbol>)] { match self { Self::reg => types! { _: I8, I16, I32, I64, F32, F64; }, - Self::freg => types! { _: F32, F64; }, + Self::freg => types! { f: F32; d: F64; }, } } } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index db8d23776e5..204d5d53361 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -928,7 +928,9 @@ pub enum InlineAsmClobberAbi { AArch64, AArch64NoX18, Arm64EC, + Avr, RiscV, + RiscVE, LoongArch, PowerPC, S390x, @@ -941,6 +943,7 @@ impl InlineAsmClobberAbi { pub fn parse( arch: InlineAsmArch, target: &Target, + target_features: &FxIndexSet<Symbol>, name: Symbol, ) -> Result<Self, &'static [&'static str]> { let name = name.as_str(); @@ -963,11 +966,13 @@ impl InlineAsmClobberAbi { _ => Err(&["C", "system", "efiapi", "aapcs"]), }, InlineAsmArch::AArch64 => match name { - "C" | "system" | "efiapi" => Ok(if aarch64::target_reserves_x18(target) { - InlineAsmClobberAbi::AArch64NoX18 - } else { - InlineAsmClobberAbi::AArch64 - }), + "C" | "system" | "efiapi" => { + Ok(if aarch64::target_reserves_x18(target, target_features) { + InlineAsmClobberAbi::AArch64NoX18 + } else { + InlineAsmClobberAbi::AArch64 + }) + } _ => Err(&["C", "system", "efiapi"]), }, InlineAsmArch::Arm64EC => match name { @@ -975,9 +980,17 @@ impl InlineAsmClobberAbi { _ => Err(&["C", "system"]), }, InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name { - "C" | "system" | "efiapi" => Ok(InlineAsmClobberAbi::RiscV), + "C" | "system" | "efiapi" => Ok(if riscv::is_e(target_features) { + InlineAsmClobberAbi::RiscVE + } else { + InlineAsmClobberAbi::RiscV + }), _ => Err(&["C", "system", "efiapi"]), }, + InlineAsmArch::Avr => match name { + "C" | "system" => Ok(InlineAsmClobberAbi::Avr), + _ => Err(&["C", "system"]), + }, InlineAsmArch::LoongArch64 => match name { "C" | "system" => Ok(InlineAsmClobberAbi::LoongArch), _ => Err(&["C", "system"]), @@ -1125,6 +1138,23 @@ impl InlineAsmClobberAbi { d24, d25, d26, d27, d28, d29, d30, d31, } }, + InlineAsmClobberAbi::Avr => clobbered_regs! { + Avr AvrInlineAsmReg { + // The list of "Call-Used Registers" according to + // https://gcc.gnu.org/wiki/avr-gcc#Call-Used_Registers + + // Clobbered registers available in inline assembly + r18, r19, r20, r21, r22, r23, r24, r25, r26, r27, r30, r31, + // As per the AVR-GCC-ABI documentation linked above, the R0 + // register is a clobbered register as well. Since we don't + // allow the usage of R0 in inline assembly, nothing has to + // be done here. + // Likewise, the T-flag in the SREG should be clobbered, but + // this is not necessary to be listed here, since the SREG + // is considered clobbered anyways unless `preserve_flags` + // is used. + } + }, InlineAsmClobberAbi::RiscV => clobbered_regs! { RiscV RiscVInlineAsmReg { // ra @@ -1148,6 +1178,31 @@ impl InlineAsmClobberAbi { v24, v25, v26, v27, v28, v29, v30, v31, } }, + InlineAsmClobberAbi::RiscVE => clobbered_regs! { + RiscV RiscVInlineAsmReg { + // Refs: + // - ILP32E https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/draft-20240829-13bfa9f54634cb60d86b9b333e109f077805b4b3/riscv-cc.adoc#ilp32e-calling-convention + // - LP64E https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/299 + + // ra + x1, + // t0-t2 + x5, x6, x7, + // a0-a5 + x10, x11, x12, x13, x14, x15, + // ft0-ft7 + f0, f1, f2, f3, f4, f5, f6, f7, + // fa0-fa7 + f10, f11, f12, f13, f14, f15, f16, f17, + // ft8-ft11 + f28, f29, f30, f31, + + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, + } + }, InlineAsmClobberAbi::LoongArch => clobbered_regs! { LoongArch LoongArchInlineAsmReg { // ra diff --git a/compiler/rustc_target/src/asm/powerpc.rs b/compiler/rustc_target/src/asm/powerpc.rs index aa8b26170be..f3934afa6d9 100644 --- a/compiler/rustc_target/src/asm/powerpc.rs +++ b/compiler/rustc_target/src/asm/powerpc.rs @@ -51,7 +51,11 @@ impl PowerPCInlineAsmRegClass { } } Self::freg => types! { _: F32, F64; }, - Self::vreg => &[], + // FIXME: vsx also supports integers?: https://github.com/rust-lang/rust/pull/131551#discussion_r1862535963 + Self::vreg => types! { + altivec: VecI8(16), VecI16(8), VecI32(4), VecF32(4); + vsx: F32, F64, VecI64(2), VecF64(2); + }, Self::cr | Self::xer => &[], } } diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs index cfd573efbc1..d6b30525379 100644 --- a/compiler/rustc_target/src/asm/riscv.rs +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -54,6 +54,10 @@ impl RiscVInlineAsmRegClass { } } +pub(crate) fn is_e(target_features: &FxIndexSet<Symbol>) -> bool { + target_features.contains(&sym::e) +} + fn not_e( _arch: InlineAsmArch, _reloc_model: RelocModel, @@ -61,7 +65,7 @@ fn not_e( _target: &Target, _is_clobber: bool, ) -> Result<(), &'static str> { - if target_features.contains(&sym::e) { + if is_e(target_features) { Err("register can't be used with the `e` target feature") } else { Ok(()) diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index fb0fe402934..746e8173807 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -661,7 +661,9 @@ impl<'a, Ty> FnAbi<'a, Ty> { } _ => (x86::Flavor::General, None), }; - x86::compute_abi_info(cx, self, x86::X86Options { flavor, regparm }); + let reg_struct_return = cx.x86_abi_opt().reg_struct_return; + let opts = x86::X86Options { flavor, regparm, reg_struct_return }; + x86::compute_abi_info(cx, self, opts); } "x86_64" => match abi { spec::abi::Abi::SysV64 { .. } => x86_64::compute_abi_info(cx, self), diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index a5af975d4d2..cd8465c09ca 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -14,6 +14,7 @@ pub(crate) enum Flavor { pub(crate) struct X86Options { pub flavor: Flavor, pub regparm: Option<u32>, + pub reg_struct_return: bool, } pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options) @@ -31,7 +32,7 @@ where // https://www.angelcode.com/dev/callconv/callconv.html // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp let t = cx.target_spec(); - if t.abi_return_struct_as_int { + if t.abi_return_struct_as_int || opts.reg_struct_return { // According to Clang, everyone but MSVC returns single-element // float aggregates directly in a floating-point register. if !t.is_like_msvc && fn_abi.ret.layout.is_single_fp_element(cx) { diff --git a/compiler/rustc_target/src/spec/base/aix.rs b/compiler/rustc_target/src/spec/base/aix.rs index 1869369b9e3..fe37d313294 100644 --- a/compiler/rustc_target/src/spec/base/aix.rs +++ b/compiler/rustc_target/src/spec/base/aix.rs @@ -4,7 +4,7 @@ use crate::spec::{Cc, CodeModel, LinkOutputKind, LinkerFlavor, TargetOptions, cr pub(crate) fn opts() -> TargetOptions { TargetOptions { abi: "vec-extabi".into(), - code_model: Some(CodeModel::Small), + code_model: Some(CodeModel::Large), cpu: "pwr7".into(), os: "aix".into(), vendor: "ibm".into(), diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs new file mode 100644 index 00000000000..206766325aa --- /dev/null +++ b/compiler/rustc_target/src/spec/json.rs @@ -0,0 +1,798 @@ +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::str::FromStr; + +use serde_json::Value; + +use super::{Target, TargetKind, TargetOptions, TargetWarnings}; +use crate::json::{Json, ToJson}; + +impl Target { + /// Loads a target descriptor from a JSON object. + pub fn from_json(obj: Json) -> Result<(Target, TargetWarnings), String> { + // While ugly, this code must remain this way to retain + // compatibility with existing JSON fields and the internal + // expected naming of the Target and TargetOptions structs. + // To ensure compatibility is retained, the built-in targets + // are round-tripped through this code to catch cases where + // the JSON parser is not updated to match the structs. + + let mut obj = match obj { + Value::Object(obj) => obj, + _ => return Err("Expected JSON object for target")?, + }; + + let mut get_req_field = |name: &str| { + obj.remove(name) + .and_then(|j| j.as_str().map(str::to_string)) + .ok_or_else(|| format!("Field {name} in target specification is required")) + }; + + let mut base = Target { + llvm_target: get_req_field("llvm-target")?.into(), + metadata: Default::default(), + pointer_width: get_req_field("target-pointer-width")? + .parse::<u32>() + .map_err(|_| "target-pointer-width must be an integer".to_string())?, + data_layout: get_req_field("data-layout")?.into(), + arch: get_req_field("arch")?.into(), + options: Default::default(), + }; + + // FIXME: This doesn't properly validate anything and just ignores the data if it's invalid. + // That's okay for now, the only use of this is when generating docs, which we don't do for + // custom targets. + if let Some(Json::Object(mut metadata)) = obj.remove("metadata") { + base.metadata.description = metadata + .remove("description") + .and_then(|desc| desc.as_str().map(|desc| desc.to_owned().into())); + base.metadata.tier = metadata + .remove("tier") + .and_then(|tier| tier.as_u64()) + .filter(|tier| (1..=3).contains(tier)); + base.metadata.host_tools = + metadata.remove("host_tools").and_then(|host| host.as_bool()); + base.metadata.std = metadata.remove("std").and_then(|host| host.as_bool()); + } + + let mut incorrect_type = vec![]; + + macro_rules! key { + ($key_name:ident) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(s) = obj.remove(&name).and_then(|s| s.as_str().map(str::to_string).map(Cow::from)) { + base.$key_name = s; + } + } ); + ($key_name:ident = $json_name:expr) => ( { + let name = $json_name; + if let Some(s) = obj.remove(name).and_then(|s| s.as_str().map(str::to_string).map(Cow::from)) { + base.$key_name = s; + } + } ); + ($key_name:ident, bool) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(s) = obj.remove(&name).and_then(|b| b.as_bool()) { + base.$key_name = s; + } + } ); + ($key_name:ident = $json_name:expr, bool) => ( { + let name = $json_name; + if let Some(s) = obj.remove(name).and_then(|b| b.as_bool()) { + base.$key_name = s; + } + } ); + ($key_name:ident, u32) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(s) = obj.remove(&name).and_then(|b| b.as_u64()) { + if s < 1 || s > 5 { + return Err("Not a valid DWARF version number".into()); + } + base.$key_name = s as u32; + } + } ); + ($key_name:ident, Option<bool>) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(s) = obj.remove(&name).and_then(|b| b.as_bool()) { + base.$key_name = Some(s); + } + } ); + ($key_name:ident, Option<u64>) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(s) = obj.remove(&name).and_then(|b| b.as_u64()) { + base.$key_name = Some(s); + } + } ); + ($key_name:ident, MergeFunctions) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::<super::MergeFunctions>() { + Ok(mergefunc) => base.$key_name = mergefunc, + _ => return Some(Err(format!("'{}' is not a valid value for \ + merge-functions. Use 'disabled', \ + 'trampolines', or 'aliases'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, RelocModel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::<super::RelocModel>() { + Ok(relocation_model) => base.$key_name = relocation_model, + _ => return Some(Err(format!("'{}' is not a valid relocation model. \ + Run `rustc --print relocation-models` to \ + see the list of supported values.", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, CodeModel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::<super::CodeModel>() { + Ok(code_model) => base.$key_name = Some(code_model), + _ => return Some(Err(format!("'{}' is not a valid code model. \ + Run `rustc --print code-models` to \ + see the list of supported values.", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, TlsModel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::<super::TlsModel>() { + Ok(tls_model) => base.$key_name = tls_model, + _ => return Some(Err(format!("'{}' is not a valid TLS model. \ + Run `rustc --print tls-models` to \ + see the list of supported values.", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, SmallDataThresholdSupport) => ( { + obj.remove("small-data-threshold-support").and_then(|o| o.as_str().and_then(|s| { + match s.parse::<super::SmallDataThresholdSupport>() { + Ok(support) => base.small_data_threshold_support = support, + _ => return Some(Err(format!("'{s}' is not a valid value for small-data-threshold-support."))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, PanicStrategy) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s { + "unwind" => base.$key_name = super::PanicStrategy::Unwind, + "abort" => base.$key_name = super::PanicStrategy::Abort, + _ => return Some(Err(format!("'{}' is not a valid value for \ + panic-strategy. Use 'unwind' or 'abort'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, RelroLevel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::<super::RelroLevel>() { + Ok(level) => base.$key_name = level, + _ => return Some(Err(format!("'{}' is not a valid value for \ + relro-level. Use 'full', 'partial, or 'off'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, Option<SymbolVisibility>) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::<super::SymbolVisibility>() { + Ok(level) => base.$key_name = Some(level), + _ => return Some(Err(format!("'{}' is not a valid value for \ + symbol-visibility. Use 'hidden', 'protected, or 'interposable'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, DebuginfoKind) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::<super::DebuginfoKind>() { + Ok(level) => base.$key_name = level, + _ => return Some(Err( + format!("'{s}' is not a valid value for debuginfo-kind. Use 'dwarf', \ + 'dwarf-dsym' or 'pdb'.") + )), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, SplitDebuginfo) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::<super::SplitDebuginfo>() { + Ok(level) => base.$key_name = level, + _ => return Some(Err(format!("'{}' is not a valid value for \ + split-debuginfo. Use 'off' or 'dsymutil'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, list) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(j) = obj.remove(&name) { + if let Some(v) = j.as_array() { + base.$key_name = v.iter() + .map(|a| a.as_str().unwrap().to_string().into()) + .collect(); + } else { + incorrect_type.push(name) + } + } + } ); + ($key_name:ident, opt_list) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(j) = obj.remove(&name) { + if let Some(v) = j.as_array() { + base.$key_name = Some(v.iter() + .map(|a| a.as_str().unwrap().to_string().into()) + .collect()); + } else { + incorrect_type.push(name) + } + } + } ); + ($key_name:ident, fallible_list) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|j| { + if let Some(v) = j.as_array() { + match v.iter().map(|a| FromStr::from_str(a.as_str().unwrap())).collect() { + Ok(l) => { base.$key_name = l }, + // FIXME: `fallible_list` can't re-use the `key!` macro for list + // elements and the error messages from that macro, so it has a bad + // generic message instead + Err(_) => return Some(Err( + format!("`{:?}` is not a valid value for `{}`", j, name) + )), + } + } else { + incorrect_type.push(name) + } + Some(Ok(())) + }).unwrap_or(Ok(())) + } ); + ($key_name:ident, optional) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(o) = obj.remove(&name) { + base.$key_name = o + .as_str() + .map(|s| s.to_string().into()); + } + } ); + ($key_name:ident = $json_name:expr, LldFlavor) => ( { + let name = $json_name; + obj.remove(name).and_then(|o| o.as_str().and_then(|s| { + if let Some(flavor) = super::LldFlavor::from_str(&s) { + base.$key_name = flavor; + } else { + return Some(Err(format!( + "'{}' is not a valid value for lld-flavor. \ + Use 'darwin', 'gnu', 'link' or 'wasm'.", + s))) + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident = $json_name:expr, LinkerFlavorCli) => ( { + let name = $json_name; + obj.remove(name).and_then(|o| o.as_str().and_then(|s| { + match super::LinkerFlavorCli::from_str(s) { + Some(linker_flavor) => base.$key_name = linker_flavor, + _ => return Some(Err(format!("'{}' is not a valid value for linker-flavor. \ + Use {}", s, super::LinkerFlavorCli::one_of()))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, StackProbeType) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| match super::StackProbeType::from_json(&o) { + Ok(v) => { + base.$key_name = v; + Some(Ok(())) + }, + Err(s) => Some(Err( + format!("`{:?}` is not a valid value for `{}`: {}", o, name, s) + )), + }).unwrap_or(Ok(())) + } ); + ($key_name:ident, SanitizerSet) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(o) = obj.remove(&name) { + if let Some(a) = o.as_array() { + for s in a { + use super::SanitizerSet; + base.$key_name |= match s.as_str() { + Some("address") => SanitizerSet::ADDRESS, + Some("cfi") => SanitizerSet::CFI, + Some("dataflow") => SanitizerSet::DATAFLOW, + Some("kcfi") => SanitizerSet::KCFI, + Some("kernel-address") => SanitizerSet::KERNELADDRESS, + Some("leak") => SanitizerSet::LEAK, + Some("memory") => SanitizerSet::MEMORY, + Some("memtag") => SanitizerSet::MEMTAG, + Some("safestack") => SanitizerSet::SAFESTACK, + Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK, + Some("thread") => SanitizerSet::THREAD, + Some("hwaddress") => SanitizerSet::HWADDRESS, + Some(s) => return Err(format!("unknown sanitizer {}", s)), + _ => return Err(format!("not a string: {:?}", s)), + }; + } + } else { + incorrect_type.push(name) + } + } + Ok::<(), String>(()) + } ); + ($key_name:ident, link_self_contained_components) => ( { + // Skeleton of what needs to be parsed: + // + // ``` + // $name: { + // "components": [ + // <array of strings> + // ] + // } + // ``` + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(o) = obj.remove(&name) { + if let Some(o) = o.as_object() { + let component_array = o.get("components") + .ok_or_else(|| format!("{name}: expected a \ + JSON object with a `components` field."))?; + let component_array = component_array.as_array() + .ok_or_else(|| format!("{name}.components: expected a JSON array"))?; + let mut components = super::LinkSelfContainedComponents::empty(); + for s in component_array { + components |= match s.as_str() { + Some(s) => { + super::LinkSelfContainedComponents::from_str(s) + .ok_or_else(|| format!("unknown \ + `-Clink-self-contained` component: {s}"))? + }, + _ => return Err(format!("not a string: {:?}", s)), + }; + } + base.$key_name = super::LinkSelfContainedDefault::WithComponents(components); + } else { + incorrect_type.push(name) + } + } + Ok::<(), String>(()) + } ); + ($key_name:ident = $json_name:expr, link_self_contained_backwards_compatible) => ( { + let name = $json_name; + obj.remove(name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::<super::LinkSelfContainedDefault>() { + Ok(lsc_default) => base.$key_name = lsc_default, + _ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \ + Use 'false', 'true', 'musl' or 'mingw'", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident = $json_name:expr, link_objects) => ( { + let name = $json_name; + if let Some(val) = obj.remove(name) { + let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ + JSON object with fields per CRT object kind.", name))?; + let mut args = super::CrtObjects::new(); + for (k, v) in obj { + let kind = super::LinkOutputKind::from_str(&k).ok_or_else(|| { + format!("{}: '{}' is not a valid value for CRT object kind. \ + Use '(dynamic,static)-(nopic,pic)-exe' or \ + '(dynamic,static)-dylib' or 'wasi-reactor-exe'", name, k) + })?; + + let v = v.as_array().ok_or_else(|| + format!("{}.{}: expected a JSON array", name, k) + )?.iter().enumerate() + .map(|(i,s)| { + let s = s.as_str().ok_or_else(|| + format!("{}.{}[{}]: expected a JSON string", name, k, i))?; + Ok(s.to_string().into()) + }) + .collect::<Result<Vec<_>, String>>()?; + + args.insert(kind, v); + } + base.$key_name = args; + } + } ); + ($key_name:ident = $json_name:expr, link_args) => ( { + let name = $json_name; + if let Some(val) = obj.remove(name) { + let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ + JSON object with fields per linker-flavor.", name))?; + let mut args = super::LinkArgsCli::new(); + for (k, v) in obj { + let flavor = super::LinkerFlavorCli::from_str(&k).ok_or_else(|| { + format!("{}: '{}' is not a valid value for linker-flavor. \ + Use 'em', 'gcc', 'ld' or 'msvc'", name, k) + })?; + + let v = v.as_array().ok_or_else(|| + format!("{}.{}: expected a JSON array", name, k) + )?.iter().enumerate() + .map(|(i,s)| { + let s = s.as_str().ok_or_else(|| + format!("{}.{}[{}]: expected a JSON string", name, k, i))?; + Ok(s.to_string().into()) + }) + .collect::<Result<Vec<_>, String>>()?; + + args.insert(flavor, v); + } + base.$key_name = args; + } + } ); + ($key_name:ident, env) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(o) = obj.remove(&name) { + if let Some(a) = o.as_array() { + for o in a { + if let Some(s) = o.as_str() { + if let [k, v] = *s.split('=').collect::<Vec<_>>() { + base.$key_name + .to_mut() + .push((k.to_string().into(), v.to_string().into())) + } + } + } + } else { + incorrect_type.push(name) + } + } + } ); + ($key_name:ident, target_families) => ( { + if let Some(value) = obj.remove("target-family") { + if let Some(v) = value.as_array() { + base.$key_name = v.iter() + .map(|a| a.as_str().unwrap().to_string().into()) + .collect(); + } else if let Some(v) = value.as_str() { + base.$key_name = vec![v.to_string().into()].into(); + } + } + } ); + ($key_name:ident, Conv) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match super::Conv::from_str(s) { + Ok(c) => { + base.$key_name = c; + Some(Ok(())) + } + Err(e) => Some(Err(e)) + } + })).unwrap_or(Ok(())) + } ); + } + + if let Some(j) = obj.remove("target-endian") { + if let Some(s) = j.as_str() { + base.endian = s.parse()?; + } else { + incorrect_type.push("target-endian".into()) + } + } + + if let Some(fp) = obj.remove("frame-pointer") { + if let Some(s) = fp.as_str() { + base.frame_pointer = s + .parse() + .map_err(|()| format!("'{s}' is not a valid value for frame-pointer"))?; + } else { + incorrect_type.push("frame-pointer".into()) + } + } + + key!(c_int_width = "target-c-int-width"); + key!(c_enum_min_bits, Option<u64>); // if None, matches c_int_width + key!(os); + key!(env); + key!(abi); + key!(vendor); + key!(linker, optional); + key!(linker_flavor_json = "linker-flavor", LinkerFlavorCli)?; + key!(lld_flavor_json = "lld-flavor", LldFlavor)?; + key!(linker_is_gnu_json = "linker-is-gnu", bool); + key!(pre_link_objects = "pre-link-objects", link_objects); + key!(post_link_objects = "post-link-objects", link_objects); + key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects); + key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects); + // Deserializes the backwards-compatible variants of `-Clink-self-contained` + key!( + link_self_contained = "crt-objects-fallback", + link_self_contained_backwards_compatible + )?; + // Deserializes the components variant of `-Clink-self-contained` + key!(link_self_contained, link_self_contained_components)?; + key!(pre_link_args_json = "pre-link-args", link_args); + key!(late_link_args_json = "late-link-args", link_args); + key!(late_link_args_dynamic_json = "late-link-args-dynamic", link_args); + key!(late_link_args_static_json = "late-link-args-static", link_args); + key!(post_link_args_json = "post-link-args", link_args); + key!(link_script, optional); + key!(link_env, env); + key!(link_env_remove, list); + key!(asm_args, list); + key!(cpu); + key!(features); + key!(dynamic_linking, bool); + key!(direct_access_external_data, Option<bool>); + key!(dll_tls_export, bool); + key!(only_cdylib, bool); + key!(executables, bool); + key!(relocation_model, RelocModel)?; + key!(code_model, CodeModel)?; + key!(tls_model, TlsModel)?; + key!(disable_redzone, bool); + key!(function_sections, bool); + key!(dll_prefix); + key!(dll_suffix); + key!(exe_suffix); + key!(staticlib_prefix); + key!(staticlib_suffix); + key!(families, target_families); + key!(abi_return_struct_as_int, bool); + key!(is_like_aix, bool); + key!(is_like_osx, bool); + key!(is_like_solaris, bool); + key!(is_like_windows, bool); + key!(is_like_msvc, bool); + key!(is_like_wasm, bool); + key!(is_like_android, bool); + key!(default_dwarf_version, u32); + key!(allows_weak_linkage, bool); + key!(has_rpath, bool); + key!(no_default_libraries, bool); + key!(position_independent_executables, bool); + key!(static_position_independent_executables, bool); + key!(plt_by_default, bool); + key!(relro_level, RelroLevel)?; + key!(archive_format); + key!(allow_asm, bool); + key!(main_needs_argc_argv, bool); + key!(has_thread_local, bool); + key!(obj_is_bitcode, bool); + key!(bitcode_llvm_cmdline); + key!(max_atomic_width, Option<u64>); + key!(min_atomic_width, Option<u64>); + key!(atomic_cas, bool); + key!(panic_strategy, PanicStrategy)?; + key!(crt_static_allows_dylibs, bool); + key!(crt_static_default, bool); + key!(crt_static_respected, bool); + key!(stack_probes, StackProbeType)?; + key!(min_global_align, Option<u64>); + key!(default_codegen_units, Option<u64>); + key!(trap_unreachable, bool); + key!(requires_lto, bool); + key!(singlethread, bool); + key!(no_builtins, bool); + key!(default_visibility, Option<SymbolVisibility>)?; + key!(emit_debug_gdb_scripts, bool); + key!(requires_uwtable, bool); + key!(default_uwtable, bool); + key!(simd_types_indirect, bool); + key!(limit_rdylib_exports, bool); + key!(override_export_symbols, opt_list); + key!(merge_functions, MergeFunctions)?; + key!(mcount = "target-mcount"); + key!(llvm_mcount_intrinsic, optional); + key!(llvm_abiname); + key!(relax_elf_relocations, bool); + key!(llvm_args, list); + key!(use_ctors_section, bool); + key!(eh_frame_header, bool); + key!(has_thumb_interworking, bool); + key!(debuginfo_kind, DebuginfoKind)?; + key!(split_debuginfo, SplitDebuginfo)?; + key!(supported_split_debuginfo, fallible_list)?; + key!(supported_sanitizers, SanitizerSet)?; + key!(generate_arange_section, bool); + key!(supports_stack_protector, bool); + key!(small_data_threshold_support, SmallDataThresholdSupport)?; + key!(entry_name); + key!(entry_abi, Conv)?; + key!(supports_xray, bool); + + base.update_from_cli(); + base.check_consistency(TargetKind::Json)?; + + // Each field should have been read using `Json::remove` so any keys remaining are unused. + let remaining_keys = obj.keys(); + Ok((base, TargetWarnings { + unused_fields: remaining_keys.cloned().collect(), + incorrect_type, + })) + } +} + +impl ToJson for Target { + fn to_json(&self) -> Json { + let mut d = serde_json::Map::new(); + let default: TargetOptions = Default::default(); + let mut target = self.clone(); + target.update_to_cli(); + + macro_rules! target_val { + ($attr:ident) => {{ + let name = (stringify!($attr)).replace("_", "-"); + d.insert(name, target.$attr.to_json()); + }}; + } + + macro_rules! target_option_val { + ($attr:ident) => {{ + let name = (stringify!($attr)).replace("_", "-"); + if default.$attr != target.$attr { + d.insert(name, target.$attr.to_json()); + } + }}; + ($attr:ident, $json_name:expr) => {{ + let name = $json_name; + if default.$attr != target.$attr { + d.insert(name.into(), target.$attr.to_json()); + } + }}; + (link_args - $attr:ident, $json_name:expr) => {{ + let name = $json_name; + if default.$attr != target.$attr { + let obj = target + .$attr + .iter() + .map(|(k, v)| (k.desc().to_string(), v.clone())) + .collect::<BTreeMap<_, _>>(); + d.insert(name.to_string(), obj.to_json()); + } + }}; + (env - $attr:ident) => {{ + let name = (stringify!($attr)).replace("_", "-"); + if default.$attr != target.$attr { + let obj = target + .$attr + .iter() + .map(|&(ref k, ref v)| format!("{k}={v}")) + .collect::<Vec<_>>(); + d.insert(name, obj.to_json()); + } + }}; + } + + target_val!(llvm_target); + target_val!(metadata); + d.insert("target-pointer-width".to_string(), self.pointer_width.to_string().to_json()); + target_val!(arch); + target_val!(data_layout); + + target_option_val!(endian, "target-endian"); + target_option_val!(c_int_width, "target-c-int-width"); + target_option_val!(os); + target_option_val!(env); + target_option_val!(abi); + target_option_val!(vendor); + target_option_val!(linker); + target_option_val!(linker_flavor_json, "linker-flavor"); + target_option_val!(lld_flavor_json, "lld-flavor"); + target_option_val!(linker_is_gnu_json, "linker-is-gnu"); + target_option_val!(pre_link_objects); + target_option_val!(post_link_objects); + target_option_val!(pre_link_objects_self_contained, "pre-link-objects-fallback"); + target_option_val!(post_link_objects_self_contained, "post-link-objects-fallback"); + target_option_val!(link_args - pre_link_args_json, "pre-link-args"); + target_option_val!(link_args - late_link_args_json, "late-link-args"); + target_option_val!(link_args - late_link_args_dynamic_json, "late-link-args-dynamic"); + target_option_val!(link_args - late_link_args_static_json, "late-link-args-static"); + target_option_val!(link_args - post_link_args_json, "post-link-args"); + target_option_val!(link_script); + target_option_val!(env - link_env); + target_option_val!(link_env_remove); + target_option_val!(asm_args); + target_option_val!(cpu); + target_option_val!(features); + target_option_val!(dynamic_linking); + target_option_val!(direct_access_external_data); + target_option_val!(dll_tls_export); + target_option_val!(only_cdylib); + target_option_val!(executables); + target_option_val!(relocation_model); + target_option_val!(code_model); + target_option_val!(tls_model); + target_option_val!(disable_redzone); + target_option_val!(frame_pointer); + target_option_val!(function_sections); + target_option_val!(dll_prefix); + target_option_val!(dll_suffix); + target_option_val!(exe_suffix); + target_option_val!(staticlib_prefix); + target_option_val!(staticlib_suffix); + target_option_val!(families, "target-family"); + target_option_val!(abi_return_struct_as_int); + target_option_val!(is_like_aix); + target_option_val!(is_like_osx); + target_option_val!(is_like_solaris); + target_option_val!(is_like_windows); + target_option_val!(is_like_msvc); + target_option_val!(is_like_wasm); + target_option_val!(is_like_android); + target_option_val!(default_dwarf_version); + target_option_val!(allows_weak_linkage); + target_option_val!(has_rpath); + target_option_val!(no_default_libraries); + target_option_val!(position_independent_executables); + target_option_val!(static_position_independent_executables); + target_option_val!(plt_by_default); + target_option_val!(relro_level); + target_option_val!(archive_format); + target_option_val!(allow_asm); + target_option_val!(main_needs_argc_argv); + target_option_val!(has_thread_local); + target_option_val!(obj_is_bitcode); + target_option_val!(bitcode_llvm_cmdline); + target_option_val!(min_atomic_width); + target_option_val!(max_atomic_width); + target_option_val!(atomic_cas); + target_option_val!(panic_strategy); + target_option_val!(crt_static_allows_dylibs); + target_option_val!(crt_static_default); + target_option_val!(crt_static_respected); + target_option_val!(stack_probes); + target_option_val!(min_global_align); + target_option_val!(default_codegen_units); + target_option_val!(trap_unreachable); + target_option_val!(requires_lto); + target_option_val!(singlethread); + target_option_val!(no_builtins); + target_option_val!(default_visibility); + target_option_val!(emit_debug_gdb_scripts); + target_option_val!(requires_uwtable); + target_option_val!(default_uwtable); + target_option_val!(simd_types_indirect); + target_option_val!(limit_rdylib_exports); + target_option_val!(override_export_symbols); + target_option_val!(merge_functions); + target_option_val!(mcount, "target-mcount"); + target_option_val!(llvm_mcount_intrinsic); + target_option_val!(llvm_abiname); + target_option_val!(relax_elf_relocations); + target_option_val!(llvm_args); + target_option_val!(use_ctors_section); + target_option_val!(eh_frame_header); + target_option_val!(has_thumb_interworking); + target_option_val!(debuginfo_kind); + target_option_val!(split_debuginfo); + target_option_val!(supported_split_debuginfo); + target_option_val!(supported_sanitizers); + target_option_val!(c_enum_min_bits); + target_option_val!(generate_arange_section); + target_option_val!(supports_stack_protector); + target_option_val!(small_data_threshold_support); + target_option_val!(entry_name); + target_option_val!(entry_abi); + target_option_val!(supports_xray); + + // Serializing `-Clink-self-contained` needs a dynamic key to support the + // backwards-compatible variants. + d.insert(self.link_self_contained.json_key().into(), self.link_self_contained.to_json()); + + Json::Object(d) + } +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index fead20ec7d1..210d67fa1aa 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -42,6 +42,7 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{fmt, io}; +use rustc_data_structures::fx::FxHashSet; use rustc_fs_util::try_canonicalize; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -65,6 +66,8 @@ pub mod abi { } mod base; +mod json; + pub use base::avr_gnu::ef_avr_arch; /// Linker is called through a C/C++ compiler. @@ -1605,13 +1608,11 @@ macro_rules! supported_targets { #[cfg(test)] mod tests { - mod tests_impl; - // Cannot put this into a separate file without duplication, make an exception. $( #[test] // `#[test]` fn $module() { - tests_impl::test_target(crate::spec::targets::$module::target()); + crate::spec::targets::$module::target().test_target() } )+ } @@ -1998,6 +1999,14 @@ impl TargetWarnings { } } +/// For the [`Target::check_consistency`] function, determines whether the given target is a builtin or a JSON +/// target. +#[derive(Copy, Clone, Debug, PartialEq)] +enum TargetKind { + Json, + Builtin, +} + /// Everything `rustc` knows about how to compile for a specific target. /// /// Every field here must be specified, and has no default value. @@ -2108,6 +2117,8 @@ pub struct X86Abi { /// On x86-32 targets, the regparm N causes the compiler to pass arguments /// in registers EAX, EDX, and ECX instead of on the stack. pub regparm: Option<u32>, + /// Override the default ABI to return small structs in registers + pub reg_struct_return: bool, } pub trait HasX86AbiOpt { @@ -2846,619 +2857,355 @@ impl Target { self.max_atomic_width.unwrap_or_else(|| self.pointer_width.into()) } - /// Loads a target descriptor from a JSON object. - pub fn from_json(obj: Json) -> Result<(Target, TargetWarnings), String> { - // While ugly, this code must remain this way to retain - // compatibility with existing JSON fields and the internal - // expected naming of the Target and TargetOptions structs. - // To ensure compatibility is retained, the built-in targets - // are round-tripped through this code to catch cases where - // the JSON parser is not updated to match the structs. - - let mut obj = match obj { - Value::Object(obj) => obj, - _ => return Err("Expected JSON object for target")?, - }; - - let mut get_req_field = |name: &str| { - obj.remove(name) - .and_then(|j| j.as_str().map(str::to_string)) - .ok_or_else(|| format!("Field {name} in target specification is required")) - }; - - let mut base = Target { - llvm_target: get_req_field("llvm-target")?.into(), - metadata: Default::default(), - pointer_width: get_req_field("target-pointer-width")? - .parse::<u32>() - .map_err(|_| "target-pointer-width must be an integer".to_string())?, - data_layout: get_req_field("data-layout")?.into(), - arch: get_req_field("arch")?.into(), - options: Default::default(), - }; - - // FIXME: This doesn't properly validate anything and just ignores the data if it's invalid. - // That's okay for now, the only use of this is when generating docs, which we don't do for - // custom targets. - if let Some(Json::Object(mut metadata)) = obj.remove("metadata") { - base.metadata.description = metadata - .remove("description") - .and_then(|desc| desc.as_str().map(|desc| desc.to_owned().into())); - base.metadata.tier = metadata - .remove("tier") - .and_then(|tier| tier.as_u64()) - .filter(|tier| (1..=3).contains(tier)); - base.metadata.host_tools = - metadata.remove("host_tools").and_then(|host| host.as_bool()); - base.metadata.std = metadata.remove("std").and_then(|host| host.as_bool()); - } - - let mut incorrect_type = vec![]; - - macro_rules! key { - ($key_name:ident) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(s) = obj.remove(&name).and_then(|s| s.as_str().map(str::to_string).map(Cow::from)) { - base.$key_name = s; - } - } ); - ($key_name:ident = $json_name:expr) => ( { - let name = $json_name; - if let Some(s) = obj.remove(name).and_then(|s| s.as_str().map(str::to_string).map(Cow::from)) { - base.$key_name = s; - } - } ); - ($key_name:ident, bool) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(s) = obj.remove(&name).and_then(|b| b.as_bool()) { - base.$key_name = s; - } - } ); - ($key_name:ident = $json_name:expr, bool) => ( { - let name = $json_name; - if let Some(s) = obj.remove(name).and_then(|b| b.as_bool()) { - base.$key_name = s; + /// Check some basic consistency of the current target. For JSON targets we are less strict; + /// some of these checks are more guidelines than strict rules. + fn check_consistency(&self, kind: TargetKind) -> Result<(), String> { + macro_rules! check { + ($b:expr, $($msg:tt)*) => { + if !$b { + return Err(format!($($msg)*)); } - } ); - ($key_name:ident, u32) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(s) = obj.remove(&name).and_then(|b| b.as_u64()) { - if s < 1 || s > 5 { - return Err("Not a valid DWARF version number".into()); - } - base.$key_name = s as u32; - } - } ); - ($key_name:ident, Option<bool>) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(s) = obj.remove(&name).and_then(|b| b.as_bool()) { - base.$key_name = Some(s); - } - } ); - ($key_name:ident, Option<u64>) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(s) = obj.remove(&name).and_then(|b| b.as_u64()) { - base.$key_name = Some(s); - } - } ); - ($key_name:ident, MergeFunctions) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<MergeFunctions>() { - Ok(mergefunc) => base.$key_name = mergefunc, - _ => return Some(Err(format!("'{}' is not a valid value for \ - merge-functions. Use 'disabled', \ - 'trampolines', or 'aliases'.", - s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, RelocModel) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<RelocModel>() { - Ok(relocation_model) => base.$key_name = relocation_model, - _ => return Some(Err(format!("'{}' is not a valid relocation model. \ - Run `rustc --print relocation-models` to \ - see the list of supported values.", s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, CodeModel) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<CodeModel>() { - Ok(code_model) => base.$key_name = Some(code_model), - _ => return Some(Err(format!("'{}' is not a valid code model. \ - Run `rustc --print code-models` to \ - see the list of supported values.", s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, TlsModel) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<TlsModel>() { - Ok(tls_model) => base.$key_name = tls_model, - _ => return Some(Err(format!("'{}' is not a valid TLS model. \ - Run `rustc --print tls-models` to \ - see the list of supported values.", s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, SmallDataThresholdSupport) => ( { - obj.remove("small-data-threshold-support").and_then(|o| o.as_str().and_then(|s| { - match s.parse::<SmallDataThresholdSupport>() { - Ok(support) => base.small_data_threshold_support = support, - _ => return Some(Err(format!("'{s}' is not a valid value for small-data-threshold-support."))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, PanicStrategy) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s { - "unwind" => base.$key_name = PanicStrategy::Unwind, - "abort" => base.$key_name = PanicStrategy::Abort, - _ => return Some(Err(format!("'{}' is not a valid value for \ - panic-strategy. Use 'unwind' or 'abort'.", - s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, RelroLevel) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<RelroLevel>() { - Ok(level) => base.$key_name = level, - _ => return Some(Err(format!("'{}' is not a valid value for \ - relro-level. Use 'full', 'partial, or 'off'.", - s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, Option<SymbolVisibility>) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<SymbolVisibility>() { - Ok(level) => base.$key_name = Some(level), - _ => return Some(Err(format!("'{}' is not a valid value for \ - symbol-visibility. Use 'hidden', 'protected, or 'interposable'.", - s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, DebuginfoKind) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<DebuginfoKind>() { - Ok(level) => base.$key_name = level, - _ => return Some(Err( - format!("'{s}' is not a valid value for debuginfo-kind. Use 'dwarf', \ - 'dwarf-dsym' or 'pdb'.") - )), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, SplitDebuginfo) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<SplitDebuginfo>() { - Ok(level) => base.$key_name = level, - _ => return Some(Err(format!("'{}' is not a valid value for \ - split-debuginfo. Use 'off' or 'dsymutil'.", - s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, list) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(j) = obj.remove(&name) { - if let Some(v) = j.as_array() { - base.$key_name = v.iter() - .map(|a| a.as_str().unwrap().to_string().into()) - .collect(); - } else { - incorrect_type.push(name) - } + } + } + macro_rules! check_eq { + ($left:expr, $right:expr, $($msg:tt)*) => { + if ($left) != ($right) { + return Err(format!($($msg)*)); } - } ); - ($key_name:ident, opt_list) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(j) = obj.remove(&name) { - if let Some(v) = j.as_array() { - base.$key_name = Some(v.iter() - .map(|a| a.as_str().unwrap().to_string().into()) - .collect()); - } else { - incorrect_type.push(name) - } + } + } + macro_rules! check_ne { + ($left:expr, $right:expr, $($msg:tt)*) => { + if ($left) == ($right) { + return Err(format!($($msg)*)); } - } ); - ($key_name:ident, fallible_list) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|j| { - if let Some(v) = j.as_array() { - match v.iter().map(|a| FromStr::from_str(a.as_str().unwrap())).collect() { - Ok(l) => { base.$key_name = l }, - // FIXME: `fallible_list` can't re-use the `key!` macro for list - // elements and the error messages from that macro, so it has a bad - // generic message instead - Err(_) => return Some(Err( - format!("`{:?}` is not a valid value for `{}`", j, name) - )), - } - } else { - incorrect_type.push(name) - } - Some(Ok(())) - }).unwrap_or(Ok(())) - } ); - ($key_name:ident, optional) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(o) = obj.remove(&name) { - base.$key_name = o - .as_str() - .map(|s| s.to_string().into()); + } + } + macro_rules! check_matches { + ($left:expr, $right:pat, $($msg:tt)*) => { + if !matches!($left, $right) { + return Err(format!($($msg)*)); } - } ); - ($key_name:ident = $json_name:expr, LldFlavor) => ( { - let name = $json_name; - obj.remove(name).and_then(|o| o.as_str().and_then(|s| { - if let Some(flavor) = LldFlavor::from_str(&s) { - base.$key_name = flavor; - } else { - return Some(Err(format!( - "'{}' is not a valid value for lld-flavor. \ - Use 'darwin', 'gnu', 'link' or 'wasm'.", - s))) - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident = $json_name:expr, LinkerFlavor) => ( { - let name = $json_name; - obj.remove(name).and_then(|o| o.as_str().and_then(|s| { - match LinkerFlavorCli::from_str(s) { - Some(linker_flavor) => base.$key_name = linker_flavor, - _ => return Some(Err(format!("'{}' is not a valid value for linker-flavor. \ - Use {}", s, LinkerFlavorCli::one_of()))), + } + } + + check_eq!( + self.is_like_osx, + self.vendor == "apple", + "`is_like_osx` must be set if and only if `vendor` is `apple`" + ); + check_eq!( + self.is_like_solaris, + self.os == "solaris" || self.os == "illumos", + "`is_like_solaris` must be set if and only if `os` is `solaris` or `illumos`" + ); + check_eq!( + self.is_like_windows, + self.os == "windows" || self.os == "uefi", + "`is_like_windows` must be set if and only if `os` is `windows` or `uefi`" + ); + check_eq!( + self.is_like_wasm, + self.arch == "wasm32" || self.arch == "wasm64", + "`is_like_wasm` must be set if and only if `arch` is `wasm32` or `wasm64`" + ); + if self.is_like_msvc { + check!(self.is_like_windows, "if `is_like_msvc` is set, `is_like_windows` must be set"); + } + if self.os == "emscripten" { + check!(self.is_like_wasm, "the `emcscripten` os only makes sense on wasm-like targets"); + } + + // Check that default linker flavor is compatible with some other key properties. + check_eq!( + self.is_like_osx, + matches!(self.linker_flavor, LinkerFlavor::Darwin(..)), + "`linker_flavor` must be `darwin` if and only if `is_like_osx` is set" + ); + check_eq!( + self.is_like_msvc, + matches!(self.linker_flavor, LinkerFlavor::Msvc(..)), + "`linker_flavor` must be `msvc` if and only if `is_like_msvc` is set" + ); + check_eq!( + self.is_like_wasm && self.os != "emscripten", + matches!(self.linker_flavor, LinkerFlavor::WasmLld(..)), + "`linker_flavor` must be `wasm-lld` if and only if `is_like_wasm` is set and the `os` is not `emscripten`", + ); + check_eq!( + self.os == "emscripten", + matches!(self.linker_flavor, LinkerFlavor::EmCc), + "`linker_flavor` must be `em-cc` if and only if `os` is `emscripten`" + ); + check_eq!( + self.arch == "bpf", + matches!(self.linker_flavor, LinkerFlavor::Bpf), + "`linker_flavor` must be `bpf` if and only if `arch` is `bpf`" + ); + check_eq!( + self.arch == "nvptx64", + matches!(self.linker_flavor, LinkerFlavor::Ptx), + "`linker_flavor` must be `ptc` if and only if `arch` is `nvptx64`" + ); + + for args in [ + &self.pre_link_args, + &self.late_link_args, + &self.late_link_args_dynamic, + &self.late_link_args_static, + &self.post_link_args, + ] { + for (&flavor, flavor_args) in args { + check!(!flavor_args.is_empty(), "linker flavor args must not be empty"); + // Check that flavors mentioned in link args are compatible with the default flavor. + match self.linker_flavor { + LinkerFlavor::Gnu(..) => { + check_matches!( + flavor, + LinkerFlavor::Gnu(..), + "mixing GNU and non-GNU linker flavors" + ); } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, StackProbeType) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| match StackProbeType::from_json(&o) { - Ok(v) => { - base.$key_name = v; - Some(Ok(())) - }, - Err(s) => Some(Err( - format!("`{:?}` is not a valid value for `{}`: {}", o, name, s) - )), - }).unwrap_or(Ok(())) - } ); - ($key_name:ident, SanitizerSet) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(o) = obj.remove(&name) { - if let Some(a) = o.as_array() { - for s in a { - base.$key_name |= match s.as_str() { - Some("address") => SanitizerSet::ADDRESS, - Some("cfi") => SanitizerSet::CFI, - Some("dataflow") => SanitizerSet::DATAFLOW, - Some("kcfi") => SanitizerSet::KCFI, - Some("kernel-address") => SanitizerSet::KERNELADDRESS, - Some("leak") => SanitizerSet::LEAK, - Some("memory") => SanitizerSet::MEMORY, - Some("memtag") => SanitizerSet::MEMTAG, - Some("safestack") => SanitizerSet::SAFESTACK, - Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK, - Some("thread") => SanitizerSet::THREAD, - Some("hwaddress") => SanitizerSet::HWADDRESS, - Some(s) => return Err(format!("unknown sanitizer {}", s)), - _ => return Err(format!("not a string: {:?}", s)), - }; - } - } else { - incorrect_type.push(name) + LinkerFlavor::Darwin(..) => { + check_matches!( + flavor, + LinkerFlavor::Darwin(..), + "mixing Darwin and non-Darwin linker flavors" + ) } - } - Ok::<(), String>(()) - } ); - ($key_name:ident, link_self_contained_components) => ( { - // Skeleton of what needs to be parsed: - // - // ``` - // $name: { - // "components": [ - // <array of strings> - // ] - // } - // ``` - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(o) = obj.remove(&name) { - if let Some(o) = o.as_object() { - let component_array = o.get("components") - .ok_or_else(|| format!("{name}: expected a \ - JSON object with a `components` field."))?; - let component_array = component_array.as_array() - .ok_or_else(|| format!("{name}.components: expected a JSON array"))?; - let mut components = LinkSelfContainedComponents::empty(); - for s in component_array { - components |= match s.as_str() { - Some(s) => { - LinkSelfContainedComponents::from_str(s) - .ok_or_else(|| format!("unknown \ - `-Clink-self-contained` component: {s}"))? - }, - _ => return Err(format!("not a string: {:?}", s)), - }; - } - base.$key_name = LinkSelfContainedDefault::WithComponents(components); - } else { - incorrect_type.push(name) + LinkerFlavor::WasmLld(..) => { + check_matches!( + flavor, + LinkerFlavor::WasmLld(..), + "mixing wasm and non-wasm linker flavors" + ) } - } - Ok::<(), String>(()) - } ); - ($key_name:ident = $json_name:expr, link_self_contained_backwards_compatible) => ( { - let name = $json_name; - obj.remove(name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<LinkSelfContainedDefault>() { - Ok(lsc_default) => base.$key_name = lsc_default, - _ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \ - Use 'false', 'true', 'musl' or 'mingw'", s))), + LinkerFlavor::Unix(..) => { + check_matches!( + flavor, + LinkerFlavor::Unix(..), + "mixing unix and non-unix linker flavors" + ); } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident = $json_name:expr, link_objects) => ( { - let name = $json_name; - if let Some(val) = obj.remove(name) { - let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ - JSON object with fields per CRT object kind.", name))?; - let mut args = CrtObjects::new(); - for (k, v) in obj { - let kind = LinkOutputKind::from_str(&k).ok_or_else(|| { - format!("{}: '{}' is not a valid value for CRT object kind. \ - Use '(dynamic,static)-(nopic,pic)-exe' or \ - '(dynamic,static)-dylib' or 'wasi-reactor-exe'", name, k) - })?; - - let v = v.as_array().ok_or_else(|| - format!("{}.{}: expected a JSON array", name, k) - )?.iter().enumerate() - .map(|(i,s)| { - let s = s.as_str().ok_or_else(|| - format!("{}.{}[{}]: expected a JSON string", name, k, i))?; - Ok(s.to_string().into()) - }) - .collect::<Result<Vec<_>, String>>()?; - - args.insert(kind, v); + LinkerFlavor::Msvc(..) => { + check_matches!( + flavor, + LinkerFlavor::Msvc(..), + "mixing MSVC and non-MSVC linker flavors" + ); } - base.$key_name = args; - } - } ); - ($key_name:ident = $json_name:expr, link_args) => ( { - let name = $json_name; - if let Some(val) = obj.remove(name) { - let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ - JSON object with fields per linker-flavor.", name))?; - let mut args = LinkArgsCli::new(); - for (k, v) in obj { - let flavor = LinkerFlavorCli::from_str(&k).ok_or_else(|| { - format!("{}: '{}' is not a valid value for linker-flavor. \ - Use 'em', 'gcc', 'ld' or 'msvc'", name, k) - })?; - - let v = v.as_array().ok_or_else(|| - format!("{}.{}: expected a JSON array", name, k) - )?.iter().enumerate() - .map(|(i,s)| { - let s = s.as_str().ok_or_else(|| - format!("{}.{}[{}]: expected a JSON string", name, k, i))?; - Ok(s.to_string().into()) - }) - .collect::<Result<Vec<_>, String>>()?; - - args.insert(flavor, v); + LinkerFlavor::EmCc + | LinkerFlavor::Bpf + | LinkerFlavor::Ptx + | LinkerFlavor::Llbc => { + check_eq!(flavor, self.linker_flavor, "mixing different linker flavors") } - base.$key_name = args; } - } ); - ($key_name:ident, env) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(o) = obj.remove(&name) { - if let Some(a) = o.as_array() { - for o in a { - if let Some(s) = o.as_str() { - if let [k, v] = *s.split('=').collect::<Vec<_>>() { - base.$key_name - .to_mut() - .push((k.to_string().into(), v.to_string().into())) - } + + // Check that link args for cc and non-cc versions of flavors are consistent. + let check_noncc = |noncc_flavor| -> Result<(), String> { + if let Some(noncc_args) = args.get(&noncc_flavor) { + for arg in flavor_args { + if let Some(suffix) = arg.strip_prefix("-Wl,") { + check!( + noncc_args.iter().any(|a| a == suffix), + " link args for cc and non-cc versions of flavors are not consistent" + ); } } - } else { - incorrect_type.push(name) - } - } - } ); - ($key_name:ident, TargetFamilies) => ( { - if let Some(value) = obj.remove("target-family") { - if let Some(v) = value.as_array() { - base.$key_name = v.iter() - .map(|a| a.as_str().unwrap().to_string().into()) - .collect(); - } else if let Some(v) = value.as_str() { - base.$key_name = vec![v.to_string().into()].into(); } + Ok(()) + }; + + match self.linker_flavor { + LinkerFlavor::Gnu(Cc::Yes, lld) => check_noncc(LinkerFlavor::Gnu(Cc::No, lld))?, + LinkerFlavor::WasmLld(Cc::Yes) => check_noncc(LinkerFlavor::WasmLld(Cc::No))?, + LinkerFlavor::Unix(Cc::Yes) => check_noncc(LinkerFlavor::Unix(Cc::No))?, + _ => {} } - } ); - ($key_name:ident, Conv) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match Conv::from_str(s) { - Ok(c) => { - base.$key_name = c; - Some(Ok(())) - } - Err(e) => Some(Err(e)) - } - })).unwrap_or(Ok(())) - } ); - } + } - if let Some(j) = obj.remove("target-endian") { - if let Some(s) = j.as_str() { - base.endian = s.parse()?; - } else { - incorrect_type.push("target-endian".into()) + // Check that link args for lld and non-lld versions of flavors are consistent. + for cc in [Cc::No, Cc::Yes] { + check_eq!( + args.get(&LinkerFlavor::Gnu(cc, Lld::No)), + args.get(&LinkerFlavor::Gnu(cc, Lld::Yes)), + "link args for lld and non-lld versions of flavors are not consistent", + ); + check_eq!( + args.get(&LinkerFlavor::Darwin(cc, Lld::No)), + args.get(&LinkerFlavor::Darwin(cc, Lld::Yes)), + "link args for lld and non-lld versions of flavors are not consistent", + ); + } + check_eq!( + args.get(&LinkerFlavor::Msvc(Lld::No)), + args.get(&LinkerFlavor::Msvc(Lld::Yes)), + "link args for lld and non-lld versions of flavors are not consistent", + ); + } + + if self.link_self_contained.is_disabled() { + check!( + self.pre_link_objects_self_contained.is_empty() + && self.post_link_objects_self_contained.is_empty(), + "if `link_self_contained` is disabled, then `pre_link_objects_self_contained` and `post_link_objects_self_contained` must be empty", + ); + } + + // If your target really needs to deviate from the rules below, + // except it and document the reasons. + // Keep the default "unknown" vendor instead. + check_ne!(self.vendor, "", "`vendor` cannot be empty"); + check_ne!(self.os, "", "`os` cannot be empty"); + if !self.can_use_os_unknown() { + // Keep the default "none" for bare metal targets instead. + check_ne!( + self.os, + "unknown", + "`unknown` os can only be used on particular targets; use `none` for bare-metal targets" + ); + } + + // Check dynamic linking stuff. + // We skip this for JSON targets since otherwise, our default values would fail this test. + // These checks are not critical for correctness, but more like default guidelines. + // FIXME (https://github.com/rust-lang/rust/issues/133459): do we want to change the JSON + // target defaults so that they pass these checks? + if kind == TargetKind::Builtin { + // BPF: when targeting user space vms (like rbpf), those can load dynamic libraries. + // hexagon: when targeting QuRT, that OS can load dynamic libraries. + // wasm{32,64}: dynamic linking is inherent in the definition of the VM. + if self.os == "none" + && (self.arch != "bpf" + && self.arch != "hexagon" + && self.arch != "wasm32" + && self.arch != "wasm64") + { + check!( + !self.dynamic_linking, + "dynamic linking is not supported on this OS/architecture" + ); + } + if self.only_cdylib + || self.crt_static_allows_dylibs + || !self.late_link_args_dynamic.is_empty() + { + check!( + self.dynamic_linking, + "dynamic linking must be allowed when `only_cdylib` or `crt_static_allows_dylibs` or `late_link_args_dynamic` are set" + ); + } + // Apparently PIC was slow on wasm at some point, see comments in wasm_base.rs + if self.dynamic_linking && !self.is_like_wasm { + check_eq!( + self.relocation_model, + RelocModel::Pic, + "targets that support dynamic linking must use the `pic` relocation model" + ); + } + if self.position_independent_executables { + check_eq!( + self.relocation_model, + RelocModel::Pic, + "targets that support position-independent executables must use the `pic` relocation model" + ); + } + // The UEFI targets do not support dynamic linking but still require PIC (#101377). + if self.relocation_model == RelocModel::Pic && (self.os != "uefi") { + check!( + self.dynamic_linking || self.position_independent_executables, + "when the relocation model is `pic`, the target must support dynamic linking or use position-independent executables. \ + Set the relocation model to `static` to avoid this requirement" + ); + } + if self.static_position_independent_executables { + check!( + self.position_independent_executables, + "if `static_position_independent_executables` is set, then `position_independent_executables` must be set" + ); } + if self.position_independent_executables { + check!( + self.executables, + "if `position_independent_executables` is set then `executables` must be set" + ); + } + } + + // Check crt static stuff + if self.crt_static_default || self.crt_static_allows_dylibs { + check!( + self.crt_static_respected, + "static CRT can be enabled but `crt_static_respected` is not set" + ); } - if let Some(fp) = obj.remove("frame-pointer") { - if let Some(s) = fp.as_str() { - base.frame_pointer = s - .parse() - .map_err(|()| format!("'{s}' is not a valid value for frame-pointer"))?; - } else { - incorrect_type.push("frame-pointer".into()) + // Check that RISC-V targets always specify which ABI they use. + match &*self.arch { + "riscv32" => { + check_matches!( + &*self.llvm_abiname, + "ilp32" | "ilp32f" | "ilp32d" | "ilp32e", + "invalid RISC-V ABI name" + ); + } + "riscv64" => { + // Note that the `lp64e` is still unstable as it's not (yet) part of the ELF psABI. + check_matches!( + &*self.llvm_abiname, + "lp64" | "lp64f" | "lp64d" | "lp64q" | "lp64e", + "invalid RISC-V ABI name" + ); + } + _ => {} + } + + // Check that the given target-features string makes some basic sense. + if !self.features.is_empty() { + let mut features_enabled = FxHashSet::default(); + let mut features_disabled = FxHashSet::default(); + for feat in self.features.split(',') { + if let Some(feat) = feat.strip_prefix("+") { + features_enabled.insert(feat); + if features_disabled.contains(feat) { + return Err(format!( + "target feature `{feat}` is both enabled and disabled" + )); + } + } else if let Some(feat) = feat.strip_prefix("-") { + features_disabled.insert(feat); + if features_enabled.contains(feat) { + return Err(format!( + "target feature `{feat}` is both enabled and disabled" + )); + } + } else { + return Err(format!( + "target feature `{feat}` is invalid, must start with `+` or `-`" + )); + } } } - key!(c_int_width = "target-c-int-width"); - key!(c_enum_min_bits, Option<u64>); // if None, matches c_int_width - key!(os); - key!(env); - key!(abi); - key!(vendor); - key!(linker, optional); - key!(linker_flavor_json = "linker-flavor", LinkerFlavor)?; - key!(lld_flavor_json = "lld-flavor", LldFlavor)?; - key!(linker_is_gnu_json = "linker-is-gnu", bool); - key!(pre_link_objects = "pre-link-objects", link_objects); - key!(post_link_objects = "post-link-objects", link_objects); - key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects); - key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects); - // Deserializes the backwards-compatible variants of `-Clink-self-contained` - key!( - link_self_contained = "crt-objects-fallback", - link_self_contained_backwards_compatible - )?; - // Deserializes the components variant of `-Clink-self-contained` - key!(link_self_contained, link_self_contained_components)?; - key!(pre_link_args_json = "pre-link-args", link_args); - key!(late_link_args_json = "late-link-args", link_args); - key!(late_link_args_dynamic_json = "late-link-args-dynamic", link_args); - key!(late_link_args_static_json = "late-link-args-static", link_args); - key!(post_link_args_json = "post-link-args", link_args); - key!(link_script, optional); - key!(link_env, env); - key!(link_env_remove, list); - key!(asm_args, list); - key!(cpu); - key!(features); - key!(dynamic_linking, bool); - key!(direct_access_external_data, Option<bool>); - key!(dll_tls_export, bool); - key!(only_cdylib, bool); - key!(executables, bool); - key!(relocation_model, RelocModel)?; - key!(code_model, CodeModel)?; - key!(tls_model, TlsModel)?; - key!(disable_redzone, bool); - key!(function_sections, bool); - key!(dll_prefix); - key!(dll_suffix); - key!(exe_suffix); - key!(staticlib_prefix); - key!(staticlib_suffix); - key!(families, TargetFamilies); - key!(abi_return_struct_as_int, bool); - key!(is_like_aix, bool); - key!(is_like_osx, bool); - key!(is_like_solaris, bool); - key!(is_like_windows, bool); - key!(is_like_msvc, bool); - key!(is_like_wasm, bool); - key!(is_like_android, bool); - key!(default_dwarf_version, u32); - key!(allows_weak_linkage, bool); - key!(has_rpath, bool); - key!(no_default_libraries, bool); - key!(position_independent_executables, bool); - key!(static_position_independent_executables, bool); - key!(plt_by_default, bool); - key!(relro_level, RelroLevel)?; - key!(archive_format); - key!(allow_asm, bool); - key!(main_needs_argc_argv, bool); - key!(has_thread_local, bool); - key!(obj_is_bitcode, bool); - key!(bitcode_llvm_cmdline); - key!(max_atomic_width, Option<u64>); - key!(min_atomic_width, Option<u64>); - key!(atomic_cas, bool); - key!(panic_strategy, PanicStrategy)?; - key!(crt_static_allows_dylibs, bool); - key!(crt_static_default, bool); - key!(crt_static_respected, bool); - key!(stack_probes, StackProbeType)?; - key!(min_global_align, Option<u64>); - key!(default_codegen_units, Option<u64>); - key!(trap_unreachable, bool); - key!(requires_lto, bool); - key!(singlethread, bool); - key!(no_builtins, bool); - key!(default_visibility, Option<SymbolVisibility>)?; - key!(emit_debug_gdb_scripts, bool); - key!(requires_uwtable, bool); - key!(default_uwtable, bool); - key!(simd_types_indirect, bool); - key!(limit_rdylib_exports, bool); - key!(override_export_symbols, opt_list); - key!(merge_functions, MergeFunctions)?; - key!(mcount = "target-mcount"); - key!(llvm_mcount_intrinsic, optional); - key!(llvm_abiname); - key!(relax_elf_relocations, bool); - key!(llvm_args, list); - key!(use_ctors_section, bool); - key!(eh_frame_header, bool); - key!(has_thumb_interworking, bool); - key!(debuginfo_kind, DebuginfoKind)?; - key!(split_debuginfo, SplitDebuginfo)?; - key!(supported_split_debuginfo, fallible_list)?; - key!(supported_sanitizers, SanitizerSet)?; - key!(generate_arange_section, bool); - key!(supports_stack_protector, bool); - key!(small_data_threshold_support, SmallDataThresholdSupport)?; - key!(entry_name); - key!(entry_abi, Conv)?; - key!(supports_xray, bool); - - base.update_from_cli(); - - // Each field should have been read using `Json::remove` so any keys remaining are unused. - let remaining_keys = obj.keys(); - Ok((base, TargetWarnings { - unused_fields: remaining_keys.cloned().collect(), - incorrect_type, - })) + Ok(()) + } + + /// Test target self-consistency and JSON encoding/decoding roundtrip. + #[cfg(test)] + fn test_target(mut self) { + let recycled_target = Target::from_json(self.to_json()).map(|(j, _)| j); + self.update_to_cli(); + self.check_consistency(TargetKind::Builtin).unwrap(); + assert_eq!(recycled_target, Ok(self)); + } + + // Add your target to the whitelist if it has `std` library + // and you certainly want "unknown" for the OS name. + fn can_use_os_unknown(&self) -> bool { + self.llvm_target == "wasm32-unknown-unknown" + || self.llvm_target == "wasm64-unknown-unknown" + || (self.env == "sgx" && self.vendor == "fortanix") } /// Load a built-in target @@ -3563,177 +3310,6 @@ impl Target { } } -impl ToJson for Target { - fn to_json(&self) -> Json { - let mut d = serde_json::Map::new(); - let default: TargetOptions = Default::default(); - let mut target = self.clone(); - target.update_to_cli(); - - macro_rules! target_val { - ($attr:ident) => {{ - let name = (stringify!($attr)).replace("_", "-"); - d.insert(name, target.$attr.to_json()); - }}; - } - - macro_rules! target_option_val { - ($attr:ident) => {{ - let name = (stringify!($attr)).replace("_", "-"); - if default.$attr != target.$attr { - d.insert(name, target.$attr.to_json()); - } - }}; - ($attr:ident, $json_name:expr) => {{ - let name = $json_name; - if default.$attr != target.$attr { - d.insert(name.into(), target.$attr.to_json()); - } - }}; - (link_args - $attr:ident, $json_name:expr) => {{ - let name = $json_name; - if default.$attr != target.$attr { - let obj = target - .$attr - .iter() - .map(|(k, v)| (k.desc().to_string(), v.clone())) - .collect::<BTreeMap<_, _>>(); - d.insert(name.to_string(), obj.to_json()); - } - }}; - (env - $attr:ident) => {{ - let name = (stringify!($attr)).replace("_", "-"); - if default.$attr != target.$attr { - let obj = target - .$attr - .iter() - .map(|&(ref k, ref v)| format!("{k}={v}")) - .collect::<Vec<_>>(); - d.insert(name, obj.to_json()); - } - }}; - } - - target_val!(llvm_target); - target_val!(metadata); - d.insert("target-pointer-width".to_string(), self.pointer_width.to_string().to_json()); - target_val!(arch); - target_val!(data_layout); - - target_option_val!(endian, "target-endian"); - target_option_val!(c_int_width, "target-c-int-width"); - target_option_val!(os); - target_option_val!(env); - target_option_val!(abi); - target_option_val!(vendor); - target_option_val!(linker); - target_option_val!(linker_flavor_json, "linker-flavor"); - target_option_val!(lld_flavor_json, "lld-flavor"); - target_option_val!(linker_is_gnu_json, "linker-is-gnu"); - target_option_val!(pre_link_objects); - target_option_val!(post_link_objects); - target_option_val!(pre_link_objects_self_contained, "pre-link-objects-fallback"); - target_option_val!(post_link_objects_self_contained, "post-link-objects-fallback"); - target_option_val!(link_args - pre_link_args_json, "pre-link-args"); - target_option_val!(link_args - late_link_args_json, "late-link-args"); - target_option_val!(link_args - late_link_args_dynamic_json, "late-link-args-dynamic"); - target_option_val!(link_args - late_link_args_static_json, "late-link-args-static"); - target_option_val!(link_args - post_link_args_json, "post-link-args"); - target_option_val!(link_script); - target_option_val!(env - link_env); - target_option_val!(link_env_remove); - target_option_val!(asm_args); - target_option_val!(cpu); - target_option_val!(features); - target_option_val!(dynamic_linking); - target_option_val!(direct_access_external_data); - target_option_val!(dll_tls_export); - target_option_val!(only_cdylib); - target_option_val!(executables); - target_option_val!(relocation_model); - target_option_val!(code_model); - target_option_val!(tls_model); - target_option_val!(disable_redzone); - target_option_val!(frame_pointer); - target_option_val!(function_sections); - target_option_val!(dll_prefix); - target_option_val!(dll_suffix); - target_option_val!(exe_suffix); - target_option_val!(staticlib_prefix); - target_option_val!(staticlib_suffix); - target_option_val!(families, "target-family"); - target_option_val!(abi_return_struct_as_int); - target_option_val!(is_like_aix); - target_option_val!(is_like_osx); - target_option_val!(is_like_solaris); - target_option_val!(is_like_windows); - target_option_val!(is_like_msvc); - target_option_val!(is_like_wasm); - target_option_val!(is_like_android); - target_option_val!(default_dwarf_version); - target_option_val!(allows_weak_linkage); - target_option_val!(has_rpath); - target_option_val!(no_default_libraries); - target_option_val!(position_independent_executables); - target_option_val!(static_position_independent_executables); - target_option_val!(plt_by_default); - target_option_val!(relro_level); - target_option_val!(archive_format); - target_option_val!(allow_asm); - target_option_val!(main_needs_argc_argv); - target_option_val!(has_thread_local); - target_option_val!(obj_is_bitcode); - target_option_val!(bitcode_llvm_cmdline); - target_option_val!(min_atomic_width); - target_option_val!(max_atomic_width); - target_option_val!(atomic_cas); - target_option_val!(panic_strategy); - target_option_val!(crt_static_allows_dylibs); - target_option_val!(crt_static_default); - target_option_val!(crt_static_respected); - target_option_val!(stack_probes); - target_option_val!(min_global_align); - target_option_val!(default_codegen_units); - target_option_val!(trap_unreachable); - target_option_val!(requires_lto); - target_option_val!(singlethread); - target_option_val!(no_builtins); - target_option_val!(default_visibility); - target_option_val!(emit_debug_gdb_scripts); - target_option_val!(requires_uwtable); - target_option_val!(default_uwtable); - target_option_val!(simd_types_indirect); - target_option_val!(limit_rdylib_exports); - target_option_val!(override_export_symbols); - target_option_val!(merge_functions); - target_option_val!(mcount, "target-mcount"); - target_option_val!(llvm_mcount_intrinsic); - target_option_val!(llvm_abiname); - target_option_val!(relax_elf_relocations); - target_option_val!(llvm_args); - target_option_val!(use_ctors_section); - target_option_val!(eh_frame_header); - target_option_val!(has_thumb_interworking); - target_option_val!(debuginfo_kind); - target_option_val!(split_debuginfo); - target_option_val!(supported_split_debuginfo); - target_option_val!(supported_sanitizers); - target_option_val!(c_enum_min_bits); - target_option_val!(generate_arange_section); - target_option_val!(supports_stack_protector); - target_option_val!(small_data_threshold_support); - target_option_val!(entry_name); - target_option_val!(entry_abi); - target_option_val!(supports_xray); - - // Serializing `-Clink-self-contained` needs a dynamic key to support the - // backwards-compatible variants. - d.insert(self.link_self_contained.json_key().into(), self.link_self_contained.to_json()); - - Json::Object(d) - } -} - /// Either a target tuple string or a path to a JSON file. #[derive(Clone, Debug)] pub enum TargetTuple { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs index 62d6ffbd34f..9817c5a8eb0 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { description: Some("ARM64 Apple visionOS".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs index a66c4f6e96b..d411f710540 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { description: Some("ARM64 Apple visionOS simulator".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs index 22b3a5f8842..14a22988a09 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs @@ -16,7 +16,6 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: "aarch64".into(), options: TargetOptions { - features: "+reserve-x18".into(), mcount: "\u{1}_mcount".into(), stack_probes: StackProbeType::Inline, supported_sanitizers: SanitizerSet::ADDRESS diff --git a/compiler/rustc_target/src/spec/targets/armv4t_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/targets/armv4t_unknown_linux_gnueabi.rs index 3a2fb753ba1..7ca2babffe9 100644 --- a/compiler/rustc_target/src/spec/targets/armv4t_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv4t_unknown_linux_gnueabi.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { description: Some("Armv4T Linux".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs index e0bc1936e22..154103c36f7 100644 --- a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { description: Some("Armv5TE Linux with uClibc".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs index 1515d8fe00a..47c6d805483 100644 --- a/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs @@ -12,7 +12,7 @@ pub(crate) fn target() -> Target { description: Some("Motorola 680x0 Linux".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "E-m:e-p:32:16:32-i8:8:8-i16:16:16-i32:16:32-n8:16:32-a:0:16-S16".into(), diff --git a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs index b7415bf683d..cc288e042d9 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { description: Some("MIPS64 for OpenWrt Linux musl 1.2.3".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/mipsisa32r6_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/mipsisa32r6_unknown_linux_gnu.rs index 2ff06dc6669..3ef0988d235 100644 --- a/compiler/rustc_target/src/spec/targets/mipsisa32r6_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/mipsisa32r6_unknown_linux_gnu.rs @@ -8,7 +8,7 @@ pub(crate) fn target() -> Target { description: Some("32-bit MIPS Release 6 Big Endian".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/mipsisa32r6el_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/mipsisa32r6el_unknown_linux_gnu.rs index 05b24669848..c75023385c2 100644 --- a/compiler/rustc_target/src/spec/targets/mipsisa32r6el_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/mipsisa32r6el_unknown_linux_gnu.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { description: Some("32-bit MIPS Release 6 Little Endian".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs index c5948b745ff..d0d0ed1acc3 100644 --- a/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs @@ -8,7 +8,7 @@ pub(crate) fn target() -> Target { description: Some("64-bit MIPS Release 6 Big Endian".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs index 5da891cc13f..62c30aebc51 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs @@ -14,7 +14,7 @@ pub(crate) fn target() -> Target { description: Some("64-bit PowerPC Linux with musl 1.2.3".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: "E-m:e-Fn32-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs index e194f5f8a36..0c1218bd059 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs @@ -12,8 +12,8 @@ pub(crate) fn target() -> Target { metadata: crate::spec::TargetMetadata { description: Some("PPC64LE FreeBSD".into()), tier: Some(3), - host_tools: Some(false), - std: Some(false), + host_tools: Some(true), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-Fn32-i64:64-n32:64".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs index e2921aa17fe..50946ae4ce6 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { description: Some("64-bit PowerPC Linux with musl 1.2.3, Little Endian".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-Fn32-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_freebsd.rs index 393946980f8..9cfc2153ac1 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_freebsd.rs @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { description: Some("PowerPC FreeBSD".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 32, data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs index 9859e8ced4f..5372a83e29a 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { description: Some("PowerPC Linux with musl 1.2.3".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_openbsd.rs index a8254af9736..7aedc2d0665 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_openbsd.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { description: None, tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs index 8b0a140c9ad..e9c57b99b92 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { description: Some("RISC-V Linux (kernel 5.4, glibc 2.33)".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs index 212de791e49..08871d9d72b 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs @@ -11,7 +11,7 @@ pub(crate) fn target() -> Target { ), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs b/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs index 8b5d4751a57..f694a1cb60d 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { description: Some("RISC-V 64-bit Android".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs index d1b0d801fd6..905bed76db4 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { description: Some("RISC-V FreeBSD".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs index 6de51f9b07e..7a887b604c5 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { description: Some("RISC-V Fuchsia".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs index 2e6fce91d4c..397c202ec1a 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { description: Some("RISC-V Linux (kernel 4.20, musl 1.2.3)".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), 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 4bde0fb729c..7a78004927b 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 @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { description: Some("S390x Linux (kernel 3.2, musl 1.2.3)".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs index 5c43ca69bd8..2f539f4f600 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { description: Some("Thumb2-mode ARMv7-A Linux with NEON, musl 1.2.3".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs index 91eb7b53fea..900dbed205c 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs @@ -14,7 +14,7 @@ pub(crate) fn target() -> Target { description: None, tier: None, host_tools: None, - std: None, + std: Some(true), }, pointer_width: 64, data_layout: diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs index 34d8383a604..ae7bfbd0394 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs @@ -19,6 +19,7 @@ pub(crate) fn target() -> Target { cpu: "esp32-s2".into(), linker: Some("xtensa-esp32s2-elf-gcc".into()), max_atomic_width: Some(32), + features: "+forced-atomics".into(), ..xtensa::opts() }, } diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs deleted file mode 100644 index bd47d12ef9f..00000000000 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ /dev/null @@ -1,182 +0,0 @@ -use std::assert_matches::assert_matches; - -use super::super::*; - -// Test target self-consistency and JSON encoding/decoding roundtrip. -pub(super) fn test_target(mut target: Target) { - let recycled_target = Target::from_json(target.to_json()).map(|(j, _)| j); - target.update_to_cli(); - target.check_consistency(); - assert_eq!(recycled_target, Ok(target)); -} - -impl Target { - fn check_consistency(&self) { - assert_eq!(self.is_like_osx, self.vendor == "apple"); - assert_eq!(self.is_like_solaris, self.os == "solaris" || self.os == "illumos"); - assert_eq!(self.is_like_windows, self.os == "windows" || self.os == "uefi"); - assert_eq!(self.is_like_wasm, self.arch == "wasm32" || self.arch == "wasm64"); - if self.is_like_msvc { - assert!(self.is_like_windows); - } - - // Check that default linker flavor is compatible with some other key properties. - assert_eq!(self.is_like_osx, matches!(self.linker_flavor, LinkerFlavor::Darwin(..))); - assert_eq!(self.is_like_msvc, matches!(self.linker_flavor, LinkerFlavor::Msvc(..))); - assert_eq!( - self.is_like_wasm && self.os != "emscripten", - matches!(self.linker_flavor, LinkerFlavor::WasmLld(..)) - ); - assert_eq!(self.os == "emscripten", matches!(self.linker_flavor, LinkerFlavor::EmCc)); - assert_eq!(self.arch == "bpf", matches!(self.linker_flavor, LinkerFlavor::Bpf)); - assert_eq!(self.arch == "nvptx64", matches!(self.linker_flavor, LinkerFlavor::Ptx)); - - for args in [ - &self.pre_link_args, - &self.late_link_args, - &self.late_link_args_dynamic, - &self.late_link_args_static, - &self.post_link_args, - ] { - for (&flavor, flavor_args) in args { - assert!(!flavor_args.is_empty()); - // Check that flavors mentioned in link args are compatible with the default flavor. - match self.linker_flavor { - LinkerFlavor::Gnu(..) => { - assert_matches!(flavor, LinkerFlavor::Gnu(..)); - } - LinkerFlavor::Darwin(..) => { - assert_matches!(flavor, LinkerFlavor::Darwin(..)) - } - LinkerFlavor::WasmLld(..) => { - assert_matches!(flavor, LinkerFlavor::WasmLld(..)) - } - LinkerFlavor::Unix(..) => { - assert_matches!(flavor, LinkerFlavor::Unix(..)); - } - LinkerFlavor::Msvc(..) => { - assert_matches!(flavor, LinkerFlavor::Msvc(..)) - } - LinkerFlavor::EmCc - | LinkerFlavor::Bpf - | LinkerFlavor::Ptx - | LinkerFlavor::Llbc => { - assert_eq!(flavor, self.linker_flavor) - } - } - - // Check that link args for cc and non-cc versions of flavors are consistent. - let check_noncc = |noncc_flavor| { - if let Some(noncc_args) = args.get(&noncc_flavor) { - for arg in flavor_args { - if let Some(suffix) = arg.strip_prefix("-Wl,") { - assert!(noncc_args.iter().any(|a| a == suffix)); - } - } - } - }; - - match self.linker_flavor { - LinkerFlavor::Gnu(Cc::Yes, lld) => check_noncc(LinkerFlavor::Gnu(Cc::No, lld)), - LinkerFlavor::WasmLld(Cc::Yes) => check_noncc(LinkerFlavor::WasmLld(Cc::No)), - LinkerFlavor::Unix(Cc::Yes) => check_noncc(LinkerFlavor::Unix(Cc::No)), - _ => {} - } - } - - // Check that link args for lld and non-lld versions of flavors are consistent. - for cc in [Cc::No, Cc::Yes] { - assert_eq!( - args.get(&LinkerFlavor::Gnu(cc, Lld::No)), - args.get(&LinkerFlavor::Gnu(cc, Lld::Yes)), - ); - assert_eq!( - args.get(&LinkerFlavor::Darwin(cc, Lld::No)), - args.get(&LinkerFlavor::Darwin(cc, Lld::Yes)), - ); - } - assert_eq!( - args.get(&LinkerFlavor::Msvc(Lld::No)), - args.get(&LinkerFlavor::Msvc(Lld::Yes)), - ); - } - - if self.link_self_contained.is_disabled() { - assert!( - self.pre_link_objects_self_contained.is_empty() - && self.post_link_objects_self_contained.is_empty() - ); - } - - // If your target really needs to deviate from the rules below, - // except it and document the reasons. - // Keep the default "unknown" vendor instead. - assert_ne!(self.vendor, ""); - assert_ne!(self.os, ""); - if !self.can_use_os_unknown() { - // Keep the default "none" for bare metal targets instead. - assert_ne!(self.os, "unknown"); - } - - // Check dynamic linking stuff - // BPF: when targeting user space vms (like rbpf), those can load dynamic libraries. - // hexagon: when targeting QuRT, that OS can load dynamic libraries. - // wasm{32,64}: dynamic linking is inherent in the definition of the VM. - if self.os == "none" - && (self.arch != "bpf" - && self.arch != "hexagon" - && self.arch != "wasm32" - && self.arch != "wasm64") - { - assert!(!self.dynamic_linking); - } - if self.only_cdylib - || self.crt_static_allows_dylibs - || !self.late_link_args_dynamic.is_empty() - { - assert!(self.dynamic_linking); - } - // Apparently PIC was slow on wasm at some point, see comments in wasm_base.rs - if self.dynamic_linking && !(self.is_like_wasm && self.os != "emscripten") { - assert_eq!(self.relocation_model, RelocModel::Pic); - } - if self.position_independent_executables { - assert_eq!(self.relocation_model, RelocModel::Pic); - } - // The UEFI targets do not support dynamic linking but still require PIC (#101377). - if self.relocation_model == RelocModel::Pic && self.os != "uefi" { - assert!(self.dynamic_linking || self.position_independent_executables); - } - if self.static_position_independent_executables { - assert!(self.position_independent_executables); - } - if self.position_independent_executables { - assert!(self.executables); - } - - // Check crt static stuff - if self.crt_static_default || self.crt_static_allows_dylibs { - assert!(self.crt_static_respected); - } - - // Check that RISC-V targets always specify which ABI they use. - match &*self.arch { - "riscv32" => { - assert_matches!(&*self.llvm_abiname, "ilp32" | "ilp32f" | "ilp32d" | "ilp32e") - } - "riscv64" => { - // Note that the `lp64e` is still unstable as it's not (yet) part of the ELF psABI. - assert_matches!(&*self.llvm_abiname, "lp64" | "lp64f" | "lp64d" | "lp64q" | "lp64e") - } - _ => {} - } - } - - // Add your target to the whitelist if it has `std` library - // and you certainly want "unknown" for the OS name. - fn can_use_os_unknown(&self) -> bool { - self.llvm_target == "wasm32-unknown-unknown" - || self.llvm_target == "wasm64-unknown-unknown" - || (self.env == "sgx" && self.vendor == "fortanix") - } -} diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 112eb862663..67c047dddfc 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -228,6 +228,12 @@ const AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("rcpc3", Unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]), // FEAT_RDM ("rdm", Stable, &["neon"]), + // This is needed for inline assembly, but shouldn't be stabilized as-is + // since it should be enabled globally using -Zfixed-x18, not + // #[target_feature]. + // Note that cfg(target_feature = "reserve-x18") is currently not set for + // targets that reserve x18 by default. + ("reserve-x18", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_SB ("sb", Stable, &[]), // FEAT_SHA1 & FEAT_SHA256 diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index c7ad14ac0bf..f856a8d7abb 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -52,7 +52,9 @@ use std::{cmp, fmt, iter}; use rustc_abi::ExternAbi; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_errors::{Applicability, Diag, DiagStyledString, IntoDiagArg, StringPart, pluralize}; +use rustc_errors::{ + Applicability, Diag, DiagStyledString, IntoDiagArg, MultiSpan, StringPart, pluralize, +}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -67,6 +69,7 @@ use rustc_middle::ty::{ self, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::{BytePos, DesugaringKind, Pos, Span, sym}; use tracing::{debug, instrument}; @@ -211,7 +214,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } /// Adds a note if the types come from similarly named crates - fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) { + fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) -> bool { + // FIXME(estebank): unify with `report_similar_impl_candidates`. The message is similar, + // even if the logic needed to detect the case is very different. use hir::def_id::CrateNum; use rustc_hir::definitions::DisambiguatedDefPathData; use ty::GenericArg; @@ -285,7 +290,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId| { + let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId, ty: &str| -> bool { // Only report definitions from different crates. If both definitions // are from a local module we could have false positives, e.g. // let _ = [{struct Foo; Foo}, {struct Foo; Foo}]; @@ -297,24 +302,112 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // We compare strings because DefPath can be different // for imported and non-imported crates + let expected_str = self.tcx.def_path_str(did1); + let found_str = self.tcx.def_path_str(did2); + let Ok(expected_abs) = abs_path(did1) else { return false }; + let Ok(found_abs) = abs_path(did2) else { return false }; let same_path = || -> Result<_, PrintError> { - Ok(self.tcx.def_path_str(did1) == self.tcx.def_path_str(did2) - || abs_path(did1)? == abs_path(did2)?) + Ok(expected_str == found_str || expected_abs == found_abs) + }; + // We want to use as unique a type path as possible. If both types are "locally + // known" by the same name, we use the "absolute path" which uses the original + // crate name instead. + let (expected, found) = if expected_str == found_str { + (expected_abs.join("::"), found_abs.join("::")) + } else { + (expected_str.clone(), found_str.clone()) }; if same_path().unwrap_or(false) { - let crate_name = self.tcx.crate_name(did1.krate); - let msg = if did1.is_local() || did2.is_local() { + // We've displayed "expected `a::b`, found `a::b`". We add context to + // differentiate the different cases where that might happen. + let expected_crate_name = self.tcx.crate_name(did1.krate); + let found_crate_name = self.tcx.crate_name(did2.krate); + let same_crate = expected_crate_name == found_crate_name; + let expected_sp = self.tcx.def_span(did1); + let found_sp = self.tcx.def_span(did2); + + let both_direct_dependencies = if !did1.is_local() + && !did2.is_local() + && let Some(data1) = self.tcx.extern_crate(did1.krate) + && let Some(data2) = self.tcx.extern_crate(did2.krate) + && data1.dependency_of == LOCAL_CRATE + && data2.dependency_of == LOCAL_CRATE + { + // If both crates are directly depended on, we don't want to mention that + // in the final message, as it is redundant wording. + // We skip the case of semver trick, where one version of the local crate + // depends on another version of itself by checking that both crates at play + // are not the current one. + true + } else { + false + }; + + let mut span: MultiSpan = vec![expected_sp, found_sp].into(); + span.push_span_label( + self.tcx.def_span(did1), + format!("this is the expected {ty} `{expected}`"), + ); + span.push_span_label( + self.tcx.def_span(did2), + format!("this is the found {ty} `{found}`"), + ); + for def_id in [did1, did2] { + let crate_name = self.tcx.crate_name(def_id.krate); + if !def_id.is_local() + && let Some(data) = self.tcx.extern_crate(def_id.krate) + { + let descr = if same_crate { + "one version of".to_string() + } else { + format!("one {ty} comes from") + }; + let dependency = if both_direct_dependencies { + if let rustc_session::cstore::ExternCrateSource::Extern(def_id) = + data.src + && let Some(name) = self.tcx.opt_item_name(def_id) + { + format!(", which is renamed locally to `{name}`") + } else { + String::new() + } + } else if data.dependency_of == LOCAL_CRATE { + ", as a direct dependency of the current crate".to_string() + } else { + let dep = self.tcx.crate_name(data.dependency_of); + format!(", as a dependency of crate `{dep}`") + }; + span.push_span_label( + data.span, + format!("{descr} crate `{crate_name}` used here{dependency}"), + ); + } + } + let msg = if (did1.is_local() || did2.is_local()) && same_crate { + format!( + "the crate `{expected_crate_name}` is compiled multiple times, \ + possibly with different configurations", + ) + } else if same_crate { format!( - "the crate `{crate_name}` is compiled multiple times, possibly with different configurations" + "two different versions of crate `{expected_crate_name}` are being \ + used; two types coming from two different versions of the same crate \ + are different types even if they look the same", ) } else { format!( - "perhaps two different versions of crate `{crate_name}` are being used?" + "two types coming from two different crates are different types even \ + if they look the same", ) }; - err.note(msg); + err.span_note(span, msg); + if same_crate { + err.help("you can use `cargo tree` to explore your dependency tree"); + } + return true; } } + false }; match terr { TypeError::Sorts(ref exp_found) => { @@ -323,14 +416,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) = (exp_found.expected.kind(), exp_found.found.kind()) { - report_path_match(err, exp_adt.did(), found_adt.did()); + return report_path_match(err, exp_adt.did(), found_adt.did(), "type"); } } TypeError::Traits(ref exp_found) => { - report_path_match(err, exp_found.expected, exp_found.found); + return report_path_match(err, exp_found.expected, exp_found.found, "trait"); } _ => (), // FIXME(#22750) handle traits and stuff } + false } fn note_error_origin( @@ -1409,6 +1503,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { label_or_note(span, terr.to_string(self.tcx)); } + if self.check_and_note_conflicting_crates(diag, terr) { + return; + } + if let Some((expected, found, path)) = expected_found { let (expected_label, found_label, exp_found) = match exp_found { Mismatch::Variable(ef) => ( @@ -1470,15 +1568,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { |prim: Ty<'tcx>, shadow: Ty<'tcx>, defid: DefId, diag: &mut Diag<'_>| { let name = shadow.sort_string(self.tcx); diag.note(format!( - "{prim} and {name} have similar names, but are actually distinct types" + "`{prim}` and {name} have similar names, but are actually distinct types" + )); + diag.note(format!( + "one `{prim}` is a primitive defined by the language", )); - diag.note(format!("{prim} is a primitive defined by the language")); let def_span = self.tcx.def_span(defid); let msg = if defid.is_local() { - format!("{name} is defined in the current crate") + format!("the other {name} is defined in the current crate") } else { let crate_name = self.tcx.crate_name(defid.krate); - format!("{name} is defined in crate `{crate_name}`") + format!("the other {name} is defined in crate `{crate_name}`") }; diag.span_note(def_span, msg); }; @@ -1666,8 +1766,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - self.check_and_note_conflicting_crates(diag, terr); - self.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id()); if let Some(exp_found) = exp_found && let exp_found = TypeError::Sorts(exp_found) @@ -1792,12 +1890,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn suggest_specify_actual_length( &self, - terr: TypeError<'_>, - trace: &TypeTrace<'_>, + terr: TypeError<'tcx>, + trace: &TypeTrace<'tcx>, span: Span, ) -> Option<TypeErrorAdditionalDiags> { let hir = self.tcx.hir(); - let TypeError::FixedArraySize(sz) = terr else { + let TypeError::ArraySize(sz) = terr else { return None; }; let tykind = match self.tcx.hir_node_by_def_id(trace.cause.body_id) { @@ -1837,10 +1935,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; if let Some(tykind) = tykind && let hir::TyKind::Array(_, length) = tykind - && let hir::ArrayLen::Body(ct) = length + && let Some((scalar, ty)) = sz.found.try_to_scalar() + && ty == self.tcx.types.usize { - let span = ct.span(); - Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { span, length: sz.found }) + let span = length.span(); + Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { + span, + length: scalar.to_target_usize(&self.tcx).unwrap(), + }) } else { None } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs index 75054b22153..218d2e753ef 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs @@ -3,6 +3,7 @@ use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; +use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{self, Binder, Region, Ty, TyCtxt, TypeFoldable}; use rustc_span::Span; use tracing::instrument; @@ -83,7 +84,7 @@ pub fn find_param_with_region<'tcx>( // May return None; sometimes the tables are not yet populated. let ty = fn_sig.inputs()[index]; let mut found_anon_region = false; - let new_param_ty = tcx.fold_regions(ty, |r, _| { + let new_param_ty = fold_regions(tcx, ty, |r, _| { if r == anon_region { found_anon_region = true; replace_region diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 107ebe8adf0..4fb02f60943 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -794,7 +794,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { closure_def_id, found_kind, expected_kind, - "async ", + "Async", ); self.note_obligation_cause(&mut err, &obligation); self.point_at_returns_when_relevant(&mut err, &obligation); @@ -1731,6 +1731,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait"); for (sp, label) in [trait_def_id, other_trait_def_id] .iter() + // The current crate-version might depend on another version of the same crate + // (Think "semver-trick"). Do not call `extern_crate` in that case for the local + // crate as that doesn't make sense and ICEs (#133563). + .filter(|def_id| !def_id.is_local()) .filter_map(|def_id| self.tcx.extern_crate(def_id.krate)) .map(|data| { let dependency = if data.dependency_of == LOCAL_CRATE { @@ -1741,9 +1745,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; ( data.span, - format!( - "one version of crate `{crate_name}` is used here, as a {dependency}" - ), + format!("one version of crate `{crate_name}` used here, as a {dependency}"), ) }) { @@ -1803,24 +1805,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { StringPart::highlighted("cargo tree".to_string()), StringPart::normal("` to explore your dependency tree".to_string()), ]); - - // FIXME: this is a giant hack for the benefit of this specific diagnostic. Because - // we're so nested in method calls before the error gets emitted, bubbling a single bit - // flag informing the top level caller to stop adding extra detail to the diagnostic, - // would actually be harder to follow. So we do something naughty here: we consume the - // diagnostic, emit it and leave in its place a "delayed bug" that will continue being - // modified but won't actually be printed to end users. This *is not ideal*, but allows - // us to reduce the verbosity of an error that is already quite verbose and increase its - // specificity. Below we modify the main message as well, in a way that *could* break if - // the implementation of Diagnostics change significantly, but that would be caught with - // a make test failure when this diagnostic is tested. - err.primary_message(format!( - "{} because the trait comes from a different crate version", - err.messages[0].0.as_str().unwrap(), - )); - let diag = err.clone(); - err.downgrade_to_delayed_bug(); - self.tcx.dcx().emit_diagnostic(diag); return true; } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 7f42c932fcf..f10314c1c9e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -1,7 +1,7 @@ use std::iter; use std::path::PathBuf; -use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, MetaItemInner}; +use rustc_ast::{AttrArgs, AttrKind, Attribute, MetaItemInner}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; @@ -639,8 +639,7 @@ impl<'tcx> OnUnimplementedDirective { let report_span = match &item.args { AttrArgs::Empty => item.path.span, AttrArgs::Delimited(args) => args.dspan.entire(), - AttrArgs::Eq(eq_span, AttrArgsEq::Ast(expr)) => eq_span.to(expr.span), - AttrArgs::Eq(span, AttrArgsEq::Hir(expr)) => span.to(expr.span), + AttrArgs::Eq { eq_span, value } => eq_span.to(value.span()), }; if let Some(item_def_id) = item_def_id.as_local() { 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 5ad15feadff..94682f501a8 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2803,6 +2803,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ObligationCauseCode::WhereClause(item_def_id, span) | ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..) + | ObligationCauseCode::HostEffectInExpr(item_def_id, span, ..) if !span.is_dummy() => { if let ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, pos) = &cause_code { @@ -2966,7 +2967,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.help(help); } } - ObligationCauseCode::WhereClause(..) | ObligationCauseCode::WhereClauseInExpr(..) => { + ObligationCauseCode::WhereClause(..) + | ObligationCauseCode::WhereClauseInExpr(..) + | ObligationCauseCode::HostEffectInExpr(..) => { // We hold the `DefId` of the item introducing the obligation, but displaying it // doesn't add user usable information. It always point at an associated item. } @@ -3835,6 +3838,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && self.predicate_must_hold_modulo_regions(&Obligation::misc( tcx, expr.span, body_id, param_env, pred, )) + && expr.span.hi() != rcvr.span.hi() { err.span_suggestion_verbose( expr.span.with_lo(rcvr.span.hi()), @@ -4112,6 +4116,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // the expected is a projection that we need to resolve. // && let Some(tail_ty) = typeck_results.expr_ty_opt(expr) && expected_found.found.is_unit() + // FIXME: this happens with macro calls. Need to figure out why the stmt + // `println!();` doesn't include the `;` in its `Span`. (#133845) + // We filter these out to avoid ICEs with debug assertions on caused by + // empty suggestions. + && expr.span.hi() != stmt.span.hi() { err.span_suggestion_verbose( expr.span.shrink_to_hi().with_hi(stmt.span.hi()), @@ -5312,9 +5321,10 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>( }; let name = tcx.item_name(proj.projection_term.def_id); let mut predicates = generics.predicates.iter().peekable(); - let mut prev: Option<&hir::WhereBoundPredicate<'_>> = None; + let mut prev: Option<(&hir::WhereBoundPredicate<'_>, Span)> = None; while let Some(pred) = predicates.next() { - let hir::WherePredicate::BoundPredicate(pred) = pred else { + let curr_span = pred.span; + let hir::WherePredicateKind::BoundPredicate(pred) = pred.kind else { continue; }; let mut bounds = pred.bounds.iter(); @@ -5340,8 +5350,8 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>( .iter() .filter(|p| { matches!( - p, - hir::WherePredicate::BoundPredicate(p) + p.kind, + hir::WherePredicateKind::BoundPredicate(p) if hir::PredicateOrigin::WhereClause == p.origin ) }) @@ -5351,20 +5361,21 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>( // There's only one `where` bound, that needs to be removed. Remove the whole // `where` clause. generics.where_clause_span - } else if let Some(hir::WherePredicate::BoundPredicate(next)) = predicates.peek() + } else if let Some(next_pred) = predicates.peek() + && let hir::WherePredicateKind::BoundPredicate(next) = next_pred.kind && pred.origin == next.origin { // There's another bound, include the comma for the current one. - pred.span.until(next.span) - } else if let Some(prev) = prev + curr_span.until(next_pred.span) + } else if let Some((prev, prev_span)) = prev && pred.origin == prev.origin { // Last bound, try to remove the previous comma. - prev.span.shrink_to_hi().to(pred.span) + prev_span.shrink_to_hi().to(curr_span) } else if pred.origin == hir::PredicateOrigin::WhereClause { - pred.span.with_hi(generics.where_clause_span.hi()) + curr_span.with_hi(generics.where_clause_span.hi()) } else { - pred.span + curr_span }; err.span_suggestion_verbose( @@ -5417,7 +5428,7 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>( ); } } - prev = Some(pred); + prev = Some((pred, curr_span)); } } diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 43244eb5dcb..ee708564a80 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -35,7 +35,7 @@ impl<'tcx> InferCtxt<'tcx> { // FIXME(#132279): This should be removed as it causes us to incorrectly // handle opaques in their defining scope. if !(param_env, ty).has_infer() { - return ty.is_copy_modulo_regions(self.tcx, self.typing_env(param_env)); + return self.tcx.type_is_copy_modulo_regions(self.typing_env(param_env), ty); } let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None); diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 09bba24ba61..97cde67799c 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -205,7 +205,9 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< // transmute checking and polymorphic MIR optimizations could // get a result which isn't correct for all monomorphizations. match self.typing_mode() { - TypingMode::Coherence | TypingMode::Analysis { .. } => false, + TypingMode::Coherence + | TypingMode::Analysis { .. } + | TypingMode::PostBorrowckAnalysis { .. } => false, TypingMode::PostAnalysis => { let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref); !poly_trait_ref.still_further_specializable() diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 311dc214de6..2b2623a050e 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -199,6 +199,10 @@ where errors } + fn has_pending_obligations(&self) -> bool { + !self.obligations.pending.is_empty() || !self.obligations.overflowed.is_empty() + } + fn pending_obligations(&self) -> PredicateObligations<'tcx> { self.obligations.clone_pending() } @@ -346,12 +350,21 @@ fn find_best_leaf_obligation<'tcx>( consider_ambiguities: bool, ) -> PredicateObligation<'tcx> { let obligation = infcx.resolve_vars_if_possible(obligation.clone()); + // FIXME: we use a probe here as the `BestObligation` visitor does not + // check whether it uses candidates which get shadowed by where-bounds. + // + // We should probably fix the visitor to not do so instead, as this also + // means the leaf obligation may be incorrect. infcx - .visit_proof_tree(obligation.clone().into(), &mut BestObligation { - obligation: obligation.clone(), - consider_ambiguities, + .fudge_inference_if_ok(|| { + infcx + .visit_proof_tree(obligation.clone().into(), &mut BestObligation { + obligation: obligation.clone(), + consider_ambiguities, + }) + .break_value() + .ok_or(()) }) - .break_value() .unwrap_or(obligation) } diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 78e92e60b23..c9297027519 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -18,7 +18,6 @@ use rustc_middle::ty::{ TypeVisitableExt, TypeVisitor, TypingMode, Upcast, }; use rustc_span::Span; -use rustc_span::symbol::Symbol; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -150,8 +149,8 @@ fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> .predicates .iter() .filter_map(|pred| { - match pred { - hir::WherePredicate::BoundPredicate(pred) + match pred.kind { + hir::WherePredicateKind::BoundPredicate(pred) if pred.bounded_ty.hir_id.owner.to_def_id() == trait_def_id => { // Fetch spans for trait bounds that are Sized: @@ -329,10 +328,7 @@ pub fn dyn_compatibility_violations_for_assoc_item( .collect(), // Associated types can only be dyn-compatible if they have `Self: Sized` bounds. ty::AssocKind::Type => { - if !tcx.features().generic_associated_types_extended() - && !tcx.generics_of(item.def_id).is_own_empty() - && !item.is_impl_trait_in_trait() - { + if !tcx.generics_of(item.def_id).is_own_empty() && !item.is_impl_trait_in_trait() { vec![DynCompatibilityViolation::GAT(item.name, item.ident(tcx).span)] } else { // We will permit associated types if they are explicitly mentioned in the trait object. @@ -679,7 +675,7 @@ fn receiver_is_dispatchable<'tcx>( // FIXME(mikeyhew) this is a total hack. Once dyn_compatible_for_dispatch is stabilized, we can // replace this with `dyn Trait` let unsized_self_ty: Ty<'tcx> = - Ty::new_param(tcx, u32::MAX, Symbol::intern("RustaceansAreAwesome")); + Ty::new_param(tcx, u32::MAX, rustc_span::sym::RustaceansAreAwesome); // `Receiver[Self => U]` let unsized_receiver_ty = diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 03e483f555d..7529ee128f5 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -213,6 +213,10 @@ where } } + fn has_pending_obligations(&self) -> bool { + self.predicates.has_pending_obligations() + } + fn pending_obligations(&self) -> PredicateObligations<'tcx> { self.predicates.map_pending_obligations(|o| o.obligation.clone()) } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 7d6c38c11f3..069fab6a6e6 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -551,8 +551,18 @@ pub fn try_evaluate_const<'tcx>( | ty::ConstKind::Placeholder(_) | ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers), ty::ConstKind::Unevaluated(uv) => { - // Postpone evaluation of constants that depend on generic parameters or inference variables. - let (args, param_env) = if tcx.features().generic_const_exprs() + // Postpone evaluation of constants that depend on generic parameters or + // inference variables. + // + // We use `TypingMode::PostAnalysis` here which is not *technically* correct + // to be revealing opaque types here as borrowcheck has not run yet. However, + // CTFE itself uses `TypingMode::PostAnalysis` unconditionally even during + // typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). + // As a result we always use a revealed env when resolving the instance to evaluate. + // + // FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself + // instead of having this logic here + let (args, typing_env) = if tcx.features().generic_const_exprs() && uv.has_non_region_infer() { // `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause @@ -568,13 +578,17 @@ pub fn try_evaluate_const<'tcx>( // the generic arguments provided for it, then we should *not* attempt to evaluate it. return Err(EvaluateConstErr::HasGenericsOrInfers); } else { - (replace_param_and_infer_args_with_placeholder(tcx, uv.args), param_env) + let args = replace_param_and_infer_args_with_placeholder(tcx, uv.args); + let typing_env = infcx + .typing_env(tcx.erase_regions(param_env)) + .with_post_analysis_normalized(tcx); + (args, typing_env) } } Err(_) | Ok(None) => { let args = GenericArgs::identity_for_item(tcx, uv.def); - let param_env = tcx.param_env(uv.def); - (args, param_env) + let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def); + (args, typing_env) } } } else if tcx.def_kind(uv.def) == DefKind::AnonConst && uv.has_non_region_infer() { @@ -593,27 +607,20 @@ pub fn try_evaluate_const<'tcx>( ); let args = GenericArgs::identity_for_item(tcx, uv.def); - let param_env = tcx.param_env(uv.def); - (args, param_env) + let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def); + (args, typing_env) } else { // FIXME: This codepath is reachable under `associated_const_equality` and in the // future will be reachable by `min_generic_const_args`. We should handle inference // variables and generic parameters properly instead of doing nothing. - (uv.args, param_env) + let typing_env = infcx + .typing_env(tcx.erase_regions(param_env)) + .with_post_analysis_normalized(tcx); + (uv.args, typing_env) }; let uv = ty::UnevaluatedConst::new(uv.def, args); - // It's not *technically* correct to be revealing opaque types here as borrowcheck has - // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even - // during typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). - // As a result we always use a revealed env when resolving the instance to evaluate. - // - // FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself - // instead of having this logic here - let typing_env = - tcx.erase_regions(infcx.typing_env(param_env)).with_post_analysis_normalized(tcx); let erased_uv = tcx.erase_regions(uv); - use rustc_middle::mir::interpret::ErrorHandled; match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, DUMMY_SP) { Ok(Ok(val)) => Ok(ty::Const::new_value( diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 4d3d8c66e62..ad62b456ad4 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -7,6 +7,7 @@ use rustc_infer::traits::{ FromSolverError, Normalized, Obligation, PredicateObligations, TraitEngine, }; use rustc_macros::extension; +use rustc_middle::span_bug; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, TypeVisitableExt, @@ -63,10 +64,18 @@ impl<'tcx> At<'_, 'tcx> { if self.infcx.next_trait_solver() { crate::solve::deeply_normalize(self, value) } else { + if fulfill_cx.has_pending_obligations() { + let pending_obligations = fulfill_cx.pending_obligations(); + span_bug!( + pending_obligations[0].cause.span, + "deeply_normalize should not be called with pending obligations: \ + {pending_obligations:#?}" + ); + } let value = self .normalize(value) .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); - let errors = fulfill_cx.select_where_possible(self.infcx); + let errors = fulfill_cx.select_all_or_error(self.infcx); let value = self.infcx.resolve_vars_if_possible(value); if errors.is_empty() { Ok(value) } else { Err(errors) } } @@ -118,9 +127,10 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>( // Opaques are treated as rigid outside of `TypingMode::PostAnalysis`, // so we can ignore those. match infcx.typing_mode() { - TypingMode::Coherence | TypingMode::Analysis { defining_opaque_types: _ } => { - flags.remove(ty::TypeFlags::HAS_TY_OPAQUE) - } + // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis + TypingMode::Coherence + | TypingMode::Analysis { .. } + | TypingMode::PostBorrowckAnalysis { .. } => flags.remove(ty::TypeFlags::HAS_TY_OPAQUE), TypingMode::PostAnalysis => {} } @@ -213,9 +223,10 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx ty::Opaque => { // Only normalize `impl Trait` outside of type inference, usually in codegen. match self.selcx.infcx.typing_mode() { - TypingMode::Coherence | TypingMode::Analysis { defining_opaque_types: _ } => { - ty.super_fold_with(self) - } + // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis + TypingMode::Coherence + | TypingMode::Analysis { .. } + | TypingMode::PostBorrowckAnalysis { .. } => ty.super_fold_with(self), TypingMode::PostAnalysis => { let recursion_limit = self.cx().recursion_limit(); if !recursion_limit.value_within_limit(self.depth) { diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 2864f277df5..49c34550f8e 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -14,7 +14,7 @@ use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::{BuiltinImplSource, ImplSource, ImplSourceUserDefinedData}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt}; +use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Term, Ty, TyCtxt, TypingMode, Upcast}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::sym; @@ -179,35 +179,11 @@ pub(super) fn poly_project_and_unify_term<'cx, 'tcx>( ) -> ProjectAndUnifyResult<'tcx> { let infcx = selcx.infcx; let r = infcx.commit_if_ok(|_snapshot| { - let old_universe = infcx.universe(); let placeholder_predicate = infcx.enter_forall_and_leak_universe(obligation.predicate); - let new_universe = infcx.universe(); let placeholder_obligation = obligation.with(infcx.tcx, placeholder_predicate); match project_and_unify_term(selcx, &placeholder_obligation) { ProjectAndUnifyResult::MismatchedProjectionTypes(e) => Err(e), - ProjectAndUnifyResult::Holds(obligations) - if old_universe != new_universe - && selcx.tcx().features().generic_associated_types_extended() => - { - // If the `generic_associated_types_extended` feature is active, then we ignore any - // obligations references lifetimes from any universe greater than or equal to the - // universe just created. Otherwise, we can end up with something like `for<'a> I: 'a`, - // which isn't quite what we want. Ideally, we want either an implied - // `for<'a where I: 'a> I: 'a` or we want to "lazily" check these hold when we - // instantiate concrete regions. There is design work to be done here; until then, - // however, this allows experimenting potential GAT features without running into - // well-formedness issues. - let new_obligations = obligations - .into_iter() - .filter(|obligation| { - let mut visitor = MaxUniverse::new(); - obligation.predicate.visit_with(&mut visitor); - visitor.max_universe() < new_universe - }) - .collect(); - Ok(ProjectAndUnifyResult::Holds(new_obligations)) - } other => Ok(other), } }); @@ -975,7 +951,9 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // transmute checking and polymorphic MIR optimizations could // get a result which isn't correct for all monomorphizations. match selcx.infcx.typing_mode() { - TypingMode::Coherence | TypingMode::Analysis { .. } => { + TypingMode::Coherence + | TypingMode::Analysis { .. } + | TypingMode::PostBorrowckAnalysis { .. } => { debug!( assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id), ?obligation.predicate, diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 22cfbb2c840..2ef9d5421ba 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -216,9 +216,9 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> { ty::Opaque => { // Only normalize `impl Trait` outside of type inference, usually in codegen. match self.infcx.typing_mode() { - TypingMode::Coherence | TypingMode::Analysis { defining_opaque_types: _ } => { - ty.try_super_fold_with(self)? - } + TypingMode::Coherence + | TypingMode::Analysis { .. } + | TypingMode::PostBorrowckAnalysis { .. } => ty.try_super_fold_with(self)?, TypingMode::PostAnalysis => { let args = data.args.try_fold_with(self)?; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index c6e41e57f0c..fe47e837dfb 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -59,12 +59,16 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, ) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> { - let normalize_op = |ty| { - let ty = ocx.normalize(&ObligationCause::dummy(), param_env, ty); + let normalize_op = |ty| -> Result<_, NoSolution> { + // We must normalize the type so we can compute the right outlives components. + // for example, if we have some constrained param type like `T: Trait<Out = U>`, + // and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`. + let ty = ocx + .deeply_normalize(&ObligationCause::dummy(), param_env, ty) + .map_err(|_| NoSolution)?; if !ocx.select_all_or_error().is_empty() { return Err(NoSolution); } - let ty = ocx.infcx.resolve_vars_if_possible(ty); let ty = OpportunisticRegionResolver::new(&ocx.infcx).fold_ty(ty); Ok(ty) }; 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 32b4567aba4..3e2c8467d32 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -18,6 +18,7 @@ use rustc_infer::traits::{ use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{self, ToPolyTraitRef, Ty, TypeVisitableExt, TypingMode}; use rustc_middle::{bug, span_bug}; +use rustc_type_ir::Interner; use tracing::{debug, instrument, trace}; use super::SelectionCandidate::*; @@ -794,6 +795,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Never | ty::Tuple(_) | ty::CoroutineWitness(..) => { + use rustc_type_ir::inherent::*; + + // Only consider auto impls of unsafe traits when there are + // no unsafe fields. + if self.tcx().trait_is_unsafe(def_id) && self_ty.has_unsafe_fields() { + return; + } + // Only consider auto impls if there are no manual impls for the root of `self_ty`. // // For example, we only consider auto candidates for `&i32: Auto` if no explicit impl diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 712856e6a8f..2fbe2e1e323 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -16,9 +16,7 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk}; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; -use rustc_middle::ty::{ - self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, Ty, TyCtxt, Upcast, -}; +use rustc_middle::ty::{self, GenericArgsRef, ToPolyTraitRef, Ty, TyCtxt, Upcast}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; use tracing::{debug, instrument}; @@ -626,7 +624,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { for assoc_type in assoc_types { let defs: &ty::Generics = tcx.generics_of(assoc_type); - if !defs.own_params.is_empty() && !tcx.features().generic_associated_types_extended() { + if !defs.own_params.is_empty() { tcx.dcx().span_delayed_bug( obligation.cause.span, "GATs in trait object shouldn't have been considered", @@ -638,60 +636,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // higher-ranked things. // Prevent, e.g., `dyn Iterator<Item = str>`. for bound in self.tcx().item_bounds(assoc_type).transpose_iter() { - let arg_bound = if defs.is_empty() { - bound.instantiate(tcx, trait_predicate.trait_ref.args) - } else { - let mut args = smallvec::SmallVec::with_capacity(defs.count()); - args.extend(trait_predicate.trait_ref.args.iter()); - let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> = - smallvec::SmallVec::with_capacity( - bound.skip_binder().kind().bound_vars().len() + defs.count(), - ); - bound_vars.extend(bound.skip_binder().kind().bound_vars().into_iter()); - GenericArgs::fill_single(&mut args, defs, &mut |param, _| match param.kind { - GenericParamDefKind::Type { .. } => { - let kind = ty::BoundTyKind::Param(param.def_id, param.name); - let bound_var = ty::BoundVariableKind::Ty(kind); - bound_vars.push(bound_var); - Ty::new_bound(tcx, ty::INNERMOST, ty::BoundTy { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind, - }) - .into() - } - GenericParamDefKind::Lifetime => { - let kind = ty::BoundRegionKind::Named(param.def_id, param.name); - let bound_var = ty::BoundVariableKind::Region(kind); - bound_vars.push(bound_var); - ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind, - }) - .into() - } - GenericParamDefKind::Const { .. } => { - let bound_var = ty::BoundVariableKind::Const; - bound_vars.push(bound_var); - ty::Const::new_bound( - tcx, - ty::INNERMOST, - ty::BoundVar::from_usize(bound_vars.len() - 1), - ) - .into() - } - }); - let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars); - let assoc_ty_args = tcx.mk_args(&args); - let bound = - bound.map_bound(|b| b.kind().skip_binder()).instantiate(tcx, assoc_ty_args); - ty::Binder::bind_with_vars(bound, bound_vars).upcast(tcx) - }; let normalized_bound = normalize_with_depth_to( self, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, - arg_bound, + bound.instantiate(tcx, trait_predicate.trait_ref.args), &mut nested, ); nested.push(obligation.with(tcx, normalized_bound)); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 3b64a47181a..d362866cbc3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -25,6 +25,7 @@ use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds}; pub use rustc_middle::traits::select::*; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::TypeErrorToStringExt; +use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths}; use rustc_middle::ty::{ self, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, @@ -1399,10 +1400,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { where OP: FnOnce(&mut Self) -> R, { - let (result, dep_node) = - self.tcx().dep_graph.with_anon_task(self.tcx(), dep_kinds::TraitSelect, || op(self)); - self.tcx().dep_graph.read_index(dep_node); - (result, dep_node) + self.tcx().dep_graph.with_anon_task(self.tcx(), dep_kinds::TraitSelect, || op(self)) } /// filter_impls filters candidates that have a positive impl for a negative @@ -1470,7 +1468,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let obligation = &stack.obligation; match self.infcx.typing_mode() { TypingMode::Coherence => {} - TypingMode::Analysis { .. } | TypingMode::PostAnalysis => return Ok(()), + TypingMode::Analysis { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => return Ok(()), } debug!("is_knowable()"); @@ -1517,6 +1517,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { TypingMode::Analysis { defining_opaque_types } => { defining_opaque_types.is_empty() || !pred.has_opaque_types() } + // The hidden types of `defined_opaque_types` is not local to the current + // inference context, so we can freely move this to the global cache. + TypingMode::PostBorrowckAnalysis { .. } => true, // The global cache is only used if there are no opaque types in // the defining scope or we're outside of analysis. // @@ -1537,14 +1540,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if self.can_use_global_caches(param_env, cache_fresh_trait_pred) { if let Some(res) = tcx.selection_cache.get(&(infcx.typing_env(param_env), pred), tcx) { - Some(res) - } else { - debug_assert_eq!(infcx.selection_cache.get(&(param_env, pred), tcx), None); - None + return Some(res); + } else if cfg!(debug_assertions) { + match infcx.selection_cache.get(&(param_env, pred), tcx) { + None | Some(Err(Overflow(OverflowError::Canonical))) => {} + res => bug!("unexpected local cache result: {res:?}"), + } } - } else { - infcx.selection_cache.get(&(param_env, pred), tcx) } + + // Subtle: we need to check the local cache even if we're able to use the + // global cache as we don't cache overflow in the global cache but need to + // cache it as otherwise rustdoc hangs when compiling diesel. + infcx.selection_cache.get(&(param_env, pred), tcx) } /// Determines whether can we safely cache the result @@ -3209,7 +3217,7 @@ fn bind_coroutine_hidden_types_above<'tcx>( // Only remap erased regions if we use them. if considering_regions { bty = bty.map_bound(|ty| { - tcx.fold_regions(ty, |r, current_depth| match r.kind() { + fold_regions(tcx, ty, |r, current_depth| match r.kind() { ty::ReErased => { let br = ty::BoundRegion { var: ty::BoundVar::from_u32(counter), diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index a9cd705465e..1430cfae51f 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -15,6 +15,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::codes::*; use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_infer::traits::Obligation; use rustc_middle::bug; use rustc_middle::query::LocalCrate; use rustc_middle::ty::print::PrintTraitRefExt as _; @@ -224,21 +225,30 @@ pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool tcx.features().specialization() || tcx.features().min_specialization() } -/// Is `impl1` a specialization of `impl2`? +/// Is `specializing_impl_def_id` a specialization of `parent_impl_def_id`? /// -/// Specialization is determined by the sets of types to which the impls apply; -/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies -/// to. +/// For every type that could apply to `specializing_impl_def_id`, we prove that +/// the `parent_impl_def_id` also applies (i.e. it has a valid impl header and +/// its where-clauses hold). +/// +/// For the purposes of const traits, we also check that the specializing +/// impl is not more restrictive than the parent impl. That is, if the +/// `parent_impl_def_id` is a const impl (conditionally based off of some `~const` +/// bounds), then `specializing_impl_def_id` must also be const for the same +/// set of types. #[instrument(skip(tcx), level = "debug")] -pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool { +pub(super) fn specializes( + tcx: TyCtxt<'_>, + (specializing_impl_def_id, parent_impl_def_id): (DefId, DefId), +) -> bool { // We check that the specializing impl comes from a crate that has specialization enabled, // or if the specializing impl is marked with `allow_internal_unstable`. // // We don't really care if the specialized impl (the parent) is in a crate that has // specialization enabled, since it's not being specialized, and it's already been checked // for coherence. - if !tcx.specialization_enabled_in(impl1_def_id.krate) { - let span = tcx.def_span(impl1_def_id); + if !tcx.specialization_enabled_in(specializing_impl_def_id.krate) { + let span = tcx.def_span(specializing_impl_def_id); if !span.allows_unstable(sym::specialization) && !span.allows_unstable(sym::min_specialization) { @@ -246,7 +256,7 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, } } - let impl1_trait_header = tcx.impl_trait_header(impl1_def_id).unwrap(); + let specializing_impl_trait_header = tcx.impl_trait_header(specializing_impl_def_id).unwrap(); // We determine whether there's a subset relationship by: // @@ -261,27 +271,123 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, // See RFC 1210 for more details and justification. // Currently we do not allow e.g., a negative impl to specialize a positive one - if impl1_trait_header.polarity != tcx.impl_polarity(impl2_def_id) { + if specializing_impl_trait_header.polarity != tcx.impl_polarity(parent_impl_def_id) { return false; } - // create a parameter environment corresponding to an identity instantiation of impl1, - // i.e. the most generic instantiation of impl1. - let param_env = tcx.param_env(impl1_def_id); + // create a parameter environment corresponding to an identity instantiation of the specializing impl, + // i.e. the most generic instantiation of the specializing impl. + let param_env = tcx.param_env(specializing_impl_def_id); - // Create an infcx, taking the predicates of impl1 as assumptions: + // Create an infcx, taking the predicates of the specializing impl as assumptions: let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - // Attempt to prove that impl2 applies, given all of the above. - fulfill_implication( - &infcx, + let specializing_impl_trait_ref = + specializing_impl_trait_header.trait_ref.instantiate_identity(); + let cause = &ObligationCause::dummy(); + debug!( + "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)", + param_env, specializing_impl_trait_ref, parent_impl_def_id + ); + + // Attempt to prove that the parent impl applies, given all of the above. + + let ocx = ObligationCtxt::new(&infcx); + let specializing_impl_trait_ref = ocx.normalize(cause, param_env, specializing_impl_trait_ref); + + if !ocx.select_all_or_error().is_empty() { + infcx.dcx().span_delayed_bug( + infcx.tcx.def_span(specializing_impl_def_id), + format!("failed to fully normalize {specializing_impl_trait_ref}"), + ); + return false; + } + + let parent_args = infcx.fresh_args_for_item(DUMMY_SP, parent_impl_def_id); + let parent_impl_trait_ref = ocx.normalize( + cause, param_env, - impl1_trait_header.trait_ref.instantiate_identity(), - impl1_def_id, - impl2_def_id, - &ObligationCause::dummy(), - ) - .is_ok() + infcx + .tcx + .impl_trait_ref(parent_impl_def_id) + .expect("expected source impl to be a trait impl") + .instantiate(infcx.tcx, parent_args), + ); + + // do the impls unify? If not, no specialization. + let Ok(()) = ocx.eq(cause, param_env, specializing_impl_trait_ref, parent_impl_trait_ref) + else { + return false; + }; + + // Now check that the source trait ref satisfies all the where clauses of the target impl. + // This is not just for correctness; we also need this to constrain any params that may + // only be referenced via projection predicates. + let predicates = ocx.normalize( + cause, + param_env, + infcx.tcx.predicates_of(parent_impl_def_id).instantiate(infcx.tcx, parent_args), + ); + let obligations = predicates_for_generics(|_, _| cause.clone(), param_env, predicates); + ocx.register_obligations(obligations); + + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + // no dice! + debug!( + "fulfill_implication: for impls on {:?} and {:?}, \ + could not fulfill: {:?} given {:?}", + specializing_impl_trait_ref, + parent_impl_trait_ref, + errors, + param_env.caller_bounds() + ); + return false; + } + + // If the parent impl is const, then the specializing impl must be const, + // and it must not be *more restrictive* than the parent impl (that is, + // it cannot be const in fewer cases than the parent impl). + if tcx.is_conditionally_const(parent_impl_def_id) { + if !tcx.is_conditionally_const(specializing_impl_def_id) { + return false; + } + + let const_conditions = ocx.normalize( + cause, + param_env, + infcx.tcx.const_conditions(parent_impl_def_id).instantiate(infcx.tcx, parent_args), + ); + ocx.register_obligations(const_conditions.into_iter().map(|(trait_ref, _)| { + Obligation::new( + infcx.tcx, + cause.clone(), + param_env, + trait_ref.to_host_effect_clause(infcx.tcx, ty::BoundConstness::Maybe), + ) + })); + + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + // no dice! + debug!( + "fulfill_implication: for impls on {:?} and {:?}, \ + could not fulfill: {:?} given {:?}", + specializing_impl_trait_ref, + parent_impl_trait_ref, + errors, + param_env.caller_bounds() + ); + return false; + } + } + + debug!( + "fulfill_implication: an impl for {:?} specializes {:?}", + specializing_impl_trait_ref, parent_impl_trait_ref + ); + + true } /// Query provider for `specialization_graph_of`. diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 437343b569c..c95b1641d1f 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -963,30 +963,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { /// bounds that must hold on the elided self type. These are derived /// from the declarations of `SomeTrait`, `Send`, and friends -- if /// they declare `trait SomeTrait : 'static`, for example, then -/// `'static` would appear in the list. The hard work is done by -/// `infer::required_region_bounds`, see that for more information. -pub fn object_region_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - existential_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, -) -> Vec<ty::Region<'tcx>> { - let predicates = existential_predicates.iter().filter_map(|predicate| { - if let ty::ExistentialPredicate::Projection(_) = predicate.skip_binder() { - None - } else { - Some(predicate.with_self_ty(tcx, tcx.types.trait_object_dummy_self)) - } - }); - - required_region_bounds(tcx, tcx.types.trait_object_dummy_self, predicates) -} - -/// Given a set of predicates that apply to an object type, returns -/// the region bounds that the (erased) `Self` type must -/// outlive. Precisely *because* the `Self` type is erased, the -/// parameter `erased_self_ty` must be supplied to indicate what type -/// has been used to represent `Self` in the predicates -/// themselves. This should really be a unique type; `FreshTy(0)` is a -/// popular choice. +/// `'static` would appear in the list. /// /// N.B., in some cases, particularly around higher-ranked bounds, /// this function returns a kind of conservative approximation. @@ -996,13 +973,14 @@ pub fn object_region_bounds<'tcx>( /// /// Requires that trait definitions have been processed so that we can /// elaborate predicates and walk supertraits. -#[instrument(skip(tcx, predicates), level = "debug", ret)] -pub(crate) fn required_region_bounds<'tcx>( +pub fn object_region_bounds<'tcx>( tcx: TyCtxt<'tcx>, - erased_self_ty: Ty<'tcx>, - predicates: impl Iterator<Item = ty::Clause<'tcx>>, + existential_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, ) -> Vec<ty::Region<'tcx>> { - assert!(!erased_self_ty.has_escaping_bound_vars()); + let erased_self_ty = tcx.types.trait_object_dummy_self; + + let predicates = + existential_predicates.iter().map(|predicate| predicate.with_self_ty(tcx, erased_self_ty)); traits::elaborate(tcx, predicates) .filter_map(|pred| { diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index 57225df0819..e5276e6d515 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -74,12 +74,21 @@ pub(crate) fn codegen_select_candidate<'tcx>( } let impl_source = infcx.resolve_vars_if_possible(impl_source); - let impl_source = infcx.tcx.erase_regions(impl_source); - if impl_source.has_infer() { - // Unused lifetimes on an impl get replaced with inference vars, but never resolved, - // causing the return value of a query to contain inference vars. We do not have a concept - // for this and will in fact ICE in stable hashing of the return value. So bail out instead. - infcx.tcx.dcx().has_errors().unwrap(); + let impl_source = tcx.erase_regions(impl_source); + if impl_source.has_non_region_infer() { + // Unused generic types or consts on an impl get replaced with inference vars, + // but never resolved, causing the return value of a query to contain inference + // vars. We do not have a concept for this and will in fact ICE in stable hashing + // of the return value. So bail out instead. + match impl_source { + ImplSource::UserDefined(impl_) => { + tcx.dcx().span_delayed_bug( + tcx.def_span(impl_.impl_def_id), + "this impl has unconstrained generic parameters", + ); + } + _ => unreachable!(), + } return Err(CodegenObligationError::FulfillmentError); } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index f19a567cd84..83463babc4f 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -319,38 +319,35 @@ pub(crate) mod rustc { ) -> Result<Self, Err> { assert!(def.is_enum()); - // Computes the variant of a given index. - let layout_of_variant = |index, encoding: Option<TagEncoding<VariantIdx>>| { - let tag = cx.tcx().tag_for_variant((cx.tcx().erase_regions(ty), index)); - let variant_def = Def::Variant(def.variant(index)); - let variant_layout = ty_variant(cx, (ty, layout), index); - Self::from_variant( - variant_def, - tag.map(|tag| (tag, index, encoding.unwrap())), - (ty, variant_layout), - layout.size, - cx, - ) - }; + // Computes the layout of a variant. + let layout_of_variant = + |index, encoding: Option<TagEncoding<VariantIdx>>| -> Result<Self, Err> { + let variant_layout = ty_variant(cx, (ty, layout), index); + if variant_layout.is_uninhabited() { + return Ok(Self::uninhabited()); + } + let tag = cx.tcx().tag_for_variant((cx.tcx().erase_regions(ty), index)); + let variant_def = Def::Variant(def.variant(index)); + Self::from_variant( + variant_def, + tag.map(|tag| (tag, index, encoding.unwrap())), + (ty, variant_layout), + layout.size, + cx, + ) + }; - // We consider three kinds of enums, each demanding a different - // treatment of their layout computation: - // 1. enums that are uninhabited ZSTs - // 2. enums that delegate their layout to a variant - // 3. enums with multiple variants match layout.variants() { - Variants::Single { .. } if layout.is_uninhabited() && layout.size == Size::ZERO => { - // The layout representation of uninhabited, ZST enums is - // defined to be like that of the `!` type, as opposed of a - // typical enum. Consequently, they cannot be descended into - // as if they typical enums. We therefore special-case this - // scenario and simply return an uninhabited `Tree`. - Ok(Self::uninhabited()) - } Variants::Single { index } => { - // `Variants::Single` on enums with variants denotes that - // the enum delegates its layout to the variant at `index`. - layout_of_variant(*index, None) + // Hilariously, `Single` is used even for 0-variant enums; + // `index` is just junk in that case. + if ty.ty_adt_def().unwrap().variants().is_empty() { + Ok(Self::uninhabited()) + } else { + // `Variants::Single` on enums with variants denotes that + // the enum delegates its layout to the variant at `index`. + layout_of_variant(*index, None) + } } Variants::Multiple { tag, tag_encoding, tag_field, .. } => { // `Variants::Multiple` denotes an enum with multiple @@ -369,7 +366,7 @@ pub(crate) mod rustc { }, )?; - return Ok(Self::def(Def::Adt(def)).then(variants)); + Ok(Self::def(Def::Adt(def)).then(variants)) } } } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index a5a9125c8b5..ae6d697794f 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -31,93 +31,51 @@ fn fn_sig_for_fn_abi<'tcx>( tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>, typing_env: ty::TypingEnv<'tcx>, -) -> ty::PolyFnSig<'tcx> { +) -> ty::FnSig<'tcx> { if let InstanceKind::ThreadLocalShim(..) = instance.def { - return ty::Binder::dummy(tcx.mk_fn_sig( + return tcx.mk_fn_sig( [], tcx.thread_local_ptr_ty(instance.def_id()), false, hir::Safety::Safe, rustc_abi::ExternAbi::Unadjusted, - )); + ); } let ty = instance.ty(tcx, typing_env); match *ty.kind() { - ty::FnDef(..) => { - // HACK(davidtwco,eddyb): This is a workaround for polymorphization considering - // parameters unused if they show up in the signature, but not in the `mir::Body` - // (i.e. due to being inside a projection that got normalized, see - // `tests/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping - // track of a polymorphization `ParamEnv` to allow normalizing later. - // - // We normalize the `fn_sig` again after instantiating at a later point. - let mut sig = match *ty.kind() { - ty::FnDef(def_id, args) => tcx - .fn_sig(def_id) - .map_bound(|fn_sig| { - tcx.normalize_erasing_regions( - ty::TypingEnv::non_body_analysis(tcx, def_id), - fn_sig, - ) - }) - .instantiate(tcx, args), - _ => unreachable!(), - }; + ty::FnDef(def_id, args) => { + let mut sig = tcx + .instantiate_bound_regions_with_erased(tcx.fn_sig(def_id).instantiate(tcx, args)); if let ty::InstanceKind::VTableShim(..) = instance.def { - // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. - sig = sig.map_bound(|mut sig| { - let mut inputs_and_output = sig.inputs_and_output.to_vec(); - inputs_and_output[0] = Ty::new_mut_ptr(tcx, inputs_and_output[0]); - sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); - sig - }); + let mut inputs_and_output = sig.inputs_and_output.to_vec(); + inputs_and_output[0] = Ty::new_mut_ptr(tcx, inputs_and_output[0]); + sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); } + sig } ty::Closure(def_id, args) => { - let sig = args.as_closure().sig(); - - let bound_vars = - tcx.mk_bound_variable_kinds_from_iter(sig.bound_vars().iter().chain(iter::once( - ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), - ))); - let br = ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind: ty::BoundRegionKind::ClosureEnv, - }; - let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); + let sig = tcx.instantiate_bound_regions_with_erased(args.as_closure().sig()); let env_ty = tcx.closure_env_ty( Ty::new_closure(tcx, def_id, args), args.as_closure().kind(), - env_region, + tcx.lifetimes.re_erased, ); - let sig = sig.skip_binder(); - ty::Binder::bind_with_vars( - tcx.mk_fn_sig( - iter::once(env_ty).chain(sig.inputs().iter().cloned()), - sig.output(), - sig.c_variadic, - sig.safety, - sig.abi, - ), - bound_vars, + tcx.mk_fn_sig( + iter::once(env_ty).chain(sig.inputs().iter().cloned()), + sig.output(), + sig.c_variadic, + sig.safety, + sig.abi, ) } ty::CoroutineClosure(def_id, args) => { let coroutine_ty = Ty::new_coroutine_closure(tcx, def_id, args); let sig = args.as_coroutine_closure().coroutine_closure_sig(); - let bound_vars = - tcx.mk_bound_variable_kinds_from_iter(sig.bound_vars().iter().chain(iter::once( - ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), - ))); - let br = ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind: ty::BoundRegionKind::ClosureEnv, - }; - let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); + // When this `CoroutineClosure` comes from a `ConstructCoroutineInClosureShim`, // make sure we respect the `target_kind` in that shim. // FIXME(async_closures): This shouldn't be needed, and we should be populating @@ -138,42 +96,32 @@ fn fn_sig_for_fn_abi<'tcx>( coroutine_ty } } else { - tcx.closure_env_ty(coroutine_ty, coroutine_kind, env_region) + tcx.closure_env_ty(coroutine_ty, coroutine_kind, tcx.lifetimes.re_erased) }; - let sig = sig.skip_binder(); - ty::Binder::bind_with_vars( - tcx.mk_fn_sig( - iter::once(env_ty).chain([sig.tupled_inputs_ty]), - sig.to_coroutine_given_kind_and_upvars( - tcx, - args.as_coroutine_closure().parent_args(), - tcx.coroutine_for_closure(def_id), - coroutine_kind, - env_region, - args.as_coroutine_closure().tupled_upvars_ty(), - args.as_coroutine_closure().coroutine_captures_by_ref_ty(), - ), - sig.c_variadic, - sig.safety, - sig.abi, + let sig = tcx.instantiate_bound_regions_with_erased(sig); + + tcx.mk_fn_sig( + iter::once(env_ty).chain([sig.tupled_inputs_ty]), + sig.to_coroutine_given_kind_and_upvars( + tcx, + args.as_coroutine_closure().parent_args(), + tcx.coroutine_for_closure(def_id), + coroutine_kind, + tcx.lifetimes.re_erased, + args.as_coroutine_closure().tupled_upvars_ty(), + args.as_coroutine_closure().coroutine_captures_by_ref_ty(), ), - bound_vars, + sig.c_variadic, + sig.safety, + sig.abi, ) } ty::Coroutine(did, args) => { let coroutine_kind = tcx.coroutine_kind(did).unwrap(); let sig = args.as_coroutine().sig(); - let bound_vars = tcx.mk_bound_variable_kinds_from_iter(iter::once( - ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), - )); - let br = ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind: ty::BoundRegionKind::ClosureEnv, - }; - - let env_ty = Ty::new_mut_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), ty); + let env_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, ty); let pin_did = tcx.require_lang_item(LangItem::Pin, None); let pin_adt_ref = tcx.adt_def(pin_did); @@ -268,7 +216,7 @@ fn fn_sig_for_fn_abi<'tcx>( } }; - let fn_sig = if let Some(resume_ty) = resume_ty { + if let Some(resume_ty) = resume_ty { tcx.mk_fn_sig( [env_ty, resume_ty], ret_ty, @@ -285,8 +233,7 @@ fn fn_sig_for_fn_abi<'tcx>( hir::Safety::Safe, rustc_abi::ExternAbi::Rust, ) - }; - ty::Binder::bind_with_vars(fn_sig, bound_vars) + } } _ => bug!("unexpected type {:?} in Instance::fn_sig", ty), } @@ -335,8 +282,16 @@ fn fn_abi_of_fn_ptr<'tcx>( query: ty::PseudoCanonicalInput<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List<Ty<'tcx>>)>, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let ty::PseudoCanonicalInput { typing_env, value: (sig, extra_args) } = query; + let cx = LayoutCx::new(tcx, typing_env); - fn_abi_new_uncached(&cx, sig, extra_args, None, None, false) + fn_abi_new_uncached( + &cx, + tcx.instantiate_bound_regions_with_erased(sig), + extra_args, + None, + None, + false, + ) } fn fn_abi_of_instance<'tcx>( @@ -503,20 +458,30 @@ fn fn_abi_sanity_check<'tcx>( // This really shouldn't happen even for sized aggregates, since // `immediate_llvm_type` will use `layout.fields` to turn this Rust type into an // LLVM type. This means all sorts of Rust type details leak into the ABI. - // However wasm sadly *does* currently use this mode so we have to allow it -- - // but we absolutely shouldn't let any more targets do that. - // (Also see <https://github.com/rust-lang/rust/issues/115666>.) + // However wasm sadly *does* currently use this mode for it's "C" ABI so we + // have to allow it -- but we absolutely shouldn't let any more targets do + // that. (Also see <https://github.com/rust-lang/rust/issues/115666>.) // // The unstable abi `PtxKernel` also uses Direct for now. // It needs to switch to something else before stabilization can happen. // (See issue: https://github.com/rust-lang/rust/issues/117271) - assert!( - matches!(&*tcx.sess.target.arch, "wasm32" | "wasm64") - || matches!(spec_abi, ExternAbi::PtxKernel | ExternAbi::Unadjusted), - "`PassMode::Direct` for aggregates only allowed for \"unadjusted\" and \"ptx-kernel\" functions and on wasm\n\ - Problematic type: {:#?}", - arg.layout, - ); + // + // And finally the unadjusted ABI is ill specified and uses Direct for all + // args, but unfortunately we need it for calling certain LLVM intrinsics. + + match spec_abi { + ExternAbi::Unadjusted => {} + ExternAbi::PtxKernel => {} + ExternAbi::C { unwind: _ } + if matches!(&*tcx.sess.target.arch, "wasm32" | "wasm64") => {} + _ => { + panic!( + "`PassMode::Direct` for aggregates only allowed for \"unadjusted\" and \"ptx-kernel\" functions and on wasm\n\ + Problematic type: {:#?}", + arg.layout, + ); + } + } } } } @@ -567,7 +532,7 @@ fn fn_abi_sanity_check<'tcx>( #[tracing::instrument(level = "debug", skip(cx, caller_location, fn_def_id, force_thin_self_ptr))] fn fn_abi_new_uncached<'tcx>( cx: &LayoutCx<'tcx>, - sig: ty::PolyFnSig<'tcx>, + sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>], caller_location: Option<Ty<'tcx>>, fn_def_id: Option<DefId>, @@ -575,7 +540,7 @@ fn fn_abi_new_uncached<'tcx>( force_thin_self_ptr: bool, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let tcx = cx.tcx(); - let sig = tcx.normalize_erasing_late_bound_regions(cx.typing_env, sig); + let sig = tcx.normalize_erasing_regions(cx.typing_env, sig); let conv = conv_from_spec_abi(cx.tcx(), sig.abi, sig.c_variadic); diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 5cd10e90538..c4637f1293c 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -6,6 +6,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::bug; use rustc_middle::query::Providers; +use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; @@ -86,7 +87,8 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' } } // FIXME: This could use a real folder, I guess. - let remapped_wf_tys = tcx.fold_regions( + let remapped_wf_tys = fold_regions( + tcx, tcx.assumed_wf_types(fn_def_id.expect_local()).to_vec(), |region, _| { // If `region` is a `ReLateParam` that is captured by the diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 8798772e398..eb30169a7d9 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -149,7 +149,8 @@ fn resolve_associated_item<'tcx>( // get a result which isn't correct for all monomorphizations. match typing_env.typing_mode { ty::TypingMode::Coherence - | ty::TypingMode::Analysis { defining_opaque_types: _ } => false, + | ty::TypingMode::Analysis { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } => false, ty::TypingMode::PostAnalysis => !trait_ref.still_further_specializable(), } }; @@ -215,15 +216,15 @@ fn resolve_associated_item<'tcx>( let args = tcx.erase_regions(args); - // Check if we just resolved an associated `const` declaration from - // a `trait` to an associated `const` definition in an `impl`, where - // the definition in the `impl` has the wrong type (for which an - // error has already been/will be emitted elsewhere). - if leaf_def.item.kind == ty::AssocKind::Const - && trait_item_id != leaf_def.item.def_id + // We check that the impl item is compatible with the trait item + // because otherwise we may ICE in const eval due to type mismatches, + // signature incompatibilities, etc. + // NOTE: We could also only enforce this in `PostAnalysis`, which + // is what CTFE and MIR inlining would care about anyways. + if trait_item_id != leaf_def.item.def_id && let Some(leaf_def_item) = leaf_def.item.def_id.as_local() { - tcx.compare_impl_const((leaf_def_item, trait_item_id))?; + tcx.ensure().compare_impl_item(leaf_def_item)?; } Some(ty::Instance::new(leaf_def.item.def_id, args)) diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 66134b81b2a..0d656f1b63b 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -81,7 +81,7 @@ fn layout_of<'tcx>( record_layout_for_printing(&cx, layout); } - invariant::partially_check_layout(&cx, &layout); + invariant::layout_sanity_check(&cx, &layout); Ok(layout) } diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index 26ea81daf78..f39b87622f4 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -1,11 +1,11 @@ use std::assert_matches::assert_matches; -use rustc_abi::{BackendRepr, FieldsShape, Scalar, Size, Variants}; +use rustc_abi::{BackendRepr, FieldsShape, Scalar, Size, TagEncoding, Variants}; use rustc_middle::bug; use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout}; /// Enforce some basic invariants on layouts. -pub(super) fn partially_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { +pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { let tcx = cx.tcx(); // Type-level uninhabitedness should always imply ABI uninhabitedness. @@ -241,7 +241,17 @@ pub(super) fn partially_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLa check_layout_abi(cx, layout); - if let Variants::Multiple { variants, .. } = &layout.variants { + if let Variants::Multiple { variants, tag, tag_encoding, .. } = &layout.variants { + if let TagEncoding::Niche { niche_start, untagged_variant, niche_variants } = tag_encoding { + let niche_size = tag.size(cx); + assert!(*niche_start <= niche_size.unsigned_int_max()); + for (idx, variant) in variants.iter_enumerated() { + // Ensure all inhabited variants are accounted for. + if !variant.is_uninhabited() { + assert!(idx == *untagged_variant || niche_variants.contains(&idx)); + } + } + } for variant in variants.iter() { // No nested "multiple". assert_matches!(variant.variants, Variants::Single { .. }); diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index d85da7d355e..1c85eb2a861 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -202,7 +202,7 @@ where } } - _ if component.is_copy_modulo_regions(tcx, self.typing_env) => {} + _ if tcx.type_is_copy_modulo_regions(self.typing_env, component) => {} ty::Closure(_, args) => { for upvar in args.as_closure().upvar_tys() { diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 61f2262dfe8..774f0660258 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -5,6 +5,7 @@ use rustc_hir::def::DefKind; use rustc_index::bit_set::BitSet; use rustc_middle::bug; use rustc_middle::query::Providers; +use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{ self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, }; @@ -197,7 +198,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> { // We have entered some binders as we've walked into the // bounds of the RPITIT. Shift these binders back out when // constructing the top-level projection predicate. - let shifted_alias_ty = self.tcx.fold_regions(unshifted_alias_ty, |re, depth| { + let shifted_alias_ty = fold_regions(self.tcx, unshifted_alias_ty, |re, depth| { if let ty::ReBound(index, bv) = re.kind() { if depth != ty::INNERMOST { return ty::Region::new_error_with_message( diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 2c1ad9de9aa..0d574445df8 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -45,14 +45,12 @@ pub trait Elaboratable<I: Interner> { pub struct ClauseWithSupertraitSpan<I: Interner> { pub pred: I::Predicate, - // Span of the original elaborated predicate. - pub original_span: I::Span, // Span of the supertrait predicatae that lead to this clause. pub supertrait_span: I::Span, } impl<I: Interner> ClauseWithSupertraitSpan<I> { pub fn new(pred: I::Predicate, span: I::Span) -> Self { - ClauseWithSupertraitSpan { pred, original_span: span, supertrait_span: span } + ClauseWithSupertraitSpan { pred, supertrait_span: span } } } impl<I: Interner> Elaboratable<I> for ClauseWithSupertraitSpan<I> { @@ -63,7 +61,6 @@ impl<I: Interner> Elaboratable<I> for ClauseWithSupertraitSpan<I> { fn child(&self, clause: <I as Interner>::Clause) -> Self { ClauseWithSupertraitSpan { pred: clause.as_predicate(), - original_span: self.original_span, supertrait_span: self.supertrait_span, } } @@ -75,11 +72,7 @@ impl<I: Interner> Elaboratable<I> for ClauseWithSupertraitSpan<I> { _parent_trait_pred: crate::Binder<I, crate::TraitPredicate<I>>, _index: usize, ) -> Self { - ClauseWithSupertraitSpan { - pred: clause.as_predicate(), - original_span: self.original_span, - supertrait_span: supertrait_span, - } + ClauseWithSupertraitSpan { pred: clause.as_predicate(), supertrait_span: supertrait_span } } } diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index 59dea769511..55671b84dbc 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -29,7 +29,7 @@ pub enum TypeError<I: Interner> { Mutability, ArgumentMutability(usize), TupleSize(ExpectedFound<usize>), - FixedArraySize(ExpectedFound<u64>), + ArraySize(ExpectedFound<I::Const>), ArgCount, RegionsDoesNotOutlive(I::Region, I::Region), @@ -69,7 +69,7 @@ impl<I: Interner> TypeError<I> { use self::TypeError::*; match self { CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | PolarityMismatch(_) | Mismatch - | AbiMismatch(_) | FixedArraySize(_) | ArgumentSorts(..) | Sorts(_) + | AbiMismatch(_) | ArraySize(_) | ArgumentSorts(..) | Sorts(_) | VariadicMismatch(_) | TargetFeatureCast(_) => false, Mutability diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 2c8e47bcbca..81c8a7d4bfa 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -214,27 +214,47 @@ impl<I: Interner> DeepRejectCtxt<I, false, true> { impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_WITH_INFER: bool> DeepRejectCtxt<I, INSTANTIATE_LHS_WITH_INFER, INSTANTIATE_RHS_WITH_INFER> { + // Quite arbitrary. Large enough to only affect a very tiny amount of impls/crates + // and small enough to prevent hangs. + const STARTING_DEPTH: usize = 8; + pub fn args_may_unify( self, obligation_args: I::GenericArgs, impl_args: I::GenericArgs, ) -> bool { + self.args_may_unify_inner(obligation_args, impl_args, Self::STARTING_DEPTH) + } + + pub fn types_may_unify(self, lhs: I::Ty, rhs: I::Ty) -> bool { + self.types_may_unify_inner(lhs, rhs, Self::STARTING_DEPTH) + } + + fn args_may_unify_inner( + self, + obligation_args: I::GenericArgs, + impl_args: I::GenericArgs, + depth: usize, + ) -> bool { + // No need to decrement the depth here as this function is only + // recursively reachable via `types_may_unify_inner` which already + // increments the depth for us. iter::zip(obligation_args.iter(), impl_args.iter()).all(|(obl, imp)| { match (obl.kind(), imp.kind()) { // We don't fast reject based on regions. (ty::GenericArgKind::Lifetime(_), ty::GenericArgKind::Lifetime(_)) => true, (ty::GenericArgKind::Type(obl), ty::GenericArgKind::Type(imp)) => { - self.types_may_unify(obl, imp) + self.types_may_unify_inner(obl, imp, depth) } (ty::GenericArgKind::Const(obl), ty::GenericArgKind::Const(imp)) => { - self.consts_may_unify(obl, imp) + self.consts_may_unify_inner(obl, imp) } _ => panic!("kind mismatch: {obl:?} {imp:?}"), } }) } - pub fn types_may_unify(self, lhs: I::Ty, rhs: I::Ty) -> bool { + fn types_may_unify_inner(self, lhs: I::Ty, rhs: I::Ty, depth: usize) -> bool { match rhs.kind() { // Start by checking whether the `rhs` type may unify with // pretty much everything. Just return `true` in that case. @@ -273,18 +293,31 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_ | ty::Placeholder(_) => {} }; + // The type system needs to support exponentially large types + // as long as they are self-similar. While most other folders + // use caching to handle them, this folder exists purely as a + // perf optimization and is incredibly hot. In pretty much all + // uses checking the cache is slower than simply recursing, so + // we instead just add an arbitrary depth cutoff. + // + // We only decrement the depth here as the match on `rhs` + // does not recurse. + let Some(depth) = depth.checked_sub(1) else { + return true; + }; + // For purely rigid types, use structural equivalence. match lhs.kind() { ty::Ref(_, lhs_ty, lhs_mutbl) => match rhs.kind() { ty::Ref(_, rhs_ty, rhs_mutbl) => { - lhs_mutbl == rhs_mutbl && self.types_may_unify(lhs_ty, rhs_ty) + lhs_mutbl == rhs_mutbl && self.types_may_unify_inner(lhs_ty, rhs_ty, depth) } _ => false, }, ty::Adt(lhs_def, lhs_args) => match rhs.kind() { ty::Adt(rhs_def, rhs_args) => { - lhs_def == rhs_def && self.args_may_unify(lhs_args, rhs_args) + lhs_def == rhs_def && self.args_may_unify_inner(lhs_args, rhs_args, depth) } _ => false, }, @@ -326,27 +359,28 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_ ty::Tuple(rhs) => { lhs.len() == rhs.len() && iter::zip(lhs.iter(), rhs.iter()) - .all(|(lhs, rhs)| self.types_may_unify(lhs, rhs)) + .all(|(lhs, rhs)| self.types_may_unify_inner(lhs, rhs, depth)) } _ => false, }, ty::Array(lhs_ty, lhs_len) => match rhs.kind() { ty::Array(rhs_ty, rhs_len) => { - self.types_may_unify(lhs_ty, rhs_ty) && self.consts_may_unify(lhs_len, rhs_len) + self.types_may_unify_inner(lhs_ty, rhs_ty, depth) + && self.consts_may_unify_inner(lhs_len, rhs_len) } _ => false, }, ty::RawPtr(lhs_ty, lhs_mutbl) => match rhs.kind() { ty::RawPtr(rhs_ty, rhs_mutbl) => { - lhs_mutbl == rhs_mutbl && self.types_may_unify(lhs_ty, rhs_ty) + lhs_mutbl == rhs_mutbl && self.types_may_unify_inner(lhs_ty, rhs_ty, depth) } _ => false, }, ty::Slice(lhs_ty) => { - matches!(rhs.kind(), ty::Slice(rhs_ty) if self.types_may_unify(lhs_ty, rhs_ty)) + matches!(rhs.kind(), ty::Slice(rhs_ty) if self.types_may_unify_inner(lhs_ty, rhs_ty, depth)) } ty::Dynamic(lhs_preds, ..) => { @@ -366,7 +400,7 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_ lhs_hdr == rhs_hdr && lhs_sig_tys.len() == rhs_sig_tys.len() && iter::zip(lhs_sig_tys.iter(), rhs_sig_tys.iter()) - .all(|(lhs, rhs)| self.types_may_unify(lhs, rhs)) + .all(|(lhs, rhs)| self.types_may_unify_inner(lhs, rhs, depth)) } _ => false, }, @@ -375,49 +409,51 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_ ty::FnDef(lhs_def_id, lhs_args) => match rhs.kind() { ty::FnDef(rhs_def_id, rhs_args) => { - lhs_def_id == rhs_def_id && self.args_may_unify(lhs_args, rhs_args) + lhs_def_id == rhs_def_id && self.args_may_unify_inner(lhs_args, rhs_args, depth) } _ => false, }, ty::Closure(lhs_def_id, lhs_args) => match rhs.kind() { ty::Closure(rhs_def_id, rhs_args) => { - lhs_def_id == rhs_def_id && self.args_may_unify(lhs_args, rhs_args) + lhs_def_id == rhs_def_id && self.args_may_unify_inner(lhs_args, rhs_args, depth) } _ => false, }, ty::CoroutineClosure(lhs_def_id, lhs_args) => match rhs.kind() { ty::CoroutineClosure(rhs_def_id, rhs_args) => { - lhs_def_id == rhs_def_id && self.args_may_unify(lhs_args, rhs_args) + lhs_def_id == rhs_def_id && self.args_may_unify_inner(lhs_args, rhs_args, depth) } _ => false, }, ty::Coroutine(lhs_def_id, lhs_args) => match rhs.kind() { ty::Coroutine(rhs_def_id, rhs_args) => { - lhs_def_id == rhs_def_id && self.args_may_unify(lhs_args, rhs_args) + lhs_def_id == rhs_def_id && self.args_may_unify_inner(lhs_args, rhs_args, depth) } _ => false, }, ty::CoroutineWitness(lhs_def_id, lhs_args) => match rhs.kind() { ty::CoroutineWitness(rhs_def_id, rhs_args) => { - lhs_def_id == rhs_def_id && self.args_may_unify(lhs_args, rhs_args) + lhs_def_id == rhs_def_id && self.args_may_unify_inner(lhs_args, rhs_args, depth) } _ => false, }, ty::Pat(lhs_ty, _) => { // FIXME(pattern_types): take pattern into account - matches!(rhs.kind(), ty::Pat(rhs_ty, _) if self.types_may_unify(lhs_ty, rhs_ty)) + matches!(rhs.kind(), ty::Pat(rhs_ty, _) if self.types_may_unify_inner(lhs_ty, rhs_ty, depth)) } ty::Error(..) => true, } } - pub fn consts_may_unify(self, lhs: I::Const, rhs: I::Const) -> bool { + // Unlike `types_may_unify_inner`, this does not take a depth as + // we never recurse from this function. + fn consts_may_unify_inner(self, lhs: I::Const, rhs: I::Const) -> bool { match rhs.kind() { ty::ConstKind::Param(_) => { if INSTANTIATE_RHS_WITH_INFER { diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 8209d6f5fe3..d337a1a8ad9 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -49,7 +49,7 @@ use std::mem; use rustc_index::{Idx, IndexVec}; use thin_vec::ThinVec; -use tracing::instrument; +use tracing::{debug, instrument}; use crate::data_structures::Lrc; use crate::inherent::*; @@ -431,3 +431,75 @@ where value.fold_with(&mut Shifter::new(cx, amount)) } } + +/////////////////////////////////////////////////////////////////////////// +// Region folder + +pub fn fold_regions<I: Interner, T>( + cx: I, + value: T, + mut f: impl FnMut(I::Region, ty::DebruijnIndex) -> I::Region, +) -> T +where + T: TypeFoldable<I>, +{ + value.fold_with(&mut RegionFolder::new(cx, &mut f)) +} + +/// Folds over the substructure of a type, visiting its component +/// types and all regions that occur *free* within it. +/// +/// That is, function pointer types and trait object can introduce +/// new bound regions which are not visited by this visitors as +/// they are not free; only regions that occur free will be +/// visited by `fld_r`. +pub struct RegionFolder<'a, I: Interner> { + cx: I, + + /// Stores the index of a binder *just outside* the stuff we have + /// visited. So this begins as INNERMOST; when we pass through a + /// binder, it is incremented (via `shift_in`). + current_index: ty::DebruijnIndex, + + /// Callback invokes for each free region. The `DebruijnIndex` + /// points to the binder *just outside* the ones we have passed + /// through. + fold_region_fn: &'a mut (dyn FnMut(I::Region, ty::DebruijnIndex) -> I::Region + 'a), +} + +impl<'a, I: Interner> RegionFolder<'a, I> { + #[inline] + pub fn new( + cx: I, + fold_region_fn: &'a mut dyn FnMut(I::Region, ty::DebruijnIndex) -> I::Region, + ) -> RegionFolder<'a, I> { + RegionFolder { cx, current_index: ty::INNERMOST, fold_region_fn } + } +} + +impl<'a, I: Interner> TypeFolder<I> for RegionFolder<'a, I> { + fn cx(&self) -> I { + self.cx + } + + fn fold_binder<T: TypeFoldable<I>>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T> { + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + #[instrument(skip(self), level = "debug", ret)] + fn fold_region(&mut self, r: I::Region) -> I::Region { + match r.kind() { + ty::ReBound(debruijn, _) if debruijn < self.current_index => { + debug!(?self.current_index, "skipped bound region"); + r + } + _ => { + debug!(?self.current_index, "folding free region"); + (self.fold_region_fn)(r, self.current_index) + } + } + } +} diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 13ad505bc04..a892b88c2c6 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -63,6 +63,12 @@ pub enum TypingMode<I: Interner> { /// } /// ``` Analysis { defining_opaque_types: I::DefiningOpaqueTypes }, + /// Any analysis after borrowck for a given body should be able to use all the + /// hidden types defined by borrowck, without being able to define any new ones. + /// + /// This is currently only used by the new solver, but should be implemented in + /// the old solver as well. + PostBorrowckAnalysis { defined_opaque_types: I::DefiningOpaqueTypes }, /// After analysis, mostly during codegen and MIR optimizations, we're able to /// reveal all opaque types. As the concrete type should *never* be observable /// directly by the user, this should not be used by checks which may expose @@ -85,6 +91,12 @@ impl<I: Interner> TypingMode<I> { pub fn analysis_in_body(cx: I, body_def_id: I::LocalDefId) -> TypingMode<I> { TypingMode::Analysis { defining_opaque_types: cx.opaque_types_defined_by(body_def_id) } } + + pub fn post_borrowck_analysis(cx: I, body_def_id: I::LocalDefId) -> TypingMode<I> { + TypingMode::PostBorrowckAnalysis { + defined_opaque_types: cx.opaque_types_defined_by(body_def_id), + } + } } pub trait InferCtxtLike: Sized { @@ -126,6 +138,7 @@ pub trait InferCtxtLike: Sized { vid: ty::RegionVid, ) -> <Self::Interner as Interner>::Region; + fn next_region_infer(&self) -> <Self::Interner as Interner>::Region; fn next_ty_infer(&self) -> <Self::Interner as Interner>::Ty; fn next_const_infer(&self) -> <Self::Interner as Interner>::Const; fn fresh_args_for_item( diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 3793d2c5241..f45c94127bd 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -136,6 +136,9 @@ pub trait Ty<I: Interner<Ty = Self>>: matches!(self.kind(), ty::FnPtr(..)) } + /// Checks whether this type is an ADT that has unsafe fields. + fn has_unsafe_fields(self) -> bool; + fn fn_sig(self, interner: I) -> ty::Binder<I, ty::FnSig<I>> { match self.kind() { ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr), @@ -257,8 +260,6 @@ pub trait Const<I: Interner<Const = Self>>: + Relate<I> + Flags { - fn try_to_target_usize(self, interner: I) -> Option<u64>; - fn new_infer(interner: I, var: ty::InferConst) -> Self; fn new_var(interner: I, var: ty::ConstVid) -> Self; diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 0ae688848eb..025ec7ae896 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -270,6 +270,9 @@ pub trait Interner: fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool; + /// Returns `true` if this is an `unsafe trait`. + fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool; + fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool; fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed; diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 6b301b16060..0b013b2017f 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -501,19 +501,10 @@ pub fn structurally_relate_tys<I: Interner, R: TypeRelation<I>>( let t = relation.relate(a_t, b_t)?; match relation.relate(sz_a, sz_b) { Ok(sz) => Ok(Ty::new_array_with_const_len(cx, t, sz)), - Err(err) => { - // Check whether the lengths are both concrete/known values, - // but are unequal, for better diagnostics. - let sz_a = sz_a.try_to_target_usize(cx); - let sz_b = sz_b.try_to_target_usize(cx); - - match (sz_a, sz_b) { - (Some(sz_a_val), Some(sz_b_val)) if sz_a_val != sz_b_val => { - Err(TypeError::FixedArraySize(ExpectedFound::new(sz_a_val, sz_b_val))) - } - _ => Err(err), - } + Err(TypeError::ConstMismatch(_)) => { + Err(TypeError::ArraySize(ExpectedFound::new(sz_a, sz_b))) } + Err(e) => Err(e), } } diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index c8abfee314e..d49f8d3093d 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -136,9 +136,9 @@ where relation.register_predicates([ty::Binder::dummy(ty::PredicateKind::Ambiguous)]); Ok(a) } - TypingMode::Analysis { .. } | TypingMode::PostAnalysis => { - structurally_relate_tys(relation, a, b) - } + TypingMode::Analysis { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => structurally_relate_tys(relation, a, b), } } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 5010fc09adc..3cc2dcbaca6 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -511,7 +511,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // This is for global caching, so we properly track query dependencies. // Everything that affects the `result` should be performed within this - // `with_anon_task` closure. If computing this goal depends on something + // `with_cached_task` closure. If computing this goal depends on something // not tracked by the cache key and from outside of this anon task, it // must not be added to the global cache. Notably, this is the case for // trait solver cycles participants. |
