diff options
Diffstat (limited to 'compiler')
337 files changed, 7037 insertions, 4349 deletions
diff --git a/compiler/rustc_abi/src/canon_abi.rs b/compiler/rustc_abi/src/canon_abi.rs index 03eeb645489..7c020be6761 100644 --- a/compiler/rustc_abi/src/canon_abi.rs +++ b/compiler/rustc_abi/src/canon_abi.rs @@ -28,6 +28,9 @@ pub enum CanonAbi { Rust, RustCold, + /// An ABI that rustc does not know how to call or define. + Custom, + /// ABIs relevant to 32-bit Arm targets Arm(ArmCall), /// ABI relevant to GPUs: the entry point for a GPU kernel @@ -57,6 +60,7 @@ impl fmt::Display for CanonAbi { CanonAbi::C => ExternAbi::C { unwind: false }, CanonAbi::Rust => ExternAbi::Rust, CanonAbi::RustCold => ExternAbi::RustCold, + CanonAbi::Custom => ExternAbi::Custom, CanonAbi::Arm(arm_call) => match arm_call { ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false }, ArmCall::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall, diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index 0bc1c8a0930..7457ae1f033 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -40,6 +40,11 @@ pub enum ExternAbi { /// Even normally-compatible Rust types can become ABI-incompatible with this ABI! Unadjusted, + /// An ABI that rustc does not know how to call or define. Functions with this ABI can + /// only be created using `#[naked]` functions or `extern "custom"` blocks, and can only + /// be called from inline assembly. + Custom, + /// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias /// and only valid on platforms that have a UEFI standard EfiApi, @@ -141,6 +146,7 @@ abi_impls! { AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt", Cdecl { unwind: false } =><= "cdecl", Cdecl { unwind: true } =><= "cdecl-unwind", + Custom =><= "custom", EfiApi =><= "efiapi", Fastcall { unwind: false } =><= "fastcall", Fastcall { unwind: true } =><= "fastcall-unwind", diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index cf40c3f7f6f..11afd359e5a 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -97,12 +97,12 @@ pub struct Path { pub tokens: Option<LazyAttrTokenStream>, } +// Succeeds if the path has a single segment that is arg-free and matches the given symbol. impl PartialEq<Symbol> for Path { #[inline] fn eq(&self, name: &Symbol) -> bool { if let [segment] = self.segments.as_ref() - && segment.args.is_none() - && segment.ident.name == *name + && segment == name { true } else { @@ -111,6 +111,15 @@ impl PartialEq<Symbol> for Path { } } +// Succeeds if the path has segments that are arg-free and match the given symbols. +impl PartialEq<&[Symbol]> for Path { + #[inline] + fn eq(&self, names: &&[Symbol]) -> bool { + self.segments.len() == names.len() + && self.segments.iter().zip(names.iter()).all(|(s1, s2)| s1 == s2) + } +} + impl<CTX: rustc_span::HashStableContext> HashStable<CTX> for Path { fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { self.segments.len().hash_stable(hcx, hasher); @@ -166,6 +175,14 @@ pub struct PathSegment { pub args: Option<P<GenericArgs>>, } +// Succeeds if the path segment is arg-free and matches the given symbol. +impl PartialEq<Symbol> for PathSegment { + #[inline] + fn eq(&self, name: &Symbol) -> bool { + self.args.is_none() && self.ident.name == *name + } +} + impl PathSegment { pub fn from_ident(ident: Ident) -> Self { PathSegment { ident, id: DUMMY_NODE_ID, args: None } @@ -693,6 +710,12 @@ impl Pat { } } +impl From<P<Pat>> for Pat { + fn from(value: P<Pat>) -> Self { + *value + } +} + /// A single field in a struct pattern. /// /// Patterns like the fields of `Foo { x, ref y, ref mut z }` @@ -1433,19 +1456,32 @@ impl Expr { } pub fn precedence(&self) -> ExprPrecedence { + fn prefix_attrs_precedence(attrs: &AttrVec) -> ExprPrecedence { + for attr in attrs { + if let AttrStyle::Outer = attr.style { + return ExprPrecedence::Prefix; + } + } + ExprPrecedence::Unambiguous + } + match &self.kind { ExprKind::Closure(closure) => { match closure.fn_decl.output { FnRetTy::Default(_) => ExprPrecedence::Jump, - FnRetTy::Ty(_) => ExprPrecedence::Unambiguous, + FnRetTy::Ty(_) => prefix_attrs_precedence(&self.attrs), } } - ExprKind::Break(..) - | ExprKind::Ret(..) - | ExprKind::Yield(..) - | ExprKind::Yeet(..) - | ExprKind::Become(..) => ExprPrecedence::Jump, + ExprKind::Break(_ /*label*/, value) + | ExprKind::Ret(value) + | ExprKind::Yield(YieldKind::Prefix(value)) + | ExprKind::Yeet(value) => match value { + Some(_) => ExprPrecedence::Jump, + None => prefix_attrs_precedence(&self.attrs), + }, + + 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 @@ -1469,7 +1505,7 @@ impl Expr { | ExprKind::Let(..) | ExprKind::Unary(..) => ExprPrecedence::Prefix, - // Never need parens + // Need parens if and only if there are prefix attributes. ExprKind::Array(_) | ExprKind::Await(..) | ExprKind::Use(..) @@ -1502,8 +1538,9 @@ impl Expr { | ExprKind::Underscore | ExprKind::UnsafeBinderCast(..) | ExprKind::While(..) + | ExprKind::Yield(YieldKind::Postfix(..)) | ExprKind::Err(_) - | ExprKind::Dummy => ExprPrecedence::Unambiguous, + | ExprKind::Dummy => prefix_attrs_precedence(&self.attrs), } } @@ -1522,17 +1559,23 @@ impl Expr { ) } - /// Creates a dummy `P<Expr>`. + /// Creates a dummy `Expr`. /// /// Should only be used when it will be replaced afterwards or as a return value when an error was encountered. - pub fn dummy() -> P<Expr> { - P(Expr { + pub fn dummy() -> Expr { + Expr { id: DUMMY_NODE_ID, kind: ExprKind::Dummy, span: DUMMY_SP, attrs: ThinVec::new(), tokens: None, - }) + } + } +} + +impl From<P<Expr>> for Expr { + fn from(value: P<Expr>) -> Self { + *value } } @@ -2343,6 +2386,12 @@ impl Clone for Ty { } } +impl From<P<Ty>> for Ty { + fn from(value: P<Ty>) -> Self { + *value + } +} + impl Ty { pub fn peel_refs(&self) -> &Self { let mut final_ty = self; @@ -3520,6 +3569,38 @@ impl FnHeader { || matches!(constness, Const::Yes(_)) || !matches!(ext, Extern::None) } + + /// Return a span encompassing the header, or none if all options are default. + pub fn span(&self) -> Option<Span> { + fn append(a: &mut Option<Span>, b: Span) { + *a = match a { + None => Some(b), + Some(x) => Some(x.to(b)), + } + } + + let mut full_span = None; + + match self.safety { + Safety::Unsafe(span) | Safety::Safe(span) => append(&mut full_span, span), + Safety::Default => {} + }; + + if let Some(coroutine_kind) = self.coroutine_kind { + append(&mut full_span, coroutine_kind.span()); + } + + if let Const::Yes(span) = self.constness { + append(&mut full_span, span); + } + + match self.ext { + Extern::Implicit(span) | Extern::Explicit(_, span) => append(&mut full_span, span), + Extern::None => {} + } + + full_span + } } impl Default for FnHeader { diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs index dd8d5ae624a..7dee2ed17b4 100644 --- a/compiler/rustc_ast/src/expand/allocator.rs +++ b/compiler/rustc_ast/src/expand/allocator.rs @@ -22,7 +22,7 @@ pub fn alloc_error_handler_name(alloc_error_handler_kind: AllocatorKind) -> &'st } } -pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable"; +pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable_v2"; pub enum AllocatorTy { Layout, diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 71a47dcfcba..07fbe8045fc 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -168,7 +168,7 @@ pub trait MutVisitor: Sized + MutVisitorResult<Result = ()> { walk_flat_map_arm(self, arm) } - fn visit_pat(&mut self, p: &mut P<Pat>) { + fn visit_pat(&mut self, p: &mut Pat) { walk_pat(self, p); } @@ -176,7 +176,7 @@ pub trait MutVisitor: Sized + MutVisitorResult<Result = ()> { walk_anon_const(self, c); } - fn visit_expr(&mut self, e: &mut P<Expr>) { + fn visit_expr(&mut self, e: &mut Expr) { walk_expr(self, e); } @@ -194,7 +194,7 @@ pub trait MutVisitor: Sized + MutVisitorResult<Result = ()> { walk_generic_arg(self, arg); } - fn visit_ty(&mut self, t: &mut P<Ty>) { + fn visit_ty(&mut self, t: &mut Ty) { walk_ty(self, t); } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index c88aa5c33ea..bd2ab34bfc1 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -884,7 +884,7 @@ macro_rules! common_visitor_and_walkers { TyKind::BareFn(function_declaration) => { let BareFnTy { safety, ext: _, generic_params, decl, decl_span } = &$($mut)? **function_declaration; - visit_safety(vis, safety); + try_visit!(visit_safety(vis, safety)); try_visit!(visit_generic_params(vis, generic_params)); try_visit!(vis.visit_fn_decl(decl)); try_visit!(visit_span(vis, decl_span)); @@ -1235,7 +1235,7 @@ macro_rules! common_visitor_and_walkers { bounds, bound_generic_params, }) => { - visit_generic_params(vis, bound_generic_params); + try_visit!(visit_generic_params(vis, bound_generic_params)); try_visit!(vis.visit_ty(bounded_ty)); walk_list!(vis, visit_param_bound, bounds, BoundKind::Bound); } @@ -1420,7 +1420,7 @@ macro_rules! common_visitor_and_walkers { let StructExpr { qself, path, fields, rest } = &$($mut)?**se; try_visit!(vis.visit_qself(qself)); try_visit!(vis.visit_path(path)); - visit_expr_fields(vis, fields); + try_visit!(visit_expr_fields(vis, fields)); match rest { StructRest::Base(expr) => try_visit!(vis.visit_expr(expr)), StructRest::Rest(_span) => {} @@ -1700,7 +1700,8 @@ fn visit_nested_use_tree<'a, V: Visitor<'a>>( } pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) -> V::Result { - let Stmt { id: _, kind, span: _ } = statement; + let Stmt { id, kind, span: _ } = statement; + try_visit!(visit_id(visitor, id)); match kind { StmtKind::Let(local) => try_visit!(visitor.visit_local(local)), StmtKind::Item(item) => try_visit!(visitor.visit_item(item)), diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index 5ef76fb64aa..50eb7c7ae99 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -179,6 +179,8 @@ ast_lowering_underscore_expr_lhs_assign = in expressions, `_` can only be used on the left-hand side of an assignment .label = `_` not allowed here +ast_lowering_union_default_field_values = unions cannot have default field values + ast_lowering_unstable_inline_assembly = inline assembly is not stable yet on this architecture ast_lowering_unstable_inline_assembly_label_operand_with_outputs = using both label and output operands for inline assembly is unstable diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 576fa9731e9..b444324ef91 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -475,3 +475,10 @@ pub(crate) struct UseConstGenericArg { #[suggestion_part(code = "{other_args}")] pub call_args: Span, } + +#[derive(Diagnostic)] +#[diag(ast_lowering_union_default_field_values)] +pub(crate) struct UnionWithDefault { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 537d4a2a6af..718edad0cc6 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -73,16 +73,15 @@ impl<'hir> LoweringContext<'_, 'hir> { // Merge attributes into the inner expression. if !e.attrs.is_empty() { let old_attrs = self.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]); - let attrs = &*self.arena.alloc_from_iter( - self.lower_attrs_vec(&e.attrs, e.span) - .into_iter() - .chain(old_attrs.iter().cloned()), - ); - if attrs.is_empty() { + let new_attrs = self + .lower_attrs_vec(&e.attrs, e.span, ex.hir_id) + .into_iter() + .chain(old_attrs.iter().cloned()); + let new_attrs = &*self.arena.alloc_from_iter(new_attrs); + if new_attrs.is_empty() { return ex; } - - self.attrs.insert(ex.hir_id.local_id, attrs); + self.attrs.insert(ex.hir_id.local_id, new_attrs); } return ex; } @@ -2035,7 +2034,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let ret_expr = self.checked_return(Some(from_residual_expr)); self.arena.alloc(self.expr(try_span, ret_expr)) }; - self.lower_attrs(ret_expr.hir_id, &attrs, ret_expr.span); + self.lower_attrs(ret_expr.hir_id, &attrs, span); let break_pat = self.pat_cf_break(try_span, residual_local); self.arm(break_pat, ret_expr) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index d1a2ddbdb34..ef27d0ef69b 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -17,6 +17,7 @@ use tracing::instrument; use super::errors::{ InvalidAbi, InvalidAbiSuggestion, MisplacedRelaxTraitBound, TupleStructWithDefault, + UnionWithDefault, }; use super::stability::{enabled_names, gate_unstable_abi}; use super::{ @@ -63,7 +64,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { for (def_id, info) in lctx.children { let owner = self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom); - debug_assert!( + assert!( matches!(owner, hir::MaybeOwner::Phantom), "duplicate copy of {def_id:?} in lctx.children" ); @@ -78,7 +79,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { match node { AstOwner::NonOwner => {} AstOwner::Crate(c) => { - debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID); + assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID); self.with_lctx(CRATE_NODE_ID, |lctx| { let module = lctx.lower_mod(&c.items, &c.spans); // FIXME(jdonszelman): is dummy span ever a problem here? @@ -145,6 +146,7 @@ impl<'hir> LoweringContext<'_, 'hir> { kind, vis_span, span: self.lower_span(i.span), + has_delayed_lints: !self.delayed_lints.is_empty(), }; self.arena.alloc(item) } @@ -315,7 +317,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { this.arena.alloc_from_iter( - enum_definition.variants.iter().map(|x| this.lower_variant(x)), + enum_definition.variants.iter().map(|x| this.lower_variant(i, x)), ) }, ); @@ -327,7 +329,7 @@ impl<'hir> LoweringContext<'_, 'hir> { generics, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), - |this| this.lower_variant_data(hir_id, struct_def), + |this| this.lower_variant_data(hir_id, i, struct_def), ); hir::ItemKind::Struct(ident, generics, struct_def) } @@ -337,7 +339,7 @@ impl<'hir> LoweringContext<'_, 'hir> { generics, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), - |this| this.lower_variant_data(hir_id, vdata), + |this| this.lower_variant_data(hir_id, i, vdata), ); hir::ItemKind::Union(ident, generics, vdata) } @@ -599,6 +601,7 @@ impl<'hir> LoweringContext<'_, 'hir> { kind, vis_span, span: this.lower_span(use_tree.span), + has_delayed_lints: !this.delayed_lints.is_empty(), }; hir::OwnerNode::Item(this.arena.alloc(item)) }); @@ -697,6 +700,7 @@ impl<'hir> LoweringContext<'_, 'hir> { kind, vis_span: self.lower_span(i.vis.span), span: self.lower_span(i.span), + has_delayed_lints: !self.delayed_lints.is_empty(), }; self.arena.alloc(item) } @@ -711,13 +715,13 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> { + fn lower_variant(&mut self, item_kind: &ItemKind, v: &Variant) -> hir::Variant<'hir> { let hir_id = self.lower_node_id(v.id); self.lower_attrs(hir_id, &v.attrs, v.span); hir::Variant { hir_id, def_id: self.local_def_id(v.id), - data: self.lower_variant_data(hir_id, &v.data), + data: self.lower_variant_data(hir_id, item_kind, &v.data), disr_expr: v.disr_expr.as_ref().map(|e| self.lower_anon_const_to_anon_const(e)), ident: self.lower_ident(v.ident), span: self.lower_span(v.span), @@ -727,15 +731,36 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_variant_data( &mut self, parent_id: hir::HirId, + item_kind: &ItemKind, vdata: &VariantData, ) -> hir::VariantData<'hir> { match vdata { - VariantData::Struct { fields, recovered } => hir::VariantData::Struct { - fields: self + VariantData::Struct { fields, recovered } => { + let fields = self .arena - .alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f))), - recovered: *recovered, - }, + .alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f))); + + if let ItemKind::Union(..) = item_kind { + for field in &fields[..] { + if let Some(default) = field.default { + // Unions cannot derive `Default`, and it's not clear how to use default + // field values of unions if that was supported. Therefore, blanket reject + // trying to use field values with unions. + if self.tcx.features().default_field_values() { + self.dcx().emit_err(UnionWithDefault { span: default.span }); + } else { + let _ = self.dcx().span_delayed_bug( + default.span, + "expected union default field values feature gate error but none \ + was produced", + ); + } + } + } + } + + hir::VariantData::Struct { fields, recovered: *recovered } + } VariantData::Tuple(fields, id) => { let ctor_id = self.lower_node_id(*id); self.alias_attrs(ctor_id, parent_id); @@ -941,6 +966,7 @@ impl<'hir> LoweringContext<'_, 'hir> { kind, span: self.lower_span(i.span), defaultness: hir::Defaultness::Default { has_value: has_default }, + has_delayed_lints: !self.delayed_lints.is_empty(), }; self.arena.alloc(item) } @@ -1100,6 +1126,7 @@ impl<'hir> LoweringContext<'_, 'hir> { vis_span: self.lower_span(i.vis.span), span: self.lower_span(i.span), defaultness, + has_delayed_lints: !self.delayed_lints.is_empty(), }; self.arena.alloc(item) } @@ -1160,7 +1187,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> hir::BodyId { let body = hir::Body { params, value: self.arena.alloc(value) }; let id = body.id(); - debug_assert_eq!(id.hir_id.owner, self.current_hir_id_owner); + assert_eq!(id.hir_id.owner, self.current_hir_id_owner); self.bodies.push((id.hir_id.local_id, self.arena.alloc(body))); id } @@ -1673,8 +1700,8 @@ impl<'hir> LoweringContext<'_, 'hir> { itctx: ImplTraitContext, f: impl FnOnce(&mut Self) -> T, ) -> (&'hir hir::Generics<'hir>, T) { - debug_assert!(self.impl_trait_defs.is_empty()); - debug_assert!(self.impl_trait_bounds.is_empty()); + assert!(self.impl_trait_defs.is_empty()); + assert!(self.impl_trait_bounds.is_empty()); // Error if `?Trait` bounds in where clauses don't refer directly to type parameters. // Note: we used to clone these bounds directly onto the type parameter (and avoid lowering diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index b99df8bd7e5..3b99a653417 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -51,6 +51,7 @@ use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId}; +use rustc_hir::lints::DelayedLint; use rustc_hir::{ self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, LifetimeSource, LifetimeSyntax, ParamName, TraitCandidate, @@ -141,6 +142,8 @@ struct LoweringContext<'a, 'hir> { allow_for_await: Arc<[Symbol]>, allow_async_fn_traits: Arc<[Symbol]>, + delayed_lints: Vec<DelayedLint>, + attribute_parser: AttributeParser<'hir>, } @@ -190,6 +193,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { allow_async_iterator: [sym::gen_future, sym::async_iterator].into(), attribute_parser: AttributeParser::new(tcx.sess, tcx.features(), registered_tools), + delayed_lints: Vec::new(), } } @@ -198,6 +202,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } +struct SpanLowerer { + is_incremental: bool, + def_id: LocalDefId, +} + +impl SpanLowerer { + fn lower(&self, span: Span) -> Span { + if self.is_incremental { + span.with_parent(Some(self.def_id)) + } else { + // Do not make spans relative when not using incremental compilation. + span + } + } +} + #[extension(trait ResolverAstLoweringExt)] impl ResolverAstLowering { fn legacy_const_generic_args(&self, expr: &Expr) -> Option<Vec<usize>> { @@ -503,7 +523,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: Span, ) -> LocalDefId { let parent = self.current_hir_id_owner.def_id; - debug_assert_ne!(node_id, ast::DUMMY_NODE_ID); + assert_ne!(node_id, ast::DUMMY_NODE_ID); assert!( self.opt_local_def_id(node_id).is_none(), "adding a def'n for node-id {:?} and def kind {:?} but a previous def'n exists: {:?}", @@ -573,6 +593,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { std::mem::replace(&mut self.item_local_id_counter, hir::ItemLocalId::new(1)); let current_impl_trait_defs = std::mem::take(&mut self.impl_trait_defs); let current_impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds); + let current_delayed_lints = std::mem::take(&mut self.delayed_lints); // Do not reset `next_node_id` and `node_id_to_def_id`: // we want `f` to be able to refer to the `LocalDefId`s that the caller created. @@ -586,10 +607,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } let item = f(self); - debug_assert_eq!(owner_id, item.def_id()); + assert_eq!(owner_id, item.def_id()); // `f` should have consumed all the elements in these vectors when constructing `item`. - debug_assert!(self.impl_trait_defs.is_empty()); - debug_assert!(self.impl_trait_bounds.is_empty()); + assert!(self.impl_trait_defs.is_empty()); + assert!(self.impl_trait_bounds.is_empty()); let info = self.make_owner_info(item); self.attrs = current_attrs; @@ -606,6 +627,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.item_local_id_counter = current_local_counter; self.impl_trait_defs = current_impl_trait_defs; self.impl_trait_bounds = current_impl_trait_bounds; + self.delayed_lints = current_delayed_lints; debug_assert!(!self.children.iter().any(|(id, _)| id == &owner_id.def_id)); self.children.push((owner_id.def_id, hir::MaybeOwner::Owner(info))); @@ -616,6 +638,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let mut bodies = std::mem::take(&mut self.bodies); let define_opaque = std::mem::take(&mut self.define_opaque); let trait_map = std::mem::take(&mut self.trait_map); + let delayed_lints = std::mem::take(&mut self.delayed_lints).into_boxed_slice(); #[cfg(debug_assertions)] for (id, attrs) in attrs.iter() { @@ -629,14 +652,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let bodies = SortedMap::from_presorted_elements(bodies); // Don't hash unless necessary, because it's expensive. - let (opt_hash_including_bodies, attrs_hash) = - self.tcx.hash_owner_nodes(node, &bodies, &attrs, define_opaque); + let (opt_hash_including_bodies, attrs_hash, delayed_lints_hash) = + self.tcx.hash_owner_nodes(node, &bodies, &attrs, &delayed_lints, define_opaque); let num_nodes = self.item_local_id_counter.as_usize(); let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes); let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies }; let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash, define_opaque }; + let delayed_lints = + hir::lints::DelayedLints { lints: delayed_lints, opt_hash: delayed_lints_hash }; - self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map }) + self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map, delayed_lints }) } /// This method allocates a new `HirId` for the given `NodeId`. @@ -759,15 +784,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }) } + fn span_lowerer(&self) -> SpanLowerer { + SpanLowerer { + is_incremental: self.tcx.sess.opts.incremental.is_some(), + def_id: self.current_hir_id_owner.def_id, + } + } + /// Intercept all spans entering HIR. /// Mark a span as relative to the current owning item. fn lower_span(&self, span: Span) -> Span { - if self.tcx.sess.opts.incremental.is_some() { - span.with_parent(Some(self.current_hir_id_owner.def_id)) - } else { - // Do not make spans relative when not using incremental compilation. - span - } + self.span_lowerer().lower(span) } fn lower_ident(&self, ident: Ident) -> Ident { @@ -889,9 +916,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { if attrs.is_empty() { &[] } else { - let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span)); + let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span), id); - debug_assert_eq!(id.owner, self.current_hir_id_owner); + assert_eq!(id.owner, self.current_hir_id_owner); let ret = self.arena.alloc_from_iter(lowered_attrs); // this is possible if an item contained syntactical attribute, @@ -909,16 +936,30 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec<hir::Attribute> { - self.attribute_parser - .parse_attribute_list(attrs, target_span, OmitDoc::Lower, |s| self.lower_span(s)) + fn lower_attrs_vec( + &mut self, + attrs: &[Attribute], + target_span: Span, + target_hir_id: HirId, + ) -> Vec<hir::Attribute> { + let l = self.span_lowerer(); + self.attribute_parser.parse_attribute_list( + attrs, + target_span, + target_hir_id, + OmitDoc::Lower, + |s| l.lower(s), + |l| { + self.delayed_lints.push(DelayedLint::AttributeParsing(l)); + }, + ) } fn alias_attrs(&mut self, id: HirId, target_id: HirId) { - debug_assert_eq!(id.owner, self.current_hir_id_owner); - debug_assert_eq!(target_id.owner, self.current_hir_id_owner); + assert_eq!(id.owner, self.current_hir_id_owner); + assert_eq!(target_id.owner, self.current_hir_id_owner); if let Some(&a) = self.attrs.get(&target_id.local_id) { - debug_assert!(!a.is_empty()); + assert!(!a.is_empty()); self.attrs.insert(id.local_id, a); } } @@ -1397,7 +1438,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) = self.resolver.get_lifetime_res(t.id) { - debug_assert_eq!(start.plus(1), end); + assert_eq!(start.plus(1), end); start } else { self.next_node_id() @@ -1805,16 +1846,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let res = match res { LifetimeRes::Param { param, .. } => hir::LifetimeKind::Param(param), LifetimeRes::Fresh { param, .. } => { - debug_assert_eq!(ident.name, kw::UnderscoreLifetime); + assert_eq!(ident.name, kw::UnderscoreLifetime); let param = self.local_def_id(param); hir::LifetimeKind::Param(param) } LifetimeRes::Infer => { - debug_assert_eq!(ident.name, kw::UnderscoreLifetime); + assert_eq!(ident.name, kw::UnderscoreLifetime); hir::LifetimeKind::Infer } LifetimeRes::Static { .. } => { - debug_assert!(matches!(ident.name, kw::StaticLifetime | kw::UnderscoreLifetime)); + assert!(matches!(ident.name, kw::StaticLifetime | kw::UnderscoreLifetime)); hir::LifetimeKind::Static } LifetimeRes::Error => hir::LifetimeKind::Error, @@ -2106,7 +2147,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ty_id, &None, path, - ParamMode::Optional, + ParamMode::Explicit, AllowReturnTypeNotation::No, // FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support ImplTraitContext::Disallowed(ImplTraitPosition::Path), @@ -2178,7 +2219,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { expr.id, qself, path, - ParamMode::Optional, + ParamMode::Explicit, AllowReturnTypeNotation::No, // FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support ImplTraitContext::Disallowed(ImplTraitPosition::Path), @@ -2244,7 +2285,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) -> hir::Stmt<'hir> { let hir_id = self.next_id(); if let Some(a) = attrs { - debug_assert!(!a.is_empty()); + assert!(!a.is_empty()); self.attrs.insert(hir_id.local_id, a); } let local = hir::LetStmt { diff --git a/compiler/rustc_ast_lowering/src/stability.rs b/compiler/rustc_ast_lowering/src/stability.rs index eb052ba1c6d..b8fa2dd3dd6 100644 --- a/compiler/rustc_ast_lowering/src/stability.rs +++ b/compiler/rustc_ast_lowering/src/stability.rs @@ -134,5 +134,8 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> { feature: sym::cmse_nonsecure_entry, explain: GateReason::Experimental, }), + ExternAbi::Custom => { + Err(UnstableAbi { abi, feature: sym::abi_custom, explain: GateReason::Experimental }) + } } } diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 80754a8f65a..9a267501230 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -1,3 +1,20 @@ +ast_passes_abi_custom_coroutine = + functions with the `"custom"` ABI cannot be `{$coroutine_kind_str}` + .suggestion = remove the `{$coroutine_kind_str}` keyword from this definiton + +ast_passes_abi_custom_invalid_signature = + invalid signature for `extern "custom"` function + .note = functions with the `"custom"` ABI cannot have any parameters or return type + .suggestion = remove the parameters and return type + +ast_passes_abi_custom_safe_foreign_function = + foreign functions with the `"custom"` ABI cannot be safe + .suggestion = remove the `safe` keyword from this definition + +ast_passes_abi_custom_safe_function = + functions with the `"custom"` ABI must be unsafe + .suggestion = add the `unsafe` keyword to this definition + ast_passes_assoc_const_without_body = associated constant in `impl` without body .suggestion = provide a definition for the constant diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index d6fe04d2994..b69a91e2f5f 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -18,6 +18,7 @@ use std::mem; use std::ops::{Deref, DerefMut}; +use std::str::FromStr; use itertools::{Either, Itertools}; use rustc_abi::ExternAbi; @@ -81,6 +82,7 @@ struct AstValidator<'a> { /// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe. extern_mod_safety: Option<Safety>, + extern_mod_abi: Option<ExternAbi>, lint_node_id: NodeId, @@ -121,10 +123,17 @@ impl<'a> AstValidator<'a> { self.outer_trait_or_trait_impl = old; } - fn with_in_extern_mod(&mut self, extern_mod_safety: Safety, f: impl FnOnce(&mut Self)) { - let old = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety)); + fn with_in_extern_mod( + &mut self, + extern_mod_safety: Safety, + abi: Option<ExternAbi>, + f: impl FnOnce(&mut Self), + ) { + let old_safety = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety)); + let old_abi = mem::replace(&mut self.extern_mod_abi, abi); f(self); - self.extern_mod_safety = old; + self.extern_mod_safety = old_safety; + self.extern_mod_abi = old_abi; } fn with_tilde_const( @@ -215,20 +224,6 @@ impl<'a> AstValidator<'a> { } } - fn visit_struct_field_def(&mut self, field: &'a FieldDef) { - if let Some(ref ident) = field.ident - && ident.name == kw::Underscore - { - self.visit_vis(&field.vis); - self.visit_ident(ident); - self.visit_ty_common(&field.ty); - self.walk_ty(&field.ty); - walk_list!(self, visit_attribute, &field.attrs); - } else { - self.visit_field_def(field); - } - } - fn dcx(&self) -> DiagCtxtHandle<'a> { self.sess.dcx() } @@ -370,6 +365,65 @@ impl<'a> AstValidator<'a> { } } + /// An `extern "custom"` function must be unsafe, and must not have any parameters or return + /// type. + fn check_custom_abi(&self, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) { + let dcx = self.dcx(); + + // An `extern "custom"` function must be unsafe. + match sig.header.safety { + Safety::Unsafe(_) => { /* all good */ } + Safety::Safe(safe_span) => { + let safe_span = + self.sess.psess.source_map().span_until_non_whitespace(safe_span.to(sig.span)); + dcx.emit_err(errors::AbiCustomSafeForeignFunction { span: sig.span, safe_span }); + } + Safety::Default => match ctxt { + FnCtxt::Foreign => { /* all good */ } + FnCtxt::Free | FnCtxt::Assoc(_) => { + self.dcx().emit_err(errors::AbiCustomSafeFunction { + span: sig.span, + unsafe_span: sig.span.shrink_to_lo(), + }); + } + }, + } + + // An `extern "custom"` function cannot be `async` and/or `gen`. + if let Some(coroutine_kind) = sig.header.coroutine_kind { + let coroutine_kind_span = self + .sess + .psess + .source_map() + .span_until_non_whitespace(coroutine_kind.span().to(sig.span)); + + self.dcx().emit_err(errors::AbiCustomCoroutine { + span: sig.span, + coroutine_kind_span, + coroutine_kind_str: coroutine_kind.as_str(), + }); + } + + // An `extern "custom"` function must not have any parameters or return type. + let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect(); + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output { + spans.push(ret_ty.span); + } + + if !spans.is_empty() { + let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo()); + let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span()); + let padding = if header_span.is_empty() { "" } else { " " }; + + self.dcx().emit_err(errors::AbiCustomInvalidSignature { + spans, + symbol: ident.name, + suggestion_span, + padding, + }); + } + } + /// This ensures that items can only be `unsafe` (or unmarked) outside of extern /// blocks. /// @@ -1005,7 +1059,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { if abi.is_none() { self.handle_missing_abi(*extern_span, item.id); } - self.with_in_extern_mod(*safety, |this| { + + let extern_abi = abi.and_then(|abi| ExternAbi::from_str(abi.symbol.as_str()).ok()); + self.with_in_extern_mod(*safety, extern_abi, |this| { visit::walk_item(this, item); }); self.extern_mod_span = old_item; @@ -1065,8 +1121,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { VariantData::Struct { fields, .. } => { self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); self.visit_generics(generics); - // Permit `Anon{Struct,Union}` as field type. - walk_list!(self, visit_struct_field_def, fields); + walk_list!(self, visit_field_def, fields); } _ => visit::walk_item(self, item), }, @@ -1078,8 +1133,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { VariantData::Struct { fields, .. } => { self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); self.visit_generics(generics); - // Permit `Anon{Struct,Union}` as field type. - walk_list!(self, visit_struct_field_def, fields); + walk_list!(self, visit_field_def, fields); } _ => visit::walk_item(self, item), } @@ -1145,6 +1199,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_foreign_fn_bodyless(*ident, body.as_deref()); self.check_foreign_fn_headerless(sig.header); self.check_foreign_item_ascii_only(*ident); + if self.extern_mod_abi == Some(ExternAbi::Custom) { + self.check_custom_abi(FnCtxt::Foreign, ident, sig); + } } ForeignItemKind::TyAlias(box TyAlias { defaultness, @@ -1352,6 +1409,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_item_safety(span, safety); } + if let FnKind::Fn(ctxt, _, fun) = fk + && let Extern::Explicit(str_lit, _) = fun.sig.header.ext + && let Ok(ExternAbi::Custom) = ExternAbi::from_str(str_lit.symbol.as_str()) + { + self.check_custom_abi(ctxt, &fun.ident, &fun.sig); + } + self.check_c_variadic_type(fk); // Functions cannot both be `const async` or `const gen` @@ -1703,6 +1767,7 @@ pub fn check_crate( outer_impl_trait_span: None, disallow_tilde_const: Some(TildeConstReason::Item), extern_mod_safety: None, + extern_mod_abi: None, lint_node_id: CRATE_NODE_ID, is_sdylib_interface, lint_buffer: lints, diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 6f9737e0831..c437e62f4d3 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -824,3 +824,67 @@ pub(crate) struct MissingAbi { #[suggestion(code = "extern \"<abi>\"", applicability = "has-placeholders")] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(ast_passes_abi_custom_safe_foreign_function)] +pub(crate) struct AbiCustomSafeForeignFunction { + #[primary_span] + pub span: Span, + + #[suggestion( + ast_passes_suggestion, + applicability = "maybe-incorrect", + code = "", + style = "verbose" + )] + pub safe_span: Span, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_abi_custom_safe_function)] +pub(crate) struct AbiCustomSafeFunction { + #[primary_span] + pub span: Span, + + #[suggestion( + ast_passes_suggestion, + applicability = "maybe-incorrect", + code = "unsafe ", + style = "verbose" + )] + pub unsafe_span: Span, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_abi_custom_coroutine)] +pub(crate) struct AbiCustomCoroutine { + #[primary_span] + pub span: Span, + + #[suggestion( + ast_passes_suggestion, + applicability = "maybe-incorrect", + code = "", + style = "verbose" + )] + pub coroutine_kind_span: Span, + pub coroutine_kind_str: &'static str, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_abi_custom_invalid_signature)] +#[note] +pub(crate) struct AbiCustomInvalidSignature { + #[primary_span] + pub spans: Vec<Span>, + + #[suggestion( + ast_passes_suggestion, + applicability = "maybe-incorrect", + code = "{padding}fn {symbol}()", + style = "verbose" + )] + pub suggestion_span: Span, + pub symbol: Symbol, + pub padding: &'static str, +} diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 3682d25d341..1ec56868f37 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -36,6 +36,16 @@ macro_rules! gate_alt { feature_err(&$visitor.sess, $name, $span, $explain).emit(); } }}; + ($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr, $notes: expr) => {{ + if !$has_feature && !$span.allows_unstable($name) { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable + let mut diag = feature_err(&$visitor.sess, $name, $span, $explain); + for note in $notes { + diag.note(*note); + } + diag.emit(); + } + }}; } /// The case involving a multispan. @@ -154,11 +164,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); // Check feature gates for built-in attributes. if let Some(BuiltinAttribute { - gate: AttributeGate::Gated(_, name, descr, has_feature), + gate: AttributeGate::Gated { feature, message, check, notes, .. }, .. }) = attr_info { - gate_alt!(self, has_feature(self.features), *name, attr.span, *descr); + gate_alt!(self, check(self.features), *feature, attr.span, *message, *notes); } // Check unstable flavors of the `#[doc]` attribute. if attr.has_name(sym::doc) { diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs index a05e2bd6a5d..a766e2006e5 100644 --- a/compiler/rustc_ast_pretty/src/pprust/mod.rs +++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs @@ -53,6 +53,18 @@ pub fn item_to_string(i: &ast::Item) -> String { State::new().item_to_string(i) } +pub fn assoc_item_to_string(i: &ast::AssocItem) -> String { + State::new().assoc_item_to_string(i) +} + +pub fn foreign_item_to_string(i: &ast::ForeignItem) -> String { + State::new().foreign_item_to_string(i) +} + +pub fn stmt_to_string(s: &ast::Stmt) -> String { + State::new().stmt_to_string(s) +} + pub fn path_to_string(p: &ast::Path) -> String { State::new().path_to_string(p) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 0990c9b27eb..3d738fa31f2 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1063,6 +1063,14 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere Self::to_string(|s| s.print_item(i)) } + fn assoc_item_to_string(&self, i: &ast::AssocItem) -> String { + Self::to_string(|s| s.print_assoc_item(i)) + } + + fn foreign_item_to_string(&self, i: &ast::ForeignItem) -> String { + Self::to_string(|s| s.print_foreign_item(i)) + } + fn path_to_string(&self, p: &ast::Path) -> String { Self::to_string(|s| s.print_path(p, false, 0)) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index c9a7e2aebd0..ee49246a4bb 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -7,8 +7,8 @@ use rustc_ast::util::classify; use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast::util::parser::{self, ExprPrecedence, Fixity}; use rustc_ast::{ - self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount, - FormatDebugHex, FormatSign, FormatTrait, YieldKind, token, + self as ast, BinOpKind, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, + FormatCount, FormatDebugHex, FormatSign, FormatTrait, YieldKind, token, }; use crate::pp::Breaks::Inconsistent; @@ -214,13 +214,6 @@ impl<'a> State<'a> { } fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) { - let needs_paren = match func.kind { - // In order to call a named field, needs parens: `(self.fun)()` - // But not for an unnamed field: `self.0()` - ast::ExprKind::Field(_, name) => !name.is_numeric(), - _ => func.precedence() < ExprPrecedence::Unambiguous, - }; - // Independent of parenthesization related to precedence, we must // parenthesize `func` if this is a statement context in which without // parentheses, a statement boundary would occur inside `func` or @@ -237,8 +230,16 @@ 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_cond_paren(func, needs_paren, fixup.leftmost_subexpression()); + let func_fixup = fixup.leftmost_subexpression_with_operator(true); + + let needs_paren = match func.kind { + // In order to call a named field, needs parens: `(self.fun)()` + // But not for an unnamed field: `self.0()` + ast::ExprKind::Field(_, name) => !name.is_numeric(), + _ => func_fixup.precedence(func) < ExprPrecedence::Unambiguous, + }; + self.print_expr_cond_paren(func, needs_paren, func_fixup); self.print_call_post(args) } @@ -281,9 +282,24 @@ impl<'a> State<'a> { rhs: &ast::Expr, fixup: FixupContext, ) { + let operator_can_begin_expr = match op { + | BinOpKind::Sub // -x + | BinOpKind::Mul // *x + | BinOpKind::And // &&x + | BinOpKind::Or // || x + | BinOpKind::BitAnd // &x + | BinOpKind::BitOr // |x| x + | BinOpKind::Shl // <<T as Trait>::Type as Trait>::CONST + | BinOpKind::Lt // <T as Trait>::CONST + => true, + _ => false, + }; + + let left_fixup = fixup.leftmost_subexpression_with_operator(operator_can_begin_expr); + let binop_prec = op.precedence(); - let left_prec = lhs.precedence(); - let right_prec = rhs.precedence(); + let left_prec = left_fixup.precedence(lhs); + let right_prec = fixup.precedence(rhs); let (mut left_needs_paren, right_needs_paren) = match op.fixity() { Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec), @@ -312,18 +328,18 @@ impl<'a> State<'a> { _ => {} } - self.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression()); + self.print_expr_cond_paren(lhs, left_needs_paren, left_fixup); self.space(); self.word_space(op.as_str()); - self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression()); + self.print_expr_cond_paren(rhs, right_needs_paren, fixup.rightmost_subexpression()); } fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) { self.word(op.as_str()); self.print_expr_cond_paren( expr, - expr.precedence() < ExprPrecedence::Prefix, - fixup.subsequent_subexpression(), + fixup.precedence(expr) < ExprPrecedence::Prefix, + fixup.rightmost_subexpression(), ); } @@ -344,8 +360,8 @@ impl<'a> State<'a> { } self.print_expr_cond_paren( expr, - expr.precedence() < ExprPrecedence::Prefix, - fixup.subsequent_subexpression(), + fixup.precedence(expr) < ExprPrecedence::Prefix, + fixup.rightmost_subexpression(), ); } @@ -590,8 +606,8 @@ impl<'a> State<'a> { self.word_space("="); self.print_expr_cond_paren( rhs, - rhs.precedence() < ExprPrecedence::Assign, - fixup.subsequent_subexpression(), + fixup.precedence(rhs) < ExprPrecedence::Assign, + fixup.rightmost_subexpression(), ); } ast::ExprKind::AssignOp(op, lhs, rhs) => { @@ -604,8 +620,8 @@ impl<'a> State<'a> { self.word_space(op.node.as_str()); self.print_expr_cond_paren( rhs, - rhs.precedence() < ExprPrecedence::Assign, - fixup.subsequent_subexpression(), + fixup.precedence(rhs) < ExprPrecedence::Assign, + fixup.rightmost_subexpression(), ); } ast::ExprKind::Field(expr, ident) => { @@ -618,10 +634,11 @@ impl<'a> State<'a> { self.print_ident(*ident); } ast::ExprKind::Index(expr, index, _) => { + let expr_fixup = fixup.leftmost_subexpression_with_operator(true); self.print_expr_cond_paren( expr, - expr.precedence() < ExprPrecedence::Unambiguous, - fixup.leftmost_subexpression(), + expr_fixup.precedence(expr) < ExprPrecedence::Unambiguous, + expr_fixup, ); self.word("["); self.print_expr(index, FixupContext::default()); @@ -634,10 +651,11 @@ impl<'a> State<'a> { // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.) let fake_prec = ExprPrecedence::LOr; if let Some(e) = start { + let start_fixup = fixup.leftmost_subexpression_with_operator(true); self.print_expr_cond_paren( e, - e.precedence() < fake_prec, - fixup.leftmost_subexpression(), + start_fixup.precedence(e) < fake_prec, + start_fixup, ); } match limits { @@ -647,8 +665,8 @@ impl<'a> State<'a> { if let Some(e) = end { self.print_expr_cond_paren( e, - e.precedence() < fake_prec, - fixup.subsequent_subexpression(), + fixup.precedence(e) < fake_prec, + fixup.rightmost_subexpression(), ); } } @@ -665,11 +683,10 @@ impl<'a> State<'a> { self.space(); self.print_expr_cond_paren( expr, - // Parenthesize if required by precedence, or in the - // case of `break 'inner: loop { break 'inner 1 } + 1` - expr.precedence() < ExprPrecedence::Jump - || (opt_label.is_none() && classify::leading_labeled_expr(expr)), - fixup.subsequent_subexpression(), + // Parenthesize `break 'inner: loop { break 'inner 1 } + 1` + // ^---------------------------------^ + opt_label.is_none() && classify::leading_labeled_expr(expr), + fixup.rightmost_subexpression(), ); } } @@ -684,11 +701,7 @@ impl<'a> State<'a> { self.word("return"); if let Some(expr) = result { self.word(" "); - self.print_expr_cond_paren( - expr, - expr.precedence() < ExprPrecedence::Jump, - fixup.subsequent_subexpression(), - ); + self.print_expr(expr, fixup.rightmost_subexpression()); } } ast::ExprKind::Yeet(result) => { @@ -697,21 +710,13 @@ impl<'a> State<'a> { self.word("yeet"); if let Some(expr) = result { self.word(" "); - self.print_expr_cond_paren( - expr, - expr.precedence() < ExprPrecedence::Jump, - fixup.subsequent_subexpression(), - ); + self.print_expr(expr, fixup.rightmost_subexpression()); } } ast::ExprKind::Become(result) => { self.word("become"); self.word(" "); - self.print_expr_cond_paren( - result, - result.precedence() < ExprPrecedence::Jump, - fixup.subsequent_subexpression(), - ); + self.print_expr(result, fixup.rightmost_subexpression()); } ast::ExprKind::InlineAsm(a) => { // FIXME: Print `builtin # asm` once macro `asm` uses `builtin_syntax`. @@ -761,11 +766,7 @@ impl<'a> State<'a> { if let Some(expr) = e { self.space(); - self.print_expr_cond_paren( - expr, - expr.precedence() < ExprPrecedence::Jump, - fixup.subsequent_subexpression(), - ); + self.print_expr(expr, fixup.rightmost_subexpression()); } } ast::ExprKind::Yield(YieldKind::Postfix(e)) => { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs index 3ef21f5cb29..eb5ac8b78a8 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs @@ -1,5 +1,6 @@ -use rustc_ast::Expr; -use rustc_ast::util::{classify, parser}; +use rustc_ast::util::classify; +use rustc_ast::util::parser::{self, ExprPrecedence}; +use rustc_ast::{Expr, ExprKind, YieldKind}; // The default amount of fixing is minimal fixing, so all fixups are set to `false` by `Default`. // Fixups should be turned on in a targeted fashion where needed. @@ -93,6 +94,24 @@ pub(crate) struct FixupContext { /// } /// ``` parenthesize_exterior_struct_lit: bool, + + /// This is the difference between: + /// + /// ```ignore (illustrative) + /// let _ = (return) - 1; // without paren, this would return -1 + /// + /// let _ = return + 1; // no paren because '+' cannot begin expr + /// ``` + next_operator_can_begin_expr: bool, + + /// This is the difference between: + /// + /// ```ignore (illustrative) + /// let _ = 1 + return 1; // no parens if rightmost subexpression + /// + /// let _ = 1 + (return 1) + 1; // needs parens + /// ``` + next_operator_can_continue_expr: bool, } impl FixupContext { @@ -134,6 +153,8 @@ impl FixupContext { match_arm: false, leftmost_subexpression_in_match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm, + next_operator_can_begin_expr: false, + next_operator_can_continue_expr: true, ..self } } @@ -148,19 +169,34 @@ impl FixupContext { leftmost_subexpression_in_stmt: false, match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm, leftmost_subexpression_in_match_arm: false, + next_operator_can_begin_expr: false, + next_operator_can_continue_expr: true, ..self } } - /// Transform this fixup into the one that should apply when printing any - /// subexpression that is neither a leftmost subexpression nor surrounded in - /// delimiters. + /// Transform this fixup into the one that should apply when printing a + /// leftmost subexpression followed by punctuation that is legal as the + /// first token of an expression. + pub(crate) fn leftmost_subexpression_with_operator( + self, + next_operator_can_begin_expr: bool, + ) -> Self { + FixupContext { next_operator_can_begin_expr, ..self.leftmost_subexpression() } + } + + /// Transform this fixup into the one that should apply when printing the + /// rightmost subexpression of the current expression. + /// + /// The rightmost subexpression is any subexpression that has a different + /// first token than the current expression, but has the same last token. + /// + /// For example in `$a + $b` and `-$b`, the subexpression `$b` is a + /// rightmost subexpression. /// - /// This is for any subexpression that has a different first token than the - /// current expression, and is not surrounded by a paren/bracket/brace. For - /// example the `$b` in `$a + $b` and `-$b`, but not the one in `[$b]` or - /// `$a.f($b)`. - pub(crate) fn subsequent_subexpression(self) -> Self { + /// Not every expression has a rightmost subexpression. For example neither + /// `[$b]` nor `$a.f($b)` have one. + pub(crate) fn rightmost_subexpression(self) -> Self { FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, @@ -193,6 +229,39 @@ 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()) + || parser::needs_par_as_let_scrutinee(self.precedence(expr)) + } + + /// Determines the effective precedence of a subexpression. Some expressions + /// have higher or lower precedence when adjacent to particular operators. + pub(crate) fn precedence(self, expr: &Expr) -> ExprPrecedence { + if self.next_operator_can_begin_expr { + // Decrease precedence of value-less jumps when followed by an + // operator that would otherwise get interpreted as beginning a + // value for the jump. + if let ExprKind::Break(..) + | ExprKind::Ret(..) + | ExprKind::Yeet(..) + | ExprKind::Yield(YieldKind::Prefix(..)) = expr.kind + { + return ExprPrecedence::Jump; + } + } + + if !self.next_operator_can_continue_expr { + // Increase precedence of expressions that extend to the end of + // current statement or group. + if let ExprKind::Break(..) + | ExprKind::Closure(..) + | ExprKind::Ret(..) + | ExprKind::Yeet(..) + | ExprKind::Yield(YieldKind::Prefix(..)) + | ExprKind::Range(None, ..) = expr.kind + { + return ExprPrecedence::Prefix; + } + } + + 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 3638eb31c61..6c442553976 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -28,7 +28,7 @@ impl<'a> State<'a> { } } - fn print_foreign_item(&mut self, item: &ast::ForeignItem) { + pub(crate) fn print_foreign_item(&mut self, item: &ast::ForeignItem) { let ast::Item { id, span, ref attrs, ref kind, ref vis, tokens: _ } = *item; self.ann.pre(self, AnnNode::SubItem(id)); self.hardbreak_if_not_bol(); @@ -548,7 +548,7 @@ impl<'a> State<'a> { } } - fn print_assoc_item(&mut self, item: &ast::AssocItem) { + pub(crate) fn print_assoc_item(&mut self, item: &ast::AssocItem) { let ast::Item { id, span, ref attrs, ref kind, ref vis, tokens: _ } = *item; self.ann.pre(self, AnnNode::SubItem(id)); self.hardbreak_if_not_bol(); diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 845e4d5e5d0..d4c43456049 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -8,7 +8,7 @@ use thin_vec::ThinVec; use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability}; -#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)] pub enum InlineAttr { None, Hint, @@ -130,24 +130,54 @@ impl Deprecation { } } -/// Represent parsed, *built in*, inert attributes. +/// Represents parsed *built-in* inert attributes. /// -/// That means attributes that are not actually ever expanded. -/// For more information on this, see the module docs on the [`rustc_attr_parsing`] crate. -/// They're instead used as markers, to guide the compilation process in various way in most every stage of the compiler. -/// These are kept around after the AST, into the HIR and further on. +/// ## Overview +/// These attributes are markers that guide the compilation process and are never expanded into other code. +/// They persist throughout the compilation phases, from AST to HIR and beyond. /// -/// The word "parsed" could be a little misleading here, because the parser already parses -/// attributes early on. However, the result, an [`ast::Attribute`] -/// is only parsed at a high level, still containing a token stream in many cases. That is -/// because the structure of the contents varies from attribute to attribute. -/// With a parsed attribute I mean that each attribute is processed individually into a -/// final structure, which on-site (the place where the attribute is useful for, think the -/// the place where `must_use` is checked) little to no extra parsing or validating needs to -/// happen. +/// ## Attribute Processing +/// While attributes are initially parsed by [`rustc_parse`] into [`ast::Attribute`], they still contain raw token streams +/// because different attributes have different internal structures. This enum represents the final, +/// fully parsed form of these attributes, where each variant contains contains all the information and +/// structure relevant for the specific attribute. /// -/// For more docs, look in [`rustc_attr_parsing`]. +/// Some attributes can be applied multiple times to the same item, and they are "collapsed" into a single +/// semantic attribute. For example: +/// ```rust +/// #[repr(C)] +/// #[repr(packed)] +/// struct S { } +/// ``` +/// This is equivalent to `#[repr(C, packed)]` and results in a single [`AttributeKind::Repr`] containing +/// both `C` and `packed` annotations. This collapsing happens during parsing and is reflected in the +/// data structures defined in this enum. /// +/// ## Usage +/// These parsed attributes are used throughout the compiler to: +/// - Control code generation (e.g., `#[repr]`) +/// - Mark API stability (`#[stable]`, `#[unstable]`) +/// - Provide documentation (`#[doc]`) +/// - Guide compiler behavior (e.g., `#[allow_internal_unstable]`) +/// +/// ## Note on Attribute Organization +/// Some attributes like `InlineAttr`, `OptimizeAttr`, and `InstructionSetAttr` are defined separately +/// from this enum because they are used in specific compiler phases (like code generation) and don't +/// need to persist throughout the entire compilation process. They are typically processed and +/// converted into their final form earlier in the compilation pipeline. +/// +/// For example: +/// - `InlineAttr` is used during code generation to control function inlining +/// - `OptimizeAttr` is used to control optimization levels +/// - `InstructionSetAttr` is used for target-specific code generation +/// +/// These attributes are handled by their respective compiler passes in the [`rustc_codegen_ssa`] crate +/// and don't need to be preserved in the same way as the attributes in this enum. +/// +/// For more details on attribute parsing, see the [`rustc_attr_parsing`] crate. +/// +/// [`rustc_parse`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/index.html +/// [`rustc_codegen_ssa`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/index.html /// [`rustc_attr_parsing`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html #[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] pub enum AttributeKind { @@ -158,6 +188,9 @@ pub enum AttributeKind { /// Represents `#[allow_internal_unstable]`. AllowInternalUnstable(ThinVec<(Symbol, Span)>), + /// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint). + AsPtr(Span), + /// Represents `#[rustc_default_body_unstable]`. BodyStability { stability: DefaultBodyStability, @@ -188,6 +221,9 @@ pub enum AttributeKind { /// Represents [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html). DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol }, + /// Represents `#[inline]` and `#[rustc_force_inline]`. + Inline(InlineAttr, Span), + /// Represents `#[rustc_macro_transparency]`. MacroTransparency(Transparency), diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs index dbfc95b047a..f8355be09ad 100644 --- a/compiler/rustc_attr_data_structures/src/lib.rs +++ b/compiler/rustc_attr_data_structures/src/lib.rs @@ -1,3 +1,7 @@ +//! Data structures for representing parsed attributes in the Rust compiler. +//! For detailed documentation about attribute processing, +//! see [rustc_attr_parsing](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html). + // tidy-alphabetical-start #![allow(internal_features)] #![doc(rust_logo)] @@ -8,6 +12,8 @@ mod attributes; mod stability; mod version; +pub mod lints; + use std::num::NonZero; pub use attributes::*; diff --git a/compiler/rustc_attr_data_structures/src/lints.rs b/compiler/rustc_attr_data_structures/src/lints.rs new file mode 100644 index 00000000000..e34c54c6d32 --- /dev/null +++ b/compiler/rustc_attr_data_structures/src/lints.rs @@ -0,0 +1,15 @@ +use rustc_macros::HashStable_Generic; +use rustc_span::Span; + +#[derive(Clone, Debug, HashStable_Generic)] +pub struct AttributeLint<Id> { + pub id: Id, + pub span: Span, + pub kind: AttributeLintKind, +} + +#[derive(Clone, Debug, HashStable_Generic)] +pub enum AttributeLintKind { + UnusedDuplicate { this: Span, other: Span, warning: bool }, + IllFormedAttributeInput { suggestions: Vec<String> }, +} diff --git a/compiler/rustc_attr_data_structures/src/stability.rs b/compiler/rustc_attr_data_structures/src/stability.rs index c0ca08a60f8..218e771c745 100644 --- a/compiler/rustc_attr_data_structures/src/stability.rs +++ b/compiler/rustc_attr_data_structures/src/stability.rs @@ -132,6 +132,7 @@ pub enum StabilityLevel { /// fn foobar() {} /// ``` implied_by: Option<Symbol>, + old_name: Option<Symbol>, }, /// `#[stable]` Stable { diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 45174c9582d..b9b386635f6 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -23,8 +23,10 @@ attr_parsing_expects_feature_list = attr_parsing_expects_features = `{$name}` expects feature names -attr_parsing_incorrect_meta_item = expected a quoted string literal -attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes +attr_parsing_ill_formed_attribute_input = {$num_suggestions -> + [1] attribute must be of the form {$suggestions} + *[other] valid forms for the attribute are {$suggestions} + } attr_parsing_incorrect_repr_format_align_one_arg = incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses @@ -81,9 +83,6 @@ attr_parsing_missing_note = attr_parsing_missing_since = missing 'since' -attr_parsing_multiple_item = - multiple '{$item}' items - attr_parsing_multiple_stability_levels = multiple stability levels @@ -122,16 +121,21 @@ attr_parsing_unsupported_literal_cfg_boolean = literal in `cfg` predicate value must be a boolean attr_parsing_unsupported_literal_cfg_string = literal in `cfg` predicate value must be a string -attr_parsing_unsupported_literal_deprecated_kv_pair = - item in `deprecated` must be a key/value pair -attr_parsing_unsupported_literal_deprecated_string = - literal in `deprecated` value must be a string attr_parsing_unsupported_literal_generic = unsupported literal attr_parsing_unsupported_literal_suggestion = consider removing the prefix +attr_parsing_unused_duplicate = + unused attribute + .suggestion = remove this attribute + .note = attribute also specified here + .warn = {-passes_previously_accepted} + attr_parsing_unused_multiple = multiple `{$name}` attributes .suggestion = remove this attribute .note = attribute also specified here + +-attr_parsing_perviously_accepted = + this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index d0465546b73..21b01a8d071 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -1,44 +1,49 @@ use std::iter; use rustc_attr_data_structures::AttributeKind; +use rustc_feature::{AttributeTemplate, template}; use rustc_span::{Span, Symbol, sym}; use super::{CombineAttributeParser, ConvertFn}; -use crate::context::AcceptContext; +use crate::context::{AcceptContext, Stage}; use crate::parser::ArgParser; use crate::session_diagnostics; pub(crate) struct AllowInternalUnstableParser; -impl CombineAttributeParser for AllowInternalUnstableParser { - const PATH: &'static [Symbol] = &[sym::allow_internal_unstable]; +impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser { + const PATH: &[Symbol] = &[sym::allow_internal_unstable]; type Item = (Symbol, Span); const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable; + const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ..."); - fn extend<'a>( - cx: &'a AcceptContext<'a>, - args: &'a ArgParser<'a>, - ) -> impl IntoIterator<Item = Self::Item> + 'a { - parse_unstable(cx, args, Self::PATH[0]).into_iter().zip(iter::repeat(cx.attr_span)) + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator<Item = Self::Item> { + parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0]) + .into_iter() + .zip(iter::repeat(cx.attr_span)) } } pub(crate) struct AllowConstFnUnstableParser; -impl CombineAttributeParser for AllowConstFnUnstableParser { - const PATH: &'static [Symbol] = &[sym::rustc_allow_const_fn_unstable]; +impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser { + const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable]; type Item = Symbol; const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable; + const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ..."); - fn extend<'a>( - cx: &'a AcceptContext<'a>, - args: &'a ArgParser<'a>, - ) -> impl IntoIterator<Item = Self::Item> + 'a { - parse_unstable(cx, args, Self::PATH[0]) + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator<Item = Self::Item> + 'c { + parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0]) } } -fn parse_unstable<'a>( - cx: &AcceptContext<'_>, - args: &'a ArgParser<'a>, +fn parse_unstable<S: Stage>( + cx: &AcceptContext<'_, '_, S>, + args: &ArgParser<'_>, symbol: Symbol, ) -> impl IntoIterator<Item = Symbol> { let mut res = Vec::new(); diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs index 6cff952fcf2..c911908dfb3 100644 --- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs +++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs @@ -1,9 +1,10 @@ use rustc_attr_data_structures::AttributeKind; +use rustc_feature::template; use rustc_span::{Span, Symbol, sym}; use thin_vec::ThinVec; use super::{AcceptMapping, AttributeParser}; -use crate::context::FinalizeContext; +use crate::context::{FinalizeContext, Stage}; use crate::session_diagnostics; #[derive(Default)] @@ -12,40 +13,36 @@ pub(crate) struct ConfusablesParser { first_span: Option<Span>, } -impl AttributeParser for ConfusablesParser { - const ATTRIBUTES: AcceptMapping<Self> = &[(&[sym::rustc_confusables], |this, cx, args| { - let Some(list) = args.list() else { - // FIXME(jdonszelmann): error when not a list? Bring validation code here. - // NOTE: currently subsequent attributes are silently ignored using - // tcx.get_attr(). - return; - }; - - if list.is_empty() { - cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span }); - } - - for param in list.mixed() { - let span = param.span(); - - let Some(lit) = param.lit() else { - cx.emit_err(session_diagnostics::IncorrectMetaItem { - span, - suggestion: Some(session_diagnostics::IncorrectMetaItemSuggestion { - lo: span.shrink_to_lo(), - hi: span.shrink_to_hi(), - }), - }); - continue; +impl<S: Stage> AttributeParser<S> for ConfusablesParser { + const ATTRIBUTES: AcceptMapping<Self, S> = &[( + &[sym::rustc_confusables], + template!(List: r#""name1", "name2", ..."#), + |this, cx, args| { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span); + return; }; - this.confusables.push(lit.symbol); - } + if list.is_empty() { + cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span }); + } + + for param in list.mixed() { + let span = param.span(); + + let Some(lit) = param.lit().and_then(|i| i.value_str()) else { + cx.expected_string_literal(span, param.lit()); + continue; + }; + + this.confusables.push(lit); + } - this.first_span.get_or_insert(cx.attr_span); - })]; + this.first_span.get_or_insert(cx.attr_span); + }, + )]; - fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> { + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { if self.confusables.is_empty() { return None; } diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 006c1fe3b9c..702ad66f578 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -1,59 +1,50 @@ use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation}; +use rustc_feature::{AttributeTemplate, template}; use rustc_span::{Span, Symbol, sym}; -use super::SingleAttributeParser; use super::util::parse_version; -use crate::context::AcceptContext; +use super::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, Stage}; use crate::parser::ArgParser; use crate::session_diagnostics; -use crate::session_diagnostics::UnsupportedLiteralReason; pub(crate) struct DeprecationParser; -fn get( - cx: &AcceptContext<'_>, +fn get<S: Stage>( + cx: &AcceptContext<'_, '_, S>, name: Symbol, param_span: Span, arg: &ArgParser<'_>, item: &Option<Symbol>, ) -> Option<Symbol> { if item.is_some() { - cx.emit_err(session_diagnostics::MultipleItem { span: param_span, item: name.to_string() }); + cx.duplicate_key(param_span, name); return None; } if let Some(v) = arg.name_value() { if let Some(value_str) = v.value_as_str() { Some(value_str) } else { - let lit = v.value_as_lit(); - cx.emit_err(session_diagnostics::UnsupportedLiteral { - span: v.value_span, - reason: UnsupportedLiteralReason::DeprecatedString, - is_bytestr: lit.kind.is_bytestr(), - start_point_span: cx.sess().source_map().start_point(lit.span), - }); + cx.expected_string_literal(v.value_span, Some(&v.value_as_lit())); None } } else { - // FIXME(jdonszelmann): suggestion? - cx.emit_err(session_diagnostics::IncorrectMetaItem { span: param_span, suggestion: None }); + cx.expected_name_value(param_span, Some(name)); None } } -impl SingleAttributeParser for DeprecationParser { - const PATH: &'static [Symbol] = &[sym::deprecated]; +impl<S: Stage> SingleAttributeParser<S> for DeprecationParser { + const PATH: &[Symbol] = &[sym::deprecated]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = template!( + Word, + List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#, + NameValueStr: "reason" + ); - fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span) { - // FIXME(jdonszelmann): merge with errors from check_attrs.rs - cx.emit_err(session_diagnostics::UnusedMultiple { - this: cx.attr_span, - other: first_span, - name: sym::deprecated, - }); - } - - fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { let features = cx.features(); let mut since = None; @@ -62,57 +53,60 @@ impl SingleAttributeParser for DeprecationParser { let is_rustc = features.staged_api(); - if let Some(value) = args.name_value() - && let Some(value_str) = value.value_as_str() - { - note = Some(value_str) - } else if let Some(list) = args.list() { - for param in list.mixed() { - let param_span = param.span(); - let Some(param) = param.meta_item() else { - cx.emit_err(session_diagnostics::UnsupportedLiteral { - span: param_span, - reason: UnsupportedLiteralReason::DeprecatedKvPair, - is_bytestr: false, - start_point_span: cx.sess().source_map().start_point(param_span), - }); - return None; - }; + match args { + ArgParser::NoArgs => { + // ok + } + ArgParser::List(list) => { + for param in list.mixed() { + let Some(param) = param.meta_item() else { + cx.unexpected_literal(param.span()); + return None; + }; - let ident_name = param.path().word_sym(); + let ident_name = param.path().word_sym(); - match ident_name { - Some(name @ sym::since) => { - since = Some(get(cx, name, param_span, param.args(), &since)?); - } - Some(name @ sym::note) => { - note = Some(get(cx, name, param_span, param.args(), ¬e)?); - } - Some(name @ sym::suggestion) => { - if !features.deprecated_suggestion() { - cx.emit_err(session_diagnostics::DeprecatedItemSuggestion { - span: param_span, - is_nightly: cx.sess().is_nightly_build(), - details: (), - }); + match ident_name { + Some(name @ sym::since) => { + since = Some(get(cx, name, param.span(), param.args(), &since)?); } + Some(name @ sym::note) => { + note = Some(get(cx, name, param.span(), param.args(), ¬e)?); + } + Some(name @ sym::suggestion) => { + if !features.deprecated_suggestion() { + cx.emit_err(session_diagnostics::DeprecatedItemSuggestion { + span: param.span(), + is_nightly: cx.sess().is_nightly_build(), + details: (), + }); + } - suggestion = Some(get(cx, name, param_span, param.args(), &suggestion)?); - } - _ => { - cx.emit_err(session_diagnostics::UnknownMetaItem { - span: param_span, - item: param.path().to_string(), - expected: if features.deprecated_suggestion() { - &["since", "note", "suggestion"] - } else { - &["since", "note"] - }, - }); - return None; + suggestion = + Some(get(cx, name, param.span(), param.args(), &suggestion)?); + } + _ => { + cx.unknown_key( + param.span(), + param.path().to_string(), + if features.deprecated_suggestion() { + &["since", "note", "suggestion"] + } else { + &["since", "note"] + }, + ); + return None; + } } } } + ArgParser::NameValue(v) => { + let Some(value) = v.value_as_str() else { + cx.expected_string_literal(v.value_span, Some(v.value_as_lit())); + return None; + }; + note = Some(value); + } } let since = if let Some(since) = since { diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs new file mode 100644 index 00000000000..25efc3ae49b --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -0,0 +1,97 @@ +// FIXME(jdonszelmann): merge these two parsers and error when both attributes are present here. +// note: need to model better how duplicate attr errors work when not using +// SingleAttributeParser which is what we have two of here. + +use rustc_attr_data_structures::lints::AttributeLintKind; +use rustc_attr_data_structures::{AttributeKind, InlineAttr}; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::{Symbol, sym}; + +use super::{AcceptContext, AttributeOrder, OnDuplicate}; +use crate::attributes::SingleAttributeParser; +use crate::context::Stage; +use crate::parser::ArgParser; + +pub(crate) struct InlineParser; + +impl<S: Stage> SingleAttributeParser<S> for InlineParser { + const PATH: &'static [Symbol] = &[sym::inline]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(Word, List: "always|never"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + match args { + ArgParser::NoArgs => Some(AttributeKind::Inline(InlineAttr::Hint, cx.attr_span)), + ArgParser::List(list) => { + let Some(l) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + + match l.meta_item().and_then(|i| i.path().word_sym()) { + Some(sym::always) => { + Some(AttributeKind::Inline(InlineAttr::Always, cx.attr_span)) + } + Some(sym::never) => { + Some(AttributeKind::Inline(InlineAttr::Never, cx.attr_span)) + } + _ => { + cx.expected_specific_argument(l.span(), vec!["always", "never"]); + return None; + } + } + } + ArgParser::NameValue(_) => { + let suggestions = + <Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "inline"); + cx.emit_lint( + AttributeLintKind::IllFormedAttributeInput { suggestions }, + cx.attr_span, + ); + return None; + } + } + } +} + +pub(crate) struct RustcForceInlineParser; + +impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser { + const PATH: &'static [Symbol] = &[sym::rustc_force_inline]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(Word, List: "reason", NameValueStr: "reason"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + let reason = match args { + ArgParser::NoArgs => None, + ArgParser::List(list) => { + let Some(l) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + + let Some(reason) = l.lit().and_then(|i| i.kind.str()) else { + cx.expected_string_literal(l.span(), l.lit()); + return None; + }; + + Some(reason) + } + ArgParser::NameValue(v) => { + let Some(reason) = v.value_as_str() else { + cx.expected_string_literal(v.value_span, Some(v.value_as_lit())); + return None; + }; + + Some(reason) + } + }; + + Some(AttributeKind::Inline( + InlineAttr::Force { attr_span: cx.attr_span, reason }, + cx.attr_span, + )) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs new file mode 100644 index 00000000000..d4c846de56e --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -0,0 +1,21 @@ +use rustc_attr_data_structures::AttributeKind; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::{Symbol, sym}; + +use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; + +pub(crate) struct AsPtrParser; + +impl<S: Stage> SingleAttributeParser<S> for AsPtrParser { + const PATH: &[Symbol] = &[sym::rustc_as_ptr]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = template!(Word); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> { + // FIXME: check that there's no args (this is currently checked elsewhere) + Some(AttributeKind::AsPtr(cx.attr_span)) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 7cceca3c24d..fa2a6087506 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -12,28 +12,33 @@ //! - [`CombineAttributeParser`]: makes it easy to implement an attribute which should combine the //! contents of attributes, if an attribute appear multiple times in a list //! -//! Attributes should be added to [`ATTRIBUTE_PARSERS`](crate::context::ATTRIBUTE_PARSERS) to be parsed. +//! Attributes should be added to `crate::context::ATTRIBUTE_PARSERS` to be parsed. use std::marker::PhantomData; use rustc_attr_data_structures::AttributeKind; +use rustc_attr_data_structures::lints::AttributeLintKind; +use rustc_feature::AttributeTemplate; use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; -use crate::context::{AcceptContext, FinalizeContext}; +use crate::context::{AcceptContext, FinalizeContext, Stage}; use crate::parser::ArgParser; +use crate::session_diagnostics::UnusedMultiple; pub(crate) mod allow_unstable; pub(crate) mod cfg; pub(crate) mod confusables; pub(crate) mod deprecation; +pub(crate) mod inline; +pub(crate) mod lint_helpers; pub(crate) mod repr; pub(crate) mod stability; pub(crate) mod transparency; pub(crate) mod util; -type AcceptFn<T> = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>); -type AcceptMapping<T> = &'static [(&'static [Symbol], AcceptFn<T>)]; +type AcceptFn<T, S> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser<'_>); +type AcceptMapping<T, S> = &'static [(&'static [Symbol], AttributeTemplate, AcceptFn<T, S>)]; /// An [`AttributeParser`] is a type which searches for syntactic attributes. /// @@ -54,11 +59,11 @@ type AcceptMapping<T> = &'static [(&'static [Symbol], AcceptFn<T>)]; /// /// For a simpler attribute parsing interface, consider using [`SingleAttributeParser`] /// or [`CombineAttributeParser`] instead. -pub(crate) trait AttributeParser: Default + 'static { +pub(crate) trait AttributeParser<S: Stage>: Default + 'static { /// The symbols for the attributes that this parser is interested in. /// /// If an attribute has this symbol, the `accept` function will be called on it. - const ATTRIBUTES: AcceptMapping<Self>; + const ATTRIBUTES: AcceptMapping<Self, S>; /// The parser has gotten a chance to accept the attributes on an item, /// here it can produce an attribute. @@ -68,7 +73,7 @@ pub(crate) trait AttributeParser: Default + 'static { /// that'd be equivalent to unconditionally applying an attribute to /// every single syntax item that could have attributes applied to it. /// Your accept mappings should determine whether this returns something. - fn finalize(self, cx: &FinalizeContext<'_>) -> Option<AttributeKind>; + fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind>; } /// Alternative to [`AttributeParser`] that automatically handles state management. @@ -80,44 +85,137 @@ pub(crate) trait AttributeParser: Default + 'static { /// /// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple /// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example. -pub(crate) trait SingleAttributeParser: 'static { - const PATH: &'static [Symbol]; +pub(crate) trait SingleAttributeParser<S: Stage>: 'static { + const PATH: &[Symbol]; + const ATTRIBUTE_ORDER: AttributeOrder; + const ON_DUPLICATE: OnDuplicate<S>; - /// Called when a duplicate attribute is found. - /// - /// `first_span` is the span of the first occurrence of this attribute. - // FIXME(jdonszelmann): default error - fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span); + /// The template this attribute parser should implement. Used for diagnostics. + const TEMPLATE: AttributeTemplate; /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] - fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind>; + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>; } -pub(crate) struct Single<T: SingleAttributeParser>(PhantomData<T>, Option<(AttributeKind, Span)>); +pub(crate) struct Single<T: SingleAttributeParser<S>, S: Stage>( + PhantomData<(S, T)>, + Option<(AttributeKind, Span)>, +); -impl<T: SingleAttributeParser> Default for Single<T> { +impl<T: SingleAttributeParser<S>, S: Stage> Default for Single<T, S> { fn default() -> Self { Self(Default::default(), Default::default()) } } -impl<T: SingleAttributeParser> AttributeParser for Single<T> { - const ATTRIBUTES: AcceptMapping<Self> = &[(T::PATH, |group: &mut Single<T>, cx, args| { - if let Some((_, s)) = group.1 { - T::on_duplicate(cx, s); - return; - } +impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S> { + const ATTRIBUTES: AcceptMapping<Self, S> = &[( + T::PATH, + <T as SingleAttributeParser<S>>::TEMPLATE, + |group: &mut Single<T, S>, cx, args| { + if let Some(pa) = T::convert(cx, args) { + match T::ATTRIBUTE_ORDER { + // keep the first and report immediately. ignore this attribute + AttributeOrder::KeepFirst => { + if let Some((_, unused)) = group.1 { + T::ON_DUPLICATE.exec::<T>(cx, cx.attr_span, unused); + return; + } + } + // keep the new one and warn about the previous, + // then replace + AttributeOrder::KeepLast => { + if let Some((_, used)) = group.1 { + T::ON_DUPLICATE.exec::<T>(cx, used, cx.attr_span); + } + } + } - if let Some(pa) = T::convert(cx, args) { - group.1 = Some((pa, cx.attr_span)); - } - })]; + group.1 = Some((pa, cx.attr_span)); + } + }, + )]; - fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> { + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { Some(self.1?.0) } } +// FIXME(jdonszelmann): logic is implemented but the attribute parsers needing +// them will be merged in another PR +#[allow(unused)] +pub(crate) enum OnDuplicate<S: Stage> { + /// Give a default warning + Warn, + + /// Duplicates will be a warning, with a note that this will be an error in the future. + WarnButFutureError, + + /// Give a default error + Error, + + /// Ignore duplicates + Ignore, + + /// Custom function called when a duplicate attribute is found. + /// + /// - `unused` is the span of the attribute that was unused or bad because of some + /// duplicate reason (see [`AttributeOrder`]) + /// - `used` is the span of the attribute that was used in favor of the unused attribute + Custom(fn(cx: &AcceptContext<'_, '_, S>, used: Span, unused: Span)), +} + +impl<S: Stage> OnDuplicate<S> { + fn exec<P: SingleAttributeParser<S>>( + &self, + cx: &mut AcceptContext<'_, '_, S>, + used: Span, + unused: Span, + ) { + match self { + OnDuplicate::Warn => cx.emit_lint( + AttributeLintKind::UnusedDuplicate { this: unused, other: used, warning: false }, + unused, + ), + OnDuplicate::WarnButFutureError => cx.emit_lint( + AttributeLintKind::UnusedDuplicate { this: unused, other: used, warning: true }, + unused, + ), + OnDuplicate::Error => { + cx.emit_err(UnusedMultiple { + this: used, + other: unused, + name: Symbol::intern( + &P::PATH.into_iter().map(|i| i.to_string()).collect::<Vec<_>>().join(".."), + ), + }); + } + OnDuplicate::Ignore => {} + OnDuplicate::Custom(f) => f(cx, used, unused), + } + } +} +// +// FIXME(jdonszelmann): logic is implemented but the attribute parsers needing +// them will be merged in another PR +#[allow(unused)] +pub(crate) enum AttributeOrder { + /// Duplicates after the first attribute will be an error. + /// + /// This should be used where duplicates would be ignored, but carry extra + /// meaning that could cause confusion. For example, `#[stable(since="1.0")] + /// #[stable(since="2.0")]`, which version should be used for `stable`? + KeepFirst, + + /// Duplicates preceding the last instance of the attribute will be a + /// warning, with a note that this will be an error in the future. + /// + /// This is the same as `FutureWarnFollowing`, except the last attribute is + /// the one that is "used". Ideally these can eventually migrate to + /// `ErrorPreceding`. + KeepLast, +} + type ConvertFn<E> = fn(ThinVec<E>) -> AttributeKind; /// Alternative to [`AttributeParser`] that automatically handles state management. @@ -127,35 +225,41 @@ type ConvertFn<E> = fn(ThinVec<E>) -> AttributeKind; /// /// [`CombineAttributeParser`] can only convert a single kind of attribute, and cannot combine multiple /// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example. -pub(crate) trait CombineAttributeParser: 'static { - const PATH: &'static [Symbol]; +pub(crate) trait CombineAttributeParser<S: Stage>: 'static { + const PATH: &[rustc_span::Symbol]; type Item; const CONVERT: ConvertFn<Self::Item>; + /// The template this attribute parser should implement. Used for diagnostics. + const TEMPLATE: AttributeTemplate; + /// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`] - fn extend<'a>( - cx: &'a AcceptContext<'a>, - args: &'a ArgParser<'a>, - ) -> impl IntoIterator<Item = Self::Item> + 'a; + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator<Item = Self::Item> + 'c; } -pub(crate) struct Combine<T: CombineAttributeParser>( - PhantomData<T>, - ThinVec<<T as CombineAttributeParser>::Item>, +pub(crate) struct Combine<T: CombineAttributeParser<S>, S: Stage>( + PhantomData<(S, T)>, + ThinVec<<T as CombineAttributeParser<S>>::Item>, ); -impl<T: CombineAttributeParser> Default for Combine<T> { +impl<T: CombineAttributeParser<S>, S: Stage> Default for Combine<T, S> { fn default() -> Self { Self(Default::default(), Default::default()) } } -impl<T: CombineAttributeParser> AttributeParser for Combine<T> { - const ATTRIBUTES: AcceptMapping<Self> = - &[(T::PATH, |group: &mut Combine<T>, cx, args| group.1.extend(T::extend(cx, args)))]; +impl<T: CombineAttributeParser<S>, S: Stage> AttributeParser<S> for Combine<T, S> { + const ATTRIBUTES: AcceptMapping<Self, S> = &[( + T::PATH, + <T as CombineAttributeParser<S>>::TEMPLATE, + |group: &mut Combine<T, S>, cx, args| group.1.extend(T::extend(cx, args)), + )]; - fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> { + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) } } } diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 69316541e19..ae9e7871874 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -1,10 +1,11 @@ use rustc_abi::Align; use rustc_ast::{IntTy, LitIntType, LitKind, UintTy}; use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr}; +use rustc_feature::{AttributeTemplate, template}; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use super::{CombineAttributeParser, ConvertFn}; -use crate::context::AcceptContext; +use crate::context::{AcceptContext, Stage}; use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser}; use crate::session_diagnostics; use crate::session_diagnostics::IncorrectReprFormatGenericCause; @@ -19,18 +20,21 @@ use crate::session_diagnostics::IncorrectReprFormatGenericCause; // FIXME(jdonszelmann): is a vec the right representation here even? isn't it just a struct? pub(crate) struct ReprParser; -impl CombineAttributeParser for ReprParser { +impl<S: Stage> CombineAttributeParser<S> for ReprParser { type Item = (ReprAttr, Span); - const PATH: &'static [Symbol] = &[sym::repr]; + const PATH: &[Symbol] = &[sym::repr]; const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr; + // FIXME(jdonszelmann): never used + const TEMPLATE: AttributeTemplate = template!(List: "C"); - fn extend<'a>( - cx: &'a AcceptContext<'a>, - args: &'a ArgParser<'a>, - ) -> impl IntoIterator<Item = Self::Item> + 'a { + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator<Item = Self::Item> + 'c { let mut reprs = Vec::new(); let Some(list) = args.list() else { + cx.expected_list(cx.attr_span); return reprs; }; @@ -91,7 +95,10 @@ fn int_type_of_word(s: Symbol) -> Option<IntType> { } } -fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option<ReprAttr> { +fn parse_repr<S: Stage>( + cx: &AcceptContext<'_, '_, S>, + param: &MetaItemParser<'_>, +) -> Option<ReprAttr> { use ReprAttr::*; // FIXME(jdonszelmann): invert the parsing here to match on the word first and then the @@ -180,8 +187,8 @@ enum AlignKind { Align, } -fn parse_repr_align( - cx: &AcceptContext<'_>, +fn parse_repr_align<S: Stage>( + cx: &AcceptContext<'_, '_, S>, list: &MetaItemListParser<'_>, param_span: Span, align_kind: AlignKind, diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index ce69a54513d..6871ff4ec9f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -5,11 +5,12 @@ use rustc_attr_data_structures::{ StableSince, UnstableReason, VERSION_PLACEHOLDER, }; use rustc_errors::ErrorGuaranteed; -use rustc_span::{Span, Symbol, sym}; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::{Ident, Span, Symbol, sym}; use super::util::parse_version; -use super::{AcceptMapping, AttributeParser, SingleAttributeParser}; -use crate::context::{AcceptContext, FinalizeContext}; +use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, FinalizeContext, Stage}; use crate::parser::{ArgParser, MetaItemParser}; use crate::session_diagnostics::{self, UnsupportedLiteralReason}; @@ -31,7 +32,7 @@ pub(crate) struct StabilityParser { impl StabilityParser { /// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate. - fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool { + fn check_duplicate<S: Stage>(&self, cx: &AcceptContext<'_, '_, S>) -> bool { if let Some((_, _)) = self.stability { cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span }); true @@ -41,31 +42,44 @@ impl StabilityParser { } } -impl AttributeParser for StabilityParser { - const ATTRIBUTES: AcceptMapping<Self> = &[ - (&[sym::stable], |this, cx, args| { - reject_outside_std!(cx); - if !this.check_duplicate(cx) - && let Some((feature, level)) = parse_stability(cx, args) - { - this.stability = Some((Stability { level, feature }, cx.attr_span)); - } - }), - (&[sym::unstable], |this, cx, args| { - reject_outside_std!(cx); - if !this.check_duplicate(cx) - && let Some((feature, level)) = parse_unstability(cx, args) - { - this.stability = Some((Stability { level, feature }, cx.attr_span)); - } - }), - (&[sym::rustc_allowed_through_unstable_modules], |this, cx, args| { - reject_outside_std!(cx); - this.allowed_through_unstable_modules = args.name_value().and_then(|i| i.value_as_str()) - }), +impl<S: Stage> AttributeParser<S> for StabilityParser { + const ATTRIBUTES: AcceptMapping<Self, S> = &[ + ( + &[sym::stable], + template!(List: r#"feature = "name", since = "version""#), + |this, cx, args| { + reject_outside_std!(cx); + if !this.check_duplicate(cx) + && let Some((feature, level)) = parse_stability(cx, args) + { + this.stability = Some((Stability { level, feature }, cx.attr_span)); + } + }, + ), + ( + &[sym::unstable], + template!(List: r#"feature = "name", reason = "...", issue = "N""#), + |this, cx, args| { + reject_outside_std!(cx); + if !this.check_duplicate(cx) + && let Some((feature, level)) = parse_unstability(cx, args) + { + this.stability = Some((Stability { level, feature }, cx.attr_span)); + } + }, + ), + ( + &[sym::rustc_allowed_through_unstable_modules], + template!(NameValueStr: "deprecation message"), + |this, cx, args| { + reject_outside_std!(cx); + this.allowed_through_unstable_modules = + args.name_value().and_then(|i| i.value_as_str()) + }, + ), ]; - fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> { + fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { if let Some(atum) = self.allowed_through_unstable_modules { if let Some(( Stability { @@ -95,9 +109,11 @@ pub(crate) struct BodyStabilityParser { stability: Option<(DefaultBodyStability, Span)>, } -impl AttributeParser for BodyStabilityParser { - const ATTRIBUTES: AcceptMapping<Self> = - &[(&[sym::rustc_default_body_unstable], |this, cx, args| { +impl<S: Stage> AttributeParser<S> for BodyStabilityParser { + const ATTRIBUTES: AcceptMapping<Self, S> = &[( + &[sym::rustc_default_body_unstable], + template!(List: r#"feature = "name", reason = "...", issue = "N""#), + |this, cx, args| { reject_outside_std!(cx); if this.stability.is_some() { cx.dcx() @@ -105,9 +121,10 @@ impl AttributeParser for BodyStabilityParser { } else if let Some((feature, level)) = parse_unstability(cx, args) { this.stability = Some((DefaultBodyStability { level, feature }, cx.attr_span)); } - })]; + }, + )]; - fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> { + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { let (stability, span) = self.stability?; Some(AttributeKind::BodyStability { stability, span }) @@ -116,13 +133,13 @@ impl AttributeParser for BodyStabilityParser { pub(crate) struct ConstStabilityIndirectParser; // FIXME(jdonszelmann): single word attribute group when we have these -impl SingleAttributeParser for ConstStabilityIndirectParser { - const PATH: &'static [Symbol] = &[sym::rustc_const_stable_indirect]; - - // ignore - fn on_duplicate(_cx: &AcceptContext<'_>, _first_span: Span) {} +impl<S: Stage> SingleAttributeParser<S> for ConstStabilityIndirectParser { + const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore; + const TEMPLATE: AttributeTemplate = template!(Word); - fn convert(_cx: &AcceptContext<'_>, _args: &ArgParser<'_>) -> Option<AttributeKind> { + fn convert(_cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> { Some(AttributeKind::ConstStabilityIndirect) } } @@ -135,7 +152,7 @@ pub(crate) struct ConstStabilityParser { impl ConstStabilityParser { /// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate. - fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool { + fn check_duplicate<S: Stage>(&self, cx: &AcceptContext<'_, '_, S>) -> bool { if let Some((_, _)) = self.stability { cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span }); true @@ -145,9 +162,9 @@ impl ConstStabilityParser { } } -impl AttributeParser for ConstStabilityParser { - const ATTRIBUTES: AcceptMapping<Self> = &[ - (&[sym::rustc_const_stable], |this, cx, args| { +impl<S: Stage> AttributeParser<S> for ConstStabilityParser { + const ATTRIBUTES: AcceptMapping<Self, S> = &[ + (&[sym::rustc_const_stable], template!(List: r#"feature = "name""#), |this, cx, args| { reject_outside_std!(cx); if !this.check_duplicate(cx) @@ -159,7 +176,7 @@ impl AttributeParser for ConstStabilityParser { )); } }), - (&[sym::rustc_const_unstable], |this, cx, args| { + (&[sym::rustc_const_unstable], template!(List: r#"feature = "name""#), |this, cx, args| { reject_outside_std!(cx); if !this.check_duplicate(cx) && let Some((feature, level)) = parse_unstability(cx, args) @@ -170,13 +187,13 @@ impl AttributeParser for ConstStabilityParser { )); } }), - (&[sym::rustc_promotable], |this, cx, _| { + (&[sym::rustc_promotable], template!(Word), |this, cx, _| { reject_outside_std!(cx); this.promotable = true; }), ]; - fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> { + fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { if self.promotable { if let Some((ref mut stab, _)) = self.stability { stab.promotable = true; @@ -196,16 +213,14 @@ impl AttributeParser for ConstStabilityParser { /// /// Emits an error when either the option was already Some, or the arguments weren't of form /// `name = value` -fn insert_value_into_option_or_error( - cx: &AcceptContext<'_>, +fn insert_value_into_option_or_error<S: Stage>( + cx: &AcceptContext<'_, '_, S>, param: &MetaItemParser<'_>, item: &mut Option<Symbol>, + name: Ident, ) -> Option<()> { if item.is_some() { - cx.emit_err(session_diagnostics::MultipleItem { - span: param.span(), - item: param.path().to_string(), - }); + cx.duplicate_key(name.span, name.name); None } else if let Some(v) = param.args().name_value() && let Some(s) = v.value_as_str() @@ -213,18 +228,15 @@ fn insert_value_into_option_or_error( *item = Some(s); Some(()) } else { - cx.emit_err(session_diagnostics::IncorrectMetaItem { - span: param.span(), - suggestion: None, - }); + cx.expected_name_value(param.span(), Some(name.name)); None } } /// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and /// its stability information. -pub(crate) fn parse_stability( - cx: &AcceptContext<'_>, +pub(crate) fn parse_stability<S: Stage>( + cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>, ) -> Option<(Symbol, StabilityLevel)> { let mut feature = None; @@ -242,9 +254,14 @@ pub(crate) fn parse_stability( return None; }; - match param.path().word_sym() { - Some(sym::feature) => insert_value_into_option_or_error(cx, ¶m, &mut feature)?, - Some(sym::since) => insert_value_into_option_or_error(cx, ¶m, &mut since)?, + let word = param.path().word(); + match word.map(|i| i.name) { + Some(sym::feature) => { + insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())? + } + Some(sym::since) => { + insert_value_into_option_or_error(cx, ¶m, &mut since, word.unwrap())? + } _ => { cx.emit_err(session_diagnostics::UnknownMetaItem { span: param_span, @@ -289,8 +306,8 @@ pub(crate) fn parse_stability( // Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable` /// attribute, and return the feature name and its stability information. -pub(crate) fn parse_unstability( - cx: &AcceptContext<'_>, +pub(crate) fn parse_unstability<S: Stage>( + cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>, ) -> Option<(Symbol, StabilityLevel)> { let mut feature = None; @@ -299,6 +316,7 @@ pub(crate) fn parse_unstability( let mut issue_num = None; let mut is_soft = false; let mut implied_by = None; + let mut old_name = None; for param in args.list()?.mixed() { let Some(param) = param.meta_item() else { cx.emit_err(session_diagnostics::UnsupportedLiteral { @@ -310,11 +328,16 @@ pub(crate) fn parse_unstability( return None; }; - match param.path().word_sym() { - Some(sym::feature) => insert_value_into_option_or_error(cx, ¶m, &mut feature)?, - Some(sym::reason) => insert_value_into_option_or_error(cx, ¶m, &mut reason)?, + let word = param.path().word(); + match word.map(|i| i.name) { + Some(sym::feature) => { + insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())? + } + Some(sym::reason) => { + insert_value_into_option_or_error(cx, ¶m, &mut reason, word.unwrap())? + } Some(sym::issue) => { - insert_value_into_option_or_error(cx, ¶m, &mut issue)?; + insert_value_into_option_or_error(cx, ¶m, &mut issue, word.unwrap())?; // These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item // is a name/value pair string literal. @@ -344,13 +367,16 @@ pub(crate) fn parse_unstability( is_soft = true; } Some(sym::implied_by) => { - insert_value_into_option_or_error(cx, ¶m, &mut implied_by)? + insert_value_into_option_or_error(cx, ¶m, &mut implied_by, word.unwrap())? + } + Some(sym::old_name) => { + insert_value_into_option_or_error(cx, ¶m, &mut old_name, word.unwrap())? } _ => { cx.emit_err(session_diagnostics::UnknownMetaItem { span: param.span(), item: param.path().to_string(), - expected: &["feature", "reason", "issue", "soft", "implied_by"], + expected: &["feature", "reason", "issue", "soft", "implied_by", "old_name"], }); return None; } @@ -375,6 +401,7 @@ pub(crate) fn parse_unstability( issue: issue_num, is_soft, implied_by, + old_name, }; Some((feature, level)) } diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index d229fc09740..ce5ceb9139a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -1,8 +1,10 @@ use rustc_attr_data_structures::AttributeKind; +use rustc_feature::{AttributeTemplate, template}; use rustc_span::hygiene::Transparency; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Symbol, sym}; -use super::{AcceptContext, SingleAttributeParser}; +use super::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, Stage}; use crate::parser::ArgParser; pub(crate) struct TransparencyParser; @@ -10,20 +12,29 @@ pub(crate) struct TransparencyParser; // FIXME(jdonszelmann): make these proper diagnostics #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] -impl SingleAttributeParser for TransparencyParser { - const PATH: &'static [Symbol] = &[sym::rustc_macro_transparency]; +impl<S: Stage> SingleAttributeParser<S> for TransparencyParser { + const PATH: &[Symbol] = &[sym::rustc_macro_transparency]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Custom(|cx, used, unused| { + cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes"); + }); + const TEMPLATE: AttributeTemplate = + template!(NameValueStr: "transparent|semitransparent|opaque"); - fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: Span) { - cx.dcx().span_err(vec![first_span, cx.attr_span], "multiple macro transparency attributes"); - } - - fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> { - match args.name_value().and_then(|nv| nv.value_as_str()) { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + let Some(nv) = args.name_value() else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + match nv.value_as_str() { Some(sym::transparent) => Some(Transparency::Transparent), Some(sym::semiopaque | sym::semitransparent) => Some(Transparency::SemiOpaque), Some(sym::opaque) => Some(Transparency::Opaque), - Some(other) => { - cx.dcx().span_err(cx.attr_span, format!("unknown macro transparency: `{other}`")); + Some(_) => { + cx.expected_specific_argument_strings( + nv.value_span, + vec!["transparent", "semitransparent", "opaque"], + ); None } None => None, diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 35fb768ad0b..51c1760da30 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -1,19 +1,24 @@ use std::cell::RefCell; use std::collections::BTreeMap; -use std::ops::Deref; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; -use rustc_ast as ast; +use private::Sealed; +use rustc_ast::{self as ast, MetaItemLit, NodeId}; use rustc_attr_data_structures::AttributeKind; +use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind}; use rustc_errors::{DiagCtxtHandle, Diagnostic}; -use rustc_feature::Features; -use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId}; +use rustc_feature::{AttributeTemplate, Features}; +use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId}; use rustc_session::Session; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser}; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; +use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; +use crate::attributes::lint_helpers::AsPtrParser; use crate::attributes::repr::ReprParser; use crate::attributes::stability::{ BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser, @@ -21,34 +26,54 @@ use crate::attributes::stability::{ use crate::attributes::transparency::TransparencyParser; use crate::attributes::{AttributeParser as _, Combine, Single}; use crate::parser::{ArgParser, MetaItemParser}; +use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem}; + +macro_rules! group_type { + ($stage: ty) => { + LazyLock<( + BTreeMap<&'static [Symbol], Vec<(AttributeTemplate, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>)>>, + Vec<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $stage>) -> Option<AttributeKind>>> + )> + }; +} macro_rules! attribute_parsers { ( pub(crate) static $name: ident = [$($names: ty),* $(,)?]; ) => { - type Accepts = BTreeMap< - &'static [Symbol], - Box<dyn Send + Sync + Fn(&AcceptContext<'_>, &ArgParser<'_>)> - >; - type Finalizes = Vec< - Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>> - >; - pub(crate) static $name: LazyLock<(Accepts, Finalizes)> = LazyLock::new(|| { - let mut accepts = Accepts::new(); - let mut finalizes = Finalizes::new(); + mod early { + use super::*; + type Combine<T> = super::Combine<T, Early>; + type Single<T> = super::Single<T, Early>; + + attribute_parsers!(@[Early] pub(crate) static $name = [$($names),*];); + } + mod late { + use super::*; + type Combine<T> = super::Combine<T, Late>; + type Single<T> = super::Single<T, Late>; + + attribute_parsers!(@[Late] pub(crate) static $name = [$($names),*];); + } + }; + ( + @[$ty: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?]; + ) => { + pub(crate) static $name: group_type!($ty) = LazyLock::new(|| { + let mut accepts = BTreeMap::<_, Vec<(AttributeTemplate, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>)>>::new(); + let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $ty>) -> Option<AttributeKind>>>::new(); $( { thread_local! { static STATE_OBJECT: RefCell<$names> = RefCell::new(<$names>::default()); }; - for (k, v) in <$names>::ATTRIBUTES { - let old = accepts.insert(*k, Box::new(|cx, args| { + for (path, template, accept_fn) in <$names>::ATTRIBUTES { + accepts.entry(*path).or_default().push((*template, Box::new(|cx, args| { STATE_OBJECT.with_borrow_mut(|s| { - v(s, cx, args) + accept_fn(s, cx, args) }) - })); - assert!(old.is_none()); + }))); } finalizes.push(Box::new(|cx| { @@ -62,7 +87,6 @@ macro_rules! attribute_parsers { }); }; } - attribute_parsers!( pub(crate) static ATTRIBUTE_PARSERS = [ // tidy-alphabetical-start @@ -79,57 +103,255 @@ attribute_parsers!( // tidy-alphabetical-end // tidy-alphabetical-start + Single<AsPtrParser>, Single<ConstStabilityIndirectParser>, Single<DeprecationParser>, + Single<InlineParser>, + Single<RustcForceInlineParser>, Single<TransparencyParser>, // tidy-alphabetical-end ]; ); +mod private { + pub trait Sealed {} + impl Sealed for super::Early {} + impl Sealed for super::Late {} +} + +// allow because it's a sealed trait +#[allow(private_interfaces)] +pub trait Stage: Sized + 'static + Sealed { + type Id: Copy; + + fn parsers() -> &'static group_type!(Self); + + fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed; +} + +// allow because it's a sealed trait +#[allow(private_interfaces)] +impl Stage for Early { + type Id = NodeId; + + fn parsers() -> &'static group_type!(Self) { + &early::ATTRIBUTE_PARSERS + } + fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { + sess.dcx().create_err(diag).delay_as_bug() + } +} + +// allow because it's a sealed trait +#[allow(private_interfaces)] +impl Stage for Late { + type Id = HirId; + + fn parsers() -> &'static group_type!(Self) { + &late::ATTRIBUTE_PARSERS + } + fn emit_err<'sess>(tcx: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { + tcx.dcx().emit_err(diag) + } +} + +/// used when parsing attributes for miscelaneous things *before* ast lowering +pub struct Early; +/// used when parsing attributes during ast lowering +pub struct Late; + /// Context given to every attribute parser when accepting /// /// Gives [`AttributeParser`]s enough information to create errors, for example. -pub(crate) struct AcceptContext<'a> { - pub(crate) finalize_cx: &'a FinalizeContext<'a>, +pub(crate) struct AcceptContext<'f, 'sess, S: Stage> { + pub(crate) finalize_cx: FinalizeContext<'f, 'sess, S>, /// The span of the attribute currently being parsed pub(crate) attr_span: Span, + + /// The expected structure of the attribute. + /// + /// Used in reporting errors to give a hint to users what the attribute *should* look like. + pub(crate) template: &'f AttributeTemplate, + + /// The name of the attribute we're currently accepting. + pub(crate) attr_path: AttrPath, } -impl<'a> AcceptContext<'a> { - pub(crate) fn emit_err(&self, diag: impl Diagnostic<'a>) -> ErrorGuaranteed { - if self.limit_diagnostics { - self.dcx().create_err(diag).delay_as_bug() - } else { - self.dcx().emit_err(diag) - } +impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { + pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { + S::emit_err(&self.sess, diag) + } + + /// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing + /// must be delayed until after HIR is built. This method will take care of the details of + /// that. + pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) { + let id = self.target_id; + (self.emit_lint)(AttributeLint { id, span, kind: lint }); + } + + pub(crate) fn unknown_key( + &self, + span: Span, + found: String, + options: &'static [&'static str], + ) -> ErrorGuaranteed { + self.emit_err(UnknownMetaItem { span, item: found, expected: options }) + } + + /// error that a string literal was expected. + /// You can optionally give the literal you did find (which you found not to be a string literal) + /// which can make better errors. For example, if the literal was a byte string it will suggest + /// removing the `b` prefix. + pub(crate) fn expected_string_literal( + &self, + span: Span, + actual_literal: Option<&MetaItemLit>, + ) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedStringLiteral { + byte_string: actual_literal.and_then(|i| { + i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span)) + }), + }, + }) + } + + pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedList, + }) + } + + /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for + /// a nicer error message talking about the specific name that was found lacking a value. + pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedNameValue(name), + }) + } + + /// emit an error that a `name = value` pair was found where that name was already seen. + pub(crate) fn duplicate_key(&self, span: Span, key: Symbol) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::DuplicateKey(key), + }) + } + + /// an error that should be emitted when a [`MetaItemOrLitParser`](crate::parser::MetaItemOrLitParser) + /// was expected *not* to be a literal, but instead a meta item. + pub(crate) fn unexpected_literal(&self, span: Span) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::UnexpectedLiteral, + }) + } + + pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedSingleArgument, + }) + } + + pub(crate) fn expected_specific_argument( + &self, + span: Span, + possibilities: Vec<&'static str>, + ) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedSpecificArgument { + possibilities, + strings: false, + }, + }) + } + + pub(crate) fn expected_specific_argument_strings( + &self, + span: Span, + possibilities: Vec<&'static str>, + ) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedSpecificArgument { + possibilities, + strings: true, + }, + }) } } -impl<'a> Deref for AcceptContext<'a> { - type Target = FinalizeContext<'a>; +impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> { + type Target = FinalizeContext<'f, 'sess, S>; fn deref(&self) -> &Self::Target { &self.finalize_cx } } +impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.finalize_cx + } +} + /// Context given to every attribute parser during finalization. /// /// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create /// errors, for example. -pub(crate) struct FinalizeContext<'a> { +pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> { /// The parse context, gives access to the session and the /// diagnostics context. - pub(crate) cx: &'a AttributeParser<'a>, + pub(crate) cx: &'p mut AttributeParser<'sess, S>, /// The span of the syntactical component this attribute was applied to pub(crate) target_span: Span, + /// The id ([`NodeId`] if `S` is `Early`, [`HirId`] if `S` is `Late`) of the syntactical component this attribute was applied to + pub(crate) target_id: S::Id, + + pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>), } -impl<'a> Deref for FinalizeContext<'a> { - type Target = AttributeParser<'a>; +impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> { + type Target = AttributeParser<'sess, S>; fn deref(&self) -> &Self::Target { - &self.cx + self.cx + } +} + +impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.cx } } @@ -141,23 +363,20 @@ pub enum OmitDoc { /// Context created once, for example as part of the ast lowering /// context, through which all attributes can be lowered. -pub struct AttributeParser<'sess> { +pub struct AttributeParser<'sess, S: Stage = Late> { #[expect(dead_code)] // FIXME(jdonszelmann): needed later to verify we parsed all attributes tools: Vec<Symbol>, - sess: &'sess Session, features: Option<&'sess Features>, + sess: &'sess Session, + stage: PhantomData<S>, /// *Only* parse attributes with this symbol. /// /// Used in cases where we want the lowering infrastructure for parse just a single attribute. parse_only: Option<Symbol>, - - /// Can be used to instruct parsers to reduce the number of diagnostics it emits. - /// Useful when using `parse_limited` and you know the attr will be reparsed later. - pub(crate) limit_diagnostics: bool, } -impl<'sess> AttributeParser<'sess> { +impl<'sess> AttributeParser<'sess, Early> { /// This method allows you to parse attributes *before* you have access to features or tools. /// One example where this is necessary, is to parse `feature` attributes themselves for /// example. @@ -168,33 +387,53 @@ impl<'sess> AttributeParser<'sess> { /// /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with /// that symbol are picked out of the list of instructions and parsed. Those are returned. + /// + /// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while + /// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed + /// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors pub fn parse_limited( sess: &'sess Session, attrs: &[ast::Attribute], sym: Symbol, target_span: Span, - limit_diagnostics: bool, + target_node_id: NodeId, ) -> Option<Attribute> { - let mut parsed = Self { - sess, + let mut p = Self { features: None, tools: Vec::new(), parse_only: Some(sym), - limit_diagnostics, - } - .parse_attribute_list(attrs, target_span, OmitDoc::Skip, std::convert::identity); - + sess, + stage: PhantomData, + }; + let mut parsed = p.parse_attribute_list( + attrs, + target_span, + target_node_id, + OmitDoc::Skip, + std::convert::identity, + |_lint| { + panic!("can't emit lints here for now (nothing uses this atm)"); + }, + ); assert!(parsed.len() <= 1); parsed.pop() } + pub fn new_early(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self { + Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData } + } +} + +impl<'sess> AttributeParser<'sess, Late> { pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self { - Self { sess, features: Some(features), tools, parse_only: None, limit_diagnostics: false } + Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData } } +} +impl<'sess, S: Stage> AttributeParser<'sess, S> { pub(crate) fn sess(&self) -> &'sess Session { - self.sess + &self.sess } pub(crate) fn features(&self) -> &'sess Features { @@ -202,25 +441,25 @@ impl<'sess> AttributeParser<'sess> { } pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> { - self.sess.dcx() + self.sess().dcx() } /// Parse a list of attributes. /// /// `target_span` is the span of the thing this list of attributes is applied to, /// and when `omit_doc` is set, doc attributes are filtered out. - pub fn parse_attribute_list<'a>( - &'a self, - attrs: &'a [ast::Attribute], + pub fn parse_attribute_list( + &mut self, + attrs: &[ast::Attribute], target_span: Span, + target_id: S::Id, omit_doc: OmitDoc, lower_span: impl Copy + Fn(Span) -> Span, + mut emit_lint: impl FnMut(AttributeLint<S::Id>), ) -> Vec<Attribute> { let mut attributes = Vec::new(); - let finalize_cx = FinalizeContext { cx: self, target_span }; - for attr in attrs { // If we're only looking for a single attribute, skip all the ones we don't care about. if let Some(expected) = self.parse_only { @@ -268,13 +507,22 @@ impl<'sess> AttributeParser<'sess> { let args = parser.args(); let parts = path.segments().map(|i| i.name).collect::<Vec<_>>(); - if let Some(accept) = ATTRIBUTE_PARSERS.0.get(parts.as_slice()) { - let cx = AcceptContext { - finalize_cx: &finalize_cx, - attr_span: lower_span(attr.span), - }; - - accept(&cx, &args) + if let Some(accepts) = S::parsers().0.get(parts.as_slice()) { + for (template, accept) in accepts { + let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext { + finalize_cx: FinalizeContext { + cx: self, + target_span, + target_id, + emit_lint: &mut emit_lint, + }, + attr_span: lower_span(attr.span), + template, + attr_path: path.get_attribute_path(), + }; + + accept(&mut cx, args) + } } else { // If we're here, we must be compiling a tool attribute... Or someone // forgot to parse their fancy new attribute. Let's warn them in any case. @@ -304,8 +552,13 @@ impl<'sess> AttributeParser<'sess> { } let mut parsed_attributes = Vec::new(); - for f in &ATTRIBUTE_PARSERS.1 { - if let Some(attr) = f(&finalize_cx) { + for f in &S::parsers().1 { + if let Some(attr) = f(&mut FinalizeContext { + cx: self, + target_span, + target_id, + emit_lint: &mut emit_lint, + }) { parsed_attributes.push(Attribute::Parsed(attr)); } } diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 15037e802ff..47eeb63bad3 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -85,7 +85,8 @@ #[macro_use] mod attributes; -mod context; +pub(crate) mod context; +mod lints; pub mod parser; mod session_diagnostics; @@ -93,6 +94,7 @@ pub use attributes::cfg::*; pub use attributes::util::{ find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version, }; -pub use context::{AttributeParser, OmitDoc}; +pub use context::{AttributeParser, Early, Late, OmitDoc}; +pub use lints::emit_attribute_lint; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs new file mode 100644 index 00000000000..fee22293b47 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/lints.rs @@ -0,0 +1,32 @@ +use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind}; +use rustc_errors::{DiagArgValue, LintEmitter}; +use rustc_hir::HirId; + +use crate::session_diagnostics; + +pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emitter: L) { + let AttributeLint { id, span, kind } = lint; + + match kind { + &AttributeLintKind::UnusedDuplicate { this, other, warning } => lint_emitter + .emit_node_span_lint( + rustc_session::lint::builtin::UNUSED_ATTRIBUTES, + *id, + *span, + session_diagnostics::UnusedDuplicate { this, other, warning }, + ), + AttributeLintKind::IllFormedAttributeInput { suggestions } => { + lint_emitter.emit_node_span_lint( + rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT, + *id, + *span, + session_diagnostics::IllFormedAttributeInput { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + }, + ); + } + } +} diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index e10e3b511db..1edbe3a9d27 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -115,7 +115,7 @@ impl<'a> ArgParser<'a> { } } - pub fn from_attr_args(value: &'a AttrArgs, dcx: DiagCtxtHandle<'a>) -> Self { + pub fn from_attr_args<'sess>(value: &'a AttrArgs, dcx: DiagCtxtHandle<'sess>) -> Self { match value { AttrArgs::Empty => Self::NoArgs, AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => { @@ -235,7 +235,7 @@ impl<'a> Debug for MetaItemParser<'a> { impl<'a> MetaItemParser<'a> { /// Create a new parser from a [`NormalAttr`], which is stored inside of any /// [`ast::Attribute`](rustc_ast::Attribute) - pub fn from_attr(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'a>) -> Self { + pub fn from_attr<'sess>(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'sess>) -> Self { Self { path: PathParser::Ast(&attr.item.path), args: ArgParser::from_attr_args(&attr.item.args, dcx), @@ -320,13 +320,13 @@ fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit } } -struct MetaItemListParserContext<'a> { +struct MetaItemListParserContext<'a, 'sess> { // the tokens inside the delimiters, so `#[some::attr(a b c)]` would have `a b c` inside inside_delimiters: Peekable<TokenStreamIter<'a>>, - dcx: DiagCtxtHandle<'a>, + dcx: DiagCtxtHandle<'sess>, } -impl<'a> MetaItemListParserContext<'a> { +impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { fn done(&mut self) -> bool { self.inside_delimiters.peek().is_none() } @@ -507,11 +507,11 @@ pub struct MetaItemListParser<'a> { } impl<'a> MetaItemListParser<'a> { - fn new(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'a>) -> MetaItemListParser<'a> { + fn new<'sess>(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'sess>) -> Self { MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx) } - fn new_tts(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'a>) -> Self { + fn new_tts<'sess>(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'sess>) -> Self { MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span) } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 2c434175b4b..57ac92a0ca1 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -2,8 +2,12 @@ use std::num::IntErrorKind; use rustc_ast as ast; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_errors::{ + Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, +}; +use rustc_feature::AttributeTemplate; +use rustc_hir::AttrPath; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; use crate::fluent_generated as fluent; @@ -12,8 +16,6 @@ pub(crate) enum UnsupportedLiteralReason { Generic, CfgString, CfgBoolean, - DeprecatedString, - DeprecatedKvPair, } #[derive(Diagnostic)] @@ -32,37 +34,6 @@ pub(crate) struct InvalidPredicate { pub predicate: String, } -#[derive(Diagnostic)] -#[diag(attr_parsing_multiple_item, code = E0538)] -pub(crate) struct MultipleItem { - #[primary_span] - pub span: Span, - - pub item: String, -} - -#[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_meta_item, code = E0539)] -pub(crate) struct IncorrectMetaItem { - #[primary_span] - pub span: Span, - - #[subdiagnostic] - pub suggestion: Option<IncorrectMetaItemSuggestion>, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - attr_parsing_incorrect_meta_item_suggestion, - applicability = "maybe-incorrect" -)] -pub(crate) struct IncorrectMetaItemSuggestion { - #[suggestion_part(code = "\"")] - pub lo: Span, - #[suggestion_part(code = "\"")] - pub hi: Span, -} - /// Error code: E0541 pub(crate) struct UnknownMetaItem<'a> { pub span: Span, @@ -217,6 +188,7 @@ pub(crate) struct InvalidReprHintNoValue { } /// Error code: E0565 +// FIXME(jdonszelmann): slowly phased out pub(crate) struct UnsupportedLiteral { pub span: Span, pub reason: UnsupportedLiteralReason, @@ -239,12 +211,6 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral { UnsupportedLiteralReason::CfgBoolean => { fluent::attr_parsing_unsupported_literal_cfg_boolean } - UnsupportedLiteralReason::DeprecatedString => { - fluent::attr_parsing_unsupported_literal_deprecated_string - } - UnsupportedLiteralReason::DeprecatedKvPair => { - fluent::attr_parsing_unsupported_literal_deprecated_kv_pair - } }, ); diag.span(self.span); @@ -451,6 +417,25 @@ pub(crate) struct UnusedMultiple { pub name: Symbol, } +#[derive(LintDiagnostic)] +#[diag(attr_parsing_unused_duplicate)] +pub(crate) struct UnusedDuplicate { + #[suggestion(code = "", applicability = "machine-applicable")] + pub this: Span, + #[note] + pub other: Span, + #[warning] + pub warning: bool, +} + +// FIXME(jdonszelmann): duplicated in rustc_lints, should be moved here completely. +#[derive(LintDiagnostic)] +#[diag(attr_parsing_ill_formed_attribute_input)] +pub(crate) struct IllFormedAttributeInput { + pub num_suggestions: usize, + pub suggestions: DiagArgValue, +} + #[derive(Diagnostic)] #[diag(attr_parsing_stability_outside_std, code = E0734)] pub(crate) struct StabilityOutsideStd { @@ -479,3 +464,115 @@ pub(crate) struct UnrecognizedReprHint { #[primary_span] pub span: Span, } + +pub(crate) enum AttributeParseErrorReason { + ExpectedStringLiteral { byte_string: Option<Span> }, + ExpectedSingleArgument, + ExpectedList, + UnexpectedLiteral, + ExpectedNameValue(Option<Symbol>), + DuplicateKey(Symbol), + ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool }, +} + +pub(crate) struct AttributeParseError { + pub(crate) span: Span, + pub(crate) attr_span: Span, + pub(crate) template: AttributeTemplate, + pub(crate) attribute: AttrPath, + pub(crate) reason: AttributeParseErrorReason, +} + +impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { + let name = self.attribute.to_string(); + + let mut diag = Diag::new(dcx, level, format!("malformed `{name}` attribute input")); + diag.span(self.attr_span); + diag.code(E0539); + match self.reason { + AttributeParseErrorReason::ExpectedStringLiteral { byte_string } => { + if let Some(start_point_span) = byte_string { + diag.span_suggestion( + start_point_span, + fluent::attr_parsing_unsupported_literal_suggestion, + "", + Applicability::MaybeIncorrect, + ); + diag.note("expected a normal string literal, not a byte string literal"); + + return diag; + } else { + diag.span_label(self.span, "expected a string literal here"); + } + } + AttributeParseErrorReason::ExpectedSingleArgument => { + diag.span_label(self.span, "expected a single argument here"); + diag.code(E0805); + } + AttributeParseErrorReason::ExpectedList => { + diag.span_label(self.span, "expected this to be a list"); + } + AttributeParseErrorReason::DuplicateKey(key) => { + diag.span_label(self.span, format!("found `{key}` used as a key more than once")); + diag.code(E0538); + } + AttributeParseErrorReason::UnexpectedLiteral => { + diag.span_label(self.span, format!("didn't expect a literal here")); + diag.code(E0565); + } + AttributeParseErrorReason::ExpectedNameValue(None) => { + diag.span_label( + self.span, + format!("expected this to be of the form `{name} = \"...\"`"), + ); + } + AttributeParseErrorReason::ExpectedNameValue(Some(name)) => { + diag.span_label( + self.span, + format!("expected this to be of the form `{name} = \"...\"`"), + ); + } + AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings } => { + let quote = if strings { '"' } else { '`' }; + match possibilities.as_slice() { + &[] => {} + &[x] => { + diag.span_label( + self.span, + format!("the only valid argument here is {quote}{x}{quote}"), + ); + } + [first, second] => { + diag.span_label(self.span, format!("valid arguments are {quote}{first}{quote} or {quote}{second}{quote}")); + } + [first @ .., second_to_last, last] => { + let mut res = String::new(); + for i in first { + res.push_str(&format!("{quote}{i}{quote}, ")); + } + res.push_str(&format!( + "{quote}{second_to_last}{quote} or {quote}{last}{quote}" + )); + + diag.span_label(self.span, format!("valid arguments are {res}")); + } + } + } + } + + let suggestions = self.template.suggestions(false, &name); + diag.span_suggestions( + self.attr_span, + if suggestions.len() == 1 { + "must be of the form" + } else { + "try changing it to one of the following valid forms of the attribute" + }, + suggestions, + Applicability::HasPlaceholders, + ); + + diag + } +} diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 7511a55b03a..57db2e9fb57 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -2,7 +2,7 @@ use std::fmt; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph; -use rustc_index::bit_set::DenseBitSet; +use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use rustc_middle::mir::{ self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges, }; @@ -548,7 +548,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { } } -type BorrowsDomain = DenseBitSet<BorrowIndex>; +type BorrowsDomain = MixedBitSet<BorrowIndex>; /// Forward dataflow computation of the set of borrows that are in scope at a particular location. /// - we gen the introduced loans @@ -564,7 +564,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { // bottom = nothing is reserved or activated yet; - DenseBitSet::new_empty(self.borrow_set.len()) + MixedBitSet::new_empty(self.borrow_set.len()) } fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 1b4bb11d87b..34d36849939 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3229,8 +3229,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { Applicability::MaybeIncorrect, ); } + + let mutability = if matches!(borrow.kind(), BorrowKind::Mut { .. }) { + "mut " + } else { + "" + }; + if !is_format_arguments_item { - let addition = format!("let binding = {};\n{}", s, " ".repeat(p)); + let addition = format!( + "let {}binding = {};\n{}", + mutability, + s, + " ".repeat(p) + ); err.multipart_suggestion_verbose( msg, vec![ diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index a845431faca..095c0df98ac 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -342,6 +342,7 @@ impl<'tcx> BorrowExplanation<'tcx> { } } } else if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() { + let sp = info.span.find_oldest_ancestor_in_same_ctxt(); if info.tail_result_is_ignored { // #85581: If the first mutable borrow's scope contains // the second borrow, this suggestion isn't helpful. @@ -349,7 +350,7 @@ impl<'tcx> BorrowExplanation<'tcx> { old.to(info.span.shrink_to_hi()).contains(new) }) { err.span_suggestion_verbose( - info.span.shrink_to_hi(), + sp.shrink_to_hi(), "consider adding semicolon after the expression so its \ temporaries are dropped sooner, before the local variables \ declared by the block are dropped", @@ -368,8 +369,8 @@ impl<'tcx> BorrowExplanation<'tcx> { local variable `x` and then make `x` be the expression at the \ end of the block", vec![ - (info.span.shrink_to_lo(), "let x = ".to_string()), - (info.span.shrink_to_hi(), "; x".to_string()), + (sp.shrink_to_lo(), "let x = ".to_string()), + (sp.shrink_to_hi(), "; x".to_string()), ], Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index e6eae7d4f5a..4d85f109020 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -29,7 +29,7 @@ use rustc_errors::LintDiagnostic; use rustc_hir as hir; use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::LocalDefId; -use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; +use rustc_index::bit_set::MixedBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::{ InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt, @@ -1151,11 +1151,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { &self, location: Location, state: &'s BorrowckDomain, - ) -> Cow<'s, DenseBitSet<BorrowIndex>> { + ) -> Cow<'s, MixedBitSet<BorrowIndex>> { if let Some(polonius) = &self.polonius_output { // Use polonius output if it has been enabled. let location = self.location_table.start_index(location); - let mut polonius_output = DenseBitSet::new_empty(self.borrow_set.len()); + let mut polonius_output = MixedBitSet::new_empty(self.borrow_set.len()); for &idx in polonius.errors_at(location) { polonius_output.insert(idx); } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 4f75dd7e992..9b6dcfd17c6 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -373,8 +373,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } fn unsized_feature_enabled(&self) -> bool { - let features = self.tcx().features(); - features.unsized_locals() || features.unsized_fn_params() + self.tcx().features().unsized_fn_params() } /// Equate the inferred type and the annotated type for user type annotations @@ -957,7 +956,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } - // When `unsized_fn_params` or `unsized_locals` is enabled, only function calls + // When `unsized_fn_params` is enabled, only function calls // and nullary ops are checked in `check_call_dest`. if !self.unsized_feature_enabled() { match self.body.local_kind(local) { @@ -1941,7 +1940,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - // When `unsized_fn_params` and `unsized_locals` are both not enabled, + // When `unsized_fn_params` is not enabled, // this check is done at `check_local`. if self.unsized_feature_enabled() { let span = term.source_info.span; diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index da01e3e9607..fe44350863c 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -155,7 +155,7 @@ impl CfgEval<'_> { impl MutVisitor for CfgEval<'_> { #[instrument(level = "trace", skip(self))] - fn visit_expr(&mut self, expr: &mut P<ast::Expr>) { + fn visit_expr(&mut self, expr: &mut ast::Expr) { self.0.configure_expr(expr, false); mut_visit::walk_expr(self, expr); } diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 0794192621a..3a20b39798d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -1,5 +1,4 @@ use ast::HasAttrs; -use ast::ptr::P; use rustc_ast::mut_visit::MutVisitor; use rustc_ast::visit::BoundKind; use rustc_ast::{ @@ -378,11 +377,11 @@ struct TypeSubstitution<'a> { } impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> { - fn visit_ty(&mut self, ty: &mut P<ast::Ty>) { + fn visit_ty(&mut self, ty: &mut ast::Ty) { if let Some(name) = ty.kind.is_simple_path() && name == self.from_name { - **ty = self.to_ty.clone(); + *ty = self.to_ty.clone(); self.rewritten = true; } else { ast::mut_visit::walk_ty(self, ty); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 6dd3adf750d..d642851bb77 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -484,7 +484,7 @@ impl<'a> TraitDef<'a> { match item { Annotatable::Item(item) => { let is_packed = matches!( - AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, true), + AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, item.id), Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(x, _)| matches!(x, ReprPacked(..))) ); diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index b439fa34f5b..b067578794b 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -118,10 +118,7 @@ pub(crate) fn expand_test_or_bench( let (item, is_stmt) = match item { Annotatable::Item(i) => (i, false), - Annotatable::Stmt(stmt) if matches!(stmt.kind, ast::StmtKind::Item(_)) => { - // FIXME: Use an 'if let' guard once they are implemented - if let ast::StmtKind::Item(i) = stmt.kind { (i, true) } else { unreachable!() } - } + Annotatable::Stmt(box ast::Stmt { kind: ast::StmtKind::Item(i), .. }) => (i, true), other => { not_testable_error(cx, attr_sp, None); return vec![other]; diff --git a/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs b/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs index 5479b0c617b..f0d1f6e2215 100644 --- a/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs +++ b/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs @@ -32,10 +32,6 @@ impl<T: CoerceUnsized<U>, U> CoerceUnsized<Wrapper<U>> for Wrapper<T> {} impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<Wrapper<U>> for Wrapper<T> {} trait Trait { - // This method isn't object-safe yet. Unsized by-value `self` is object-safe (but not callable - // without unsized_locals), but wrappers around `Self` currently are not. - // FIXME (mikeyhew) uncomment this when unsized rvalues object-safety is implemented - // fn wrapper(self: Wrapper<Self>) -> i32; fn ptr_wrapper(self: Ptr<Wrapper<Self>>) -> i32; fn wrapper_ptr(self: Wrapper<Ptr<Self>>) -> i32; fn wrapper_ptr_wrapper(self: Wrapper<Ptr<Wrapper<Self>>>) -> i32; diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 1dc799c0aee..524ebde1c74 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -14,8 +14,14 @@ #![no_core] #![allow(dead_code, internal_features, ambiguous_wide_pointer_comparisons)] +#[lang = "pointee_sized"] +pub trait PointeeSized {} + +#[lang = "meta_sized"] +pub trait MetaSized: PointeeSized {} + #[lang = "sized"] -pub trait Sized {} +pub trait Sized: MetaSized {} #[lang = "destruct"] pub trait Destruct {} @@ -24,35 +30,35 @@ pub trait Destruct {} pub trait Tuple {} #[lang = "unsize"] -pub trait Unsize<T: ?Sized> {} +pub trait Unsize<T: PointeeSized>: PointeeSized {} #[lang = "coerce_unsized"] pub trait CoerceUnsized<T> {} -impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} -impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {} -impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {} -impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} +impl<'a, 'b: 'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<&'a U> for &'b T {} +impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<&'a mut U> for &'a mut T {} +impl<T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*const U> for *const T {} +impl<T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*mut U> for *mut T {} #[lang = "dispatch_from_dyn"] pub trait DispatchFromDyn<T> {} // &T -> &U -impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} +impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<&'a U> for &'a T {} // &mut T -> &mut U -impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} +impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<&'a mut U> for &'a mut T {} // *const T -> *const U -impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {} +impl<T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<*const U> for *const T {} // *mut T -> *mut U -impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} -impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U>> for Box<T> {} +impl<T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<*mut U> for *mut T {} +impl<T: MetaSized + Unsize<U>, U: MetaSized> DispatchFromDyn<Box<U>> for Box<T> {} #[lang = "legacy_receiver"] pub trait LegacyReceiver {} -impl<T: ?Sized> LegacyReceiver for &T {} -impl<T: ?Sized> LegacyReceiver for &mut T {} -impl<T: ?Sized> LegacyReceiver for Box<T> {} +impl<T: PointeeSized> LegacyReceiver for &T {} +impl<T: PointeeSized> LegacyReceiver for &mut T {} +impl<T: MetaSized> LegacyReceiver for Box<T> {} #[lang = "copy"] pub trait Copy {} @@ -74,9 +80,9 @@ impl Copy for isize {} impl Copy for f32 {} impl Copy for f64 {} impl Copy for char {} -impl<'a, T: ?Sized> Copy for &'a T {} -impl<T: ?Sized> Copy for *const T {} -impl<T: ?Sized> Copy for *mut T {} +impl<'a, T: PointeeSized> Copy for &'a T {} +impl<T: PointeeSized> Copy for *const T {} +impl<T: PointeeSized> Copy for *mut T {} impl<T: Copy> Copy for Option<T> {} #[lang = "sync"] @@ -94,17 +100,17 @@ unsafe impl Sync for i32 {} unsafe impl Sync for isize {} unsafe impl Sync for char {} unsafe impl Sync for f32 {} -unsafe impl<'a, T: ?Sized> Sync for &'a T {} +unsafe impl<'a, T: PointeeSized> Sync for &'a T {} unsafe impl<T: Sync, const N: usize> Sync for [T; N] {} #[lang = "freeze"] unsafe auto trait Freeze {} -unsafe impl<T: ?Sized> Freeze for PhantomData<T> {} -unsafe impl<T: ?Sized> Freeze for *const T {} -unsafe impl<T: ?Sized> Freeze for *mut T {} -unsafe impl<T: ?Sized> Freeze for &T {} -unsafe impl<T: ?Sized> Freeze for &mut T {} +unsafe impl<T: PointeeSized> Freeze for PhantomData<T> {} +unsafe impl<T: PointeeSized> Freeze for *const T {} +unsafe impl<T: PointeeSized> Freeze for *mut T {} +unsafe impl<T: PointeeSized> Freeze for &T {} +unsafe impl<T: PointeeSized> Freeze for &mut T {} #[lang = "structural_peq"] pub trait StructuralPartialEq {} @@ -443,7 +449,7 @@ pub enum Option<T> { pub use Option::*; #[lang = "phantom_data"] -pub struct PhantomData<T: ?Sized>; +pub struct PhantomData<T: PointeeSized>; #[lang = "fn_once"] #[rustc_paren_sugar] @@ -564,18 +570,18 @@ pub trait Deref { #[repr(transparent)] #[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] -pub struct NonNull<T: ?Sized>(pub *const T); +pub struct NonNull<T: PointeeSized>(pub *const T); -impl<T: ?Sized, U: ?Sized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {} -impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {} +impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {} +impl<T: PointeeSized, U: PointeeSized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {} -pub struct Unique<T: ?Sized> { +pub struct Unique<T: PointeeSized> { pub pointer: NonNull<T>, pub _marker: PhantomData<T>, } -impl<T: ?Sized, U: ?Sized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsize<U> {} -impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {} +impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsize<U> {} +impl<T: PointeeSized, U: PointeeSized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {} #[lang = "global_alloc_ty"] pub struct Global; @@ -644,9 +650,9 @@ pub mod intrinsics { #[rustc_intrinsic] pub unsafe fn size_of_val<T: ?::Sized>(val: *const T) -> usize; #[rustc_intrinsic] - pub fn min_align_of<T>() -> usize; + pub fn align_of<T>() -> usize; #[rustc_intrinsic] - pub unsafe fn min_align_of_val<T: ?::Sized>(val: *const T) -> usize; + pub unsafe fn align_of_val<T: ?::Sized>(val: *const T) -> usize; #[rustc_intrinsic] pub unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize); #[rustc_intrinsic] diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 93ca2e0e421..1499f948deb 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -204,11 +204,8 @@ fn main() { assert_eq!(intrinsics::size_of_val(a) as u8, 16); assert_eq!(intrinsics::size_of_val(&0u32) as u8, 4); - assert_eq!(intrinsics::min_align_of::<u16>() as u8, 2); - assert_eq!( - intrinsics::min_align_of_val(&a) as u8, - intrinsics::min_align_of::<&str>() as u8 - ); + assert_eq!(intrinsics::align_of::<u16>() as u8, 2); + assert_eq!(intrinsics::align_of_val(&a) as u8, intrinsics::align_of::<&str>() as u8); assert!(!intrinsics::needs_drop::<u8>()); assert!(!intrinsics::needs_drop::<[u8]>()); diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index fe5b220117f..4c6fd907815 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -51,6 +51,11 @@ pub(crate) fn conv_to_call_conv( CanonAbi::Rust | CanonAbi::C => default_call_conv, CanonAbi::RustCold => CallConv::Cold, + // Functions with this calling convention can only be called from assembly, but it is + // possible to declare an `extern "custom"` block, so the backend still needs a calling + // convention for declaring foreign functions. + CanonAbi::Custom => default_call_conv, + CanonAbi::X86(x86_call) => match x86_call { X86Call::SysV64 => CallConv::SystemV, X86Call::Win64 => CallConv::WindowsFastcall, diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index 6d8614aca69..cd0afee0cfb 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -1,6 +1,6 @@ //! Argument passing -use cranelift_codegen::ir::{ArgumentExtension, ArgumentPurpose}; +use cranelift_codegen::ir::ArgumentPurpose; use rustc_abi::{Reg, RegKind}; use rustc_target::callconv::{ ArgAbi, ArgAttributes, ArgExtension as RustcArgExtension, CastTarget, PassMode, @@ -32,16 +32,26 @@ fn reg_to_abi_param(reg: Reg) -> AbiParam { AbiParam::new(clif_ty) } -fn apply_arg_attrs_to_abi_param(mut param: AbiParam, arg_attrs: ArgAttributes) -> AbiParam { +fn apply_attrs_to_abi_param(param: AbiParam, arg_attrs: ArgAttributes) -> AbiParam { match arg_attrs.arg_ext { - RustcArgExtension::None => {} - RustcArgExtension::Zext => param.extension = ArgumentExtension::Uext, - RustcArgExtension::Sext => param.extension = ArgumentExtension::Sext, + RustcArgExtension::None => param, + RustcArgExtension::Zext => param.uext(), + RustcArgExtension::Sext => param.sext(), } - param } -fn cast_target_to_abi_params(cast: &CastTarget) -> SmallVec<[AbiParam; 2]> { +fn cast_target_to_abi_params(cast: &CastTarget) -> SmallVec<[(Size, AbiParam); 2]> { + if let Some(offset_from_start) = cast.rest_offset { + assert!(cast.prefix[1..].iter().all(|p| p.is_none())); + assert_eq!(cast.rest.unit.size, cast.rest.total); + let first = cast.prefix[0].unwrap(); + let second = cast.rest.unit; + return smallvec![ + (Size::ZERO, reg_to_abi_param(first)), + (offset_from_start, reg_to_abi_param(second)) + ]; + } + let (rest_count, rem_bytes) = if cast.rest.unit.size.bytes() == 0 { (0, 0) } else { @@ -56,25 +66,32 @@ fn cast_target_to_abi_params(cast: &CastTarget) -> SmallVec<[AbiParam; 2]> { // different types in Cranelift IR. Instead a single array of primitive types is used. // Create list of fields in the main structure - let mut args = cast + let args = cast .prefix .iter() .flatten() .map(|®| reg_to_abi_param(reg)) - .chain((0..rest_count).map(|_| reg_to_abi_param(cast.rest.unit))) - .collect::<SmallVec<_>>(); + .chain((0..rest_count).map(|_| reg_to_abi_param(cast.rest.unit))); + + let mut res = SmallVec::new(); + let mut offset = Size::ZERO; + + for arg in args { + res.push((offset, arg)); + offset += Size::from_bytes(arg.value_type.bytes()); + } // Append final integer if rem_bytes != 0 { // Only integers can be really split further. assert_eq!(cast.rest.unit.kind, RegKind::Integer); - args.push(reg_to_abi_param(Reg { - kind: RegKind::Integer, - size: Size::from_bytes(rem_bytes), - })); + res.push(( + offset, + reg_to_abi_param(Reg { kind: RegKind::Integer, size: Size::from_bytes(rem_bytes) }), + )); } - args + res } impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { @@ -82,7 +99,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { match self.mode { PassMode::Ignore => smallvec![], PassMode::Direct(attrs) => match self.layout.backend_repr { - BackendRepr::Scalar(scalar) => smallvec![apply_arg_attrs_to_abi_param( + BackendRepr::Scalar(scalar) => smallvec![apply_attrs_to_abi_param( AbiParam::new(scalar_to_clif_type(tcx, scalar)), attrs )], @@ -97,34 +114,34 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { let a = scalar_to_clif_type(tcx, a); let b = scalar_to_clif_type(tcx, b); smallvec![ - apply_arg_attrs_to_abi_param(AbiParam::new(a), attrs_a), - apply_arg_attrs_to_abi_param(AbiParam::new(b), attrs_b), + apply_attrs_to_abi_param(AbiParam::new(a), attrs_a), + apply_attrs_to_abi_param(AbiParam::new(b), attrs_b), ] } _ => unreachable!("{:?}", self.layout.backend_repr), }, PassMode::Cast { ref cast, pad_i32 } => { assert!(!pad_i32, "padding support not yet implemented"); - cast_target_to_abi_params(cast) + cast_target_to_abi_params(cast).into_iter().map(|(_, param)| param).collect() } PassMode::Indirect { attrs, meta_attrs: None, on_stack } => { if on_stack { // Abi requires aligning struct size to pointer size let size = self.layout.size.align_to(tcx.data_layout.pointer_align.abi); let size = u32::try_from(size.bytes()).unwrap(); - smallvec![apply_arg_attrs_to_abi_param( + smallvec![apply_attrs_to_abi_param( AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructArgument(size),), attrs )] } else { - smallvec![apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs)] + smallvec![apply_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs)] } } PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => { assert!(!on_stack); smallvec![ - apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs), - apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), meta_attrs), + apply_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs), + apply_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), meta_attrs), ] } } @@ -133,30 +150,47 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option<AbiParam>, Vec<AbiParam>) { match self.mode { PassMode::Ignore => (None, vec![]), - PassMode::Direct(_) => match self.layout.backend_repr { - BackendRepr::Scalar(scalar) => { - (None, vec![AbiParam::new(scalar_to_clif_type(tcx, scalar))]) - } + PassMode::Direct(attrs) => match self.layout.backend_repr { + BackendRepr::Scalar(scalar) => ( + None, + vec![apply_attrs_to_abi_param( + AbiParam::new(scalar_to_clif_type(tcx, scalar)), + attrs, + )], + ), BackendRepr::SimdVector { .. } => { let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout); - (None, vec![AbiParam::new(vector_ty)]) + (None, vec![apply_attrs_to_abi_param(AbiParam::new(vector_ty), attrs)]) } _ => unreachable!("{:?}", self.layout.backend_repr), }, - PassMode::Pair(_, _) => match self.layout.backend_repr { + PassMode::Pair(attrs_a, attrs_b) => match self.layout.backend_repr { BackendRepr::ScalarPair(a, b) => { let a = scalar_to_clif_type(tcx, a); let b = scalar_to_clif_type(tcx, b); - (None, vec![AbiParam::new(a), AbiParam::new(b)]) + ( + None, + vec![ + apply_attrs_to_abi_param(AbiParam::new(a), attrs_a), + apply_attrs_to_abi_param(AbiParam::new(b), attrs_b), + ], + ) } _ => unreachable!("{:?}", self.layout.backend_repr), }, - PassMode::Cast { ref cast, .. } => { - (None, cast_target_to_abi_params(cast).into_iter().collect()) - } - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack } => { + PassMode::Cast { ref cast, .. } => ( + None, + cast_target_to_abi_params(cast).into_iter().map(|(_, param)| param).collect(), + ), + PassMode::Indirect { attrs, meta_attrs: None, on_stack } => { assert!(!on_stack); - (Some(AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructReturn)), vec![]) + ( + Some(apply_attrs_to_abi_param( + AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructReturn), + attrs, + )), + vec![], + ) } PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { unreachable!("unsized return value") @@ -172,12 +206,14 @@ pub(super) fn to_casted_value<'tcx>( ) -> SmallVec<[Value; 2]> { let (ptr, meta) = arg.force_stack(fx); assert!(meta.is_none()); - let mut offset = 0; cast_target_to_abi_params(cast) .into_iter() - .map(|param| { - let val = ptr.offset_i64(fx, offset).load(fx, param.value_type, MemFlags::new()); - offset += i64::from(param.value_type.bytes()); + .map(|(offset, param)| { + let val = ptr.offset_i64(fx, offset.bytes() as i64).load( + fx, + param.value_type, + MemFlags::new(), + ); val }) .collect() @@ -190,7 +226,7 @@ pub(super) fn from_casted_value<'tcx>( cast: &CastTarget, ) -> CValue<'tcx> { let abi_params = cast_target_to_abi_params(cast); - let abi_param_size: u32 = abi_params.iter().map(|param| param.value_type.bytes()).sum(); + let abi_param_size: u32 = abi_params.iter().map(|(_, param)| param.value_type.bytes()).sum(); let layout_size = u32::try_from(layout.size.bytes()).unwrap(); let ptr = fx.create_stack_slot( // Stack slot size may be bigger for example `[u8; 3]` which is packed into an `i32`. @@ -199,16 +235,13 @@ pub(super) fn from_casted_value<'tcx>( std::cmp::max(abi_param_size, layout_size), u32::try_from(layout.align.abi.bytes()).unwrap(), ); - let mut offset = 0; let mut block_params_iter = block_params.iter().copied(); - for param in abi_params { - let val = ptr.offset_i64(fx, offset).store( + for (offset, _) in abi_params { + ptr.offset_i64(fx, offset.bytes() as i64).store( fx, block_params_iter.next().unwrap(), MemFlags::new(), - ); - offset += i64::from(param.value_type.bytes()); - val + ) } assert_eq!(block_params_iter.next(), None, "Leftover block param"); CValue::by_ref(ptr, layout) diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index 9cff8a84db3..ffb932a3c38 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -1,6 +1,7 @@ //! Allocator shim // Adapted from rustc +use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use rustc_ast::expand::allocator::{ ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, alloc_error_handler_name, default_fn_name, global_fn_name, @@ -97,16 +98,31 @@ fn codegen_inner( data.define(Box::new([val])); module.define_data(data_id, &data).unwrap(); - let data_id = module - .declare_data( - &mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), - Linkage::Export, - false, - false, - ) - .unwrap(); - let mut data = DataDescription::new(); - data.set_align(1); - data.define(Box::new([0])); - module.define_data(data_id, &data).unwrap(); + { + let sig = Signature { + call_conv: module.target_config().default_call_conv, + params: vec![], + returns: vec![], + }; + let func_id = module + .declare_function( + &mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), + Linkage::Export, + &sig, + ) + .unwrap(); + + let mut ctx = Context::new(); + ctx.func.signature = sig; + let mut func_ctx = FunctionBuilderContext::new(); + let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); + + let block = bcx.create_block(); + bcx.switch_to_block(block); + bcx.ins().return_(&[]); + bcx.seal_all_blocks(); + bcx.finalize(); + + module.define_function(func_id, &mut ctx).unwrap(); + } } diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 5d07c94859f..442151fe32d 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -11,7 +11,6 @@ use std::thread::JoinHandle; use cranelift_object::{ObjectBuilder, ObjectModule}; use rustc_codegen_ssa::assert_module_sources::CguReuse; use rustc_codegen_ssa::back::link::ensure_removed; -use rustc_codegen_ssa::back::metadata::create_compressed_metadata_file; use rustc_codegen_ssa::base::determine_cgu_reuse; use rustc_codegen_ssa::{ CodegenResults, CompiledModule, CrateInfo, ModuleKind, errors as ssa_errors, @@ -19,7 +18,6 @@ use rustc_codegen_ssa::{ use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; -use rustc_metadata::EncodedMetadata; use rustc_metadata::fs::copy_to_stdout; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -61,8 +59,6 @@ impl<HCX> HashStable<HCX> for OngoingModuleCodegen { pub(crate) struct OngoingCodegen { modules: Vec<OngoingModuleCodegen>, allocator_module: Option<CompiledModule>, - metadata_module: Option<CompiledModule>, - metadata: EncodedMetadata, crate_info: CrateInfo, concurrency_limiter: ConcurrencyLimiter, } @@ -134,8 +130,6 @@ impl OngoingCodegen { let codegen_results = CodegenResults { modules, allocator_module: self.allocator_module, - metadata_module: self.metadata_module, - metadata: self.metadata, crate_info: self.crate_info, }; @@ -646,42 +640,6 @@ 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_for_cgu( - OutputType::Metadata, - &metadata_cgu_name, - tcx.sess.invocation_temp.as_deref(), - ); - - 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, - links_from_incr_cache: Vec::new(), - } -} - 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); @@ -706,11 +664,7 @@ fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option<CompiledModule> { } } -pub(crate) fn run_aot( - tcx: TyCtxt<'_>, - metadata: EncodedMetadata, - need_metadata_module: bool, -) -> Box<OngoingCodegen> { +pub(crate) fn run_aot(tcx: TyCtxt<'_>) -> Box<OngoingCodegen> { // FIXME handle `-Ctarget-cpu=native` let target_cpu = match tcx.sess.opts.cg.target_cpu { Some(ref name) => name, @@ -726,8 +680,6 @@ pub(crate) fn run_aot( return Box::new(OngoingCodegen { modules: vec![], allocator_module: None, - metadata_module: None, - metadata, crate_info: CrateInfo::new(tcx, target_cpu), concurrency_limiter: ConcurrencyLimiter::new(0), }); @@ -787,14 +739,9 @@ pub(crate) fn run_aot( let allocator_module = emit_allocator_module(tcx); - let metadata_module = - if need_metadata_module { Some(emit_metadata_module(tcx, &metadata)) } else { None }; - Box::new(OngoingCodegen { modules, allocator_module, - metadata_module, - metadata, crate_info: CrateInfo::new(tcx, target_cpu), concurrency_limiter: concurrency_limiter.0, }) diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 1d1cf884e48..df5748c34d1 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -586,7 +586,7 @@ fn codegen_regular_intrinsic_call<'tcx>( let (size, _align) = crate::unsize::size_and_align_of(fx, layout, meta); ret.write_cvalue(fx, CValue::by_val(size, usize_layout)); } - sym::min_align_of_val => { + sym::align_of_val => { intrinsic_args!(fx, args => (ptr); intrinsic); let layout = fx.layout_of(generic_args.type_at(0)); @@ -613,7 +613,7 @@ fn codegen_regular_intrinsic_call<'tcx>( intrinsic_args!(fx, args => (vtable); intrinsic); let vtable = vtable.load_scalar(fx); - let align = crate::vtable::min_align_of_obj(fx, vtable); + let align = crate::vtable::align_of_obj(fx, vtable); ret.write_cvalue(fx, CValue::by_val(align, usize_layout)); } diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 8ef623cde00..07ea29f3024 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -46,7 +46,6 @@ use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::settings::{self, Configurable}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CodegenResults, TargetConfig}; -use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; use rustc_session::config::OutputFilenames; @@ -238,12 +237,7 @@ impl CodegenBackend for CraneliftCodegenBackend { println!("Cranelift version: {}", cranelift_codegen::VERSION); } - fn codegen_crate( - &self, - tcx: TyCtxt<'_>, - metadata: EncodedMetadata, - need_metadata_module: bool, - ) -> Box<dyn Any> { + fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box<dyn Any> { info!("codegen crate {}", tcx.crate_name(LOCAL_CRATE)); let config = self.config.clone().unwrap_or_else(|| { BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args) @@ -256,7 +250,7 @@ impl CodegenBackend for CraneliftCodegenBackend { #[cfg(not(feature = "jit"))] tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); } else { - driver::aot::run_aot(tcx, metadata, need_metadata_module) + driver::aot::run_aot(tcx) } } diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 662546e4999..df60b05c463 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -212,7 +212,7 @@ pub(crate) fn size_and_align_of<'tcx>( // load size/align from vtable ( crate::vtable::size_of_obj(fx, info.unwrap()), - crate::vtable::min_align_of_obj(fx, info.unwrap()), + crate::vtable::align_of_obj(fx, info.unwrap()), ) } ty::Slice(_) | ty::Str => { diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs index 05a8e3c3342..1fae56949bc 100644 --- a/compiler/rustc_codegen_cranelift/src/vtable.rs +++ b/compiler/rustc_codegen_cranelift/src/vtable.rs @@ -31,7 +31,7 @@ pub(crate) fn size_of_obj(fx: &mut FunctionCx<'_, '_, '_>, vtable: Value) -> Val ) } -pub(crate) fn min_align_of_obj(fx: &mut FunctionCx<'_, '_, '_>, vtable: Value) -> Value { +pub(crate) fn align_of_obj(fx: &mut FunctionCx<'_, '_, '_>, vtable: Value) -> Value { let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize; fx.bcx.ins().load( fx.pointer_type, diff --git a/compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs b/compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs index b299aa87974..c26606f0bdd 100644 --- a/compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs +++ b/compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs @@ -37,10 +37,6 @@ impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<Wrapper<U>> for Wrapper<T> {} trait Trait { - // This method isn't object-safe yet. Unsized by-value `self` is object-safe (but not callable - // without unsized_locals), but wrappers around `Self` currently are not. - // FIXME (mikeyhew) uncomment this when unsized rvalues object-safety is implemented - // fn wrapper(self: Wrapper<Self>) -> i32; fn ptr_wrapper(self: Ptr<Wrapper<Self>>) -> i32; fn wrapper_ptr(self: Wrapper<Ptr<Self>>) -> i32; fn wrapper_ptr_wrapper(self: Wrapper<Ptr<Wrapper<Self>>>) -> i32; diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index d1d8e8fd5bc..9dfb12be243 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -19,8 +19,14 @@ unsafe extern "C" fn _Unwind_Resume() { intrinsics::unreachable(); } +#[lang = "pointee_sized"] +pub trait PointeeSized {} + +#[lang = "meta_sized"] +pub trait MetaSized: PointeeSized {} + #[lang = "sized"] -pub trait Sized {} +pub trait Sized: MetaSized {} #[lang = "destruct"] pub trait Destruct {} @@ -29,35 +35,35 @@ pub trait Destruct {} pub trait Tuple {} #[lang = "unsize"] -pub trait Unsize<T: ?Sized> {} +pub trait Unsize<T: PointeeSized>: PointeeSized {} #[lang = "coerce_unsized"] pub trait CoerceUnsized<T> {} -impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} -impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {} -impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {} -impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} +impl<'a, 'b: 'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<&'a U> for &'b T {} +impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<&'a mut U> for &'a mut T {} +impl<T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*const U> for *const T {} +impl<T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*mut U> for *mut T {} #[lang = "dispatch_from_dyn"] pub trait DispatchFromDyn<T> {} // &T -> &U -impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} +impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<&'a U> for &'a T {} // &mut T -> &mut U -impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} +impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<&'a mut U> for &'a mut T {} // *const T -> *const U -impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {} +impl<T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<*const U> for *const T {} // *mut T -> *mut U -impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} -impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U, ()>> for Box<T, ()> {} +impl<T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<*mut U> for *mut T {} +impl<T: MetaSized + Unsize<U>, U: MetaSized> DispatchFromDyn<Box<U, ()>> for Box<T, ()> {} #[lang = "legacy_receiver"] pub trait LegacyReceiver {} -impl<T: ?Sized> LegacyReceiver for &T {} -impl<T: ?Sized> LegacyReceiver for &mut T {} -impl<T: ?Sized, A: Allocator> LegacyReceiver for Box<T, A> {} +impl<T: PointeeSized> LegacyReceiver for &T {} +impl<T: PointeeSized> LegacyReceiver for &mut T {} +impl<T: MetaSized> LegacyReceiver for Box<T> {} #[lang = "receiver"] trait Receiver {} @@ -84,9 +90,9 @@ impl Copy for i128 {} impl Copy for f32 {} impl Copy for f64 {} impl Copy for char {} -impl<'a, T: ?Sized> Copy for &'a T {} -impl<T: ?Sized> Copy for *const T {} -impl<T: ?Sized> Copy for *mut T {} +impl<'a, T: PointeeSized> Copy for &'a T {} +impl<T: PointeeSized> Copy for *const T {} +impl<T: PointeeSized> Copy for *mut T {} #[lang = "sync"] pub unsafe trait Sync {} @@ -102,17 +108,17 @@ unsafe impl Sync for i16 {} unsafe impl Sync for i32 {} unsafe impl Sync for isize {} unsafe impl Sync for char {} -unsafe impl<'a, T: ?Sized> Sync for &'a T {} +unsafe impl<'a, T: PointeeSized> Sync for &'a T {} unsafe impl Sync for [u8; 16] {} #[lang = "freeze"] unsafe auto trait Freeze {} -unsafe impl<T: ?Sized> Freeze for PhantomData<T> {} -unsafe impl<T: ?Sized> Freeze for *const T {} -unsafe impl<T: ?Sized> Freeze for *mut T {} -unsafe impl<T: ?Sized> Freeze for &T {} -unsafe impl<T: ?Sized> Freeze for &mut T {} +unsafe impl<T: PointeeSized> Freeze for PhantomData<T> {} +unsafe impl<T: PointeeSized> Freeze for *const T {} +unsafe impl<T: PointeeSized> Freeze for *mut T {} +unsafe impl<T: PointeeSized> Freeze for &T {} +unsafe impl<T: PointeeSized> Freeze for &mut T {} #[lang = "structural_peq"] pub trait StructuralPartialEq {} @@ -456,7 +462,7 @@ pub enum Option<T> { pub use Option::*; #[lang = "phantom_data"] -pub struct PhantomData<T: ?Sized>; +pub struct PhantomData<T: PointeeSized>; #[lang = "fn_once"] #[rustc_paren_sugar] @@ -576,18 +582,18 @@ impl Allocator for Global {} #[repr(transparent)] #[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] -pub struct NonNull<T: ?Sized>(pub *const T); +pub struct NonNull<T: PointeeSized>(pub *const T); -impl<T: ?Sized, U: ?Sized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {} -impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {} +impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {} +impl<T: PointeeSized, U: PointeeSized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {} -pub struct Unique<T: ?Sized> { +pub struct Unique<T: PointeeSized> { pub pointer: NonNull<T>, pub _marker: PhantomData<T>, } -impl<T: ?Sized, U: ?Sized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsize<U> {} -impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {} +impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsize<U> {} +impl<T: PointeeSized, U: PointeeSized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {} #[lang = "owned_box"] pub struct Box<T: ?Sized, A: Allocator = Global>(Unique<T>, A); @@ -655,9 +661,9 @@ pub mod intrinsics { #[rustc_intrinsic] pub unsafe fn size_of_val<T: ?::Sized>(val: *const T) -> usize; #[rustc_intrinsic] - pub fn min_align_of<T>() -> usize; + pub fn align_of<T>() -> usize; #[rustc_intrinsic] - pub unsafe fn min_align_of_val<T: ?::Sized>(val: *const T) -> usize; + pub unsafe fn align_of_val<T: ?::Sized>(val: *const T) -> usize; #[rustc_intrinsic] pub unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize); #[rustc_intrinsic] diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs index 4cbe66c5e4c..c3bd62e5897 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs @@ -153,7 +153,7 @@ fn main() { let slice = &[0, 1] as &[i32]; let slice_ptr = slice as *const [i32] as *const i32; - let align = intrinsics::min_align_of::<*const i32>(); + let align = intrinsics::align_of::<*const i32>(); assert_eq!(slice_ptr as usize % align, 0); //return; @@ -194,8 +194,8 @@ fn main() { assert_eq!(intrinsics::size_of_val(a) as u8, 8); assert_eq!(intrinsics::size_of_val(&0u32) as u8, 4); - assert_eq!(intrinsics::min_align_of::<u16>() as u8, 2); - assert_eq!(intrinsics::min_align_of_val(&a) as u8, intrinsics::min_align_of::<&str>() as u8); + assert_eq!(intrinsics::align_of::<u16>() as u8, 2); + assert_eq!(intrinsics::align_of_val(&a) as u8, intrinsics::align_of::<&str>() as u8); assert!(!intrinsics::needs_drop::<u8>()); assert!(!intrinsics::needs_drop::<[u8]>()); diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl index 546bfc87b68..18a8a5a1e04 100644 --- a/compiler/rustc_codegen_gcc/messages.ftl +++ b/compiler/rustc_codegen_gcc/messages.ftl @@ -2,9 +2,6 @@ codegen_gcc_unknown_ctarget_feature_prefix = unknown feature specified for `-Ctarget-feature`: `{$feature}` .note = features must begin with a `+` to enable or `-` to disable it -codegen_gcc_forbidden_ctarget_feature = - target feature `{$feature}` cannot be toggled with `-Ctarget-feature`: {$reason} - codegen_gcc_unwinding_inline_asm = GCC backend does not support unwinding from inline asm @@ -26,10 +23,6 @@ codegen_gcc_unknown_ctarget_feature = .possible_feature = you might have meant: `{$rust_feature}` .consider_filing_feature_request = consider filing a feature request -codegen_gcc_unstable_ctarget_feature = - unstable feature specified for `-Ctarget-feature`: `{$feature}` - .note = this feature is not stably supported; its behavior can change in the future - codegen_gcc_missing_features = add the missing features in a `target_feature` attribute diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 3d0c258f576..08f3d281904 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -239,12 +239,16 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &str) -> Option<FnAttribute<'gcc>> { let attribute = match conv { CanonAbi::C | CanonAbi::Rust => return None, + CanonAbi::RustCold => FnAttribute::Cold, + // Functions with this calling convention can only be called from assembly, but it is + // possible to declare an `extern "custom"` block, so the backend still needs a calling + // convention for declaring foreign functions. + CanonAbi::Custom => return None, CanonAbi::Arm(arm_call) => match arm_call { ArmCall::CCmseNonSecureCall => FnAttribute::ArmCmseNonsecureCall, ArmCall::CCmseNonSecureEntry => FnAttribute::ArmCmseNonsecureEntry, ArmCall::Aapcs => FnAttribute::ArmPcs("aapcs"), }, - CanonAbi::RustCold => FnAttribute::Cold, CanonAbi::GpuKernel => { if arch == "amdgpu" { FnAttribute::GcnAmdGpuHsaKernel diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs index f4ebd42ee2d..cf8aa500c77 100644 --- a/compiler/rustc_codegen_gcc/src/allocator.rs +++ b/compiler/rustc_codegen_gcc/src/allocator.rs @@ -57,7 +57,7 @@ pub(crate) unsafe fn codegen( let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); - create_wrapper_function(tcx, context, &from_name, &to_name, &types, output); + create_wrapper_function(tcx, context, &from_name, Some(&to_name), &types, output); } } @@ -66,7 +66,7 @@ pub(crate) unsafe fn codegen( tcx, context, &mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), - &mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind)), + Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))), &[usize, usize], None, ); @@ -81,21 +81,21 @@ pub(crate) unsafe fn codegen( let value = context.new_rvalue_from_int(i8, value as i32); global.global_set_initializer_rvalue(value); - let name = mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE); - let global = context.new_global(None, GlobalKind::Exported, i8, name); - #[cfg(feature = "master")] - global.add_attribute(VarAttribute::Visibility(symbol_visibility_to_gcc( - tcx.sess.default_visibility(), - ))); - let value = context.new_rvalue_from_int(i8, 0); - global.global_set_initializer_rvalue(value); + create_wrapper_function( + tcx, + context, + &mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), + None, + &[], + None, + ); } fn create_wrapper_function( tcx: TyCtxt<'_>, context: &Context<'_>, from_name: &str, - to_name: &str, + to_name: Option<&str>, types: &[Type<'_>], output: Option<Type<'_>>, ) { @@ -124,34 +124,40 @@ fn create_wrapper_function( // TODO(antoyo): emit unwind tables. } - let args: Vec<_> = types - .iter() - .enumerate() - .map(|(index, typ)| context.new_parameter(None, *typ, format!("param{}", index))) - .collect(); - let callee = context.new_function( - None, - FunctionType::Extern, - output.unwrap_or(void), - &args, - to_name, - false, - ); - #[cfg(feature = "master")] - callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); - let block = func.new_block("entry"); - let args = args - .iter() - .enumerate() - .map(|(i, _)| func.get_param(i as i32).to_rvalue()) - .collect::<Vec<_>>(); - let ret = context.new_call(None, callee, &args); - //llvm::LLVMSetTailCall(ret, True); - if output.is_some() { - block.end_with_return(None, ret); + if let Some(to_name) = to_name { + let args: Vec<_> = types + .iter() + .enumerate() + .map(|(index, typ)| context.new_parameter(None, *typ, format!("param{}", index))) + .collect(); + let callee = context.new_function( + None, + FunctionType::Extern, + output.unwrap_or(void), + &args, + to_name, + false, + ); + #[cfg(feature = "master")] + callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); + + let args = args + .iter() + .enumerate() + .map(|(i, _)| func.get_param(i as i32).to_rvalue()) + .collect::<Vec<_>>(); + let ret = context.new_call(None, callee, &args); + //llvm::LLVMSetTailCall(ret, True); + if output.is_some() { + block.end_with_return(None, ret); + } else { + block.add_eval(None, ret); + block.end_with_void_return(None); + } } else { + assert!(output.is_none()); block.end_with_void_return(None); } diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index d1fb8d8f9d6..68c6156fa4b 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -30,7 +30,7 @@ use rustc_middle::ty::{self, AtomicOrdering, Instance, Ty, TyCtxt}; use rustc_span::Span; use rustc_span::def_id::DefId; use rustc_target::callconv::FnAbi; -use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, WasmCAbi, X86Abi}; +use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, X86Abi}; use crate::common::{SignType, TypeReflection, type_is_pointer}; use crate::context::CodegenCx; @@ -897,7 +897,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn checked_binop( &mut self, oop: OverflowOp, - typ: Ty<'_>, + typ: Ty<'tcx>, lhs: Self::Value, rhs: Self::Value, ) -> (Self::Value, Self::Value) { @@ -2394,12 +2394,6 @@ impl<'tcx> HasTargetSpec for Builder<'_, '_, 'tcx> { } } -impl<'tcx> HasWasmCAbiOpt for Builder<'_, '_, 'tcx> { - fn wasm_c_abi_opt(&self) -> WasmCAbi { - self.cx.wasm_c_abi_opt() - } -} - impl<'tcx> HasX86AbiOpt for Builder<'_, '_, 'tcx> { fn x86_abi_opt(&self) -> X86Abi { self.cx.x86_abi_opt() diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index c6c43201f21..4955e039e7b 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -19,9 +19,7 @@ use rustc_middle::ty::{self, ExistentialTraitRef, Instance, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::source_map::respan; use rustc_span::{DUMMY_SP, Span}; -use rustc_target::spec::{ - HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, TlsModel, WasmCAbi, X86Abi, -}; +use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, TlsModel, X86Abi}; #[cfg(feature = "master")] use crate::abi::conv_to_fn_attribute; @@ -512,12 +510,6 @@ impl<'gcc, 'tcx> HasTargetSpec for CodegenCx<'gcc, 'tcx> { } } -impl<'gcc, 'tcx> HasWasmCAbiOpt for CodegenCx<'gcc, 'tcx> { - fn wasm_c_abi_opt(&self) -> WasmCAbi { - self.tcx.sess.opts.unstable_opts.wasm_c_abi - } -} - impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> { fn x86_abi_opt(&self) -> X86Abi { X86Abi { diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index ccd9abe3804..7786be9ae5d 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -17,21 +17,6 @@ pub(crate) struct UnknownCTargetFeature<'a> { pub rust_feature: PossibleFeature<'a>, } -#[derive(Diagnostic)] -#[diag(codegen_gcc_unstable_ctarget_feature)] -#[note] -pub(crate) struct UnstableCTargetFeature<'a> { - pub feature: &'a str, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_forbidden_ctarget_feature)] -pub(crate) struct ForbiddenCTargetFeature<'a> { - pub feature: &'a str, - pub enabled: &'a str, - pub reason: &'a str, -} - #[derive(Subdiagnostic)] pub(crate) enum PossibleFeature<'a> { #[help(codegen_gcc_possible_feature)] diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 2b053abdd19..d90e66aea31 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -5,13 +5,17 @@ use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unord::UnordSet; use rustc_session::Session; +use rustc_session::features::{StabilityExt, retpoline_features_by_flags}; use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; use smallvec::{SmallVec, smallvec}; -use crate::errors::{ - ForbiddenCTargetFeature, PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix, - UnstableCTargetFeature, -}; +use crate::errors::{PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix}; + +fn gcc_features_by_flags(sess: &Session) -> Vec<&str> { + let mut features: Vec<&str> = Vec::new(); + retpoline_features_by_flags(sess, &mut features); + features +} /// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, /// `--target` and similar). @@ -45,7 +49,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri // Compute implied features let mut all_rust_features = vec![]; - for feature in sess.opts.cg.target_feature.split(',') { + for feature in sess.opts.cg.target_feature.split(',').chain(gcc_features_by_flags(sess)) { if let Some(feature) = feature.strip_prefix('+') { all_rust_features.extend( UnordSet::from(sess.target.implied_target_features(feature)) @@ -94,18 +98,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri sess.dcx().emit_warn(unknown_feature); } Some(&(_, stability, _)) => { - if let Err(reason) = stability.toggle_allowed() { - sess.dcx().emit_warn(ForbiddenCTargetFeature { - feature, - enabled: if enable { "enabled" } else { "disabled" }, - reason, - }); - } else if stability.requires_nightly().is_some() { - // An unstable feature. Warn about using it. (It makes little sense - // to hard-error here since we just warn about fully unknown - // features above). - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); - } + stability.verify_feature_enabled_by_flag(sess, enable, feature); } } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 9e05b8f23aa..c921851b42b 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -626,7 +626,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { bx.lifetime_start(llscratch, scratch_size); // ... where we first store the value... - bx.store(val, llscratch, scratch_align); + rustc_codegen_ssa::mir::store_cast(bx, cast, val, llscratch, scratch_align); // ... and then memcpy it to the intended destination. bx.memcpy( diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 0591ffa42e4..dbecbc42618 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -48,7 +48,6 @@ extern crate rustc_index; #[cfg(feature = "master")] extern crate rustc_interface; extern crate rustc_macros; -extern crate rustc_metadata; extern crate rustc_middle; extern crate rustc_session; extern crate rustc_span; @@ -106,7 +105,6 @@ use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetCon use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::IntoDynSyncSend; use rustc_errors::DiagCtxtHandle; -use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; @@ -230,20 +228,9 @@ impl CodegenBackend for GccCodegenBackend { providers.global_backend_features = |tcx, ()| gcc_util::global_gcc_features(tcx.sess, true) } - fn codegen_crate( - &self, - tcx: TyCtxt<'_>, - metadata: EncodedMetadata, - need_metadata_module: bool, - ) -> Box<dyn Any> { + fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box<dyn Any> { let target_cpu = target_cpu(tcx.sess); - let res = codegen_crate( - self.clone(), - tcx, - target_cpu.to_string(), - metadata, - need_metadata_module, - ); + let res = codegen_crate(self.clone(), tcx, target_cpu.to_string()); Box::new(res) } diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index bda121c67fb..3faeb9b3b22 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -10,11 +10,6 @@ codegen_llvm_dynamic_linking_with_lto = codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture -codegen_llvm_forbidden_ctarget_feature = - target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason} - .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -codegen_llvm_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344> - codegen_llvm_from_llvm_diag = {$message} codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message} @@ -76,10 +71,6 @@ codegen_llvm_unknown_ctarget_feature_prefix = codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo -codegen_llvm_unstable_ctarget_feature = - unstable feature specified for `-Ctarget-feature`: `{$feature}` - .note = this feature is not stably supported; its behavior can change in the future - codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err} codegen_llvm_write_ir = failed to write LLVM IR to {$path} diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 119cd634f98..4b07c8aef91 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -229,7 +229,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { let llscratch = bx.alloca(scratch_size, scratch_align); bx.lifetime_start(llscratch, scratch_size); // ...store the value... - bx.store(val, llscratch, scratch_align); + rustc_codegen_ssa::mir::store_cast(bx, cast, val, llscratch, scratch_align); // ... and then memcpy it to the intended destination. bx.memcpy( dst.val.llval, @@ -649,6 +649,10 @@ impl llvm::CallConv { match conv { CanonAbi::C | CanonAbi::Rust => llvm::CCallConv, CanonAbi::RustCold => llvm::PreserveMost, + // Functions with this calling convention can only be called from assembly, but it is + // possible to declare an `extern "custom"` block, so the backend still needs a calling + // convention for declaring foreign functions. + CanonAbi::Custom => llvm::CCallConv, CanonAbi::GpuKernel => { if arch == "amdgpu" { llvm::AmdgpuKernel diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 4a78e694979..9dca63cfc8d 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -57,7 +57,7 @@ pub(crate) unsafe fn codegen( let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); - create_wrapper_function(tcx, &cx, &from_name, &to_name, &args, output, false); + create_wrapper_function(tcx, &cx, &from_name, Some(&to_name), &args, output, false); } } @@ -66,7 +66,7 @@ pub(crate) unsafe fn codegen( tcx, &cx, &mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), - &mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind)), + Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))), &[usize, usize], // size, align None, true, @@ -81,11 +81,16 @@ pub(crate) unsafe fn codegen( let llval = llvm::LLVMConstInt(i8, val as u64, False); llvm::set_initializer(ll_g, llval); - let name = mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE); - let ll_g = cx.declare_global(&name, i8); - llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility())); - let llval = llvm::LLVMConstInt(i8, 0, False); - llvm::set_initializer(ll_g, llval); + // __rust_no_alloc_shim_is_unstable_v2 + create_wrapper_function( + tcx, + &cx, + &mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), + None, + &[], + None, + false, + ); } if tcx.sess.opts.debuginfo != DebugInfo::None { @@ -99,7 +104,7 @@ fn create_wrapper_function( tcx: TyCtxt<'_>, cx: &SimpleCx<'_>, from_name: &str, - to_name: &str, + to_name: Option<&str>, args: &[&Type], output: Option<&Type>, no_return: bool, @@ -128,33 +133,38 @@ fn create_wrapper_function( attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]); } - let callee = declare_simple_fn( - &cx, - to_name, - llvm::CallConv::CCallConv, - llvm::UnnamedAddr::Global, - llvm::Visibility::Hidden, - ty, - ); - if let Some(no_return) = no_return { - // -> ! DIFlagNoReturn - attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]); - } - llvm::set_visibility(callee, llvm::Visibility::Hidden); - let llbb = unsafe { llvm::LLVMAppendBasicBlockInContext(cx.llcx, llfn, c"entry".as_ptr()) }; - let mut bx = SBuilder::build(&cx, llbb); - let args = args - .iter() - .enumerate() - .map(|(i, _)| llvm::get_param(llfn, i as c_uint)) - .collect::<Vec<_>>(); - let ret = bx.call(ty, callee, &args, None); - llvm::LLVMSetTailCall(ret, True); - if output.is_some() { - bx.ret(ret); + + if let Some(to_name) = to_name { + let callee = declare_simple_fn( + &cx, + to_name, + llvm::CallConv::CCallConv, + llvm::UnnamedAddr::Global, + llvm::Visibility::Hidden, + ty, + ); + if let Some(no_return) = no_return { + // -> ! DIFlagNoReturn + attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]); + } + llvm::set_visibility(callee, llvm::Visibility::Hidden); + + let args = args + .iter() + .enumerate() + .map(|(i, _)| llvm::get_param(llfn, i as c_uint)) + .collect::<Vec<_>>(); + let ret = bx.call(ty, callee, &args, None); + llvm::LLVMSetTailCall(ret, True); + if output.is_some() { + bx.ret(ret); + } else { + bx.ret_void() + } } else { + assert!(output.is_none()); bx.ret_void() } } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 4185aef8b31..9ddadcf16aa 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -1021,6 +1021,15 @@ fn llvm_fixup_input<'ll, 'tcx>( ) if element.primitive() == Primitive::Float(Float::F16) => { bx.bitcast(value, bx.type_vector(bx.type_i16(), count)) } + (LoongArch(LoongArchInlineAsmRegClass::freg), BackendRepr::Scalar(s)) + if s.primitive() == Primitive::Float(Float::F16) => + { + // Smaller floats are always "NaN-boxed" inside larger floats on LoongArch. + let value = bx.bitcast(value, bx.type_i16()); + let value = bx.zext(value, bx.type_i32()); + let value = bx.or(value, bx.const_u32(0xFFFF_0000)); + bx.bitcast(value, bx.type_f32()) + } (Mips(MipsInlineAsmRegClass::reg), BackendRepr::Scalar(s)) => { match s.primitive() { // MIPS only supports register-length arithmetics. @@ -1178,6 +1187,13 @@ fn llvm_fixup_output<'ll, 'tcx>( ) if element.primitive() == Primitive::Float(Float::F16) => { bx.bitcast(value, bx.type_vector(bx.type_f16(), count)) } + (LoongArch(LoongArchInlineAsmRegClass::freg), BackendRepr::Scalar(s)) + if s.primitive() == Primitive::Float(Float::F16) => + { + let value = bx.bitcast(value, bx.type_i32()); + let value = bx.trunc(value, bx.type_i16()); + bx.bitcast(value, bx.type_f16()) + } (Mips(MipsInlineAsmRegClass::reg), BackendRepr::Scalar(s)) => { match s.primitive() { // MIPS only supports register-length arithmetics. @@ -1318,6 +1334,11 @@ fn llvm_fixup_output_type<'ll, 'tcx>( ) if element.primitive() == Primitive::Float(Float::F16) => { cx.type_vector(cx.type_i16(), count) } + (LoongArch(LoongArchInlineAsmRegClass::freg), BackendRepr::Scalar(s)) + if s.primitive() == Primitive::Float(Float::F16) => + { + cx.type_f32() + } (Mips(MipsInlineAsmRegClass::reg), BackendRepr::Scalar(s)) => { match s.primitive() { // MIPS only supports register-length arithmetics. diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index ec006b59192..5e9594dd06b 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -14,7 +14,6 @@ use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; -use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv, LayoutError, LayoutOfHelpers, @@ -484,73 +483,31 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn checked_binop( &mut self, oop: OverflowOp, - ty: Ty<'_>, + ty: Ty<'tcx>, lhs: Self::Value, rhs: Self::Value, ) -> (Self::Value, Self::Value) { - use rustc_middle::ty::IntTy::*; - use rustc_middle::ty::UintTy::*; - use rustc_middle::ty::{Int, Uint}; - - let new_kind = match ty.kind() { - Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.pointer_width)), - Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.pointer_width)), - t @ (Uint(_) | Int(_)) => *t, - _ => panic!("tried to get overflow intrinsic for op applied to non-int type"), + let (size, signed) = ty.int_size_and_signed(self.tcx); + let width = size.bits(); + + if oop == OverflowOp::Sub && !signed { + // Emit sub and icmp instead of llvm.usub.with.overflow. LLVM considers these + // to be the canonical form. It will attempt to reform llvm.usub.with.overflow + // in the backend if profitable. + let sub = self.sub(lhs, rhs); + let cmp = self.icmp(IntPredicate::IntULT, lhs, rhs); + return (sub, cmp); + } + + let oop_str = match oop { + OverflowOp::Add => "add", + OverflowOp::Sub => "sub", + OverflowOp::Mul => "mul", }; - let name = match oop { - OverflowOp::Add => match new_kind { - Int(I8) => "llvm.sadd.with.overflow.i8", - Int(I16) => "llvm.sadd.with.overflow.i16", - Int(I32) => "llvm.sadd.with.overflow.i32", - Int(I64) => "llvm.sadd.with.overflow.i64", - Int(I128) => "llvm.sadd.with.overflow.i128", - - Uint(U8) => "llvm.uadd.with.overflow.i8", - Uint(U16) => "llvm.uadd.with.overflow.i16", - Uint(U32) => "llvm.uadd.with.overflow.i32", - Uint(U64) => "llvm.uadd.with.overflow.i64", - Uint(U128) => "llvm.uadd.with.overflow.i128", - - _ => unreachable!(), - }, - OverflowOp::Sub => match new_kind { - Int(I8) => "llvm.ssub.with.overflow.i8", - Int(I16) => "llvm.ssub.with.overflow.i16", - Int(I32) => "llvm.ssub.with.overflow.i32", - Int(I64) => "llvm.ssub.with.overflow.i64", - Int(I128) => "llvm.ssub.with.overflow.i128", - - Uint(_) => { - // Emit sub and icmp instead of llvm.usub.with.overflow. LLVM considers these - // to be the canonical form. It will attempt to reform llvm.usub.with.overflow - // in the backend if profitable. - let sub = self.sub(lhs, rhs); - let cmp = self.icmp(IntPredicate::IntULT, lhs, rhs); - return (sub, cmp); - } - - _ => unreachable!(), - }, - OverflowOp::Mul => match new_kind { - Int(I8) => "llvm.smul.with.overflow.i8", - Int(I16) => "llvm.smul.with.overflow.i16", - Int(I32) => "llvm.smul.with.overflow.i32", - Int(I64) => "llvm.smul.with.overflow.i64", - Int(I128) => "llvm.smul.with.overflow.i128", - - Uint(U8) => "llvm.umul.with.overflow.i8", - Uint(U16) => "llvm.umul.with.overflow.i16", - Uint(U32) => "llvm.umul.with.overflow.i32", - Uint(U64) => "llvm.umul.with.overflow.i64", - Uint(U128) => "llvm.umul.with.overflow.i128", - - _ => unreachable!(), - }, - }; + let name = format!("llvm.{}{oop_str}.with.overflow", if signed { 's' } else { 'u' }); - let res = self.call_intrinsic(name, &[lhs, rhs]); + let res = self.call_intrinsic(name, &[self.type_ix(width)], &[lhs, rhs]); (self.extract_value(res, 0), self.extract_value(res, 1)) } @@ -954,11 +911,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - self.fptoint_sat(false, val, dest_ty) + self.call_intrinsic("llvm.fptoui.sat", &[dest_ty, self.val_ty(val)], &[val]) } fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - self.fptoint_sat(true, val, dest_ty) + self.call_intrinsic("llvm.fptosi.sat", &[dest_ty, self.val_ty(val)], &[val]) } fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { @@ -981,15 +938,12 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { if self.cx.type_kind(src_ty) != TypeKind::Vector { let float_width = self.cx.float_width(src_ty); let int_width = self.cx.int_width(dest_ty); - let name = match (int_width, float_width) { - (32, 32) => Some("llvm.wasm.trunc.unsigned.i32.f32"), - (32, 64) => Some("llvm.wasm.trunc.unsigned.i32.f64"), - (64, 32) => Some("llvm.wasm.trunc.unsigned.i64.f32"), - (64, 64) => Some("llvm.wasm.trunc.unsigned.i64.f64"), - _ => None, - }; - if let Some(name) = name { - return self.call_intrinsic(name, &[val]); + if matches!((int_width, float_width), (32 | 64, 32 | 64)) { + return self.call_intrinsic( + "llvm.wasm.trunc.unsigned", + &[dest_ty, src_ty], + &[val], + ); } } } @@ -1003,15 +957,12 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { if self.cx.type_kind(src_ty) != TypeKind::Vector { let float_width = self.cx.float_width(src_ty); let int_width = self.cx.int_width(dest_ty); - let name = match (int_width, float_width) { - (32, 32) => Some("llvm.wasm.trunc.signed.i32.f32"), - (32, 64) => Some("llvm.wasm.trunc.signed.i32.f64"), - (64, 32) => Some("llvm.wasm.trunc.signed.i64.f32"), - (64, 64) => Some("llvm.wasm.trunc.signed.i64.f64"), - _ => None, - }; - if let Some(name) = name { - return self.call_intrinsic(name, &[val]); + if matches!((int_width, float_width), (32 | 64, 32 | 64)) { + return self.call_intrinsic( + "llvm.wasm.trunc.signed", + &[dest_ty, src_ty], + &[val], + ); } } } @@ -1084,22 +1035,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { return None; } - let name = match (ty.is_signed(), ty.primitive_size(self.tcx).bits()) { - (true, 8) => "llvm.scmp.i8.i8", - (true, 16) => "llvm.scmp.i8.i16", - (true, 32) => "llvm.scmp.i8.i32", - (true, 64) => "llvm.scmp.i8.i64", - (true, 128) => "llvm.scmp.i8.i128", - - (false, 8) => "llvm.ucmp.i8.i8", - (false, 16) => "llvm.ucmp.i8.i16", - (false, 32) => "llvm.ucmp.i8.i32", - (false, 64) => "llvm.ucmp.i8.i64", - (false, 128) => "llvm.ucmp.i8.i128", + let size = ty.primitive_size(self.tcx); + let name = if ty.is_signed() { "llvm.scmp" } else { "llvm.ucmp" }; - _ => bug!("three-way compare unsupported for type {ty:?}"), - }; - Some(self.call_intrinsic(name, &[lhs, rhs])) + Some(self.call_intrinsic(name, &[self.type_i8(), self.type_ix(size.bits())], &[lhs, rhs])) } /* Miscellaneous instructions */ @@ -1385,11 +1324,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn lifetime_start(&mut self, ptr: &'ll Value, size: Size) { - self.call_lifetime_intrinsic("llvm.lifetime.start.p0i8", ptr, size); + self.call_lifetime_intrinsic("llvm.lifetime.start", ptr, size); } fn lifetime_end(&mut self, ptr: &'ll Value, size: Size) { - self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size); + self.call_lifetime_intrinsic("llvm.lifetime.end", ptr, size); } fn call( @@ -1454,7 +1393,8 @@ impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> { // Forward to the `get_static` method of `CodegenCx` let global = self.cx().get_static(def_id); if self.cx().tcx.is_thread_local_static(def_id) { - let pointer = self.call_intrinsic("llvm.threadlocal.address", &[global]); + let pointer = + self.call_intrinsic("llvm.threadlocal.address", &[self.val_ty(global)], &[global]); // Cast to default address space if globals are in a different addrspace self.pointercast(pointer, self.type_ptr()) } else { @@ -1649,12 +1589,17 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> { } impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { - pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value { - let (ty, f) = self.cx.get_intrinsic(intrinsic); + pub(crate) fn call_intrinsic( + &mut self, + base_name: impl Into<Cow<'static, str>>, + type_params: &[&'ll Type], + args: &[&'ll Value], + ) -> &'ll Value { + let (ty, f) = self.cx.get_intrinsic(base_name.into(), type_params); self.call(ty, None, None, f, args, None, None) } - fn call_lifetime_intrinsic(&mut self, intrinsic: &str, ptr: &'ll Value, size: Size) { + fn call_lifetime_intrinsic(&mut self, intrinsic: &'static str, ptr: &'ll Value, size: Size) { let size = size.bytes(); if size == 0 { return; @@ -1664,7 +1609,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { return; } - self.call_intrinsic(intrinsic, &[self.cx.const_u64(size), ptr]); + self.call_intrinsic(intrinsic, &[self.val_ty(ptr)], &[self.cx.const_u64(size), ptr]); } } impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> { @@ -1689,31 +1634,6 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> { } } impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { - fn fptoint_sat(&mut self, signed: bool, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - let src_ty = self.cx.val_ty(val); - let (float_ty, int_ty, vector_length) = if self.cx.type_kind(src_ty) == TypeKind::Vector { - assert_eq!(self.cx.vector_length(src_ty), self.cx.vector_length(dest_ty)); - ( - self.cx.element_type(src_ty), - self.cx.element_type(dest_ty), - Some(self.cx.vector_length(src_ty)), - ) - } else { - (src_ty, dest_ty, None) - }; - let float_width = self.cx.float_width(float_ty); - let int_width = self.cx.int_width(int_ty); - - let instr = if signed { "fptosi" } else { "fptoui" }; - let name = if let Some(vector_length) = vector_length { - format!("llvm.{instr}.sat.v{vector_length}i{int_width}.v{vector_length}f{float_width}") - } else { - format!("llvm.{instr}.sat.i{int_width}.f{float_width}") - }; - let f = self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty)); - self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None, None) - } - pub(crate) fn landing_pad( &mut self, ty: &'ll Type, @@ -1819,7 +1739,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { // llvm.type.test intrinsic. The LowerTypeTests link-time optimization pass replaces // calls to this intrinsic with code to test type membership. let typeid = self.get_metadata_value(typeid_metadata); - let cond = self.call_intrinsic("llvm.type.test", &[llfn, typeid]); + let cond = self.call_intrinsic("llvm.type.test", &[], &[llfn, typeid]); let bb_pass = self.append_sibling_block("type_test.pass"); let bb_fail = self.append_sibling_block("type_test.fail"); self.cond_br(cond, bb_pass, bb_fail); @@ -1887,7 +1807,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { num_counters: &'ll Value, index: &'ll Value, ) { - self.call_intrinsic("llvm.instrprof.increment", &[fn_name, hash, num_counters, index]); + self.call_intrinsic("llvm.instrprof.increment", &[], &[fn_name, hash, num_counters, index]); } /// Emits a call to `llvm.instrprof.mcdc.parameters`. @@ -1906,7 +1826,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { hash: &'ll Value, bitmap_bits: &'ll Value, ) { - self.call_intrinsic("llvm.instrprof.mcdc.parameters", &[fn_name, hash, bitmap_bits]); + self.call_intrinsic("llvm.instrprof.mcdc.parameters", &[], &[fn_name, hash, bitmap_bits]); } #[instrument(level = "debug", skip(self))] @@ -1918,7 +1838,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { mcdc_temp: &'ll Value, ) { let args = &[fn_name, hash, bitmap_index, mcdc_temp]; - self.call_intrinsic("llvm.instrprof.mcdc.tvbitmap.update", args); + self.call_intrinsic("llvm.instrprof.mcdc.tvbitmap.update", &[], args); } #[instrument(level = "debug", skip(self))] diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index c5c13ac097a..b07d9a5cfca 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -114,7 +114,7 @@ fn match_args_from_caller_to_enzyme<'ll>( let mul = unsafe { llvm::LLVMBuildMul( builder.llbuilder, - cx.get_const_i64(elem_bytes_size), + cx.get_const_int(cx.type_i64(), elem_bytes_size), next_outer_arg, UNNAMED, ) @@ -385,7 +385,7 @@ fn generate_enzyme_call<'ll>( if attrs.width > 1 { let enzyme_width = cx.create_metadata("enzyme_width".to_string()).unwrap(); args.push(cx.get_metadata_value(enzyme_width)); - args.push(cx.get_const_i64(attrs.width as u64)); + args.push(cx.get_const_int(cx.type_i64(), attrs.width as u64)); } let has_sret = has_sret(outer_fn); diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 3cfa96393e9..ae5add59322 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -99,14 +99,14 @@ impl<'ll, CX: Borrow<SCx<'ll>>> BackendTypes for GenericCx<'ll, CX> { type DIVariable = &'ll llvm::debuginfo::DIVariable; } -impl<'ll> CodegenCx<'ll, '_> { +impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> { pub(crate) fn const_array(&self, ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Value { let len = u64::try_from(elts.len()).expect("LLVMConstArray2 elements len overflow"); unsafe { llvm::LLVMConstArray2(ty, elts.as_ptr(), len) } } pub(crate) fn const_bytes(&self, bytes: &[u8]) -> &'ll Value { - bytes_in_context(self.llcx, bytes) + bytes_in_context(self.llcx(), bytes) } pub(crate) fn const_get_elt(&self, v: &'ll Value, idx: u64) -> &'ll Value { diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 8d6e1d8941b..0324dff6ff2 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1,4 +1,4 @@ -use std::borrow::Borrow; +use std::borrow::{Borrow, Cow}; use std::cell::{Cell, RefCell}; use std::ffi::{CStr, c_char, c_uint}; use std::marker::PhantomData; @@ -137,7 +137,8 @@ pub(crate) struct FullCx<'ll, 'tcx> { eh_catch_typeinfo: Cell<Option<&'ll Value>>, pub rust_try_fn: Cell<Option<(&'ll Type, &'ll Value)>>, - intrinsics: RefCell<FxHashMap<&'static str, (&'ll Type, &'ll Value)>>, + intrinsics: + RefCell<FxHashMap<(Cow<'static, str>, SmallVec<[&'ll Type; 2]>), (&'ll Type, &'ll Value)>>, /// A counter that is used for generating local symbol names local_gen_sym_counter: Cell<usize>, @@ -678,11 +679,8 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> { llvm::LLVMMetadataAsValue(self.llcx(), metadata) } - // FIXME(autodiff): We should split `ConstCodegenMethods` to pull the reusable parts - // onto a trait that is also implemented for GenericCx. - pub(crate) fn get_const_i64(&self, n: u64) -> &'ll Value { - let ty = unsafe { llvm::LLVMInt64TypeInContext(self.llcx()) }; - unsafe { llvm::LLVMConstInt(ty, n, llvm::False) } + pub(crate) fn get_const_int(&self, ty: &'ll Type, val: u64) -> &'ll Value { + unsafe { llvm::LLVMConstInt(ty, val, llvm::False) } } pub(crate) fn get_function(&self, name: &str) -> Option<&'ll Value> { @@ -842,410 +840,41 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } impl<'ll> CodegenCx<'ll, '_> { - pub(crate) fn get_intrinsic(&self, key: &str) -> (&'ll Type, &'ll Value) { - if let Some(v) = self.intrinsics.borrow().get(key).cloned() { - return v; - } - - self.declare_intrinsic(key).unwrap_or_else(|| bug!("unknown intrinsic '{}'", key)) - } - - fn insert_intrinsic( + pub(crate) fn get_intrinsic( &self, - name: &'static str, - args: Option<&[&'ll llvm::Type]>, - ret: &'ll llvm::Type, - ) -> (&'ll llvm::Type, &'ll llvm::Value) { - let fn_ty = if let Some(args) = args { - self.type_func(args, ret) - } else { - self.type_variadic_func(&[], ret) - }; - let f = self.declare_cfn(name, llvm::UnnamedAddr::No, fn_ty); - self.intrinsics.borrow_mut().insert(name, (fn_ty, f)); - (fn_ty, f) - } - - fn declare_intrinsic(&self, key: &str) -> Option<(&'ll Type, &'ll Value)> { - macro_rules! ifn { - ($name:expr, fn() -> $ret:expr) => ( - if key == $name { - return Some(self.insert_intrinsic($name, Some(&[]), $ret)); - } - ); - ($name:expr, fn(...) -> $ret:expr) => ( - if key == $name { - return Some(self.insert_intrinsic($name, None, $ret)); - } - ); - ($name:expr, fn($($arg:expr),*) -> $ret:expr) => ( - if key == $name { - return Some(self.insert_intrinsic($name, Some(&[$($arg),*]), $ret)); - } - ); - } - macro_rules! mk_struct { - ($($field_ty:expr),*) => (self.type_struct( &[$($field_ty),*], false)) - } - - let ptr = self.type_ptr(); - let void = self.type_void(); - let i1 = self.type_i1(); - let t_i8 = self.type_i8(); - let t_i16 = self.type_i16(); - let t_i32 = self.type_i32(); - let t_i64 = self.type_i64(); - let t_i128 = self.type_i128(); - let t_isize = self.type_isize(); - let t_f16 = self.type_f16(); - let t_f32 = self.type_f32(); - let t_f64 = self.type_f64(); - let t_f128 = self.type_f128(); - let t_metadata = self.type_metadata(); - let t_token = self.type_token(); - - ifn!("llvm.wasm.get.exception", fn(t_token) -> ptr); - ifn!("llvm.wasm.get.ehselector", fn(t_token) -> t_i32); - - ifn!("llvm.wasm.trunc.unsigned.i32.f32", fn(t_f32) -> t_i32); - ifn!("llvm.wasm.trunc.unsigned.i32.f64", fn(t_f64) -> t_i32); - ifn!("llvm.wasm.trunc.unsigned.i64.f32", fn(t_f32) -> t_i64); - ifn!("llvm.wasm.trunc.unsigned.i64.f64", fn(t_f64) -> t_i64); - ifn!("llvm.wasm.trunc.signed.i32.f32", fn(t_f32) -> t_i32); - ifn!("llvm.wasm.trunc.signed.i32.f64", fn(t_f64) -> t_i32); - ifn!("llvm.wasm.trunc.signed.i64.f32", fn(t_f32) -> t_i64); - ifn!("llvm.wasm.trunc.signed.i64.f64", fn(t_f64) -> t_i64); - - ifn!("llvm.fptosi.sat.i8.f32", fn(t_f32) -> t_i8); - ifn!("llvm.fptosi.sat.i16.f32", fn(t_f32) -> t_i16); - ifn!("llvm.fptosi.sat.i32.f32", fn(t_f32) -> t_i32); - ifn!("llvm.fptosi.sat.i64.f32", fn(t_f32) -> t_i64); - ifn!("llvm.fptosi.sat.i128.f32", fn(t_f32) -> t_i128); - ifn!("llvm.fptosi.sat.i8.f64", fn(t_f64) -> t_i8); - ifn!("llvm.fptosi.sat.i16.f64", fn(t_f64) -> t_i16); - ifn!("llvm.fptosi.sat.i32.f64", fn(t_f64) -> t_i32); - ifn!("llvm.fptosi.sat.i64.f64", fn(t_f64) -> t_i64); - ifn!("llvm.fptosi.sat.i128.f64", fn(t_f64) -> t_i128); - - ifn!("llvm.fptoui.sat.i8.f32", fn(t_f32) -> t_i8); - ifn!("llvm.fptoui.sat.i16.f32", fn(t_f32) -> t_i16); - ifn!("llvm.fptoui.sat.i32.f32", fn(t_f32) -> t_i32); - ifn!("llvm.fptoui.sat.i64.f32", fn(t_f32) -> t_i64); - ifn!("llvm.fptoui.sat.i128.f32", fn(t_f32) -> t_i128); - ifn!("llvm.fptoui.sat.i8.f64", fn(t_f64) -> t_i8); - ifn!("llvm.fptoui.sat.i16.f64", fn(t_f64) -> t_i16); - ifn!("llvm.fptoui.sat.i32.f64", fn(t_f64) -> t_i32); - ifn!("llvm.fptoui.sat.i64.f64", fn(t_f64) -> t_i64); - ifn!("llvm.fptoui.sat.i128.f64", fn(t_f64) -> t_i128); - - ifn!("llvm.trap", fn() -> void); - ifn!("llvm.debugtrap", fn() -> void); - ifn!("llvm.frameaddress", fn(t_i32) -> ptr); - - ifn!("llvm.powi.f16.i32", fn(t_f16, t_i32) -> t_f16); - ifn!("llvm.powi.f32.i32", fn(t_f32, t_i32) -> t_f32); - ifn!("llvm.powi.f64.i32", fn(t_f64, t_i32) -> t_f64); - ifn!("llvm.powi.f128.i32", fn(t_f128, t_i32) -> t_f128); - - ifn!("llvm.pow.f16", fn(t_f16, t_f16) -> t_f16); - ifn!("llvm.pow.f32", fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.pow.f64", fn(t_f64, t_f64) -> t_f64); - ifn!("llvm.pow.f128", fn(t_f128, t_f128) -> t_f128); - - ifn!("llvm.sqrt.f16", fn(t_f16) -> t_f16); - ifn!("llvm.sqrt.f32", fn(t_f32) -> t_f32); - ifn!("llvm.sqrt.f64", fn(t_f64) -> t_f64); - ifn!("llvm.sqrt.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.sin.f16", fn(t_f16) -> t_f16); - ifn!("llvm.sin.f32", fn(t_f32) -> t_f32); - ifn!("llvm.sin.f64", fn(t_f64) -> t_f64); - ifn!("llvm.sin.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.cos.f16", fn(t_f16) -> t_f16); - ifn!("llvm.cos.f32", fn(t_f32) -> t_f32); - ifn!("llvm.cos.f64", fn(t_f64) -> t_f64); - ifn!("llvm.cos.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.exp.f16", fn(t_f16) -> t_f16); - ifn!("llvm.exp.f32", fn(t_f32) -> t_f32); - ifn!("llvm.exp.f64", fn(t_f64) -> t_f64); - ifn!("llvm.exp.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.exp2.f16", fn(t_f16) -> t_f16); - ifn!("llvm.exp2.f32", fn(t_f32) -> t_f32); - ifn!("llvm.exp2.f64", fn(t_f64) -> t_f64); - ifn!("llvm.exp2.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.log.f16", fn(t_f16) -> t_f16); - ifn!("llvm.log.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.log10.f16", fn(t_f16) -> t_f16); - ifn!("llvm.log10.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log10.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log10.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.log2.f16", fn(t_f16) -> t_f16); - ifn!("llvm.log2.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log2.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log2.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.fma.f16", fn(t_f16, t_f16, t_f16) -> t_f16); - ifn!("llvm.fma.f32", fn(t_f32, t_f32, t_f32) -> t_f32); - ifn!("llvm.fma.f64", fn(t_f64, t_f64, t_f64) -> t_f64); - ifn!("llvm.fma.f128", fn(t_f128, t_f128, t_f128) -> t_f128); - - ifn!("llvm.fmuladd.f16", fn(t_f16, t_f16, t_f16) -> t_f16); - ifn!("llvm.fmuladd.f32", fn(t_f32, t_f32, t_f32) -> t_f32); - ifn!("llvm.fmuladd.f64", fn(t_f64, t_f64, t_f64) -> t_f64); - ifn!("llvm.fmuladd.f128", fn(t_f128, t_f128, t_f128) -> t_f128); - - ifn!("llvm.fabs.f16", fn(t_f16) -> t_f16); - ifn!("llvm.fabs.f32", fn(t_f32) -> t_f32); - ifn!("llvm.fabs.f64", fn(t_f64) -> t_f64); - ifn!("llvm.fabs.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.minnum.f16", fn(t_f16, t_f16) -> t_f16); - ifn!("llvm.minnum.f32", fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.minnum.f64", fn(t_f64, t_f64) -> t_f64); - ifn!("llvm.minnum.f128", fn(t_f128, t_f128) -> t_f128); - - ifn!("llvm.minimum.f16", fn(t_f16, t_f16) -> t_f16); - ifn!("llvm.minimum.f32", fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.minimum.f64", fn(t_f64, t_f64) -> t_f64); - // There are issues on x86_64 and aarch64 with the f128 variant. - // - https://github.com/llvm/llvm-project/issues/139380 - // - https://github.com/llvm/llvm-project/issues/139381 - // ifn!("llvm.minimum.f128", fn(t_f128, t_f128) -> t_f128); - - ifn!("llvm.maxnum.f16", fn(t_f16, t_f16) -> t_f16); - ifn!("llvm.maxnum.f32", fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.maxnum.f64", fn(t_f64, t_f64) -> t_f64); - ifn!("llvm.maxnum.f128", fn(t_f128, t_f128) -> t_f128); - - ifn!("llvm.maximum.f16", fn(t_f16, t_f16) -> t_f16); - ifn!("llvm.maximum.f32", fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.maximum.f64", fn(t_f64, t_f64) -> t_f64); - // There are issues on x86_64 and aarch64 with the f128 variant. - // - https://github.com/llvm/llvm-project/issues/139380 - // - https://github.com/llvm/llvm-project/issues/139381 - // ifn!("llvm.maximum.f128", fn(t_f128, t_f128) -> t_f128); - - ifn!("llvm.floor.f16", fn(t_f16) -> t_f16); - ifn!("llvm.floor.f32", fn(t_f32) -> t_f32); - ifn!("llvm.floor.f64", fn(t_f64) -> t_f64); - ifn!("llvm.floor.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.ceil.f16", fn(t_f16) -> t_f16); - ifn!("llvm.ceil.f32", fn(t_f32) -> t_f32); - ifn!("llvm.ceil.f64", fn(t_f64) -> t_f64); - ifn!("llvm.ceil.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.trunc.f16", fn(t_f16) -> t_f16); - ifn!("llvm.trunc.f32", fn(t_f32) -> t_f32); - ifn!("llvm.trunc.f64", fn(t_f64) -> t_f64); - ifn!("llvm.trunc.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.copysign.f16", fn(t_f16, t_f16) -> t_f16); - ifn!("llvm.copysign.f32", fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.copysign.f64", fn(t_f64, t_f64) -> t_f64); - ifn!("llvm.copysign.f128", fn(t_f128, t_f128) -> t_f128); - - ifn!("llvm.round.f16", fn(t_f16) -> t_f16); - ifn!("llvm.round.f32", fn(t_f32) -> t_f32); - ifn!("llvm.round.f64", fn(t_f64) -> t_f64); - ifn!("llvm.round.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.roundeven.f16", fn(t_f16) -> t_f16); - ifn!("llvm.roundeven.f32", fn(t_f32) -> t_f32); - ifn!("llvm.roundeven.f64", fn(t_f64) -> t_f64); - ifn!("llvm.roundeven.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.rint.f16", fn(t_f16) -> t_f16); - ifn!("llvm.rint.f32", fn(t_f32) -> t_f32); - ifn!("llvm.rint.f64", fn(t_f64) -> t_f64); - ifn!("llvm.rint.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.nearbyint.f16", fn(t_f16) -> t_f16); - ifn!("llvm.nearbyint.f32", fn(t_f32) -> t_f32); - ifn!("llvm.nearbyint.f64", fn(t_f64) -> t_f64); - ifn!("llvm.nearbyint.f128", fn(t_f128) -> t_f128); - - ifn!("llvm.ctpop.i8", fn(t_i8) -> t_i8); - ifn!("llvm.ctpop.i16", fn(t_i16) -> t_i16); - ifn!("llvm.ctpop.i32", fn(t_i32) -> t_i32); - ifn!("llvm.ctpop.i64", fn(t_i64) -> t_i64); - ifn!("llvm.ctpop.i128", fn(t_i128) -> t_i128); - - ifn!("llvm.ctlz.i8", fn(t_i8, i1) -> t_i8); - ifn!("llvm.ctlz.i16", fn(t_i16, i1) -> t_i16); - ifn!("llvm.ctlz.i32", fn(t_i32, i1) -> t_i32); - ifn!("llvm.ctlz.i64", fn(t_i64, i1) -> t_i64); - ifn!("llvm.ctlz.i128", fn(t_i128, i1) -> t_i128); - - ifn!("llvm.cttz.i8", fn(t_i8, i1) -> t_i8); - ifn!("llvm.cttz.i16", fn(t_i16, i1) -> t_i16); - ifn!("llvm.cttz.i32", fn(t_i32, i1) -> t_i32); - ifn!("llvm.cttz.i64", fn(t_i64, i1) -> t_i64); - ifn!("llvm.cttz.i128", fn(t_i128, i1) -> t_i128); - - ifn!("llvm.bswap.i16", fn(t_i16) -> t_i16); - ifn!("llvm.bswap.i32", fn(t_i32) -> t_i32); - ifn!("llvm.bswap.i64", fn(t_i64) -> t_i64); - ifn!("llvm.bswap.i128", fn(t_i128) -> t_i128); - - ifn!("llvm.bitreverse.i8", fn(t_i8) -> t_i8); - ifn!("llvm.bitreverse.i16", fn(t_i16) -> t_i16); - ifn!("llvm.bitreverse.i32", fn(t_i32) -> t_i32); - ifn!("llvm.bitreverse.i64", fn(t_i64) -> t_i64); - ifn!("llvm.bitreverse.i128", fn(t_i128) -> t_i128); - - ifn!("llvm.fshl.i8", fn(t_i8, t_i8, t_i8) -> t_i8); - ifn!("llvm.fshl.i16", fn(t_i16, t_i16, t_i16) -> t_i16); - ifn!("llvm.fshl.i32", fn(t_i32, t_i32, t_i32) -> t_i32); - ifn!("llvm.fshl.i64", fn(t_i64, t_i64, t_i64) -> t_i64); - ifn!("llvm.fshl.i128", fn(t_i128, t_i128, t_i128) -> t_i128); - - ifn!("llvm.fshr.i8", fn(t_i8, t_i8, t_i8) -> t_i8); - ifn!("llvm.fshr.i16", fn(t_i16, t_i16, t_i16) -> t_i16); - ifn!("llvm.fshr.i32", fn(t_i32, t_i32, t_i32) -> t_i32); - ifn!("llvm.fshr.i64", fn(t_i64, t_i64, t_i64) -> t_i64); - ifn!("llvm.fshr.i128", fn(t_i128, t_i128, t_i128) -> t_i128); - - ifn!("llvm.sadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct! {t_i8, i1}); - ifn!("llvm.sadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct! {t_i16, i1}); - ifn!("llvm.sadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct! {t_i32, i1}); - ifn!("llvm.sadd.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct! {t_i64, i1}); - ifn!("llvm.sadd.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct! {t_i128, i1}); - - ifn!("llvm.uadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct! {t_i8, i1}); - ifn!("llvm.uadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct! {t_i16, i1}); - ifn!("llvm.uadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct! {t_i32, i1}); - ifn!("llvm.uadd.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct! {t_i64, i1}); - ifn!("llvm.uadd.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct! {t_i128, i1}); - - ifn!("llvm.ssub.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct! {t_i8, i1}); - ifn!("llvm.ssub.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct! {t_i16, i1}); - ifn!("llvm.ssub.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct! {t_i32, i1}); - ifn!("llvm.ssub.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct! {t_i64, i1}); - ifn!("llvm.ssub.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct! {t_i128, i1}); - - ifn!("llvm.usub.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct! {t_i8, i1}); - ifn!("llvm.usub.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct! {t_i16, i1}); - ifn!("llvm.usub.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct! {t_i32, i1}); - ifn!("llvm.usub.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct! {t_i64, i1}); - ifn!("llvm.usub.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct! {t_i128, i1}); - - ifn!("llvm.smul.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct! {t_i8, i1}); - ifn!("llvm.smul.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct! {t_i16, i1}); - ifn!("llvm.smul.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct! {t_i32, i1}); - ifn!("llvm.smul.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct! {t_i64, i1}); - ifn!("llvm.smul.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct! {t_i128, i1}); - - ifn!("llvm.umul.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct! {t_i8, i1}); - ifn!("llvm.umul.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct! {t_i16, i1}); - ifn!("llvm.umul.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct! {t_i32, i1}); - ifn!("llvm.umul.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct! {t_i64, i1}); - ifn!("llvm.umul.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct! {t_i128, i1}); - - ifn!("llvm.sadd.sat.i8", fn(t_i8, t_i8) -> t_i8); - ifn!("llvm.sadd.sat.i16", fn(t_i16, t_i16) -> t_i16); - ifn!("llvm.sadd.sat.i32", fn(t_i32, t_i32) -> t_i32); - ifn!("llvm.sadd.sat.i64", fn(t_i64, t_i64) -> t_i64); - ifn!("llvm.sadd.sat.i128", fn(t_i128, t_i128) -> t_i128); - - ifn!("llvm.uadd.sat.i8", fn(t_i8, t_i8) -> t_i8); - ifn!("llvm.uadd.sat.i16", fn(t_i16, t_i16) -> t_i16); - ifn!("llvm.uadd.sat.i32", fn(t_i32, t_i32) -> t_i32); - ifn!("llvm.uadd.sat.i64", fn(t_i64, t_i64) -> t_i64); - ifn!("llvm.uadd.sat.i128", fn(t_i128, t_i128) -> t_i128); - - ifn!("llvm.ssub.sat.i8", fn(t_i8, t_i8) -> t_i8); - ifn!("llvm.ssub.sat.i16", fn(t_i16, t_i16) -> t_i16); - ifn!("llvm.ssub.sat.i32", fn(t_i32, t_i32) -> t_i32); - ifn!("llvm.ssub.sat.i64", fn(t_i64, t_i64) -> t_i64); - ifn!("llvm.ssub.sat.i128", fn(t_i128, t_i128) -> t_i128); - - ifn!("llvm.usub.sat.i8", fn(t_i8, t_i8) -> t_i8); - ifn!("llvm.usub.sat.i16", fn(t_i16, t_i16) -> t_i16); - ifn!("llvm.usub.sat.i32", fn(t_i32, t_i32) -> t_i32); - ifn!("llvm.usub.sat.i64", fn(t_i64, t_i64) -> t_i64); - ifn!("llvm.usub.sat.i128", fn(t_i128, t_i128) -> t_i128); - - ifn!("llvm.scmp.i8.i8", fn(t_i8, t_i8) -> t_i8); - ifn!("llvm.scmp.i8.i16", fn(t_i16, t_i16) -> t_i8); - ifn!("llvm.scmp.i8.i32", fn(t_i32, t_i32) -> t_i8); - ifn!("llvm.scmp.i8.i64", fn(t_i64, t_i64) -> t_i8); - ifn!("llvm.scmp.i8.i128", fn(t_i128, t_i128) -> t_i8); - - ifn!("llvm.ucmp.i8.i8", fn(t_i8, t_i8) -> t_i8); - ifn!("llvm.ucmp.i8.i16", fn(t_i16, t_i16) -> t_i8); - ifn!("llvm.ucmp.i8.i32", fn(t_i32, t_i32) -> t_i8); - ifn!("llvm.ucmp.i8.i64", fn(t_i64, t_i64) -> t_i8); - ifn!("llvm.ucmp.i8.i128", fn(t_i128, t_i128) -> t_i8); - - ifn!("llvm.lifetime.start.p0i8", fn(t_i64, ptr) -> void); - ifn!("llvm.lifetime.end.p0i8", fn(t_i64, ptr) -> void); - - // FIXME: This is an infinitesimally small portion of the types you can - // pass to this intrinsic, if we can ever lazily register intrinsics we - // should register these when they're used, that way any type can be - // passed. - ifn!("llvm.is.constant.i1", fn(i1) -> i1); - ifn!("llvm.is.constant.i8", fn(t_i8) -> i1); - ifn!("llvm.is.constant.i16", fn(t_i16) -> i1); - ifn!("llvm.is.constant.i32", fn(t_i32) -> i1); - ifn!("llvm.is.constant.i64", fn(t_i64) -> i1); - ifn!("llvm.is.constant.i128", fn(t_i128) -> i1); - ifn!("llvm.is.constant.isize", fn(t_isize) -> i1); - ifn!("llvm.is.constant.f16", fn(t_f16) -> i1); - ifn!("llvm.is.constant.f32", fn(t_f32) -> i1); - ifn!("llvm.is.constant.f64", fn(t_f64) -> i1); - ifn!("llvm.is.constant.f128", fn(t_f128) -> i1); - ifn!("llvm.is.constant.ptr", fn(ptr) -> i1); - - ifn!("llvm.expect.i1", fn(i1, i1) -> i1); - ifn!("llvm.eh.typeid.for", fn(ptr) -> t_i32); - ifn!("llvm.localescape", fn(...) -> void); - ifn!("llvm.localrecover", fn(ptr, ptr, t_i32) -> ptr); - ifn!("llvm.x86.seh.recoverfp", fn(ptr, ptr) -> ptr); - - ifn!("llvm.assume", fn(i1) -> void); - ifn!("llvm.prefetch", fn(ptr, t_i32, t_i32, t_i32) -> void); - + base_name: Cow<'static, str>, + type_params: &[&'ll Type], + ) -> (&'ll Type, &'ll Value) { + *self + .intrinsics + .borrow_mut() + .entry((base_name, SmallVec::from_slice(type_params))) + .or_insert_with_key(|(base_name, type_params)| { + self.declare_intrinsic(base_name, type_params) + }) + } + + fn declare_intrinsic( + &self, + base_name: &str, + type_params: &[&'ll Type], + ) -> (&'ll Type, &'ll Value) { // This isn't an "LLVM intrinsic", but LLVM's optimization passes // recognize it like one (including turning it into `bcmp` sometimes) // and we use it to implement intrinsics like `raw_eq` and `compare_bytes` - match self.sess().target.arch.as_ref() { - "avr" | "msp430" => ifn!("memcmp", fn(ptr, ptr, t_isize) -> t_i16), - _ => ifn!("memcmp", fn(ptr, ptr, t_isize) -> t_i32), - } - - // variadic intrinsics - ifn!("llvm.va_start", fn(ptr) -> void); - ifn!("llvm.va_end", fn(ptr) -> void); - ifn!("llvm.va_copy", fn(ptr, ptr) -> void); - - if self.sess().instrument_coverage() { - ifn!("llvm.instrprof.increment", fn(ptr, t_i64, t_i32, t_i32) -> void); - ifn!("llvm.instrprof.mcdc.parameters", fn(ptr, t_i64, t_i32) -> void); - ifn!("llvm.instrprof.mcdc.tvbitmap.update", fn(ptr, t_i64, t_i32, ptr) -> void); - } - - ifn!("llvm.type.test", fn(ptr, t_metadata) -> i1); - ifn!("llvm.type.checked.load", fn(ptr, t_i32, t_metadata) -> mk_struct! {ptr, i1}); + if base_name == "memcmp" { + let fn_ty = self + .type_func(&[self.type_ptr(), self.type_ptr(), self.type_isize()], self.type_int()); + let f = self.declare_cfn("memcmp", llvm::UnnamedAddr::No, fn_ty); - if self.sess().opts.debuginfo != DebugInfo::None { - ifn!("llvm.dbg.declare", fn(t_metadata, t_metadata) -> void); - ifn!("llvm.dbg.value", fn(t_metadata, t_i64, t_metadata) -> void); + return (fn_ty, f); } - ifn!("llvm.ptrmask", fn(ptr, t_isize) -> ptr); - ifn!("llvm.threadlocal.address", fn(ptr) -> ptr); + let intrinsic = llvm::Intrinsic::lookup(base_name.as_bytes()) + .unwrap_or_else(|| bug!("Unknown intrinsic: `{base_name}`")); + let f = intrinsic.get_declaration(self.llmod, &type_params); - None + (self.get_type_of_global(f), f) } pub(crate) fn eh_catch_typeinfo(&self) -> &'ll Value { diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index eaafc680712..8bc74fbec7e 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -24,23 +24,6 @@ pub(crate) struct UnknownCTargetFeature<'a> { pub rust_feature: PossibleFeature<'a>, } -#[derive(Diagnostic)] -#[diag(codegen_llvm_unstable_ctarget_feature)] -#[note] -pub(crate) struct UnstableCTargetFeature<'a> { - pub feature: &'a str, -} - -#[derive(Diagnostic)] -#[diag(codegen_llvm_forbidden_ctarget_feature)] -#[note] -#[note(codegen_llvm_forbidden_ctarget_feature_issue)] -pub(crate) struct ForbiddenCTargetFeature<'a> { - pub feature: &'a str, - pub enabled: &'a str, - pub reason: &'a str, -} - #[derive(Subdiagnostic)] pub(crate) enum PossibleFeature<'a> { #[help(codegen_llvm_possible_feature)] diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 10697b9a71f..f7f062849a8 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -15,7 +15,7 @@ use rustc_middle::ty::{self, GenericArgsRef, Ty}; use rustc_middle::{bug, span_bug}; use rustc_span::{Span, Symbol, sym}; use rustc_symbol_mangling::mangle_internal_symbol; -use rustc_target::spec::{HasTargetSpec, PanicStrategy}; +use rustc_target::spec::PanicStrategy; use tracing::debug; use crate::abi::FnAbiLlvmExt; @@ -27,137 +27,140 @@ use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; use crate::value::Value; -fn get_simple_intrinsic<'ll>( - cx: &CodegenCx<'ll, '_>, +fn call_simple_intrinsic<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, name: Symbol, -) -> Option<(&'ll Type, &'ll Value)> { - let llvm_name = match name { - sym::sqrtf16 => "llvm.sqrt.f16", - sym::sqrtf32 => "llvm.sqrt.f32", - sym::sqrtf64 => "llvm.sqrt.f64", - sym::sqrtf128 => "llvm.sqrt.f128", - - sym::powif16 => "llvm.powi.f16.i32", - sym::powif32 => "llvm.powi.f32.i32", - sym::powif64 => "llvm.powi.f64.i32", - sym::powif128 => "llvm.powi.f128.i32", - - sym::sinf16 => "llvm.sin.f16", - sym::sinf32 => "llvm.sin.f32", - sym::sinf64 => "llvm.sin.f64", - sym::sinf128 => "llvm.sin.f128", - - sym::cosf16 => "llvm.cos.f16", - sym::cosf32 => "llvm.cos.f32", - sym::cosf64 => "llvm.cos.f64", - sym::cosf128 => "llvm.cos.f128", - - sym::powf16 => "llvm.pow.f16", - sym::powf32 => "llvm.pow.f32", - sym::powf64 => "llvm.pow.f64", - sym::powf128 => "llvm.pow.f128", - - sym::expf16 => "llvm.exp.f16", - sym::expf32 => "llvm.exp.f32", - sym::expf64 => "llvm.exp.f64", - sym::expf128 => "llvm.exp.f128", - - sym::exp2f16 => "llvm.exp2.f16", - sym::exp2f32 => "llvm.exp2.f32", - sym::exp2f64 => "llvm.exp2.f64", - sym::exp2f128 => "llvm.exp2.f128", - - sym::logf16 => "llvm.log.f16", - sym::logf32 => "llvm.log.f32", - sym::logf64 => "llvm.log.f64", - sym::logf128 => "llvm.log.f128", - - sym::log10f16 => "llvm.log10.f16", - sym::log10f32 => "llvm.log10.f32", - sym::log10f64 => "llvm.log10.f64", - sym::log10f128 => "llvm.log10.f128", - - sym::log2f16 => "llvm.log2.f16", - sym::log2f32 => "llvm.log2.f32", - sym::log2f64 => "llvm.log2.f64", - sym::log2f128 => "llvm.log2.f128", - - sym::fmaf16 => "llvm.fma.f16", - sym::fmaf32 => "llvm.fma.f32", - sym::fmaf64 => "llvm.fma.f64", - sym::fmaf128 => "llvm.fma.f128", - - sym::fmuladdf16 => "llvm.fmuladd.f16", - sym::fmuladdf32 => "llvm.fmuladd.f32", - sym::fmuladdf64 => "llvm.fmuladd.f64", - sym::fmuladdf128 => "llvm.fmuladd.f128", - - sym::fabsf16 => "llvm.fabs.f16", - sym::fabsf32 => "llvm.fabs.f32", - sym::fabsf64 => "llvm.fabs.f64", - sym::fabsf128 => "llvm.fabs.f128", - - sym::minnumf16 => "llvm.minnum.f16", - sym::minnumf32 => "llvm.minnum.f32", - sym::minnumf64 => "llvm.minnum.f64", - sym::minnumf128 => "llvm.minnum.f128", - - sym::minimumf16 => "llvm.minimum.f16", - sym::minimumf32 => "llvm.minimum.f32", - sym::minimumf64 => "llvm.minimum.f64", + args: &[OperandRef<'tcx, &'ll Value>], +) -> Option<&'ll Value> { + let (base_name, type_params): (&'static str, &[&'ll Type]) = match name { + sym::sqrtf16 => ("llvm.sqrt", &[bx.type_f16()]), + sym::sqrtf32 => ("llvm.sqrt", &[bx.type_f32()]), + sym::sqrtf64 => ("llvm.sqrt", &[bx.type_f64()]), + sym::sqrtf128 => ("llvm.sqrt", &[bx.type_f128()]), + + sym::powif16 => ("llvm.powi", &[bx.type_f16(), bx.type_i32()]), + sym::powif32 => ("llvm.powi", &[bx.type_f32(), bx.type_i32()]), + sym::powif64 => ("llvm.powi", &[bx.type_f64(), bx.type_i32()]), + sym::powif128 => ("llvm.powi", &[bx.type_f128(), bx.type_i32()]), + + sym::sinf16 => ("llvm.sin", &[bx.type_f16()]), + sym::sinf32 => ("llvm.sin", &[bx.type_f32()]), + sym::sinf64 => ("llvm.sin", &[bx.type_f64()]), + sym::sinf128 => ("llvm.sin", &[bx.type_f128()]), + + sym::cosf16 => ("llvm.cos", &[bx.type_f16()]), + sym::cosf32 => ("llvm.cos", &[bx.type_f32()]), + sym::cosf64 => ("llvm.cos", &[bx.type_f64()]), + sym::cosf128 => ("llvm.cos", &[bx.type_f128()]), + + sym::powf16 => ("llvm.pow", &[bx.type_f16()]), + sym::powf32 => ("llvm.pow", &[bx.type_f32()]), + sym::powf64 => ("llvm.pow", &[bx.type_f64()]), + sym::powf128 => ("llvm.pow", &[bx.type_f128()]), + + sym::expf16 => ("llvm.exp", &[bx.type_f16()]), + sym::expf32 => ("llvm.exp", &[bx.type_f32()]), + sym::expf64 => ("llvm.exp", &[bx.type_f64()]), + sym::expf128 => ("llvm.exp", &[bx.type_f128()]), + + sym::exp2f16 => ("llvm.exp2", &[bx.type_f16()]), + sym::exp2f32 => ("llvm.exp2", &[bx.type_f32()]), + sym::exp2f64 => ("llvm.exp2", &[bx.type_f64()]), + sym::exp2f128 => ("llvm.exp2", &[bx.type_f128()]), + + sym::logf16 => ("llvm.log", &[bx.type_f16()]), + sym::logf32 => ("llvm.log", &[bx.type_f32()]), + sym::logf64 => ("llvm.log", &[bx.type_f64()]), + sym::logf128 => ("llvm.log", &[bx.type_f128()]), + + sym::log10f16 => ("llvm.log10", &[bx.type_f16()]), + sym::log10f32 => ("llvm.log10", &[bx.type_f32()]), + sym::log10f64 => ("llvm.log10", &[bx.type_f64()]), + sym::log10f128 => ("llvm.log10", &[bx.type_f128()]), + + sym::log2f16 => ("llvm.log2", &[bx.type_f16()]), + sym::log2f32 => ("llvm.log2", &[bx.type_f32()]), + sym::log2f64 => ("llvm.log2", &[bx.type_f64()]), + sym::log2f128 => ("llvm.log2", &[bx.type_f128()]), + + sym::fmaf16 => ("llvm.fma", &[bx.type_f16()]), + sym::fmaf32 => ("llvm.fma", &[bx.type_f32()]), + sym::fmaf64 => ("llvm.fma", &[bx.type_f64()]), + sym::fmaf128 => ("llvm.fma", &[bx.type_f128()]), + + sym::fmuladdf16 => ("llvm.fmuladd", &[bx.type_f16()]), + sym::fmuladdf32 => ("llvm.fmuladd", &[bx.type_f32()]), + sym::fmuladdf64 => ("llvm.fmuladd", &[bx.type_f64()]), + sym::fmuladdf128 => ("llvm.fmuladd", &[bx.type_f128()]), + + sym::fabsf16 => ("llvm.fabs", &[bx.type_f16()]), + sym::fabsf32 => ("llvm.fabs", &[bx.type_f32()]), + sym::fabsf64 => ("llvm.fabs", &[bx.type_f64()]), + sym::fabsf128 => ("llvm.fabs", &[bx.type_f128()]), + + sym::minnumf16 => ("llvm.minnum", &[bx.type_f16()]), + sym::minnumf32 => ("llvm.minnum", &[bx.type_f32()]), + sym::minnumf64 => ("llvm.minnum", &[bx.type_f64()]), + sym::minnumf128 => ("llvm.minnum", &[bx.type_f128()]), + + sym::minimumf16 => ("llvm.minimum", &[bx.type_f16()]), + sym::minimumf32 => ("llvm.minimum", &[bx.type_f32()]), + sym::minimumf64 => ("llvm.minimum", &[bx.type_f64()]), // There are issues on x86_64 and aarch64 with the f128 variant, // let's instead use the instrinsic fallback body. - // sym::minimumf128 => "llvm.minimum.f128", - sym::maxnumf16 => "llvm.maxnum.f16", - sym::maxnumf32 => "llvm.maxnum.f32", - sym::maxnumf64 => "llvm.maxnum.f64", - sym::maxnumf128 => "llvm.maxnum.f128", - - sym::maximumf16 => "llvm.maximum.f16", - sym::maximumf32 => "llvm.maximum.f32", - sym::maximumf64 => "llvm.maximum.f64", + // sym::minimumf128 => ("llvm.minimum", &[cx.type_f128()]), + sym::maxnumf16 => ("llvm.maxnum", &[bx.type_f16()]), + sym::maxnumf32 => ("llvm.maxnum", &[bx.type_f32()]), + sym::maxnumf64 => ("llvm.maxnum", &[bx.type_f64()]), + sym::maxnumf128 => ("llvm.maxnum", &[bx.type_f128()]), + + sym::maximumf16 => ("llvm.maximum", &[bx.type_f16()]), + sym::maximumf32 => ("llvm.maximum", &[bx.type_f32()]), + sym::maximumf64 => ("llvm.maximum", &[bx.type_f64()]), // There are issues on x86_64 and aarch64 with the f128 variant, // let's instead use the instrinsic fallback body. - // sym::maximumf128 => "llvm.maximum.f128", - sym::copysignf16 => "llvm.copysign.f16", - sym::copysignf32 => "llvm.copysign.f32", - sym::copysignf64 => "llvm.copysign.f64", - sym::copysignf128 => "llvm.copysign.f128", - - sym::floorf16 => "llvm.floor.f16", - sym::floorf32 => "llvm.floor.f32", - sym::floorf64 => "llvm.floor.f64", - sym::floorf128 => "llvm.floor.f128", - - sym::ceilf16 => "llvm.ceil.f16", - sym::ceilf32 => "llvm.ceil.f32", - sym::ceilf64 => "llvm.ceil.f64", - sym::ceilf128 => "llvm.ceil.f128", - - sym::truncf16 => "llvm.trunc.f16", - sym::truncf32 => "llvm.trunc.f32", - sym::truncf64 => "llvm.trunc.f64", - sym::truncf128 => "llvm.trunc.f128", + // sym::maximumf128 => ("llvm.maximum", &[cx.type_f128()]), + sym::copysignf16 => ("llvm.copysign", &[bx.type_f16()]), + sym::copysignf32 => ("llvm.copysign", &[bx.type_f32()]), + sym::copysignf64 => ("llvm.copysign", &[bx.type_f64()]), + sym::copysignf128 => ("llvm.copysign", &[bx.type_f128()]), + + sym::floorf16 => ("llvm.floor", &[bx.type_f16()]), + sym::floorf32 => ("llvm.floor", &[bx.type_f32()]), + sym::floorf64 => ("llvm.floor", &[bx.type_f64()]), + sym::floorf128 => ("llvm.floor", &[bx.type_f128()]), + + sym::ceilf16 => ("llvm.ceil", &[bx.type_f16()]), + sym::ceilf32 => ("llvm.ceil", &[bx.type_f32()]), + sym::ceilf64 => ("llvm.ceil", &[bx.type_f64()]), + sym::ceilf128 => ("llvm.ceil", &[bx.type_f128()]), + + sym::truncf16 => ("llvm.trunc", &[bx.type_f16()]), + sym::truncf32 => ("llvm.trunc", &[bx.type_f32()]), + sym::truncf64 => ("llvm.trunc", &[bx.type_f64()]), + sym::truncf128 => ("llvm.trunc", &[bx.type_f128()]), // We could use any of `rint`, `nearbyint`, or `roundeven` // for this -- they are all identical in semantics when // assuming the default FP environment. // `rint` is what we used for $forever. - sym::round_ties_even_f16 => "llvm.rint.f16", - sym::round_ties_even_f32 => "llvm.rint.f32", - sym::round_ties_even_f64 => "llvm.rint.f64", - sym::round_ties_even_f128 => "llvm.rint.f128", + sym::round_ties_even_f16 => ("llvm.rint", &[bx.type_f16()]), + sym::round_ties_even_f32 => ("llvm.rint", &[bx.type_f32()]), + sym::round_ties_even_f64 => ("llvm.rint", &[bx.type_f64()]), + sym::round_ties_even_f128 => ("llvm.rint", &[bx.type_f128()]), - sym::roundf16 => "llvm.round.f16", - sym::roundf32 => "llvm.round.f32", - sym::roundf64 => "llvm.round.f64", - sym::roundf128 => "llvm.round.f128", - - sym::ptr_mask => "llvm.ptrmask", + sym::roundf16 => ("llvm.round", &[bx.type_f16()]), + sym::roundf32 => ("llvm.round", &[bx.type_f32()]), + sym::roundf64 => ("llvm.round", &[bx.type_f64()]), + sym::roundf128 => ("llvm.round", &[bx.type_f128()]), _ => return None, }; - Some(cx.get_intrinsic(llvm_name)) + Some(bx.call_intrinsic( + base_name, + type_params, + &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), + )) } impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { @@ -173,36 +176,24 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let name = tcx.item_name(instance.def_id()); let fn_args = instance.args; - let simple = get_simple_intrinsic(self, name); + let simple = call_simple_intrinsic(self, name, args); let llval = match name { - _ if simple.is_some() => { - let (simple_ty, simple_fn) = simple.unwrap(); - self.call( - simple_ty, - None, - None, - simple_fn, - &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), - None, - Some(instance), + _ if simple.is_some() => simple.unwrap(), + sym::ptr_mask => { + let ptr = args[0].immediate(); + self.call_intrinsic( + "llvm.ptrmask", + &[self.val_ty(ptr), self.type_isize()], + &[ptr, args[1].immediate()], ) } sym::is_val_statically_known => { - let intrinsic_type = args[0].layout.immediate_llvm_type(self.cx); - let kind = self.type_kind(intrinsic_type); - let intrinsic_name = match kind { - TypeKind::Pointer | TypeKind::Integer => { - Some(format!("llvm.is.constant.{intrinsic_type:?}")) - } - // LLVM float types' intrinsic names differ from their type names. - TypeKind::Half => Some(format!("llvm.is.constant.f16")), - TypeKind::Float => Some(format!("llvm.is.constant.f32")), - TypeKind::Double => Some(format!("llvm.is.constant.f64")), - TypeKind::FP128 => Some(format!("llvm.is.constant.f128")), - _ => None, - }; - if let Some(intrinsic_name) = intrinsic_name { - self.call_intrinsic(&intrinsic_name, &[args[0].immediate()]) + if let OperandValue::Immediate(imm) = args[0].val { + self.call_intrinsic( + "llvm.is.constant", + &[args[0].layout.immediate_llvm_type(self.cx)], + &[imm], + ) } else { self.const_bool(false) } @@ -246,9 +237,14 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ); return Ok(()); } - sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[]), + sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[], &[]), sym::va_copy => { - self.call_intrinsic("llvm.va_copy", &[args[0].immediate(), args[1].immediate()]) + let dest = args[0].immediate(); + self.call_intrinsic( + "llvm.va_copy", + &[self.val_ty(dest)], + &[dest, args[1].immediate()], + ) } sym::va_arg => { match result.layout.backend_repr { @@ -322,14 +318,11 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { sym::prefetch_write_instruction => (1, 0), _ => bug!(), }; + let ptr = args[0].immediate(); self.call_intrinsic( "llvm.prefetch", - &[ - args[0].immediate(), - self.const_i32(rw), - args[1].immediate(), - self.const_i32(cache_type), - ], + &[self.val_ty(ptr)], + &[ptr, self.const_i32(rw), args[1].immediate(), self.const_i32(cache_type)], ) } sym::carrying_mul_add => { @@ -385,11 +378,13 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } let (size, signed) = ty.int_size_and_signed(self.tcx); let width = size.bits(); + let llty = self.type_ix(width); match name { sym::ctlz | sym::cttz => { let y = self.const_bool(false); let ret = self.call_intrinsic( - &format!("llvm.{name}.i{width}"), + format!("llvm.{name}"), + &[llty], &[args[0].immediate(), y], ); @@ -397,62 +392,54 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } 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]); + let ret = + self.call_intrinsic("llvm.ctlz", &[llty], &[args[0].immediate(), y]); self.intcast(ret, result.layout.llvm_type(self), 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]); + let ret = + self.call_intrinsic("llvm.cttz", &[llty], &[args[0].immediate(), y]); self.intcast(ret, result.layout.llvm_type(self), false) } sym::ctpop => { - let ret = self.call_intrinsic( - &format!("llvm.ctpop.i{width}"), - &[args[0].immediate()], - ); + let ret = + self.call_intrinsic("llvm.ctpop", &[llty], &[args[0].immediate()]); self.intcast(ret, result.layout.llvm_type(self), 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()], - ) + self.call_intrinsic("llvm.bswap", &[llty], &[args[0].immediate()]) } } - sym::bitreverse => self.call_intrinsic( - &format!("llvm.bitreverse.i{width}"), - &[args[0].immediate()], - ), + sym::bitreverse => { + self.call_intrinsic("llvm.bitreverse", &[llty], &[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); + let llvm_name = format!("llvm.fsh{}", if is_left { 'l' } else { 'r' }); // 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]) + self.call_intrinsic(llvm_name, &[llty], &[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{}", + let llvm_name = format!( + "llvm.{}{}.sat", if signed { 's' } else { 'u' }, if is_add { "add" } else { "sub" }, - width ); - self.call_intrinsic(llvm_name, &[lhs, rhs]) + self.call_intrinsic(llvm_name, &[llty], &[lhs, rhs]) } _ => bug!(), } @@ -484,11 +471,8 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.icmp(IntPredicate::IntEQ, a_val, b_val) } else { let n = self.const_usize(layout.size().bytes()); - let cmp = self.call_intrinsic("memcmp", &[a, b, n]); - match self.cx.sess().target.arch.as_ref() { - "avr" | "msp430" => self.icmp(IntPredicate::IntEQ, cmp, self.const_i16(0)), - _ => self.icmp(IntPredicate::IntEQ, cmp, self.const_i32(0)), - } + let cmp = self.call_intrinsic("memcmp", &[], &[a, b, n]); + self.icmp(IntPredicate::IntEQ, cmp, self.const_int(self.type_int(), 0)) } } @@ -496,6 +480,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { // Here we assume that the `memcmp` provided by the target is a NOP for size 0. let cmp = self.call_intrinsic( "memcmp", + &[], &[args[0].immediate(), args[1].immediate(), args[2].immediate()], ); // Some targets have `memcmp` returning `i16`, but the intrinsic is always `i32`. @@ -619,18 +604,22 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } fn abort(&mut self) { - self.call_intrinsic("llvm.trap", &[]); + self.call_intrinsic("llvm.trap", &[], &[]); } fn assume(&mut self, val: Self::Value) { if self.cx.sess().opts.optimize != rustc_session::config::OptLevel::No { - self.call_intrinsic("llvm.assume", &[val]); + self.call_intrinsic("llvm.assume", &[], &[val]); } } fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value { if self.cx.sess().opts.optimize != rustc_session::config::OptLevel::No { - self.call_intrinsic("llvm.expect.i1", &[cond, self.const_bool(expected)]) + self.call_intrinsic( + "llvm.expect", + &[self.type_i1()], + &[cond, self.const_bool(expected)], + ) } else { cond } @@ -644,17 +633,20 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ) -> Self::Value { let typeid = self.get_metadata_value(typeid); let vtable_byte_offset = self.const_i32(vtable_byte_offset as i32); - let type_checked_load = - self.call_intrinsic("llvm.type.checked.load", &[llvtable, vtable_byte_offset, typeid]); + let type_checked_load = self.call_intrinsic( + "llvm.type.checked.load", + &[], + &[llvtable, vtable_byte_offset, typeid], + ); self.extract_value(type_checked_load, 0) } fn va_start(&mut self, va_list: &'ll Value) -> &'ll Value { - self.call_intrinsic("llvm.va_start", &[va_list]) + self.call_intrinsic("llvm.va_start", &[self.val_ty(va_list)], &[va_list]) } fn va_end(&mut self, va_list: &'ll Value) -> &'ll Value { - self.call_intrinsic("llvm.va_end", &[va_list]) + self.call_intrinsic("llvm.va_end", &[self.val_ty(va_list)], &[va_list]) } } @@ -893,8 +885,8 @@ fn codegen_wasm_try<'ll, 'tcx>( let null = bx.const_null(bx.type_ptr()); let funclet = bx.catch_pad(cs, &[null]); - let ptr = bx.call_intrinsic("llvm.wasm.get.exception", &[funclet.cleanuppad()]); - let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[funclet.cleanuppad()]); + let ptr = bx.call_intrinsic("llvm.wasm.get.exception", &[], &[funclet.cleanuppad()]); + let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[], &[funclet.cleanuppad()]); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet), None); @@ -1031,7 +1023,7 @@ fn codegen_emcc_try<'ll, 'tcx>( let selector = bx.extract_value(vals, 1); // Check if the typeid we got is the one for a Rust panic. - let rust_typeid = bx.call_intrinsic("llvm.eh.typeid.for", &[tydesc]); + let rust_typeid = bx.call_intrinsic("llvm.eh.typeid.for", &[bx.val_ty(tydesc)], &[tydesc]); let is_rust_panic = bx.icmp(IntPredicate::IntEQ, selector, rust_typeid); let is_rust_panic = bx.zext(is_rust_panic, bx.type_bool()); @@ -1522,56 +1514,37 @@ fn generic_simd_intrinsic<'ll, 'tcx>( }}; } - let (elem_ty_str, elem_ty) = if let ty::Float(f) = in_elem.kind() { - let elem_ty = bx.cx.type_float_from_ty(*f); - match f.bit_width() { - 16 => ("f16", elem_ty), - 32 => ("f32", elem_ty), - 64 => ("f64", elem_ty), - 128 => ("f128", elem_ty), - _ => return_error!(InvalidMonomorphization::FloatingPointVector { - span, - name, - f_ty: *f, - in_ty, - }), - } + let elem_ty = if let ty::Float(f) = in_elem.kind() { + bx.cx.type_float_from_ty(*f) } else { return_error!(InvalidMonomorphization::FloatingPointType { span, name, in_ty }); }; let vec_ty = bx.type_vector(elem_ty, in_len); - let (intr_name, fn_ty) = match name { - sym::simd_ceil => ("ceil", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fabs => ("fabs", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fcos => ("cos", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fexp2 => ("exp2", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fexp => ("exp", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_flog10 => ("log10", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_flog2 => ("log2", bx.type_func(&[vec_ty], vec_ty)), - 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_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fsqrt => ("sqrt", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_round => ("round", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_trunc => ("trunc", bx.type_func(&[vec_ty], vec_ty)), + let intr_name = match name { + sym::simd_ceil => "llvm.ceil", + sym::simd_fabs => "llvm.fabs", + sym::simd_fcos => "llvm.cos", + sym::simd_fexp2 => "llvm.exp2", + sym::simd_fexp => "llvm.exp", + sym::simd_flog10 => "llvm.log10", + sym::simd_flog2 => "llvm.log2", + sym::simd_flog => "llvm.log", + sym::simd_floor => "llvm.floor", + sym::simd_fma => "llvm.fma", + sym::simd_relaxed_fma => "llvm.fmuladd", + sym::simd_fsin => "llvm.sin", + sym::simd_fsqrt => "llvm.sqrt", + sym::simd_round => "llvm.round", + sym::simd_trunc => "llvm.trunc", _ => return_error!(InvalidMonomorphization::UnrecognizedIntrinsic { span, name }), }; - let llvm_name = &format!("llvm.{intr_name}.v{in_len}{elem_ty_str}"); - let f = bx.declare_cfn(llvm_name, llvm::UnnamedAddr::No, fn_ty); - let c = bx.call( - fn_ty, - None, - None, - f, + Ok(bx.call_intrinsic( + intr_name, + &[vec_ty], &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), - None, - None, - ); - Ok(c) + )) } if std::matches!( @@ -1595,29 +1568,6 @@ fn generic_simd_intrinsic<'ll, 'tcx>( return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args); } - // FIXME: use: - // https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Function.h#L182 - // https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Intrinsics.h#L81 - fn llvm_vector_str(bx: &Builder<'_, '_, '_>, elem_ty: Ty<'_>, vec_len: u64) -> String { - match *elem_ty.kind() { - ty::Int(v) => format!( - "v{}i{}", - vec_len, - // Normalize to prevent crash if v: IntTy::Isize - v.normalize(bx.target_spec().pointer_width).bit_width().unwrap() - ), - ty::Uint(v) => format!( - "v{}i{}", - vec_len, - // Normalize to prevent crash if v: UIntTy::Usize - v.normalize(bx.target_spec().pointer_width).bit_width().unwrap() - ), - ty::Float(v) => format!("v{}f{}", vec_len, v.bit_width()), - ty::RawPtr(_, _) => format!("v{}p0", vec_len), - _ => unreachable!(), - } - } - fn llvm_vector_ty<'ll>(cx: &CodegenCx<'ll, '_>, elem_ty: Ty<'_>, vec_len: u64) -> &'ll Type { let elem_ty = match *elem_ty.kind() { ty::Int(v) => cx.type_int_from_ty(v), @@ -1698,38 +1648,22 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ); // Alignment of T, must be a constant integer value: - let alignment_ty = bx.type_i32(); let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32); // Truncate the mask vector to a vector of i1s: let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len); - let mask_ty = bx.type_vector(bx.type_i1(), in_len); // Type of the vector of pointers: let llvm_pointer_vec_ty = llvm_vector_ty(bx, element_ty1, in_len); - let llvm_pointer_vec_str = llvm_vector_str(bx, element_ty1, in_len); // Type of the vector of elements: let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len); - let llvm_elem_vec_str = llvm_vector_str(bx, element_ty0, in_len); - let llvm_intrinsic = - format!("llvm.masked.gather.{llvm_elem_vec_str}.{llvm_pointer_vec_str}"); - let fn_ty = bx.type_func( - &[llvm_pointer_vec_ty, alignment_ty, mask_ty, llvm_elem_vec_ty], - llvm_elem_vec_ty, - ); - let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - let v = bx.call( - fn_ty, - None, - None, - f, + return Ok(bx.call_intrinsic( + "llvm.masked.gather", + &[llvm_elem_vec_ty, llvm_pointer_vec_ty], &[args[1].immediate(), alignment, mask, args[0].immediate()], - None, - None, - ); - return Ok(v); + )); } if name == sym::simd_masked_load { @@ -1795,32 +1729,20 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ); let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len); - let mask_ty = bx.type_vector(bx.type_i1(), mask_len); // Alignment of T, must be a constant integer value: - let alignment_ty = bx.type_i32(); let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32); let llvm_pointer = bx.type_ptr(); // Type of the vector of elements: let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len); - let llvm_elem_vec_str = llvm_vector_str(bx, values_elem, values_len); - - let llvm_intrinsic = format!("llvm.masked.load.{llvm_elem_vec_str}.p0"); - let fn_ty = bx - .type_func(&[llvm_pointer, alignment_ty, mask_ty, llvm_elem_vec_ty], llvm_elem_vec_ty); - let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - let v = bx.call( - fn_ty, - None, - None, - f, + + return Ok(bx.call_intrinsic( + "llvm.masked.load", + &[llvm_elem_vec_ty, llvm_pointer], &[args[1].immediate(), alignment, mask, args[2].immediate()], - None, - None, - ); - return Ok(v); + )); } if name == sym::simd_masked_store { @@ -1880,33 +1802,20 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ); let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len); - let mask_ty = bx.type_vector(bx.type_i1(), mask_len); // Alignment of T, must be a constant integer value: - let alignment_ty = bx.type_i32(); let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32); - let ret_t = bx.type_void(); - let llvm_pointer = bx.type_ptr(); // Type of the vector of elements: let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len); - let llvm_elem_vec_str = llvm_vector_str(bx, values_elem, values_len); - - let llvm_intrinsic = format!("llvm.masked.store.{llvm_elem_vec_str}.p0"); - let fn_ty = bx.type_func(&[llvm_elem_vec_ty, llvm_pointer, alignment_ty, mask_ty], ret_t); - let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - let v = bx.call( - fn_ty, - None, - None, - f, + + return Ok(bx.call_intrinsic( + "llvm.masked.store", + &[llvm_elem_vec_ty, llvm_pointer], &[args[2].immediate(), args[1].immediate(), alignment, mask], - None, - None, - ); - return Ok(v); + )); } if name == sym::simd_scatter { @@ -1971,38 +1880,22 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ); // Alignment of T, must be a constant integer value: - let alignment_ty = bx.type_i32(); let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32); // Truncate the mask vector to a vector of i1s: let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len); - let mask_ty = bx.type_vector(bx.type_i1(), in_len); - - let ret_t = bx.type_void(); // Type of the vector of pointers: let llvm_pointer_vec_ty = llvm_vector_ty(bx, element_ty1, in_len); - let llvm_pointer_vec_str = llvm_vector_str(bx, element_ty1, in_len); // Type of the vector of elements: let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len); - let llvm_elem_vec_str = llvm_vector_str(bx, element_ty0, in_len); - - let llvm_intrinsic = - format!("llvm.masked.scatter.{llvm_elem_vec_str}.{llvm_pointer_vec_str}"); - let fn_ty = - bx.type_func(&[llvm_elem_vec_ty, llvm_pointer_vec_ty, alignment_ty, mask_ty], ret_t); - let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - let v = bx.call( - fn_ty, - None, - None, - f, + + return Ok(bx.call_intrinsic( + "llvm.masked.scatter", + &[llvm_elem_vec_ty, llvm_pointer_vec_ty], &[args[0].immediate(), args[1].immediate(), alignment, mask], - None, - None, - ); - return Ok(v); + )); } macro_rules! arith_red { @@ -2431,40 +2324,31 @@ fn generic_simd_intrinsic<'ll, 'tcx>( }, in_len as u64, ); - let intrinsic_name = match name { - sym::simd_bswap => "bswap", - sym::simd_bitreverse => "bitreverse", - sym::simd_ctlz => "ctlz", - sym::simd_ctpop => "ctpop", - sym::simd_cttz => "cttz", + let llvm_intrinsic = match name { + sym::simd_bswap => "llvm.bswap", + sym::simd_bitreverse => "llvm.bitreverse", + sym::simd_ctlz => "llvm.ctlz", + sym::simd_ctpop => "llvm.ctpop", + sym::simd_cttz => "llvm.cttz", _ => unreachable!(), }; let int_size = in_elem.int_size_and_signed(bx.tcx()).0.bits(); - let llvm_intrinsic = &format!("llvm.{}.v{}i{}", intrinsic_name, in_len, int_size,); return match name { // byte swap is no-op for i8/u8 sym::simd_bswap if int_size == 8 => Ok(args[0].immediate()), sym::simd_ctlz | sym::simd_cttz => { // for the (int, i1 immediate) pair, the second arg adds `(0, true) => poison` - let fn_ty = bx.type_func(&[vec_ty, bx.type_i1()], vec_ty); let dont_poison_on_zero = bx.const_int(bx.type_i1(), 0); - let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - Ok(bx.call( - fn_ty, - None, - None, - f, + Ok(bx.call_intrinsic( + llvm_intrinsic, + &[vec_ty], &[args[0].immediate(), dont_poison_on_zero], - None, - None, )) } sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctpop => { // simple unary argument cases - let fn_ty = bx.type_func(&[vec_ty], vec_ty); - let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None, None)) + Ok(bx.call_intrinsic(llvm_intrinsic, &[vec_ty], &[args[0].immediate()])) } _ => unreachable!(), }; @@ -2495,10 +2379,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let lhs = args[0].immediate(); let rhs = args[1].immediate(); let is_add = name == sym::simd_saturating_add; - let ptr_bits = bx.tcx().data_layout.pointer_size.bits() as _; - let (signed, elem_width, elem_ty) = match *in_elem.kind() { - ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_int_from_ty(i)), - ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_uint_from_ty(i)), + let (signed, elem_ty) = match *in_elem.kind() { + ty::Int(i) => (true, bx.cx.type_int_from_ty(i)), + ty::Uint(i) => (false, bx.cx.type_uint_from_ty(i)), _ => { return_error!(InvalidMonomorphization::ExpectedVectorElementType { span, @@ -2508,19 +2391,14 @@ fn generic_simd_intrinsic<'ll, 'tcx>( }); } }; - let llvm_intrinsic = &format!( - "llvm.{}{}.sat.v{}i{}", + let llvm_intrinsic = format!( + "llvm.{}{}.sat", if signed { 's' } else { 'u' }, if is_add { "add" } else { "sub" }, - in_len, - elem_width ); let vec_ty = bx.cx.type_vector(elem_ty, in_len as u64); - let fn_ty = bx.type_func(&[vec_ty, vec_ty], vec_ty); - let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - let v = bx.call(fn_ty, None, None, f, &[lhs, rhs], None, None); - return Ok(v); + return Ok(bx.call_intrinsic(llvm_intrinsic, &[vec_ty], &[lhs, rhs])); } span_bug!(span, "unknown SIMD intrinsic"); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 6890923a594..cdfffbe47bf 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -340,18 +340,11 @@ impl CodegenBackend for LlvmCodegenBackend { target_config(sess) } - fn codegen_crate<'tcx>( - &self, - tcx: TyCtxt<'tcx>, - metadata: EncodedMetadata, - need_metadata_module: bool, - ) -> Box<dyn Any> { + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box<dyn Any> { Box::new(rustc_codegen_ssa::base::codegen_crate( LlvmCodegenBackend(()), tcx, crate::llvm_util::target_cpu(tcx.sess).to_string(), - metadata, - need_metadata_module, )) } @@ -376,14 +369,20 @@ impl CodegenBackend for LlvmCodegenBackend { (codegen_results, work_products) } - fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) { + fn link( + &self, + sess: &Session, + codegen_results: CodegenResults, + metadata: EncodedMetadata, + 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, metadata, outputs); } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e27fbf94f34..91ada856d59 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -15,6 +15,7 @@ use std::fmt::Debug; use std::marker::PhantomData; +use std::num::NonZero; use std::ptr; use bitflags::bitflags; @@ -1077,8 +1078,6 @@ unsafe extern "C" { // Operations on other types pub(crate) fn LLVMVoidTypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMTokenTypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMMetadataTypeInContext(C: &Context) -> &Type; // Operations on all values pub(crate) fn LLVMTypeOf(Val: &Value) -> &Type; @@ -1195,6 +1194,15 @@ unsafe extern "C" { // Operations on functions pub(crate) fn LLVMSetFunctionCallConv(Fn: &Value, CC: c_uint); + // Operations about llvm intrinsics + pub(crate) fn LLVMLookupIntrinsicID(Name: *const c_char, NameLen: size_t) -> c_uint; + pub(crate) fn LLVMGetIntrinsicDeclaration<'a>( + Mod: &'a Module, + ID: NonZero<c_uint>, + ParamTypes: *const &'a Type, + ParamCount: size_t, + ) -> &'a Value; + // Operations on parameters pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>; pub(crate) safe fn LLVMCountParams(Fn: &Value) -> c_uint; diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index ed23f911930..661174a80df 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -1,6 +1,7 @@ #![allow(non_snake_case)] use std::ffi::{CStr, CString}; +use std::num::NonZero; use std::ptr; use std::str::FromStr; use std::string::FromUtf8Error; @@ -327,6 +328,28 @@ pub(crate) fn get_value_name(value: &Value) -> &[u8] { } } +#[derive(Debug, Copy, Clone)] +pub(crate) struct Intrinsic { + id: NonZero<c_uint>, +} + +impl Intrinsic { + pub(crate) fn lookup(name: &[u8]) -> Option<Self> { + let id = unsafe { LLVMLookupIntrinsicID(name.as_c_char_ptr(), name.len()) }; + NonZero::new(id).map(|id| Self { id }) + } + + pub(crate) fn get_declaration<'ll>( + self, + llmod: &'ll Module, + type_params: &[&'ll Type], + ) -> &'ll Value { + unsafe { + LLVMGetIntrinsicDeclaration(llmod, self.id, type_params.as_ptr(), type_params.len()) + } + } +} + /// Safe wrapper for `LLVMSetValueName2` from a byte slice pub(crate) fn set_value_name(value: &Value, name: &[u8]) { unsafe { diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 9718c95f38a..0e77bc43df8 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -16,6 +16,7 @@ use rustc_fs_util::path_to_c_string; use rustc_middle::bug; use rustc_session::Session; use rustc_session::config::{PrintKind, PrintRequest}; +use rustc_session::features::{StabilityExt, retpoline_features_by_flags}; use rustc_span::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport}; use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES}; @@ -23,8 +24,7 @@ use smallvec::{SmallVec, smallvec}; use crate::back::write::create_informational_target_machine; use crate::errors::{ - FixedX18InvalidArch, ForbiddenCTargetFeature, PossibleFeature, UnknownCTargetFeature, - UnknownCTargetFeaturePrefix, UnstableCTargetFeature, + FixedX18InvalidArch, PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix, }; use crate::llvm; @@ -707,6 +707,12 @@ pub(crate) fn target_cpu(sess: &Session) -> &str { handle_native(cpu_name) } +fn llvm_features_by_flags(sess: &Session) -> Vec<&str> { + let mut features: Vec<&str> = Vec::new(); + retpoline_features_by_flags(sess, &mut features); + features +} + /// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, /// `--target` and similar). pub(crate) fn global_llvm_features( @@ -787,7 +793,7 @@ pub(crate) fn global_llvm_features( // Compute implied features let mut all_rust_features = vec![]; - for feature in sess.opts.cg.target_feature.split(',') { + for feature in sess.opts.cg.target_feature.split(',').chain(llvm_features_by_flags(sess)) { if let Some(feature) = feature.strip_prefix('+') { all_rust_features.extend( UnordSet::from(sess.target.implied_target_features(feature)) @@ -840,18 +846,7 @@ pub(crate) fn global_llvm_features( sess.dcx().emit_warn(unknown_feature); } Some((_, stability, _)) => { - if let Err(reason) = stability.toggle_allowed() { - sess.dcx().emit_warn(ForbiddenCTargetFeature { - feature, - enabled: if enable { "enabled" } else { "disabled" }, - reason, - }); - } else if stability.requires_nightly().is_some() { - // An unstable feature. Warn about using it. It makes little sense - // to hard-error here since we just warn about fully unknown - // features above. - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); - } + stability.verify_feature_enabled_by_flag(sess, enable, feature); } } diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 169036f5152..453eca2bbe1 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -1,4 +1,5 @@ use std::borrow::Borrow; +use std::hash::{Hash, Hasher}; use std::{fmt, ptr}; use libc::{c_char, c_uint}; @@ -25,6 +26,14 @@ impl PartialEq for Type { } } +impl Eq for Type {} + +impl Hash for Type { + fn hash<H: Hasher>(&self, state: &mut H) { + ptr::hash(self, state); + } +} + impl fmt::Debug for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str( @@ -49,13 +58,6 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> { pub(crate) fn type_void(&self) -> &'ll Type { unsafe { llvm::LLVMVoidTypeInContext(self.llcx()) } } - pub(crate) fn type_token(&self) -> &'ll Type { - unsafe { llvm::LLVMTokenTypeInContext(self.llcx()) } - } - - pub(crate) fn type_metadata(&self) -> &'ll Type { - unsafe { llvm::LLVMMetadataTypeInContext(self.llcx()) } - } ///x Creates an integer type with the given number of bits, e.g., i24 pub(crate) fn type_ix(&self, num_bits: u64) -> &'ll Type { diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 91f6af7fb93..5322fe58cf3 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -198,8 +198,6 @@ codegen_ssa_linking_failed = linking with `{$linker_path}` failed: {$exit_status codegen_ssa_malformed_cgu_name = found malformed codegen unit name `{$user_path}`. codegen units names must always start with the name of the crate (`{$crate_name}` in this case). -codegen_ssa_metadata_object_file_write = error writing metadata object file: {$error} - codegen_ssa_missing_cpp_build_tool_component = or a necessary component may be missing from the "C++ build tools" workload codegen_ssa_missing_features = add the missing features in a `target_feature` attribute diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 1e1bdfb5977..84a56f6b0b5 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -14,11 +14,12 @@ use object::read::macho::FatArch; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; use rustc_fs_util::TempDirBuilder; +use rustc_metadata::EncodedMetadata; use rustc_session::Session; use rustc_span::Symbol; use tracing::trace; -use super::metadata::search_for_section; +use super::metadata::{create_compressed_metadata_file, search_for_section}; use crate::common; // Re-exporting for rustc_codegen_llvm::back::archive pub use crate::errors::{ArchiveBuildFailure, ExtractBundledLibsError, UnknownArchiveKind}; @@ -58,6 +59,15 @@ impl From<ImportLibraryItem> for COFFShortExport { pub trait ArchiveBuilderBuilder { fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a>; + fn create_dylib_metadata_wrapper( + &self, + sess: &Session, + metadata: &EncodedMetadata, + symbol_name: &str, + ) -> Vec<u8> { + create_compressed_metadata_file(sess, metadata, symbol_name) + } + /// Creates a DLL Import Library <https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-creation#creating-an-import-library>. /// and returns the path on disk to that import library. /// This functions doesn't take `self` so that it can be called from diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 168077260a6..8882ba359b7 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -23,7 +23,8 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_macros::LintDiagnostic; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; use rustc_metadata::{ - NativeLibSearchFallback, find_native_static_library, walk_native_lib_search_dirs, + EncodedMetadata, NativeLibSearchFallback, find_native_static_library, + walk_native_lib_search_dirs, }; use rustc_middle::bug; use rustc_middle::lint::lint_level; @@ -68,29 +69,13 @@ pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { } } -fn check_link_info_print_request(sess: &Session, crate_types: &[CrateType]) { - let print_native_static_libs = - sess.opts.prints.iter().any(|p| p.kind == PrintKind::NativeStaticLibs); - let has_staticlib = crate_types.iter().any(|ct| *ct == CrateType::Staticlib); - if print_native_static_libs { - if !has_staticlib { - sess.dcx() - .warn(format!("cannot output linkage information without staticlib crate-type")); - sess.dcx() - .note(format!("consider `--crate-type staticlib` to print linkage information")); - } else if !sess.opts.output_types.should_link() { - sess.dcx() - .warn(format!("cannot output linkage information when --emit link is not passed")); - } - } -} - /// Performs the linkage portion of the compilation phase. This will generate all /// of the requested outputs for this compilation session. pub fn link_binary( sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: CodegenResults, + metadata: EncodedMetadata, outputs: &OutputFilenames, ) { let _timer = sess.timer("link_binary"); @@ -142,6 +127,7 @@ pub fn link_binary( sess, archive_builder_builder, &codegen_results, + &metadata, RlibFlavor::Normal, &path, ) @@ -152,6 +138,7 @@ pub fn link_binary( sess, archive_builder_builder, &codegen_results, + &metadata, &out_filename, &path, ); @@ -163,6 +150,7 @@ pub fn link_binary( crate_type, &out_filename, &codegen_results, + &metadata, path.as_ref(), ); } @@ -203,8 +191,6 @@ pub fn link_binary( } } - check_link_info_print_request(sess, &codegen_results.crate_info.crate_types); - // Remove the temporary object file and metadata if we aren't saving temps. sess.time("link_binary_remove_temps", || { // If the user requests that temporaries are saved, don't delete any. @@ -226,11 +212,7 @@ pub fn link_binary( let remove_temps_from_module = |module: &CompiledModule| maybe_remove_temps_from_module(false, false, module); - // Otherwise, always remove the metadata and allocator module temporaries. - if let Some(ref metadata_module) = codegen_results.metadata_module { - remove_temps_from_module(metadata_module); - } - + // Otherwise, always remove the allocator module temporaries. if let Some(ref allocator_module) = codegen_results.allocator_module { remove_temps_from_module(allocator_module); } @@ -312,6 +294,7 @@ fn link_rlib<'a>( sess: &'a Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, + metadata: &EncodedMetadata, flavor: RlibFlavor, tmpdir: &MaybeTempDir, ) -> Box<dyn ArchiveBuilder + 'a> { @@ -319,12 +302,9 @@ fn link_rlib<'a>( let trailing_metadata = match flavor { RlibFlavor::Normal => { - let (metadata, metadata_position) = create_wrapper_file( - sess, - ".rmeta".to_string(), - codegen_results.metadata.stub_or_full(), - ); - let metadata = emit_wrapper_file(sess, &metadata, tmpdir, METADATA_FILENAME); + let (metadata, metadata_position) = + create_wrapper_file(sess, ".rmeta".to_string(), metadata.stub_or_full()); + let metadata = emit_wrapper_file(sess, &metadata, tmpdir.as_ref(), METADATA_FILENAME); match metadata_position { MetadataPosition::First => { // Most of the time metadata in rlib files is wrapped in a "dummy" object @@ -392,7 +372,7 @@ fn link_rlib<'a>( let src = read(path) .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()); + let wrapper_file = emit_wrapper_file(sess, &data, tmpdir.as_ref(), filename.as_str()); packed_bundled_libs.push(wrapper_file); } else { let path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess); @@ -473,6 +453,7 @@ fn link_staticlib( sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, + metadata: &EncodedMetadata, out_filename: &Path, tempdir: &MaybeTempDir, ) { @@ -481,6 +462,7 @@ fn link_staticlib( sess, archive_builder_builder, codegen_results, + metadata, RlibFlavor::StaticlibBase, tempdir, ); @@ -694,6 +676,7 @@ fn link_natively( crate_type: CrateType, out_filename: &Path, codegen_results: &CodegenResults, + metadata: &EncodedMetadata, tmpdir: &Path, ) { info!("preparing {:?} to {:?}", crate_type, out_filename); @@ -718,6 +701,7 @@ fn link_natively( tmpdir, temp_filename, codegen_results, + metadata, self_contained_components, ); @@ -1065,11 +1049,11 @@ fn link_natively( match strip { Strip::Debuginfo => { // FIXME: AIX's strip utility only offers option to strip line number information. - strip_with_external_utility(sess, stripcmd, out_filename, &["-X32_64", "-l"]) + strip_with_external_utility(sess, stripcmd, temp_filename, &["-X32_64", "-l"]) } Strip::Symbols => { // Must be noted this option might remove symbol __aix_rust_metadata and thus removes .info section which contains metadata. - strip_with_external_utility(sess, stripcmd, out_filename, &["-X32_64", "-r"]) + strip_with_external_utility(sess, stripcmd, temp_filename, &["-X32_64", "-r"]) } Strip::None => {} } @@ -2095,17 +2079,25 @@ fn add_local_crate_allocator_objects(cmd: &mut dyn Linker, codegen_results: &Cod /// Add object files containing metadata for the current crate. fn add_local_crate_metadata_objects( cmd: &mut dyn Linker, + sess: &Session, + archive_builder_builder: &dyn ArchiveBuilderBuilder, crate_type: CrateType, + tmpdir: &Path, codegen_results: &CodegenResults, + metadata: &EncodedMetadata, ) { // When linking a dynamic library, we put the metadata into a section of the // executable. This metadata is in a separate object file from the main - // object file, so we link that in here. - if matches!(crate_type, CrateType::Dylib | CrateType::ProcMacro) - && let Some(m) = &codegen_results.metadata_module - && let Some(obj) = &m.object - { - cmd.add_object(obj); + // object file, so we create and link it in here. + if matches!(crate_type, CrateType::Dylib | CrateType::ProcMacro) { + let data = archive_builder_builder.create_dylib_metadata_wrapper( + sess, + &metadata, + &codegen_results.crate_info.metadata_symbol, + ); + let obj = emit_wrapper_file(sess, &data, tmpdir, "rmeta.o"); + + cmd.add_object(&obj); } } @@ -2195,6 +2187,7 @@ fn linker_with_args( tmpdir: &Path, out_filename: &Path, codegen_results: &CodegenResults, + metadata: &EncodedMetadata, self_contained_components: LinkSelfContainedComponents, ) -> Command { let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled(); @@ -2269,7 +2262,15 @@ fn linker_with_args( // in this DAG so far because they can only depend on other native libraries // and such dependencies are also required to be specified. add_local_crate_regular_objects(cmd, codegen_results); - add_local_crate_metadata_objects(cmd, crate_type, codegen_results); + add_local_crate_metadata_objects( + cmd, + sess, + archive_builder_builder, + crate_type, + tmpdir, + codegen_results, + metadata, + ); add_local_crate_allocator_objects(cmd, codegen_results); // Avoid linking to dynamic libraries unless they satisfy some undefined symbols diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index a16862c41ee..d091c46d9c1 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -379,6 +379,24 @@ pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 { }; e_flags } + Architecture::PowerPc64 => { + const EF_PPC64_ABI_UNKNOWN: u32 = 0; + const EF_PPC64_ABI_ELF_V1: u32 = 1; + const EF_PPC64_ABI_ELF_V2: u32 = 2; + + match sess.target.options.llvm_abiname.as_ref() { + // If the flags do not correctly indicate the ABI, + // linkers such as ld.lld assume that the ppc64 object files are always ELFv2 + // which leads to broken binaries if ELFv1 is used for the object files. + "elfv1" => EF_PPC64_ABI_ELF_V1, + "elfv2" => EF_PPC64_ABI_ELF_V2, + "" if sess.target.options.binary_format.to_object() == BinaryFormat::Elf => { + bug!("No ABI specified for this PPC64 ELF target"); + } + // Fall back + _ => EF_PPC64_ABI_UNKNOWN, + } + } _ => 0, } } diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 92b9b6e132e..d0b6c7470fb 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -219,6 +219,7 @@ fn exported_symbols_provider_local<'tcx>( .chain([ mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), mangle_internal_symbol(tcx, OomStrategy::SYMBOL), + mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), ]) { let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name)); @@ -232,19 +233,6 @@ fn exported_symbols_provider_local<'tcx>( }, )); } - - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new( - tcx, - &mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), - )); - symbols.push(( - exported_symbol, - SymbolExportInfo { - level: SymbolExportLevel::Rust, - kind: SymbolExportKind::Data, - used: false, - }, - )) } if tcx.sess.instrument_coverage() || tcx.sess.opts.cg.profile_generate.enabled() { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index a41ca8ce28b..bbf9cceef2a 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -24,7 +24,6 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_incremental::{ copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, }; -use rustc_metadata::EncodedMetadata; use rustc_metadata::fs::copy_to_stdout; use rustc_middle::bug; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; @@ -142,7 +141,6 @@ impl ModuleConfig { || match kind { ModuleKind::Regular => sess.opts.output_types.contains_key(&OutputType::Object), ModuleKind::Allocator => false, - ModuleKind::Metadata => sess.opts.output_types.contains_key(&OutputType::Metadata), }; let emit_obj = if !should_emit_obj { @@ -350,7 +348,6 @@ pub struct CodegenContext<B: WriteBackendMethods> { pub output_filenames: Arc<OutputFilenames>, pub invocation_temp: Option<String>, pub regular_module_config: Arc<ModuleConfig>, - pub metadata_module_config: Arc<ModuleConfig>, pub allocator_module_config: Arc<ModuleConfig>, pub tm_factory: TargetMachineFactoryFn<B>, pub msvc_imps_needed: bool, @@ -395,7 +392,6 @@ impl<B: WriteBackendMethods> CodegenContext<B> { pub fn config(&self, kind: ModuleKind) -> &ModuleConfig { match kind { ModuleKind::Regular => &self.regular_module_config, - ModuleKind::Metadata => &self.metadata_module_config, ModuleKind::Allocator => &self.allocator_module_config, } } @@ -474,8 +470,6 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>( backend: B, tcx: TyCtxt<'_>, target_cpu: String, - metadata: EncodedMetadata, - metadata_module: Option<CompiledModule>, ) -> OngoingCodegen<B> { let (coordinator_send, coordinator_receive) = channel(); @@ -485,7 +479,6 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>( let crate_info = CrateInfo::new(tcx, target_cpu); let regular_config = ModuleConfig::new(ModuleKind::Regular, tcx, no_builtins); - let metadata_config = ModuleConfig::new(ModuleKind::Metadata, tcx, no_builtins); let allocator_config = ModuleConfig::new(ModuleKind::Allocator, tcx, no_builtins); let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); @@ -499,15 +492,12 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>( codegen_worker_send, coordinator_receive, Arc::new(regular_config), - Arc::new(metadata_config), Arc::new(allocator_config), coordinator_send.clone(), ); OngoingCodegen { backend, - metadata, - metadata_module, crate_info, codegen_worker_receive, @@ -843,12 +833,6 @@ pub(crate) fn compute_per_cgu_lto_type( sess_crate_types: &[CrateType], module_kind: ModuleKind, ) -> ComputedLtoType { - // Metadata modules never participate in LTO regardless of the lto - // settings. - if module_kind == ModuleKind::Metadata { - return ComputedLtoType::No; - } - // If the linker does LTO, we don't have to do it. Note that we // keep doing full LTO, if it is requested, as not to break the // assumption that the output will be a single module. @@ -1029,10 +1013,7 @@ fn finish_intra_module_work<B: ExtraBackendMethods>( let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); - if !cgcx.opts.unstable_opts.combine_cgu - || module.kind == ModuleKind::Metadata - || module.kind == ModuleKind::Allocator - { + if !cgcx.opts.unstable_opts.combine_cgu || module.kind == ModuleKind::Allocator { let module = B::codegen(cgcx, dcx, module, module_config)?; Ok(WorkItemResult::Finished(module)) } else { @@ -1123,7 +1104,6 @@ fn start_executing_work<B: ExtraBackendMethods>( codegen_worker_send: Sender<CguMessage>, coordinator_receive: Receiver<Box<dyn Any + Send>>, regular_config: Arc<ModuleConfig>, - metadata_config: Arc<ModuleConfig>, allocator_config: Arc<ModuleConfig>, tx_to_llvm_workers: Sender<Box<dyn Any + Send>>, ) -> thread::JoinHandle<Result<CompiledModules, ()>> { @@ -1216,7 +1196,6 @@ fn start_executing_work<B: ExtraBackendMethods>( diag_emitter: shared_emitter.clone(), output_filenames: Arc::clone(tcx.output_filenames(())), regular_module_config: regular_config, - metadata_module_config: metadata_config, allocator_module_config: allocator_config, tm_factory: backend.target_machine_factory(tcx.sess, ol, backend_features), msvc_imps_needed: msvc_imps_needed(tcx), @@ -1673,7 +1652,6 @@ fn start_executing_work<B: ExtraBackendMethods>( assert!(compiled_allocator_module.is_none()); compiled_allocator_module = Some(compiled_module); } - ModuleKind::Metadata => bug!("Should be handled separately"), } } Ok(WorkItemResult::NeedsLink(module)) => { @@ -2055,8 +2033,6 @@ impl<B: ExtraBackendMethods> Drop for Coordinator<B> { pub struct OngoingCodegen<B: ExtraBackendMethods> { pub backend: B, - pub metadata: EncodedMetadata, - pub metadata_module: Option<CompiledModule>, pub crate_info: CrateInfo, pub codegen_worker_receive: Receiver<CguMessage>, pub shared_emitter_main: SharedEmitterMain, @@ -2096,12 +2072,10 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> { ( CodegenResults { - metadata: self.metadata, crate_info: self.crate_info, modules: compiled_modules.modules, allocator_module: compiled_modules.allocator_module, - metadata_module: self.metadata_module, }, work_products, ) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index c2d6a26de0f..a3d6c73ba85 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -15,11 +15,10 @@ use rustc_data_structures::unord::UnordMap; use rustc_hir::ItemId; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; -use rustc_metadata::EncodedMetadata; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; -use rustc_middle::middle::exported_symbols::SymbolExportKind; -use rustc_middle::middle::{exported_symbols, lang_items}; +use rustc_middle::middle::exported_symbols::{self, SymbolExportKind}; +use rustc_middle::middle::lang_items; use rustc_middle::mir::BinOp; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem, MonoItemPartitions}; @@ -28,7 +27,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::Session; -use rustc_session::config::{self, CrateType, EntryFnType, OutputType}; +use rustc_session::config::{self, CrateType, EntryFnType}; use rustc_span::{DUMMY_SP, Symbol, sym}; use rustc_symbol_mangling::mangle_internal_symbol; use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt}; @@ -37,7 +36,6 @@ use tracing::{debug, info}; use crate::assert_module_sources::CguReuse; use crate::back::link::are_upstream_rust_objects_already_included; -use crate::back::metadata::create_compressed_metadata_file; use crate::back::write::{ ComputedLtoType, OngoingCodegen, compute_per_cgu_lto_type, start_async_codegen, submit_codegened_module_to_llvm, submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, @@ -48,8 +46,7 @@ use crate::mir::operand::OperandValue; use crate::mir::place::PlaceRef; use crate::traits::*; use crate::{ - CachedModuleCodegen, CodegenLintLevels, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, - errors, meth, mir, + CachedModuleCodegen, CodegenLintLevels, CrateInfo, ModuleCodegen, ModuleKind, errors, meth, mir, }; pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate { @@ -669,12 +666,10 @@ pub fn codegen_crate<B: ExtraBackendMethods>( backend: B, tcx: TyCtxt<'_>, target_cpu: String, - metadata: EncodedMetadata, - need_metadata_module: bool, ) -> OngoingCodegen<B> { // Skip crate items and just output metadata in -Z no-codegen mode. if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { - let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, metadata, None); + let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu); ongoing_codegen.codegen_finished(tcx); @@ -707,39 +702,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( } } - let metadata_module = need_metadata_module.then(|| { - // Emit compressed metadata object. - let metadata_cgu_name = - cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata")).to_string(); - tcx.sess.time("write_compressed_metadata", || { - let file_name = tcx.output_filenames(()).temp_path_for_cgu( - OutputType::Metadata, - &metadata_cgu_name, - tcx.sess.invocation_temp.as_deref(), - ); - let data = create_compressed_metadata_file( - tcx.sess, - &metadata, - &exported_symbols::metadata_symbol_name(tcx), - ); - if let Err(error) = std::fs::write(&file_name, data) { - tcx.dcx().emit_fatal(errors::MetadataObjectFileWrite { error }); - } - CompiledModule { - name: metadata_cgu_name, - kind: ModuleKind::Metadata, - object: Some(file_name), - dwarf_object: None, - bytecode: None, - assembly: None, - llvm_ir: None, - links_from_incr_cache: Vec::new(), - } - }) - }); - - let ongoing_codegen = - start_async_codegen(backend.clone(), tcx, target_cpu, metadata, metadata_module); + let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu); // Codegen an allocator shim, if necessary. if let Some(kind) = allocator_kind_for_codegen(tcx) { @@ -1010,6 +973,7 @@ impl CrateInfo { windows_subsystem, natvis_debugger_visualizers: Default::default(), lint_levels: CodegenLintLevels::from_tcx(tcx), + metadata_symbol: exported_symbols::metadata_symbol_name(tcx), }; info.native_libraries.reserve(n_crates); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 0b31fa8fa88..188a9a98ce7 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -4,7 +4,9 @@ use rustc_abi::ExternAbi; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr}; use rustc_attr_data_structures::ReprAttr::ReprAlign; -use rustc_attr_data_structures::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_attr_data_structures::{ + AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, find_attr, +}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; @@ -21,7 +23,6 @@ use rustc_session::parse::feature_err; use rustc_session::{Session, lint}; use rustc_span::{Ident, Span, sym}; use rustc_target::spec::SanitizerSet; -use tracing::debug; use crate::errors; use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr}; @@ -83,7 +84,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let rust_target_features = tcx.rust_target_features(LOCAL_CRATE); - 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(); @@ -449,48 +449,14 @@ 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; - } - - if attr.is_word() { - return InlineAttr::Hint; - } - let Some(ref items) = attr.meta_item_list() else { - return ia; - }; - inline_span = Some(attr.span()); - - let [item] = &items[..] else { - tcx.dcx().emit_err(errors::ExpectedOneArgument { span: attr.span() }); - return InlineAttr::None; - }; - - if item.has_name(sym::always) { - InlineAttr::Always - } else if item.has_name(sym::never) { - InlineAttr::Never - } else { - tcx.dcx().emit_err(errors::InvalidArgument { span: items[0].span() }); - - InlineAttr::None - } - }); - codegen_fn_attrs.inline = attrs.iter().fold(codegen_fn_attrs.inline, |ia, attr| { - if !attr.has_name(sym::rustc_force_inline) || !tcx.features().rustc_attrs() { - return ia; - } - - if attr.is_word() { - InlineAttr::Force { attr_span: attr.span(), reason: None } - } else if let Some(val) = attr.value_str() { - InlineAttr::Force { attr_span: attr.span(), reason: Some(val) } - } else { - debug!("`rustc_force_inline` not checked by attribute validation"); - ia - } - }); + let inline_span; + (codegen_fn_attrs.inline, inline_span) = if let Some((inline_attr, span)) = + find_attr!(attrs, AttributeKind::Inline(i, span) => (*i, *span)) + { + (inline_attr, Some(span)) + } else { + (InlineAttr::None, None) + }; // naked function MUST NOT be inlined! This attribute is required for the rust compiler itself, // but not for the code generation backend because at that point the naked function will just be @@ -511,7 +477,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { return OptimizeAttr::Default; }; - inline_span = Some(attr.span()); let [item] = &items[..] else { tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() }); return OptimizeAttr::Default; diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index f843347db92..5387b2a7f81 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -209,13 +209,6 @@ pub(crate) struct OutOfRangeInteger { } #[derive(Diagnostic)] -#[diag(codegen_ssa_expected_one_argument, code = E0534)] -pub(crate) struct ExpectedOneArgument { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(codegen_ssa_expected_one_argument, code = E0722)] pub(crate) struct ExpectedOneArgumentOptimize { #[primary_span] @@ -223,14 +216,6 @@ pub(crate) struct ExpectedOneArgumentOptimize { } #[derive(Diagnostic)] -#[diag(codegen_ssa_invalid_argument, code = E0535)] -#[help] -pub(crate) struct InvalidArgument { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(codegen_ssa_invalid_argument, code = E0722)] pub(crate) struct InvalidArgumentOptimize { #[primary_span] @@ -778,12 +763,6 @@ pub(crate) struct MultipleMainFunctions { } #[derive(Diagnostic)] -#[diag(codegen_ssa_metadata_object_file_write)] -pub(crate) struct MetadataObjectFileWrite { - pub error: Error, -} - -#[derive(Diagnostic)] #[diag(codegen_ssa_invalid_windows_subsystem)] pub(crate) struct InvalidWindowsSubsystem { pub subsystem: Symbol, diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 84919645cf0..523c9f2ad1c 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -31,6 +31,7 @@ use rustc_data_structures::unord::UnordMap; use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::CrateNum; use rustc_macros::{Decodable, Encodable, HashStable}; +use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::WorkProduct; use rustc_middle::lint::LevelAndSource; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; @@ -169,7 +170,6 @@ pub(crate) struct CachedModuleCodegen { #[derive(Copy, Clone, Debug, PartialEq, Encodable, Decodable)] pub enum ModuleKind { Regular, - Metadata, Allocator, } @@ -233,6 +233,7 @@ pub struct CrateInfo { pub windows_subsystem: Option<String>, pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>, pub lint_levels: CodegenLintLevels, + pub metadata_symbol: String, } /// Target-specific options that get set in `cfg(...)`. @@ -257,8 +258,6 @@ pub struct TargetConfig { pub struct CodegenResults { pub modules: Vec<CompiledModule>, pub allocator_module: Option<CompiledModule>, - pub metadata_module: Option<CompiledModule>, - pub metadata: rustc_metadata::EncodedMetadata, pub crate_info: CrateInfo, } @@ -303,6 +302,7 @@ impl CodegenResults { sess: &Session, rlink_file: &Path, codegen_results: &CodegenResults, + metadata: &EncodedMetadata, outputs: &OutputFilenames, ) -> Result<usize, io::Error> { let mut encoder = FileEncoder::new(rlink_file)?; @@ -312,6 +312,7 @@ impl CodegenResults { encoder.emit_raw_bytes(&RLINK_VERSION.to_be_bytes()); encoder.emit_str(sess.cfg_version); Encodable::encode(codegen_results, &mut encoder); + Encodable::encode(metadata, &mut encoder); Encodable::encode(outputs, &mut encoder); encoder.finish().map_err(|(_path, err)| err) } @@ -319,7 +320,7 @@ impl CodegenResults { pub fn deserialize_rlink( sess: &Session, data: Vec<u8>, - ) -> Result<(Self, OutputFilenames), CodegenErrors> { + ) -> Result<(Self, EncodedMetadata, OutputFilenames), CodegenErrors> { // The Decodable machinery is not used here because it panics if the input data is invalid // and because its internal representation may change. if !data.starts_with(RLINK_MAGIC) { @@ -350,8 +351,9 @@ impl CodegenResults { } let codegen_results = CodegenResults::decode(&mut decoder); + let metadata = EncodedMetadata::decode(&mut decoder); let outputs = OutputFilenames::decode(&mut decoder); - Ok((codegen_results, outputs)) + Ok((codegen_results, metadata, outputs)) } } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 43b87171d51..3df97429e09 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1,6 +1,6 @@ use std::cmp; -use rustc_abi::{BackendRepr, ExternAbi, HasDataLayout, Reg, Size, WrappingRange}; +use rustc_abi::{Align, BackendRepr, ExternAbi, HasDataLayout, Reg, Size, WrappingRange}; use rustc_ast as ast; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_data_structures::packed::Pu128; @@ -13,7 +13,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; +use rustc_target::callconv::{ArgAbi, CastTarget, FnAbi, PassMode}; use tracing::{debug, info}; use super::operand::OperandRef; @@ -558,8 +558,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } ZeroSized => bug!("ZST return value shouldn't be in PassMode::Cast"), }; - let ty = bx.cast_backend_type(cast_ty); - bx.load(ty, llslot, self.fn_abi.ret.layout.align.abi) + load_cast(bx, cast_ty, llslot, self.fn_abi.ret.layout.align.abi) } }; bx.ret(llval); @@ -1618,8 +1617,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { MemFlags::empty(), ); // ...and then load it with the ABI type. - let cast_ty = bx.cast_backend_type(cast); - llval = bx.load(cast_ty, llscratch, scratch_align); + llval = load_cast(bx, cast, llscratch, scratch_align); bx.lifetime_end(llscratch, scratch_size); } else { // We can't use `PlaceRef::load` here because the argument @@ -1969,3 +1967,47 @@ enum ReturnDest<'tcx, V> { /// Store a direct return value to an operand local place. DirectOperand(mir::Local), } + +fn load_cast<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + cast: &CastTarget, + ptr: Bx::Value, + align: Align, +) -> Bx::Value { + let cast_ty = bx.cast_backend_type(cast); + if let Some(offset_from_start) = cast.rest_offset { + assert!(cast.prefix[1..].iter().all(|p| p.is_none())); + assert_eq!(cast.rest.unit.size, cast.rest.total); + let first_ty = bx.reg_backend_type(&cast.prefix[0].unwrap()); + let second_ty = bx.reg_backend_type(&cast.rest.unit); + let first = bx.load(first_ty, ptr, align); + let second_ptr = bx.inbounds_ptradd(ptr, bx.const_usize(offset_from_start.bytes())); + let second = bx.load(second_ty, second_ptr, align.restrict_for_offset(offset_from_start)); + let res = bx.cx().const_poison(cast_ty); + let res = bx.insert_value(res, first, 0); + bx.insert_value(res, second, 1) + } else { + bx.load(cast_ty, ptr, align) + } +} + +pub fn store_cast<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + cast: &CastTarget, + value: Bx::Value, + ptr: Bx::Value, + align: Align, +) { + if let Some(offset_from_start) = cast.rest_offset { + assert!(cast.prefix[1..].iter().all(|p| p.is_none())); + assert_eq!(cast.rest.unit.size, cast.rest.total); + assert!(cast.prefix[0].is_some()); + let first = bx.extract_value(value, 0); + let second = bx.extract_value(value, 1); + bx.store(first, ptr, align); + let second_ptr = bx.inbounds_ptradd(ptr, bx.const_usize(offset_from_start.bytes())); + bx.store(second, second_ptr, align.restrict_for_offset(offset_from_start)); + } else { + bx.store(value, ptr, align); + }; +} diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index e217c09939e..27fcab8ed2d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -118,7 +118,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (llsize, _) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta); llsize } - sym::min_align_of_val => { + sym::align_of_val => { let tp_ty = fn_args.type_at(0); let (_, meta) = args[0].val.pointer_parts(); let (_, llalign) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta); diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 96a04473aba..66c4af4c935 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -26,6 +26,7 @@ pub mod place; mod rvalue; mod statement; +pub use self::block::store_cast; use self::debuginfo::{FunctionDebugContext, PerLocalVarDebugInfo}; use self::operand::{OperandRef, OperandValue}; use self::place::PlaceRef; @@ -259,7 +260,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } PassMode::Cast { ref cast, .. } => { debug!("alloc: {:?} (return place) -> place", local); - let size = cast.size(&start_bx); + let size = cast.size(&start_bx).max(layout.size); return LocalRef::Place(PlaceRef::alloca_size(&mut start_bx, size, layout)); } _ => {} diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 46fb9a89513..b805dc094e9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,14 +1,13 @@ use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind}; use rustc_attr_data_structures::InstructionSetAttr; -use rustc_hir::def_id::DefId; use rustc_middle::mir::mono::{Linkage, MonoItemData, Visibility}; use rustc_middle::mir::{InlineAsmOperand, START_BLOCK}; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; use rustc_middle::ty::{Instance, Ty, TyCtxt, TypeVisitableExt}; -use rustc_middle::{bug, span_bug, ty}; +use rustc_middle::{bug, ty}; use rustc_span::sym; use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; -use rustc_target::spec::{BinaryFormat, WasmCAbi}; +use rustc_target::spec::BinaryFormat; use crate::common; use crate::mir::AsmCodegenMethods; @@ -287,12 +286,7 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, "{}", arch_prefix).unwrap(); } writeln!(begin, "{asm_name}:").unwrap(); - writeln!( - begin, - ".functype {asm_name} {}", - wasm_functype(tcx, fn_abi, instance.def_id()) - ) - .unwrap(); + writeln!(begin, ".functype {asm_name} {}", wasm_functype(tcx, fn_abi)).unwrap(); writeln!(end).unwrap(); // .size is ignored for function symbols, so we can skip it @@ -333,7 +327,7 @@ fn prefix_and_suffix<'tcx>( /// The webassembly type signature for the given function. /// /// Used by the `.functype` directive on wasm targets. -fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, def_id: DefId) -> String { +fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String { let mut signature = String::with_capacity(64); let ptr_type = match tcx.data_layout.pointer_size.bits() { @@ -342,17 +336,6 @@ fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, def_id other => bug!("wasm pointer size cannot be {other} bits"), }; - // FIXME: remove this once the wasm32-unknown-unknown ABI is fixed - // please also add `wasm32-unknown-unknown` back in `tests/assembly/wasm32-naked-fn.rs` - // basically the commit introducing this comment should be reverted - if let PassMode::Pair { .. } = fn_abi.ret.mode { - let _ = WasmCAbi::Legacy { with_lint: true }; - span_bug!( - tcx.def_span(def_id), - "cannot return a pair (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666" - ); - } - let hidden_return = matches!(fn_abi.ret.mode, PassMode::Indirect { .. }); signature.push('('); @@ -366,7 +349,7 @@ fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, def_id let mut it = fn_abi.args.iter().peekable(); while let Some(arg_abi) = it.next() { - wasm_type(tcx, &mut signature, arg_abi, ptr_type, def_id); + wasm_type(&mut signature, arg_abi, ptr_type); if it.peek().is_some() { signature.push_str(", "); } @@ -375,7 +358,7 @@ fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, def_id signature.push_str(") -> ("); if !hidden_return { - wasm_type(tcx, &mut signature, &fn_abi.ret, ptr_type, def_id); + wasm_type(&mut signature, &fn_abi.ret, ptr_type); } signature.push(')'); @@ -383,27 +366,13 @@ fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, def_id signature } -fn wasm_type<'tcx>( - tcx: TyCtxt<'tcx>, - signature: &mut String, - arg_abi: &ArgAbi<'_, Ty<'tcx>>, - ptr_type: &'static str, - def_id: DefId, -) { +fn wasm_type<'tcx>(signature: &mut String, arg_abi: &ArgAbi<'_, Ty<'tcx>>, ptr_type: &'static str) { match arg_abi.mode { PassMode::Ignore => { /* do nothing */ } PassMode::Direct(_) => { let direct_type = match arg_abi.layout.backend_repr { BackendRepr::Scalar(scalar) => wasm_primitive(scalar.primitive(), ptr_type), BackendRepr::SimdVector { .. } => "v128", - BackendRepr::Memory { .. } => { - // FIXME: remove this branch once the wasm32-unknown-unknown ABI is fixed - let _ = WasmCAbi::Legacy { with_lint: true }; - span_bug!( - tcx.def_span(def_id), - "cannot use memory args (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666" - ); - } other => unreachable!("unexpected BackendRepr: {:?}", other), }; diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 6bb3150c1c5..640d197c219 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -8,6 +8,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; +use rustc_session::features::StabilityExt; use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; @@ -66,7 +67,7 @@ pub(crate) fn from_target_feature_attr( // Only allow target features whose feature gates have been enabled // and which are permitted to be toggled. - if let Err(reason) = stability.toggle_allowed() { + if let Err(reason) = stability.is_toggle_permitted(tcx.sess) { tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { span: item.span(), feature, diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 95bf3b16685..29ec7eb1da3 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -74,12 +74,7 @@ pub trait CodegenBackend { fn provide(&self, _providers: &mut Providers) {} - fn codegen_crate<'tcx>( - &self, - tcx: TyCtxt<'tcx>, - metadata: EncodedMetadata, - need_metadata_module: bool, - ) -> Box<dyn Any>; + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box<dyn Any>; /// This is called on the returned `Box<dyn Any>` from [`codegen_crate`](Self::codegen_crate) /// @@ -94,8 +89,14 @@ pub trait CodegenBackend { ) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>); /// 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); + fn link( + &self, + sess: &Session, + codegen_results: CodegenResults, + metadata: EncodedMetadata, + outputs: &OutputFilenames, + ) { + link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, metadata, outputs); } } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 7f78bc75695..f35f551d590 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -23,7 +23,7 @@ use crate::common::{AtomicRmwBinOp, IntPredicate, RealPredicate, Synchronization use crate::mir::operand::{OperandRef, OperandValue}; use crate::mir::place::{PlaceRef, PlaceValue}; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum OverflowOp { Add, Sub, @@ -215,7 +215,7 @@ pub trait BuilderMethods<'a, 'tcx>: fn checked_binop( &mut self, oop: OverflowOp, - ty: Ty<'_>, + ty: Ty<'tcx>, lhs: Self::Value, rhs: Self::Value, ) -> (Self::Value, Self::Value); diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index ab27182c211..96c39c7bb32 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -120,7 +120,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.copy_op(&val, dest)?; } - sym::min_align_of_val | sym::size_of_val => { + sym::align_of_val | sym::size_of_val => { // Avoid `deref_pointer` -- this is not a deref, the ptr does not have to be // dereferenceable! let place = self.ref_to_mplace(&self.read_immediate(&args[0])?)?; @@ -129,7 +129,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .ok_or_else(|| err_unsup_format!("`extern type` does not have known layout"))?; let result = match intrinsic_name { - sym::min_align_of_val => align.bytes(), + sym::align_of_val => align.bytes(), sym::size_of_val => size.bytes(), _ => bug!(), }; @@ -139,13 +139,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::needs_drop | sym::type_id | sym::type_name | sym::variant_count => { let gid = GlobalId { instance, promoted: None }; - let ty = match intrinsic_name { - sym::variant_count => self.tcx.types.usize, - sym::needs_drop => self.tcx.types.bool, - sym::type_id => self.tcx.types.u128, - sym::type_name => Ty::new_static_str(self.tcx.tcx), - _ => bug!(), - }; + let ty = self + .tcx + .fn_sig(instance.def_id()) + .instantiate(self.tcx.tcx, instance.args) + .output() + .no_bound_vars() + .unwrap(); let val = self .ctfe_query(|tcx| tcx.const_eval_global_id(self.typing_env, gid, tcx.span))?; let val = self.const_val_to_op(val, ty, Some(dest.layout))?; diff --git a/compiler/rustc_data_structures/src/aligned.rs b/compiler/rustc_data_structures/src/aligned.rs index a636d09fcae..111740e5509 100644 --- a/compiler/rustc_data_structures/src/aligned.rs +++ b/compiler/rustc_data_structures/src/aligned.rs @@ -1,5 +1,7 @@ use std::ptr::Alignment; +use rustc_serialize::PointeeSized; + /// Returns the ABI-required minimum alignment of a type in bytes. /// /// This is equivalent to [`align_of`], but also works for some unsized @@ -17,7 +19,7 @@ pub const fn align_of<T: ?Sized + Aligned>() -> Alignment { /// example `[T]` has alignment of `T`. /// /// [`align_of::<Self>()`]: align_of -pub unsafe trait Aligned { +pub unsafe trait Aligned: PointeeSized { /// Alignment of `Self`. const ALIGN: Alignment; } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index eb3817a80a7..0431182e9e2 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -12,6 +12,7 @@ #![allow(rustc::potential_query_instability)] #![cfg_attr(bootstrap, feature(cfg_match))] #![cfg_attr(not(bootstrap), feature(cfg_select))] +#![cfg_attr(not(bootstrap), feature(sized_hierarchy))] #![deny(unsafe_op_in_unsafe_fn)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] @@ -43,6 +44,9 @@ use std::fmt; pub use atomic_ref::AtomicRef; pub use ena::{snapshot_vec, undo_log, unify}; pub use rustc_index::static_assert_size; +// re-exported for `rustc_smir` +// FIXME(sized_hierarchy): remove with `cfg(bootstrap)`, see `rustc_serialize/src/lib.rs` +pub use rustc_serialize::PointeeSized; pub mod aligned; pub mod base_n; diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index e0df1b232e1..4846bc997f1 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -1,5 +1,7 @@ use std::alloc::Allocator; +use rustc_serialize::PointeeSized; + #[diagnostic::on_unimplemented(message = "`{Self}` doesn't implement `DynSend`. \ Add it to `rustc_data_structures::marker` or use `IntoDynSyncSend` if it's already `Send`")] // This is an auto trait for types which can be sent across threads if `sync::is_dyn_thread_safe()` @@ -15,7 +17,7 @@ pub unsafe auto trait DynSend {} pub unsafe auto trait DynSync {} // Same with `Sync` and `Send`. -unsafe impl<T: DynSync + ?Sized> DynSend for &T {} +unsafe impl<T: DynSync + ?Sized + PointeeSized> DynSend for &T {} macro_rules! impls_dyn_send_neg { ($([$t1: ty $(where $($generics1: tt)*)?])*) => { @@ -27,9 +29,9 @@ macro_rules! impls_dyn_send_neg { impls_dyn_send_neg!( [std::env::Args] [std::env::ArgsOs] - [*const T where T: ?Sized] - [*mut T where T: ?Sized] - [std::ptr::NonNull<T> where T: ?Sized] + [*const T where T: ?Sized + PointeeSized] + [*mut T where T: ?Sized + PointeeSized] + [std::ptr::NonNull<T> where T: ?Sized + PointeeSized] [std::rc::Rc<T, A> where T: ?Sized, A: Allocator] [std::rc::Weak<T, A> where T: ?Sized, A: Allocator] [std::sync::MutexGuard<'_, T> where T: ?Sized] @@ -100,12 +102,12 @@ macro_rules! impls_dyn_sync_neg { impls_dyn_sync_neg!( [std::env::Args] [std::env::ArgsOs] - [*const T where T: ?Sized] - [*mut T where T: ?Sized] + [*const T where T: ?Sized + PointeeSized] + [*mut T where T: ?Sized + PointeeSized] [std::cell::Cell<T> where T: ?Sized] [std::cell::RefCell<T> where T: ?Sized] [std::cell::UnsafeCell<T> where T: ?Sized] - [std::ptr::NonNull<T> where T: ?Sized] + [std::ptr::NonNull<T> where T: ?Sized + PointeeSized] [std::rc::Rc<T, A> where T: ?Sized, A: Allocator] [std::rc::Weak<T, A> where T: ?Sized, A: Allocator] [std::cell::OnceCell<T> where T] @@ -175,10 +177,10 @@ impl_dyn_sync!( [thin_vec::ThinVec<T> where T: DynSync] ); -pub fn assert_dyn_sync<T: ?Sized + DynSync>() {} -pub fn assert_dyn_send<T: ?Sized + DynSend>() {} -pub fn assert_dyn_send_val<T: ?Sized + DynSend>(_t: &T) {} -pub fn assert_dyn_send_sync_val<T: ?Sized + DynSync + DynSend>(_t: &T) {} +pub fn assert_dyn_sync<T: ?Sized + PointeeSized + DynSync>() {} +pub fn assert_dyn_send<T: ?Sized + PointeeSized + DynSend>() {} +pub fn assert_dyn_send_val<T: ?Sized + PointeeSized + DynSend>(_t: &T) {} +pub fn assert_dyn_send_sync_val<T: ?Sized + PointeeSized + DynSync + DynSend>(_t: &T) {} #[derive(Copy, Clone)] pub struct FromDyn<T>(T); @@ -231,10 +233,10 @@ impl<T> std::ops::DerefMut for FromDyn<T> { // an instance of `DynSend` and `DynSync`, since the compiler cannot infer // it automatically in some cases. (e.g. Box<dyn Send / Sync>) #[derive(Copy, Clone)] -pub struct IntoDynSyncSend<T: ?Sized>(pub T); +pub struct IntoDynSyncSend<T: ?Sized + PointeeSized>(pub T); -unsafe impl<T: ?Sized + Send> DynSend for IntoDynSyncSend<T> {} -unsafe impl<T: ?Sized + Sync> DynSync for IntoDynSyncSend<T> {} +unsafe impl<T: ?Sized + PointeeSized + Send> DynSend for IntoDynSyncSend<T> {} +unsafe impl<T: ?Sized + PointeeSized + Sync> DynSync for IntoDynSyncSend<T> {} impl<T> std::ops::Deref for IntoDynSyncSend<T> { type Target = T; diff --git a/compiler/rustc_data_structures/src/thousands/mod.rs b/compiler/rustc_data_structures/src/thousands/mod.rs index e7ab7ec2932..b251ebe58f6 100644 --- a/compiler/rustc_data_structures/src/thousands/mod.rs +++ b/compiler/rustc_data_structures/src/thousands/mod.rs @@ -1,16 +1,37 @@ -//! This is an extremely bare-bones alternative to the `thousands` crate on -//! crates.io, for printing large numbers in a readable fashion. +//! This is a bare-bones alternative to the `thousands` crate on crates.io, for +//! printing large numbers in a readable fashion. #[cfg(test)] mod tests; -// Converts the number to a string, with underscores as the thousands separator. -pub fn format_with_underscores(n: usize) -> String { - let mut s = n.to_string(); - let mut i = s.len(); - while i > 3 { +fn format_with_underscores(mut s: String) -> String { + // Ignore a leading '-'. + let start = if s.starts_with('-') { 1 } else { 0 }; + + // Stop after the first non-digit, e.g. '.' or 'e' for floats. + let non_digit = s[start..].find(|c: char| !c.is_digit(10)); + let end = if let Some(non_digit) = non_digit { start + non_digit } else { s.len() }; + + // Insert underscores within `start..end`. + let mut i = end; + while i > start + 3 { i -= 3; s.insert(i, '_'); } s } + +/// Print a `usize` with underscore separators. +pub fn usize_with_underscores(n: usize) -> String { + format_with_underscores(format!("{n}")) +} + +/// Print an `isize` with underscore separators. +pub fn isize_with_underscores(n: isize) -> String { + format_with_underscores(format!("{n}")) +} + +/// Print an `f64` with precision 1 (one decimal place) and underscore separators. +pub fn f64p1_with_underscores(n: f64) -> String { + format_with_underscores(format!("{n:.1}")) +} diff --git a/compiler/rustc_data_structures/src/thousands/tests.rs b/compiler/rustc_data_structures/src/thousands/tests.rs index 906605d9a93..0f9a648802b 100644 --- a/compiler/rustc_data_structures/src/thousands/tests.rs +++ b/compiler/rustc_data_structures/src/thousands/tests.rs @@ -2,13 +2,51 @@ use super::*; #[test] fn test_format_with_underscores() { - assert_eq!("0", format_with_underscores(0)); - assert_eq!("1", format_with_underscores(1)); - assert_eq!("99", format_with_underscores(99)); - assert_eq!("345", format_with_underscores(345)); - assert_eq!("1_000", format_with_underscores(1_000)); - assert_eq!("12_001", format_with_underscores(12_001)); - assert_eq!("999_999", format_with_underscores(999_999)); - assert_eq!("1_000_000", format_with_underscores(1_000_000)); - assert_eq!("12_345_678", format_with_underscores(12_345_678)); + assert_eq!("", format_with_underscores("".to_string())); + assert_eq!("0", format_with_underscores("0".to_string())); + assert_eq!("12_345.67e14", format_with_underscores("12345.67e14".to_string())); + assert_eq!("-1_234.5678e10", format_with_underscores("-1234.5678e10".to_string())); + assert_eq!("------", format_with_underscores("------".to_string())); + assert_eq!("abcdefgh", format_with_underscores("abcdefgh".to_string())); + assert_eq!("-1b", format_with_underscores("-1b".to_string())); + assert_eq!("-3_456xyz", format_with_underscores("-3456xyz".to_string())); +} + +#[test] +fn test_usize_with_underscores() { + assert_eq!("0", usize_with_underscores(0)); + assert_eq!("1", usize_with_underscores(1)); + assert_eq!("99", usize_with_underscores(99)); + assert_eq!("345", usize_with_underscores(345)); + assert_eq!("1_000", usize_with_underscores(1_000)); + assert_eq!("12_001", usize_with_underscores(12_001)); + assert_eq!("999_999", usize_with_underscores(999_999)); + assert_eq!("1_000_000", usize_with_underscores(1_000_000)); + assert_eq!("12_345_678", usize_with_underscores(12_345_678)); +} + +#[test] +fn test_isize_with_underscores() { + assert_eq!("0", isize_with_underscores(0)); + assert_eq!("-1", isize_with_underscores(-1)); + assert_eq!("99", isize_with_underscores(99)); + assert_eq!("345", isize_with_underscores(345)); + assert_eq!("-1_000", isize_with_underscores(-1_000)); + assert_eq!("12_001", isize_with_underscores(12_001)); + assert_eq!("-999_999", isize_with_underscores(-999_999)); + assert_eq!("1_000_000", isize_with_underscores(1_000_000)); + assert_eq!("-12_345_678", isize_with_underscores(-12_345_678)); +} + +#[test] +fn test_f64p1_with_underscores() { + assert_eq!("0.0", f64p1_with_underscores(0f64)); + assert_eq!("0.0", f64p1_with_underscores(0.00000001)); + assert_eq!("-0.0", f64p1_with_underscores(-0.00000001)); + assert_eq!("1.0", f64p1_with_underscores(0.9999999)); + assert_eq!("-1.0", f64p1_with_underscores(-0.9999999)); + assert_eq!("345.5", f64p1_with_underscores(345.4999999)); + assert_eq!("-100_000.0", f64p1_with_underscores(-100_000f64)); + assert_eq!("123_456_789.1", f64p1_with_underscores(123456789.123456789)); + assert_eq!("-123_456_789.1", f64p1_with_underscores(-123456789.123456789)); } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 54a331a4904..d53126d0414 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -557,27 +557,34 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { let rlink_data = fs::read(file).unwrap_or_else(|err| { dcx.emit_fatal(RlinkUnableToRead { err }); }); - let (codegen_results, outputs) = match CodegenResults::deserialize_rlink(sess, rlink_data) { - Ok((codegen, outputs)) => (codegen, outputs), - Err(err) => { - match err { - CodegenErrors::WrongFileType => dcx.emit_fatal(RLinkWrongFileType), - CodegenErrors::EmptyVersionNumber => dcx.emit_fatal(RLinkEmptyVersionNumber), - CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => dcx - .emit_fatal(RLinkEncodingVersionMismatch { version_array, rlink_version }), - CodegenErrors::RustcVersionMismatch { rustc_version } => { - dcx.emit_fatal(RLinkRustcVersionMismatch { - rustc_version, - current_version: sess.cfg_version, - }) - } - CodegenErrors::CorruptFile => { - dcx.emit_fatal(RlinkCorruptFile { file }); - } - }; - } - }; - compiler.codegen_backend.link(sess, codegen_results, &outputs); + let (codegen_results, metadata, outputs) = + match CodegenResults::deserialize_rlink(sess, rlink_data) { + Ok((codegen, metadata, outputs)) => (codegen, metadata, outputs), + Err(err) => { + match err { + CodegenErrors::WrongFileType => dcx.emit_fatal(RLinkWrongFileType), + CodegenErrors::EmptyVersionNumber => { + dcx.emit_fatal(RLinkEmptyVersionNumber) + } + CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => { + dcx.emit_fatal(RLinkEncodingVersionMismatch { + version_array, + rlink_version, + }) + } + CodegenErrors::RustcVersionMismatch { rustc_version } => { + dcx.emit_fatal(RLinkRustcVersionMismatch { + rustc_version, + current_version: sess.cfg_version, + }) + } + CodegenErrors::CorruptFile => { + dcx.emit_fatal(RlinkCorruptFile { file }); + } + }; + } + }; + compiler.codegen_backend.link(sess, codegen_results, metadata, &outputs); } else { dcx.emit_fatal(RlinkNotAFile {}); } @@ -1500,13 +1507,31 @@ pub fn init_rustc_env_logger(early_dcx: &EarlyDiagCtxt) { /// This allows tools to enable rust logging without having to magically match rustc's /// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose -/// the values directly rather than having to set an environment variable. +/// the logger config directly rather than having to set an environment variable. pub fn init_logger(early_dcx: &EarlyDiagCtxt, cfg: rustc_log::LoggerConfig) { if let Err(error) = rustc_log::init_logger(cfg) { early_dcx.early_fatal(error.to_string()); } } +/// This allows tools to enable rust logging without having to magically match rustc's +/// tracing crate version. In contrast to `init_rustc_env_logger`, it allows you to +/// choose the logger config directly rather than having to set an environment variable. +/// Moreover, in contrast to `init_logger`, it allows you to add a custom tracing layer +/// via `build_subscriber`, for example `|| Registry::default().with(custom_layer)`. +pub fn init_logger_with_additional_layer<F, T>( + early_dcx: &EarlyDiagCtxt, + cfg: rustc_log::LoggerConfig, + build_subscriber: F, +) where + F: FnOnce() -> T, + T: rustc_log::BuildSubscriberRet, +{ + if let Err(error) = rustc_log::init_logger_with_additional_layer(cfg, build_subscriber) { + early_dcx.early_fatal(error.to_string()); + } +} + /// Install our usual `ctrlc` handler, which sets [`rustc_const_eval::CTRL_C_RECEIVED`]. /// Making this handler optional lets tools can install a different handler, if they wish. pub fn install_ctrlc_handler() { diff --git a/compiler/rustc_error_codes/src/error_codes/E0534.md b/compiler/rustc_error_codes/src/error_codes/E0534.md index 1ca9411b8d4..023c38c730c 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0534.md +++ b/compiler/rustc_error_codes/src/error_codes/E0534.md @@ -1,8 +1,14 @@ +#### Note: this error code is no longer emitted by the compiler + +This is because it was too specific to the `inline` attribute. +Similar diagnostics occur for other attributes too. +The example here will now emit `E0805` + The `inline` attribute was malformed. Erroneous code example: -```compile_fail,E0534 +```compile_fail,E0805 #[inline()] // error: expected one argument pub fn something() {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0535.md b/compiler/rustc_error_codes/src/error_codes/E0535.md index 0cf3118b02c..93e2ba53826 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0535.md +++ b/compiler/rustc_error_codes/src/error_codes/E0535.md @@ -1,8 +1,13 @@ -An unknown argument was given to the `inline` attribute. +#### Note: this error code is no longer emitted by the compiler + +This is because it was too specific to the `inline` attribute. +Similar diagnostics occur for other attributes too. +The example here will now emit `E0539` + Erroneous code example: -```compile_fail,E0535 +```compile_fail,E0539 #[inline(unknown)] // error: invalid argument pub fn something() {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0539.md b/compiler/rustc_error_codes/src/error_codes/E0539.md index 6b2e23ba2d8..c76b60ac108 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0539.md +++ b/compiler/rustc_error_codes/src/error_codes/E0539.md @@ -24,8 +24,7 @@ struct Stable; const fn stable_fn() {} ``` -Meta items are the key-value pairs inside of an attribute. -To fix these issues you need to give required key-value pairs. +To fix the above example, you can write the following: ``` #![feature(staged_api)] @@ -49,3 +48,29 @@ struct Stable; #[rustc_const_stable(feature = "stable_fn", since = "1.39.0")] // ok! const fn stable_fn() {} ``` + +Several causes of this are, +an attribute may have expected you to give a list but you gave a +`name = value` pair: + +```compile_fail,E0539 +// wrong, should be `#[repr(C)]` +#[repr = "C"] +struct Foo {} +``` + +Or a `name = value` pair, but you gave a list: + +```compile_fail,E0539 +// wrong, should be `note = "reason"` +#[deprecated(since = "1.0.0", note("reason"))] +struct Foo {} +``` + +Or it expected some specific word but you gave an unexpected one: + +```compile_fail,E0539 +// should be `always` or `never` +#[inline(maybe_if_you_feel_like_it)] +fn foo() {} +``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0565.md b/compiler/rustc_error_codes/src/error_codes/E0565.md index d5bba941c1d..34152eb7cfe 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0565.md +++ b/compiler/rustc_error_codes/src/error_codes/E0565.md @@ -9,10 +9,9 @@ struct Repr {} fn main() {} ``` -Literals in attributes are new and largely unsupported in built-in attributes. -Work to support literals where appropriate is ongoing. Try using an unquoted -name instead: - +Not all attributes support literals in their input, +and in some cases they expect an identifier instead. +That would be the solution in the case of `repr`: ``` #[repr(C)] // ok! struct Repr {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0805.md b/compiler/rustc_error_codes/src/error_codes/E0805.md new file mode 100644 index 00000000000..b1ed3a11d48 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0805.md @@ -0,0 +1,26 @@ +An attribute was given an invalid number of arguments + +Erroneous code example: + +```compile_fail,E0805 +#[inline()] // error! should either have a single argument, or no parentheses +fn foo() {} + +#[inline(always, never)] // error! should have only one argument, not two +fn bar() {} +``` + +To fix this, either give the right number of arguments the attribute needs. +In the case of inline, this could be none at all: + +``` +#[inline] +fn foo() {} +``` + +or only one: + +``` +#[inline(always)] +fn foo() {} +``` diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index 2488d870899..6f5e4829802 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -547,6 +547,7 @@ E0801: 0801, E0802: 0802, E0803: 0803, E0804: 0804, +E0805: 0805, ); ) } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index fe01e289334..6ab6f96079e 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -34,6 +34,7 @@ use crate::snippet::{ Annotation, AnnotationColumn, AnnotationType, Line, MultilineAnnotation, Style, StyledString, }; use crate::styled_buffer::StyledBuffer; +use crate::timings::TimingRecord; use crate::translation::{Translate, to_fluent_args}; use crate::{ CodeSuggestion, DiagInner, DiagMessage, ErrCode, FluentBundle, LazyFallbackBundle, Level, @@ -164,11 +165,16 @@ impl Margin { } } +pub enum TimingEvent { + Start, + End, +} + const ANONYMIZED_LINE_NUM: &str = "LL"; pub type DynEmitter = dyn Emitter + DynSend; -/// Emitter trait for emitting errors. +/// Emitter trait for emitting errors and other structured information. pub trait Emitter: Translate { /// Emit a structured diagnostic. fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry); @@ -177,6 +183,10 @@ pub trait Emitter: Translate { /// Currently only supported for the JSON format. fn emit_artifact_notification(&mut self, _path: &Path, _artifact_type: &str) {} + /// Emit a timestamp with start/end of a timing section. + /// Currently only supported for the JSON format. + fn emit_timing_section(&mut self, _record: TimingRecord, _event: TimingEvent) {} + /// Emit a report about future breakage. /// Currently only supported for the JSON format. fn emit_future_breakage_report(&mut self, _diags: Vec<DiagInner>, _registry: &Registry) {} diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index a6583407b7e..d67e2ba2d60 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -28,9 +28,10 @@ use termcolor::{ColorSpec, WriteColor}; use crate::diagnostic::IsLint; use crate::emitter::{ ColorConfig, Destination, Emitter, HumanEmitter, HumanReadableErrorType, OutputTheme, - should_show_source_code, + TimingEvent, should_show_source_code, }; use crate::registry::Registry; +use crate::timings::{TimingRecord, TimingSection}; use crate::translation::{Translate, to_fluent_args}; use crate::{ CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, Subdiag, Suggestions, @@ -104,6 +105,7 @@ impl JsonEmitter { enum EmitTyped<'a> { Diagnostic(Diagnostic), Artifact(ArtifactNotification<'a>), + SectionTiming(SectionTimestamp<'a>), FutureIncompat(FutureIncompatReport<'a>), UnusedExtern(UnusedExterns<'a>), } @@ -135,6 +137,21 @@ impl Emitter for JsonEmitter { } } + fn emit_timing_section(&mut self, record: TimingRecord, event: TimingEvent) { + let event = match event { + TimingEvent::Start => "start", + TimingEvent::End => "end", + }; + let name = match record.section { + TimingSection::Linking => "link", + }; + let data = SectionTimestamp { name, event, timestamp: record.timestamp }; + let result = self.emit(EmitTyped::SectionTiming(data)); + if let Err(e) = result { + panic!("failed to print timing section: {e:?}"); + } + } + fn emit_future_breakage_report(&mut self, diags: Vec<crate::DiagInner>, registry: &Registry) { let data: Vec<FutureBreakageItem<'_>> = diags .into_iter() @@ -264,6 +281,16 @@ struct ArtifactNotification<'a> { } #[derive(Serialize)] +struct SectionTimestamp<'a> { + /// Name of the section + name: &'a str, + /// Start/end of the section + event: &'a str, + /// Opaque timestamp. + timestamp: u128, +} + +#[derive(Serialize)] struct FutureBreakageItem<'a> { // Always EmitTyped::Diagnostic, but we want to make sure it gets serialized // with "$message_type". diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 133bd361ee7..0bd259366de 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -7,6 +7,7 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(not(bootstrap), allow(rustc::direct_use_of_rustc_type_ir))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] @@ -60,8 +61,9 @@ pub use rustc_error_messages::{ SubdiagMessage, fallback_fluent_bundle, fluent_bundle, }; use rustc_hashes::Hash128; -use rustc_lint_defs::LintExpectationId; +use rustc_hir::HirId; pub use rustc_lint_defs::{Applicability, listify, pluralize}; +use rustc_lint_defs::{Lint, LintExpectationId}; use rustc_macros::{Decodable, Encodable}; pub use rustc_span::ErrorGuaranteed; pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker}; @@ -73,7 +75,9 @@ pub use snippet::Style; pub use termcolor::{Color, ColorSpec, WriteColor}; use tracing::debug; +use crate::emitter::TimingEvent; use crate::registry::Registry; +use crate::timings::TimingRecord; pub mod annotate_snippet_emitter_writer; pub mod codes; @@ -89,6 +93,7 @@ mod snippet; mod styled_buffer; #[cfg(test)] mod tests; +pub mod timings; pub mod translation; pub type PResult<'a, T> = Result<T, Diag<'a>>; @@ -101,6 +106,19 @@ rustc_data_structures::static_assert_size!(PResult<'_, ()>, 24); #[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24); +/// Used to avoid depending on `rustc_middle` in `rustc_attr_parsing`. +/// Always the `TyCtxt`. +pub trait LintEmitter: Copy { + #[track_caller] + fn emit_node_span_lint( + self, + lint: &'static Lint, + hir_id: HirId, + span: impl Into<MultiSpan>, + decorator: impl for<'a> LintDiagnostic<'a, ()>, + ); +} + #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] pub enum SuggestionStyle { /// Hide the suggested code when displaying this suggestion inline. @@ -1142,6 +1160,14 @@ impl<'a> DiagCtxtHandle<'a> { self.inner.borrow_mut().emitter.emit_artifact_notification(path, artifact_type); } + pub fn emit_timing_section_start(&self, record: TimingRecord) { + self.inner.borrow_mut().emitter.emit_timing_section(record, TimingEvent::Start); + } + + pub fn emit_timing_section_end(&self, record: TimingRecord) { + self.inner.borrow_mut().emitter.emit_timing_section(record, TimingEvent::End); + } + pub fn emit_future_breakage_report(&self) { let inner = &mut *self.inner.borrow_mut(); let diags = std::mem::take(&mut inner.future_breakage_diagnostics); diff --git a/compiler/rustc_errors/src/timings.rs b/compiler/rustc_errors/src/timings.rs new file mode 100644 index 00000000000..27fc9df8d79 --- /dev/null +++ b/compiler/rustc_errors/src/timings.rs @@ -0,0 +1,80 @@ +use std::time::Instant; + +use crate::DiagCtxtHandle; + +/// A high-level section of the compilation process. +#[derive(Copy, Clone, Debug)] +pub enum TimingSection { + /// Time spent linking. + Linking, +} + +/// Section with attached timestamp +#[derive(Copy, Clone, Debug)] +pub struct TimingRecord { + pub section: TimingSection, + /// Microseconds elapsed since some predetermined point in time (~start of the rustc process). + pub timestamp: u128, +} + +impl TimingRecord { + fn from_origin(origin: Instant, section: TimingSection) -> Self { + Self { section, timestamp: Instant::now().duration_since(origin).as_micros() } + } + + pub fn section(&self) -> TimingSection { + self.section + } + + pub fn timestamp(&self) -> u128 { + self.timestamp + } +} + +/// Manages emission of start/end section timings, enabled through `--json=timings`. +pub struct TimingSectionHandler { + /// Time when the compilation session started. + /// If `None`, timing is disabled. + origin: Option<Instant>, +} + +impl TimingSectionHandler { + pub fn new(enabled: bool) -> Self { + let origin = if enabled { Some(Instant::now()) } else { None }; + Self { origin } + } + + /// Returns a RAII guard that will immediately emit a start the provided section, and then emit + /// its end when it is dropped. + pub fn start_section<'a>( + &self, + diag_ctxt: DiagCtxtHandle<'a>, + section: TimingSection, + ) -> TimingSectionGuard<'a> { + TimingSectionGuard::create(diag_ctxt, section, self.origin) + } +} + +/// RAII wrapper for starting and ending section timings. +pub struct TimingSectionGuard<'a> { + dcx: DiagCtxtHandle<'a>, + section: TimingSection, + origin: Option<Instant>, +} + +impl<'a> TimingSectionGuard<'a> { + fn create(dcx: DiagCtxtHandle<'a>, section: TimingSection, origin: Option<Instant>) -> Self { + if let Some(origin) = origin { + dcx.emit_timing_section_start(TimingRecord::from_origin(origin, section)); + } + Self { dcx, section, origin } + } +} + +impl<'a> Drop for TimingSectionGuard<'a> { + fn drop(&mut self) { + if let Some(origin) = self.origin { + self.dcx.emit_timing_section_end(TimingRecord::from_origin(origin, self.section)); + } + } +} diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 08b7a362083..8b7c47dad99 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -113,7 +113,7 @@ expand_meta_var_expr_unrecognized_var = variable `{$key}` is not recognized in meta-variable expression expand_missing_fragment_specifier = missing fragment specifier - .note = fragment specifiers must be specified in the 2024 edition + .note = fragment specifiers must be provided .suggestion_add_fragspec = try adding a specifier here .valid = {$valid} diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index c7b975d8f3e..7a29f8c9fbd 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -12,7 +12,7 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; use rustc_attr_data_structures::{AttributeKind, Deprecation, Stability, find_attr}; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::sync; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; use rustc_feature::Features; @@ -35,6 +35,7 @@ use crate::base::ast::MetaItemInner; use crate::errors; use crate::expand::{self, AstFragment, Invocation}; use crate::module::DirOwnership; +use crate::stats::MacroStat; // When adding new variants, make sure to // adjust the `visit_*` / `flat_map_*` calls in `InvocationCollector` @@ -727,6 +728,7 @@ pub enum SyntaxExtensionKind { /// A trivial attribute "macro" that does nothing, /// only keeps the attribute and marks it as inert, /// thus making it ineligible for further expansion. + /// E.g. `#[default]`, `#[rustfmt::skip]`. NonMacroAttr, /// A token-based derive macro. @@ -1189,6 +1191,8 @@ pub struct ExtCtxt<'a> { /// in the AST, but insert it here so that we know /// not to expand it again. pub(super) expanded_inert_attrs: MarkedAttrs, + /// `-Zmacro-stats` data. + pub macro_stats: FxHashMap<(Symbol, MacroKind), MacroStat>, } impl<'a> ExtCtxt<'a> { @@ -1218,6 +1222,7 @@ impl<'a> ExtCtxt<'a> { expansions: FxIndexMap::default(), expanded_inert_attrs: MarkedAttrs::new(), buffered_early_lint: vec![], + macro_stats: Default::default(), } } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 569165a64e5..9fd524ef45c 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -42,13 +42,22 @@ use crate::module::{ DirOwnership, ParsedExternalMod, mod_dir_path, mod_file_path_from_attr, parse_external_mod, }; use crate::placeholders::{PlaceholderExpander, placeholder}; +use crate::stats::*; macro_rules! ast_fragments { ( $($Kind:ident($AstTy:ty) { $kind_name:expr; - $(one fn $mut_visit_ast:ident; fn $visit_ast:ident;)? - $(many fn $flat_map_ast_elt:ident; fn $visit_ast_elt:ident($($args:tt)*);)? + $(one + fn $mut_visit_ast:ident; + fn $visit_ast:ident; + fn $ast_to_string:path; + )? + $(many + fn $flat_map_ast_elt:ident; + fn $visit_ast_elt:ident($($args:tt)*); + fn $ast_to_string_elt:path; + )? fn $make_ast:ident; })* ) => { @@ -61,7 +70,7 @@ macro_rules! ast_fragments { } /// "Discriminant" of an AST fragment. - #[derive(Copy, Clone, PartialEq, Eq)] + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum AstFragmentKind { OptExpr, MethodReceiverExpr, @@ -151,6 +160,21 @@ macro_rules! ast_fragments { } V::Result::output() } + + pub(crate) fn to_string(&self) -> String { + match self { + AstFragment::OptExpr(Some(expr)) => pprust::expr_to_string(expr), + AstFragment::OptExpr(None) => unreachable!(), + AstFragment::MethodReceiverExpr(expr) => pprust::expr_to_string(expr), + $($(AstFragment::$Kind(ast) => $ast_to_string(ast),)?)* + $($( + AstFragment::$Kind(ast) => { + // The closure unwraps a `P` if present, or does nothing otherwise. + elems_to_string(&*ast, |ast| $ast_to_string_elt(&*ast)) + } + )?)* + } + } } impl<'a> MacResult for crate::mbe::macro_rules::ParserAnyMacro<'a> { @@ -163,76 +187,98 @@ macro_rules! ast_fragments { } ast_fragments! { - Expr(P<ast::Expr>) { "expression"; one fn visit_expr; fn visit_expr; fn make_expr; } - Pat(P<ast::Pat>) { "pattern"; one fn visit_pat; fn visit_pat; fn make_pat; } - Ty(P<ast::Ty>) { "type"; one fn visit_ty; fn visit_ty; fn make_ty; } + Expr(P<ast::Expr>) { + "expression"; + one fn visit_expr; fn visit_expr; fn pprust::expr_to_string; + fn make_expr; + } + Pat(P<ast::Pat>) { + "pattern"; + one fn visit_pat; fn visit_pat; fn pprust::pat_to_string; + fn make_pat; + } + Ty(P<ast::Ty>) { + "type"; + one fn visit_ty; fn visit_ty; fn pprust::ty_to_string; + fn make_ty; + } Stmts(SmallVec<[ast::Stmt; 1]>) { - "statement"; many fn flat_map_stmt; fn visit_stmt(); fn make_stmts; + "statement"; + many fn flat_map_stmt; fn visit_stmt(); fn pprust::stmt_to_string; + fn make_stmts; } Items(SmallVec<[P<ast::Item>; 1]>) { - "item"; many fn flat_map_item; fn visit_item(); fn make_items; + "item"; + many fn flat_map_item; fn visit_item(); fn pprust::item_to_string; + fn make_items; } TraitItems(SmallVec<[P<ast::AssocItem>; 1]>) { "trait item"; - many fn flat_map_assoc_item; - fn visit_assoc_item(AssocCtxt::Trait); + many fn flat_map_assoc_item; fn visit_assoc_item(AssocCtxt::Trait); + fn pprust::assoc_item_to_string; fn make_trait_items; } ImplItems(SmallVec<[P<ast::AssocItem>; 1]>) { "impl item"; - many fn flat_map_assoc_item; - fn visit_assoc_item(AssocCtxt::Impl { of_trait: false }); + many fn flat_map_assoc_item; fn visit_assoc_item(AssocCtxt::Impl { of_trait: false }); + fn pprust::assoc_item_to_string; fn make_impl_items; } TraitImplItems(SmallVec<[P<ast::AssocItem>; 1]>) { "impl item"; - many fn flat_map_assoc_item; - fn visit_assoc_item(AssocCtxt::Impl { of_trait: true }); + many fn flat_map_assoc_item; fn visit_assoc_item(AssocCtxt::Impl { of_trait: true }); + fn pprust::assoc_item_to_string; fn make_trait_impl_items; } ForeignItems(SmallVec<[P<ast::ForeignItem>; 1]>) { "foreign item"; - many fn flat_map_foreign_item; - fn visit_foreign_item(); + many fn flat_map_foreign_item; fn visit_foreign_item(); fn pprust::foreign_item_to_string; fn make_foreign_items; } Arms(SmallVec<[ast::Arm; 1]>) { - "match arm"; many fn flat_map_arm; fn visit_arm(); fn make_arms; + "match arm"; + many fn flat_map_arm; fn visit_arm(); fn unreachable_to_string; + fn make_arms; } ExprFields(SmallVec<[ast::ExprField; 1]>) { - "field expression"; many fn flat_map_expr_field; fn visit_expr_field(); fn make_expr_fields; + "field expression"; + many fn flat_map_expr_field; fn visit_expr_field(); fn unreachable_to_string; + fn make_expr_fields; } PatFields(SmallVec<[ast::PatField; 1]>) { "field pattern"; - many fn flat_map_pat_field; - fn visit_pat_field(); + many fn flat_map_pat_field; fn visit_pat_field(); fn unreachable_to_string; fn make_pat_fields; } GenericParams(SmallVec<[ast::GenericParam; 1]>) { "generic parameter"; - many fn flat_map_generic_param; - fn visit_generic_param(); + many fn flat_map_generic_param; fn visit_generic_param(); fn unreachable_to_string; fn make_generic_params; } Params(SmallVec<[ast::Param; 1]>) { - "function parameter"; many fn flat_map_param; fn visit_param(); fn make_params; + "function parameter"; + many fn flat_map_param; fn visit_param(); fn unreachable_to_string; + fn make_params; } FieldDefs(SmallVec<[ast::FieldDef; 1]>) { "field"; - many fn flat_map_field_def; - fn visit_field_def(); + many fn flat_map_field_def; fn visit_field_def(); fn unreachable_to_string; fn make_field_defs; } Variants(SmallVec<[ast::Variant; 1]>) { - "variant"; many fn flat_map_variant; fn visit_variant(); fn make_variants; + "variant"; many fn flat_map_variant; fn visit_variant(); fn unreachable_to_string; + fn make_variants; } WherePredicates(SmallVec<[ast::WherePredicate; 1]>) { "where predicate"; - many fn flat_map_where_predicate; - fn visit_where_predicate(); + many fn flat_map_where_predicate; fn visit_where_predicate(); fn unreachable_to_string; fn make_where_predicates; - } - Crate(ast::Crate) { "crate"; one fn visit_crate; fn visit_crate; fn make_crate; } + } + Crate(ast::Crate) { + "crate"; + one fn visit_crate; fn visit_crate; fn unreachable_to_string; + fn make_crate; + } } pub enum SupportsMacroExpansion { @@ -270,7 +316,7 @@ impl AstFragmentKind { } } - fn expect_from_annotatables<I: IntoIterator<Item = Annotatable>>( + pub(crate) fn expect_from_annotatables<I: IntoIterator<Item = Annotatable>>( self, items: I, ) -> AstFragment { @@ -686,13 +732,26 @@ impl<'a, 'b> MacroExpander<'a, 'b> { return ExpandResult::Ready(invoc.fragment_kind.dummy(invoc.span(), guar)); } + let macro_stats = self.cx.sess.opts.unstable_opts.macro_stats; + let (fragment_kind, span) = (invoc.fragment_kind, invoc.span()); ExpandResult::Ready(match invoc.kind { InvocationKind::Bang { mac, span } => match ext { SyntaxExtensionKind::Bang(expander) => { match expander.expand(self.cx, span, mac.args.tokens.clone()) { Ok(tok_result) => { - self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span) + let fragment = + self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span); + if macro_stats { + update_bang_macro_stats( + self.cx, + fragment_kind, + span, + mac, + &fragment, + ); + } + fragment } Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)), } @@ -708,13 +767,15 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }); } }; - let result = if let Some(result) = fragment_kind.make_from(tok_result) { - result + if let Some(fragment) = fragment_kind.make_from(tok_result) { + if macro_stats { + update_bang_macro_stats(self.cx, fragment_kind, span, mac, &fragment); + } + fragment } else { let guar = self.error_wrong_fragment_kind(fragment_kind, &mac, span); fragment_kind.dummy(span, guar) - }; - result + } } _ => unreachable!(), }, @@ -746,24 +807,39 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } _ => item.to_tokens(), }; - let attr_item = attr.unwrap_normal_item(); + let attr_item = attr.get_normal_item(); if let AttrArgs::Eq { .. } = attr_item.args { self.cx.dcx().emit_err(UnsupportedKeyValue { span }); } let inner_tokens = attr_item.args.inner_tokens(); match expander.expand(self.cx, span, inner_tokens, tokens) { - Ok(tok_result) => self.parse_ast_fragment( - tok_result, - fragment_kind, - &attr_item.path, - span, - ), + Ok(tok_result) => { + let fragment = self.parse_ast_fragment( + tok_result, + fragment_kind, + &attr_item.path, + span, + ); + if macro_stats { + update_attr_macro_stats( + self.cx, + fragment_kind, + span, + &attr_item.path, + &attr, + item, + &fragment, + ); + } + fragment + } Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)), } } SyntaxExtensionKind::LegacyAttr(expander) => { match validate_attr::parse_meta(&self.cx.sess.psess, &attr) { Ok(meta) => { + let item_clone = macro_stats.then(|| item.clone()); let items = match expander.expand(self.cx, span, &meta, item, false) { ExpandResult::Ready(items) => items, ExpandResult::Retry(item) => { @@ -782,7 +858,19 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let guar = self.cx.dcx().emit_err(RemoveExprNotSupported { span }); fragment_kind.dummy(span, guar) } else { - fragment_kind.expect_from_annotatables(items) + let fragment = fragment_kind.expect_from_annotatables(items); + if macro_stats { + update_attr_macro_stats( + self.cx, + fragment_kind, + span, + &meta.path, + &attr, + item_clone.unwrap(), + &fragment, + ); + } + fragment } } Err(err) => { @@ -792,6 +880,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } } SyntaxExtensionKind::NonMacroAttr => { + // `-Zmacro-stats` ignores these because they don't do any real expansion. self.cx.expanded_inert_attrs.mark(&attr); item.visit_attrs(|attrs| attrs.insert(pos, attr)); fragment_kind.expect_from_annotatables(iter::once(item)) @@ -822,7 +911,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }); } }; - fragment_kind.expect_from_annotatables(items) + let fragment = fragment_kind.expect_from_annotatables(items); + if macro_stats { + update_derive_macro_stats( + self.cx, + fragment_kind, + span, + &meta.path, + &fragment, + ); + } + fragment } _ => unreachable!(), }, @@ -852,6 +951,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let single_delegations = build_single_delegations::<Node>( self.cx, deleg, &item, &suffixes, item.span, true, ); + // `-Zmacro-stats` ignores these because they don't seem important. fragment_kind.expect_from_annotatables( single_delegations .map(|item| Annotatable::AssocItem(P(item), AssocCtxt::Impl { of_trait })), @@ -1668,7 +1768,7 @@ impl InvocationCollectorNode for ast::Crate { } } -impl InvocationCollectorNode for P<ast::Ty> { +impl InvocationCollectorNode for ast::Ty { type OutputTy = P<ast::Ty>; const KIND: AstFragmentKind = AstFragmentKind::Ty; fn to_annotatable(self) -> Annotatable { @@ -1691,7 +1791,7 @@ impl InvocationCollectorNode for P<ast::Ty> { } } -impl InvocationCollectorNode for P<ast::Pat> { +impl InvocationCollectorNode for ast::Pat { type OutputTy = P<ast::Pat>; const KIND: AstFragmentKind = AstFragmentKind::Pat; fn to_annotatable(self) -> Annotatable { @@ -1714,11 +1814,11 @@ impl InvocationCollectorNode for P<ast::Pat> { } } -impl InvocationCollectorNode for P<ast::Expr> { +impl InvocationCollectorNode for ast::Expr { type OutputTy = P<ast::Expr>; const KIND: AstFragmentKind = AstFragmentKind::Expr; fn to_annotatable(self) -> Annotatable { - Annotatable::Expr(self) + Annotatable::Expr(P(self)) } fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_expr() @@ -1855,29 +1955,29 @@ impl DummyAstNode for ast::Crate { } } -impl DummyAstNode for P<ast::Ty> { +impl DummyAstNode for ast::Ty { fn dummy() -> Self { - P(ast::Ty { + ast::Ty { id: DUMMY_NODE_ID, kind: TyKind::Dummy, span: Default::default(), tokens: Default::default(), - }) + } } } -impl DummyAstNode for P<ast::Pat> { +impl DummyAstNode for ast::Pat { fn dummy() -> Self { - P(ast::Pat { + ast::Pat { id: DUMMY_NODE_ID, kind: PatKind::Wild, span: Default::default(), tokens: Default::default(), - }) + } } } -impl DummyAstNode for P<ast::Expr> { +impl DummyAstNode for ast::Expr { fn dummy() -> Self { ast::Expr::dummy() } @@ -1885,7 +1985,7 @@ impl DummyAstNode for P<ast::Expr> { impl DummyAstNode for AstNodeWrapper<P<ast::Expr>, MethodReceiverTag> { fn dummy() -> Self { - AstNodeWrapper::new(ast::Expr::dummy(), MethodReceiverTag) + AstNodeWrapper::new(P(ast::Expr::dummy()), MethodReceiverTag) } } @@ -2172,7 +2272,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } } - fn visit_node<Node: InvocationCollectorNode<OutputTy = Node> + DummyAstNode>( + fn visit_node<Node: InvocationCollectorNode<OutputTy: Into<Node>> + DummyAstNode>( &mut self, node: &mut Node, ) { @@ -2197,6 +2297,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { *node = self .collect_attr((attr, pos, derives), n.to_annotatable(), Node::KIND) .make_ast::<Node>() + .into() } }, None if node.is_mac_call() => { @@ -2204,7 +2305,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let (mac, attrs, _) = n.take_mac_call(); self.check_attributes(&attrs, &mac); - *node = self.collect_bang(mac, Node::KIND).make_ast::<Node>() + *node = self.collect_bang(mac, Node::KIND).make_ast::<Node>().into() } None if node.delegation().is_some() => unreachable!(), None => { @@ -2314,15 +2415,15 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { self.visit_node(node) } - fn visit_ty(&mut self, node: &mut P<ast::Ty>) { + fn visit_ty(&mut self, node: &mut ast::Ty) { self.visit_node(node) } - fn visit_pat(&mut self, node: &mut P<ast::Pat>) { + fn visit_pat(&mut self, node: &mut ast::Pat) { self.visit_node(node) } - fn visit_expr(&mut self, node: &mut P<ast::Expr>) { + fn visit_expr(&mut self, node: &mut ast::Expr) { // FIXME: Feature gating is performed inconsistently between `Expr` and `OptExpr`. if let Some(attr) = node.attrs.first() { self.cfg().maybe_emit_expr_attr_err(attr); diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 515d82296ca..64be7649775 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -20,6 +20,7 @@ mod errors; mod mbe; mod placeholders; mod proc_macro_server; +mod stats; pub use mbe::macro_rules::compile_declarative_macro; pub mod base; diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 1a2db233b7a..3cd803c3e84 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -112,9 +112,8 @@ use rustc_ast::{DUMMY_NODE_ID, NodeId}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::MultiSpan; use rustc_lint_defs::BuiltinLintDiag; -use rustc_session::lint::builtin::{META_VARIABLE_MISUSE, MISSING_FRAGMENT_SPECIFIER}; +use rustc_session::lint::builtin::META_VARIABLE_MISUSE; use rustc_session::parse::ParseSess; -use rustc_span::edition::Edition; use rustc_span::{ErrorGuaranteed, MacroRulesNormalizedIdent, Span, kw}; use smallvec::SmallVec; @@ -266,23 +265,11 @@ fn check_binders( // Similarly, this can only happen when checking a toplevel macro. TokenTree::MetaVarDecl(span, name, kind) => { if kind.is_none() && node_id != DUMMY_NODE_ID { - // FIXME: Report this as a hard error eventually and remove equivalent errors from - // `parse_tt_inner` and `nameize`. Until then the error may be reported twice, once - // as a hard error and then once as a buffered lint. - if span.edition() >= Edition::Edition2024 { - psess.dcx().emit_err(errors::MissingFragmentSpecifier { - span, - add_span: span.shrink_to_hi(), - valid: VALID_FRAGMENT_NAMES_MSG, - }); - } else { - psess.buffer_lint( - MISSING_FRAGMENT_SPECIFIER, - span, - node_id, - BuiltinLintDiag::MissingFragmentSpecifier, - ); - } + psess.dcx().emit_err(errors::MissingFragmentSpecifier { + span, + add_span: span.shrink_to_hi(), + valid: VALID_FRAGMENT_NAMES_MSG, + }); } if !macros.is_empty() { psess.dcx().span_bug(span, "unexpected MetaVarDecl in nested lhs"); diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 3dcb20c8c76..2c486a02bdf 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -332,9 +332,9 @@ impl MutVisitor for PlaceholderExpander { } } - fn visit_expr(&mut self, expr: &mut P<ast::Expr>) { + fn visit_expr(&mut self, expr: &mut ast::Expr) { match expr.kind { - ast::ExprKind::MacCall(_) => *expr = self.remove(expr.id).make_expr(), + ast::ExprKind::MacCall(_) => *expr = *self.remove(expr.id).make_expr(), _ => walk_expr(self, expr), } } @@ -399,16 +399,16 @@ impl MutVisitor for PlaceholderExpander { stmts } - fn visit_pat(&mut self, pat: &mut P<ast::Pat>) { + fn visit_pat(&mut self, pat: &mut ast::Pat) { match pat.kind { - ast::PatKind::MacCall(_) => *pat = self.remove(pat.id).make_pat(), + ast::PatKind::MacCall(_) => *pat = *self.remove(pat.id).make_pat(), _ => walk_pat(self, pat), } } - fn visit_ty(&mut self, ty: &mut P<ast::Ty>) { + fn visit_ty(&mut self, ty: &mut ast::Ty) { match ty.kind { - ast::TyKind::MacCall(_) => *ty = self.remove(ty.id).make_ty(), + ast::TyKind::MacCall(_) => *ty = *self.remove(ty.id).make_ty(), _ => walk_ty(self, ty), } } diff --git a/compiler/rustc_expand/src/stats.rs b/compiler/rustc_expand/src/stats.rs new file mode 100644 index 00000000000..6b2ad30dffd --- /dev/null +++ b/compiler/rustc_expand/src/stats.rs @@ -0,0 +1,171 @@ +use std::iter; + +use rustc_ast::ptr::P; +use rustc_ast::{self as ast, DUMMY_NODE_ID, Expr, ExprKind}; +use rustc_ast_pretty::pprust; +use rustc_span::hygiene::{ExpnKind, MacroKind}; +use rustc_span::{Span, Symbol, kw, sym}; +use smallvec::SmallVec; + +use crate::base::{Annotatable, ExtCtxt}; +use crate::expand::{AstFragment, AstFragmentKind}; + +#[derive(Default)] +pub struct MacroStat { + /// Number of uses of the macro. + pub uses: usize, + + /// Net increase in number of lines of code (when pretty-printed), i.e. + /// `lines(output) - lines(invocation)`. Can be negative because a macro + /// output may be smaller than the invocation. + pub lines: isize, + + /// Net increase in number of lines of code (when pretty-printed), i.e. + /// `bytes(output) - bytes(invocation)`. Can be negative because a macro + /// output may be smaller than the invocation. + pub bytes: isize, +} + +pub(crate) fn elems_to_string<T>(elems: &SmallVec<[T; 1]>, f: impl Fn(&T) -> String) -> String { + let mut s = String::new(); + for (i, elem) in elems.iter().enumerate() { + if i > 0 { + s.push('\n'); + } + s.push_str(&f(elem)); + } + s +} + +pub(crate) fn unreachable_to_string<T>(_: &T) -> String { + unreachable!() +} + +pub(crate) fn update_bang_macro_stats( + ecx: &mut ExtCtxt<'_>, + fragment_kind: AstFragmentKind, + span: Span, + mac: P<ast::MacCall>, + fragment: &AstFragment, +) { + // Does this path match any of the include macros, e.g. `include!`? + // Ignore them. They would have large numbers but are entirely + // unsurprising and uninteresting. + let is_include_path = mac.path == sym::include + || mac.path == sym::include_bytes + || mac.path == sym::include_str + || mac.path == [sym::std, sym::include].as_slice() // std::include + || mac.path == [sym::std, sym::include_bytes].as_slice() // std::include_bytes + || mac.path == [sym::std, sym::include_str].as_slice(); // std::include_str + if is_include_path { + return; + } + + // The call itself (e.g. `println!("hi")`) is the input. Need to wrap + // `mac` in something printable; `ast::Expr` is as good as anything + // else. + let expr = Expr { + id: DUMMY_NODE_ID, + kind: ExprKind::MacCall(mac), + span: Default::default(), + attrs: Default::default(), + tokens: None, + }; + let input = pprust::expr_to_string(&expr); + + // Get `mac` back out of `expr`. + let ast::Expr { kind: ExprKind::MacCall(mac), .. } = expr else { unreachable!() }; + + update_macro_stats(ecx, MacroKind::Bang, fragment_kind, span, &mac.path, &input, fragment); +} + +pub(crate) fn update_attr_macro_stats( + ecx: &mut ExtCtxt<'_>, + fragment_kind: AstFragmentKind, + span: Span, + path: &ast::Path, + attr: &ast::Attribute, + item: Annotatable, + fragment: &AstFragment, +) { + // Does this path match `#[derive(...)]` in any of its forms? If so, + // ignore it because the individual derives will go through the + // `Invocation::Derive` handling separately. + let is_derive_path = *path == sym::derive + // ::core::prelude::v1::derive + || *path == [kw::PathRoot, sym::core, sym::prelude, sym::v1, sym::derive].as_slice(); + if is_derive_path { + return; + } + + // The attribute plus the item itself constitute the input, which we + // measure. + let input = format!( + "{}\n{}", + pprust::attribute_to_string(attr), + fragment_kind.expect_from_annotatables(iter::once(item)).to_string(), + ); + update_macro_stats(ecx, MacroKind::Attr, fragment_kind, span, path, &input, fragment); +} + +pub(crate) fn update_derive_macro_stats( + ecx: &mut ExtCtxt<'_>, + fragment_kind: AstFragmentKind, + span: Span, + path: &ast::Path, + fragment: &AstFragment, +) { + // Use something like `#[derive(Clone)]` for the measured input, even + // though it may have actually appeared in a multi-derive attribute + // like `#[derive(Clone, Copy, Debug)]`. + let input = format!("#[derive({})]", pprust::path_to_string(path)); + update_macro_stats(ecx, MacroKind::Derive, fragment_kind, span, path, &input, fragment); +} + +pub(crate) fn update_macro_stats( + ecx: &mut ExtCtxt<'_>, + macro_kind: MacroKind, + fragment_kind: AstFragmentKind, + span: Span, + path: &ast::Path, + input: &str, + fragment: &AstFragment, +) { + fn lines_and_bytes(s: &str) -> (usize, usize) { + (s.trim_end().split('\n').count(), s.len()) + } + + // Measure the size of the output by pretty-printing it and counting + // the lines and bytes. + let name = Symbol::intern(&pprust::path_to_string(path)); + let output = fragment.to_string(); + let (in_l, in_b) = lines_and_bytes(input); + let (out_l, out_b) = lines_and_bytes(&output); + + // This code is useful for debugging `-Zmacro-stats`. For every + // invocation it prints the full input and output. + if false { + let name = ExpnKind::Macro(macro_kind, name).descr(); + let crate_name = &ecx.ecfg.crate_name; + let span = ecx + .sess + .source_map() + .span_to_string(span, rustc_span::FileNameDisplayPreference::Local); + eprint!( + "\ + -------------------------------\n\ + {name}: [{crate_name}] ({fragment_kind:?}) {span}\n\ + -------------------------------\n\ + {input}\n\ + -- ({in_l} lines, {in_b} bytes) --> ({out_l} lines, {out_b} bytes) --\n\ + {output}\n\ + " + ); + } + + // The recorded size is the difference between the input and the output. + let entry = ecx.macro_stats.entry((name, macro_kind)).or_insert(MacroStat::default()); + entry.uses += 1; + entry.lines += out_l as isize - in_l as isize; + entry.bytes += out_b as isize - in_b as isize; +} diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index c117e0fcf7c..b5218ec267c 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -9,7 +9,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_span::edition::Edition; use rustc_span::{Symbol, sym}; -use crate::{Features, Stability}; +use crate::Features; type GateFn = fn(&Features) -> bool; @@ -94,34 +94,24 @@ pub enum AttributeSafety { Unsafe { unsafe_since: Option<Edition> }, } -#[derive(Clone, Copy)] +#[derive(Clone, Debug, Copy)] pub enum AttributeGate { - /// Is gated by a given feature gate, reason - /// and function to check if enabled - Gated(Stability, Symbol, &'static str, fn(&Features) -> bool), - + /// A gated attribute which requires a feature gate to be enabled. + Gated { + /// The feature gate, for example `#![feature(rustc_attrs)]` for rustc_* attributes. + feature: Symbol, + /// The error message displayed when an attempt is made to use the attribute without its feature gate. + message: &'static str, + /// Check function to be called during the `PostExpansionVisitor` pass. + check: fn(&Features) -> bool, + /// Notes to be displayed when an attempt is made to use the attribute without its feature gate. + notes: &'static [&'static str], + }, /// Ungated attribute, can be used on all release channels Ungated, } -// fn() is not Debug -impl std::fmt::Debug for AttributeGate { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match *self { - Self::Gated(ref stab, name, expl, _) => { - write!(fmt, "Gated({stab:?}, {name}, {expl})") - } - Self::Ungated => write!(fmt, "Ungated"), - } - } -} - -impl AttributeGate { - fn is_deprecated(&self) -> bool { - matches!(*self, Self::Gated(Stability::Deprecated(_, _), ..)) - } -} - +// FIXME(jdonszelmann): move to rustc_attr_data_structures /// A template that the attribute input must match. /// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now. #[derive(Clone, Copy, Default)] @@ -138,6 +128,26 @@ pub struct AttributeTemplate { pub name_value_str: Option<&'static str>, } +impl AttributeTemplate { + pub fn suggestions(&self, inner: bool, name: impl std::fmt::Display) -> Vec<String> { + let mut suggestions = vec![]; + let inner = if inner { "!" } else { "" }; + if self.word { + suggestions.push(format!("#{inner}[{name}]")); + } + if let Some(descr) = self.list { + suggestions.push(format!("#{inner}[{name}({descr})]")); + } + suggestions.extend(self.one_of.iter().map(|&word| format!("#{inner}[{name}({word})]"))); + if let Some(descr) = self.name_value_str { + suggestions.push(format!("#{inner}[{name} = \"{descr}\"]")); + } + suggestions.sort(); + + suggestions + } +} + /// How to handle multiple duplicate attributes on the same item. #[derive(Clone, Copy, Default)] pub enum AttributeDuplicates { @@ -192,20 +202,21 @@ pub enum AttributeDuplicates { /// A convenience macro for constructing attribute templates. /// E.g., `template!(Word, List: "description")` means that the attribute /// supports forms `#[attr]` and `#[attr(description)]`. +#[macro_export] macro_rules! template { - (Word) => { template!(@ true, None, &[], None) }; - (List: $descr: expr) => { template!(@ false, Some($descr), &[], None) }; - (OneOf: $one_of: expr) => { template!(@ false, None, $one_of, None) }; - (NameValueStr: $descr: expr) => { template!(@ false, None, &[], Some($descr)) }; - (Word, List: $descr: expr) => { template!(@ true, Some($descr), &[], None) }; - (Word, NameValueStr: $descr: expr) => { template!(@ true, None, &[], Some($descr)) }; + (Word) => { $crate::template!(@ true, None, &[], None) }; + (List: $descr: expr) => { $crate::template!(@ false, Some($descr), &[], None) }; + (OneOf: $one_of: expr) => { $crate::template!(@ false, None, $one_of, None) }; + (NameValueStr: $descr: expr) => { $crate::template!(@ false, None, &[], Some($descr)) }; + (Word, List: $descr: expr) => { $crate::template!(@ true, Some($descr), &[], None) }; + (Word, NameValueStr: $descr: expr) => { $crate::template!(@ true, None, &[], Some($descr)) }; (List: $descr1: expr, NameValueStr: $descr2: expr) => { - template!(@ false, Some($descr1), &[], Some($descr2)) + $crate::template!(@ false, Some($descr1), &[], Some($descr2)) }; (Word, List: $descr1: expr, NameValueStr: $descr2: expr) => { - template!(@ true, Some($descr1), &[], Some($descr2)) + $crate::template!(@ true, Some($descr1), &[], Some($descr2)) }; - (@ $word: expr, $list: expr, $one_of: expr, $name_value_str: expr) => { AttributeTemplate { + (@ $word: expr, $list: expr, $one_of: expr, $name_value_str: expr) => { $crate::AttributeTemplate { word: $word, list: $list, one_of: $one_of, name_value_str: $name_value_str } }; } @@ -247,7 +258,7 @@ macro_rules! ungated { } macro_rules! gated { - (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => { + (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $message:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, encode_cross_crate: $encode_cross_crate, @@ -255,10 +266,15 @@ macro_rules! gated { safety: AttributeSafety::Unsafe { unsafe_since: None }, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::$gate, $msg, Features::$gate), + gate: Gated { + feature: sym::$gate, + message: $message, + check: Features::$gate, + notes: &[], + }, } }; - (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => { + (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $message:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, encode_cross_crate: $encode_cross_crate, @@ -266,10 +282,15 @@ macro_rules! gated { safety: AttributeSafety::Unsafe { unsafe_since: None }, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::$attr, $msg, Features::$attr), + gate: Gated { + feature: sym::$attr, + message: $message, + check: Features::$attr, + notes: &[], + }, } }; - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $message:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, encode_cross_crate: $encode_cross_crate, @@ -277,10 +298,15 @@ macro_rules! gated { safety: AttributeSafety::Normal, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::$gate, $msg, Features::$gate), + gate: Gated { + feature: sym::$gate, + message: $message, + check: Features::$gate, + notes: &[], + }, } }; - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $message:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, encode_cross_crate: $encode_cross_crate, @@ -288,7 +314,12 @@ macro_rules! gated { safety: AttributeSafety::Normal, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::$attr, $msg, Features::$attr), + gate: Gated { + feature: sym::$attr, + message: $message, + check: Features::$attr, + notes: &[], + }, } }; } @@ -304,12 +335,11 @@ macro_rules! rustc_attr { concat!( "the `#[", stringify!($attr), - "]` attribute is just used for rustc unit tests \ - and will never be stable", + "]` attribute is used for rustc unit tests" ), ) }; - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $($notes:expr),* $(,)?) => { BuiltinAttribute { name: sym::$attr, encode_cross_crate: $encode_cross_crate, @@ -317,7 +347,17 @@ macro_rules! rustc_attr { safety: AttributeSafety::Normal, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, Features::rustc_attrs), + gate: Gated { + feature: sym::rustc_attrs, + message: "use of an internal attribute", + check: Features::rustc_attrs, + notes: &[ + concat!("the `#[", + stringify!($attr), + "]` attribute is an internal implementation detail that will never be stable"), + $($notes),* + ] + }, } }; } @@ -328,9 +368,6 @@ macro_rules! experimental { }; } -const IMPL_DETAIL: &str = "internal implementation detail"; -const INTERNAL_UNSTABLE: &str = "this is an internal attribute that will never be stable"; - #[derive(PartialEq)] pub enum EncodeCrossCrate { Yes, @@ -668,7 +705,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_deprecated_safe_2024, Normal, template!(List: r#"audit_that = "...""#), ErrorFollowing, EncodeCrossCrate::Yes, - "rustc_deprecated_safe_2024 is supposed to be used in libstd only", + "`#[rustc_deprecated_safe_2024]` is used to declare functions unsafe across the edition 2024 boundary", ), rustc_attr!( rustc_pub_transparent, Normal, template!(Word), @@ -695,7 +732,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ErrorFollowing, EncodeCrossCrate::No, "`rustc_never_type_options` is used to experiment with never type fallback and work on \ - never type stabilization, and will never be stable" + never type stabilization" ), // ========================================================================== @@ -704,23 +741,23 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_allocator, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, IMPL_DETAIL + EncodeCrossCrate::No, ), rustc_attr!( rustc_nounwind, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, IMPL_DETAIL + EncodeCrossCrate::No, ), rustc_attr!( rustc_reallocator, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, IMPL_DETAIL + EncodeCrossCrate::No, ), rustc_attr!( rustc_deallocator, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, IMPL_DETAIL + EncodeCrossCrate::No, ), rustc_attr!( rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, IMPL_DETAIL + EncodeCrossCrate::No, ), gated!( default_lib_allocator, Normal, template!(Word), WarnFollowing, @@ -762,7 +799,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!( rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, INTERNAL_UNSTABLE + EncodeCrossCrate::No, ), // ========================================================================== @@ -772,11 +809,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_builtin_macro, Normal, template!(Word, List: "name, /*opt*/ attributes(name1, name2, ...)"), ErrorFollowing, - EncodeCrossCrate::Yes, IMPL_DETAIL + EncodeCrossCrate::Yes, ), rustc_attr!( rustc_proc_macro_decls, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, INTERNAL_UNSTABLE + EncodeCrossCrate::No, ), rustc_attr!( rustc_macro_transparency, Normal, @@ -786,7 +823,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_autodiff, Normal, template!(Word, List: r#""...""#), DuplicatesOk, - EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + EncodeCrossCrate::Yes, ), // Traces that are left when `cfg` and `cfg_attr` attributes are expanded. // The attributes are not gated, to avoid stability errors, but they cannot be used in stable @@ -812,54 +849,53 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ NameValueStr: "message" ), ErrorFollowing, EncodeCrossCrate::Yes, - INTERNAL_UNSTABLE + "see `#[diagnostic::on_unimplemented]` for the stable equivalent of this attribute" ), rustc_attr!( rustc_confusables, Normal, template!(List: r#""name1", "name2", ..."#), ErrorFollowing, EncodeCrossCrate::Yes, - INTERNAL_UNSTABLE, ), // Enumerates "identity-like" conversion methods to suggest on type mismatch. rustc_attr!( rustc_conversion_suggestion, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + WarnFollowing, EncodeCrossCrate::Yes, ), // Prevents field reads in the marked trait or method to be considered // during dead code analysis. rustc_attr!( rustc_trivial_field_reads, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + WarnFollowing, EncodeCrossCrate::Yes, ), // Used by the `rustc::potential_query_instability` lint to warn methods which // might not be stable during incremental compilation. rustc_attr!( rustc_lint_query_instability, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + WarnFollowing, EncodeCrossCrate::Yes, ), // Used by the `rustc::untracked_query_information` lint to warn methods which // might not be stable during incremental compilation. rustc_attr!( rustc_lint_untracked_query_information, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + WarnFollowing, EncodeCrossCrate::Yes, ), // Used by the `rustc::diagnostic_outside_of_impl` lints to assist in changes to diagnostic // APIs. Any function with this attribute will be checked by that lint. rustc_attr!( rustc_lint_diagnostics, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + WarnFollowing, EncodeCrossCrate::Yes, ), // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions` // types (as well as any others in future). rustc_attr!( rustc_lint_opt_ty, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + WarnFollowing, EncodeCrossCrate::Yes, ), // Used by the `rustc::bad_opt_access` lint on fields // types (as well as any others in future). rustc_attr!( rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), - WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + WarnFollowing, EncodeCrossCrate::Yes, ), // ========================================================================== @@ -868,28 +904,30 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_promotable, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, IMPL_DETAIL), + EncodeCrossCrate::No, ), rustc_attr!( rustc_legacy_const_generics, Normal, template!(List: "N"), ErrorFollowing, - EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + EncodeCrossCrate::Yes, ), - // Do not const-check this function's body. It will always get replaced during CTFE. + // Do not const-check this function's body. It will always get replaced during CTFE via `hook_special_const_fn`. rustc_attr!( rustc_do_not_const_check, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + EncodeCrossCrate::Yes, "`#[rustc_do_not_const_check]` skips const-check for this function's body", ), - // Ensure the argument to this function is &&str during const-check. rustc_attr!( rustc_const_panic_str, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + EncodeCrossCrate::Yes, "`#[rustc_const_panic_str]` ensures the argument to this function is &&str during const-check", ), rustc_attr!( rustc_const_stable_indirect, Normal, - template!(Word), WarnFollowing, EncodeCrossCrate::No, IMPL_DETAIL, + template!(Word), + WarnFollowing, + EncodeCrossCrate::No, + "this is an internal implementation detail", ), rustc_attr!( rustc_intrinsic_const_stable_indirect, Normal, - template!(Word), WarnFollowing, EncodeCrossCrate::No, IMPL_DETAIL, + template!(Word), WarnFollowing, EncodeCrossCrate::No, "this is an internal implementation detail", ), gated!( rustc_allow_const_fn_unstable, Normal, @@ -905,21 +943,21 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_layout_scalar_valid_range_start, Normal, template!(List: "value"), ErrorFollowing, EncodeCrossCrate::Yes, "the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable \ - niche optimizations in libcore and libstd and will never be stable", + niche optimizations in the standard library", ), rustc_attr!( rustc_layout_scalar_valid_range_end, Normal, template!(List: "value"), ErrorFollowing, EncodeCrossCrate::Yes, "the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \ - niche optimizations in libcore and libstd and will never be stable", + niche optimizations in the standard library", ), rustc_attr!( rustc_nonnull_optimization_guaranteed, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, "the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document \ - guaranteed niche optimizations in libcore and libstd and will never be stable\n\ - (note that the compiler does not even check whether the type indeed is being non-null-optimized; \ - it is your responsibility to ensure that the attribute is only used on types that are optimized)", + guaranteed niche optimizations in the standard library", + "the compiler does not even check whether the type indeed is being non-null-optimized; \ + it is your responsibility to ensure that the attribute is only used on types that are optimized", ), // ========================================================================== @@ -932,17 +970,17 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_as_ptr, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, - "#[rustc_as_ptr] is used to mark functions returning pointers to their inner allocations." + "`#[rustc_as_ptr]` is used to mark functions returning pointers to their inner allocations." ), rustc_attr!( rustc_pass_by_value, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, - "#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference." + "`#[rustc_pass_by_value]` is used to mark types that must be passed by value instead of reference." ), rustc_attr!( rustc_never_returns_null_ptr, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, - "#[rustc_never_returns_null_ptr] is used to mark functions returning non-null pointers." + "`#[rustc_never_returns_null_ptr]` is used to mark functions returning non-null pointers." ), rustc_attr!( rustc_no_implicit_autorefs, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, @@ -950,15 +988,15 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!( rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No, - "#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`." + "`#![rustc_coherence_is_core]` allows inherent methods on builtin types, only intended to be used in `core`." ), rustc_attr!( rustc_coinductive, AttributeType::Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, - "#![rustc_coinductive] changes a trait to be coinductive, allowing cycles in the trait solver." + "`#[rustc_coinductive]` changes a trait to be coinductive, allowing cycles in the trait solver." ), rustc_attr!( rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No, - "#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl." + "`#[rustc_allow_incoherent_impl]` has to be added to all impl items of an incoherent inherent impl." ), rustc_attr!( rustc_preserve_ub_checks, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No, @@ -970,7 +1008,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(Word), ErrorFollowing, EncodeCrossCrate::No, - "#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls" + "`#[rustc_deny_explicit_impl]` enforces that a trait can have no user-provided impls" ), rustc_attr!( rustc_do_not_implement_via_object, @@ -978,14 +1016,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(Word), ErrorFollowing, EncodeCrossCrate::No, - "#[rustc_do_not_implement_via_object] opts out of the automatic trait impl for trait objects \ + "`#[rustc_do_not_implement_via_object]` opts out of the automatic trait impl for trait objects \ (`impl Trait for dyn Trait`)" ), rustc_attr!( rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, - "#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \ - the given type by annotating all impl items with #[rustc_allow_incoherent_impl]." + "`#[rustc_has_incoherent_inherent_impls]` allows the addition of incoherent inherent impls for \ + the given type by annotating all impl items with `#[rustc_allow_incoherent_impl]`." ), BuiltinAttribute { @@ -996,12 +1034,13 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ safety: AttributeSafety::Normal, template: template!(NameValueStr: "name"), duplicates: ErrorFollowing, - gate: Gated( - Stability::Unstable, - sym::rustc_attrs, - "diagnostic items compiler internal support for linting", - Features::rustc_attrs, - ), + gate: Gated{ + feature: sym::rustc_attrs, + message: "use of an internal attribute", + check: Features::rustc_attrs, + notes: &["the `#[rustc_diagnostic_item]` attribute allows the compiler to reference types \ + from the standard library for diagnostic purposes"], + }, }, gated!( // Used in resolve: @@ -1015,14 +1054,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_inherit_overflow_checks, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, "the `#[rustc_inherit_overflow_checks]` attribute is just used to control \ - overflow checking behavior of several libcore functions that are inlined \ - across crates and will never be stable", + overflow checking behavior of several functions in the standard library that are inlined \ + across crates", ), rustc_attr!( rustc_reservation_impl, Normal, template!(NameValueStr: "reservation message"), ErrorFollowing, EncodeCrossCrate::Yes, "the `#[rustc_reservation_impl]` attribute is internally used \ - for reserving for `for<T> From<!> for T` impl" + for reserving `impl<T> From<!> for T` as part of the effort to stabilize `!`" ), rustc_attr!( rustc_test_marker, Normal, template!(NameValueStr: "name"), WarnFollowing, @@ -1053,12 +1092,13 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_must_implement_one_of, Normal, template!(List: "function1, function2, ..."), ErrorFollowing, EncodeCrossCrate::No, "the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete \ - definition of a trait, it's currently in experimental form and should be changed before \ - being exposed outside of the std" + definition of a trait. Its syntax and semantics are highly experimental and will be \ + subject to change before stabilization", ), rustc_attr!( rustc_doc_primitive, Normal, template!(NameValueStr: "primitive name"), ErrorFollowing, - EncodeCrossCrate::Yes, r#"`rustc_doc_primitive` is a rustc internal attribute"#, + EncodeCrossCrate::Yes, "the `#[rustc_doc_primitive]` attribute is used by the standard library \ + to provide a way to generate documentation for primitive types", ), gated!( rustc_intrinsic, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, intrinsics, @@ -1066,11 +1106,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!( rustc_no_mir_inline, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, - "#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen" + "`#[rustc_no_mir_inline]` prevents the MIR inliner from inlining a function while not affecting codegen" ), rustc_attr!( rustc_force_inline, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, EncodeCrossCrate::Yes, - "#[rustc_force_inline] forces a free function to be inlined" + "`#[rustc_force_inline]` forces a free function to be inlined" ), // ========================================================================== @@ -1209,10 +1249,6 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), ]; -pub fn deprecated_attributes() -> Vec<&'static BuiltinAttribute> { - BUILTIN_ATTRIBUTES.iter().filter(|attr| attr.gate.is_deprecated()).collect() -} - pub fn is_builtin_attr_name(name: Symbol) -> bool { BUILTIN_ATTRIBUTE_MAP.get(&name).is_some() } diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 25764755a8f..dbc0daa3d83 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -40,14 +40,6 @@ pub struct Feature { issue: Option<NonZero<u32>>, } -#[derive(Copy, Clone, Debug)] -pub enum Stability { - Unstable, - // First argument is tracking issue link; second argument is an optional - // help message, which defaults to "remove this attribute". - Deprecated(&'static str, Option<&'static str>), -} - #[derive(Clone, Copy, Debug, Hash)] pub enum UnstableFeatures { /// Disallow use of unstable features, as on beta/stable channels. @@ -144,9 +136,8 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZero<u pub use accepted::ACCEPTED_LANG_FEATURES; pub use builtin_attrs::{ AttributeDuplicates, AttributeGate, AttributeSafety, AttributeTemplate, AttributeType, - BUILTIN_ATTRIBUTE_MAP, BUILTIN_ATTRIBUTES, BuiltinAttribute, GatedCfg, deprecated_attributes, - encode_cross_crate, find_gated_cfg, is_builtin_attr_name, is_stable_diagnostic_attribute, - is_valid_for_get_attr, + BUILTIN_ATTRIBUTE_MAP, BUILTIN_ATTRIBUTES, BuiltinAttribute, GatedCfg, encode_cross_crate, + find_gated_cfg, is_builtin_attr_name, is_stable_diagnostic_attribute, is_valid_for_get_attr, }; pub use removed::REMOVED_LANG_FEATURES; pub use unstable::{ diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 9738f169595..0cd090b25a4 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -263,6 +263,8 @@ declare_features! ( /// Allows unnamed fields of struct and union type (removed, unnamed_fields, "1.83.0", Some(49804), Some("feature needs redesign"), 131045), (removed, unsafe_no_drop_flag, "1.0.0", None, None), + /// Allows unsized rvalues at arguments and parameters. + (removed, unsized_locals, "CURRENT_RUSTC_VERSION", Some(48055), Some("removed due to implementation concerns; see https://github.com/rust-lang/rust/issues/111942")), (removed, unsized_tuple_coercion, "1.87.0", Some(42877), Some("The feature restricts possible layouts for tuples, and this restriction is not worth it."), 137728), /// Allows `union` fields that don't implement `Copy` as long as they don't have any drop glue. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 594021d78d2..5e42b919f9d 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -237,6 +237,8 @@ declare_features! ( (internal, profiler_runtime, "1.18.0", None), /// Allows using `rustc_*` attributes (RFC 572). (internal, rustc_attrs, "1.0.0", None), + /// Introduces a hierarchy of `Sized` traits (RFC 3729). + (unstable, sized_hierarchy, "CURRENT_RUSTC_VERSION", None), /// Allows using the `#[stable]` and `#[unstable]` attributes. (internal, staged_api, "1.0.0", None), /// Added for testing unstable lints; perma-unstable. @@ -353,6 +355,8 @@ declare_features! ( (unstable, abi_avr_interrupt, "1.45.0", Some(69664)), /// Allows `extern "C-cmse-nonsecure-call" fn()`. (unstable, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391)), + /// Allows `extern "custom" fn()`. + (unstable, abi_custom, "CURRENT_RUSTC_VERSION", Some(140829)), /// Allows `extern "gpu-kernel" fn()`. (unstable, abi_gpu_kernel, "1.86.0", Some(135467)), /// Allows `extern "msp430-interrupt" fn()`. @@ -665,8 +669,6 @@ declare_features! ( (incomplete, unsized_const_params, "1.82.0", Some(95174)), /// Allows unsized fn parameters. (internal, unsized_fn_params, "1.49.0", Some(48055)), - /// Allows unsized rvalues at arguments and parameters. - (incomplete, unsized_locals, "1.30.0", Some(48055)), /// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute. (unstable, used_with_arg, "1.60.0", Some(93798)), /// Allows use of attributes in `where` clauses. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 6f288bb39b9..556f50a85af 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -34,6 +34,7 @@ use crate::def::{CtorKind, DefKind, PerNS, Res}; use crate::def_id::{DefId, LocalDefIdMap}; pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId}; use crate::intravisit::{FnKind, VisitorExt}; +use crate::lints::DelayedLints; #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)] pub enum AngleBrackets { @@ -1526,6 +1527,10 @@ pub struct OwnerInfo<'hir> { /// Map indicating what traits are in scope for places where this /// is relevant; generated by resolve. pub trait_map: ItemLocalMap<Box<[TraitCandidate]>>, + + /// Lints delayed during ast lowering to be emitted + /// after hir has completely built + pub delayed_lints: DelayedLints, } impl<'tcx> OwnerInfo<'tcx> { @@ -2280,12 +2285,23 @@ pub struct Expr<'hir> { } impl Expr<'_> { - pub fn precedence(&self) -> ExprPrecedence { + pub fn precedence( + &self, + for_each_attr: &dyn Fn(HirId, &mut dyn FnMut(&Attribute)), + ) -> ExprPrecedence { + let prefix_attrs_precedence = || -> ExprPrecedence { + let mut has_outer_attr = false; + for_each_attr(self.hir_id, &mut |attr: &Attribute| { + has_outer_attr |= matches!(attr.style(), AttrStyle::Outer) + }); + if has_outer_attr { ExprPrecedence::Prefix } else { ExprPrecedence::Unambiguous } + }; + match &self.kind { ExprKind::Closure(closure) => { match closure.fn_decl.output { FnRetTy::DefaultReturn(_) => ExprPrecedence::Jump, - FnRetTy::Return(_) => ExprPrecedence::Unambiguous, + FnRetTy::Return(_) => prefix_attrs_precedence(), } } @@ -2310,7 +2326,7 @@ impl Expr<'_> { | ExprKind::Let(..) | ExprKind::Unary(..) => ExprPrecedence::Prefix, - // Never need parens + // Need parens if and only if there are prefix attributes. ExprKind::Array(_) | ExprKind::Block(..) | ExprKind::Call(..) @@ -2332,9 +2348,9 @@ impl Expr<'_> { | ExprKind::Type(..) | ExprKind::UnsafeBinderCast(..) | ExprKind::Use(..) - | ExprKind::Err(_) => ExprPrecedence::Unambiguous, + | ExprKind::Err(_) => prefix_attrs_precedence(), - ExprKind::DropTemps(expr, ..) => expr.precedence(), + ExprKind::DropTemps(expr, ..) => expr.precedence(for_each_attr), } } @@ -3059,6 +3075,7 @@ pub struct TraitItem<'hir> { pub kind: TraitItemKind<'hir>, pub span: Span, pub defaultness: Defaultness, + pub has_delayed_lints: bool, } macro_rules! expect_methods_self_kind { @@ -3163,6 +3180,7 @@ pub struct ImplItem<'hir> { pub defaultness: Defaultness, pub span: Span, pub vis_span: Span, + pub has_delayed_lints: bool, } impl<'hir> ImplItem<'hir> { @@ -4082,6 +4100,7 @@ pub struct Item<'hir> { pub kind: ItemKind<'hir>, pub span: Span, pub vis_span: Span, + pub has_delayed_lints: bool, } impl<'hir> Item<'hir> { @@ -4487,6 +4506,7 @@ pub struct ForeignItem<'hir> { pub owner_id: OwnerId, pub span: Span, pub vis_span: Span, + pub has_delayed_lints: bool, } impl ForeignItem<'_> { @@ -4969,7 +4989,7 @@ mod size_asserts { static_assert_size!(Expr<'_>, 64); static_assert_size!(ExprKind<'_>, 48); static_assert_size!(FnDecl<'_>, 40); - static_assert_size!(ForeignItem<'_>, 88); + static_assert_size!(ForeignItem<'_>, 96); static_assert_size!(ForeignItemKind<'_>, 56); static_assert_size!(GenericArg<'_>, 16); static_assert_size!(GenericBound<'_>, 64); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index bebac3a4b78..57e49625148 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -537,7 +537,7 @@ pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) -> } pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::Result { - let Item { owner_id: _, kind, span: _, vis_span: _ } = item; + let Item { owner_id: _, kind, span: _, vis_span: _, has_delayed_lints: _ } = item; try_visit!(visitor.visit_id(item.hir_id())); match *kind { ItemKind::ExternCrate(orig_name, ident) => { @@ -656,7 +656,8 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>( visitor: &mut V, foreign_item: &'v ForeignItem<'v>, ) -> V::Result { - let ForeignItem { ident, kind, owner_id: _, span: _, vis_span: _ } = foreign_item; + let ForeignItem { ident, kind, owner_id: _, span: _, vis_span: _, has_delayed_lints: _ } = + foreign_item; try_visit!(visitor.visit_id(foreign_item.hir_id())); try_visit!(visitor.visit_ident(*ident)); @@ -1205,7 +1206,15 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>( visitor: &mut V, trait_item: &'v TraitItem<'v>, ) -> V::Result { - let TraitItem { ident, generics, ref defaultness, ref kind, span, owner_id: _ } = *trait_item; + let TraitItem { + ident, + generics, + ref defaultness, + ref kind, + span, + owner_id: _, + has_delayed_lints: _, + } = *trait_item; let hir_id = trait_item.hir_id(); try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(&generics)); @@ -1261,6 +1270,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>( ref defaultness, span: _, vis_span: _, + has_delayed_lints: _, } = *impl_item; try_visit!(visitor.visit_ident(ident)); diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 21d36ed54cd..3a08e5ae336 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -165,6 +165,8 @@ pub fn extract(attrs: &[impl AttributeExt]) -> Option<(Symbol, Span)> { language_item_table! { // Variant name, Name, Getter method name, Target Generic requirements; Sized, sym::sized, sized_trait, Target::Trait, GenericRequirement::Exact(0); + MetaSized, sym::meta_sized, meta_sized_trait, Target::Trait, GenericRequirement::Exact(0); + PointeeSized, sym::pointee_sized, pointee_sized_trait, Target::Trait, GenericRequirement::Exact(0); Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1); /// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ"). StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None; diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index c6fe475b460..dafd31336fb 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -27,6 +27,7 @@ mod hir; pub mod hir_id; pub mod intravisit; pub mod lang_items; +pub mod lints; pub mod pat_util; mod stable_hash_impls; mod target; diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs new file mode 100644 index 00000000000..7be6c32e57e --- /dev/null +++ b/compiler/rustc_hir/src/lints.rs @@ -0,0 +1,23 @@ +use rustc_attr_data_structures::lints::AttributeLint; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_macros::HashStable_Generic; + +use crate::HirId; + +/// During ast lowering, no lints can be emitted. +/// That is because lints attach to nodes either in the AST, or on the built HIR. +/// When attached to AST nodes, they're emitted just before building HIR, +/// and then there's a gap where no lints can be emitted until HIR is done. +/// The variants in this enum represent lints that are temporarily stashed during +/// AST lowering to be emitted once HIR is built. +#[derive(Clone, Debug, HashStable_Generic)] +pub enum DelayedLint { + AttributeParsing(AttributeLint<HirId>), +} + +#[derive(Debug)] +pub struct DelayedLints { + pub lints: Box<[DelayedLint]>, + // Only present when the crate hash is needed. + pub opt_hash: Option<Fingerprint>, +} diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 91ea88cae47..6acf1524b60 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -6,6 +6,7 @@ use crate::hir::{ AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId, }; use crate::hir_id::{HirId, ItemLocalId}; +use crate::lints::DelayedLints; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro @@ -102,6 +103,13 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for OwnerNodes<' } } +impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for DelayedLints { + fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { + let DelayedLints { opt_hash, .. } = *self; + opt_hash.unwrap().hash_stable(hcx, hasher); + } +} + impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for AttributeMap<'tcx> { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { // We ignore the `map` since it refers to information included in `opt_hash` which is diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index 899370b34e4..5d6c49ee862 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -14,6 +14,7 @@ rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 4fcd9f8a646..f768bd157ab 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -1,3 +1,7 @@ +hir_analysis_abi_custom_clothed_function = + items with the `"custom"` ABI can only be declared externally or defined via naked functions + .suggestion = convert this to an `#[unsafe(naked)]` function + hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_ident}` in bounds of `{$qself}` .label = ambiguous associated {$assoc_kind} `{$assoc_ident}` @@ -595,7 +599,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 functions with the {$convention} calling convention are not supported .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 60ca0155bdd..32fec0604c0 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -21,7 +21,7 @@ use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::util::Discr; use rustc_middle::ty::{ - AdtDef, BottomUpFolder, GenericArgKind, RegionKind, TypeFoldable, TypeSuperVisitable, + AdtDef, BottomUpFolder, FnSig, GenericArgKind, RegionKind, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, fold_regions, }; use rustc_session::lint::builtin::UNINHABITED_STATIC; @@ -100,6 +100,18 @@ pub fn check_abi_fn_ptr(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Ex } } +pub fn check_custom_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, fn_sig: FnSig<'_>, fn_sig_span: Span) { + if fn_sig.abi == ExternAbi::Custom { + // Function definitions that use `extern "custom"` must be naked functions. + if !tcx.has_attr(def_id, sym::naked) { + tcx.dcx().emit_err(crate::errors::AbiCustomClothedFunction { + span: fn_sig_span, + naked_span: tcx.def_span(def_id).shrink_to_lo(), + }); + } + } +} + fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) { let def = tcx.adt_def(def_id); let span = tcx.def_span(def_id); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 481cdaa4c6c..060fc51b7bd 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -71,7 +71,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::box_new | sym::breakpoint | sym::size_of - | sym::min_align_of + | sym::align_of | sym::needs_drop | sym::caller_location | sym::add_with_overflow @@ -200,10 +200,8 @@ pub(crate) fn check_intrinsic_type( sym::abort => (0, 0, vec![], tcx.types.never), sym::unreachable => (0, 0, vec![], tcx.types.never), sym::breakpoint => (0, 0, vec![], tcx.types.unit), - sym::size_of | sym::pref_align_of | sym::min_align_of | sym::variant_count => { - (1, 0, vec![], tcx.types.usize) - } - sym::size_of_val | sym::min_align_of_val => { + sym::size_of | sym::align_of | sym::variant_count => (1, 0, vec![], tcx.types.usize), + sym::size_of_val | sym::align_of_val => { (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize) } sym::rustc_peek => (1, 0, vec![param(0)], param(0)), diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index fad8abf5fae..5b8aa28102c 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -72,10 +72,11 @@ pub mod wfcheck; use std::num::NonZero; -pub use check::{check_abi, check_abi_fn_ptr}; +pub use check::{check_abi, check_abi_fn_ptr, check_custom_abi}; use rustc_abi::{ExternAbi, VariantIdx}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err}; +use rustc_hir::LangItem; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_index::bit_set::DenseBitSet; @@ -331,7 +332,7 @@ fn bounds_from_generic_predicates<'tcx>( ty::ClauseKind::Trait(trait_predicate) => { let entry = types.entry(trait_predicate.self_ty()).or_default(); let def_id = trait_predicate.def_id(); - if !tcx.is_default_trait(def_id) { + if !tcx.is_default_trait(def_id) && !tcx.is_lang_item(def_id, LangItem::Sized) { // Do not add that restriction to the list if it is a positive requirement. entry.push(trait_predicate.def_id()); } diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 7cb31a64e13..c458878da15 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -10,6 +10,7 @@ use std::mem; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; +use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Arm, Block, Expr, LetStmt, Pat, PatKind, Stmt}; @@ -752,13 +753,19 @@ fn resolve_local<'tcx>( record_rvalue_scope_if_borrow_expr(visitor, arm.body, blk_id); } } - hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) => { - // FIXME(@dingxiangfei2009): choose call arguments here - // for candidacy for extended parameter rule application - } - hir::ExprKind::Index(..) => { - // FIXME(@dingxiangfei2009): select the indices - // as candidate for rvalue scope rules + hir::ExprKind::Call(func, args) => { + // Recurse into tuple constructors, such as `Some(&temp())`. + // + // That way, there is no difference between `Some(..)` and `Some { 0: .. }`, + // even though the former is syntactically a function call. + if let hir::ExprKind::Path(path) = &func.kind + && let hir::QPath::Resolved(None, path) = path + && let Res::SelfCtor(_) | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) = path.res + { + for arg in args { + record_rvalue_scope_if_borrow_expr(visitor, arg, blk_id); + } + } } _ => {} } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index b8dc01cbc03..20d0e87b7a7 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1258,6 +1258,11 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) -> Result<(), ErrorGuarant debug!(?item.owner_id); let def_id = item.owner_id.def_id; + if tcx.is_lang_item(def_id.into(), LangItem::PointeeSized) { + // `PointeeSized` is removed during lowering. + return Ok(()); + } + let trait_def = tcx.trait_def(def_id); if trait_def.is_marker || matches!(trait_def.specialization_kind, TraitSpecializationKind::Marker) diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index e1df0d60452..6e22ac5a28a 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -494,7 +494,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { // Only visit the type looking for `_` if we didn't fix the type above visitor.visit_ty_unambig(a); - self.lowerer().lower_arg_ty(a, None) + self.lowerer().lower_ty(a) }) .collect(); diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 5f1cdeddc19..53c44cdc411 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -44,6 +44,14 @@ fn associated_type_bounds<'tcx>( | PredicateFilter::SelfOnly | PredicateFilter::SelfTraitThatDefines(_) | PredicateFilter::SelfAndAssociatedTypeBounds => { + icx.lowerer().add_sizedness_bounds( + &mut bounds, + item_ty, + hir_bounds, + None, + None, + span, + ); icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span); } // `ConstIfConst` is only interested in `~const` bounds. @@ -333,6 +341,14 @@ fn opaque_type_bounds<'tcx>( | PredicateFilter::SelfOnly | PredicateFilter::SelfTraitThatDefines(_) | PredicateFilter::SelfAndAssociatedTypeBounds => { + icx.lowerer().add_sizedness_bounds( + &mut bounds, + item_ty, + hir_bounds, + None, + None, + span, + ); icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span); } //`ConstIfConst` is only interested in `~const` bounds. diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index ce0f83d0ec2..c337765c5fe 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -162,7 +162,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen .map(|t| ty::Binder::dummy(t.instantiate_identity())); } } - ItemKind::Trait(_, _, _, _, self_bounds, ..) | ItemKind::TraitAlias(_, _, self_bounds) => { is_trait = Some((self_bounds, item.span)); @@ -183,21 +182,29 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // and the explicit where-clauses, but to get the full set of predicates // on a trait we must also consider the bounds that follow the trait's name, // like `trait Foo: A + B + C`. - if let Some(self_bounds) = is_trait { + if let Some((self_bounds, span)) = is_trait { let mut bounds = Vec::new(); icx.lowerer().lower_bounds( tcx.types.self_param, - self_bounds.0, + self_bounds, &mut bounds, ty::List::empty(), PredicateFilter::All, ); + icx.lowerer().add_sizedness_bounds( + &mut bounds, + tcx.types.self_param, + self_bounds, + None, + Some(def_id), + span, + ); icx.lowerer().add_default_super_traits( def_id, &mut bounds, - self_bounds.0, + self_bounds, hir_generics, - self_bounds.1, + span, ); predicates.extend(bounds); } @@ -224,6 +231,14 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen let param_ty = icx.lowerer().lower_ty_param(param.hir_id); let mut bounds = Vec::new(); // Implicit bounds are added to type params unless a `?Trait` bound is found + icx.lowerer().add_sizedness_bounds( + &mut bounds, + param_ty, + &[], + Some((param.def_id, hir_generics.predicates)), + None, + param.span, + ); icx.lowerer().add_default_traits( &mut bounds, param_ty, diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index b907ec090e5..f5821aed03f 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -276,8 +276,7 @@ fn create_generic_args<'tcx>( tcx.impl_trait_header(parent).unwrap().trait_ref.instantiate_identity().args; let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id); - let method_args = - tcx.mk_args_from_iter(trait_args.iter().skip(callee_generics.parent_count)); + let method_args = tcx.mk_args(&trait_args[callee_generics.parent_count..]); let method_args = build_generic_args(tcx, sig_id, def_id, method_args); tcx.mk_args_from_iter(parent_args.iter().chain(method_args)) diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index a27d1ed6c53..8de2aec95a7 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -633,7 +633,7 @@ pub(crate) struct VariadicFunctionCompatibleConvention<'a> { #[primary_span] #[label] pub span: Span, - pub conventions: &'a str, + pub convention: &'a str, } #[derive(Diagnostic)] @@ -1698,3 +1698,17 @@ pub(crate) struct SelfInTypeAlias { #[label] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_abi_custom_clothed_function)] +pub(crate) struct AbiCustomClothedFunction { + #[primary_span] + pub span: Span, + #[suggestion( + hir_analysis_suggestion, + applicability = "maybe-incorrect", + code = "#[unsafe(naked)]\n", + style = "short" + )] + pub naked_span: 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 bdc42c7a2d9..ea1dfdfd806 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -4,15 +4,15 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; -use rustc_hir::AmbigArg; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{AmbigArg, LangItem, PolyTraitRef}; use rustc_middle::bug; use rustc_middle::ty::{ - self as ty, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, Upcast, + self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + TypeVisitor, Upcast, }; -use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw}; use rustc_trait_selection::traits; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -23,23 +23,213 @@ use crate::hir_ty_lowering::{ AssocItemQSelf, FeedConstTy, HirTyLowerer, PredicateFilter, RegionInferReason, }; +#[derive(Debug, Default)] +struct CollectedBound { + /// `Trait` + positive: bool, + /// `?Trait` + maybe: bool, + /// `!Trait` + negative: bool, +} + +impl CollectedBound { + /// Returns `true` if any of `Trait`, `?Trait` or `!Trait` were encountered. + fn any(&self) -> bool { + self.positive || self.maybe || self.negative + } +} + +#[derive(Debug)] +struct CollectedSizednessBounds { + // Collected `Sized` bounds + sized: CollectedBound, + // Collected `MetaSized` bounds + meta_sized: CollectedBound, + // Collected `PointeeSized` bounds + pointee_sized: CollectedBound, +} + +impl CollectedSizednessBounds { + /// Returns `true` if any of `Trait`, `?Trait` or `!Trait` were encountered for `Sized`, + /// `MetaSized` or `PointeeSized`. + fn any(&self) -> bool { + self.sized.any() || self.meta_sized.any() || self.pointee_sized.any() + } +} + +fn search_bounds_for<'tcx>( + hir_bounds: &'tcx [hir::GenericBound<'tcx>], + self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, + mut f: impl FnMut(&'tcx PolyTraitRef<'tcx>), +) { + let mut search_bounds = |hir_bounds: &'tcx [hir::GenericBound<'tcx>]| { + for hir_bound in hir_bounds { + let hir::GenericBound::Trait(ptr) = hir_bound else { + continue; + }; + + f(ptr) + } + }; + + search_bounds(hir_bounds); + if let Some((self_ty, where_clause)) = self_ty_where_predicates { + for clause in where_clause { + if let hir::WherePredicateKind::BoundPredicate(pred) = clause.kind + && pred.is_param_bound(self_ty.to_def_id()) + { + search_bounds(pred.bounds); + } + } + } +} + +fn collect_unbounds<'tcx>( + hir_bounds: &'tcx [hir::GenericBound<'tcx>], + self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, +) -> SmallVec<[&'tcx PolyTraitRef<'tcx>; 1]> { + let mut unbounds: SmallVec<[_; 1]> = SmallVec::new(); + search_bounds_for(hir_bounds, self_ty_where_predicates, |ptr| { + if matches!(ptr.modifiers.polarity, hir::BoundPolarity::Maybe(_)) { + unbounds.push(ptr); + } + }); + unbounds +} + +fn collect_bounds<'a, 'tcx>( + hir_bounds: &'a [hir::GenericBound<'tcx>], + self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, + target_did: DefId, +) -> CollectedBound { + let mut collect_into = CollectedBound::default(); + search_bounds_for(hir_bounds, self_ty_where_predicates, |ptr| { + if !matches!(ptr.trait_ref.path.res, Res::Def(DefKind::Trait, did) if did == target_did) { + return; + } + + match ptr.modifiers.polarity { + hir::BoundPolarity::Maybe(_) => collect_into.maybe = true, + hir::BoundPolarity::Negative(_) => collect_into.negative = true, + hir::BoundPolarity::Positive => collect_into.positive = true, + } + }); + collect_into +} + +fn collect_sizedness_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + hir_bounds: &'tcx [hir::GenericBound<'tcx>], + self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, + span: Span, +) -> CollectedSizednessBounds { + let sized_did = tcx.require_lang_item(LangItem::Sized, span); + let sized = collect_bounds(hir_bounds, self_ty_where_predicates, sized_did); + + let meta_sized_did = tcx.require_lang_item(LangItem::MetaSized, span); + let meta_sized = collect_bounds(hir_bounds, self_ty_where_predicates, meta_sized_did); + + let pointee_sized_did = tcx.require_lang_item(LangItem::PointeeSized, span); + let pointee_sized = collect_bounds(hir_bounds, self_ty_where_predicates, pointee_sized_did); + + CollectedSizednessBounds { sized, meta_sized, pointee_sized } +} + +/// Add a trait bound for `did`. +fn add_trait_bound<'tcx>( + tcx: TyCtxt<'tcx>, + bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, + self_ty: Ty<'tcx>, + did: DefId, + span: Span, +) { + let trait_ref = ty::TraitRef::new(tcx, did, [self_ty]); + // Preferable to put sizedness obligations first, since we report better errors for `Sized` + // ambiguity. + bounds.insert(0, (trait_ref.upcast(tcx), span)); +} + impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { - pub(crate) fn add_default_traits( + /// Skip `PointeeSized` bounds. + /// + /// `PointeeSized` is a "fake bound" insofar as anywhere a `PointeeSized` bound exists, there + /// is actually the absence of any bounds. This avoids limitations around non-global where + /// clauses being preferred over item bounds (where `PointeeSized` bounds would be + /// proven) - which can result in errors when a `PointeeSized` supertrait/bound/predicate is + /// added to some items. + pub(crate) fn should_skip_sizedness_bound<'hir>( + &self, + bound: &'hir hir::GenericBound<'tcx>, + ) -> bool { + bound + .trait_ref() + .and_then(|tr| tr.trait_def_id()) + .map(|did| self.tcx().is_lang_item(did, LangItem::PointeeSized)) + .unwrap_or(false) + } + + /// Adds sizedness bounds to a trait, trait alias, parameter, opaque type or associated type. + /// + /// - On parameters, opaque type and associated types, add default `Sized` bound if no explicit + /// sizedness bounds are present. + /// - On traits and trait aliases, add default `MetaSized` supertrait if no explicit sizedness + /// bounds are present. + /// - On parameters, opaque type, associated types and trait aliases, add a `MetaSized` bound if + /// a `?Sized` bound is present. + pub(crate) fn add_sizedness_bounds( &self, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, self_ty: Ty<'tcx>, - hir_bounds: &[hir::GenericBound<'tcx>], + hir_bounds: &'tcx [hir::GenericBound<'tcx>], self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, + trait_did: Option<LocalDefId>, span: Span, ) { - self.add_default_traits_with_filter( - bounds, - self_ty, - hir_bounds, - self_ty_where_predicates, - span, - |_| true, - ); + let tcx = self.tcx(); + + let meta_sized_did = tcx.require_lang_item(LangItem::MetaSized, span); + let pointee_sized_did = tcx.require_lang_item(LangItem::PointeeSized, span); + + // If adding sizedness bounds to a trait, then there are some relevant early exits + if let Some(trait_did) = trait_did { + let trait_did = trait_did.to_def_id(); + // Never add a default supertrait to `PointeeSized`. + if trait_did == pointee_sized_did { + return; + } + // Don't add default sizedness supertraits to auto traits because it isn't possible to + // relax an automatically added supertrait on the defn itself. + if tcx.trait_is_auto(trait_did) { + return; + } + } else { + // Report invalid unbounds on sizedness-bounded generic parameters. + let unbounds = collect_unbounds(hir_bounds, self_ty_where_predicates); + self.check_and_report_invalid_unbounds_on_param(unbounds); + } + + let collected = collect_sizedness_bounds(tcx, hir_bounds, self_ty_where_predicates, span); + if (collected.sized.maybe || collected.sized.negative) + && !collected.sized.positive + && !collected.meta_sized.any() + && !collected.pointee_sized.any() + { + // `?Sized` is equivalent to `MetaSized` (but only add the bound if there aren't any + // other explicit ones) - this can happen for trait aliases as well as bounds. + add_trait_bound(tcx, bounds, self_ty, meta_sized_did, span); + } else if !collected.any() { + if trait_did.is_some() { + // If there are no explicit sizedness bounds on a trait then add a default + // `MetaSized` supertrait. + add_trait_bound(tcx, bounds, self_ty, meta_sized_did, span); + } else { + // If there are no explicit sizedness bounds on a parameter then add a default + // `Sized` bound. + let sized_did = tcx.require_lang_item(LangItem::Sized, span); + add_trait_bound(tcx, bounds, self_ty, sized_did, span); + } + } } /// Checks whether `Self: DefaultAutoTrait` bounds should be added on trait super bounds @@ -146,13 +336,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if !self.requires_default_supertraits(trait_bounds, trait_generics) { let self_ty_where_predicates = (parent, trait_item.generics.predicates); - self.add_default_traits_with_filter( + self.add_default_traits( bounds, tcx.types.self_param, &[], Some(self_ty_where_predicates), trait_item.span, - |tr| tr != hir::LangItem::Sized, ); } } @@ -174,41 +363,37 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assert!(matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias)); if self.requires_default_supertraits(hir_bounds, hir_generics) { let self_ty_where_predicates = (trait_def_id, hir_generics.predicates); - self.add_default_traits_with_filter( + self.add_default_traits( bounds, self.tcx().types.self_param, hir_bounds, Some(self_ty_where_predicates), span, - |default_trait| default_trait != hir::LangItem::Sized, ); } } - pub(crate) fn add_default_traits_with_filter( + pub(crate) fn add_default_traits( &self, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, self_ty: Ty<'tcx>, hir_bounds: &[hir::GenericBound<'tcx>], self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, span: Span, - f: impl Fn(hir::LangItem) -> bool, ) { - self.tcx().default_traits().iter().filter(|&&default_trait| f(default_trait)).for_each( - |default_trait| { - self.add_default_trait( - *default_trait, - bounds, - self_ty, - hir_bounds, - self_ty_where_predicates, - span, - ); - }, - ); + self.tcx().default_traits().iter().for_each(|default_trait| { + self.add_default_trait( + *default_trait, + bounds, + self_ty, + hir_bounds, + self_ty_where_predicates, + span, + ); + }); } - /// Add a `Sized` or `experimental_default_bounds` bounds to the `bounds` if appropriate. + /// Add a `experimental_default_bounds` bound to the `bounds` if appropriate. /// /// Doesn't add the bound if the HIR bounds contain any of `Trait`, `?Trait` or `!Trait`. pub(crate) fn add_default_trait( @@ -220,7 +405,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, span: Span, ) { - let trait_id = self.tcx().lang_items().get(trait_); + let tcx = self.tcx(); + let trait_id = tcx.lang_items().get(trait_); if let Some(trait_id) = trait_id && self.do_not_provide_default_trait_bound( trait_id, @@ -228,11 +414,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty_where_predicates, ) { - // There was no `?Trait` or `!Trait` bound; - // add `Trait` if it's available. - let trait_ref = ty::TraitRef::new(self.tcx(), trait_id, [self_ty]); - // Preferable to put this obligation first, since we report better errors for sized ambiguity. - bounds.insert(0, (trait_ref.upcast(self.tcx()), span)); + add_trait_bound(tcx, bounds, self_ty, trait_id, span); } } @@ -242,90 +424,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_bounds: &'a [hir::GenericBound<'tcx>], self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, ) -> bool { - let tcx = self.tcx(); - let mut seen_negative_bound = false; - let mut seen_positive_bound = false; - - // Try to find an unbound in bounds. - let mut unbounds: SmallVec<[_; 1]> = SmallVec::new(); - let mut search_bounds = |hir_bounds: &'a [hir::GenericBound<'tcx>]| { - for hir_bound in hir_bounds { - let hir::GenericBound::Trait(ptr) = hir_bound else { - continue; - }; - match ptr.modifiers.polarity { - hir::BoundPolarity::Maybe(_) => unbounds.push(ptr), - hir::BoundPolarity::Negative(_) => { - if ptr.trait_ref.path.res == Res::Def(DefKind::Trait, trait_def_id) { - seen_negative_bound = true; - } - } - hir::BoundPolarity::Positive => { - if ptr.trait_ref.path.res == Res::Def(DefKind::Trait, trait_def_id) { - seen_positive_bound = true; - } - } - } - } - }; - search_bounds(hir_bounds); - if let Some((self_ty, where_clause)) = self_ty_where_predicates { - for clause in where_clause { - if let hir::WherePredicateKind::BoundPredicate(pred) = clause.kind - && pred.is_param_bound(self_ty.to_def_id()) - { - search_bounds(pred.bounds); - } - } - } - - let mut unique_bounds = FxIndexSet::default(); - let mut seen_repeat = false; - for unbound in &unbounds { - if let Res::Def(DefKind::Trait, unbound_def_id) = unbound.trait_ref.path.res { - seen_repeat |= !unique_bounds.insert(unbound_def_id); - } - } - if unbounds.len() > 1 { - let err = errors::MultipleRelaxedDefaultBounds { - spans: unbounds.iter().map(|ptr| ptr.span).collect(), - }; - if seen_repeat { - self.dcx().emit_err(err); - } else if !tcx.features().more_maybe_bounds() { - self.tcx().sess.create_feature_err(err, sym::more_maybe_bounds).emit(); - }; - } - - let mut seen_unbound = false; - for unbound in unbounds { - let unbound_def_id = unbound.trait_ref.trait_def_id(); - if unbound_def_id == Some(trait_def_id) { - seen_unbound = true; - } - let emit_relax_err = || { - let unbound_traits = match tcx.sess.opts.unstable_opts.experimental_default_bounds { - true => "`?Sized` and `experimental_default_bounds`", - false => "`?Sized`", - }; - // There was a `?Trait` bound, but it was neither `?Sized` nor `experimental_default_bounds`. - self.dcx().span_err( - unbound.span, - format!( - "relaxing a default bound only does something for {}; \ - all other traits are not bound by default", - unbound_traits - ), - ); - }; - match unbound_def_id { - Some(def_id) if !tcx.is_default_trait(def_id) => emit_relax_err(), - None => emit_relax_err(), - _ => {} - } - } - - !(seen_unbound || seen_negative_bound || seen_positive_bound) + let collected = collect_bounds(hir_bounds, self_ty_where_predicates, trait_def_id); + !collected.any() } /// Lower HIR bounds into `bounds` given the self type `param_ty` and the overarching late-bound vars if any. @@ -361,6 +461,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { 'tcx: 'hir, { for hir_bound in hir_bounds { + if self.should_skip_sizedness_bound(hir_bound) { + continue; + } + // In order to avoid cycles, when we're lowering `SelfTraitThatDefines`, // we skip over any traits that don't define the given associated type. if let PredicateFilter::SelfTraitThatDefines(assoc_ident) = predicate_filter { @@ -927,7 +1031,7 @@ struct GenericParamAndBoundVarCollector<'a, 'tcx> { impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'_, 'tcx> { type Result = ControlFlow<ErrorGuaranteed>; - fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>( + fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>( &mut self, binder: &ty::Binder<'tcx, T>, ) -> Self::Result { 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 c7cdf1d5bd2..05465b47a26 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 @@ -61,14 +61,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let ast_bounds: Vec<_> = hir_bounds.iter().map(|&trait_ref| hir::GenericBound::Trait(trait_ref)).collect(); - self.add_default_traits_with_filter( - &mut user_written_bounds, - dummy_self, - &ast_bounds, - None, - span, - |tr| tr != hir::LangItem::Sized, - ); + self.add_default_traits(&mut user_written_bounds, dummy_self, &ast_bounds, None, span); let (elaborated_trait_bounds, elaborated_projection_bounds) = traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied()); 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 45fee0fa402..1cda6dff21e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -8,7 +8,7 @@ use rustc_errors::{ }; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, HirId}; +use rustc_hir::{self as hir, HirId, LangItem, PolyTraitRef}; use rustc_middle::bug; use rustc_middle::ty::fast_reject::{TreatParams, simplify_type}; use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _}; @@ -34,6 +34,57 @@ use crate::fluent_generated as fluent; use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer}; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { + /// Check for multiple relaxed default bounds and relaxed bounds of non-sizedness traits. + pub(crate) fn check_and_report_invalid_unbounds_on_param( + &self, + unbounds: SmallVec<[&PolyTraitRef<'_>; 1]>, + ) { + let tcx = self.tcx(); + + let sized_did = tcx.require_lang_item(LangItem::Sized, DUMMY_SP); + + let mut unique_bounds = FxIndexSet::default(); + let mut seen_repeat = false; + for unbound in &unbounds { + if let Res::Def(DefKind::Trait, unbound_def_id) = unbound.trait_ref.path.res { + seen_repeat |= !unique_bounds.insert(unbound_def_id); + } + } + + if unbounds.len() > 1 { + let err = errors::MultipleRelaxedDefaultBounds { + spans: unbounds.iter().map(|ptr| ptr.span).collect(), + }; + + if seen_repeat { + tcx.dcx().emit_err(err); + } else if !tcx.features().more_maybe_bounds() { + tcx.sess.create_feature_err(err, sym::more_maybe_bounds).emit(); + }; + } + + for unbound in unbounds { + if let Res::Def(DefKind::Trait, did) = unbound.trait_ref.path.res + && ((did == sized_did) || tcx.is_default_trait(did)) + { + continue; + } + + let unbound_traits = match tcx.sess.opts.unstable_opts.experimental_default_bounds { + true => "`?Sized` and `experimental_default_bounds`", + false => "`?Sized`", + }; + self.dcx().span_err( + unbound.span, + format!( + "relaxing a default bound only does something for {}; all other traits are \ + not bound by default", + unbound_traits + ), + ); + } + } + /// On missing type parameters, emit an E0393 error and provide a structured suggestion using /// the type parameter's name as a placeholder. pub(crate) fn report_missing_type_params( 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 4deb47dfff8..bf407cbaccb 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2708,16 +2708,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - pub fn lower_arg_ty(&self, ty: &hir::Ty<'tcx>, expected_ty: Option<Ty<'tcx>>) -> Ty<'tcx> { - match ty.kind { - hir::TyKind::Infer(()) if let Some(expected_ty) = expected_ty => { - self.record_ty(ty.hir_id, expected_ty, ty.span); - expected_ty - } - _ => self.lower_ty(ty), - } - } - /// Lower a function type from the HIR to our internal notion of a function signature. #[instrument(level = "debug", skip(self, hir_id, safety, abi, decl, generics, hir_ty), ret)] pub fn lower_fn_ty( diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index a92ee89186c..7c8c9425a03 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -92,8 +92,9 @@ mod variance; pub use errors::NoVariantNamed; use rustc_abi::ExternAbi; -use rustc_hir as hir; use rustc_hir::def::DefKind; +use rustc_hir::lints::DelayedLint; +use rustc_hir::{self as hir}; use rustc_middle::middle; use rustc_middle::mir::interpret::GlobalId; use rustc_middle::query::Providers; @@ -114,12 +115,6 @@ 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"; - // ABIs which can stably use varargs if !decl.c_variadic || matches!(abi, ExternAbi::C { .. } | ExternAbi::Cdecl { .. }) { return; @@ -139,20 +134,18 @@ fn require_c_abi_if_c_variadic( // Looks like we need to pick an error to emit. // Is there any feature which we could have enabled to make this work? + let unstable_explain = + format!("C-variadic functions with the {abi} calling convention are unstable"); match abi { ExternAbi::System { .. } => { - feature_err(&tcx.sess, sym::extern_system_varargs, span, UNSTABLE_EXPLAIN) + feature_err(&tcx.sess, sym::extern_system_varargs, span, unstable_explain) } abi if abi.supports_varargs() => { - feature_err(&tcx.sess, sym::extended_varargs_abi_support, span, UNSTABLE_EXPLAIN) + feature_err(&tcx.sess, sym::extended_varargs_abi_support, span, unstable_explain) } _ => tcx.dcx().create_err(errors::VariadicFunctionCompatibleConvention { span, - conventions: if tcx.sess.opts.unstable_features.is_nightly_build() { - CONVENTIONS_UNSTABLE - } else { - CONVENTIONS_STABLE - }, + convention: &format!("{abi}"), }), } .emit(); @@ -174,6 +167,14 @@ pub fn provide(providers: &mut Providers) { }; } +fn emit_delayed_lint(lint: &DelayedLint, tcx: TyCtxt<'_>) { + match lint { + DelayedLint::AttributeParsing(attribute_lint) => { + rustc_attr_parsing::emit_attribute_lint(attribute_lint, tcx) + } + } +} + pub fn check_crate(tcx: TyCtxt<'_>) { let _prof_timer = tcx.sess.timer("type_check_crate"); @@ -192,6 +193,42 @@ pub fn check_crate(tcx: TyCtxt<'_>) { let _: R = tcx.ensure_ok().crate_inherent_impls_overlap_check(()); }); + tcx.sess.time("emit_ast_lowering_delayed_lints", || { + // sanity check in debug mode that all lints are really noticed + // and we really will emit them all in the loop right below. + // + // during ast lowering, when creating items, foreign items, trait items and impl items + // we store in them whether they have any lints in their owner node that should be + // picked up by `hir_crate_items`. However, theoretically code can run between that + // boolean being inserted into the item and the owner node being created. + // We don't want any new lints to be emitted there + // (though honestly, you have to really try to manage to do that but still), + // but this check is there to catch that. + #[cfg(debug_assertions)] + { + // iterate over all owners + for owner_id in tcx.hir_crate_items(()).owners() { + // if it has delayed lints + if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) { + if !delayed_lints.lints.is_empty() { + // assert that delayed_lint_items also picked up this item to have lints + assert!( + tcx.hir_crate_items(()).delayed_lint_items().any(|i| i == owner_id) + ); + } + } + } + } + + for owner_id in tcx.hir_crate_items(()).delayed_lint_items() { + if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) { + for lint in &delayed_lints.lints { + emit_delayed_lint(lint, tcx); + } + } + } + }); + tcx.par_hir_body_owners(|item_def_id| { let def_kind = tcx.def_kind(item_def_id); // Make sure we evaluate all static and (non-associated) const items, even if unused. diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index b23b3125c59..fc507285860 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -80,6 +80,13 @@ impl<'a> State<'a> { (self.attrs)(id) } + fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence { + let for_each_attr = |id: HirId, callback: &mut dyn FnMut(&hir::Attribute)| { + self.attrs(id).iter().for_each(callback); + }; + expr.precedence(&for_each_attr) + } + fn print_attrs_as_inner(&mut self, attrs: &[hir::Attribute]) { self.print_either_attributes(attrs, ast::AttrStyle::Inner) } @@ -1164,7 +1171,7 @@ impl<'a> State<'a> { } self.space(); self.word_space("="); - let npals = || parser::needs_par_as_let_scrutinee(init.precedence()); + let npals = || parser::needs_par_as_let_scrutinee(self.precedence(init)); self.print_expr_cond_paren(init, Self::cond_needs_par(init) || npals()) } @@ -1265,7 +1272,7 @@ impl<'a> State<'a> { fn print_expr_call(&mut self, func: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let needs_paren = match func.kind { hir::ExprKind::Field(..) => true, - _ => func.precedence() < ExprPrecedence::Unambiguous, + _ => self.precedence(func) < ExprPrecedence::Unambiguous, }; self.print_expr_cond_paren(func, needs_paren); @@ -1279,7 +1286,10 @@ impl<'a> State<'a> { args: &[hir::Expr<'_>], ) { let base_args = args; - self.print_expr_cond_paren(receiver, receiver.precedence() < ExprPrecedence::Unambiguous); + self.print_expr_cond_paren( + receiver, + self.precedence(receiver) < ExprPrecedence::Unambiguous, + ); self.word("."); self.print_ident(segment.ident); @@ -1293,8 +1303,8 @@ impl<'a> State<'a> { fn print_expr_binary(&mut self, op: hir::BinOpKind, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { let binop_prec = op.precedence(); - let left_prec = lhs.precedence(); - let right_prec = rhs.precedence(); + let left_prec = self.precedence(lhs); + let right_prec = self.precedence(rhs); let (mut left_needs_paren, right_needs_paren) = match op.fixity() { Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec), @@ -1323,7 +1333,7 @@ impl<'a> State<'a> { fn print_expr_unary(&mut self, op: hir::UnOp, expr: &hir::Expr<'_>) { self.word(op.as_str()); - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Prefix); + self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Prefix); } fn print_expr_addr_of( @@ -1340,7 +1350,7 @@ impl<'a> State<'a> { self.print_mutability(mutability, true); } } - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Prefix); + self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Prefix); } fn print_literal(&mut self, lit: &hir::Lit) { @@ -1483,7 +1493,7 @@ impl<'a> State<'a> { self.print_literal(lit); } hir::ExprKind::Cast(expr, ty) => { - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Cast); + self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Cast); self.space(); self.word_space("as"); self.print_type(ty); @@ -1580,24 +1590,30 @@ impl<'a> State<'a> { self.print_block(blk, cb, ib); } hir::ExprKind::Assign(lhs, rhs, _) => { - self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign); + self.print_expr_cond_paren(lhs, self.precedence(lhs) <= ExprPrecedence::Assign); self.space(); self.word_space("="); - self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign); + self.print_expr_cond_paren(rhs, self.precedence(rhs) < ExprPrecedence::Assign); } hir::ExprKind::AssignOp(op, lhs, rhs) => { - self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign); + self.print_expr_cond_paren(lhs, self.precedence(lhs) <= ExprPrecedence::Assign); self.space(); self.word_space(op.node.as_str()); - self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign); + self.print_expr_cond_paren(rhs, self.precedence(rhs) < ExprPrecedence::Assign); } hir::ExprKind::Field(expr, ident) => { - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Unambiguous); + self.print_expr_cond_paren( + expr, + self.precedence(expr) < ExprPrecedence::Unambiguous, + ); self.word("."); self.print_ident(ident); } hir::ExprKind::Index(expr, index, _) => { - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Unambiguous); + self.print_expr_cond_paren( + expr, + self.precedence(expr) < ExprPrecedence::Unambiguous, + ); self.word("["); self.print_expr(index); self.word("]"); @@ -1611,7 +1627,7 @@ impl<'a> State<'a> { } if let Some(expr) = opt_expr { self.space(); - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump); + self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Jump); } } hir::ExprKind::Continue(destination) => { @@ -1625,13 +1641,13 @@ impl<'a> State<'a> { self.word("return"); if let Some(expr) = result { self.word(" "); - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump); + self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Jump); } } hir::ExprKind::Become(result) => { self.word("become"); self.word(" "); - self.print_expr_cond_paren(result, result.precedence() < ExprPrecedence::Jump); + self.print_expr_cond_paren(result, self.precedence(result) < ExprPrecedence::Jump); } hir::ExprKind::InlineAsm(asm) => { self.word("asm!"); @@ -1669,7 +1685,7 @@ impl<'a> State<'a> { } hir::ExprKind::Yield(expr, _) => { self.word_space("yield"); - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump); + self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Jump); } hir::ExprKind::Err(_) => { self.popen(); diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 3bdd1b48666..ac7ff65528d 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -1,3 +1,7 @@ +hir_typeck_abi_custom_call = + functions with the `"custom"` ABI cannot be called + .note = an `extern "custom"` function can only be called from within inline assembly + hir_typeck_add_missing_parentheses_in_range = you must surround the range in parentheses to call its `{$func_name}` function hir_typeck_add_return_type_add = try adding a return type diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index d173fe7c2c2..80bff09d0a4 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -1,5 +1,6 @@ use std::iter; +use rustc_abi::ExternAbi; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey}; use rustc_hir::def::{self, CtorKind, Namespace, Res}; @@ -83,6 +84,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { while result.is_none() && autoderef.next().is_some() { result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef); } + self.check_call_custom_abi(autoderef.final_ty(false), call_expr.span); self.register_predicates(autoderef.into_obligations()); let output = match result { @@ -135,6 +137,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { output } + /// Functions of type `extern "custom" fn(/* ... */)` cannot be called using `ExprKind::Call`. + /// + /// These functions have a calling convention that is unknown to rust, hence it cannot generate + /// code for the call. The only way to execute such a function is via inline assembly. + fn check_call_custom_abi(&self, callee_ty: Ty<'tcx>, span: Span) { + let abi = match callee_ty.kind() { + ty::FnDef(def_id, _) => self.tcx.fn_sig(def_id).skip_binder().skip_binder().abi, + ty::FnPtr(_, header) => header.abi, + _ => return, + }; + + if let ExternAbi::Custom = abi { + self.tcx.dcx().emit_err(errors::AbiCustomCall { span }); + } + } + #[instrument(level = "debug", skip(self, call_expr, callee_expr, arg_exprs, autoderef), ret)] fn try_overloaded_call_step( &self, @@ -602,7 +620,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Ok(rest_snippet) = rest_snippet { - let sugg = if callee_expr.precedence() >= ExprPrecedence::Unambiguous { + let sugg = if self.precedence(callee_expr) >= ExprPrecedence::Unambiguous { vec![ (up_to_rcvr_span, "".to_string()), (rest_span, format!(".{}({rest_snippet}", segment.ident)), diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index e17cfc15a43..6f8abc1e67d 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -1100,7 +1100,7 @@ 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(); + let expr_prec = fcx.precedence(self.expr); let needs_parens = expr_prec < ExprPrecedence::Unambiguous; let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize)); diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index d9fa56fefeb..24092c01125 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1662,9 +1662,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { blk_id, expression, ); - if !fcx.tcx.features().unsized_locals() { - unsized_return = self.is_return_ty_definitely_unsized(fcx); - } + unsized_return = self.is_return_ty_definitely_unsized(fcx); } ObligationCauseCode::ReturnValue(return_expr_id) => { err = self.report_return_mismatched_types( @@ -1676,9 +1674,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { return_expr_id, expression, ); - if !fcx.tcx.features().unsized_locals() { - unsized_return = self.is_return_ty_definitely_unsized(fcx); - } + unsized_return = self.is_return_ty_definitely_unsized(fcx); } ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { arm_span, diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 774815015d5..abb8cdc1cdf 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -1163,3 +1163,10 @@ pub(crate) struct NakedFunctionsMustNakedAsm { #[label] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(hir_typeck_abi_custom_call)] +pub(crate) struct AbiCustomCall { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index dfc7935d02b..55c39d960e7 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -5,7 +5,8 @@ //! //! See [`rustc_hir_analysis::check`] for more context on type checking in general. -use rustc_abi::{FIRST_VARIANT, FieldIdx}; +use rustc_abi::{ExternAbi, FIRST_VARIANT, FieldIdx}; +use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::unord::UnordMap; @@ -17,7 +18,7 @@ use rustc_errors::{ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, HirId, QPath}; +use rustc_hir::{Attribute, ExprKind, HirId, QPath}; use rustc_hir_analysis::NoVariantNamed; use rustc_hir_analysis::hir_ty_lowering::{FeedConstTy, HirTyLowerer as _}; use rustc_infer::infer; @@ -54,6 +55,30 @@ use crate::{ }; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub(crate) fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence { + let for_each_attr = |id: HirId, callback: &mut dyn FnMut(&Attribute)| { + for attr in self.tcx.hir_attrs(id) { + // For the purpose of rendering suggestions, disregard attributes + // that originate from desugaring of any kind. For example, `x?` + // desugars to `#[allow(unreachable_code)] match ...`. Failing to + // ignore the prefix attribute in the desugaring would cause this + // suggestion: + // + // let y: u32 = x?.try_into().unwrap(); + // ++++++++++++++++++++ + // + // to be rendered as: + // + // let y: u32 = (x?).try_into().unwrap(); + // + +++++++++++++++++++++ + if attr.span().desugaring_kind().is_none() { + callback(attr); + } + } + }; + expr.precedence(&for_each_attr) + } + /// Check an expr with an expectation type, and also demand that the expr's /// evaluated type is a subtype of the expectation at the end. This is a /// *hard* requirement. @@ -809,9 +834,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } - // Here we want to prevent struct constructors from returning unsized types. - // There were two cases this happened: fn pointer coercion in stable - // and usual function call in presence of unsized_locals. + // Here we want to prevent struct constructors from returning unsized types, + // which can happen with fn pointer coercion on stable. // Also, as we just want to check sizedness, instead of introducing // placeholder lifetimes with probing, we just replace higher lifetimes // with fresh vars. @@ -1627,6 +1651,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(method.def_id), ); + // Functions of type `extern "custom" fn(/* ... */)` cannot be called using + // `ExprKind::MethodCall`. These functions have a calling convention that is + // unknown to rust, hence it cannot generate code for the call. The only way + // to execute such a function is via inline assembly. + if let ExternAbi::Custom = method.sig.abi { + self.tcx.dcx().emit_err(crate::errors::AbiCustomCall { span: expr.span }); + } + method.sig.output() } Err(error) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index a43449a8f99..2df19cb21d5 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -22,8 +22,9 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, Ty, TyCtxt, - TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, UserSelfTy, + self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, + SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, + UserSelfTy, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -439,7 +440,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || {}, ); // Sized types have static alignment, and so do slices. - if tail.is_trivially_sized(self.tcx) || matches!(tail.kind(), ty::Slice(..)) { + if tail.has_trivial_sizedness(self.tcx, SizedTraitKind::Sized) + || matches!(tail.kind(), ty::Slice(..)) + { // Nothing else is required here. } else { // We can't be sure, let's required full `Sized`. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 393556928af..e979798a402 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -382,7 +382,7 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { _hir_id: rustc_hir::HirId, _hir_ty: Option<&hir::Ty<'_>>, ) -> (Vec<Ty<'tcx>>, Ty<'tcx>) { - let input_tys = decl.inputs.iter().map(|a| self.lowerer().lower_arg_ty(a, None)).collect(); + let input_tys = decl.inputs.iter().map(|a| self.lowerer().lower_ty(a)).collect(); let output_ty = match decl.output { hir::FnRetTy::Return(output) => self.lowerer().lower_ty(output), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 66af085cfd4..7e5f1d97a8b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -399,7 +399,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() < ExprPrecedence::Unambiguous { + } else if self.precedence(expr) < ExprPrecedence::Unambiguous { vec![ (expr.span.shrink_to_lo(), "(".to_string()), (expr.span.shrink_to_hi(), format!(").{}()", conversion_method_name)), @@ -1395,7 +1395,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let span = expr.span.find_oldest_ancestor_in_same_ctxt(); - let mut sugg = if expr.precedence() >= ExprPrecedence::Unambiguous { + let mut sugg = if self.precedence(expr) >= ExprPrecedence::Unambiguous { vec![(span.shrink_to_hi(), ".into()".to_owned())] } else { vec![ @@ -3106,7 +3106,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() < ExprPrecedence::Unambiguous { + let close_paren = if self.precedence(expr) < ExprPrecedence::Unambiguous { sugg.push((expr.span.shrink_to_lo(), "(".to_string())); ")" } else { @@ -3131,7 +3131,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let len = src.trim_end_matches(&checked_ty.to_string()).len(); span.with_lo(span.lo() + BytePos(len as u32)) }, - if expr.precedence() < ExprPrecedence::Unambiguous { + if self.precedence(expr) < ExprPrecedence::Unambiguous { // Readd `)` format!("{expected_ty})") } else { diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 7d99b0e7869..f1d6476a0f3 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -202,7 +202,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { ), ); } - } else if !self.fcx.tcx.features().unsized_locals() { + } else { self.fcx.require_type_is_sized( var_ty, p.span, diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index a45a7715340..043a687914b 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -48,7 +48,7 @@ use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_e use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{HirId, HirIdMap, Node}; -use rustc_hir_analysis::check::check_abi; +use rustc_hir_analysis::check::{check_abi, check_custom_abi}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; use rustc_middle::query::Providers; @@ -138,7 +138,7 @@ fn typeck_with_inspect<'tcx>( // for visit the asm expr of the body. let ty = fcx.check_expr(body.value); fcx.write_ty(id, ty); - } else if let Some(hir::FnSig { header, decl, .. }) = node.fn_sig() { + } else if let Some(hir::FnSig { header, decl, span: fn_sig_span }) = node.fn_sig() { let fn_sig = if decl.output.is_suggestable_infer_ty().is_some() { // In the case that we're recovering `fn() -> W<_>` or some other return // type that has an infer in it, lower the type directly so that it'll @@ -150,6 +150,8 @@ fn typeck_with_inspect<'tcx>( }; check_abi(tcx, id, span, fn_sig.abi()); + check_custom_abi(tcx, def_id, fn_sig.skip_binder(), *fn_sig_span); + loops::check(tcx, def_id, body); // Compute the function signature from point of view of inside the fn. diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 2fac13b7201..288d915e85c 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -14,7 +14,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag, MultiSpan, StashKey, pluralize, struct_span_code_err}; +use rustc_errors::{ + Applicability, Diag, DiagStyledString, MultiSpan, StashKey, pluralize, struct_span_code_err, +}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; @@ -722,7 +724,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let def_path = tcx.def_path_str(adt_def.did()); err.span_suggestion( - ty.span.to(item_ident.span), + sugg_span, format!("to construct a value of type `{}`, use the explicit path", def_path), def_path, Applicability::MachineApplicable, @@ -1569,7 +1571,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params || suggested_derive { + if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() + || restrict_type_params + || suggested_derive + || self.lookup_alternative_tuple_impls(&mut err, &unsatisfied_predicates) + { } else { self.suggest_traits_to_import( &mut err, @@ -1744,6 +1750,119 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit() } + /// If the predicate failure is caused by an unmet bound on a tuple, recheck if the bound would + /// succeed if all the types on the tuple had no borrows. This is a common problem for libraries + /// like Bevy and ORMs, which rely heavily on traits being implemented on tuples. + fn lookup_alternative_tuple_impls( + &self, + err: &mut Diag<'_>, + unsatisfied_predicates: &[( + ty::Predicate<'tcx>, + Option<ty::Predicate<'tcx>>, + Option<ObligationCause<'tcx>>, + )], + ) -> bool { + let mut found_tuple = false; + for (pred, root, _ob) in unsatisfied_predicates { + let mut preds = vec![pred]; + if let Some(root) = root { + // We will look at both the current predicate and the root predicate that caused it + // to be needed. If calling something like `<(A, &B)>::default()`, then `pred` is + // `&B: Default` and `root` is `(A, &B): Default`, which is the one we are checking + // for further down, so we check both. + preds.push(root); + } + for pred in preds { + if let Some(clause) = pred.as_clause() + && let Some(clause) = clause.as_trait_clause() + && let ty = clause.self_ty().skip_binder() + && let ty::Tuple(types) = ty.kind() + { + let path = clause.skip_binder().trait_ref.print_only_trait_path(); + let def_id = clause.def_id(); + let ty = Ty::new_tup( + self.tcx, + self.tcx.mk_type_list_from_iter(types.iter().map(|ty| ty.peel_refs())), + ); + let args = ty::GenericArgs::for_item(self.tcx, def_id, |param, _| { + if param.index == 0 { + ty.into() + } else { + self.infcx.var_for_def(DUMMY_SP, param) + } + }); + if self + .infcx + .type_implements_trait(def_id, args, self.param_env) + .must_apply_modulo_regions() + { + // "`Trait` is implemented for `(A, B)` but not for `(A, &B)`" + let mut msg = DiagStyledString::normal(format!("`{path}` ")); + msg.push_highlighted("is"); + msg.push_normal(" implemented for `("); + let len = types.len(); + for (i, t) in types.iter().enumerate() { + msg.push( + format!("{}", with_forced_trimmed_paths!(t.peel_refs())), + t.peel_refs() != t, + ); + if i < len - 1 { + msg.push_normal(", "); + } + } + msg.push_normal(")` but "); + msg.push_highlighted("not"); + msg.push_normal(" for `("); + for (i, t) in types.iter().enumerate() { + msg.push( + format!("{}", with_forced_trimmed_paths!(t)), + t.peel_refs() != t, + ); + if i < len - 1 { + msg.push_normal(", "); + } + } + msg.push_normal(")`"); + + // Find the span corresponding to the impl that was found to point at it. + if let Some(impl_span) = self + .tcx + .all_impls(def_id) + .filter(|&impl_def_id| { + let header = self.tcx.impl_trait_header(impl_def_id).unwrap(); + let trait_ref = header.trait_ref.instantiate( + self.tcx, + self.infcx.fresh_args_for_item(DUMMY_SP, impl_def_id), + ); + + let value = ty::fold_regions(self.tcx, ty, |_, _| { + self.tcx.lifetimes.re_erased + }); + // FIXME: Don't bother dealing with non-lifetime binders here... + if value.has_escaping_bound_vars() { + return false; + } + self.infcx.can_eq(ty::ParamEnv::empty(), trait_ref.self_ty(), value) + && header.polarity == ty::ImplPolarity::Positive + }) + .map(|impl_def_id| self.tcx.def_span(impl_def_id)) + .next() + { + err.highlighted_span_note(impl_span, msg.0); + } else { + err.highlighted_note(msg.0); + } + found_tuple = true; + } + // If `pred` was already on the tuple, we don't need to look at the root + // obligation too. + break; + } + } + } + found_tuple + } + /// If an appropriate error source is not found, check method chain for possible candidates fn lookup_segments_chain_for_no_match_method( &self, diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 5b5253c7e7e..982cfa2e42b 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -492,7 +492,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let final_upvar_tys = self.final_upvar_tys(closure_def_id); debug!(?closure_hir_id, ?args, ?final_upvar_tys); - if self.tcx.features().unsized_locals() || self.tcx.features().unsized_fn_params() { + if self.tcx.features().unsized_fn_params() { for capture in self.typeck_results.borrow().closure_min_captures_flattened(closure_def_id) { diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 550707ed4bc..18cee03ba2e 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -16,6 +16,7 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(not(bootstrap), allow(rustc::direct_use_of_rustc_type_ir))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 99520a3fea3..02d1ebdb31a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -8,9 +8,9 @@ use std::{env, fs, iter}; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::jobserver::Proxy; -use rustc_data_structures::parallel; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal}; +use rustc_data_structures::{parallel, thousands}; use rustc_expand::base::{ExtCtxt, LintStoreExpand}; use rustc_feature::Features; use rustc_fs_util::try_canonicalize; @@ -18,6 +18,7 @@ use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap}; use rustc_hir::definitions::Definitions; use rustc_incremental::setup_dep_graph; use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore, unerased_lint_store}; +use rustc_metadata::EncodedMetadata; use rustc_metadata::creader::CStore; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepsType; @@ -35,7 +36,8 @@ use rustc_session::parse::feature_err; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; use rustc_span::{ - DUMMY_SP, ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym, + DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, + Symbol, sym, }; use rustc_target::spec::PanicStrategy; use rustc_trait_selection::traits; @@ -205,7 +207,7 @@ fn configure_and_expand( // Expand macros now! let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate)); - // The rest is error reporting + // The rest is error reporting and stats sess.psess.buffered_lints.with_lock(|buffered_lints: &mut Vec<BufferedEarlyLint>| { buffered_lints.append(&mut ecx.buffered_early_lint); @@ -228,6 +230,10 @@ fn configure_and_expand( } } + if ecx.sess.opts.unstable_opts.macro_stats { + print_macro_stats(&ecx); + } + krate }); @@ -288,6 +294,76 @@ fn configure_and_expand( krate } +fn print_macro_stats(ecx: &ExtCtxt<'_>) { + use std::fmt::Write; + + // No instability because we immediately sort the produced vector. + #[allow(rustc::potential_query_instability)] + let mut macro_stats: Vec<_> = ecx + .macro_stats + .iter() + .map(|((name, kind), stat)| { + // This gives the desired sort order: sort by bytes, then lines, etc. + (stat.bytes, stat.lines, stat.uses, name, *kind) + }) + .collect(); + macro_stats.sort_unstable(); + macro_stats.reverse(); // bigger items first + + let prefix = "macro-stats"; + let name_w = 32; + let uses_w = 7; + let lines_w = 11; + let avg_lines_w = 11; + let bytes_w = 11; + let avg_bytes_w = 11; + let banner_w = name_w + uses_w + lines_w + avg_lines_w + bytes_w + avg_bytes_w; + + // We write all the text into a string and print it with a single + // `eprint!`. This is an attempt to minimize interleaved text if multiple + // rustc processes are printing macro-stats at the same time (e.g. with + // `RUSTFLAGS='-Zmacro-stats' cargo build`). It still doesn't guarantee + // non-interleaving, though. + let mut s = String::new(); + _ = writeln!(s, "{prefix} {}", "=".repeat(banner_w)); + _ = writeln!(s, "{prefix} MACRO EXPANSION STATS: {}", ecx.ecfg.crate_name); + _ = writeln!( + s, + "{prefix} {:<name_w$}{:>uses_w$}{:>lines_w$}{:>avg_lines_w$}{:>bytes_w$}{:>avg_bytes_w$}", + "Macro Name", "Uses", "Lines", "Avg Lines", "Bytes", "Avg Bytes", + ); + _ = writeln!(s, "{prefix} {}", "-".repeat(banner_w)); + // It's helpful to print something when there are no entries, otherwise it + // might look like something went wrong. + if macro_stats.is_empty() { + _ = writeln!(s, "{prefix} (none)"); + } + for (bytes, lines, uses, name, kind) in macro_stats { + let mut name = ExpnKind::Macro(kind, *name).descr(); + let avg_lines = lines as f64 / uses as f64; + let avg_bytes = bytes as f64 / uses as f64; + if name.len() >= name_w { + // If the name is long, print it on a line by itself, then + // set the name to empty and print things normally, to show the + // stats on the next line. + _ = writeln!(s, "{prefix} {:<name_w$}", name); + name = String::new(); + } + _ = writeln!( + s, + "{prefix} {:<name_w$}{:>uses_w$}{:>lines_w$}{:>avg_lines_w$}{:>bytes_w$}{:>avg_bytes_w$}", + name, + thousands::usize_with_underscores(uses), + thousands::isize_with_underscores(lines), + thousands::f64p1_with_underscores(avg_lines), + thousands::isize_with_underscores(bytes), + thousands::f64p1_with_underscores(avg_bytes), + ); + } + _ = writeln!(s, "{prefix} {}", "=".repeat(banner_w)); + eprint!("{s}"); +} + fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { let sess = tcx.sess; let (resolver, krate) = &*tcx.resolver_for_lowering().borrow(); @@ -1099,7 +1175,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) { pub(crate) fn start_codegen<'tcx>( codegen_backend: &dyn CodegenBackend, tcx: TyCtxt<'tcx>, -) -> Box<dyn Any> { +) -> (Box<dyn Any>, EncodedMetadata) { // Hook for tests. if let Some((def_id, _)) = tcx.entry_fn(()) && tcx.has_attr(def_id, sym::rustc_delayed_bug_from_inside_query) @@ -1122,11 +1198,9 @@ pub(crate) fn start_codegen<'tcx>( info!("Pre-codegen\n{:?}", tcx.debug_stats()); - let (metadata, need_metadata_module) = rustc_metadata::fs::encode_and_write_metadata(tcx); + let metadata = rustc_metadata::fs::encode_and_write_metadata(tcx); - let codegen = tcx.sess.time("codegen_crate", move || { - codegen_backend.codegen_crate(tcx, metadata, need_metadata_module) - }); + let codegen = tcx.sess.time("codegen_crate", move || codegen_backend.codegen_crate(tcx)); info!("Post-codegen\n{:?}", tcx.debug_stats()); @@ -1136,7 +1210,7 @@ pub(crate) fn start_codegen<'tcx>( tcx.sess.code_stats.print_type_sizes(); } - codegen + (codegen, metadata) } /// Compute and validate the crate name. diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index c8914c9be9c..877440ec7d2 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -4,7 +4,9 @@ use std::sync::Arc; use rustc_codegen_ssa::CodegenResults; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::svh::Svh; +use rustc_errors::timings::TimingSection; use rustc_hir::def_id::LOCAL_CRATE; +use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::DepGraph; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -18,6 +20,7 @@ pub struct Linker { output_filenames: Arc<OutputFilenames>, // Only present when incr. comp. is enabled. crate_hash: Option<Svh>, + metadata: EncodedMetadata, ongoing_codegen: Box<dyn Any>, } @@ -26,7 +29,7 @@ impl Linker { tcx: TyCtxt<'_>, codegen_backend: &dyn CodegenBackend, ) -> Linker { - let ongoing_codegen = passes::start_codegen(codegen_backend, tcx); + let (ongoing_codegen, metadata) = passes::start_codegen(codegen_backend, tcx); Linker { dep_graph: tcx.dep_graph.clone(), @@ -36,6 +39,7 @@ impl Linker { } else { None }, + metadata, ongoing_codegen, } } @@ -75,6 +79,7 @@ impl Linker { sess, &rlink_file, &codegen_results, + &self.metadata, &*self.output_filenames, ) .unwrap_or_else(|error| { @@ -84,6 +89,7 @@ impl Linker { } let _timer = sess.prof.verbose_generic_activity("link_crate"); - codegen_backend.link(sess, codegen_results, &self.output_filenames) + let _timing = sess.timings.start_section(sess.dcx(), TimingSection::Linking); + codegen_backend.link(sess, codegen_results, self.metadata, &self.output_filenames) } } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 558f13a832c..a0012b04c4f 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -27,7 +27,7 @@ use rustc_span::source_map::{RealFileLoader, SourceMapInputs}; use rustc_span::{FileName, SourceFileHashAlgorithm, sym}; use rustc_target::spec::{ CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, - RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel, WasmCAbi, + RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel, }; use crate::interface::{initialize_checked_jobserver, parse_cfg}; @@ -709,6 +709,7 @@ fn test_unstable_options_tracking_hash() { untracked!(llvm_time_trace, true); untracked!(ls, vec!["all".to_owned()]); untracked!(macro_backtrace, true); + untracked!(macro_stats, true); untracked!(meta_stats, true); untracked!(mir_include_spans, MirIncludeSpans::On); untracked!(nll_facts, true); @@ -801,6 +802,7 @@ fn test_unstable_options_tracking_hash() { tracked!(force_unstable_if_unmarked, true); tracked!(function_return, FunctionReturn::ThunkExtern); tracked!(function_sections, Some(false)); + tracked!(hint_mostly_unused, true); tracked!(human_readable_cgu_names, true); tracked!(incremental_ignore_spans, true); tracked!(inline_mir, Some(true)); @@ -881,7 +883,6 @@ fn test_unstable_options_tracking_hash() { tracked!(verify_llvm_ir, true); tracked!(virtual_function_elimination, true); tracked!(wasi_exec_model, Some(WasiExecModel::Reactor)); - tracked!(wasm_c_abi, WasmCAbi::Spec); // tidy-alphabetical-end macro_rules! tracked_no_crate_hash { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index fd2e2ba39ac..8d9f2385b71 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -72,9 +72,6 @@ lint_builtin_const_no_mangle = const items should never be `#[no_mangle]` lint_builtin_decl_unsafe_fn = declaration of an `unsafe` function lint_builtin_decl_unsafe_method = declaration of an `unsafe` method -lint_builtin_deprecated_attr_link = use of deprecated attribute `{$name}`: {$reason}. See {$link} - .msg_suggestion = {$msg} - .default_suggestion = remove this attribute lint_builtin_deref_nullptr = dereferencing a null pointer .label = this code causes undefined behavior when executed @@ -533,8 +530,6 @@ lint_mismatched_lifetime_syntaxes_suggestion_implicit = lint_mismatched_lifetime_syntaxes_suggestion_mixed = one option is to remove the lifetime for references and use the anonymous lifetime for paths -lint_missing_fragment_specifier = missing fragment specifier - lint_missing_unsafe_on_extern = extern blocks should be unsafe .suggestion = needs `unsafe` before the extern keyword @@ -743,7 +738,8 @@ lint_redundant_semicolons = [true] semicolons *[false] semicolon } - .suggestion = remove {$multiple -> + +lint_redundant_semicolons_suggestion = remove {$multiple_semicolons -> [true] these semicolons *[false] this semicolon } @@ -816,6 +812,9 @@ lint_tykind = usage of `ty::TyKind` lint_tykind_kind = usage of `ty::TyKind::<kind>` .suggestion = try using `ty::<kind>` directly +lint_type_ir_direct_use = do not use `rustc_type_ir` unless you are implementing type system internals + .note = use `rustc_middle::ty` instead + lint_type_ir_inherent_usage = do not use `rustc_type_ir::inherent` unless you're inside of the trait solver .note = the method or struct you're looking for is likely defined somewhere else downstream in the compiler diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 69e9f8e1b2c..dedea54f8e0 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -22,7 +22,7 @@ use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust::expr_to_string; use rustc_errors::{Applicability, LintDiagnostic}; -use rustc_feature::{AttributeGate, BuiltinAttribute, GateIssue, Stability, deprecated_attributes}; +use rustc_feature::GateIssue; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; @@ -48,8 +48,7 @@ use rustc_trait_selection::traits::{self}; use crate::errors::BuiltinEllipsisInclusiveRangePatterns; use crate::lints::{ - BuiltinAnonymousParams, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink, - BuiltinDeprecatedAttrLinkSuggestion, BuiltinDerefNullptr, BuiltinDoubleNegations, + BuiltinAnonymousParams, BuiltinConstNoMangle, BuiltinDerefNullptr, BuiltinDoubleNegations, BuiltinDoubleNegationsAddParens, BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives, BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures, BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, @@ -799,53 +798,6 @@ impl EarlyLintPass for AnonymousParameters { } } -/// Check for use of attributes which have been deprecated. -#[derive(Clone)] -pub struct DeprecatedAttr { - // This is not free to compute, so we want to keep it around, rather than - // compute it for every attribute. - depr_attrs: Vec<&'static BuiltinAttribute>, -} - -impl_lint_pass!(DeprecatedAttr => []); - -impl Default for DeprecatedAttr { - fn default() -> Self { - DeprecatedAttr { depr_attrs: deprecated_attributes() } - } -} - -impl EarlyLintPass for DeprecatedAttr { - fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { - for BuiltinAttribute { name, gate, .. } in &self.depr_attrs { - if attr.ident().map(|ident| ident.name) == Some(*name) { - if let &AttributeGate::Gated( - Stability::Deprecated(link, suggestion), - name, - reason, - _, - ) = gate - { - let suggestion = match suggestion { - Some(msg) => { - BuiltinDeprecatedAttrLinkSuggestion::Msg { suggestion: attr.span, msg } - } - None => { - BuiltinDeprecatedAttrLinkSuggestion::Default { suggestion: attr.span } - } - }; - cx.emit_span_lint( - DEPRECATED, - attr.span, - BuiltinDeprecatedAttrLink { name, reason, link, suggestion }, - ); - } - return; - } - } - } -} - fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &[ast::Attribute]) { use rustc_ast::token::CommentKind; diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 5679d4566dc..b6bf45dfbcf 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -7,6 +7,7 @@ use std::cell::Cell; use std::slice; use rustc_ast::BindingMode; +use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_data_structures::unord::UnordMap; @@ -850,6 +851,20 @@ impl<'tcx> LateContext<'tcx> { }) } + /// Returns the effective precedence of an expression for the purpose of + /// rendering diagnostic. This is not the same as the precedence that would + /// be used for pretty-printing HIR by rustc_hir_pretty. + pub fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence { + let for_each_attr = |id: hir::HirId, callback: &mut dyn FnMut(&hir::Attribute)| { + for attr in self.tcx.hir_attrs(id) { + if attr.span().desugaring_kind().is_none() { + callback(attr); + } + } + }; + expr.precedence(&for_each_attr) + } + /// If the given expression is a local binding, find the initializer expression. /// If that initializer expression is another local binding, find its initializer again. /// diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs index 91c7922638d..c737919db9c 100644 --- a/compiler/rustc_lint/src/dangling.rs +++ b/compiler/rustc_lint/src/dangling.rs @@ -1,4 +1,5 @@ use rustc_ast::visit::{visit_opt, walk_list}; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem}; @@ -133,7 +134,7 @@ fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { && 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) + && find_attr!(cx.tcx.get_all_attrs(fn_id), AttributeKind::AsPtr(_)) { // FIXME: use `emit_node_lint` when `#[primary_span]` is added. cx.tcx.emit_node_span_lint( diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 12666d383f9..48e3bbb79fa 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -33,10 +33,8 @@ pub struct EarlyContextAndPass<'ecx, 'tcx, T: EarlyLintPass> { } impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> { - // This always-inlined function is for the hot call site. - #[inline(always)] #[allow(rustc::diagnostic_outside_of_impl)] - fn inlined_check_id(&mut self, id: ast::NodeId) { + fn check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint; self.context.opt_span_lint(lint_id.lint, span, |diag| { @@ -45,11 +43,6 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> { } } - // This non-inlined function is for the cold call sites. - fn check_id(&mut self, id: ast::NodeId) { - self.inlined_check_id(id) - } - /// Merge the lints specified by any lint attributes into the /// current lint context, call the provided function, then reset the /// lints in effect to their previous state. @@ -61,7 +54,6 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> { debug!(?id); let push = self.context.builder.push(attrs, is_crate_node, None); - self.inlined_check_id(id); debug!("early context: enter_attrs({:?})", attrs); lint_callback!(self, check_attributes, attrs); ensure_sufficient_stack(|| f(self)); @@ -136,12 +128,8 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> // the AST struct that they wrap (e.g. an item) self.with_lint_attrs(s.id, s.attrs(), |cx| { lint_callback!(cx, check_stmt, s); + ast_visit::walk_stmt(cx, s); }); - // The visitor for the AST struct wrapped - // by the statement (e.g. `Item`) will call - // `with_lint_attrs`, so do this walk - // outside of the above `with_lint_attrs` call - ast_visit::walk_stmt(self, s); } fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, span: Span, id: ast::NodeId) { diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 60c477dd6c7..3b0a36186b6 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -432,9 +432,6 @@ pub fn decorate_builtin_lint( BuiltinLintDiag::CfgAttrNoAttributes => { lints::CfgAttrNoAttributes.decorate_lint(diag); } - BuiltinLintDiag::MissingFragmentSpecifier => { - lints::MissingFragmentSpecifier.decorate_lint(diag); - } BuiltinLintDiag::MetaVariableStillRepeating(name) => { lints::MetaVariableStillRepeating { name }.decorate_lint(diag); } diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index 8124d7f7c86..aa6f36a67f0 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -15,8 +15,7 @@ use rustc_middle::ty::relate::{ Relate, RelateResult, TypeRelation, structurally_relate_consts, structurally_relate_tys, }; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, - TypeVisitor, + self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint::FutureIncompatibilityReason; @@ -210,7 +209,7 @@ where VarFn: FnOnce() -> FxHashMap<DefId, ty::Variance>, OutlivesFn: FnOnce() -> OutlivesEnvironment<'tcx>, { - fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) { + fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) { // When we get into a binder, we need to add its own bound vars to the scope. let mut added = vec![]; for arg in t.bound_vars() { diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 1805a674d68..d8fc46aa9ab 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -14,8 +14,8 @@ use {rustc_ast as ast, rustc_hir as hir}; use crate::lints::{ BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag, - SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrInherentUsage, - TypeIrTraitUsage, UntranslatableDiag, + SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrDirectUse, + TypeIrInherentUsage, TypeIrTraitUsage, UntranslatableDiag, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; @@ -301,8 +301,18 @@ declare_tool_lint! { "usage `rustc_type_ir`-specific abstraction traits outside of trait system", report_in_external_macro: true } +declare_tool_lint! { + /// The `direct_use_of_rustc_type_ir` lint detects usage of `rustc_type_ir`. + /// + /// This module should only be used within the trait solver and some desirable + /// crates like rustc_middle. + pub rustc::DIRECT_USE_OF_RUSTC_TYPE_IR, + Allow, + "usage `rustc_type_ir` abstraction outside of trait system", + report_in_external_macro: true +} -declare_lint_pass!(TypeIr => [NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_TRAITS]); +declare_lint_pass!(TypeIr => [DIRECT_USE_OF_RUSTC_TYPE_IR, NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_TRAITS]); impl<'tcx> LateLintPass<'tcx> for TypeIr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { @@ -372,6 +382,21 @@ impl<'tcx> LateLintPass<'tcx> for TypeIr { NonGlobImportTypeIrInherent { suggestion: lo.eq_ctxt(hi).then(|| lo.to(hi)), snippet }, ); } + + fn check_path( + &mut self, + cx: &LateContext<'tcx>, + path: &rustc_hir::Path<'tcx>, + _: rustc_hir::HirId, + ) { + if let Some(seg) = path.segments.iter().find(|seg| { + seg.res + .opt_def_id() + .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::type_ir, def_id)) + }) { + cx.emit_span_lint(DIRECT_USE_OF_RUSTC_TYPE_IR, seg.ident.span, TypeIrDirectUse); + } + } } declare_tool_lint! { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 72bfeaddbf1..20568f35a47 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -174,7 +174,6 @@ early_lint_methods!( AnonymousParameters: AnonymousParameters, EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(), NonCamelCaseTypes: NonCamelCaseTypes, - DeprecatedAttr: DeprecatedAttr::default(), WhileTrue: WhileTrue, NonAsciiIdents: NonAsciiIdents, IncompleteInternalFeatures: IncompleteInternalFeatures, @@ -619,6 +618,12 @@ fn register_builtins(store: &mut LintStore) { "converted into hard error, \ see <https://github.com/rust-lang/rust/issues/116558> for more information", ); + store.register_removed( + "missing_fragment_specifier", + "converted into hard error, \ + see <https://github.com/rust-lang/rust/issues/40107> for more information", + ); + store.register_removed("wasm_c_abi", "the wasm C ABI has been fixed"); } fn register_internals(store: &mut LintStore) { @@ -663,6 +668,7 @@ fn register_internals(store: &mut LintStore) { LintId::of(USAGE_OF_TYPE_IR_TRAITS), LintId::of(BAD_OPT_ACCESS), LintId::of(SPAN_USE_EQ_CTXT), + LintId::of(DIRECT_USE_OF_RUSTC_TYPE_IR), ], ); } diff --git a/compiler/rustc_lint/src/lifetime_syntax.rs b/compiler/rustc_lint/src/lifetime_syntax.rs index 31b038e6a46..95b7b69bd5a 100644 --- a/compiler/rustc_lint/src/lifetime_syntax.rs +++ b/compiler/rustc_lint/src/lifetime_syntax.rs @@ -84,19 +84,45 @@ impl<'tcx> LateLintPass<'tcx> for LifetimeSyntax { _: rustc_span::Span, _: rustc_span::def_id::LocalDefId, ) { - let mut input_map = Default::default(); - let mut output_map = Default::default(); + check_fn_like(cx, fd); + } - for input in fd.inputs { - LifetimeInfoCollector::collect(input, &mut input_map); + #[instrument(skip_all)] + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, ti: &'tcx hir::TraitItem<'tcx>) { + match ti.kind { + hir::TraitItemKind::Const(..) => {} + hir::TraitItemKind::Fn(fn_sig, _trait_fn) => check_fn_like(cx, fn_sig.decl), + hir::TraitItemKind::Type(..) => {} } + } - if let hir::FnRetTy::Return(output) = fd.output { - LifetimeInfoCollector::collect(output, &mut output_map); + #[instrument(skip_all)] + fn check_foreign_item( + &mut self, + cx: &LateContext<'tcx>, + fi: &'tcx rustc_hir::ForeignItem<'tcx>, + ) { + match fi.kind { + hir::ForeignItemKind::Fn(fn_sig, _idents, _generics) => check_fn_like(cx, fn_sig.decl), + hir::ForeignItemKind::Static(..) => {} + hir::ForeignItemKind::Type => {} } + } +} + +fn check_fn_like<'tcx>(cx: &LateContext<'tcx>, fd: &'tcx hir::FnDecl<'tcx>) { + let mut input_map = Default::default(); + let mut output_map = Default::default(); - report_mismatches(cx, &input_map, &output_map); + for input in fd.inputs { + LifetimeInfoCollector::collect(input, &mut input_map); } + + if let hir::FnRetTy::Return(output) = fd.output { + LifetimeInfoCollector::collect(output, &mut output_map); + } + + report_mismatches(cx, &input_map, &output_map); } #[instrument(skip_all)] diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 9d3c74a9a2b..abdf8e3853b 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -199,32 +199,6 @@ pub(crate) struct BuiltinAnonymousParams<'a> { pub ty_snip: &'a str, } -// FIXME(davidtwco) translatable deprecated attr -#[derive(LintDiagnostic)] -#[diag(lint_builtin_deprecated_attr_link)] -pub(crate) struct BuiltinDeprecatedAttrLink<'a> { - pub name: Symbol, - pub reason: &'a str, - pub link: &'a str, - #[subdiagnostic] - pub suggestion: BuiltinDeprecatedAttrLinkSuggestion<'a>, -} - -#[derive(Subdiagnostic)] -pub(crate) enum BuiltinDeprecatedAttrLinkSuggestion<'a> { - #[suggestion(lint_msg_suggestion, code = "", applicability = "machine-applicable")] - Msg { - #[primary_span] - suggestion: Span, - msg: &'a str, - }, - #[suggestion(lint_default_suggestion, code = "", applicability = "machine-applicable")] - Default { - #[primary_span] - suggestion: Span, - }, -} - #[derive(LintDiagnostic)] #[diag(lint_builtin_unused_doc_comment)] pub(crate) struct BuiltinUnusedDocComment<'a> { @@ -996,6 +970,11 @@ pub(crate) struct TypeIrInherentUsage; pub(crate) struct TypeIrTraitUsage; #[derive(LintDiagnostic)] +#[diag(lint_type_ir_direct_use)] +#[note] +pub(crate) struct TypeIrDirectUse; + +#[derive(LintDiagnostic)] #[diag(lint_non_glob_import_type_ir_inherent)] pub(crate) struct NonGlobImportTypeIrInherent { #[suggestion(code = "{snippet}", applicability = "maybe-incorrect")] @@ -1564,8 +1543,16 @@ pub(crate) struct PassByValueDiag { #[diag(lint_redundant_semicolons)] pub(crate) struct RedundantSemicolonsDiag { pub multiple: bool, - #[suggestion(code = "", applicability = "maybe-incorrect")] - pub suggestion: Span, + #[subdiagnostic] + pub suggestion: Option<RedundantSemicolonsSuggestion>, +} + +#[derive(Subdiagnostic)] +#[suggestion(lint_redundant_semicolons_suggestion, code = "", applicability = "maybe-incorrect")] +pub(crate) struct RedundantSemicolonsSuggestion { + pub multiple_semicolons: bool, + #[primary_span] + pub span: Span, } // traits.rs @@ -2617,10 +2604,6 @@ pub(crate) struct DuplicateMacroAttribute; pub(crate) struct CfgAttrNoAttributes; #[derive(LintDiagnostic)] -#[diag(lint_missing_fragment_specifier)] -pub(crate) struct MissingFragmentSpecifier; - -#[derive(LintDiagnostic)] #[diag(lint_metavariable_still_repeating)] pub(crate) struct MetaVariableStillRepeating { pub name: MacroRulesNormalizedIdent, @@ -2648,6 +2631,7 @@ pub(crate) struct UnusedCrateDependency { pub local_crate: Symbol, } +// FIXME(jdonszelmann): duplicated in rustc_attr_parsing, should be moved there completely. #[derive(LintDiagnostic)] #[diag(lint_ill_formed_attribute_input)] pub(crate) struct IllFormedAttributeInput { diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index dea7c8ac708..3cc55eaa0f2 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -46,7 +46,8 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable { .tcx .explicit_super_predicates_of(def_id) .iter_identity_copied() - .filter_map(|(pred, _)| pred.as_trait_clause()); + .filter_map(|(pred, _)| pred.as_trait_clause()) + .filter(|pred| !cx.tcx.is_lang_item(pred.def_id(), hir::LangItem::MetaSized)); if direct_super_traits_iter.count() > 1 { cx.emit_span_lint( MULTIPLE_SUPERTRAIT_UPCASTABLE, diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 31c18074466..1b60466a589 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -164,7 +164,7 @@ impl NonCamelCaseTypes { impl EarlyLintPass for NonCamelCaseTypes { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { let has_repr_c = matches!( - AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, true), + AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, it.id), Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(r, _)| r == &ReprAttr::ReprC) ); diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs index b43e4938b73..f6d2fbe4261 100644 --- a/compiler/rustc_lint/src/redundant_semicolon.rs +++ b/compiler/rustc_lint/src/redundant_semicolon.rs @@ -2,7 +2,7 @@ use rustc_ast::{Block, StmtKind}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::Span; -use crate::lints::RedundantSemicolonsDiag; +use crate::lints::{RedundantSemicolonsDiag, RedundantSemicolonsSuggestion}; use crate::{EarlyContext, EarlyLintPass, LintContext}; declare_lint! { @@ -44,16 +44,21 @@ impl EarlyLintPass for RedundantSemicolons { fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, bool)>) { if let Some((span, multiple)) = seq.take() { - // FIXME: Find a better way of ignoring the trailing - // semicolon from macro expansion if span == rustc_span::DUMMY_SP { return; } + // Ignore redundant semicolons inside macro expansion.(issue #142143) + let suggestion = if span.from_expansion() { + None + } else { + Some(RedundantSemicolonsSuggestion { multiple_semicolons: multiple, span }) + }; + cx.emit_span_lint( REDUNDANT_SEMICOLONS, span, - RedundantSemicolonsDiag { multiple, suggestion: span }, + RedundantSemicolonsDiag { multiple, suggestion }, ); } } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index fec23354c91..aaba0c14b1c 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -196,7 +196,8 @@ declare_lint! { /// same address after being merged together. UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS, Warn, - "detects unpredictable function pointer comparisons" + "detects unpredictable function pointer comparisons", + report_in_external_macro } #[derive(Copy, Clone, Default)] diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 777118e69fb..b0ea9689e50 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -65,7 +65,6 @@ declare_lint_pass! { MACRO_USE_EXTERN_CRATE, META_VARIABLE_MISUSE, MISSING_ABI, - MISSING_FRAGMENT_SPECIFIER, MISSING_UNSAFE_ON_EXTERN, MUST_NOT_SUSPEND, NAMED_ARGUMENTS_USED_POSITIONALLY, @@ -141,7 +140,6 @@ declare_lint_pass! { UNUSED_VARIABLES, USELESS_DEPRECATED, WARNINGS, - WASM_C_ABI, // tidy-alphabetical-end ] } @@ -1418,51 +1416,6 @@ declare_lint! { } declare_lint! { - /// The `missing_fragment_specifier` lint is issued when an unused pattern in a - /// `macro_rules!` macro definition has a meta-variable (e.g. `$e`) that is not - /// followed by a fragment specifier (e.g. `:expr`). - /// - /// This warning can always be fixed by removing the unused pattern in the - /// `macro_rules!` macro definition. - /// - /// ### Example - /// - /// ```rust,compile_fail,edition2021 - /// macro_rules! foo { - /// () => {}; - /// ($name) => { }; - /// } - /// - /// fn main() { - /// foo!(); - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// To fix this, remove the unused pattern from the `macro_rules!` macro definition: - /// - /// ```rust - /// macro_rules! foo { - /// () => {}; - /// } - /// fn main() { - /// foo!(); - /// } - /// ``` - pub MISSING_FRAGMENT_SPECIFIER, - Deny, - "detects missing fragment specifiers in unused `macro_rules!` patterns", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseError, - reference: "issue #40107 <https://github.com/rust-lang/rust/issues/40107>", - report_in_deps: true, - }; -} - -declare_lint! { /// The `late_bound_lifetime_arguments` lint detects generic lifetime /// arguments in path segments with late bound lifetime parameters. /// @@ -3664,7 +3617,7 @@ declare_lint! { "use of unsupported calling convention", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::FutureReleaseError, - report_in_deps: true, + report_in_deps: false, reference: "issue #137018 <https://github.com/rust-lang/rust/issues/137018>", }; } @@ -4146,6 +4099,7 @@ declare_lint! { @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange(Edition::Edition2024), reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html>", + report_in_deps: true, }; @edition Edition2024 => Deny; report_in_external_macro @@ -4200,6 +4154,7 @@ declare_lint! { @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionAndFutureReleaseError(Edition::Edition2024), reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html>", + report_in_deps: true, }; report_in_external_macro } @@ -5027,50 +4982,6 @@ declare_lint! { } declare_lint! { - /// The `wasm_c_abi` lint detects usage of the `extern "C"` ABI of wasm that is affected - /// by a planned ABI change that has the goal of aligning Rust with the standard C ABI - /// of this target. - /// - /// ### Example - /// - /// ```rust,ignore (needs wasm32-unknown-unknown) - /// #[repr(C)] - /// struct MyType(i32, i32); - /// - /// extern "C" my_fun(x: MyType) {} - /// ``` - /// - /// This will produce: - /// - /// ```text - /// error: this function function definition is affected by the wasm ABI transition: it passes an argument of non-scalar type `MyType` - /// --> $DIR/wasm_c_abi_transition.rs:17:1 - /// | - /// | pub extern "C" fn my_fun(_x: MyType) {} - /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - /// | - /// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - /// = note: for more information, see issue #138762 <https://github.com/rust-lang/rust/issues/138762> - /// = help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target - /// ``` - /// - /// ### Explanation - /// - /// Rust has historically implemented a non-spec-compliant C ABI on wasm32-unknown-unknown. This - /// has caused incompatibilities with other compilers and Wasm targets. In a future version - /// of Rust, this will be fixed, and therefore code relying on the non-spec-compliant C ABI will - /// stop functioning. - pub WASM_C_ABI, - Warn, - "detects code relying on rustc's non-spec-compliant wasm C ABI", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseError, - reference: "issue #138762 <https://github.com/rust-lang/rust/issues/138762>", - report_in_deps: true, - }; -} - -declare_lint! { /// The `aarch64_softfloat_neon` lint detects usage of `#[target_feature(enable = "neon")]` on /// softfloat aarch64 targets. Enabling this target feature causes LLVM to alter the ABI of /// function calls, making this attribute unsound to use. diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 1d9b7a7fcb9..cd402c9234f 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -778,7 +778,6 @@ pub enum BuiltinLintDiag { UnnameableTestItems, DuplicateMacroAttribute, CfgAttrNoAttributes, - MissingFragmentSpecifier, MetaVariableStillRepeating(MacroRulesNormalizedIdent), MetaVariableWrongOperator, DuplicateMatcherBinding, diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index 1bb502ca3d0..df648bbd489 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -43,6 +43,7 @@ use tracing_subscriber::filter::{Directive, EnvFilter, LevelFilter}; use tracing_subscriber::fmt::FmtContext; use tracing_subscriber::fmt::format::{self, FormatEvent, FormatFields}; use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::{Layer, Registry}; /// The values of all the environment variables that matter for configuring a logger. /// Errors are explicitly preserved so that we can share error handling. @@ -72,6 +73,36 @@ impl LoggerConfig { /// Initialize the logger with the given values for the filter, coloring, and other options env variables. pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> { + init_logger_with_additional_layer(cfg, || Registry::default()) +} + +/// Trait alias for the complex return type of `build_subscriber` in +/// [init_logger_with_additional_layer]. A [Registry] with any composition of [tracing::Subscriber]s +/// (e.g. `Registry::default().with(custom_layer)`) should be compatible with this type. +/// Having an alias is also useful so rustc_driver_impl does not need to explicitly depend on +/// `tracing_subscriber`. +pub trait BuildSubscriberRet: + tracing::Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span> + Send + Sync +{ +} + +impl< + T: tracing::Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span> + Send + Sync, +> BuildSubscriberRet for T +{ +} + +/// Initialize the logger with the given values for the filter, coloring, and other options env variables. +/// Additionally add a custom layer to collect logging and tracing events via `build_subscriber`, +/// for example: `|| Registry::default().with(custom_layer)`. +pub fn init_logger_with_additional_layer<F, T>( + cfg: LoggerConfig, + build_subscriber: F, +) -> Result<(), Error> +where + F: FnOnce() -> T, + T: BuildSubscriberRet, +{ let filter = match cfg.filter { Ok(env) => EnvFilter::new(env), _ => EnvFilter::default().add_directive(Directive::from(LevelFilter::WARN)), @@ -124,7 +155,7 @@ pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> { Err(_) => {} // no wraptree } - let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer); + let subscriber = build_subscriber().with(layer.with_filter(filter)); match cfg.backtrace { Ok(backtrace_target) => { let fmt_layer = tracing_subscriber::fmt::layer() diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index e57534b847e..1eaad26ff8e 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -4,9 +4,9 @@ use std::{fs, io}; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_fs_util::TempDirBuilder; use rustc_middle::ty::TyCtxt; +use rustc_session::Session; use rustc_session::config::{CrateType, OutFileName, OutputType}; use rustc_session::output::filename_for_metadata; -use rustc_session::{MetadataKind, Session}; use crate::errors::{ BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile, @@ -22,13 +22,8 @@ pub const METADATA_FILENAME: &str = "lib.rmeta"; /// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a /// directory being searched for `extern crate` (observing an incomplete file). /// The returned path is the temporary file containing the complete metadata. -pub fn emit_wrapper_file( - sess: &Session, - data: &[u8], - tmpdir: &MaybeTempDir, - name: &str, -) -> PathBuf { - let out_filename = tmpdir.as_ref().join(name); +pub fn emit_wrapper_file(sess: &Session, data: &[u8], tmpdir: &Path, name: &str) -> PathBuf { + let out_filename = tmpdir.join(name); let result = fs::write(&out_filename, data); if let Err(err) = result { @@ -38,7 +33,7 @@ pub fn emit_wrapper_file( out_filename } -pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { +pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata { let out_filename = filename_for_metadata(tcx.sess, tcx.output_filenames(())); // To avoid races with another rustc process scanning the output directory, // we need to write the file somewhere else and atomically move it to its @@ -59,25 +54,20 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { None }; - // Always create a file at `metadata_filename`, even if we have nothing to write to it. - // This simplifies the creation of the output `out_filename` when requested. - let metadata_kind = tcx.metadata_kind(); - match metadata_kind { - MetadataKind::None => { - std::fs::File::create(&metadata_filename).unwrap_or_else(|err| { - tcx.dcx().emit_fatal(FailedCreateFile { filename: &metadata_filename, err }); + if tcx.needs_metadata() { + encode_metadata(tcx, &metadata_filename, metadata_stub_filename.as_deref()); + } else { + // Always create a file at `metadata_filename`, even if we have nothing to write to it. + // This simplifies the creation of the output `out_filename` when requested. + std::fs::File::create(&metadata_filename).unwrap_or_else(|err| { + tcx.dcx().emit_fatal(FailedCreateFile { filename: &metadata_filename, err }); + }); + if let Some(metadata_stub_filename) = &metadata_stub_filename { + std::fs::File::create(metadata_stub_filename).unwrap_or_else(|err| { + tcx.dcx().emit_fatal(FailedCreateFile { filename: &metadata_stub_filename, err }); }); - if let Some(metadata_stub_filename) = &metadata_stub_filename { - std::fs::File::create(metadata_stub_filename).unwrap_or_else(|err| { - tcx.dcx() - .emit_fatal(FailedCreateFile { filename: &metadata_stub_filename, err }); - }); - } - } - MetadataKind::Uncompressed | MetadataKind::Compressed => { - encode_metadata(tcx, &metadata_filename, metadata_stub_filename.as_deref()) } - }; + } let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata"); @@ -118,9 +108,7 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { tcx.dcx().emit_fatal(FailedCreateEncodedMetadata { err }); }); - let need_metadata_module = metadata_kind == MetadataKind::Compressed; - - (metadata, need_metadata_module) + metadata } #[cfg(not(target_os = "linux"))] diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 3ab989d2d3b..00bd32eb0eb 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -10,7 +10,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::{Mmap, MmapMut}; use rustc_data_structures::sync::{join, par_for_each_in}; use rustc_data_structures::temp_dir::MaybeTempDir; -use rustc_data_structures::thousands::format_with_underscores; +use rustc_data_structures::thousands::usize_with_underscores; use rustc_feature::Features; use rustc_hir as hir; use rustc_hir::def_id::{CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE, LocalDefId, LocalDefIdSet}; @@ -789,7 +789,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { "{} {:<23}{:>10} ({:4.1}%)", prefix, label, - format_with_underscores(size), + usize_with_underscores(size), perc(size) ); } @@ -798,7 +798,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { "{} {:<23}{:>10} (of which {:.1}% are zero bytes)", prefix, "Total", - format_with_underscores(total_bytes), + usize_with_underscores(total_bytes), perc(zero_bytes) ); eprintln!("{prefix}"); diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 3de97c8c0d9..e5e1ae508ed 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -1233,6 +1233,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod body_owners: body_owners.into_boxed_slice(), opaques: opaques.into_boxed_slice(), nested_bodies: nested_bodies.into_boxed_slice(), + delayed_lint_items: Box::new([]), } } @@ -1254,6 +1255,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { body_owners, opaques, nested_bodies, + delayed_lint_items, .. } = collector; @@ -1266,6 +1268,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { body_owners: body_owners.into_boxed_slice(), opaques: opaques.into_boxed_slice(), nested_bodies: nested_bodies.into_boxed_slice(), + delayed_lint_items: delayed_lint_items.into_boxed_slice(), } } @@ -1282,6 +1285,7 @@ struct ItemCollector<'tcx> { body_owners: Vec<LocalDefId>, opaques: Vec<LocalDefId>, nested_bodies: Vec<LocalDefId>, + delayed_lint_items: Vec<OwnerId>, } impl<'tcx> ItemCollector<'tcx> { @@ -1297,6 +1301,7 @@ impl<'tcx> ItemCollector<'tcx> { body_owners: Vec::default(), opaques: Vec::default(), nested_bodies: Vec::default(), + delayed_lint_items: Vec::default(), } } } @@ -1314,6 +1319,9 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { } self.items.push(item.item_id()); + if self.crate_collector && item.has_delayed_lints { + self.delayed_lint_items.push(item.item_id().owner_id); + } // Items that are modules are handled here instead of in visit_mod. if let ItemKind::Mod(_, module) = &item.kind { @@ -1329,6 +1337,9 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { fn visit_foreign_item(&mut self, item: &'hir ForeignItem<'hir>) { self.foreign_items.push(item.foreign_item_id()); + if self.crate_collector && item.has_delayed_lints { + self.delayed_lint_items.push(item.foreign_item_id().owner_id); + } intravisit::walk_foreign_item(self, item) } @@ -1362,6 +1373,10 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { } self.trait_items.push(item.trait_item_id()); + if self.crate_collector && item.has_delayed_lints { + self.delayed_lint_items.push(item.trait_item_id().owner_id); + } + intravisit::walk_trait_item(self, item) } @@ -1371,6 +1386,10 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { } self.impl_items.push(item.impl_item_id()); + if self.crate_collector && item.has_delayed_lints { + self.delayed_lint_items.push(item.impl_item_id().owner_id); + } + intravisit::walk_impl_item(self, item) } } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index d1f5caaafb2..9f79ed4b5a5 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -12,6 +12,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{DynSend, DynSync, try_par_for_each_in}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; +use rustc_hir::lints::DelayedLint; use rustc_hir::*; use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_span::{ErrorGuaranteed, ExpnId, Span}; @@ -31,6 +32,8 @@ pub struct ModuleItems { opaques: Box<[LocalDefId]>, body_owners: Box<[LocalDefId]>, nested_bodies: Box<[LocalDefId]>, + // only filled with hir_crate_items, not with hir_module_items + delayed_lint_items: Box<[OwnerId]>, } impl ModuleItems { @@ -48,6 +51,10 @@ impl ModuleItems { self.trait_items.iter().copied() } + pub fn delayed_lint_items(&self) -> impl Iterator<Item = OwnerId> { + self.delayed_lint_items.iter().copied() + } + /// Returns all items that are associated with some `impl` block (both inherent and trait impl /// blocks). pub fn impl_items(&self) -> impl Iterator<Item = ImplItemId> { @@ -161,8 +168,9 @@ impl<'tcx> TyCtxt<'tcx> { node: OwnerNode<'_>, bodies: &SortedMap<ItemLocalId, &Body<'_>>, attrs: &SortedMap<ItemLocalId, &[Attribute]>, + delayed_lints: &[DelayedLint], define_opaque: Option<&[(Span, LocalDefId)]>, - ) -> (Option<Fingerprint>, Option<Fingerprint>) { + ) -> (Option<Fingerprint>, Option<Fingerprint>, Option<Fingerprint>) { if self.needs_crate_hash() { self.with_stable_hashing_context(|mut hcx| { let mut stable_hasher = StableHasher::new(); @@ -178,10 +186,16 @@ impl<'tcx> TyCtxt<'tcx> { define_opaque.hash_stable(&mut hcx, &mut stable_hasher); let h2 = stable_hasher.finish(); - (Some(h1), Some(h2)) + + // hash lints emitted during ast lowering + let mut stable_hasher = StableHasher::new(); + delayed_lints.hash_stable(&mut hcx, &mut stable_hasher); + let h3 = stable_hasher.finish(); + + (Some(h1), Some(h2), Some(h3)) }) } else { - (None, None) + (None, None, None) } } } @@ -214,6 +228,8 @@ pub fn provide(providers: &mut Providers) { providers.hir_attr_map = |tcx, id| { tcx.hir_crate(()).owners[id.def_id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs) }; + providers.opt_ast_lowering_delayed_lints = + |tcx, id| tcx.hir_crate(()).owners[id.def_id].as_owner().map(|o| &o.delayed_lints); providers.def_span = |tcx, def_id| tcx.hir_span(tcx.local_def_id_to_hir_id(def_id)); providers.def_ident_span = |tcx, def_id| { let hir_id = tcx.local_def_id_to_hir_id(def_id); diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 667361b3ca0..c488ef9b575 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -28,6 +28,8 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(not(bootstrap), allow(rustc::direct_use_of_rustc_type_ir))] +#![cfg_attr(not(bootstrap), feature(sized_hierarchy))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(allocator_api)] diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs index 4587dcaddc4..6ca1e620704 100644 --- a/compiler/rustc_middle/src/middle/mod.rs +++ b/compiler/rustc_middle/src/middle/mod.rs @@ -12,7 +12,7 @@ pub mod lib_features { #[derive(HashStable, TyEncodable, TyDecodable)] pub enum FeatureStability { AcceptedSince(Symbol), - Unstable, + Unstable { old_name: Option<Symbol> }, } #[derive(HashStable, Debug, Default)] diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 454ab8c107f..ab6a65ed526 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -411,7 +411,7 @@ impl<'tcx> TyCtxt<'tcx> { match stability { Some(Stability { - level: attrs::StabilityLevel::Unstable { reason, issue, is_soft, implied_by }, + level: attrs::StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. }, feature, .. }) => { diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index f2f975a6968..56f19d7929d 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1133,13 +1133,6 @@ pub type AssertMessage<'tcx> = AssertKind<Operand<'tcx>>; /// Each local naturally corresponds to the place `Place { local, projection: [] }`. This place has /// the address of the local's allocation and the type of the local. /// -/// **Needs clarification:** Unsized locals seem to present a bit of an issue. Their allocation -/// can't actually be created on `StorageLive`, because it's unclear how big to make the allocation. -/// Furthermore, MIR produces assignments to unsized locals, although that is not permitted under -/// `#![feature(unsized_locals)]` in Rust. Besides just putting "unsized locals are special and -/// different" in a bunch of places, I (JakobDegen) don't know how to incorporate this behavior into -/// the current MIR semantics in a clean way - possibly this needs some design work first. -/// /// For places that are not locals, ie they have a non-empty list of projections, we define the /// values as a function of the parent place, that is the place with its last [`ProjectionElem`] /// stripped. The way this is computed of course depends on the kind of that last projection diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 9ed1f10455a..4d914c42cfc 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -328,6 +328,14 @@ impl Key for (DefId, SimplifiedType) { } } +impl Key for (DefId, ty::SizedTraitKind) { + type Cache<V> = DefaultCache<Self, V>; + + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + self.0.default_span(tcx) + } +} + impl<'tcx> Key for GenericArgsRef<'tcx> { type Cache<V> = DefaultCache<Self, V>; diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d8c927e00db..930d9fba433 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1,8 +1,64 @@ -//! Defines the various compiler queries. //! -//! For more information on the query system, see -//! ["Queries: demand-driven compilation"](https://rustc-dev-guide.rust-lang.org/query.html). -//! This chapter includes instructions for adding new queries. +//! # The rustc Query System: Query Definitions and Modifiers +//! +//! The core processes in rustc are shipped as queries. Each query is a demand-driven function from some key to a value. +//! The execution result of the function is cached and directly read during the next request, thereby improving compilation efficiency. +//! Some results are saved locally and directly read during the next compilation, which are core of incremental compilation. +//! +//! ## How to Read This Module +//! +//! Each `query` block in this file defines a single query, specifying its key and value types, along with various modifiers. +//! These query definitions are processed by the [`rustc_macros`], which expands them into the necessary boilerplate code +//! for the query system—including the [`Providers`] struct (a function table for all query implementations, where each field is +//! a function pointer to the actual provider), caching, and dependency graph integration. +//! **Note:** The `Providers` struct is not a Rust trait, but a struct generated by the `rustc_macros` to hold all provider functions. +//! The `rustc_macros` also supports a set of **query modifiers** (see below) that control the behavior of each query. +//! +//! The actual provider functions are implemented in various modules and registered into the `Providers` struct +//! during compiler initialization (see [`rustc_interface::passes::DEFAULT_QUERY_PROVIDERS`]). +//! +//! [`rustc_macros`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_macros/index.html +//! [`rustc_interface::passes::DEFAULT_QUERY_PROVIDERS`]: ../../rustc_interface/passes/static.DEFAULT_QUERY_PROVIDERS.html +//! +//! ## Query Modifiers +//! +//! Query modifiers are special flags that alter the behavior of a query. They are parsed and processed by the `rustc_macros` +//! The main modifiers are: +//! +//! - `desc { ... }`: Sets the human-readable description for diagnostics and profiling. Required for every query. +//! - `arena_cache`: Use an arena for in-memory caching of the query result. +//! - `cache_on_disk_if { ... }`: Cache the query result to disk if the provided block evaluates to true. +//! - `fatal_cycle`: If a dependency cycle is detected, abort compilation with a fatal error. +//! - `cycle_delay_bug`: If a dependency cycle is detected, emit a delayed bug instead of aborting immediately. +//! - `cycle_stash`: If a dependency cycle is detected, stash the error for later handling. +//! - `no_hash`: Do not hash the query result for incremental compilation; just mark as dirty if recomputed. +//! - `anon`: Make the query anonymous in the dependency graph (no dep node is created). +//! - `eval_always`: Always evaluate the query, ignoring its dependencies and cached results. +//! - `depth_limit`: Impose a recursion depth limit on the query to prevent stack overflows. +//! - `separate_provide_extern`: Use separate provider functions for local and external crates. +//! - `feedable`: Allow the query result to be set from another query ("fed" externally). +//! - `return_result_from_ensure_ok`: When called via `tcx.ensure_ok()`, return `Result<(), ErrorGuaranteed>` instead of `()`. +//! If the query needs to be executed and returns an error, the error is returned to the caller. +//! Only valid for queries returning `Result<_, ErrorGuaranteed>`. +//! +//! For the up-to-date list, see the `QueryModifiers` struct in +//! [`rustc_macros/src/query.rs`](https://github.com/rust-lang/rust/blob/master/compiler/rustc_macros/src/query.rs) +//! and for more details in incremental compilation, see the +//! [Query modifiers in incremental compilation](https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation-in-detail.html#query-modifiers) section of the rustc-dev-guide. +//! +//! ## Query Expansion and Code Generation +//! +//! The [`rustc_macros::rustc_queries`] macro expands each query definition into: +//! - A method on [`TyCtxt`] (and [`TyCtxtAt`]) for invoking the query. +//! - Provider traits and structs for supplying the query's value. +//! - Caching and dependency graph integration. +//! - Support for incremental compilation, disk caching, and arena allocation as controlled by the modifiers. +//! +//! [`rustc_macros::rustc_queries`]: ../../rustc_macros/macro.rustc_queries.html +//! +//! The macro-based approach allows the query system to be highly flexible and maintainable, while minimizing boilerplate. +//! +//! For more details, see the [rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/query.html). #![allow(unused_parens)] @@ -81,8 +137,8 @@ use crate::ty::layout::ValidityRequirement; use crate::ty::print::{PrintTraitRefExt, describe_as_module}; use crate::ty::util::AlwaysRequiresDrop; use crate::ty::{ - self, CrateInherentImpls, GenericArg, GenericArgsRef, PseudoCanonicalInput, Ty, TyCtxt, - TyCtxtFeed, + self, CrateInherentImpls, GenericArg, GenericArgsRef, PseudoCanonicalInput, SizedTraitKind, Ty, + TyCtxt, TyCtxtFeed, }; use crate::{dep_graph, mir, thir}; @@ -221,6 +277,14 @@ rustc_queries! { feedable } + /// Gives access to lints emitted during ast lowering. + /// + /// This can be conveniently accessed by `tcx.hir_*` methods. + /// Avoid calling this query directly. + query opt_ast_lowering_delayed_lints(key: hir::OwnerId) -> Option<&'tcx hir::lints::DelayedLints> { + desc { |tcx| "getting AST lowering delayed lints in `{}`", tcx.def_path_str(key) } + } + /// Returns the *default* of the const pararameter given by `DefId`. /// /// E.g., given `struct Ty<const N: usize = 3>;` this returns `3` for `N`. @@ -846,9 +910,10 @@ rustc_queries! { cache_on_disk_if { key.is_local() } separate_provide_extern } - - query adt_sized_constraint(key: DefId) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> { - desc { |tcx| "computing the `Sized` constraint for `{}`", tcx.def_path_str(key) } + query adt_sizedness_constraint( + key: (DefId, SizedTraitKind) + ) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> { + desc { |tcx| "computing the sizedness constraint for `{}`", tcx.def_path_str(key.0) } } query adt_dtorck_constraint( diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index 145561b76c4..ef5223de0e8 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -49,6 +49,13 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> { self, folder: &mut F, ) -> Result<Self, F::Error> { + // Perf testing has found that this check is slightly faster than + // folding and re-interning an empty `ExternalConstraintsData`. + // See: <https://github.com/rust-lang/rust/pull/142430>. + if self.is_empty() { + return Ok(self); + } + Ok(FallibleTypeFolder::cx(folder).mk_external_constraints(ExternalConstraintsData { region_constraints: self.region_constraints.clone().try_fold_with(folder)?, opaque_types: self @@ -64,6 +71,13 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> { } fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self { + // Perf testing has found that this check is slightly faster than + // folding and re-interning an empty `ExternalConstraintsData`. + // See: <https://github.com/rust-lang/rust/pull/142430>. + if self.is_empty() { + return self; + } + TypeFolder::cx(folder).mk_external_constraints(ExternalConstraintsData { region_constraints: self.region_constraints.clone().fold_with(folder), opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(), diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index d92b4f9c06b..6d5a3abf665 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -229,8 +229,12 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> { ) } - fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> { - self.sized_constraint(tcx) + fn sizedness_constraint( + self, + tcx: TyCtxt<'tcx>, + sizedness: ty::SizedTraitKind, + ) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> { + self.sizedness_constraint(tcx, sizedness) } fn is_fundamental(self) -> bool { @@ -634,10 +638,15 @@ impl<'tcx> AdtDef<'tcx> { tcx.adt_async_destructor(self.did()) } - /// Returns a type such that `Self: Sized` if and only if that type is `Sized`, - /// or `None` if the type is always sized. - pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> { - if self.is_struct() { tcx.adt_sized_constraint(self.did()) } else { None } + /// If this ADT is a struct, returns a type such that `Self: {Meta,Pointee,}Sized` if and only + /// if that type is `{Meta,Pointee,}Sized`, or `None` if this ADT is always + /// `{Meta,Pointee,}Sized`. + pub fn sizedness_constraint( + self, + tcx: TyCtxt<'tcx>, + sizedness: ty::SizedTraitKind, + ) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> { + if self.is_struct() { tcx.adt_sizedness_constraint((self.did(), sizedness)) } else { None } } } diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index e0f70737add..3d7965f6cfe 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -13,7 +13,7 @@ use std::marker::DiscriminantKind; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LocalDefId; -use rustc_serialize::{Decodable, Encodable}; +use rustc_serialize::{Decodable, Encodable, PointeeSized}; use rustc_span::source_map::Spanned; use rustc_span::{Span, SpanDecoder, SpanEncoder}; @@ -96,7 +96,7 @@ impl<'tcx, E: TyEncoder<'tcx>> EncodableWithShorthand<'tcx, E> for ty::Predicate /// /// `Decodable` can still be implemented in cases where `Decodable` is required /// by a trait bound. -pub trait RefDecodable<'tcx, D: TyDecoder<'tcx>> { +pub trait RefDecodable<'tcx, D: TyDecoder<'tcx>>: PointeeSized { fn decode(d: &mut D) -> &'tcx Self; } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 455ac660412..2fbaa2221a1 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -163,6 +163,10 @@ impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> { Const::new_bound(tcx, debruijn, var) } + fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderConst) -> Self { + Const::new_placeholder(tcx, placeholder) + } + fn new_unevaluated(interner: TyCtxt<'tcx>, uv: ty::UnevaluatedConst<'tcx>) -> Self { Const::new_unevaluated(interner, uv) } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index cb132ae5573..f1395c242f2 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -4,7 +4,7 @@ pub mod tls; -use std::assert_matches::{assert_matches, debug_assert_matches}; +use std::assert_matches::debug_assert_matches; use std::borrow::Borrow; use std::cmp::Ordering; use std::env::VarError; @@ -30,7 +30,7 @@ use rustc_data_structures::sync::{ self, DynSend, DynSync, FreezeReadGuard, Lock, RwLock, WorkerLocal, }; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan, + Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, LintEmitter, MultiSpan, }; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; @@ -43,11 +43,12 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::cache::WithDepNode; use rustc_query_system::dep_graph::DepNodeIndex; use rustc_query_system::ich::StableHashingContext; +use rustc_serialize::PointeeSized; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_session::config::CrateType; use rustc_session::cstore::{CrateStoreDyn, Untracked}; use rustc_session::lint::Lint; -use rustc_session::{Limit, MetadataKind, Session}; +use rustc_session::{Limit, Session}; use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_type_ir::TyKind::*; @@ -283,9 +284,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> { def_id: DefId, args: ty::GenericArgsRef<'tcx>, ) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) { - assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst); + debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst); let trait_def_id = self.parent(def_id); - assert_matches!(self.def_kind(trait_def_id), DefKind::Trait); + debug_assert_matches!(self.def_kind(trait_def_id), DefKind::Trait); let trait_generics = self.generics_of(trait_def_id); ( ty::TraitRef::new_from_args(self, trait_def_id, args.truncate_to(self, trait_generics)), @@ -774,7 +775,9 @@ bidirectional_lang_item_map! { FutureOutput, Iterator, Metadata, + MetaSized, Option, + PointeeSized, PointeeTrait, Poll, Sized, @@ -1350,8 +1353,8 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> { let bodies = Default::default(); let attrs = hir::AttributeMap::EMPTY; - let (opt_hash_including_bodies, _) = - self.tcx.hash_owner_nodes(node, &bodies, &attrs.map, attrs.define_opaque); + let (opt_hash_including_bodies, _, _) = + self.tcx.hash_owner_nodes(node, &bodies, &attrs.map, &[], attrs.define_opaque); let node = node.into(); self.opt_hir_owner_nodes(Some(self.tcx.arena.alloc(hir::OwnerNodes { opt_hash_including_bodies, @@ -1389,6 +1392,18 @@ pub struct TyCtxt<'tcx> { gcx: &'tcx GlobalCtxt<'tcx>, } +impl<'tcx> LintEmitter for TyCtxt<'tcx> { + fn emit_node_span_lint( + self, + lint: &'static Lint, + hir_id: HirId, + span: impl Into<MultiSpan>, + decorator: impl for<'a> LintDiagnostic<'a, ()>, + ) { + self.emit_node_span_lint(lint, hir_id, span, decorator); + } +} + // Explicitly implement `DynSync` and `DynSend` for `TyCtxt` to short circuit trait resolution. Its // field are asserted to implement these traits below, so this is trivially safe, and it greatly // speeds-up compilation of this crate and its dependents. @@ -1604,16 +1619,17 @@ impl<'tcx> TyCtxt<'tcx> { self.reserve_and_set_memory_dedup(alloc, salt) } + /// Traits added on all bounds by default, excluding `Sized` which is treated separately. pub fn default_traits(self) -> &'static [rustc_hir::LangItem] { - match self.sess.opts.unstable_opts.experimental_default_bounds { - true => &[ - LangItem::Sized, + if self.sess.opts.unstable_opts.experimental_default_bounds { + &[ LangItem::DefaultTrait1, LangItem::DefaultTrait2, LangItem::DefaultTrait3, LangItem::DefaultTrait4, - ], - false => &[LangItem::Sized], + ] + } else { + &[] } } @@ -1846,23 +1862,14 @@ impl<'tcx> TyCtxt<'tcx> { &self.crate_types } - pub fn metadata_kind(self) -> MetadataKind { - self.crate_types() - .iter() - .map(|ty| match *ty { - CrateType::Executable - | CrateType::Staticlib - | CrateType::Cdylib - | CrateType::Sdylib => MetadataKind::None, - CrateType::Rlib => MetadataKind::Uncompressed, - CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed, - }) - .max() - .unwrap_or(MetadataKind::None) - } - pub fn needs_metadata(self) -> bool { - self.metadata_kind() != MetadataKind::None + self.crate_types().iter().any(|ty| match *ty { + CrateType::Executable + | CrateType::Staticlib + | CrateType::Cdylib + | CrateType::Sdylib => false, + CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true, + }) } pub fn needs_crate_hash(self) -> bool { @@ -2535,17 +2542,17 @@ impl<'tcx> TyCtxt<'tcx> { // this type just holds a pointer to it, but it still effectively owns it. It // impls `Borrow` so that it can be looked up using the original // (non-arena-memory-owning) types. -struct InternedInSet<'tcx, T: ?Sized>(&'tcx T); +struct InternedInSet<'tcx, T: ?Sized + PointeeSized>(&'tcx T); -impl<'tcx, T: 'tcx + ?Sized> Clone for InternedInSet<'tcx, T> { +impl<'tcx, T: 'tcx + ?Sized + PointeeSized> Clone for InternedInSet<'tcx, T> { fn clone(&self) -> Self { InternedInSet(self.0) } } -impl<'tcx, T: 'tcx + ?Sized> Copy for InternedInSet<'tcx, T> {} +impl<'tcx, T: 'tcx + ?Sized + PointeeSized> Copy for InternedInSet<'tcx, T> {} -impl<'tcx, T: 'tcx + ?Sized> IntoPointer for InternedInSet<'tcx, T> { +impl<'tcx, T: 'tcx + ?Sized + PointeeSized> IntoPointer for InternedInSet<'tcx, T> { fn into_pointer(&self) -> *const () { self.0 as *const _ as *const () } diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index 5e038f91675..7d34d8df3f3 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -588,7 +588,7 @@ impl<'tcx> GenericArgs<'tcx> { } pub fn truncate_to(&self, tcx: TyCtxt<'tcx>, generics: &ty::Generics) -> GenericArgsRef<'tcx> { - tcx.mk_args_from_iter(self.iter().take(generics.count())) + tcx.mk_args(&self[..generics.count()]) } pub fn print_as_list(&self) -> String { diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 5ba4e5446e9..68adfb3cdb3 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -79,7 +79,7 @@ pub enum InstanceKind<'tcx> { Intrinsic(DefId), /// `<T as Trait>::method` where `method` receives unsizeable `self: Self` (part of the - /// `unsized_locals` feature). + /// `unsized_fn_params` feature). /// /// The generated shim will take `Self` via `*mut Self` - conceptually this is `&owned Self` - /// and dereference the argument to call the original function. diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index c2ae6b06192..13c281a6182 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -16,9 +16,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension}; use rustc_session::config::OptLevel; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use rustc_target::callconv::FnAbi; -use rustc_target::spec::{ - HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, PanicStrategy, Target, WasmCAbi, X86Abi, -}; +use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, PanicStrategy, Target, X86Abi}; use tracing::debug; use {rustc_abi as abi, rustc_hir as hir}; @@ -565,12 +563,6 @@ impl<'tcx> HasTargetSpec for TyCtxt<'tcx> { } } -impl<'tcx> HasWasmCAbiOpt for TyCtxt<'tcx> { - fn wasm_c_abi_opt(&self) -> WasmCAbi { - self.sess.opts.unstable_opts.wasm_c_abi - } -} - impl<'tcx> HasX86AbiOpt for TyCtxt<'tcx> { fn x86_abi_opt(&self) -> X86Abi { X86Abi { @@ -625,12 +617,6 @@ impl<'tcx> HasTargetSpec for LayoutCx<'tcx> { } } -impl<'tcx> HasWasmCAbiOpt for LayoutCx<'tcx> { - fn wasm_c_abi_opt(&self) -> WasmCAbi { - self.calc.cx.wasm_c_abi_opt() - } -} - impl<'tcx> HasX86AbiOpt for LayoutCx<'tcx> { fn x86_abi_opt(&self) -> X86Abi { self.calc.cx.x86_abi_opt() @@ -1266,6 +1252,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi) | RiscvInterruptS | CCmseNonSecureCall | CCmseNonSecureEntry + | Custom | Unadjusted => false, Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind, } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index dc3f2844e5a..97408e31854 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -60,6 +60,7 @@ pub use rustc_type_ir::fast_reject::DeepRejectCtxt; )] use rustc_type_ir::inherent; pub use rustc_type_ir::relate::VarianceDiagInfo; +pub use rustc_type_ir::solve::SizedTraitKind; pub use rustc_type_ir::*; #[allow(hidden_glob_reexports, unused_imports)] use rustc_type_ir::{InferCtxtLike, Interner}; @@ -933,7 +934,9 @@ impl Placeholder<BoundVar> { pub type PlaceholderRegion = Placeholder<BoundRegion>; -impl rustc_type_ir::inherent::PlaceholderLike for PlaceholderRegion { +impl<'tcx> rustc_type_ir::inherent::PlaceholderLike<TyCtxt<'tcx>> for PlaceholderRegion { + type Bound = BoundRegion; + fn universe(self) -> UniverseIndex { self.universe } @@ -946,14 +949,20 @@ impl rustc_type_ir::inherent::PlaceholderLike for PlaceholderRegion { Placeholder { universe: ui, ..self } } - fn new(ui: UniverseIndex, var: BoundVar) -> Self { + fn new(ui: UniverseIndex, bound: BoundRegion) -> Self { + Placeholder { universe: ui, bound } + } + + fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self { Placeholder { universe: ui, bound: BoundRegion { var, kind: BoundRegionKind::Anon } } } } pub type PlaceholderType = Placeholder<BoundTy>; -impl rustc_type_ir::inherent::PlaceholderLike for PlaceholderType { +impl<'tcx> rustc_type_ir::inherent::PlaceholderLike<TyCtxt<'tcx>> for PlaceholderType { + type Bound = BoundTy; + fn universe(self) -> UniverseIndex { self.universe } @@ -966,7 +975,11 @@ impl rustc_type_ir::inherent::PlaceholderLike for PlaceholderType { Placeholder { universe: ui, ..self } } - fn new(ui: UniverseIndex, var: BoundVar) -> Self { + fn new(ui: UniverseIndex, bound: BoundTy) -> Self { + Placeholder { universe: ui, bound } + } + + fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self { Placeholder { universe: ui, bound: BoundTy { var, kind: BoundTyKind::Anon } } } } @@ -980,7 +993,9 @@ pub struct BoundConst<'tcx> { pub type PlaceholderConst = Placeholder<BoundVar>; -impl rustc_type_ir::inherent::PlaceholderLike for PlaceholderConst { +impl<'tcx> rustc_type_ir::inherent::PlaceholderLike<TyCtxt<'tcx>> for PlaceholderConst { + type Bound = BoundVar; + fn universe(self) -> UniverseIndex { self.universe } @@ -993,7 +1008,11 @@ impl rustc_type_ir::inherent::PlaceholderLike for PlaceholderConst { Placeholder { universe: ui, ..self } } - fn new(ui: UniverseIndex, var: BoundVar) -> Self { + fn new(ui: UniverseIndex, bound: BoundVar) -> Self { + Placeholder { universe: ui, bound } + } + + fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self { Placeholder { universe: ui, bound: var } } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 673a89a8134..c10277c75a7 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1069,24 +1069,35 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let mut traits = FxIndexMap::default(); let mut fn_traits = FxIndexMap::default(); + let mut lifetimes = SmallVec::<[ty::Region<'tcx>; 1]>::new(); + let mut has_sized_bound = false; let mut has_negative_sized_bound = false; - let mut lifetimes = SmallVec::<[ty::Region<'tcx>; 1]>::new(); + let mut has_meta_sized_bound = false; for (predicate, _) in bounds.iter_instantiated_copied(tcx, args) { let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { ty::ClauseKind::Trait(pred) => { - // Don't print `+ Sized`, but rather `+ ?Sized` if absent. - if tcx.is_lang_item(pred.def_id(), LangItem::Sized) { - match pred.polarity { + // With `feature(sized_hierarchy)`, don't print `?Sized` as an alias for + // `MetaSized`, and skip sizedness bounds to be added at the end. + match tcx.as_lang_item(pred.def_id()) { + Some(LangItem::Sized) => match pred.polarity { ty::PredicatePolarity::Positive => { has_sized_bound = true; continue; } ty::PredicatePolarity::Negative => has_negative_sized_bound = true, + }, + Some(LangItem::MetaSized) => { + has_meta_sized_bound = true; + continue; + } + Some(LangItem::PointeeSized) => { + bug!("`PointeeSized` is removed during lowering"); } + _ => (), } self.insert_trait_and_projection( @@ -1255,8 +1266,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { })?; } + let using_sized_hierarchy = self.tcx().features().sized_hierarchy(); let add_sized = has_sized_bound && (first || has_negative_sized_bound); - let add_maybe_sized = !has_sized_bound && !has_negative_sized_bound; + let add_maybe_sized = + has_meta_sized_bound && !has_negative_sized_bound && !using_sized_hierarchy; + // Set `has_pointee_sized_bound` if there were no `Sized` or `MetaSized` bounds. + let has_pointee_sized_bound = + !has_sized_bound && !has_meta_sized_bound && !has_negative_sized_bound; if add_sized || add_maybe_sized { if !first { write!(self, " + ")?; @@ -1265,6 +1281,16 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { write!(self, "?")?; } write!(self, "Sized")?; + } else if has_meta_sized_bound && using_sized_hierarchy { + if !first { + write!(self, " + ")?; + } + write!(self, "MetaSized")?; + } else if has_pointee_sized_bound && using_sized_hierarchy { + if !first { + write!(self, " + ")?; + } + write!(self, "PointeeSized")?; } if !with_forced_trimmed_paths() { diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index 3e4f7a79d53..cc25cd16567 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -148,6 +148,10 @@ impl<'tcx> rustc_type_ir::inherent::Region<TyCtxt<'tcx>> for Region<'tcx> { Region::new_bound(tcx, debruijn, ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon }) } + fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderRegion) -> Self { + Region::new_placeholder(tcx, placeholder) + } + fn new_static(tcx: TyCtxt<'tcx>) -> Self { tcx.lifetimes.re_static } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index cbf545c01c5..58829f72a72 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -15,6 +15,7 @@ use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, extension}; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use rustc_type_ir::TyKind::*; +use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::walk::TypeWalker; use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind, TypeVisitableExt, elaborate}; use tracing::instrument; @@ -1677,7 +1678,7 @@ impl<'tcx> Ty<'tcx> { let Some(pointee_ty) = self.builtin_deref(true) else { bug!("Type {self:?} is not a pointer or reference type") }; - if pointee_ty.is_trivially_sized(tcx) { + if pointee_ty.has_trivial_sizedness(tcx, SizedTraitKind::Sized) { tcx.types.unit } else { match pointee_ty.ptr_metadata_ty_or_tail(tcx, |x| x) { @@ -1778,17 +1779,17 @@ impl<'tcx> Ty<'tcx> { } } - /// Fast path helper for testing if a type is `Sized`. + /// Fast path helper for testing if a type is `Sized` or `MetaSized`. /// - /// Returning true means the type is known to implement `Sized`. Returning `false` means - /// nothing -- could be sized, might not be. + /// Returning true means the type is known to implement the sizedness trait. Returning `false` + /// means nothing -- could be sized, might not be. /// /// Note that we could never rely on the fact that a type such as `[_]` is trivially `!Sized` /// because we could be in a type environment with a bound such as `[_]: Copy`. A function with /// such a bound obviously never can be called, but that doesn't mean it shouldn't typecheck. /// This is why this method doesn't return `Option<bool>`. #[instrument(skip(tcx), level = "debug")] - pub fn is_trivially_sized(self, tcx: TyCtxt<'tcx>) -> bool { + pub fn has_trivial_sizedness(self, tcx: TyCtxt<'tcx>, sizedness: SizedTraitKind) -> bool { match self.kind() { ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) @@ -1811,13 +1812,20 @@ impl<'tcx> Ty<'tcx> { | ty::Error(_) | ty::Dynamic(_, _, ty::DynStar) => true, - ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false, + ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) => match sizedness { + SizedTraitKind::Sized => false, + SizedTraitKind::MetaSized => true, + }, + + ty::Foreign(..) => match sizedness { + SizedTraitKind::Sized | SizedTraitKind::MetaSized => false, + }, - ty::Tuple(tys) => tys.last().is_none_or(|ty| ty.is_trivially_sized(tcx)), + ty::Tuple(tys) => tys.last().is_none_or(|ty| ty.has_trivial_sizedness(tcx, sizedness)), ty::Adt(def, args) => def - .sized_constraint(tcx) - .is_none_or(|ty| ty.instantiate(tcx, args).is_trivially_sized(tcx)), + .sizedness_constraint(tcx, sizedness) + .is_none_or(|ty| ty.instantiate(tcx, args).has_trivial_sizedness(tcx, sizedness)), ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) | ty::Bound(..) => false, diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 461d92f8006..51f57e71ce9 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -16,6 +16,7 @@ use rustc_index::bit_set::GrowableBitSet; use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension}; use rustc_session::Limit; use rustc_span::sym; +use rustc_type_ir::solve::SizedTraitKind; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -1132,7 +1133,8 @@ impl<'tcx> Ty<'tcx> { /// strange rules like `<T as Foo<'static>>::Bar: Sized` that /// actually carry lifetime requirements. pub fn is_sized(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool { - self.is_trivially_sized(tcx) || tcx.is_sized_raw(typing_env.as_query_input(self)) + self.has_trivial_sizedness(tcx, SizedTraitKind::Sized) + || tcx.is_sized_raw(typing_env.as_query_input(self)) } /// Checks whether values of this type `T` implement the `Freeze` diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index f8042174599..3853a804a92 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -66,7 +66,7 @@ impl<'tcx> TyCtxt<'tcx> { { type Result = ControlFlow<()>; - fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>( + fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>( &mut self, t: &Binder<'tcx, T>, ) -> Self::Result { @@ -168,7 +168,7 @@ impl LateBoundRegionsCollector { } impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector { - fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) { + fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) { self.current_index.shift_in(1); t.super_visit_with(self); self.current_index.shift_out(1); diff --git a/compiler/rustc_mir_build/src/builder/expr/as_operand.rs b/compiler/rustc_mir_build/src/builder/expr/as_operand.rs index 2059610ee47..982e7aa8246 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_operand.rs @@ -55,9 +55,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// local variable of unsized type. For example, consider this program: /// /// ``` - /// #![feature(unsized_locals, unsized_fn_params)] + /// #![feature(unsized_fn_params)] /// # use core::fmt::Debug; - /// fn foo(p: dyn Debug) { dbg!(p); } + /// fn foo(_p: dyn Debug) { /* ... */ } /// /// fn bar(box_p: Box<dyn Debug>) { foo(*box_p); } /// ``` @@ -84,7 +84,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// will actually provide a pointer to the interior of the box, and not move the `dyn Debug` /// value to the stack. /// - /// See #68304 for more details. + /// See <https://github.com/rust-lang/rust/issues/68304> for more details. pub(crate) fn as_local_call_operand( &mut self, block: BasicBlock, diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index d5d0d56f528..06c6b46a9c2 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1417,9 +1417,9 @@ fn check_field_tys_sized<'tcx>( coroutine_layout: &CoroutineLayout<'tcx>, def_id: LocalDefId, ) { - // No need to check if unsized_locals/unsized_fn_params is disabled, + // No need to check if unsized_fn_params is disabled, // since we will error during typeck. - if !tcx.features().unsized_locals() && !tcx.features().unsized_fn_params() { + if !tcx.features().unsized_fn_params() { return; } diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs index 6021e795d21..dc68629ec0d 100644 --- a/compiler/rustc_mir_transform/src/coroutine/drop.rs +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -132,6 +132,7 @@ fn build_poll_switch<'tcx>( body: &mut Body<'tcx>, poll_enum: Ty<'tcx>, poll_unit_place: &Place<'tcx>, + fut_pin_place: &Place<'tcx>, ready_block: BasicBlock, yield_block: BasicBlock, ) -> BasicBlock { @@ -162,9 +163,11 @@ fn build_poll_switch<'tcx>( Rvalue::Discriminant(*poll_unit_place), ))), }; + let storage_dead = + Statement { source_info, kind: StatementKind::StorageDead(fut_pin_place.local) }; let unreachable_block = insert_term_block(body, TerminatorKind::Unreachable); body.basic_blocks_mut().push(BasicBlockData { - statements: [discr_assign].to_vec(), + statements: [storage_dead, discr_assign].to_vec(), terminator: Some(Terminator { source_info, kind: TerminatorKind::SwitchInt { @@ -332,10 +335,17 @@ pub(super) fn expand_async_drops<'tcx>( kind: StatementKind::Assign(Box::new((context_ref_place, arg))), }); let yield_block = insert_term_block(body, TerminatorKind::Unreachable); // `kind` replaced later to yield - let switch_block = - build_poll_switch(tcx, body, poll_enum, &poll_unit_place, target, yield_block); let (pin_bb, fut_pin_place) = build_pin_fut(tcx, body, fut_place.clone(), UnwindAction::Continue); + let switch_block = build_poll_switch( + tcx, + body, + poll_enum, + &poll_unit_place, + &fut_pin_place, + target, + yield_block, + ); let call_bb = build_poll_call( tcx, body, @@ -357,16 +367,17 @@ pub(super) fn expand_async_drops<'tcx>( body.local_decls.push(LocalDecl::new(context_mut_ref, source_info.span)), ); let drop_yield_block = insert_term_block(body, TerminatorKind::Unreachable); // `kind` replaced later to yield + let (pin_bb2, fut_pin_place2) = + build_pin_fut(tcx, body, fut_place, UnwindAction::Continue); let drop_switch_block = build_poll_switch( tcx, body, poll_enum, &poll_unit_place, + &fut_pin_place2, drop.unwrap(), drop_yield_block, ); - let (pin_bb2, fut_pin_place2) = - build_pin_fut(tcx, body, fut_place, UnwindAction::Continue); let drop_call_bb = build_poll_call( tcx, body, diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 727d4a126d2..6d7b7e10ef6 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -50,6 +50,13 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { _ => {} } + // If the crate is likely to be mostly unused, use cross-crate inlining to defer codegen until + // the function is referenced, in order to skip codegen for unused functions. This is + // intentionally after the check for `inline(never)`, so that `inline(never)` wins. + if tcx.sess.opts.unstable_opts.hint_mostly_unused { + return true; + } + let sig = tcx.fn_sig(def_id).instantiate_identity(); for ty in sig.inputs().skip_binder().iter().chain(std::iter::once(&sig.output().skip_binder())) { diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index 3a5e2620b14..c9bc52c6c7e 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -390,6 +390,20 @@ where Location { block: self.succ, statement_index: 0 }, StatementKind::StorageDead(fut.local), ); + // StorageDead(fut) in unwind block (at the begin) + if let Unwind::To(block) = unwind { + self.elaborator.patch().add_statement( + Location { block, statement_index: 0 }, + StatementKind::StorageDead(fut.local), + ); + } + // StorageDead(fut) in dropline block (at the begin) + if let Some(block) = dropline { + self.elaborator.patch().add_statement( + Location { block, statement_index: 0 }, + StatementKind::StorageDead(fut.local), + ); + } // #1:pin_obj_bb >>> call Pin<ObjTy>::new_unchecked(&mut obj) self.elaborator.patch().patch_terminator( diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 92c30d239b5..bda71ceaa55 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -240,8 +240,6 @@ struct VnState<'body, 'tcx> { next_opaque: usize, /// Cache the deref values. derefs: Vec<VnIndex>, - /// Cache the value of the `unsized_locals` features, to avoid fetching it repeatedly in a loop. - feature_unsized_locals: bool, ssa: &'body SsaLocals, dominators: Dominators<BasicBlock>, reused_locals: DenseBitSet<Local>, @@ -273,7 +271,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { evaluated: IndexVec::with_capacity(num_values), next_opaque: 1, derefs: Vec::new(), - feature_unsized_locals: tcx.features().unsized_locals(), ssa, dominators, reused_locals: DenseBitSet::new_empty(local_decls.len()), @@ -329,13 +326,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn assign(&mut self, local: Local, value: VnIndex) { debug_assert!(self.ssa.is_ssa(local)); self.locals[local] = Some(value); - - // Only register the value if its type is `Sized`, as we will emit copies of it. - let is_sized = !self.feature_unsized_locals - || self.local_decls[local].ty.is_sized(self.tcx, self.typing_env()); - if is_sized { - self.rev_locals[value].push(local); - } + self.rev_locals[value].push(local); } fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 52f4c39c09b..fa29ab985b7 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -150,12 +150,12 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { }); terminator.kind = TerminatorKind::Goto { target }; } - sym::size_of | sym::min_align_of => { + sym::size_of | sym::align_of => { let target = target.unwrap(); let tp_ty = generic_args.type_at(0); let null_op = match intrinsic.name { sym::size_of => NullOp::SizeOf, - sym::min_align_of => NullOp::AlignOf, + sym::align_of => NullOp::AlignOf, _ => bug!("unexpected intrinsic"), }; block.statements.push(Statement { diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 8f88228d9bb..a54e548ad70 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -74,7 +74,11 @@ impl SimplifyCfg { } pub(super) fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - CfgSimplifier::new(tcx, body).simplify(); + if CfgSimplifier::new(tcx, body).simplify() { + // `simplify` returns that it changed something. We must invalidate the CFG caches as they + // are not consistent with the modified CFG any more. + body.basic_blocks.invalidate_cfg_cache(); + } remove_dead_blocks(body); // FIXME: Should probably be moved into some kind of pass manager @@ -121,12 +125,16 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { // Preserve `SwitchInt` reads on built and analysis MIR, or if `-Zmir-preserve-ub`. let preserve_switch_reads = matches!(body.phase, MirPhase::Built | MirPhase::Analysis(_)) || tcx.sess.opts.unstable_opts.mir_preserve_ub; - let basic_blocks = body.basic_blocks_mut(); + // Do not clear caches yet. The caller to `simplify` will do it if anything changed. + let basic_blocks = body.basic_blocks.as_mut_preserves_cfg(); CfgSimplifier { preserve_switch_reads, basic_blocks, pred_count } } - fn simplify(mut self) { + /// Returns whether we actually simplified anything. In that case, the caller *must* invalidate + /// the CFG caches of the MIR body. + #[must_use] + fn simplify(mut self) -> bool { self.strip_nops(); // Vec of the blocks that should be merged. We store the indices here, instead of the @@ -134,6 +142,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { // We do not push the statements directly into the target block (`bb`) as that is slower // due to additional reallocations let mut merged_blocks = Vec::new(); + let mut outer_changed = false; loop { let mut changed = false; @@ -177,7 +186,11 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { if !changed { break; } + + outer_changed = true; } + + outer_changed } /// This function will return `None` if diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl index 35bedf4318f..2bd19e81b01 100644 --- a/compiler/rustc_monomorphize/messages.ftl +++ b/compiler/rustc_monomorphize/messages.ftl @@ -60,11 +60,4 @@ monomorphize_start_not_found = using `fn main` requires the standard library monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined -monomorphize_wasm_c_abi_transition = - this function {$is_call -> - [true] call - *[false] definition - } involves an argument of type `{$ty}` which is affected by the wasm ABI transition - .help = the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target - monomorphize_written_to_path = the full type name has been written to '{$path}' diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index acf77b5916e..938c427b56c 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -100,12 +100,3 @@ pub(crate) struct AbiRequiredTargetFeature<'a> { /// Whether this is a problem at a call site or at a declaration. pub is_call: bool, } - -#[derive(LintDiagnostic)] -#[diag(monomorphize_wasm_c_abi_transition)] -#[help] -pub(crate) struct WasmCAbiTransition<'a> { - pub ty: Ty<'a>, - /// Whether this is a problem at a call site or at a declaration. - pub is_call: bool, -} diff --git a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs index 8dbbb4d1713..b8c001d357e 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs @@ -3,13 +3,10 @@ use rustc_abi::{BackendRepr, CanonAbi, RegKind, X86Call}; use rustc_hir::{CRATE_HIR_ID, HirId}; use rustc_middle::mir::{self, Location, traversal}; -use rustc_middle::ty::layout::LayoutCx; -use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt, TypingEnv}; -use rustc_session::lint::builtin::WASM_C_ABI; +use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt}; use rustc_span::def_id::DefId; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; -use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; -use rustc_target::spec::{HasWasmCAbiOpt, WasmCAbi}; +use rustc_target::callconv::{FnAbi, PassMode}; use crate::errors; @@ -81,73 +78,6 @@ fn do_check_simd_vector_abi<'tcx>( } } -/// Determines whether the given argument is passed the same way on the old and new wasm ABIs. -fn wasm_abi_safe<'tcx>(tcx: TyCtxt<'tcx>, arg: &ArgAbi<'tcx, Ty<'tcx>>) -> bool { - if matches!(arg.layout.backend_repr, BackendRepr::Scalar(_)) { - return true; - } - - // Both the old and the new ABIs treat vector types like `v128` the same - // way. - if uses_vector_registers(&arg.mode, &arg.layout.backend_repr) { - return true; - } - - // This matches `unwrap_trivial_aggregate` in the wasm ABI logic. - if arg.layout.is_aggregate() { - let cx = LayoutCx::new(tcx, TypingEnv::fully_monomorphized()); - if let Some(unit) = arg.layout.homogeneous_aggregate(&cx).ok().and_then(|ha| ha.unit()) { - let size = arg.layout.size; - // Ensure there's just a single `unit` element in `arg`. - if unit.size == size { - return true; - } - } - } - - // Zero-sized types are dropped in both ABIs, so they're safe - if arg.layout.is_zst() { - return true; - } - - false -} - -/// Warns against usage of `extern "C"` on wasm32-unknown-unknown that is affected by the -/// ABI transition. -fn do_check_wasm_abi<'tcx>( - tcx: TyCtxt<'tcx>, - abi: &FnAbi<'tcx, Ty<'tcx>>, - is_call: bool, - loc: impl Fn() -> (Span, HirId), -) { - // Only proceed for `extern "C" fn` on wasm32-unknown-unknown (same check as what - // `adjust_for_foreign_abi` uses to call `compute_wasm_abi_info`), and only proceed if - // `wasm_c_abi_opt` indicates we should emit the lint. - if !(tcx.sess.target.arch == "wasm32" - && tcx.sess.target.os == "unknown" - && tcx.wasm_c_abi_opt() == WasmCAbi::Legacy { with_lint: true } - && abi.conv == CanonAbi::C) - { - return; - } - // Warn against all types whose ABI will change. Return values are not affected by this change. - for arg_abi in abi.args.iter() { - if wasm_abi_safe(tcx, arg_abi) { - continue; - } - let (span, hir_id) = loc(); - tcx.emit_node_span_lint( - WASM_C_ABI, - hir_id, - span, - errors::WasmCAbiTransition { ty: arg_abi.layout.ty, is_call }, - ); - // Let's only warn once per function. - break; - } -} - /// Checks that the ABI of a given instance of a function does not contain vector-passed arguments /// or return values for which the corresponding target feature is not enabled. fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { @@ -173,7 +103,6 @@ fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { ) }; do_check_simd_vector_abi(tcx, abi, instance.def_id(), /*is_call*/ false, loc); - do_check_wasm_abi(tcx, abi, /*is_call*/ false, loc); } /// Checks that a call expression does not try to pass a vector-passed argument which requires a @@ -212,7 +141,6 @@ fn check_call_site_abi<'tcx>( return; }; do_check_simd_vector_abi(tcx, callee_abi, caller.def_id(), /*is_call*/ true, loc); - do_check_wasm_abi(tcx, callee_abi, /*is_call*/ true, loc); } fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &mir::Body<'tcx>) { diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index cea77533178..47ed9e87244 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -435,13 +435,13 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { }, ty::Placeholder(placeholder) => match self.canonicalize_mode { CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy( - PlaceholderLike::new(placeholder.universe(), self.variables.len().into()), + PlaceholderLike::new_anon(placeholder.universe(), self.variables.len().into()), ), CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder), }, ty::Param(_) => match self.canonicalize_mode { CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy( - PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()), + PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()), ), CanonicalizeMode::Response { .. } => panic!("param ty in response: {t:?}"), }, @@ -594,7 +594,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz }, ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode { CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst( - PlaceholderLike::new(placeholder.universe(), self.variables.len().into()), + PlaceholderLike::new_anon(placeholder.universe(), self.variables.len().into()), ), CanonicalizeMode::Response { .. } => { CanonicalVarKind::PlaceholderConst(placeholder) @@ -602,7 +602,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz }, ty::ConstKind::Param(_) => match self.canonicalize_mode { CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst( - PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()), + PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()), ), CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"), }, diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index 92cdc28a37b..e3f42c181fa 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -7,10 +7,12 @@ // tidy-alphabetical-start #![allow(rustc::usage_of_type_ir_inherent)] #![allow(rustc::usage_of_type_ir_traits)] +#![cfg_attr(not(bootstrap), allow(rustc::direct_use_of_rustc_type_ir))] // tidy-alphabetical-end pub mod canonicalizer; pub mod coherence; pub mod delegate; +pub mod placeholder; pub mod resolve; pub mod solve; diff --git a/compiler/rustc_next_trait_solver/src/placeholder.rs b/compiler/rustc_next_trait_solver/src/placeholder.rs new file mode 100644 index 00000000000..c88fb8defae --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/placeholder.rs @@ -0,0 +1,158 @@ +use core::panic; + +use rustc_type_ir::data_structures::IndexMap; +use rustc_type_ir::inherent::*; +use rustc_type_ir::{ + self as ty, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, +}; + +pub struct BoundVarReplacer<'a, Infcx, I = <Infcx as InferCtxtLike>::Interner> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, +{ + infcx: &'a Infcx, + // These three maps track the bound variable that were replaced by placeholders. It might be + // nice to remove these since we already have the `kind` in the placeholder; we really just need + // the `var` (but we *could* bring that into scope if we were to track them as we pass them). + mapped_regions: IndexMap<I::PlaceholderRegion, I::BoundRegion>, + mapped_types: IndexMap<I::PlaceholderTy, I::BoundTy>, + mapped_consts: IndexMap<I::PlaceholderConst, I::BoundConst>, + // The current depth relative to *this* folding, *not* the entire normalization. In other words, + // the depth of binders we've passed here. + current_index: ty::DebruijnIndex, + // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy: + // we don't actually create a universe until we see a bound var we have to replace. + universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>, +} + +impl<'a, Infcx, I> BoundVarReplacer<'a, Infcx, I> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, +{ + /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that + /// use a binding level above `universe_indices.len()`, we fail. + pub fn replace_bound_vars<T: TypeFoldable<I>>( + infcx: &'a Infcx, + universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>, + value: T, + ) -> ( + T, + IndexMap<I::PlaceholderRegion, I::BoundRegion>, + IndexMap<I::PlaceholderTy, I::BoundTy>, + IndexMap<I::PlaceholderConst, I::BoundConst>, + ) { + let mut replacer = BoundVarReplacer { + infcx, + mapped_regions: Default::default(), + mapped_types: Default::default(), + mapped_consts: Default::default(), + current_index: ty::INNERMOST, + universe_indices, + }; + + let value = value.fold_with(&mut replacer); + + (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts) + } + + fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex { + let infcx = self.infcx; + let index = + self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1; + let universe = self.universe_indices[index].unwrap_or_else(|| { + for i in self.universe_indices.iter_mut().take(index + 1) { + *i = i.or_else(|| Some(infcx.create_next_universe())) + } + self.universe_indices[index].unwrap() + }); + universe + } +} + +impl<Infcx, I> TypeFolder<I> for BoundVarReplacer<'_, Infcx, I> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, +{ + fn cx(&self) -> I { + self.infcx.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 + } + + fn fold_region(&mut self, r: I::Region) -> I::Region { + match r.kind() { + ty::ReBound(debruijn, _) + if debruijn.as_usize() + >= self.current_index.as_usize() + self.universe_indices.len() => + { + panic!( + "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); + } + ty::ReBound(debruijn, br) if debruijn >= self.current_index => { + let universe = self.universe_for(debruijn); + let p = PlaceholderLike::new(universe, br); + self.mapped_regions.insert(p, br); + Region::new_placeholder(self.cx(), p) + } + _ => r, + } + } + + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { + match t.kind() { + ty::Bound(debruijn, _) + if debruijn.as_usize() + 1 + > self.current_index.as_usize() + self.universe_indices.len() => + { + panic!( + "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); + } + ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { + let universe = self.universe_for(debruijn); + let p = PlaceholderLike::new(universe, bound_ty); + self.mapped_types.insert(p, bound_ty); + Ty::new_placeholder(self.cx(), p) + } + _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), + _ => t, + } + } + + fn fold_const(&mut self, ct: I::Const) -> I::Const { + match ct.kind() { + ty::ConstKind::Bound(debruijn, _) + if debruijn.as_usize() + 1 + > self.current_index.as_usize() + self.universe_indices.len() => + { + panic!( + "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); + } + ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => { + let universe = self.universe_for(debruijn); + let p = PlaceholderLike::new(universe, bound_const); + self.mapped_consts.insert(p, bound_const); + Const::new_placeholder(self.cx(), p) + } + _ => ct.super_fold_with(self), + } + } + + fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { + if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } + } +} 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 542e212e1bf..a300558c0c9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -8,6 +8,7 @@ use std::ops::ControlFlow; use derive_where::derive_where; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ self as ty, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _, TypeVisitor, TypingMode, Upcast as _, elaborate, @@ -203,13 +204,15 @@ where goal: Goal<I, Self>, ) -> Result<Candidate<I>, NoSolution>; - /// A type is `Sized` if its tail component is `Sized`. + /// A type is `Sized` if its tail component is `Sized` and a type is `MetaSized` if its tail + /// component is `MetaSized`. /// /// These components are given by built-in rules from - /// [`structural_traits::instantiate_constituent_tys_for_sized_trait`]. - fn consider_builtin_sized_candidate( + /// [`structural_traits::instantiate_constituent_tys_for_sizedness_trait`]. + fn consider_builtin_sizedness_candidates( ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, + sizedness: SizedTraitKind, ) -> Result<Candidate<I>, NoSolution>; /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. @@ -466,7 +469,15 @@ where G::consider_trait_alias_candidate(self, goal) } else { match cx.as_lang_item(trait_def_id) { - Some(TraitSolverLangItem::Sized) => G::consider_builtin_sized_candidate(self, goal), + Some(TraitSolverLangItem::Sized) => { + G::consider_builtin_sizedness_candidates(self, goal, SizedTraitKind::Sized) + } + Some(TraitSolverLangItem::MetaSized) => { + G::consider_builtin_sizedness_candidates(self, goal, SizedTraitKind::MetaSized) + } + Some(TraitSolverLangItem::PointeeSized) => { + unreachable!("`PointeeSized` is removed during lowering"); + } Some(TraitSolverLangItem::Copy | TraitSolverLangItem::Clone) => { G::consider_builtin_copy_clone_candidate(self, goal) } @@ -1014,7 +1025,11 @@ where return Ok(CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)); } - match assumption.visit_with(&mut FindParamInClause { ecx: self, param_env }) { + match assumption.visit_with(&mut FindParamInClause { + ecx: self, + param_env, + universes: vec![], + }) { ControlFlow::Break(Err(NoSolution)) => Err(NoSolution), ControlFlow::Break(Ok(())) => Ok(CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)), ControlFlow::Continue(()) => Ok(CandidateSource::ParamEnv(ParamEnvSource::Global)), @@ -1025,6 +1040,7 @@ where struct FindParamInClause<'a, 'b, D: SolverDelegate<Interner = I>, I: Interner> { ecx: &'a mut EvalCtxt<'b, D>, param_env: I::ParamEnv, + universes: Vec<Option<ty::UniverseIndex>>, } impl<D, I> TypeVisitor<I> for FindParamInClause<'_, '_, D, I> @@ -1034,31 +1050,42 @@ where { type Result = ControlFlow<Result<(), NoSolution>>; - fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result { - self.ecx.enter_forall(t.clone(), |ecx, v| { - v.visit_with(&mut FindParamInClause { ecx, param_env: self.param_env }) - }) + fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result { + self.universes.push(None); + t.super_visit_with(self)?; + self.universes.pop(); + ControlFlow::Continue(()) } fn visit_ty(&mut self, ty: I::Ty) -> Self::Result { + let ty = self.ecx.replace_bound_vars(ty, &mut self.universes); let Ok(ty) = self.ecx.structurally_normalize_ty(self.param_env, ty) else { return ControlFlow::Break(Err(NoSolution)); }; - if let ty::Placeholder(_) = ty.kind() { - ControlFlow::Break(Ok(())) + if let ty::Placeholder(p) = ty.kind() { + if p.universe() == ty::UniverseIndex::ROOT { + ControlFlow::Break(Ok(())) + } else { + ControlFlow::Continue(()) + } } else { ty.super_visit_with(self) } } fn visit_const(&mut self, ct: I::Const) -> Self::Result { + let ct = self.ecx.replace_bound_vars(ct, &mut self.universes); let Ok(ct) = self.ecx.structurally_normalize_const(self.param_env, ct) else { return ControlFlow::Break(Err(NoSolution)); }; - if let ty::ConstKind::Placeholder(_) = ct.kind() { - ControlFlow::Break(Ok(())) + if let ty::ConstKind::Placeholder(p) = ct.kind() { + if p.universe() == ty::UniverseIndex::ROOT { + ControlFlow::Break(Ok(())) + } else { + ControlFlow::Continue(()) + } } else { ct.super_visit_with(self) } @@ -1066,10 +1093,17 @@ where fn visit_region(&mut self, r: I::Region) -> Self::Result { match self.ecx.eager_resolve_region(r).kind() { - ty::ReStatic | ty::ReError(_) => ControlFlow::Continue(()), - ty::ReVar(_) | ty::RePlaceholder(_) => ControlFlow::Break(Ok(())), - ty::ReErased | ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReBound(..) => { - unreachable!() + ty::ReStatic | ty::ReError(_) | ty::ReBound(..) => ControlFlow::Continue(()), + ty::RePlaceholder(p) => { + if p.universe() == ty::UniverseIndex::ROOT { + ControlFlow::Break(Ok(())) + } else { + ControlFlow::Continue(()) + } + } + ty::ReVar(_) => ControlFlow::Break(Ok(())), + ty::ReErased | ty::ReEarlyParam(_) | ty::ReLateParam(_) => { + unreachable!("unexpected region in param-env clause") } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 4a2a7761f7f..f39d3226009 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -5,6 +5,7 @@ use derive_where::derive_where; use rustc_type_ir::data_structures::HashMap; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::solve::inspect::ProbeKind; use rustc_type_ir::{ self as ty, FallibleTypeFolder, Interner, Movability, Mutability, TypeFoldable, @@ -104,8 +105,9 @@ where } #[instrument(level = "trace", skip(ecx), ret)] -pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<D, I>( +pub(in crate::solve) fn instantiate_constituent_tys_for_sizedness_trait<D, I>( ecx: &EvalCtxt<'_, D>, + sizedness: SizedTraitKind, ty: I::Ty, ) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution> where @@ -113,8 +115,9 @@ where I: Interner, { match ty.kind() { - // impl Sized for u*, i*, bool, f*, FnDef, FnPtr, *(const/mut) T, char, &mut? T, [T; N], dyn* Trait, ! - // impl Sized for Coroutine, CoroutineWitness, Closure, CoroutineClosure + // impl {Meta,}Sized for u*, i*, bool, f*, FnDef, FnPtr, *(const/mut) T, char + // impl {Meta,}Sized for &mut? T, [T; N], dyn* Trait, !, Coroutine, CoroutineWitness + // impl {Meta,}Sized for Closure, CoroutineClosure ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) @@ -135,13 +138,16 @@ where | ty::Dynamic(_, _, ty::DynStar) | ty::Error(_) => Ok(ty::Binder::dummy(vec![])), - ty::Str - | ty::Slice(_) - | ty::Dynamic(..) - | ty::Foreign(..) - | ty::Alias(..) - | ty::Param(_) - | ty::Placeholder(..) => Err(NoSolution), + // impl {Meta,}Sized for str, [T], dyn Trait + ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness { + SizedTraitKind::Sized => Err(NoSolution), + SizedTraitKind::MetaSized => Ok(ty::Binder::dummy(vec![])), + }, + + // impl {} for extern type + ty::Foreign(..) => Err(NoSolution), + + ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => Err(NoSolution), ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { @@ -150,22 +156,27 @@ where ty::UnsafeBinder(bound_ty) => Ok(bound_ty.map_bound(|ty| vec![ty])), - // impl Sized for () - // impl Sized for (T1, T2, .., Tn) where Tn: Sized if n >= 1 + // impl {Meta,}Sized for () + // impl {Meta,}Sized for (T1, T2, .., Tn) where Tn: {Meta,}Sized if n >= 1 ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.last().map_or_else(Vec::new, |ty| vec![ty]))), - // impl Sized for Adt<Args...> where sized_constraint(Adt)<Args...>: Sized - // `sized_constraint(Adt)` is the deepest struct trail that can be determined - // by the definition of `Adt`, independent of the generic args. - // impl Sized for Adt<Args...> if sized_constraint(Adt) == None - // As a performance optimization, `sized_constraint(Adt)` can return `None` - // if the ADTs definition implies that it is sized by for all possible args. + // impl {Meta,}Sized for Adt<Args...> + // where {meta,pointee,}sized_constraint(Adt)<Args...>: {Meta,}Sized + // + // `{meta,pointee,}sized_constraint(Adt)` is the deepest struct trail that can be + // determined by the definition of `Adt`, independent of the generic args. + // + // impl {Meta,}Sized for Adt<Args...> + // if {meta,pointee,}sized_constraint(Adt) == None + // + // As a performance optimization, `{meta,pointee,}sized_constraint(Adt)` can return `None` + // if the ADTs definition implies that it is {meta,}sized by for all possible args. // In this case, the builtin impl will have no nested subgoals. This is a - // "best effort" optimization and `sized_constraint` may return `Some`, even - // if the ADT is sized for all possible args. + // "best effort" optimization and `{meta,pointee,}sized_constraint` may return `Some`, + // even if the ADT is {meta,pointee,}sized for all possible args. ty::Adt(def, args) => { - if let Some(sized_crit) = def.sized_constraint(ecx.cx()) { - Ok(ty::Binder::dummy(vec![sized_crit.instantiate(ecx.cx(), args)])) + if let Some(crit) = def.sizedness_constraint(ecx.cx(), sizedness) { + Ok(ty::Binder::dummy(vec![crit.instantiate(ecx.cx(), args)])) } else { Ok(ty::Binder::dummy(vec![])) } diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 8413c2abbb9..1690c908d12 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -4,6 +4,7 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::solve::inspect::ProbeKind; use rustc_type_ir::{self as ty, Interner, elaborate}; use tracing::instrument; @@ -198,11 +199,12 @@ where unreachable!("trait aliases are never const") } - fn consider_builtin_sized_candidate( + fn consider_builtin_sizedness_candidates( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal<I, Self>, + _sizedness: SizedTraitKind, ) -> Result<Candidate<I>, NoSolution> { - unreachable!("Sized is never const") + unreachable!("Sized/MetaSized is never const") } fn consider_builtin_copy_clone_candidate( 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 345ece20b7e..7ead0a6d6b7 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 @@ -19,6 +19,7 @@ use tracing::{debug, instrument, trace}; use super::has_only_region_constraints; use crate::coherence; use crate::delegate::SolverDelegate; +use crate::placeholder::BoundVarReplacer; use crate::solve::inspect::{self, ProofTreeBuilder}; use crate::solve::search_graph::SearchGraph; use crate::solve::{ @@ -1232,6 +1233,14 @@ where ) -> Result<Certainty, NoSolution> { self.delegate.is_transmutable(dst, src, assume) } + + pub(super) fn replace_bound_vars<T: TypeFoldable<I>>( + &self, + t: T, + universes: &mut Vec<Option<ty::UniverseIndex>>, + ) -> T { + BoundVarReplacer::replace_bound_vars(&**self.delegate, universes, t).0 + } } /// Eagerly replace aliases with inference variables, emitting `AliasRelate` 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 2fddc0044cb..d51c87fe68e 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,6 +6,7 @@ mod opaque_types; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{self as ty, Interner, NormalizesTo, PredicateKind, Upcast as _}; use tracing::instrument; @@ -413,11 +414,12 @@ where panic!("trait aliases do not have associated types: {:?}", goal); } - fn consider_builtin_sized_candidate( + fn consider_builtin_sizedness_candidates( _ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, + _sizedness: SizedTraitKind, ) -> Result<Candidate<I>, NoSolution> { - panic!("`Sized` does not have an associated type: {:?}", goal); + panic!("`Sized`/`MetaSized` does not have an associated type: {:?}", goal); } fn consider_builtin_copy_clone_candidate( 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 110c67a8e21..8ee116b090d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -4,9 +4,9 @@ use rustc_type_ir::data_structures::IndexSet; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; -use rustc_type_ir::solve::CanonicalResponse; +use rustc_type_ir::solve::{CanonicalResponse, SizedTraitKind}; use rustc_type_ir::{ - self as ty, Interner, Movability, TraitPredicate, TypeVisitableExt as _, TypingMode, + self as ty, Interner, Movability, TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode, Upcast as _, elaborate, }; use tracing::{debug, instrument, trace}; @@ -131,9 +131,11 @@ where assumption: I::Clause, ) -> Result<(), NoSolution> { if let Some(trait_clause) = assumption.as_trait_clause() { - if trait_clause.def_id() == goal.predicate.def_id() - && trait_clause.polarity() == goal.predicate.polarity - { + if trait_clause.polarity() != goal.predicate.polarity { + return Err(NoSolution); + } + + if trait_clause.def_id() == goal.predicate.def_id() { if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( goal.predicate.trait_ref.args, trait_clause.skip_binder().trait_ref.args, @@ -141,6 +143,17 @@ where return Ok(()); } } + + // PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so + // check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds + // are syntactic sugar for a lack of bounds so don't need this. + if ecx.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::MetaSized) + && ecx.cx().is_lang_item(trait_clause.def_id(), TraitSolverLangItem::Sized) + { + let meta_sized_clause = + trait_predicate_with_def_id(ecx.cx(), trait_clause, goal.predicate.def_id()); + return Self::fast_reject_assumption(ecx, goal, meta_sized_clause); + } } Err(NoSolution) @@ -154,6 +167,17 @@ where ) -> QueryResult<I> { let trait_clause = assumption.as_trait_clause().unwrap(); + // PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so + // check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds + // are syntactic sugar for a lack of bounds so don't need this. + if ecx.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::MetaSized) + && ecx.cx().is_lang_item(trait_clause.def_id(), TraitSolverLangItem::Sized) + { + let meta_sized_clause = + trait_predicate_with_def_id(ecx.cx(), trait_clause, goal.predicate.def_id()); + return Self::match_assumption(ecx, goal, meta_sized_clause, then); + } + let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause); ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?; @@ -245,9 +269,10 @@ where }) } - fn consider_builtin_sized_candidate( + fn consider_builtin_sizedness_candidates( ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, + sizedness: SizedTraitKind, ) -> Result<Candidate<I>, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); @@ -256,7 +281,11 @@ where ecx.probe_and_evaluate_goal_for_constituent_tys( CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial), goal, - structural_traits::instantiate_constituent_tys_for_sized_trait, + |ecx, ty| { + structural_traits::instantiate_constituent_tys_for_sizedness_trait( + ecx, sizedness, ty, + ) + }, ) } @@ -812,6 +841,25 @@ where } } +/// Small helper function to change the `def_id` of a trait predicate - this is not normally +/// something that you want to do, as different traits will require different args and so making +/// it easy to change the trait is something of a footgun, but it is useful in the narrow +/// circumstance of changing from `MetaSized` to `Sized`, which happens as part of the lazy +/// elaboration of sizedness candidates. +#[inline(always)] +fn trait_predicate_with_def_id<I: Interner>( + cx: I, + clause: ty::Binder<I, ty::TraitPredicate<I>>, + did: I::DefId, +) -> I::Clause { + clause + .map_bound(|c| TraitPredicate { + trait_ref: TraitRef::new_from_args(cx, did, c.trait_ref.args), + polarity: c.polarity, + }) + .upcast(cx) +} + impl<D, I> EvalCtxt<'_, D> where D: SolverDelegate<Interner = I>, diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 1f221b4bf78..f6e0b08b140 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -299,10 +299,12 @@ parse_float_literal_unsupported_base = {$base} float literal is not supported parse_fn_pointer_cannot_be_async = an `fn` pointer type cannot be `async` .label = `async` because of this .suggestion = remove the `async` qualifier + .note = allowed qualifiers are: `unsafe` and `extern` parse_fn_pointer_cannot_be_const = an `fn` pointer type cannot be `const` .label = `const` because of this .suggestion = remove the `const` qualifier + .note = allowed qualifiers are: `unsafe` and `extern` parse_fn_ptr_with_generics = function pointer types may not have generic parameters .suggestion = consider moving the lifetime {$arity -> diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 2dba568a258..0f0c5434800 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2938,22 +2938,22 @@ pub(crate) struct DynAfterMut { #[derive(Diagnostic)] #[diag(parse_fn_pointer_cannot_be_const)] +#[note] pub(crate) struct FnPointerCannotBeConst { #[primary_span] - pub span: Span, #[label] - pub qualifier: Span, + pub span: Span, #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] pub suggestion: Span, } #[derive(Diagnostic)] #[diag(parse_fn_pointer_cannot_be_async)] +#[note] pub(crate) struct FnPointerCannotBeAsync { #[primary_span] - pub span: Span, #[label] - pub qualifier: Span, + pub span: Span, #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] pub suggestion: Span, } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index b49a13ce584..a64bb424117 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -686,23 +686,34 @@ impl<'a> Parser<'a> { } if let token::DocComment(kind, style, _) = self.token.kind { - // We have something like `expr //!val` where the user likely meant `expr // !val` - let pos = self.token.span.lo() + BytePos(2); - let span = self.token.span.with_lo(pos).with_hi(pos); - err.span_suggestion_verbose( - span, - format!( - "add a space before {} to write a regular comment", - match (kind, style) { - (token::CommentKind::Line, ast::AttrStyle::Inner) => "`!`", - (token::CommentKind::Block, ast::AttrStyle::Inner) => "`!`", - (token::CommentKind::Line, ast::AttrStyle::Outer) => "the last `/`", - (token::CommentKind::Block, ast::AttrStyle::Outer) => "the last `*`", - }, - ), - " ".to_string(), - Applicability::MachineApplicable, - ); + // This is to avoid suggesting converting a doc comment to a regular comment + // when missing a comma before the doc comment in lists (#142311): + // + // ``` + // enum Foo{ + // A /// xxxxxxx + // B, + // } + // ``` + if !expected.contains(&TokenType::Comma) { + // We have something like `expr //!val` where the user likely meant `expr // !val` + let pos = self.token.span.lo() + BytePos(2); + let span = self.token.span.with_lo(pos).with_hi(pos); + err.span_suggestion_verbose( + span, + format!( + "add a space before {} to write a regular comment", + match (kind, style) { + (token::CommentKind::Line, ast::AttrStyle::Inner) => "`!`", + (token::CommentKind::Block, ast::AttrStyle::Inner) => "`!`", + (token::CommentKind::Line, ast::AttrStyle::Outer) => "the last `/`", + (token::CommentKind::Block, ast::AttrStyle::Outer) => "the last `*`", + }, + ), + " ".to_string(), + Applicability::MaybeIncorrect, + ); + } } let sp = if self.token == token::Eof { @@ -2273,23 +2284,18 @@ impl<'a> Parser<'a> { ), // Also catches `fn foo(&a)`. PatKind::Ref(ref inner_pat, mutab) - if matches!(inner_pat.clone().kind, PatKind::Ident(..)) => + if let PatKind::Ident(_, ident, _) = inner_pat.clone().kind => { - match inner_pat.clone().kind { - PatKind::Ident(_, ident, _) => { - let mutab = mutab.prefix_str(); - ( - ident, - "self: ", - format!("{ident}: &{mutab}TypeName"), - "_: ", - pat.span.shrink_to_lo(), - pat.span, - pat.span.shrink_to_lo(), - ) - } - _ => unreachable!(), - } + let mutab = mutab.prefix_str(); + ( + ident, + "self: ", + format!("{ident}: &{mutab}TypeName"), + "_: ", + pat.span.shrink_to_lo(), + pat.span, + pat.span.shrink_to_lo(), + ) } _ => { // Otherwise, try to get a type and emit a suggestion. diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 93489aa8ee9..93c76c47f06 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -4087,7 +4087,7 @@ impl<'a> CondChecker<'a> { } impl MutVisitor for CondChecker<'_> { - fn visit_expr(&mut self, e: &mut P<Expr>) { + fn visit_expr(&mut self, e: &mut Expr) { self.depth += 1; use ForbiddenLetReason::*; diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index a325c2a57ab..658ed4bd41c 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -23,7 +23,7 @@ use super::{ AttrWrapper, ExpKeywordPair, ExpTokenPair, FollowedByType, ForceCollect, Parser, PathStyle, Recovered, Trailing, UsePreAttrPos, }; -use crate::errors::{self, MacroExpandsToAdtField}; +use crate::errors::{self, FnPointerCannotBeAsync, FnPointerCannotBeConst, MacroExpandsToAdtField}; use crate::{exp, fluent_generated as fluent}; impl<'a> Parser<'a> { @@ -2402,7 +2402,7 @@ impl<'a> Parser<'a> { case: Case, ) -> PResult<'a, (Ident, FnSig, Generics, Option<P<FnContract>>, Option<P<Block>>)> { let fn_span = self.token.span; - let header = self.parse_fn_front_matter(vis, case)?; // `const ... fn` + let header = self.parse_fn_front_matter(vis, case, FrontMatterParsingMode::Function)?; // `const ... fn` let ident = self.parse_ident()?; // `foo` let mut generics = self.parse_generics()?; // `<'a, T, ...>` let decl = match self.parse_fn_decl( @@ -2658,16 +2658,37 @@ impl<'a> Parser<'a> { /// /// `vis` represents the visibility that was already parsed, if any. Use /// `Visibility::Inherited` when no visibility is known. + /// + /// If `parsing_mode` is `FrontMatterParsingMode::FunctionPtrType`, we error on `const` and `async` qualifiers, + /// which are not allowed in function pointer types. pub(super) fn parse_fn_front_matter( &mut self, orig_vis: &Visibility, case: Case, + parsing_mode: FrontMatterParsingMode, ) -> PResult<'a, FnHeader> { let sp_start = self.token.span; let constness = self.parse_constness(case); + if parsing_mode == FrontMatterParsingMode::FunctionPtrType + && let Const::Yes(const_span) = constness + { + self.dcx().emit_err(FnPointerCannotBeConst { + span: const_span, + suggestion: const_span.until(self.token.span), + }); + } let async_start_sp = self.token.span; let coroutine_kind = self.parse_coroutine_kind(case); + if parsing_mode == FrontMatterParsingMode::FunctionPtrType + && let Some(ast::CoroutineKind::Async { span: async_span, .. }) = coroutine_kind + { + self.dcx().emit_err(FnPointerCannotBeAsync { + span: async_span, + suggestion: async_span.until(self.token.span), + }); + } + // FIXME(gen_blocks): emit a similar error for `gen fn()` let unsafe_start_sp = self.token.span; let safety = self.parse_safety(case); @@ -2703,6 +2724,11 @@ impl<'a> Parser<'a> { enum WrongKw { Duplicated(Span), Misplaced(Span), + /// `MisplacedDisallowedQualifier` is only used instead of `Misplaced`, + /// when the misplaced keyword is disallowed by the current `FrontMatterParsingMode`. + /// In this case, we avoid generating the suggestion to swap around the keywords, + /// as we already generated a suggestion to remove the keyword earlier. + MisplacedDisallowedQualifier, } // We may be able to recover @@ -2716,7 +2742,21 @@ impl<'a> Parser<'a> { Const::Yes(sp) => Some(WrongKw::Duplicated(sp)), Const::No => { recover_constness = Const::Yes(self.token.span); - Some(WrongKw::Misplaced(async_start_sp)) + match parsing_mode { + FrontMatterParsingMode::Function => { + Some(WrongKw::Misplaced(async_start_sp)) + } + FrontMatterParsingMode::FunctionPtrType => { + self.dcx().emit_err(FnPointerCannotBeConst { + span: self.token.span, + suggestion: self + .token + .span + .with_lo(self.prev_token.span.hi()), + }); + Some(WrongKw::MisplacedDisallowedQualifier) + } + } } } } else if self.check_keyword(exp!(Async)) { @@ -2742,7 +2782,21 @@ impl<'a> Parser<'a> { closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID, }); - Some(WrongKw::Misplaced(unsafe_start_sp)) + match parsing_mode { + FrontMatterParsingMode::Function => { + Some(WrongKw::Misplaced(async_start_sp)) + } + FrontMatterParsingMode::FunctionPtrType => { + self.dcx().emit_err(FnPointerCannotBeAsync { + span: self.token.span, + suggestion: self + .token + .span + .with_lo(self.prev_token.span.hi()), + }); + Some(WrongKw::MisplacedDisallowedQualifier) + } + } } } } else if self.check_keyword(exp!(Unsafe)) { @@ -2840,14 +2894,20 @@ impl<'a> Parser<'a> { // FIXME(gen_blocks): add keyword recovery logic for genness - if wrong_kw.is_some() + if let Some(wrong_kw) = wrong_kw && self.may_recover() && self.look_ahead(1, |tok| tok.is_keyword_case(kw::Fn, case)) { // Advance past the misplaced keyword and `fn` self.bump(); self.bump(); - err.emit(); + // When we recover from a `MisplacedDisallowedQualifier`, we already emitted an error for the disallowed qualifier + // So we don't emit another error that the qualifier is unexpected. + if matches!(wrong_kw, WrongKw::MisplacedDisallowedQualifier) { + err.cancel(); + } else { + err.emit(); + } return Ok(FnHeader { constness: recover_constness, safety: recover_safety, @@ -3194,3 +3254,12 @@ enum IsMacroRulesItem { Yes { has_bang: bool }, No, } + +#[derive(Copy, Clone, PartialEq, Eq)] +pub(super) enum FrontMatterParsingMode { + /// Parse the front matter of a function declaration + Function, + /// Parse the front matter of a function pointet type. + /// For function pointer types, the `const` and `async` keywords are not permitted. + FunctionPtrType, +} diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 7a226136e23..64653ee2a04 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1094,7 +1094,7 @@ impl<'a> Parser<'a> { fn make_all_value_bindings_mutable(pat: &mut P<Pat>) -> bool { struct AddMut(bool); impl MutVisitor for AddMut { - fn visit_pat(&mut self, pat: &mut P<Pat>) { + fn visit_pat(&mut self, pat: &mut Pat) { if let PatKind::Ident(BindingMode(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 9ddfc179e9b..620a34044d1 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -15,10 +15,11 @@ use thin_vec::{ThinVec, thin_vec}; use super::{Parser, PathStyle, SeqSep, TokenType, Trailing}; use crate::errors::{ self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType, - FnPointerCannotBeAsync, FnPointerCannotBeConst, FnPtrWithGenerics, FnPtrWithGenericsSugg, - HelpUseLatestEdition, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, - NestedCVariadicType, ReturnTypesUseThinArrow, + FnPtrWithGenerics, FnPtrWithGenericsSugg, HelpUseLatestEdition, InvalidDynKeyword, + LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, + ReturnTypesUseThinArrow, }; +use crate::parser::item::FrontMatterParsingMode; use crate::{exp, maybe_recover_from_interpolated_ty_qpath}; /// Signals whether parsing a type should allow `+`. @@ -669,62 +670,16 @@ impl<'a> Parser<'a> { tokens: None, }; let span_start = self.token.span; - let ast::FnHeader { ext, safety, constness, coroutine_kind } = - self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?; - let fn_start_lo = self.prev_token.span.lo(); + let ast::FnHeader { ext, safety, .. } = self.parse_fn_front_matter( + &inherited_vis, + Case::Sensitive, + FrontMatterParsingMode::FunctionPtrType, + )?; if self.may_recover() && self.token == TokenKind::Lt { self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; } let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; - let whole_span = lo.to(self.prev_token.span); - - // Order/parsing of "front matter" follows: - // `<constness> <coroutine_kind> <safety> <extern> fn()` - // ^ ^ ^ ^ ^ - // | | | | fn_start_lo - // | | | ext_sp.lo - // | | safety_sp.lo - // | coroutine_sp.lo - // const_sp.lo - if let ast::Const::Yes(const_span) = constness { - let next_token_lo = if let Some( - ast::CoroutineKind::Async { span, .. } - | ast::CoroutineKind::Gen { span, .. } - | ast::CoroutineKind::AsyncGen { span, .. }, - ) = coroutine_kind - { - span.lo() - } else if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety { - span.lo() - } else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext { - span.lo() - } else { - fn_start_lo - }; - let sugg_span = const_span.with_hi(next_token_lo); - self.dcx().emit_err(FnPointerCannotBeConst { - span: whole_span, - qualifier: const_span, - suggestion: sugg_span, - }); - } - if let Some(ast::CoroutineKind::Async { span: async_span, .. }) = coroutine_kind { - let next_token_lo = if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety - { - span.lo() - } else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext { - span.lo() - } else { - fn_start_lo - }; - let sugg_span = async_span.with_hi(next_token_lo); - self.dcx().emit_err(FnPointerCannotBeAsync { - span: whole_span, - qualifier: async_span, - suggestion: sugg_span, - }); - } - // FIXME(gen_blocks): emit a similar error for `gen fn()` + let decl_span = span_start.to(self.prev_token.span); Ok(TyKind::BareFn(P(BareFnTy { ext, safety, generic_params: params, decl, decl_span }))) } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 555ab3cdb2b..b3096e46b09 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -282,11 +282,22 @@ fn emit_malformed_attribute( name: Symbol, template: AttributeTemplate, ) { + // attrs with new parsers are locally validated so excluded here + if matches!( + name, + sym::inline + | sym::rustc_force_inline + | sym::rustc_confusables + | sym::repr + | sym::deprecated + ) { + return; + } + // Some of previously accepted forms were used in practice, // report them as warnings for now. - let should_warn = |name| { - matches!(name, sym::doc | sym::ignore | sym::inline | sym::link | sym::test | sym::bench) - }; + let should_warn = + |name| matches!(name, sym::doc | sym::ignore | sym::link | sym::test | sym::bench); let error_msg = format!("malformed `{name}` attribute input"); let mut suggestions = vec![]; diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 9dd064aca66..42bd0f5d847 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -29,58 +29,45 @@ pub enum ParseMode { Format, /// An inline assembly template string for `asm!`. InlineAsm, + /// A format string for use in diagnostic attributes. + /// + /// Similar to `format_args!`, however only named ("captured") arguments + /// are allowed, and no format modifiers are permitted. + Diagnostic, } /// A piece is a portion of the format string which represents the next part /// to emit. These are emitted as a stream by the `Parser` class. #[derive(Clone, Debug, PartialEq)] -pub enum Piece<'a> { +pub enum Piece<'input> { /// A literal string which should directly be emitted - Lit(&'a str), + Lit(&'input str), /// This describes that formatting should process the next argument (as /// specified inside) for emission. - NextArgument(Box<Argument<'a>>), + NextArgument(Box<Argument<'input>>), } /// Representation of an argument specification. #[derive(Clone, Debug, PartialEq)] -pub struct Argument<'a> { +pub struct Argument<'input> { /// Where to find this argument - pub position: Position<'a>, + pub position: Position<'input>, /// The span of the position indicator. Includes any whitespace in implicit /// positions (`{ }`). pub position_span: Range<usize>, /// How to format the argument - pub format: FormatSpec<'a>, + pub format: FormatSpec<'input>, } -impl<'a> Argument<'a> { +impl<'input> Argument<'input> { pub fn is_identifier(&self) -> bool { - matches!(self.position, Position::ArgumentNamed(_)) - && matches!( - self.format, - FormatSpec { - fill: None, - fill_span: None, - align: AlignUnknown, - sign: None, - alternate: false, - zero_pad: false, - debug_hex: None, - precision: CountImplied, - precision_span: None, - width: CountImplied, - width_span: None, - ty: "", - ty_span: None, - }, - ) + matches!(self.position, Position::ArgumentNamed(_)) && self.format == FormatSpec::default() } } /// Specification for the formatting of an argument in the format string. -#[derive(Clone, Debug, PartialEq)] -pub struct FormatSpec<'a> { +#[derive(Clone, Debug, PartialEq, Default)] +pub struct FormatSpec<'input> { /// Optionally specified character to fill alignment with. pub fill: Option<char>, /// Span of the optionally specified fill character. @@ -96,30 +83,30 @@ pub struct FormatSpec<'a> { /// The `x` or `X` flag. (Only for `Debug`.) pub debug_hex: Option<DebugHex>, /// The integer precision to use. - pub precision: Count<'a>, + pub precision: Count<'input>, /// The span of the precision formatting flag (for diagnostics). pub precision_span: Option<Range<usize>>, /// The string width requested for the resulting format. - pub width: Count<'a>, + pub width: Count<'input>, /// The span of the width formatting flag (for diagnostics). pub width_span: Option<Range<usize>>, /// The descriptor string representing the name of the format desired for /// this argument, this can be empty or any number of characters, although /// it is required to be one word. - pub ty: &'a str, + pub ty: &'input str, /// The span of the descriptor string (for diagnostics). pub ty_span: Option<Range<usize>>, } /// Enum describing where an argument for a format can be located. #[derive(Clone, Debug, PartialEq)] -pub enum Position<'a> { +pub enum Position<'input> { /// The argument is implied to be located at an index ArgumentImplicitlyIs(usize), /// The argument is located at a specific index given in the format, ArgumentIs(usize), /// The argument has a name. - ArgumentNamed(&'a str), + ArgumentNamed(&'input str), } impl Position<'_> { @@ -132,7 +119,7 @@ impl Position<'_> { } /// Enum of alignments which are supported. -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Default)] pub enum Alignment { /// The value will be aligned to the left. AlignLeft, @@ -141,6 +128,7 @@ pub enum Alignment { /// The value will be aligned in the center. AlignCenter, /// The value will take on a default alignment. + #[default] AlignUnknown, } @@ -164,17 +152,18 @@ pub enum DebugHex { /// A count is used for the precision and width parameters of an integer, and /// can reference either an argument or a literal integer. -#[derive(Clone, Debug, PartialEq)] -pub enum Count<'a> { +#[derive(Clone, Debug, PartialEq, Default)] +pub enum Count<'input> { /// The count is specified explicitly. CountIs(u16), /// The count is specified by the argument with the given name. - CountIsName(&'a str, Range<usize>), + CountIsName(&'input str, Range<usize>), /// The count is specified by the argument at the given index. CountIsParam(usize), /// The count is specified by a star (like in `{:.*}`) that refers to the argument at the given index. CountIsStar(usize), /// The count is implied and cannot be explicitly specified. + #[default] CountImplied, } @@ -208,10 +197,10 @@ pub enum Suggestion { /// /// This is a recursive-descent parser for the sake of simplicity, and if /// necessary there's probably lots of room for improvement performance-wise. -pub struct Parser<'a> { +pub struct Parser<'input> { mode: ParseMode, /// Input to be parsed - input: &'a str, + input: &'input str, /// Tuples of the span in the code snippet (input as written before being unescaped), the pos in input, and the char in input input_vec: Vec<(Range<usize>, usize, char)>, /// Index into input_vec @@ -237,15 +226,15 @@ pub struct Parser<'a> { pub line_spans: Vec<Range<usize>>, } -impl<'a> Iterator for Parser<'a> { - type Item = Piece<'a>; +impl<'input> Iterator for Parser<'input> { + type Item = Piece<'input>; - fn next(&mut self) -> Option<Piece<'a>> { - if let Some(&(Range { start, end }, idx, ch)) = self.input_vec.get(self.input_vec_index) { + fn next(&mut self) -> Option<Piece<'input>> { + if let Some((Range { start, end }, idx, ch)) = self.peek() { match ch { '{' => { self.input_vec_index += 1; - if let Some(&(_, i, '{')) = self.input_vec.get(self.input_vec_index) { + if let Some((_, i, '{')) = self.peek() { self.input_vec_index += 1; // double open brace escape: "{{" // next state after this is either end-of-input or seen-a-brace @@ -254,25 +243,21 @@ impl<'a> Iterator for Parser<'a> { // single open brace self.last_open_brace = Some(start..end); let arg = self.argument(); - if let Some(close_brace_range) = self.consume_closing_brace(&arg) { + self.ws(); + if let Some((close_brace_range, _)) = self.consume_pos('}') { if self.is_source_literal { self.arg_places.push(start..close_brace_range.end); } - } else if let Some(&(_, _, c)) = self.input_vec.get(self.input_vec_index) { - match c { - '?' => self.suggest_format_debug(), - '<' | '^' | '>' => self.suggest_format_align(c), - _ => { - self.suggest_positional_arg_instead_of_captured_arg(arg.clone()) - } - } + } else { + self.missing_closing_brace(&arg); } + Some(Piece::NextArgument(Box::new(arg))) } } '}' => { self.input_vec_index += 1; - if let Some(&(_, i, '}')) = self.input_vec.get(self.input_vec_index) { + if let Some((_, i, '}')) = self.peek() { self.input_vec_index += 1; // double close brace escape: "}}" // next state after this is either end-of-input or start @@ -307,14 +292,14 @@ impl<'a> Iterator for Parser<'a> { } } -impl<'a> Parser<'a> { +impl<'input> Parser<'input> { /// Creates a new parser for the given unescaped input string and /// optional code snippet (the input as written before being unescaped), /// where `style` is `Some(nr_hashes)` when the snippet is a raw string with that many hashes. /// If the input comes via `println` or `panic`, then it has a newline already appended, /// which is reflected in the `appended_newline` parameter. pub fn new( - input: &'a str, + input: &'input str, style: Option<usize>, snippet: Option<String>, appended_newline: bool, @@ -406,6 +391,16 @@ impl<'a> Parser<'a> { } } + /// Peeks at the current position, without incrementing the pointer. + pub fn peek(&self) -> Option<(Range<usize>, usize, char)> { + self.input_vec.get(self.input_vec_index).cloned() + } + + /// Peeks at the current position + 1, without incrementing the pointer. + pub fn peek_ahead(&self) -> Option<(Range<usize>, usize, char)> { + self.input_vec.get(self.input_vec_index + 1).cloned() + } + /// Optionally consumes the specified character. If the character is not at /// the current position, then the current iterator isn't moved and `false` is /// returned, otherwise the character is consumed and `true` is returned. @@ -418,27 +413,19 @@ impl<'a> Parser<'a> { /// returned, otherwise the character is consumed and the current position is /// returned. fn consume_pos(&mut self, ch: char) -> Option<(Range<usize>, usize)> { - if let Some((r, i, c)) = self.input_vec.get(self.input_vec_index) { - if ch == *c { - self.input_vec_index += 1; - return Some((r.clone(), *i)); - } + if let Some((r, i, c)) = self.peek() + && ch == c + { + self.input_vec_index += 1; + return Some((r, i)); } + None } - /// Forces consumption of the specified character. If the character is not - /// found, an error is emitted. - fn consume_closing_brace(&mut self, arg: &Argument<'_>) -> Option<Range<usize>> { - self.ws(); - - let (range, description) = if let Some((r, _, c)) = self.input_vec.get(self.input_vec_index) - { - if *c == '}' { - self.input_vec_index += 1; - return Some(r.clone()); - } - // or r.clone()? + /// Called if a closing brace was not found. + fn missing_closing_brace(&mut self, arg: &Argument<'_>) { + let (range, description) = if let Some((r, _, c)) = self.peek() { (r.start..r.start, format!("expected `}}`, found `{}`", c.escape_debug())) } else { ( @@ -471,7 +458,13 @@ impl<'a> Parser<'a> { suggestion: Suggestion::None, }); - None + if let Some((_, _, c)) = self.peek() { + match c { + '?' => self.suggest_format_debug(), + '<' | '^' | '>' => self.suggest_format_align(c), + _ => self.suggest_positional_arg_instead_of_captured_arg(arg), + } + } } /// Consumes all whitespace characters until the first non-whitespace character @@ -483,11 +476,11 @@ impl<'a> Parser<'a> { /// Parses all of a string which is to be considered a "raw literal" in a /// format string. This is everything outside of the braces. - fn string(&mut self, start: usize) -> &'a str { - while let Some((r, i, c)) = self.input_vec.get(self.input_vec_index) { + fn string(&mut self, start: usize) -> &'input str { + while let Some((r, i, c)) = self.peek() { match c { '{' | '}' => { - return &self.input[start..*i]; + return &self.input[start..i]; } '\n' if self.is_source_literal => { self.input_vec_index += 1; @@ -507,7 +500,7 @@ impl<'a> Parser<'a> { } /// Parses an `Argument` structure, or what's contained within braces inside the format string. - fn argument(&mut self) -> Argument<'a> { + fn argument(&mut self) -> Argument<'input> { let start_idx = self.input_vec_index; let position = self.position(); @@ -518,6 +511,7 @@ impl<'a> Parser<'a> { let format = match self.mode { ParseMode::Format => self.format(), ParseMode::InlineAsm => self.inline_asm(), + ParseMode::Diagnostic => self.diagnostic(), }; // Resolve position after parsing format spec. @@ -536,31 +530,27 @@ impl<'a> Parser<'a> { /// integer index of an argument, a named argument, or a blank string. /// Returns `Some(parsed_position)` if the position is not implicitly /// consuming a macro argument, `None` if it's the case. - fn position(&mut self) -> Option<Position<'a>> { + fn position(&mut self) -> Option<Position<'input>> { if let Some(i) = self.integer() { Some(ArgumentIs(i.into())) } else { - match self.input_vec.get(self.input_vec_index) { - Some((range, _, c)) if rustc_lexer::is_id_start(*c) => { + match self.peek() { + Some((range, _, c)) if rustc_lexer::is_id_start(c) => { let start = range.start; let word = self.word(); // Recover from `r#ident` in format strings. - // FIXME: use a let chain - if word == "r" { - if let Some((r, _, '#')) = self.input_vec.get(self.input_vec_index) { - if self - .input_vec - .get(self.input_vec_index + 1) - .is_some_and(|(_, _, c)| rustc_lexer::is_id_start(*c)) - { - self.input_vec_index += 1; - let prefix_end = r.end; - let word = self.word(); - let prefix_span = start..prefix_end; - let full_span = - start..self.input_vec_index2range(self.input_vec_index).start; - self.errors.insert(0, ParseError { + if word == "r" + && let Some((r, _, '#')) = self.peek() + && self.peek_ahead().is_some_and(|(_, _, c)| rustc_lexer::is_id_start(c)) + { + self.input_vec_index += 1; + let prefix_end = r.end; + let word = self.word(); + let prefix_span = start..prefix_end; + let full_span = + start..self.input_vec_index2range(self.input_vec_index).start; + self.errors.insert(0, ParseError { description: "raw identifiers are not supported".to_owned(), note: Some("identifiers in format strings can be keywords and don't need to be prefixed with `r#`".to_string()), label: "raw identifier used here".to_owned(), @@ -568,9 +558,7 @@ impl<'a> Parser<'a> { secondary_label: None, suggestion: Suggestion::RemoveRawIdent(prefix_span), }); - return Some(ArgumentNamed(word)); - } - } + return Some(ArgumentNamed(word)); } Some(ArgumentNamed(word)) @@ -584,7 +572,7 @@ impl<'a> Parser<'a> { } fn input_vec_index2pos(&self, index: usize) -> usize { - if let Some(&(_, pos, _)) = self.input_vec.get(index) { pos } else { self.input.len() } + if let Some((_, pos, _)) = self.input_vec.get(index) { *pos } else { self.input.len() } } fn input_vec_index2range(&self, index: usize) -> Range<usize> { @@ -597,33 +585,18 @@ impl<'a> Parser<'a> { /// Parses a format specifier at the current position, returning all of the /// relevant information in the `FormatSpec` struct. - fn format(&mut self) -> FormatSpec<'a> { - let mut spec = FormatSpec { - fill: None, - fill_span: None, - align: AlignUnknown, - sign: None, - alternate: false, - zero_pad: false, - debug_hex: None, - precision: CountImplied, - precision_span: None, - width: CountImplied, - width_span: None, - ty: &self.input[..0], - ty_span: None, - }; + fn format(&mut self) -> FormatSpec<'input> { + let mut spec = FormatSpec::default(); + if !self.consume(':') { return spec; } // fill character - if let Some(&(ref r, _, c)) = self.input_vec.get(self.input_vec_index) { - if let Some((_, _, '>' | '<' | '^')) = self.input_vec.get(self.input_vec_index + 1) { - self.input_vec_index += 1; - spec.fill = Some(c); - spec.fill_span = Some(r.clone()); - } + if let (Some((r, _, c)), Some((_, _, '>' | '<' | '^'))) = (self.peek(), self.peek_ahead()) { + self.input_vec_index += 1; + spec.fill = Some(c); + spec.fill_span = Some(r); } // Alignment if self.consume('<') { @@ -701,24 +674,21 @@ impl<'a> Parser<'a> { } } else if let Some((range, _)) = self.consume_pos('?') { spec.ty = "?"; - if let Some((r, _, c)) = self.input_vec.get(self.input_vec_index) { - match c { - '#' | 'x' | 'X' => self.errors.insert( - 0, - ParseError { - description: format!("expected `}}`, found `{c}`"), - note: None, - label: "expected `'}'`".into(), - span: r.clone(), - secondary_label: None, - suggestion: Suggestion::ReorderFormatParameter( - range.start..r.end, - format!("{c}?"), - ), - }, - ), - _ => (), - } + if let Some((r, _, c @ ('#' | 'x' | 'X'))) = self.peek() { + self.errors.insert( + 0, + ParseError { + description: format!("expected `}}`, found `{c}`"), + note: None, + label: "expected `'}'`".into(), + span: r.clone(), + secondary_label: None, + suggestion: Suggestion::ReorderFormatParameter( + range.start..r.end, + format!("{c}?"), + ), + }, + ); } } else { spec.ty = self.word(); @@ -733,22 +703,9 @@ impl<'a> Parser<'a> { /// Parses an inline assembly template modifier at the current position, returning the modifier /// in the `ty` field of the `FormatSpec` struct. - fn inline_asm(&mut self) -> FormatSpec<'a> { - let mut spec = FormatSpec { - fill: None, - fill_span: None, - align: AlignUnknown, - sign: None, - alternate: false, - zero_pad: false, - debug_hex: None, - precision: CountImplied, - precision_span: None, - width: CountImplied, - width_span: None, - ty: &self.input[..0], - ty_span: None, - }; + fn inline_asm(&mut self) -> FormatSpec<'input> { + let mut spec = FormatSpec::default(); + if !self.consume(':') { return spec; } @@ -764,10 +721,26 @@ impl<'a> Parser<'a> { spec } + /// Always returns an empty `FormatSpec` + fn diagnostic(&mut self) -> FormatSpec<'input> { + let mut spec = FormatSpec::default(); + + let Some((Range { start, .. }, start_idx)) = self.consume_pos(':') else { + return spec; + }; + + spec.ty = self.string(start_idx); + spec.ty_span = { + let end = self.input_vec_index2range(self.input_vec_index).start; + Some(start..end) + }; + spec + } + /// Parses a `Count` parameter at the current position. This does not check /// for 'CountIsNextParam' because that is only used in precision, not /// width. - fn count(&mut self) -> Count<'a> { + fn count(&mut self) -> Count<'input> { if let Some(i) = self.integer() { if self.consume('$') { CountIsParam(i.into()) } else { CountIs(i) } } else { @@ -786,10 +759,10 @@ impl<'a> Parser<'a> { /// Parses a word starting at the current position. A word is the same as a /// Rust identifier, except that it can't start with `_` character. - fn word(&mut self) -> &'a str { + fn word(&mut self) -> &'input str { let index = self.input_vec_index; - match self.input_vec.get(self.input_vec_index) { - Some(&(ref r, i, c)) if rustc_lexer::is_id_start(c) => { + match self.peek() { + Some((ref r, i, c)) if rustc_lexer::is_id_start(c) => { self.input_vec_index += 1; (r.start, i) } @@ -798,7 +771,7 @@ impl<'a> Parser<'a> { } }; let (err_end, end): (usize, usize) = loop { - if let Some(&(ref r, i, c)) = self.input_vec.get(self.input_vec_index) { + if let Some((ref r, i, c)) = self.peek() { if rustc_lexer::is_id_continue(c) { self.input_vec_index += 1; } else { @@ -828,7 +801,7 @@ impl<'a> Parser<'a> { let mut found = false; let mut overflow = false; let start_index = self.input_vec_index; - while let Some(&(_, _, c)) = self.input_vec.get(self.input_vec_index) { + while let Some((_, _, c)) = self.peek() { if let Some(i) = c.to_digit(10) { self.input_vec_index += 1; let (tmp, mul_overflow) = cur.overflowing_mul(10); @@ -897,7 +870,7 @@ impl<'a> Parser<'a> { } } - fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: Argument<'a>) { + fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: &Argument<'_>) { // If the argument is not an identifier, it is not a field access. if !arg.is_identifier() { return; diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index e6a7f24034a..a6c7e1890ab 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -553,3 +553,45 @@ fn asm_concat() { assert_eq!(parser.by_ref().collect::<Vec<Piece<'static>>>(), &[Lit(asm)]); assert_eq!(parser.line_spans, &[]); } + +#[test] +fn diagnostic_format_flags() { + let lit = "{thing:blah}"; + let mut parser = Parser::new(lit, None, None, false, ParseMode::Diagnostic); + assert!(!parser.is_source_literal); + + let [NextArgument(arg)] = &*parser.by_ref().collect::<Vec<Piece<'static>>>() else { panic!() }; + + assert_eq!( + **arg, + Argument { + position: ArgumentNamed("thing"), + position_span: 2..7, + format: FormatSpec { ty: ":blah", ty_span: Some(7..12), ..Default::default() }, + } + ); + + assert_eq!(parser.line_spans, &[]); + assert!(parser.errors.is_empty()); +} + +#[test] +fn diagnostic_format_mod() { + let lit = "{thing:+}"; + let mut parser = Parser::new(lit, None, None, false, ParseMode::Diagnostic); + assert!(!parser.is_source_literal); + + let [NextArgument(arg)] = &*parser.by_ref().collect::<Vec<Piece<'static>>>() else { panic!() }; + + assert_eq!( + **arg, + Argument { + position: ArgumentNamed("thing"), + position_span: 2..7, + format: FormatSpec { ty: ":+", ty_span: Some(7..9), ..Default::default() }, + } + ); + + assert_eq!(parser.line_spans, &[]); + assert!(parser.errors.is_empty()); +} diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index a4ef065ea2c..7c237d708c0 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -290,6 +290,9 @@ passes_duplicate_lang_item_crate_depends = .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path} .second_definition_path = second definition in `{$crate_name}` loaded from {$path} +passes_enum_variant_same_name = + it is impossible to refer to the {$descr} `{$dead_name}` because it is shadowed by this enum variant with the same name + passes_export_name = attribute should be applied to a free function, impl method or static .label = not a free function, impl method or static @@ -716,6 +719,9 @@ passes_unknown_external_lang_item = passes_unknown_feature = unknown feature `{$feature}` +passes_unknown_feature_alias = + feature `{$alias}` has been renamed to `{$feature}` + passes_unknown_lang_item = definition of an unknown lang item: `{$name}` .label = definition of unknown lang item `{$name}` diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4e2be8ff0b8..5ce803aa1f8 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -10,7 +10,7 @@ use std::collections::hash_map::Entry; use rustc_abi::{Align, ExternAbi, Size}; use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast}; -use rustc_attr_data_structures::{AttributeKind, ReprAttr, find_attr}; +use rustc_attr_data_structures::{AttributeKind, InlineAttr, ReprAttr, find_attr}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; @@ -124,6 +124,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::Stability { span, .. } | AttributeKind::ConstStability { span, .. }, ) => self.check_stability_promotable(*span, target), + Attribute::Parsed(AttributeKind::Inline(InlineAttr::Force { .. }, ..)) => {} // handled separately below + Attribute::Parsed(AttributeKind::Inline(kind, attr_span)) => { + self.check_inline(hir_id, *attr_span, span, kind, target) + } Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self .check_allow_internal_unstable( hir_id, @@ -147,6 +151,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::ConstStabilityIndirect | AttributeKind::MacroTransparency(_), ) => { /* do nothing */ } + Attribute::Parsed(AttributeKind::AsPtr(attr_span)) => { + self.check_applied_to_fn_or_method(hir_id, *attr_span, span, target) + } Attribute::Unparsed(_) => { match attr.path().as_slice() { [sym::diagnostic, sym::do_not_recommend, ..] => { @@ -155,7 +162,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::diagnostic, sym::on_unimplemented, ..] => { self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) } - [sym::inline, ..] => self.check_inline(hir_id, attr, span, target), [sym::coverage, ..] => self.check_coverage(attr, span, target), [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target), [sym::no_sanitize, ..] => { @@ -188,26 +194,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_rustc_std_internal_symbol(attr, span, target) } [sym::naked, ..] => self.check_naked(hir_id, attr, span, target, attrs), - [sym::rustc_as_ptr, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) - } [sym::rustc_no_implicit_autorefs, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) + self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } [sym::rustc_never_returns_null_ptr, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) + self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } [sym::rustc_legacy_const_generics, ..] => { self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item) } [sym::rustc_lint_query_instability, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) + self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } [sym::rustc_lint_untracked_query_information, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) + self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } [sym::rustc_lint_diagnostics, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) + self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } [sym::rustc_lint_opt_ty, ..] => self.check_rustc_lint_opt_ty(attr, span, target), [sym::rustc_lint_opt_deny_field_access, ..] => { @@ -367,11 +370,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_rustc_force_inline(hir_id, attrs, span, target); } - fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { + fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr_span: Span, sym: &str) { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::IgnoredAttrWithMacro { sym }, ); } @@ -431,7 +434,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if an `#[inline]` is applied to a function or a closure. - fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + fn check_inline( + &self, + hir_id: HirId, + attr_span: Span, + defn_span: Span, + kind: &InlineAttr, + target: Target, + ) { match target { Target::Fn | Target::Closure @@ -440,7 +450,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::IgnoredInlineAttrFnProto, ) } @@ -451,25 +461,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Target::AssocConst => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::IgnoredInlineAttrConstants, ), // FIXME(#80564): Same for fields, arms, and macro defs Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline") + self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "inline") } _ => { - self.dcx().emit_err(errors::InlineNotFnOrClosure { - attr_span: attr.span(), - defn_span: span, - }); + self.dcx().emit_err(errors::InlineNotFnOrClosure { attr_span, defn_span }); } } // `#[inline]` is ignored if the symbol must be codegened upstream because it's exported. if let Some(did) = hir_id.as_owner() && self.tcx.def_kind(did).has_codegen_attrs() - && !matches!(attr.meta_item_list().as_deref(), Some([item]) if item.has_name(sym::never)) + && kind != &InlineAttr::Never { let attrs = self.tcx.codegen_fn_attrs(did); // Not checking naked as `#[inline]` is forbidden for naked functions anyways. @@ -477,7 +484,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::InlineIgnoredForExported {}, ); } @@ -676,6 +683,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ) => { continue; } + Attribute::Parsed(AttributeKind::Inline(.., span)) => { + self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute { + span: *span, + naked_span: attr.span(), + attr: sym::inline.to_string(), + }); + + return; + } + // FIXME(jdonszelmann): make exhaustive _ => {} } @@ -787,7 +804,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { for attr in attrs { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "track_caller"); } } _ => { @@ -830,7 +847,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "non_exhaustive"); } _ => { self.dcx().emit_err(errors::NonExhaustiveWrongLocation { @@ -850,7 +867,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "marker"); } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { @@ -904,7 +921,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "target_feature"); } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { @@ -1619,7 +1636,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "cold"); } _ => { // FIXME: #[cold] was previously allowed on non-functions and some crates used @@ -1661,7 +1678,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "link_name"); } _ => { // FIXME: #[cold] was previously allowed on non-functions/statics and some crates @@ -1695,7 +1712,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "no_link"); } _ => { self.dcx().emit_err(errors::NoLink { attr_span: attr.span(), span }); @@ -1717,7 +1734,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "export_name"); } _ => { self.dcx().emit_err(errors::ExportName { attr_span: attr.span(), span }); @@ -1825,15 +1842,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_applied_to_fn_or_method( &self, hir_id: HirId, - attr: &Attribute, - span: Span, + attr_span: Span, + defn_span: Span, target: Target, ) { let is_function = matches!(target, Target::Fn | Target::Method(..)); if !is_function { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span: attr.span(), - defn_span: span, + attr_span, + defn_span, on_crate: hir_id == CRATE_HIR_ID, }); } @@ -1891,7 +1908,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "link_section"); } _ => { // FIXME: #[link_section] was previously allowed on non-functions/statics and some @@ -1916,7 +1933,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "no_mangle"); } // FIXME: #[no_mangle] was previously allowed on non-functions/statics, this should be an error // The error should specify that the item that is wrong is specifically a *foreign* fn/static @@ -2263,9 +2280,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // `#[allow_internal_unstable]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. - Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable") - } + Target::Field | Target::Arm | Target::MacroDef => self + .inline_attr_str_error_with_macro_def( + hir_id, + attr.span(), + "allow_internal_unstable", + ), _ => { self.tcx .dcx() @@ -2638,8 +2658,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: Span, target: Target, ) { - let force_inline_attr = attrs.iter().find(|attr| attr.has_name(sym::rustc_force_inline)); - match (target, force_inline_attr) { + match ( + target, + find_attr!(attrs, AttributeKind::Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span), + ) { (Target::Closure, None) => { let is_coro = matches!( self.tcx.hir_expect_expr(hir_id).kind, @@ -2651,20 +2673,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ); let parent_did = self.tcx.hir_get_parent_item(hir_id).to_def_id(); let parent_span = self.tcx.def_span(parent_did); - let parent_force_inline_attr = - self.tcx.get_attr(parent_did, sym::rustc_force_inline); - if let Some(attr) = parent_force_inline_attr - && is_coro + + if let Some(attr_span) = find_attr!( + self.tcx.get_all_attrs(parent_did), + AttributeKind::Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span + ) && is_coro { - self.dcx().emit_err(errors::RustcForceInlineCoro { - attr_span: attr.span(), - span: parent_span, - }); + self.dcx() + .emit_err(errors::RustcForceInlineCoro { attr_span, span: parent_span }); } } (Target::Fn, _) => (), - (_, Some(attr)) => { - self.dcx().emit_err(errors::RustcForceInline { attr_span: attr.span(), span }); + (_, Some(attr_span)) => { + self.dcx().emit_err(errors::RustcForceInline { attr_span, span }); } (_, None) => (), } @@ -2885,10 +2906,9 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) { let attrs = tcx.hir_attrs(item.hir_id()); - for attr in attrs { - if attr.has_name(sym::inline) { - tcx.dcx().emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span() }); - } + if let Some(attr_span) = find_attr!(attrs, AttributeKind::Inline(i, span) if !matches!(i, InlineAttr::Force{..}) => *span) + { + tcx.dcx().emit_err(errors::NonExportedMacroInvalidAttrs { attr_span }); } } @@ -2908,6 +2928,7 @@ pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { check_mod_attrs, ..*providers }; } +// FIXME(jdonszelmann): remove, check during parsing fn check_duplicates( tcx: TyCtxt<'_>, attr: &Attribute, diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index e597c819a3a..4257d8e8d16 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -14,7 +14,7 @@ use rustc_errors::MultiSpan; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Node, PatKind, QPath, TyKind}; +use rustc_hir::{self as hir, ImplItem, ImplItemKind, Node, PatKind, QPath, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; @@ -936,7 +936,9 @@ enum ShouldWarnAboutField { #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum ReportOn { + /// Report on something that hasn't got a proper name to refer to TupleField, + /// Report on something that has got a name, which could be a field but also a method NamedField, } @@ -1061,6 +1063,31 @@ impl<'tcx> DeadVisitor<'tcx> { None }; + let enum_variants_with_same_name = dead_codes + .iter() + .filter_map(|dead_item| { + if let Node::ImplItem(ImplItem { + kind: ImplItemKind::Fn(..) | ImplItemKind::Const(..), + .. + }) = tcx.hir_node_by_def_id(dead_item.def_id) + && let Some(impl_did) = tcx.opt_parent(dead_item.def_id.to_def_id()) + && let DefKind::Impl { of_trait: false } = tcx.def_kind(impl_did) + && let ty::Adt(maybe_enum, _) = tcx.type_of(impl_did).skip_binder().kind() + && maybe_enum.is_enum() + && let Some(variant) = + maybe_enum.variants().iter().find(|i| i.name == dead_item.name) + { + Some(crate::errors::EnumVariantSameName { + descr: tcx.def_descr(dead_item.def_id.to_def_id()), + dead_name: dead_item.name, + variant_span: tcx.def_span(variant.def_id), + }) + } else { + None + } + }) + .collect(); + let diag = match report_on { ReportOn::TupleField => { let tuple_fields = if let Some(parent_id) = parent_item @@ -1114,6 +1141,7 @@ impl<'tcx> DeadVisitor<'tcx> { name_list, parent_info, ignored_derived_impls, + enum_variants_with_same_name, }, }; diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index 17a729f422a..8f572af02c2 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -10,7 +10,7 @@ //! * Compiler internal types like `Ty` and `TyCtxt` use rustc_hir::diagnostic_items::DiagnosticItems; -use rustc_hir::{Attribute, OwnerId}; +use rustc_hir::{Attribute, CRATE_OWNER_ID, OwnerId}; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::{DefId, LOCAL_CRATE}; @@ -67,7 +67,7 @@ fn diagnostic_items(tcx: TyCtxt<'_>, _: LocalCrate) -> DiagnosticItems { // Collect diagnostic items in this crate. let crate_items = tcx.hir_crate_items(()); - for id in crate_items.owners() { + for id in crate_items.owners().chain(std::iter::once(CRATE_OWNER_ID)) { observe_item(tcx, &mut diagnostic_items, id); } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 74ce92624bd..f0d4b610f63 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1435,6 +1435,15 @@ pub(crate) struct UnknownFeature { } #[derive(Diagnostic)] +#[diag(passes_unknown_feature_alias, code = E0635)] +pub(crate) struct RenamedFeature { + #[primary_span] + pub span: Span, + pub feature: Symbol, + pub alias: Symbol, +} + +#[derive(Diagnostic)] #[diag(passes_implied_feature_not_exist)] pub(crate) struct ImpliedFeatureNotExist { #[primary_span] @@ -1478,6 +1487,9 @@ pub(crate) enum MultipleDeadCodes<'tcx> { participle: &'tcx str, name_list: DiagSymbolList, #[subdiagnostic] + // only on DeadCodes since it's never a problem for tuple struct fields + enum_variants_with_same_name: Vec<EnumVariantSameName<'tcx>>, + #[subdiagnostic] parent_info: Option<ParentInfo<'tcx>>, #[subdiagnostic] ignored_derived_impls: Option<IgnoredDerivedImpls>, @@ -1499,6 +1511,15 @@ pub(crate) enum MultipleDeadCodes<'tcx> { } #[derive(Subdiagnostic)] +#[note(passes_enum_variant_same_name)] +pub(crate) struct EnumVariantSameName<'tcx> { + #[primary_span] + pub variant_span: Span, + pub dead_name: Symbol, + pub descr: &'tcx str, +} + +#[derive(Subdiagnostic)] #[label(passes_parent_info)] pub(crate) struct ParentInfo<'tcx> { pub num: usize, diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 6852153a2e7..46e6c0bf7da 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -5,7 +5,7 @@ use rustc_ast::visit::BoundKind; use rustc_ast::{self as ast, NodeId, visit as ast_visit}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::thousands::format_with_underscores; +use rustc_data_structures::thousands::usize_with_underscores; use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit}; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -140,10 +140,10 @@ impl<'k> StatCollector<'k> { "{} {:<18}{:>10} ({:4.1}%){:>14}{:>14}", prefix, label, - format_with_underscores(size), + usize_with_underscores(size), percent(size, total_size), - format_with_underscores(node.stats.count), - format_with_underscores(node.stats.size) + usize_with_underscores(node.stats.count), + usize_with_underscores(node.stats.size) ); if !node.subnodes.is_empty() { // We will soon sort, so the initial order does not matter. @@ -159,9 +159,9 @@ impl<'k> StatCollector<'k> { "{} - {:<18}{:>10} ({:4.1}%){:>14}", prefix, label, - format_with_underscores(size), + usize_with_underscores(size), percent(size, total_size), - format_with_underscores(subnode.count), + usize_with_underscores(subnode.count), ); } } @@ -171,8 +171,8 @@ impl<'k> StatCollector<'k> { "{} {:<18}{:>10} {:>14}", prefix, "Total", - format_with_underscores(total_size), - format_with_underscores(total_count), + usize_with_underscores(total_size), + usize_with_underscores(total_count), ); eprintln!("{prefix}"); } diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index b3e6ee9512c..127e0df1332 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -40,7 +40,7 @@ impl<'tcx> LibFeatureCollector<'tcx> { }; let feature_stability = match level { - StabilityLevel::Unstable { .. } => FeatureStability::Unstable, + StabilityLevel::Unstable { old_name, .. } => FeatureStability::Unstable { old_name }, StabilityLevel::Stable { since, .. } => FeatureStability::AcceptedSince(match since { StableSince::Version(v) => Symbol::intern(&v.to_string()), StableSince::Current => sym::env_CFG_RELEASE, @@ -71,7 +71,7 @@ impl<'tcx> LibFeatureCollector<'tcx> { }); } } - (FeatureStability::AcceptedSince(_), Some((FeatureStability::Unstable, _))) => { + (FeatureStability::AcceptedSince(_), Some((FeatureStability::Unstable { .. }, _))) => { self.tcx.dcx().emit_err(FeaturePreviouslyDeclared { span, feature, @@ -79,7 +79,7 @@ impl<'tcx> LibFeatureCollector<'tcx> { prev_declared: "unstable", }); } - (FeatureStability::Unstable, Some((FeatureStability::AcceptedSince(_), _))) => { + (FeatureStability::Unstable { .. }, Some((FeatureStability::AcceptedSince(_), _))) => { self.tcx.dcx().emit_err(FeaturePreviouslyDeclared { span, feature, @@ -88,7 +88,7 @@ impl<'tcx> LibFeatureCollector<'tcx> { }); } // duplicate `unstable` feature is ok. - (FeatureStability::Unstable, Some((FeatureStability::Unstable, _))) => {} + (FeatureStability::Unstable { .. }, Some((FeatureStability::Unstable { .. }, _))) => {} } } } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 45e26c8999a..56d9f5bf785 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -718,6 +718,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { issue: NonZero::new(27812), is_soft: false, implied_by: None, + old_name: None, }, feature: sym::rustc_private, }; @@ -1161,8 +1162,8 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { defined_features: &LibFeatures, all_implications: &UnordMap<Symbol, Symbol>, ) { - for (feature, since) in defined_features.to_sorted_vec() { - if let FeatureStability::AcceptedSince(since) = since + for (feature, stability) in defined_features.to_sorted_vec() { + if let FeatureStability::AcceptedSince(since) = stability && let Some(span) = remaining_lib_features.get(&feature) { // Warn if the user has enabled an already-stable lib feature. @@ -1181,6 +1182,12 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { // implications from this crate. remaining_implications.remove(&feature); + if let FeatureStability::Unstable { old_name: Some(alias) } = stability { + if let Some(span) = remaining_lib_features.swap_remove(&alias) { + tcx.dcx().emit_err(errors::RenamedFeature { span, feature, alias }); + } + } + if remaining_lib_features.is_empty() && remaining_implications.is_empty() { break; } diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index e1b6adc6cc1..96d0c02c4a6 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use rustc_ast as ast; use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -7,7 +5,9 @@ use rustc_hir::definitions::DefPathHash; use rustc_session::Session; use rustc_session::cstore::Untracked; use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, CachingSourceMapView, DUMMY_SP, SourceFile, Span, SpanData, Symbol}; +use rustc_span::{ + BytePos, CachingSourceMapView, DUMMY_SP, Span, SpanData, StableSourceFileId, Symbol, +}; use crate::ich; @@ -118,7 +118,7 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { fn span_data_to_lines_and_cols( &mut self, span: &SpanData, - ) -> Option<(Arc<SourceFile>, usize, BytePos, usize, BytePos)> { + ) -> Option<(StableSourceFileId, usize, BytePos, usize, BytePos)> { self.source_map().span_data_to_lines_and_cols(span) } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index c30ed781f35..650a827ba56 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1098,22 +1098,20 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { self.r.potentially_unused_imports.push(import); module.for_each_child(self, |this, ident, ns, binding| { if ns == MacroNS { - let imported_binding = - if this.r.is_accessible_from(binding.vis, this.parent_scope.module) { - this.r.import(binding, import) - } else if !this.r.is_builtin_macro(binding.res()) - && !this.r.macro_use_prelude.contains_key(&ident.name) - { - // - `!r.is_builtin_macro(res)` excluding the built-in macros such as `Debug` or `Hash`. - // - `!r.macro_use_prelude.contains_key(name)` excluding macros defined in other extern - // crates such as `std`. - // FIXME: This branch should eventually be removed. - let import = macro_use_import(this, span, true); - this.r.import(binding, import) - } else { + let import = if this.r.is_accessible_from(binding.vis, this.parent_scope.module) + { + import + } else { + // FIXME: This branch is used for reporting the `private_macro_use` lint + // and should eventually be removed. + if this.r.macro_use_prelude.contains_key(&ident.name) { + // Do not override already existing entries with compatibility entries. return; - }; - this.add_macro_use_binding(ident.name, imported_binding, span, allow_shadowing); + } + macro_use_import(this, span, true) + }; + let import_binding = this.r.import(binding, import); + this.add_macro_use_binding(ident.name, import_binding, span, allow_shadowing); } }); } else { diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index dc16fe212b1..f8e0a6936a0 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -128,7 +128,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { // FIXME(jdonszelmann) make one of these in the resolver? // FIXME(jdonszelmann) don't care about tools here maybe? Just parse what we can. // Does that prevents errors from happening? maybe - let parser = AttributeParser::new( + let mut parser = AttributeParser::new_early( &self.resolver.tcx.sess, self.resolver.tcx.features(), Vec::new(), @@ -136,8 +136,14 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { let attrs = parser.parse_attribute_list( &i.attrs, i.span, + i.id, OmitDoc::Skip, std::convert::identity, + |_l| { + // FIXME(jdonszelmann): emit lints here properly + // NOTE that before new attribute parsing, they didn't happen either + // but it would be nice if we could change that. + }, ); let macro_data = diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 201b1c0a493..9149974a617 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -7,7 +7,7 @@ use rustc_ast::{ use rustc_ast_pretty::pprust; use rustc_attr_data_structures::{self as attr, Stability}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::unord::UnordSet; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, MultiSpan, SuggestionStyle, @@ -1054,6 +1054,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { false, false, None, + None, ) else { continue; }; @@ -1482,7 +1483,35 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope: &ParentScope<'ra>, ident: Ident, krate: &Crate, + sugg_span: Option<Span>, ) { + // Bring imported but unused `derive` macros into `macro_map` so we ensure they can be used + // for suggestions. + self.visit_scopes( + ScopeSet::Macro(MacroKind::Derive), + &parent_scope, + ident.span.ctxt(), + |this, scope, _use_prelude, _ctxt| { + let Scope::Module(m, _) = scope else { + return None; + }; + for (_, resolution) in this.resolutions(m).borrow().iter() { + let Some(binding) = resolution.borrow().binding else { + continue; + }; + let Res::Def(DefKind::Macro(MacroKind::Derive | MacroKind::Attr), def_id) = + binding.res() + else { + continue; + }; + // By doing this all *imported* macros get added to the `macro_map` even if they + // are *unused*, which makes the later suggestions find them and work. + let _ = this.get_macro_by_def_id(def_id); + } + None::<()> + }, + ); + let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind); let suggestion = self.early_lookup_typo_candidate( ScopeSet::Macro(macro_kind), @@ -1490,7 +1519,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ident, is_expected, ); - self.add_typo_suggestion(err, suggestion, ident.span); + if !self.add_typo_suggestion(err, suggestion, ident.span) { + self.detect_derive_attribute(err, ident, parent_scope, sugg_span); + } let import_suggestions = self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected); @@ -1623,6 +1654,105 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } + /// Given an attribute macro that failed to be resolved, look for `derive` macros that could + /// provide it, either as-is or with small typos. + fn detect_derive_attribute( + &self, + err: &mut Diag<'_>, + ident: Ident, + parent_scope: &ParentScope<'ra>, + sugg_span: Option<Span>, + ) { + // Find all of the `derive`s in scope and collect their corresponding declared + // attributes. + // FIXME: this only works if the crate that owns the macro that has the helper_attr + // has already been imported. + let mut derives = vec![]; + let mut all_attrs: UnordMap<Symbol, Vec<_>> = UnordMap::default(); + // We're collecting these in a hashmap, and handle ordering the output further down. + #[allow(rustc::potential_query_instability)] + for (def_id, data) in &self.macro_map { + for helper_attr in &data.ext.helper_attrs { + let item_name = self.tcx.item_name(*def_id); + all_attrs.entry(*helper_attr).or_default().push(item_name); + if helper_attr == &ident.name { + derives.push(item_name); + } + } + } + let kind = MacroKind::Derive.descr(); + if !derives.is_empty() { + // We found an exact match for the missing attribute in a `derive` macro. Suggest it. + let mut derives: Vec<String> = derives.into_iter().map(|d| d.to_string()).collect(); + derives.sort(); + derives.dedup(); + let msg = match &derives[..] { + [derive] => format!(" `{derive}`"), + [start @ .., last] => format!( + "s {} and `{last}`", + start.iter().map(|d| format!("`{d}`")).collect::<Vec<_>>().join(", ") + ), + [] => unreachable!("we checked for this to be non-empty 10 lines above!?"), + }; + let msg = format!( + "`{}` is an attribute that can be used by the {kind}{msg}, you might be \ + missing a `derive` attribute", + ident.name, + ); + let sugg_span = if let ModuleKind::Def(DefKind::Enum, id, _) = parent_scope.module.kind + { + let span = self.def_span(id); + if span.from_expansion() { + None + } else { + // For enum variants sugg_span is empty but we can get the enum's Span. + Some(span.shrink_to_lo()) + } + } else { + // For items this `Span` will be populated, everything else it'll be None. + sugg_span + }; + match sugg_span { + Some(span) => { + err.span_suggestion_verbose( + span, + msg, + format!("#[derive({})]\n", derives.join(", ")), + Applicability::MaybeIncorrect, + ); + } + None => { + err.note(msg); + } + } + } else { + // We didn't find an exact match. Look for close matches. If any, suggest fixing typo. + let all_attr_names = all_attrs.keys().map(|s| *s).into_sorted_stable_ord(); + if let Some(best_match) = find_best_match_for_name(&all_attr_names, ident.name, None) + && let Some(macros) = all_attrs.get(&best_match) + { + let mut macros: Vec<String> = macros.into_iter().map(|d| d.to_string()).collect(); + macros.sort(); + macros.dedup(); + let msg = match ¯os[..] { + [] => return, + [name] => format!(" `{name}` accepts"), + [start @ .., end] => format!( + "s {} and `{end}` accept", + start.iter().map(|m| format!("`{m}`")).collect::<Vec<_>>().join(", "), + ), + }; + let msg = format!("the {kind}{msg} the similarly named `{best_match}` attribute"); + err.span_suggestion_verbose( + ident.span, + msg, + best_match, + Applicability::MaybeIncorrect, + ); + } + } + } + pub(crate) fn add_typo_suggestion( &self, err: &mut Diag<'_>, diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 180d6af219d..68fbe48ebcb 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -460,6 +460,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { true, force, ignore_import, + None, ) { Ok((Some(ext), _)) => { if ext.helper_attrs.contains(&ident.name) { diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 816efd0d5fa..e989209e177 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -133,7 +133,9 @@ impl<'ra> std::fmt::Debug for ImportKind<'ra> { .field("target", target) .field("id", id) .finish(), - MacroUse { .. } => f.debug_struct("MacroUse").finish(), + MacroUse { warn_private } => { + f.debug_struct("MacroUse").field("warn_private", warn_private).finish() + } MacroExport => f.debug_struct("MacroExport").finish(), } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 3dc285fdab6..338d9edcd22 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -415,24 +415,24 @@ pub(crate) enum AliasPossibility { } #[derive(Copy, Clone, Debug)] -pub(crate) enum PathSource<'a, 'c> { +pub(crate) enum PathSource<'a, 'ast, 'ra> { /// Type paths `Path`. Type, /// Trait paths in bounds or impls. Trait(AliasPossibility), /// Expression paths `path`, with optional parent context. - Expr(Option<&'a Expr>), + Expr(Option<&'ast Expr>), /// Paths in path patterns `Path`. Pat, /// Paths in struct expressions and patterns `Path { .. }`. Struct, /// Paths in tuple struct patterns `Path(..)`. - TupleStruct(Span, &'a [Span]), + TupleStruct(Span, &'ra [Span]), /// `m::A::B` in `<T as m::A>::B::C`. /// /// Second field holds the "cause" of this one, i.e. the context within /// which the trait item is resolved. Used for diagnostics. - TraitItem(Namespace, &'c PathSource<'a, 'c>), + TraitItem(Namespace, &'a PathSource<'a, 'ast, 'ra>), /// Paths in delegation item Delegation, /// An arg in a `use<'a, N>` precise-capturing bound. @@ -443,7 +443,7 @@ pub(crate) enum PathSource<'a, 'c> { DefineOpaques, } -impl<'a> PathSource<'a, '_> { +impl PathSource<'_, '_, '_> { fn namespace(self) -> Namespace { match self { PathSource::Type @@ -773,7 +773,7 @@ struct LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes. -impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { +impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn visit_attribute(&mut self, _: &'ast Attribute) { // We do not want to resolve expressions that appear in attributes, // as they do not correspond to actual code. @@ -1462,7 +1462,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r } } -impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { +impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { fn new(resolver: &'a mut Resolver<'ra, 'tcx>) -> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // During late resolution we only track the module component of the parent scope, // although it may be useful to track other components as well for diagnostics. @@ -2010,7 +2010,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &mut self, partial_res: PartialRes, path: &[Segment], - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, path_span: Span, ) { let proj_start = path.len() - partial_res.unresolved_segments(); @@ -4161,7 +4161,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { id: NodeId, qself: &Option<P<QSelf>>, path: &Path, - source: PathSource<'ast, '_>, + source: PathSource<'_, 'ast, '_>, ) { self.smart_resolve_path_fragment( qself, @@ -4178,7 +4178,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &mut self, qself: &Option<P<QSelf>>, path: &[Segment], - source: PathSource<'ast, '_>, + source: PathSource<'_, 'ast, '_>, finalize: Finalize, record_partial_res: RecordPartialRes, parent_qself: Option<&QSelf>, @@ -4482,7 +4482,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { span: Span, defer_to_typeck: bool, finalize: Finalize, - source: PathSource<'ast, '_>, + source: PathSource<'_, 'ast, '_>, ) -> Result<Option<PartialRes>, Spanned<ResolutionError<'ra>>> { let mut fin_res = None; @@ -4509,7 +4509,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let path_seg = |seg: &Segment| PathSegment::from_ident(seg.ident); let path = Path { segments: path.iter().map(path_seg).collect(), span, tokens: None }; if let Ok((_, res)) = - self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false, None) + self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false, None, None) { return Ok(Some(PartialRes::new(res))); } @@ -4525,7 +4525,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { path: &[Segment], ns: Namespace, finalize: Finalize, - source: PathSource<'ast, '_>, + source: PathSource<'_, 'ast, '_>, ) -> Result<Option<PartialRes>, Spanned<ResolutionError<'ra>>> { debug!( "resolve_qpath(qself={:?}, path={:?}, ns={:?}, finalize={:?})", diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 2f6aed35f25..d5dd3bdb6cd 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -170,12 +170,12 @@ impl TypoCandidate { } } -impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { +impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn make_base_error( &mut self, path: &[Segment], span: Span, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, res: Option<Res>, ) -> BaseError { // Make the base error. @@ -421,7 +421,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { path: &[Segment], following_seg: Option<&Segment>, span: Span, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, res: Option<Res>, qself: Option<&QSelf>, ) -> (Diag<'tcx>, Vec<ImportSuggestion>) { @@ -539,7 +539,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { path: &[Segment], following_seg: Option<&Segment>, span: Span, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, res: Option<Res>, qself: Option<&QSelf>, ) { @@ -650,7 +650,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn try_lookup_name_relaxed( &mut self, err: &mut Diag<'_>, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, path: &[Segment], following_seg: Option<&Segment>, span: Span, @@ -940,7 +940,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_trait_and_bounds( &mut self, err: &mut Diag<'_>, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, res: Option<Res>, span: Span, base_error: &BaseError, @@ -1017,7 +1017,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_typo( &mut self, err: &mut Diag<'_>, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, path: &[Segment], following_seg: Option<&Segment>, span: Span, @@ -1063,7 +1063,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_shadowed( &mut self, err: &mut Diag<'_>, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, path: &[Segment], following_seg: Option<&Segment>, span: Span, @@ -1096,7 +1096,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn err_code_special_cases( &mut self, err: &mut Diag<'_>, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, path: &[Segment], span: Span, ) { @@ -1141,7 +1141,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_self_ty( &mut self, err: &mut Diag<'_>, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, path: &[Segment], span: Span, ) -> bool { @@ -1164,7 +1164,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_self_value( &mut self, err: &mut Diag<'_>, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, path: &[Segment], span: Span, ) -> bool { @@ -1332,7 +1332,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_swapping_misplaced_self_ty_and_trait( &mut self, err: &mut Diag<'_>, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, res: Option<Res>, span: Span, ) { @@ -1361,7 +1361,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &mut self, err: &mut Diag<'_>, res: Option<Res>, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, ) { let PathSource::TupleStruct(_, _) = source else { return }; let Some(Res::Def(DefKind::Fn, _)) = res else { return }; @@ -1373,7 +1373,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &mut self, err: &mut Diag<'_>, res: Option<Res>, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, span: Span, ) { let PathSource::Trait(_) = source else { return }; @@ -1422,7 +1422,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_pattern_match_with_let( &mut self, err: &mut Diag<'_>, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, span: Span, ) -> bool { if let PathSource::Expr(_) = source @@ -1448,7 +1448,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn get_single_associated_item( &mut self, path: &[Segment], - source: &PathSource<'_, '_>, + source: &PathSource<'_, '_, '_>, filter_fn: &impl Fn(Res) -> bool, ) -> Option<TypoSuggestion> { if let crate::PathSource::TraitItem(_, _) = source { @@ -1556,7 +1556,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { /// Check if the source is call expression and the first argument is `self`. If true, /// return the span of whole call and the span for all arguments expect the first one (`self`). - fn call_has_self_arg(&self, source: PathSource<'_, '_>) -> Option<(Span, Option<Span>)> { + fn call_has_self_arg(&self, source: PathSource<'_, '_, '_>) -> Option<(Span, Option<Span>)> { let mut has_self_arg = None; if let PathSource::Expr(Some(parent)) = source && let ExprKind::Call(_, args) = &parent.kind @@ -1614,7 +1614,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &mut self, err: &mut Diag<'_>, span: Span, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, path: &[Segment], res: Res, path_str: &str, @@ -1666,7 +1666,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } }; - let find_span = |source: &PathSource<'_, '_>, err: &mut Diag<'_>| { + let find_span = |source: &PathSource<'_, '_, '_>, err: &mut Diag<'_>| { match source { PathSource::Expr(Some(Expr { span, kind: ExprKind::Call(_, _), .. })) | PathSource::TupleStruct(span, _) => { @@ -2699,7 +2699,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_using_enum_variant( &mut self, err: &mut Diag<'_>, - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, def_id: DefId, span: Span, ) { @@ -2877,7 +2877,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { pub(crate) fn suggest_adding_generic_parameter( &self, path: &[Segment], - source: PathSource<'_, '_>, + source: PathSource<'_, '_, '_>, ) -> Option<(Span, &'static str, String, Applicability)> { let (ident, span) = match path { [segment] diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 9ba70abd4d9..f0540725416 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1139,7 +1139,7 @@ pub struct Resolver<'ra, 'tcx> { proc_macro_stubs: FxHashSet<LocalDefId>, /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: - Vec<(Ident, MacroKind, ParentScope<'ra>, Option<NameBinding<'ra>>)>, + Vec<(Ident, MacroKind, ParentScope<'ra>, Option<NameBinding<'ra>>, Option<Span>)>, multi_segment_macro_resolutions: Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'ra>, Option<Res>, Namespace)>, builtin_attrs: Vec<(Ident, ParentScope<'ra>)>, @@ -1934,12 +1934,26 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } if let NameBindingKind::Import { import, binding } = used_binding.kind { if let ImportKind::MacroUse { warn_private: true } = import.kind { - self.lint_buffer().buffer_lint( - PRIVATE_MACRO_USE, - import.root_id, - ident.span, - BuiltinLintDiag::MacroIsPrivate(ident), - ); + // Do not report the lint if the macro name resolves in stdlib prelude + // even without the problematic `macro_use` import. + let found_in_stdlib_prelude = self.prelude.is_some_and(|prelude| { + self.maybe_resolve_ident_in_module( + ModuleOrUniformRoot::Module(prelude), + ident, + MacroNS, + &ParentScope::module(self.empty_module, self), + None, + ) + .is_ok() + }); + if !found_in_stdlib_prelude { + self.lint_buffer().buffer_lint( + PRIVATE_MACRO_USE, + import.root_id, + ident.span, + BuiltinLintDiag::MacroIsPrivate(ident), + ); + } } // Avoid marking `extern crate` items that refer to a name from extern prelude, // but not introduce it, as used if they are accessed from lexical scope. diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index ee905065b96..1b82e9c9799 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -12,7 +12,8 @@ use rustc_attr_data_structures::StabilityLevel; use rustc_data_structures::intern::Interned; use rustc_errors::{Applicability, DiagCtxtHandle, StashKey}; use rustc_expand::base::{ - DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, SyntaxExtensionKind, + Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, + SyntaxExtensionKind, }; use rustc_expand::compile_declarative_macro; use rustc_expand::expand::{ @@ -294,6 +295,14 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { && self.tcx.def_kind(mod_def_id) == DefKind::Mod }) .map(|&InvocationParent { parent_def: mod_def_id, .. }| mod_def_id); + let sugg_span = match &invoc.kind { + InvocationKind::Attr { item: Annotatable::Item(item), .. } + if !item.span.from_expansion() => + { + Some(item.span.shrink_to_lo()) + } + _ => None, + }; let (ext, res) = self.smart_resolve_macro_path( path, kind, @@ -304,6 +313,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { force, deleg_impl, looks_like_invoc_in_mod_inert_attr, + sugg_span, )?; let span = invoc.span(); @@ -386,6 +396,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { true, force, None, + None, ) { Ok((Some(ext), _)) => { if !ext.helper_attrs.is_empty() { @@ -528,6 +539,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { force: bool, deleg_impl: Option<LocalDefId>, invoc_in_mod_inert_attr: Option<LocalDefId>, + suggestion_span: Option<Span>, ) -> Result<(Arc<SyntaxExtension>, Res), Indeterminate> { let (ext, res) = match self.resolve_macro_or_delegation_path( path, @@ -538,6 +550,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { deleg_impl, invoc_in_mod_inert_attr.map(|def_id| (def_id, node_id)), None, + suggestion_span, ) { Ok((Some(ext), res)) => (ext, res), Ok((None, res)) => (self.dummy_ext(kind), res), @@ -681,6 +694,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { trace: bool, force: bool, ignore_import: Option<Import<'ra>>, + suggestion_span: Option<Span>, ) -> Result<(Option<Arc<SyntaxExtension>>, Res), Determinacy> { self.resolve_macro_or_delegation_path( path, @@ -691,6 +705,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None, None, ignore_import, + suggestion_span, ) } @@ -704,6 +719,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { deleg_impl: Option<LocalDefId>, invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>, ignore_import: Option<Import<'ra>>, + suggestion_span: Option<Span>, ) -> Result<(Option<Arc<SyntaxExtension>>, Res), Determinacy> { let path_span = ast_path.span; let mut path = Segment::from_path(ast_path); @@ -768,6 +784,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { kind, *parent_scope, binding.ok(), + suggestion_span, )); } @@ -905,7 +922,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } let macro_resolutions = mem::take(&mut self.single_segment_macro_resolutions); - for (ident, kind, parent_scope, initial_binding) in macro_resolutions { + for (ident, kind, parent_scope, initial_binding, sugg_span) in macro_resolutions { match self.early_resolve_ident_in_lexical_scope( ident, ScopeSet::Macro(kind), @@ -946,7 +963,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { expected, ident, }); - self.unresolved_macro_suggestions(&mut err, kind, &parent_scope, ident, krate); + self.unresolved_macro_suggestions( + &mut err, + kind, + &parent_scope, + ident, + krate, + sugg_span, + ); err.emit(); } } @@ -974,7 +998,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) { let span = path.span; if let Some(stability) = &ext.stability { - if let StabilityLevel::Unstable { reason, issue, is_soft, implied_by } = stability.level + if let StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. } = + stability.level { let feature = stability.feature; diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index 34be35e36ac..656ecfcab36 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -3,6 +3,7 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::internal)] +#![cfg_attr(not(bootstrap), feature(sized_hierarchy))] #![cfg_attr(test, feature(test))] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", @@ -27,3 +28,19 @@ mod serialize; pub mod int_overflow; pub mod leb128; pub mod opaque; + +// This has nothing to do with `rustc_serialize` but it is convenient to define it in one place +// for the rest of the compiler so that `cfg(bootstrap)` doesn't need to be littered throughout +// the compiler wherever `PointeeSized` would be used. `rustc_serialize` happens to be the deepest +// crate in the crate graph which uses `PointeeSized`. +// +// When bootstrap bumps, remove both the `cfg(not(bootstrap))` and `cfg(bootstrap)` lines below +// and just import `std::marker::PointeeSized` whereever this item was used. + +#[cfg(not(bootstrap))] +pub use std::marker::PointeeSized; + +#[cfg(bootstrap)] +pub trait PointeeSized {} +#[cfg(bootstrap)] +impl<T: ?Sized> PointeeSized for T {} diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index 1eefd76f92b..8940d10696d 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs @@ -142,7 +142,7 @@ pub trait Decoder { /// `rustc_metadata::rmeta::Lazy`. /// * `TyEncodable` should be used for types that are only serialized in crate /// metadata or the incremental cache. This is most types in `rustc_middle`. -pub trait Encodable<S: Encoder> { +pub trait Encodable<S: Encoder>: crate::PointeeSized { fn encode(&self, s: &mut S); } @@ -198,7 +198,7 @@ direct_serialize_impls! { char emit_char read_char } -impl<S: Encoder, T: ?Sized> Encodable<S> for &T +impl<S: Encoder, T: ?Sized + crate::PointeeSized> Encodable<S> for &T where T: Encodable<S>, { diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 528c52eace7..61953614c77 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -40,6 +40,11 @@ session_file_is_not_writeable = output file {$file} is not writeable -- check it session_file_write_fail = failed to write `{$path}` due to error `{$err}` +session_forbidden_ctarget_feature = + target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason} + .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +session_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344> + session_function_return_requires_x86_or_x86_64 = `-Zfunction-return` (except `keep`) is only supported on x86 and x86_64 session_function_return_thunk_extern_requires_non_large_code_model = `-Zfunction-return=thunk-extern` is only supported on non-large code models @@ -132,6 +137,9 @@ session_target_stack_protector_not_supported = `-Z stack-protector={$stack_prote session_unleashed_feature_help_named = skipping check for `{$gate}` feature session_unleashed_feature_help_unnamed = skipping check that does not even have a feature gate +session_unstable_ctarget_feature = + unstable feature specified for `-Ctarget-feature`: `{$feature}` + .note = this feature is not stably supported; its behavior can change in the future session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` requires `-Clto` session_unsupported_crate_type_for_target = diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index cb6034c0bf2..87e4b0a17aa 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1367,6 +1367,7 @@ impl Default for Options { real_rustc_dev_source_base_dir: None, edition: DEFAULT_EDITION, json_artifact_notifications: false, + json_timings: false, json_unused_externs: JsonUnusedExterns::No, json_future_incompat: false, pretty: None, @@ -1881,6 +1882,9 @@ pub struct JsonConfig { pub json_rendered: HumanReadableErrorType, pub json_color: ColorConfig, json_artifact_notifications: bool, + /// Output start and end timestamps of several high-level compilation sections + /// (frontend, backend, linker). + json_timings: bool, pub json_unused_externs: JsonUnusedExterns, json_future_incompat: bool, } @@ -1922,6 +1926,7 @@ pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Json let mut json_artifact_notifications = false; let mut json_unused_externs = JsonUnusedExterns::No; let mut json_future_incompat = false; + let mut json_timings = false; for option in matches.opt_strs("json") { // For now conservatively forbid `--color` with `--json` since `--json` // won't actually be emitting any colors and anything colorized is @@ -1938,6 +1943,7 @@ pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Json } "diagnostic-rendered-ansi" => json_color = ColorConfig::Always, "artifacts" => json_artifact_notifications = true, + "timings" => json_timings = true, "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud, "unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent, "future-incompat" => json_future_incompat = true, @@ -1950,6 +1956,7 @@ pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Json json_rendered, json_color, json_artifact_notifications, + json_timings, json_unused_externs, json_future_incompat, } @@ -2477,6 +2484,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M json_rendered, json_color, json_artifact_notifications, + json_timings, json_unused_externs, json_future_incompat, } = parse_json(early_dcx, matches); @@ -2498,6 +2506,10 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let mut unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers); let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches); + if !unstable_opts.unstable_options && json_timings { + early_dcx.early_fatal("--json=timings is unstable and requires using `-Zunstable-options`"); + } + check_error_format_stability(early_dcx, &unstable_opts, error_format); let output_types = parse_output_types(early_dcx, &unstable_opts, matches); @@ -2650,6 +2662,15 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let prints = collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches); + // -Zretpoline-external-thunk also requires -Zretpoline + if unstable_opts.retpoline_external_thunk { + unstable_opts.retpoline = true; + target_modifiers.insert( + OptionsTargetModifiers::UnstableOptions(UnstableOptionsTargetModifiers::retpoline), + "true".to_string(), + ); + } + let cg = cg; let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m)); @@ -2774,6 +2795,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M real_rustc_dev_source_base_dir, edition, json_artifact_notifications, + json_timings, json_unused_externs, json_future_incompat, pretty, @@ -3066,7 +3088,7 @@ pub(crate) mod dep_tracking { use rustc_target::spec::{ CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTuple, - TlsModel, WasmCAbi, + TlsModel, }; use super::{ @@ -3118,6 +3140,7 @@ pub(crate) mod dep_tracking { } impl_dep_tracking_hash_via_hash!( + (), AutoDiff, bool, usize, @@ -3177,7 +3200,6 @@ pub(crate) mod dep_tracking { Polonius, InliningThreshold, FunctionReturn, - WasmCAbi, Align, ); diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index bf95014843d..9c591dcf619 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -501,3 +501,20 @@ pub(crate) struct SoftFloatIgnored; #[note] #[note(session_soft_float_deprecated_issue)] pub(crate) struct SoftFloatDeprecated; + +#[derive(Diagnostic)] +#[diag(session_forbidden_ctarget_feature)] +#[note] +#[note(session_forbidden_ctarget_feature_issue)] +pub(crate) struct ForbiddenCTargetFeature<'a> { + pub feature: &'a str, + pub enabled: &'a str, + pub reason: &'a str, +} + +#[derive(Diagnostic)] +#[diag(session_unstable_ctarget_feature)] +#[note] +pub(crate) struct UnstableCTargetFeature<'a> { + pub feature: &'a str, +} diff --git a/compiler/rustc_session/src/features.rs b/compiler/rustc_session/src/features.rs new file mode 100644 index 00000000000..70a088a236f --- /dev/null +++ b/compiler/rustc_session/src/features.rs @@ -0,0 +1,59 @@ +use rustc_target::target_features::Stability; + +use crate::Session; +use crate::errors::{ForbiddenCTargetFeature, UnstableCTargetFeature}; + +pub trait StabilityExt { + /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`. + /// Otherwise, some features also may only be enabled by flag (target modifier). + /// (It might still be nightly-only even if this returns `true`, so make sure to also check + /// `requires_nightly`.) + fn is_toggle_permitted(&self, sess: &Session) -> Result<(), &'static str>; + + /// Check that feature is correctly enabled/disabled by command line flag (emits warnings) + fn verify_feature_enabled_by_flag(&self, sess: &Session, enable: bool, feature: &str); +} + +impl StabilityExt for Stability { + fn is_toggle_permitted(&self, sess: &Session) -> Result<(), &'static str> { + match self { + Stability::Forbidden { reason } => Err(reason), + Stability::TargetModifierOnly { reason, flag } => { + if !sess.opts.target_feature_flag_enabled(*flag) { Err(reason) } else { Ok(()) } + } + _ => Ok(()), + } + } + fn verify_feature_enabled_by_flag(&self, sess: &Session, enable: bool, feature: &str) { + if let Err(reason) = self.is_toggle_permitted(sess) { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: if enable { "enabled" } else { "disabled" }, + reason, + }); + } else if self.requires_nightly().is_some() { + // An unstable feature. Warn about using it. It makes little sense + // to hard-error here since we just warn about fully unknown + // features above. + sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + } + } +} + +pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec<&str>) { + // -Zretpoline without -Zretpoline-external-thunk enables + // retpoline-indirect-branches and retpoline-indirect-calls target features + let unstable_opts = &sess.opts.unstable_opts; + if unstable_opts.retpoline && !unstable_opts.retpoline_external_thunk { + features.push("+retpoline-indirect-branches"); + features.push("+retpoline-indirect-calls"); + } + // -Zretpoline-external-thunk (maybe, with -Zretpoline too) enables + // retpoline-external-thunk, retpoline-indirect-branches and + // retpoline-indirect-calls target features + if unstable_opts.retpoline_external_thunk { + features.push("+retpoline-external-thunk"); + features.push("+retpoline-indirect-branches"); + features.push("+retpoline-indirect-calls"); + } +} diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 5e5872ee068..4added19e56 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -29,6 +29,7 @@ pub use session::*; pub mod output; pub use getopts; +pub mod features; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 8d8340e60c3..31b4237d3b2 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -16,7 +16,7 @@ use rustc_span::{RealFileName, SourceFileHashAlgorithm}; use rustc_target::spec::{ CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, - TargetTuple, TlsModel, WasmCAbi, + TargetTuple, TlsModel, }; use crate::config::*; @@ -290,6 +290,14 @@ macro_rules! top_level_options { mods.sort_by(|a, b| a.opt.cmp(&b.opt)); mods } + + pub fn target_feature_flag_enabled(&self, flag: &str) -> bool { + match flag { + "retpoline" => self.unstable_opts.retpoline, + "retpoline-external-thunk" => self.unstable_opts.retpoline_external_thunk, + _ => false, + } + } } ); } @@ -412,6 +420,10 @@ top_level_options!( /// by the compiler. json_artifact_notifications: bool [TRACKED], + /// `true` if we're emitting JSON timings with the start and end of + /// high-level compilation sections + json_timings: bool [UNTRACKED], + /// `true` if we're emitting a JSON blob containing the unused externs json_unused_externs: JsonUnusedExterns [UNTRACKED], @@ -804,7 +816,7 @@ mod desc { "either a boolean (`yes`, `no`, `on`, `off`, etc), or a non-negative number"; pub(crate) const parse_llvm_module_flag: &str = "<key>:<type>:<value>:<behavior>. Type must currently be `u32`. Behavior should be one of (`error`, `warning`, `require`, `override`, `append`, `appendunique`, `max`, `min`)"; pub(crate) const parse_function_return: &str = "`keep` or `thunk-extern`"; - pub(crate) const parse_wasm_c_abi: &str = "`legacy` or `spec`"; + pub(crate) const parse_wasm_c_abi: &str = "`spec`"; pub(crate) const parse_mir_include_spans: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or `nll` (default: `nll`)"; pub(crate) const parse_align: &str = "a number that is a power of 2 between 1 and 2^29"; @@ -1900,14 +1912,8 @@ pub mod parse { true } - pub(crate) fn parse_wasm_c_abi(slot: &mut WasmCAbi, v: Option<&str>) -> bool { - match v { - Some("spec") => *slot = WasmCAbi::Spec, - // Explicitly setting the `-Z` flag suppresses the lint. - Some("legacy") => *slot = WasmCAbi::Legacy { with_lint: false }, - _ => return false, - } - true + pub(crate) fn parse_wasm_c_abi(_slot: &mut (), v: Option<&str>) -> bool { + v == Some("spec") } pub(crate) fn parse_mir_include_spans(slot: &mut MirIncludeSpans, v: Option<&str>) -> bool { @@ -2243,6 +2249,8 @@ options! { environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"), has_thread_local: Option<bool> = (None, parse_opt_bool, [TRACKED], "explicitly enable the `cfg(target_thread_local)` directive"), + hint_mostly_unused: bool = (false, parse_bool, [TRACKED], + "hint that most of this crate will go unused, to minimize work for uncalled functions"), human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], "generate human-readable, predictable names for codegen units (default: no)"), identify_regions: bool = (false, parse_bool, [UNTRACKED], @@ -2315,6 +2323,8 @@ options! { (space separated)"), macro_backtrace: bool = (false, parse_bool, [UNTRACKED], "show macro backtraces (default: no)"), + macro_stats: bool = (false, parse_bool, [UNTRACKED], + "print some statistics about macro expansions (default: no)"), maximal_hir_to_mir_coverage: bool = (false, parse_bool, [TRACKED], "save as much information as possible about the correspondence between MIR and HIR \ as source scopes (default: no)"), @@ -2456,6 +2466,11 @@ options! { remark_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED], "directory into which to write optimization remarks (if not specified, they will be \ written to standard error output)"), + retpoline: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], + "enables retpoline-indirect-branches and retpoline-indirect-calls target features (default: no)"), + retpoline_external_thunk: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], + "enables retpoline-external-thunk, retpoline-indirect-branches and retpoline-indirect-calls \ + target features (default: no)"), sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], "use a sanitizer"), sanitizer_cfi_canonical_jump_tables: Option<bool> = (Some(true), parse_opt_bool, [TRACKED], @@ -2637,8 +2652,11 @@ written to standard error output)"), Requires `-Clto[=[fat,yes]]`"), wasi_exec_model: Option<WasiExecModel> = (None, parse_wasi_exec_model, [TRACKED], "whether to build a wasi command or reactor"), - wasm_c_abi: WasmCAbi = (WasmCAbi::Legacy { with_lint: true }, parse_wasm_c_abi, [TRACKED], - "use spec-compliant C ABI for `wasm32-unknown-unknown` (default: legacy)"), + // This option only still exists to provide a more gradual transition path for people who need + // the spec-complaint C ABI to be used. + // FIXME remove this after a couple releases + wasm_c_abi: () = ((), parse_wasm_c_abi, [TRACKED], + "use spec-compliant C ABI for `wasm32-unknown-unknown` (deprecated, always enabled)"), write_long_types_to_disk: bool = (true, parse_bool, [UNTRACKED], "whether long type names should be written to files instead of being printed in errors"), // tidy-alphabetical-end diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 1fe521bd32d..87c848cf857 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -17,7 +17,7 @@ use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue}; use rustc_span::edition::Edition; use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{FilePathMapping, SourceMap}; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use crate::Session; use crate::config::{Cfg, CheckCfg}; @@ -192,8 +192,11 @@ pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>( } else { err.subdiagnostic(FeatureDiagnosticHelp { feature }); } - - if sess.opts.unstable_opts.ui_testing { + if feature == sym::rustc_attrs { + // We're unlikely to stabilize something out of `rustc_attrs` + // without at least renaming it, so pointing out how old + // the compiler is will do little good. + } else if sess.opts.unstable_opts.ui_testing { err.subdiagnostic(SuggestUpgradeCompiler::ui_testing()); } else if let Some(suggestion) = SuggestUpgradeCompiler::new() { err.subdiagnostic(suggestion); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 6b85e0abc86..ca42c5a4256 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -18,6 +18,7 @@ use rustc_errors::emitter::{ DynEmitter, HumanEmitter, HumanReadableErrorType, OutputTheme, stderr_destination, }; use rustc_errors::json::JsonEmitter; +use rustc_errors::timings::TimingSectionHandler; use rustc_errors::{ Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort, FluentBundle, LazyFallbackBundle, TerminalUrl, fallback_fluent_bundle, @@ -156,6 +157,9 @@ pub struct Session { /// Used by `-Z self-profile`. pub prof: SelfProfilerRef, + /// Used to emit section timings events (enabled by `--json=timings`). + pub timings: TimingSectionHandler, + /// Data about code being compiled, gathered during compilation. pub code_stats: CodeStats, @@ -215,13 +219,6 @@ pub struct Session { pub invocation_temp: Option<String>, } -#[derive(PartialEq, Eq, PartialOrd, Ord)] -pub enum MetadataKind { - None, - Uncompressed, - Compressed, -} - #[derive(Clone, Copy)] pub enum CodegenUnits { /// Specified by the user. In this case we try fairly hard to produce the @@ -1133,6 +1130,8 @@ pub fn build_session( .as_ref() .map(|_| rng().next_u32().to_base_fixed_len(CASE_INSENSITIVE).to_string()); + let timings = TimingSectionHandler::new(sopts.json_timings); + let sess = Session { target, host, @@ -1143,6 +1142,7 @@ pub fn build_session( io, incr_comp_session: RwLock::new(IncrCompSession::NotInitialized), prof, + timings, code_stats: Default::default(), lint_store: None, driver_lint_caps, diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs index 771ff98d58d..63623cfad72 100644 --- a/compiler/rustc_smir/src/lib.rs +++ b/compiler/rustc_smir/src/lib.rs @@ -9,6 +9,7 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::usage_of_ty_tykind)] +#![cfg_attr(not(bootstrap), feature(sized_hierarchy))] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(attr(allow(unused_variables), deny(warnings))) diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index 6e13b87c41d..a4c6f186222 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -496,6 +496,7 @@ impl RustcInternal for Abi { Abi::RustCold => rustc_abi::ExternAbi::RustCold, Abi::RiscvInterruptM => rustc_abi::ExternAbi::RiscvInterruptM, Abi::RiscvInterruptS => rustc_abi::ExternAbi::RiscvInterruptS, + Abi::Custom => rustc_abi::ExternAbi::Custom, } } } diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index ef8c88355f6..baa4c0681e8 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -12,7 +12,8 @@ use rustc_middle::ty::layout::{ }; use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths}; use rustc_middle::ty::{ - GenericPredicates, Instance, List, ScalarInt, TyCtxt, TypeVisitableExt, ValTree, + CoroutineArgsExt, GenericPredicates, Instance, List, ScalarInt, TyCtxt, TypeVisitableExt, + ValTree, }; use rustc_middle::{mir, ty}; use rustc_span::def_id::LOCAL_CRATE; @@ -22,9 +23,9 @@ use stable_mir::mir::mono::{InstanceDef, StaticDef}; use stable_mir::mir::{BinOp, Body, Place, UnOp}; use stable_mir::target::{MachineInfo, MachineSize}; use stable_mir::ty::{ - AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef, - ForeignItemKind, GenericArgs, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, Ty, - TyConst, TyKind, UintTy, VariantDef, + AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, CoroutineDef, Discr, FieldDef, FnDef, + ForeignDef, ForeignItemKind, GenericArgs, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, + Span, Ty, TyConst, TyKind, UintTy, VariantDef, VariantIdx, }; use stable_mir::{Crate, CrateDef, CrateItem, CrateNum, DefId, Error, Filename, ItemKind, Symbol}; @@ -447,6 +448,30 @@ impl<'tcx> SmirCtxt<'tcx> { def.internal(&mut *tables, tcx).variants().len() } + /// Discriminant for a given variant index of AdtDef + pub fn adt_discr_for_variant(&self, adt: AdtDef, variant: VariantIdx) -> Discr { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let adt = adt.internal(&mut *tables, tcx); + let variant = variant.internal(&mut *tables, tcx); + adt.discriminant_for_variant(tcx, variant).stable(&mut *tables) + } + + /// Discriminant for a given variand index and args of a coroutine + pub fn coroutine_discr_for_variant( + &self, + coroutine: CoroutineDef, + args: &GenericArgs, + variant: VariantIdx, + ) -> Discr { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let coroutine = coroutine.def_id().internal(&mut *tables, tcx); + let args = args.internal(&mut *tables, tcx); + let variant = variant.internal(&mut *tables, tcx); + args.as_coroutine().discriminant_for_variant(coroutine, tcx, variant).stable(&mut *tables) + } + /// The name of a variant. pub fn variant_name(&self, def: VariantDef) -> Symbol { let mut tables = self.0.borrow_mut(); diff --git a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs index 46f1ca61cec..35d5b7fb89a 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs @@ -101,6 +101,7 @@ impl<'tcx> Stable<'tcx> for CanonAbi { CanonAbi::C => CallConvention::C, CanonAbi::Rust => CallConvention::Rust, CanonAbi::RustCold => CallConvention::Cold, + CanonAbi::Custom => CallConvention::Custom, CanonAbi::Arm(arm_call) => match arm_call { ArmCall::Aapcs => CallConvention::ArmAapcs, ArmCall::CCmseNonSecureCall => CallConvention::CCmseNonSecureCall, diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index b0c9dba78a6..b4239ddd896 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -879,6 +879,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi { ExternAbi::RustCold => Abi::RustCold, ExternAbi::RiscvInterruptM => Abi::RiscvInterruptM, ExternAbi::RiscvInterruptS => Abi::RiscvInterruptS, + ExternAbi::Custom => Abi::Custom, } } } @@ -959,3 +960,11 @@ impl<'tcx> Stable<'tcx> for ty::ImplTraitInTraitData { } } } + +impl<'tcx> Stable<'tcx> for rustc_middle::ty::util::Discr<'tcx> { + type T = stable_mir::ty::Discr; + + fn stable(&self, tables: &mut Tables<'_>) -> Self::T { + stable_mir::ty::Discr { val: self.val, ty: self.ty.stable(tables) } + } +} diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index b5003baaf63..64fe181c26d 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -9,6 +9,7 @@ use std::ops::RangeInclusive; +use rustc_data_structures::PointeeSized; use rustc_hir::def::DefKind; use rustc_middle::mir; use rustc_middle::mir::interpret::AllocId; @@ -158,7 +159,7 @@ pub(crate) fn new_item_kind(kind: DefKind) -> ItemKind { } /// Trait used to convert between an internal MIR type to a Stable MIR type. -pub trait Stable<'cx> { +pub trait Stable<'cx>: PointeeSized { /// The stable representation of the type implementing Stable. type T; /// Converts an object to the equivalent Stable MIR representation. diff --git a/compiler/rustc_smir/src/stable_mir/abi.rs b/compiler/rustc_smir/src/stable_mir/abi.rs index 347c6ed16a2..d8a2b97662c 100644 --- a/compiler/rustc_smir/src/stable_mir/abi.rs +++ b/compiler/rustc_smir/src/stable_mir/abi.rs @@ -430,6 +430,8 @@ pub enum CallConvention { PreserveMost, PreserveAll, + Custom, + // Target-specific calling conventions. ArmAapcs, CCmseNonSecureCall, diff --git a/compiler/rustc_smir/src/stable_mir/compiler_interface.rs b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs index 3967ad13eeb..2668fba9f4f 100644 --- a/compiler/rustc_smir/src/stable_mir/compiler_interface.rs +++ b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs @@ -13,10 +13,10 @@ use stable_mir::mir::mono::{Instance, InstanceDef, StaticDef}; use stable_mir::mir::{BinOp, Body, Place, UnOp}; use stable_mir::target::MachineInfo; use stable_mir::ty::{ - AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef, - ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics, - ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, TraitDecl, - TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, + AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, CoroutineDef, Discr, FieldDef, FnDef, + ForeignDef, ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, + Generics, ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, + TraitDecl, TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, VariantIdx, }; use stable_mir::{ AssocItems, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls, @@ -230,6 +230,21 @@ impl<'tcx> SmirInterface<'tcx> { self.cx.adt_variants_len(def) } + /// Discriminant for a given variant index of AdtDef + pub(crate) fn adt_discr_for_variant(&self, adt: AdtDef, variant: VariantIdx) -> Discr { + self.cx.adt_discr_for_variant(adt, variant) + } + + /// Discriminant for a given variand index and args of a coroutine + pub(crate) fn coroutine_discr_for_variant( + &self, + coroutine: CoroutineDef, + args: &GenericArgs, + variant: VariantIdx, + ) -> Discr { + self.cx.coroutine_discr_for_variant(coroutine, args, variant) + } + /// The name of a variant. pub(crate) fn variant_name(&self, def: VariantDef) -> Symbol { self.cx.variant_name(def) diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 45936857d33..4415cd6e2e3 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -756,6 +756,12 @@ crate_def! { pub CoroutineDef; } +impl CoroutineDef { + pub fn discriminant_for_variant(&self, args: &GenericArgs, idx: VariantIdx) -> Discr { + with(|cx| cx.coroutine_discr_for_variant(*self, args, idx)) + } +} + crate_def! { #[derive(Serialize)] pub CoroutineClosureDef; @@ -831,6 +837,15 @@ impl AdtDef { pub fn repr(&self) -> ReprOptions { with(|cx| cx.adt_repr(*self)) } + + pub fn discriminant_for_variant(&self, idx: VariantIdx) -> Discr { + with(|cx| cx.adt_discr_for_variant(*self, idx)) + } +} + +pub struct Discr { + pub val: u128, + pub ty: Ty, } /// Definition of a variant, which can be either a struct / union field or an enum variant. @@ -1111,6 +1126,7 @@ pub enum Abi { RustCold, RiscvInterruptM, RiscvInterruptS, + Custom, } /// A binder represents a possibly generic type and its bound vars. diff --git a/compiler/rustc_span/src/caching_source_map_view.rs b/compiler/rustc_span/src/caching_source_map_view.rs index d8a4cc2f2e2..a887b50ec1e 100644 --- a/compiler/rustc_span/src/caching_source_map_view.rs +++ b/compiler/rustc_span/src/caching_source_map_view.rs @@ -2,7 +2,7 @@ use std::ops::Range; use std::sync::Arc; use crate::source_map::SourceMap; -use crate::{BytePos, Pos, RelativeBytePos, SourceFile, SpanData}; +use crate::{BytePos, Pos, RelativeBytePos, SourceFile, SpanData, StableSourceFileId}; #[derive(Clone)] struct CacheEntry { @@ -114,7 +114,7 @@ impl<'sm> CachingSourceMapView<'sm> { pub fn span_data_to_lines_and_cols( &mut self, span_data: &SpanData, - ) -> Option<(Arc<SourceFile>, usize, BytePos, usize, BytePos)> { + ) -> Option<(StableSourceFileId, usize, BytePos, usize, BytePos)> { self.time_stamp += 1; // Check if lo and hi are in the cached lines. @@ -132,7 +132,7 @@ impl<'sm> CachingSourceMapView<'sm> { } ( - Arc::clone(&lo.file), + lo.file.stable_id, lo.line_number, span_data.lo - lo.line.start, hi.line_number, @@ -226,7 +226,7 @@ impl<'sm> CachingSourceMapView<'sm> { assert_eq!(lo.file_index, hi.file_index); Some(( - Arc::clone(&lo.file), + lo.file.stable_id, lo.line_number, span_data.lo - lo.line.start, hi.line_number, diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index b621920d62b..315dedec107 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1135,7 +1135,7 @@ impl ExpnKind { } /// The kind of macro invocation or definition. -#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum MacroKind { /// A bang macro `foo!()`. diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index ed74dea5f1e..c8a29a2f68f 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -2600,7 +2600,7 @@ pub trait HashStableContext { fn span_data_to_lines_and_cols( &mut self, span: &SpanData, - ) -> Option<(Arc<SourceFile>, usize, BytePos, usize, BytePos)>; + ) -> Option<(StableSourceFileId, usize, BytePos, usize, BytePos)>; fn hashing_controls(&self) -> HashingControls; } @@ -2657,7 +2657,7 @@ where }; Hash::hash(&TAG_VALID_SPAN, hasher); - Hash::hash(&file.stable_id, hasher); + Hash::hash(&file, hasher); // Hash both the length and the end location (line/column) of a span. If we // hash only the length, for example, then two otherwise equal spans with diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d66f98871b9..812a254990c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -407,6 +407,7 @@ symbols! { abi_amdgpu_kernel, abi_avr_interrupt, abi_c_cmse_nonsecure_call, + abi_custom, abi_efiapi, abi_gpu_kernel, abi_msp430_interrupt, @@ -429,6 +430,8 @@ symbols! { aggregate_raw_ptr, alias, align, + align_of, + align_of_val, alignment, all, alloc, @@ -1352,9 +1355,8 @@ symbols! { memtag, message, meta, + meta_sized, metadata_type, - min_align_of, - min_align_of_val, min_const_fn, min_const_generics, min_const_unsafe_fn, @@ -1512,6 +1514,7 @@ symbols! { offset_of_nested, offset_of_slice, ok_or_else, + old_name, omit_gdb_pretty_printer_section, on, on_unimplemented, @@ -1611,6 +1614,7 @@ symbols! { plugin_registrar, plugins, pointee, + pointee_sized, pointee_trait, pointer, pointer_like, @@ -2014,6 +2018,7 @@ symbols! { size_of, size_of_val, sized, + sized_hierarchy, skip, slice, slice_from_raw_parts, @@ -2178,6 +2183,7 @@ symbols! { type_changing_struct_update, type_const, type_id, + type_ir, type_ir_infer_ctxt_like, type_ir_inherent, type_ir_interner, @@ -2288,6 +2294,7 @@ symbols! { usize_legacy_fn_max_value, usize_legacy_fn_min_value, usize_legacy_mod, + v1, v8plus, va_arg, va_copy, @@ -2675,7 +2682,7 @@ impl Interner { assert_eq!( strings.len(), init.len() + extra.len(), - "`init` or `extra` contain duplicate symbols", + "there are duplicate symbols in the rustc symbol list and the extra symbols added by the driver", ); Interner(Lock::new(InternerInner { arena: Default::default(), strings })) } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 49a5e20d7cf..1db8ad72b32 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -85,10 +85,6 @@ pub fn mangle_internal_symbol<'tcx>(tcx: TyCtxt<'tcx>, item_name: &str) -> Strin if item_name == "rust_eh_personality" { // rust_eh_personality must not be renamed as LLVM hard-codes the name return "rust_eh_personality".to_owned(); - } else if item_name == "__rust_no_alloc_shim_is_unstable" { - // Temporary back compat hack to give people the chance to migrate to - // include #[rustc_std_internal_symbol]. - return "__rust_no_alloc_shim_is_unstable".to_owned(); } let prefix = "_R"; diff --git a/compiler/rustc_target/src/asm/loongarch.rs b/compiler/rustc_target/src/asm/loongarch.rs index b4ea6fc592a..51e7ee8daa4 100644 --- a/compiler/rustc_target/src/asm/loongarch.rs +++ b/compiler/rustc_target/src/asm/loongarch.rs @@ -34,11 +34,15 @@ impl LoongArchInlineAsmRegClass { pub fn supported_types( self, - _arch: InlineAsmArch, + arch: InlineAsmArch, ) -> &'static [(InlineAsmType, Option<Symbol>)] { - match self { - Self::reg => types! { _: I8, I16, I32, I64, F32, F64; }, - Self::freg => types! { f: F32; d: F64; }, + match (self, arch) { + (Self::reg, InlineAsmArch::LoongArch64) => { + types! { _: I8, I16, I32, I64, F16, F32, F64; } + } + (Self::reg, InlineAsmArch::LoongArch32) => types! { _: I8, I16, I32, F16, F32; }, + (Self::freg, _) => types! { f: F16, F32; d: F64; }, + _ => unreachable!("unsupported register class"), } } } diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index 89f324bc313..77c0cf06fc1 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -2,9 +2,7 @@ use rustc_abi::{ BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Size, TyAbiInterface, }; -use crate::callconv::{ - ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, PassMode, Uniform, -}; +use crate::callconv::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Uniform}; fn extend_integer_width_mips<Ty>(arg: &mut ArgAbi<'_, Ty>, bits: u64) { // Always sign extend u32 values on 64-bit mips @@ -140,16 +138,7 @@ where // Extract first 8 chunks as the prefix let rest_size = size - Size::from_bytes(8) * prefix_index as u64; - arg.cast_to(CastTarget { - prefix, - rest: Uniform::new(Reg::i64(), rest_size), - attrs: ArgAttributes { - regular: ArgAttribute::default(), - arg_ext: ArgExtension::None, - pointee_size: Size::ZERO, - pointee_align: None, - }, - }); + arg.cast_to(CastTarget::prefixed(prefix, Uniform::new(Reg::i64(), rest_size))); } pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index f9ecf02f857..71cc2a45563 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -7,7 +7,7 @@ use rustc_abi::{ use rustc_macros::HashStable_Generic; pub use crate::spec::AbiMap; -use crate::spec::{HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, WasmCAbi}; +use crate::spec::{HasTargetSpec, HasX86AbiOpt}; mod aarch64; mod amdgpu; @@ -197,6 +197,17 @@ impl ArgAttributes { } } +impl From<ArgAttribute> for ArgAttributes { + fn from(value: ArgAttribute) -> Self { + Self { + regular: value, + arg_ext: ArgExtension::None, + pointee_size: Size::ZERO, + pointee_align: None, + } + } +} + /// An argument passed entirely registers with the /// same kind (e.g., HFA / HVA on PPC64 and AArch64). #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)] @@ -251,6 +262,9 @@ impl Uniform { #[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub struct CastTarget { pub prefix: [Option<Reg>; 8], + /// The offset of `rest` from the start of the value. Currently only implemented for a `Reg` + /// pair created by the `offset_pair` method. + pub rest_offset: Option<Size>, pub rest: Uniform, pub attrs: ArgAttributes, } @@ -263,42 +277,45 @@ impl From<Reg> for CastTarget { impl From<Uniform> for CastTarget { fn from(uniform: Uniform) -> CastTarget { - CastTarget { - prefix: [None; 8], - rest: uniform, - attrs: ArgAttributes { - regular: ArgAttribute::default(), - arg_ext: ArgExtension::None, - pointee_size: Size::ZERO, - pointee_align: None, - }, - } + Self::prefixed([None; 8], uniform) } } impl CastTarget { - pub fn pair(a: Reg, b: Reg) -> CastTarget { - CastTarget { + pub fn prefixed(prefix: [Option<Reg>; 8], rest: Uniform) -> Self { + Self { prefix, rest_offset: None, rest, attrs: ArgAttributes::new() } + } + + pub fn offset_pair(a: Reg, offset_from_start: Size, b: Reg) -> Self { + Self { prefix: [Some(a), None, None, None, None, None, None, None], - rest: Uniform::from(b), - attrs: ArgAttributes { - regular: ArgAttribute::default(), - arg_ext: ArgExtension::None, - pointee_size: Size::ZERO, - pointee_align: None, - }, + rest_offset: Some(offset_from_start), + rest: b.into(), + attrs: ArgAttributes::new(), } } + pub fn with_attrs(mut self, attrs: ArgAttributes) -> Self { + self.attrs = attrs; + self + } + + pub fn pair(a: Reg, b: Reg) -> CastTarget { + Self::prefixed([Some(a), None, None, None, None, None, None, None], Uniform::from(b)) + } + /// When you only access the range containing valid data, you can use this unaligned size; /// otherwise, use the safer `size` method. pub fn unaligned_size<C: HasDataLayout>(&self, _cx: &C) -> Size { // Prefix arguments are passed in specific designated registers - let prefix_size = self - .prefix - .iter() - .filter_map(|x| x.map(|reg| reg.size)) - .fold(Size::ZERO, |acc, size| acc + size); + let prefix_size = if let Some(offset_from_start) = self.rest_offset { + offset_from_start + } else { + self.prefix + .iter() + .filter_map(|x| x.map(|reg| reg.size)) + .fold(Size::ZERO, |acc, size| acc + size) + }; // Remaining arguments are passed in chunks of the unit size let rest_size = self.rest.unit.size * self.rest.total.bytes().div_ceil(self.rest.unit.size.bytes()); @@ -322,9 +339,22 @@ impl CastTarget { /// Checks if these two `CastTarget` are equal enough to be considered "the same for all /// function call ABIs". pub fn eq_abi(&self, other: &Self) -> bool { - let CastTarget { prefix: prefix_l, rest: rest_l, attrs: attrs_l } = self; - let CastTarget { prefix: prefix_r, rest: rest_r, attrs: attrs_r } = other; - prefix_l == prefix_r && rest_l == rest_r && attrs_l.eq_abi(attrs_r) + let CastTarget { + prefix: prefix_l, + rest_offset: rest_offset_l, + rest: rest_l, + attrs: attrs_l, + } = self; + let CastTarget { + prefix: prefix_r, + rest_offset: rest_offset_r, + rest: rest_r, + attrs: attrs_r, + } = other; + prefix_l == prefix_r + && rest_offset_l == rest_offset_r + && rest_l == rest_r + && attrs_l.eq_abi(attrs_r) } } @@ -593,7 +623,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { pub fn adjust_for_foreign_abi<C>(&mut self, cx: &C, abi: ExternAbi) where Ty: TyAbiInterface<'a, C> + Copy, - C: HasDataLayout + HasTargetSpec + HasWasmCAbiOpt + HasX86AbiOpt, + C: HasDataLayout + HasTargetSpec + HasX86AbiOpt, { if abi == ExternAbi::X86Interrupt { if let Some(arg) = self.args.first_mut() { @@ -669,14 +699,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { "hexagon" => hexagon::compute_abi_info(self), "xtensa" => xtensa::compute_abi_info(cx, self), "riscv32" | "riscv64" => riscv::compute_abi_info(cx, self), - "wasm32" => { - if spec.os == "unknown" && matches!(cx.wasm_c_abi_opt(), WasmCAbi::Legacy { .. }) { - wasm::compute_wasm_abi_info(self) - } else { - wasm::compute_c_abi_info(cx, self) - } - } - "wasm64" => wasm::compute_c_abi_info(cx, self), + "wasm32" | "wasm64" => wasm::compute_abi_info(cx, self), "bpf" => bpf::compute_abi_info(self), arch => panic!("no lowering implemented for {arch}"), } diff --git a/compiler/rustc_target/src/callconv/nvptx64.rs b/compiler/rustc_target/src/callconv/nvptx64.rs index a26e7dac5ba..44977de7fcb 100644 --- a/compiler/rustc_target/src/callconv/nvptx64.rs +++ b/compiler/rustc_target/src/callconv/nvptx64.rs @@ -1,6 +1,6 @@ use rustc_abi::{HasDataLayout, Reg, Size, TyAbiInterface}; -use super::{ArgAttribute, ArgAttributes, ArgExtension, CastTarget}; +use super::CastTarget; use crate::callconv::{ArgAbi, FnAbi, Uniform}; fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) { @@ -34,16 +34,10 @@ fn classify_aggregate<Ty>(arg: &mut ArgAbi<'_, Ty>) { }; if align_bytes == size.bytes() { - arg.cast_to(CastTarget { - prefix: [Some(reg), None, None, None, None, None, None, None], - rest: Uniform::new(Reg::i8(), Size::from_bytes(0)), - attrs: ArgAttributes { - regular: ArgAttribute::default(), - arg_ext: ArgExtension::None, - pointee_size: Size::ZERO, - pointee_align: None, - }, - }); + arg.cast_to(CastTarget::prefixed( + [Some(reg), None, None, None, None, None, None, None], + Uniform::new(Reg::i8(), Size::ZERO), + )); } else { arg.cast_to(Uniform::new(reg, size)); } @@ -78,11 +72,10 @@ where }; if arg.layout.size.bytes() / align_bytes == 1 { // Make sure we pass the struct as array at the LLVM IR level and not as a single integer. - arg.cast_to(CastTarget { - prefix: [Some(unit), None, None, None, None, None, None, None], - rest: Uniform::new(unit, Size::ZERO), - attrs: ArgAttributes::new(), - }); + arg.cast_to(CastTarget::prefixed( + [Some(unit), None, None, None, None, None, None, None], + Uniform::new(unit, Size::ZERO), + )); } else { arg.cast_to(Uniform::new(unit, arg.layout.size)); } diff --git a/compiler/rustc_target/src/callconv/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs index cd1d3cd1eee..6a2038f9381 100644 --- a/compiler/rustc_target/src/callconv/riscv.rs +++ b/compiler/rustc_target/src/callconv/riscv.rs @@ -14,16 +14,16 @@ use crate::spec::HasTargetSpec; #[derive(Copy, Clone)] enum RegPassKind { - Float(Reg), - Integer(Reg), + Float { offset_from_start: Size, ty: Reg }, + Integer { offset_from_start: Size, ty: Reg }, Unknown, } #[derive(Copy, Clone)] enum FloatConv { - FloatPair(Reg, Reg), + FloatPair { first_ty: Reg, second_ty_offset_from_start: Size, second_ty: Reg }, Float(Reg), - MixedPair(Reg, Reg), + MixedPair { first_ty: Reg, second_ty_offset_from_start: Size, second_ty: Reg }, } #[derive(Copy, Clone)] @@ -43,6 +43,7 @@ fn should_use_fp_conv_helper<'a, Ty, C>( flen: u64, field1_kind: &mut RegPassKind, field2_kind: &mut RegPassKind, + offset_from_start: Size, ) -> Result<(), CannotUseFpConv> where Ty: TyAbiInterface<'a, C> + Copy, @@ -55,16 +56,16 @@ where } match (*field1_kind, *field2_kind) { (RegPassKind::Unknown, _) => { - *field1_kind = RegPassKind::Integer(Reg { - kind: RegKind::Integer, - size: arg_layout.size, - }); + *field1_kind = RegPassKind::Integer { + offset_from_start, + ty: Reg { kind: RegKind::Integer, size: arg_layout.size }, + }; } - (RegPassKind::Float(_), RegPassKind::Unknown) => { - *field2_kind = RegPassKind::Integer(Reg { - kind: RegKind::Integer, - size: arg_layout.size, - }); + (RegPassKind::Float { .. }, RegPassKind::Unknown) => { + *field2_kind = RegPassKind::Integer { + offset_from_start, + ty: Reg { kind: RegKind::Integer, size: arg_layout.size }, + }; } _ => return Err(CannotUseFpConv), } @@ -75,12 +76,16 @@ where } match (*field1_kind, *field2_kind) { (RegPassKind::Unknown, _) => { - *field1_kind = - RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size }); + *field1_kind = RegPassKind::Float { + offset_from_start, + ty: Reg { kind: RegKind::Float, size: arg_layout.size }, + }; } (_, RegPassKind::Unknown) => { - *field2_kind = - RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size }); + *field2_kind = RegPassKind::Float { + offset_from_start, + ty: Reg { kind: RegKind::Float, size: arg_layout.size }, + }; } _ => return Err(CannotUseFpConv), } @@ -102,13 +107,14 @@ where flen, field1_kind, field2_kind, + offset_from_start, ); } return Err(CannotUseFpConv); } } FieldsShape::Array { count, .. } => { - for _ in 0..count { + for i in 0..count { let elem_layout = arg_layout.field(cx, 0); should_use_fp_conv_helper( cx, @@ -117,6 +123,7 @@ where flen, field1_kind, field2_kind, + offset_from_start + elem_layout.size * i, )?; } } @@ -127,7 +134,15 @@ where } for i in arg_layout.fields.index_by_increasing_offset() { let field = arg_layout.field(cx, i); - should_use_fp_conv_helper(cx, &field, xlen, flen, field1_kind, field2_kind)?; + should_use_fp_conv_helper( + cx, + &field, + xlen, + flen, + field1_kind, + field2_kind, + offset_from_start + arg_layout.fields.offset(i), + )?; } } }, @@ -146,14 +161,52 @@ where { let mut field1_kind = RegPassKind::Unknown; let mut field2_kind = RegPassKind::Unknown; - if should_use_fp_conv_helper(cx, arg, xlen, flen, &mut field1_kind, &mut field2_kind).is_err() { + if should_use_fp_conv_helper( + cx, + arg, + xlen, + flen, + &mut field1_kind, + &mut field2_kind, + Size::ZERO, + ) + .is_err() + { return None; } match (field1_kind, field2_kind) { - (RegPassKind::Integer(l), RegPassKind::Float(r)) => Some(FloatConv::MixedPair(l, r)), - (RegPassKind::Float(l), RegPassKind::Integer(r)) => Some(FloatConv::MixedPair(l, r)), - (RegPassKind::Float(l), RegPassKind::Float(r)) => Some(FloatConv::FloatPair(l, r)), - (RegPassKind::Float(f), RegPassKind::Unknown) => Some(FloatConv::Float(f)), + ( + RegPassKind::Integer { offset_from_start, .. } + | RegPassKind::Float { offset_from_start, .. }, + _, + ) if offset_from_start != Size::ZERO => { + panic!("type {:?} has a first field with non-zero offset {offset_from_start:?}", arg.ty) + } + ( + RegPassKind::Integer { ty: first_ty, .. }, + RegPassKind::Float { offset_from_start, ty: second_ty }, + ) => Some(FloatConv::MixedPair { + first_ty, + second_ty_offset_from_start: offset_from_start, + second_ty, + }), + ( + RegPassKind::Float { ty: first_ty, .. }, + RegPassKind::Integer { offset_from_start, ty: second_ty }, + ) => Some(FloatConv::MixedPair { + first_ty, + second_ty_offset_from_start: offset_from_start, + second_ty, + }), + ( + RegPassKind::Float { ty: first_ty, .. }, + RegPassKind::Float { offset_from_start, ty: second_ty }, + ) => Some(FloatConv::FloatPair { + first_ty, + second_ty_offset_from_start: offset_from_start, + second_ty, + }), + (RegPassKind::Float { ty, .. }, RegPassKind::Unknown) => Some(FloatConv::Float(ty)), _ => None, } } @@ -171,11 +224,19 @@ where FloatConv::Float(f) => { arg.cast_to(f); } - FloatConv::FloatPair(l, r) => { - arg.cast_to(CastTarget::pair(l, r)); + FloatConv::FloatPair { first_ty, second_ty_offset_from_start, second_ty } => { + arg.cast_to(CastTarget::offset_pair( + first_ty, + second_ty_offset_from_start, + second_ty, + )); } - FloatConv::MixedPair(l, r) => { - arg.cast_to(CastTarget::pair(l, r)); + FloatConv::MixedPair { first_ty, second_ty_offset_from_start, second_ty } => { + arg.cast_to(CastTarget::offset_pair( + first_ty, + second_ty_offset_from_start, + second_ty, + )); } } return false; @@ -239,15 +300,27 @@ fn classify_arg<'a, Ty, C>( arg.cast_to(f); return; } - Some(FloatConv::FloatPair(l, r)) if *avail_fprs >= 2 => { + Some(FloatConv::FloatPair { first_ty, second_ty_offset_from_start, second_ty }) + if *avail_fprs >= 2 => + { *avail_fprs -= 2; - arg.cast_to(CastTarget::pair(l, r)); + arg.cast_to(CastTarget::offset_pair( + first_ty, + second_ty_offset_from_start, + second_ty, + )); return; } - Some(FloatConv::MixedPair(l, r)) if *avail_fprs >= 1 && *avail_gprs >= 1 => { + Some(FloatConv::MixedPair { first_ty, second_ty_offset_from_start, second_ty }) + if *avail_fprs >= 1 && *avail_gprs >= 1 => + { *avail_gprs -= 1; *avail_fprs -= 1; - arg.cast_to(CastTarget::pair(l, r)); + arg.cast_to(CastTarget::offset_pair( + first_ty, + second_ty_offset_from_start, + second_ty, + )); return; } _ => (), diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index 7ca0031fc59..186826c08fc 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -5,9 +5,7 @@ use rustc_abi::{ TyAndLayout, }; -use crate::callconv::{ - ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Uniform, -}; +use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Uniform}; use crate::spec::HasTargetSpec; #[derive(Clone, Debug)] @@ -197,16 +195,10 @@ where rest_size = rest_size - Reg::i32().size; } - arg.cast_to(CastTarget { - prefix: data.prefix, - rest: Uniform::new(Reg::i64(), rest_size), - attrs: ArgAttributes { - regular: data.arg_attribute, - arg_ext: ArgExtension::None, - pointee_size: Size::ZERO, - pointee_align: None, - }, - }); + arg.cast_to( + CastTarget::prefixed(data.prefix, Uniform::new(Reg::i64(), rest_size)) + .with_attrs(data.arg_attribute.into()), + ); return; } } diff --git a/compiler/rustc_target/src/callconv/wasm.rs b/compiler/rustc_target/src/callconv/wasm.rs index 881168c98c3..a308f378ee8 100644 --- a/compiler/rustc_target/src/callconv/wasm.rs +++ b/compiler/rustc_target/src/callconv/wasm.rs @@ -59,7 +59,7 @@ where } /// The purpose of this ABI is to match the C ABI (aka clang) exactly. -pub(crate) fn compute_c_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, @@ -75,43 +75,3 @@ where classify_arg(cx, arg); } } - -/// The purpose of this ABI is for matching the WebAssembly standard. This -/// intentionally diverges from the C ABI and is specifically crafted to take -/// advantage of LLVM's support of multiple returns in WebAssembly. -/// -/// This ABI is *bad*! It uses `PassMode::Direct` for `abi::Aggregate` types, which leaks LLVM -/// implementation details into the ABI. It's just hard to fix because ABIs are hard to change. -/// Also see <https://github.com/rust-lang/rust/issues/115666>. -pub(crate) fn compute_wasm_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) { - if !fn_abi.ret.is_ignore() { - classify_ret_wasm_abi(&mut fn_abi.ret); - } - - for arg in fn_abi.args.iter_mut() { - if arg.is_ignore() { - continue; - } - classify_arg_wasm_abi(arg); - } - - fn classify_ret_wasm_abi<Ty>(ret: &mut ArgAbi<'_, Ty>) { - if !ret.layout.is_sized() { - // Not touching this... - return; - } - // FIXME: this is bad! https://github.com/rust-lang/rust/issues/115666 - ret.make_direct_deprecated(); - ret.extend_integer_width_to(32); - } - - fn classify_arg_wasm_abi<Ty>(arg: &mut ArgAbi<'_, Ty>) { - if !arg.layout.is_sized() { - // Not touching this... - return; - } - // FIXME: this is bad! https://github.com/rust-lang/rust/issues/115666 - arg.make_direct_deprecated(); - arg.extend_integer_width_to(32); - } -} diff --git a/compiler/rustc_target/src/callconv/x86_64.rs b/compiler/rustc_target/src/callconv/x86_64.rs index 300b19f62e7..700ee73c8fd 100644 --- a/compiler/rustc_target/src/callconv/x86_64.rs +++ b/compiler/rustc_target/src/callconv/x86_64.rs @@ -7,6 +7,7 @@ use rustc_abi::{ }; use crate::callconv::{ArgAbi, CastTarget, FnAbi}; +use crate::spec::HasTargetSpec; /// Classification of "eightbyte" components. // N.B., the order of the variants is from general to specific, @@ -175,7 +176,7 @@ const MAX_SSE_REGS: usize = 8; // XMM0-7 pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, - C: HasDataLayout, + C: HasDataLayout + HasTargetSpec, { let mut int_regs = MAX_INT_REGS; let mut sse_regs = MAX_SSE_REGS; @@ -236,7 +237,7 @@ where if arg.layout.is_aggregate() { let size = arg.layout.size; arg.cast_to(cast_target(cls, size)); - } else { + } else if is_arg || cx.target_spec().is_like_darwin { arg.extend_integer_width_to(32); } } diff --git a/compiler/rustc_target/src/spec/abi_map.rs b/compiler/rustc_target/src/spec/abi_map.rs index c4978a8e52a..4659bbdb890 100644 --- a/compiler/rustc_target/src/spec/abi_map.rs +++ b/compiler/rustc_target/src/spec/abi_map.rs @@ -71,6 +71,8 @@ impl AbiMap { (ExternAbi::RustCold, _) if self.os == OsKind::Windows => CanonAbi::Rust, (ExternAbi::RustCold, _) => CanonAbi::RustCold, + (ExternAbi::Custom, _) => CanonAbi::Custom, + (ExternAbi::System { .. }, Arch::X86) if os == OsKind::Windows && !has_c_varargs => { CanonAbi::X86(X86Call::Stdcall) } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index c360fe63a00..7a49f004072 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -6,33 +6,36 @@ //! to a new platform, and allows for an unprecedented level of control over how //! the compiler works. //! -//! # Using custom targets +//! # Using targets and target.json //! -//! A target tuple, as passed via `rustc --target=TUPLE`, will first be -//! compared against the list of built-in targets. This is to ease distributing -//! rustc (no need for configuration files) and also to hold these built-in -//! targets as immutable and sacred. If `TUPLE` is not one of the built-in -//! targets, rustc will check if a file named `TUPLE` exists. If it does, it -//! will be loaded as the target configuration. If the file does not exist, -//! rustc will search each directory in the environment variable -//! `RUST_TARGET_PATH` for a file named `TUPLE.json`. The first one found will -//! be loaded. If no file is found in any of those directories, a fatal error -//! will be given. +//! Invoking "rustc --target=${TUPLE}" will result in rustc initiating the [`Target::search`] by +//! - checking if "$TUPLE" is a complete path to a json (ending with ".json") and loading if so +//! - checking builtin targets for "${TUPLE}" +//! - checking directories in "${RUST_TARGET_PATH}" for "${TUPLE}.json" +//! - checking for "${RUSTC_SYSROOT}/lib/rustlib/${TUPLE}/target.json" //! -//! Projects defining their own targets should use -//! `--target=path/to/my-awesome-platform.json` instead of adding to -//! `RUST_TARGET_PATH`. +//! Code will then be compiled using the first discovered target spec. //! //! # Defining a new target //! -//! Targets are defined using [JSON](https://json.org/). The `Target` struct in -//! this module defines the format the JSON file should take, though each -//! underscore in the field names should be replaced with a hyphen (`-`) in the -//! JSON file. Some fields are required in every target specification, such as -//! `llvm-target`, `target-endian`, `target-pointer-width`, `data-layout`, -//! `arch`, and `os`. In general, options passed to rustc with `-C` override -//! the target's settings, though `target-feature` and `link-args` will *add* -//! to the list specified by the target, rather than replace. +//! Targets are defined using a struct which additionally has serialization to and from [JSON]. +//! The `Target` struct in this module loosely corresponds with the format the JSON takes. +//! We usually try to make the fields equivalent but we have given up on a 1:1 correspondence +//! between the JSON and the actual structure itself. +//! +//! Some fields are required in every target spec, and they should be embedded in Target directly. +//! Optional keys are in TargetOptions, but Target derefs to it, for no practical difference. +//! Most notable is the "data-layout" field which specifies Rust's notion of sizes and alignments +//! for several key types, such as f64, pointers, and so on. +//! +//! At one point we felt `-C` options should override the target's settings, like in C compilers, +//! but that was an essentially-unmarked route for making code incorrect and Rust unsound. +//! Confronted with programmers who prefer a compiler with a good UX instead of a lethal weapon, +//! we have almost-entirely recanted that notion, though we hope "target modifiers" will offer +//! a way to have a decent UX yet still extend the necessary compiler controls, without +//! requiring a new target spec for each and every single possible target micro-variant. +//! +//! [JSON]: https://json.org use std::borrow::Cow; use std::collections::BTreeMap; @@ -2233,22 +2236,6 @@ impl HasTargetSpec for Target { } } -/// Which C ABI to use for `wasm32-unknown-unknown`. -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum WasmCAbi { - /// Spec-compliant C ABI. - Spec, - /// Legacy ABI. Which is non-spec-compliant. - Legacy { - /// Indicates whether the `wasm_c_abi` lint should be emitted. - with_lint: bool, - }, -} - -pub trait HasWasmCAbiOpt { - fn wasm_c_abi_opt(&self) -> WasmCAbi; -} - /// x86 (32-bit) abi options. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct X86Abi { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs index dba45776c94..66733d5d4b8 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.llvm_abiname = "elfv2".into(); Target { llvm_target: "powerpc64-unknown-freebsd".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs index 1f67bc7f3c2..ecf68ddff8c 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.llvm_abiname = "elfv1".into(); Target { llvm_target: "powerpc64-unknown-linux-gnu".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 49413d27a45..e205aef8285 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 @@ -12,6 +12,7 @@ pub(crate) fn target() -> Target { base.stack_probes = StackProbeType::Inline; // FIXME(compiler-team#422): musl targets should be dynamically linked by default. base.crt_static_default = true; + base.llvm_abiname = "elfv2".into(); Target { llvm_target: "powerpc64-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs index f5ca54291c6..bcb328020ee 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.llvm_abiname = "elfv2".into(); Target { llvm_target: "powerpc64-unknown-openbsd".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs index 3e4a58f568a..37c888ba514 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.llvm_abiname = "elfv1".into(); Target { llvm_target: "powerpc64-unknown-linux-gnu".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 4640d537e8e..3096c4d14ad 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs @@ -8,6 +8,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.llvm_abiname = "elfv2".into(); Target { llvm_target: "powerpc64le-unknown-freebsd".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs index dd3f660d81e..9e406af53b5 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs @@ -8,6 +8,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.llvm_abiname = "elfv2".into(); Target { llvm_target: "powerpc64le-unknown-linux-gnu".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 9e2bfe2c56f..f145c5b8c14 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 @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.stack_probes = StackProbeType::Inline; // FIXME(compiler-team#422): musl targets should be dynamically linked by default. base.crt_static_default = true; + base.llvm_abiname = "elfv2".into(); Target { llvm_target: "powerpc64le-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 5d1182fce48..a1eac1fba25 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -34,6 +34,9 @@ pub enum Stability { /// particular for features are actually ABI configuration flags (not all targets are as nice as /// RISC-V and have an explicit way to set the ABI separate from target features). Forbidden { reason: &'static str }, + /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be set + /// by target modifier flag. Target modifier flags are tracked to be consistent in linked modules. + TargetModifierOnly { reason: &'static str, flag: &'static str }, } use Stability::*; @@ -49,6 +52,7 @@ impl<CTX> HashStable<CTX> for Stability { Stability::Forbidden { reason } => { reason.hash_stable(hcx, hasher); } + Stability::TargetModifierOnly { .. } => {} } } } @@ -74,16 +78,7 @@ impl Stability { Stability::Unstable(nightly_feature) => Some(nightly_feature), Stability::Stable { .. } => None, Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"), - } - } - - /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`. - /// (It might still be nightly-only even if this returns `true`, so make sure to also check - /// `requires_nightly`.) - pub fn toggle_allowed(&self) -> Result<(), &'static str> { - match self { - Stability::Forbidden { reason } => Err(reason), - _ => Ok(()), + Stability::TargetModifierOnly { .. } => None, } } } @@ -453,6 +448,30 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("prfchw", Unstable(sym::prfchw_target_feature), &[]), ("rdrand", Stable, &[]), ("rdseed", Stable, &[]), + ( + "retpoline-external-thunk", + Stability::TargetModifierOnly { + reason: "use `retpoline-external-thunk` target modifier flag instead", + flag: "retpoline-external-thunk", + }, + &[], + ), + ( + "retpoline-indirect-branches", + Stability::TargetModifierOnly { + reason: "use `retpoline` target modifier flag instead", + flag: "retpoline", + }, + &[], + ), + ( + "retpoline-indirect-calls", + Stability::TargetModifierOnly { + reason: "use `retpoline` target modifier flag instead", + flag: "retpoline", + }, + &[], + ), ("rtm", Unstable(sym::rtm_target_feature), &[]), ("sha", Stable, &["sse2"]), ("sha512", Stable, &["avx2"]), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 275b580d794..39f115ce0cd 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -199,7 +199,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // avoid inundating the user with unnecessary errors, but we now // check upstream for type errors and don't add the obligations to // begin with in those cases. - if self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Sized) { + if matches!( + self.tcx.as_lang_item(trait_pred.def_id()), + Some(LangItem::Sized | LangItem::MetaSized) + ) { match self.tainted_by_errors() { None => { let err = self.emit_inference_failure_err( diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 78f9287b407..d4cc1ceb280 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -13,7 +13,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{Applicability, Diag, E0038, E0276, MultiSpan, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{self as hir, AmbigArg, LangItem}; +use rustc_hir::{self as hir, AmbigArg}; use rustc_infer::traits::solve::Goal; use rustc_infer::traits::{ DynCompatibilityViolation, Obligation, ObligationCause, ObligationCauseCode, @@ -160,17 +160,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) .collect(); - // Ensure `T: Sized` and `T: WF` obligations come last. This lets us display diagnostics - // with more relevant type information and hide redundant E0282 errors. - errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) - if self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) => - { - 1 + // Ensure `T: Sized`, `T: MetaSized`, `T: PointeeSized` and `T: WF` obligations come last. + // This lets us display diagnostics with more relevant type information and hide redundant + // E0282 errors. + errors.sort_by_key(|e| { + let maybe_sizedness_did = match e.obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => Some(pred.def_id()), + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => Some(pred.def_id()), + _ => None, + }; + + match e.obligation.predicate.kind().skip_binder() { + _ if maybe_sizedness_did == self.tcx.lang_items().sized_trait() => 1, + _ if maybe_sizedness_did == self.tcx.lang_items().meta_sized_trait() => 2, + _ if maybe_sizedness_did == self.tcx.lang_items().pointee_sized_trait() => 3, + ty::PredicateKind::Coerce(_) => 4, + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 5, + _ => 0, } - ty::PredicateKind::Coerce(_) => 2, - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3, - _ => 0, }); for (index, error) in errors.iter().enumerate() { @@ -327,19 +334,26 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti let trait_ref = tcx.impl_trait_ref(impl_def_id)?.instantiate_identity(); let mut w = "impl".to_owned(); - let args = ty::GenericArgs::identity_for_item(tcx, impl_def_id); + #[derive(Debug, Default)] + struct SizednessFound { + sized: bool, + meta_sized: bool, + } - // FIXME: Currently only handles ?Sized. - // Needs to support ?Move and ?DynSized when they are implemented. - let mut types_without_default_bounds = FxIndexSet::default(); - let sized_trait = tcx.lang_items().sized_trait(); + let mut types_with_sizedness_bounds = FxIndexMap::<_, SizednessFound>::default(); + + let args = ty::GenericArgs::identity_for_item(tcx, impl_def_id); let arg_names = args.iter().map(|k| k.to_string()).filter(|k| k != "'_").collect::<Vec<_>>(); if !arg_names.is_empty() { - types_without_default_bounds.extend(args.types()); w.push('<'); w.push_str(&arg_names.join(", ")); w.push('>'); + + for ty in args.types() { + // `PointeeSized` params might have no predicates. + types_with_sizedness_bounds.insert(ty, SizednessFound::default()); + } } write!( @@ -351,24 +365,47 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti ) .unwrap(); - // The predicates will contain default bounds like `T: Sized`. We need to - // remove these bounds, and add `T: ?Sized` to any untouched type parameters. let predicates = tcx.predicates_of(impl_def_id).predicates; - let mut pretty_predicates = - Vec::with_capacity(predicates.len() + types_without_default_bounds.len()); + let mut pretty_predicates = Vec::with_capacity(predicates.len()); + + let sized_trait = tcx.lang_items().sized_trait(); + let meta_sized_trait = tcx.lang_items().meta_sized_trait(); for (p, _) in predicates { - if let Some(poly_trait_ref) = p.as_trait_clause() { - if Some(poly_trait_ref.def_id()) == sized_trait { - // FIXME(#120456) - is `swap_remove` correct? - types_without_default_bounds.swap_remove(&poly_trait_ref.self_ty().skip_binder()); + // Accumulate the sizedness bounds for each self ty. + if let Some(trait_clause) = p.as_trait_clause() { + let self_ty = trait_clause.self_ty().skip_binder(); + let sizedness_of = types_with_sizedness_bounds.entry(self_ty).or_default(); + if Some(trait_clause.def_id()) == sized_trait { + sizedness_of.sized = true; + continue; + } else if Some(trait_clause.def_id()) == meta_sized_trait { + sizedness_of.meta_sized = true; continue; } } + pretty_predicates.push(p.to_string()); } - pretty_predicates.extend(types_without_default_bounds.iter().map(|ty| format!("{ty}: ?Sized"))); + for (ty, sizedness) in types_with_sizedness_bounds { + if !tcx.features().sized_hierarchy() { + if sizedness.sized { + // Maybe a default bound, don't write anything. + } else { + pretty_predicates.push(format!("{ty}: ?Sized")); + } + } else { + if sizedness.sized { + // Maybe a default bound, don't write anything. + pretty_predicates.push(format!("{ty}: Sized")); + } else if sizedness.meta_sized { + pretty_predicates.push(format!("{ty}: MetaSized")); + } else { + pretty_predicates.push(format!("{ty}: PointeeSized")); + } + } + } if !pretty_predicates.is_empty() { write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap(); @@ -519,7 +556,7 @@ fn attempt_dyn_to_enum_suggestion( let Some(impl_type) = tcx.type_of(*impl_id).no_bound_vars() else { return None }; // Obviously unsized impl types won't be usable in an enum. - // Note: this doesn't use `Ty::is_trivially_sized` because that function + // Note: this doesn't use `Ty::has_trivial_sizedness` because that function // defaults to assuming that things are *not* sized, whereas we want to // fall back to assuming that things may be sized. match impl_type.kind() { 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 37968386e9a..89dab90dc68 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 @@ -810,7 +810,8 @@ impl<'tcx> OnUnimplementedFormatString { let mut result = Ok(()); - match FormatString::parse(self.symbol, self.span, &ctx) { + let snippet = tcx.sess.source_map().span_to_snippet(self.span).ok(); + match FormatString::parse(self.symbol, snippet, self.span, &ctx) { // Warnings about format specifiers, deprecated parameters, wrong parameters etc. // In other words we'd like to let the author know, but we can still try to format the string later Ok(FormatString { warnings, .. }) => { @@ -848,34 +849,27 @@ impl<'tcx> OnUnimplementedFormatString { } } } - // Errors from the underlying `rustc_parse_format::Parser` - Err(errors) => { + // Error from the underlying `rustc_parse_format::Parser` + Err(e) => { // we cannot return errors from processing the format string as hard error here // as the diagnostic namespace guarantees that malformed input cannot cause an error // // if we encounter any error while processing we nevertheless want to show it as warning // so that users are aware that something is not correct - for e in errors { - if self.is_diagnostic_namespace_variant { - if let Some(trait_def_id) = trait_def_id.as_local() { - tcx.emit_node_span_lint( - UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(trait_def_id), - self.span, - WrappedParserError { description: e.description, label: e.label }, - ); - } - } else { - let reported = struct_span_code_err!( - tcx.dcx(), + if self.is_diagnostic_namespace_variant { + if let Some(trait_def_id) = trait_def_id.as_local() { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(trait_def_id), self.span, - E0231, - "{}", - e.description, - ) - .emit(); - result = Err(reported); + WrappedParserError { description: e.description, label: e.label }, + ); } + } else { + let reported = + struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,) + .emit(); + result = Err(reported); } } } @@ -896,7 +890,8 @@ impl<'tcx> OnUnimplementedFormatString { Ctx::RustcOnUnimplemented { tcx, trait_def_id } }; - if let Ok(s) = FormatString::parse(self.symbol, self.span, &ctx) { + // No point passing a snippet here, we already did that in `verify` + if let Ok(s) = FormatString::parse(self.symbol, None, self.span, &ctx) { s.format(args) } else { // we cannot return errors from processing the format string as hard error here diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs index e8ea9f2d23e..171d05230d4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs @@ -198,7 +198,7 @@ enum LitOrArg { impl FilterFormatString { fn parse(input: Symbol) -> Self { - let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Format) + let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic) .map(|p| match p { Piece::Lit(s) => LitOrArg::Lit(s.to_owned()), // We just ignore formatspecs here diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs index 7c1dfc1728f..3e8b906fa93 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs @@ -5,12 +5,11 @@ use errors::*; use rustc_middle::ty::print::TraitRefPrintSugared; use rustc_middle::ty::{GenericParamDefKind, TyCtxt}; use rustc_parse_format::{ - Alignment, Argument, Count, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, - Position, + Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position, }; use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES; use rustc_span::def_id::DefId; -use rustc_span::{BytePos, Pos, Span, Symbol, kw, sym}; +use rustc_span::{InnerSpan, Span, Symbol, kw, sym}; /// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces", /// either as string pieces or dynamic arguments. @@ -160,32 +159,32 @@ impl FormatString { pub fn parse<'tcx>( input: Symbol, + snippet: Option<String>, span: Span, ctx: &Ctx<'tcx>, - ) -> Result<Self, Vec<ParseError>> { + ) -> Result<Self, ParseError> { let s = input.as_str(); - let mut parser = Parser::new(s, None, None, false, ParseMode::Format); - let mut pieces = Vec::new(); + let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic); + let pieces: Vec<_> = parser.by_ref().collect(); + + if let Some(err) = parser.errors.into_iter().next() { + return Err(err); + } let mut warnings = Vec::new(); - for piece in &mut parser { - match piece { - RpfPiece::Lit(lit) => { - pieces.push(Piece::Lit(lit.into())); - } + let pieces = pieces + .into_iter() + .map(|piece| match piece { + RpfPiece::Lit(lit) => Piece::Lit(lit.into()), RpfPiece::NextArgument(arg) => { - warn_on_format_spec(arg.format.clone(), &mut warnings, span); - let arg = parse_arg(&arg, ctx, &mut warnings, span); - pieces.push(Piece::Arg(arg)); + warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal); + let arg = parse_arg(&arg, ctx, &mut warnings, span, parser.is_source_literal); + Piece::Arg(arg) } - } - } + }) + .collect(); - if parser.errors.is_empty() { - Ok(FormatString { input, pieces, span, warnings }) - } else { - Err(parser.errors) - } + Ok(FormatString { input, pieces, span, warnings }) } pub fn format(&self, args: &FormatArgs<'_>) -> String { @@ -229,11 +228,12 @@ fn parse_arg<'tcx>( ctx: &Ctx<'tcx>, warnings: &mut Vec<FormatWarning>, input_span: Span, + is_source_literal: bool, ) -> FormatArg { let (Ctx::RustcOnUnimplemented { tcx, trait_def_id } | Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }) = ctx; - let span = slice_span(input_span, arg.position_span.clone()); + let span = slice_span(input_span, arg.position_span.clone(), is_source_literal); match arg.position { // Something like "hello {name}" @@ -283,39 +283,24 @@ fn parse_arg<'tcx>( /// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything /// with specifiers, so emit a warning if they are used. -fn warn_on_format_spec(spec: FormatSpec<'_>, warnings: &mut Vec<FormatWarning>, input_span: Span) { - if !matches!( - spec, - FormatSpec { - fill: None, - fill_span: None, - align: Alignment::AlignUnknown, - sign: None, - alternate: false, - zero_pad: false, - debug_hex: None, - precision: Count::CountImplied, - precision_span: None, - width: Count::CountImplied, - width_span: None, - ty: _, - ty_span: _, - }, - ) { - let span = spec.ty_span.map(|inner| slice_span(input_span, inner)).unwrap_or(input_span); +fn warn_on_format_spec( + spec: &FormatSpec<'_>, + warnings: &mut Vec<FormatWarning>, + input_span: Span, + is_source_literal: bool, +) { + if spec.ty != "" { + let span = spec + .ty_span + .as_ref() + .map(|inner| slice_span(input_span, inner.clone(), is_source_literal)) + .unwrap_or(input_span); warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() }) } } -fn slice_span(input: Span, range: Range<usize>) -> Span { - let span = input.data(); - - Span::new( - span.lo + BytePos::from_usize(range.start), - span.lo + BytePos::from_usize(range.end), - span.ctxt, - span.parent, - ) +fn slice_span(input: Span, Range { start, end }: Range<usize>, is_source_literal: bool) -> Span { + if is_source_literal { input.from_inner(InnerSpan { start, end }) } else { input } } pub mod errors { 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 68bd9440538..ee5a5b247ce 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2995,9 +2995,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if local { err.note("all local variables must have a statically known size"); } - if !tcx.features().unsized_locals() { - err.help("unsized locals are gated as an unstable feature"); - } } ObligationCauseCode::SizedArgumentType(hir_id) => { let mut ty = None; diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 69a0c0809b5..b247c2c2968 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -12,7 +12,7 @@ use rustc_infer::traits::solve::Goal; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::Certainty; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitableExt as _, TypingMode, + self, SizedTraitKind, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitableExt as _, TypingMode, }; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; @@ -79,7 +79,14 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< Some(LangItem::Sized) if self .resolve_vars_if_possible(trait_pred.self_ty().skip_binder()) - .is_trivially_sized(self.0.tcx) => + .has_trivial_sizedness(self.0.tcx, SizedTraitKind::Sized) => + { + return Some(Certainty::Yes); + } + Some(LangItem::MetaSized) + if self + .resolve_vars_if_possible(trait_pred.self_ty().skip_binder()) + .has_trivial_sizedness(self.0.tcx, SizedTraitKind::MetaSized) => { return Some(Certainty::Yes); } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 93c7dae9c5b..81893cdcc7e 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -462,6 +462,7 @@ fn impl_intersection_has_negative_obligation( let param_env = infcx.resolve_vars_if_possible(param_env); util::elaborate(tcx, tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args)) + .elaborate_sized() .any(|(clause, _)| try_prove_negated_where_clause(infcx, clause, param_env)) } diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 220a847cc23..ee30956295a 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -7,15 +7,15 @@ use std::ops::ControlFlow; use rustc_errors::FatalError; -use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, LangItem}; use rustc_middle::query::Providers; use rustc_middle::ty::{ self, EarlyBinder, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast, elaborate, }; -use rustc_span::Span; +use rustc_span::{DUMMY_SP, Span}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -414,8 +414,8 @@ fn virtual_call_violations_for_method<'tcx>( let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0)); - // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on. - // However, this is already considered dyn compatible. We allow it as a special case here. + // `self: Self` can't be dispatched on. + // However, this is considered dyn compatible. We allow it as a special case here. // FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_dispatchable` allows // `Receiver: Unsize<Receiver[Self => dyn Trait]>`. if receiver_ty != tcx.types.self_param { @@ -543,11 +543,11 @@ fn receiver_for_self_ty<'tcx>( /// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result in /// a new check that `Trait` is dyn-compatible, creating a cycle. /// Instead, we emulate a placeholder by introducing a new type parameter `U` such that -/// `Self: Unsize<U>` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`. +/// `Self: Unsize<U>` and `U: Trait + MetaSized`, and use `U` in place of `dyn Trait`. /// /// Written as a chalk-style query: /// ```ignore (not-rust) -/// forall (U: Trait + ?Sized) { +/// forall (U: Trait + MetaSized) { /// if (Self: Unsize<U>) { /// Receiver: DispatchFromDyn<Receiver[Self => U]> /// } @@ -567,9 +567,10 @@ fn receiver_is_dispatchable<'tcx>( ) -> bool { debug!("receiver_is_dispatchable: method = {:?}, receiver_ty = {:?}", method, receiver_ty); - let traits = (tcx.lang_items().unsize_trait(), tcx.lang_items().dispatch_from_dyn_trait()); - let (Some(unsize_did), Some(dispatch_from_dyn_did)) = traits else { - debug!("receiver_is_dispatchable: Missing Unsize or DispatchFromDyn traits"); + let (Some(unsize_did), Some(dispatch_from_dyn_did)) = + (tcx.lang_items().unsize_trait(), tcx.lang_items().dispatch_from_dyn_trait()) + else { + debug!("receiver_is_dispatchable: Missing `Unsize` or `DispatchFromDyn` traits"); return false; }; @@ -583,7 +584,7 @@ fn receiver_is_dispatchable<'tcx>( receiver_for_self_ty(tcx, receiver_ty, unsized_self_ty, method.def_id); // create a modified param env, with `Self: Unsize<U>` and `U: Trait` (and all of - // its supertraits) added to caller bounds. `U: ?Sized` is already implied here. + // its supertraits) added to caller bounds. `U: MetaSized` is already implied here. let param_env = { // N.B. We generally want to emulate the construction of the `unnormalized_param_env` // in the param-env query here. The fact that we don't just start with the clauses @@ -612,6 +613,12 @@ fn receiver_is_dispatchable<'tcx>( let trait_predicate = ty::TraitRef::new_from_args(tcx, trait_def_id, args); predicates.push(trait_predicate.upcast(tcx)); + let meta_sized_predicate = { + let meta_sized_did = tcx.require_lang_item(LangItem::MetaSized, DUMMY_SP); + ty::TraitRef::new(tcx, meta_sized_did, [unsized_self_ty]).upcast(tcx) + }; + predicates.push(meta_sized_predicate); + normalize_param_env_or_error( tcx, ty::ParamEnv::new(tcx.mk_clauses(&predicates)), diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index eb34cb10c68..a54eb80fedc 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -9,7 +9,7 @@ use rustc_macros::extension; pub use rustc_middle::traits::query::NormalizationResult; use rustc_middle::ty::{ self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, - TypeVisitableExt, TypeVisitor, TypingMode, + TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, }; use rustc_span::DUMMY_SP; use tracing::{debug, info, instrument}; @@ -127,7 +127,7 @@ struct MaxEscapingBoundVarVisitor { } impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxEscapingBoundVarVisitor { - fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) { + fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) { self.outer_index.shift_in(1); t.super_visit_with(self); self.outer_index.shift_out(1); 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 97ecf9702e6..81ce58a93fa 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -14,7 +14,7 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind}; use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; -use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode, elaborate}; +use rustc_middle::ty::{self, SizedTraitKind, Ty, TypeVisitableExt, TypingMode, elaborate}; use rustc_middle::{bug, span_bug}; use tracing::{debug, instrument, trace}; @@ -87,7 +87,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates.vec.push(BuiltinCandidate { has_nested: false }); } Some(LangItem::Sized) => { - self.assemble_builtin_sized_candidate(obligation, &mut candidates); + self.assemble_builtin_sized_candidate( + obligation, + &mut candidates, + SizedTraitKind::Sized, + ); + } + Some(LangItem::MetaSized) => { + self.assemble_builtin_sized_candidate( + obligation, + &mut candidates, + SizedTraitKind::MetaSized, + ); + } + Some(LangItem::PointeeSized) => { + bug!("`PointeeSized` is removed during lowering"); } Some(LangItem::Unsize) => { self.assemble_candidates_for_unsizing(obligation, &mut candidates); @@ -201,6 +215,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } selcx.infcx.probe(|_| { + let bound = util::lazily_elaborate_sizedness_candidate( + selcx.infcx, + obligation, + bound, + ); + // We checked the polarity already match selcx.match_normalize_trait_ref( obligation, @@ -245,14 +265,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .caller_bounds() .iter() .filter_map(|p| p.as_trait_clause()) - // Micro-optimization: filter out predicates relating to different traits. - .filter(|p| p.def_id() == stack.obligation.predicate.def_id()) + // Micro-optimization: filter out predicates with different polarities. .filter(|p| p.polarity() == stack.obligation.predicate.polarity()); let drcx = DeepRejectCtxt::relate_rigid_rigid(self.tcx()); let obligation_args = stack.obligation.predicate.skip_binder().trait_ref.args; // Keep only those bounds which may apply, and propagate overflow if it occurs. for bound in bounds { + let bound = + util::lazily_elaborate_sizedness_candidate(self.infcx, stack.obligation, bound); + + // Micro-optimization: filter out predicates relating to different traits. + if bound.def_id() != stack.obligation.predicate.def_id() { + continue; + } + let bound_trait_ref = bound.map_bound(|t| t.trait_ref); if !drcx.args_may_unify(obligation_args, bound_trait_ref.skip_binder().args) { continue; @@ -1086,15 +1113,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - /// Assembles the trait which are built-in to the language itself: - /// `Copy`, `Clone` and `Sized`. + /// Assembles the `Sized` and `MetaSized` traits which are built-in to the language itself. #[instrument(level = "debug", skip(self, candidates))] fn assemble_builtin_sized_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, + sizedness: SizedTraitKind, ) { - match self.sized_conditions(obligation) { + match self.sizedness_conditions(obligation, sizedness) { BuiltinImplConditions::Where(nested) => { candidates .vec diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 786afd7cf48..80f71c78993 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -14,7 +14,9 @@ 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, GenericArgsRef, Region, Ty, TyCtxt, Upcast, elaborate}; +use rustc_middle::ty::{ + self, GenericArgsRef, Region, SizedTraitKind, Ty, TyCtxt, Upcast, elaborate, +}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; use thin_vec::thin_vec; @@ -164,10 +166,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) .break_value() .expect("expected to index into clause that exists"); - let candidate = candidate_predicate + let candidate_predicate = candidate_predicate .as_trait_clause() - .expect("projection candidate is not a trait predicate") - .map_bound(|t| t.trait_ref); + .expect("projection candidate is not a trait predicate"); + let candidate_predicate = + util::lazily_elaborate_sizedness_candidate(self.infcx, obligation, candidate_predicate); + + let candidate = candidate_predicate.map_bound(|t| t.trait_ref); let candidate = self.infcx.instantiate_binder_with_fresh_vars( obligation.cause.span, @@ -224,6 +229,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> PredicateObligations<'tcx> { debug!(?obligation, ?param, "confirm_param_candidate"); + let param = util::lazily_elaborate_sizedness_candidate( + self.infcx, + obligation, + param.upcast(self.infcx.tcx), + ) + .map_bound(|p| p.trait_ref); + // During evaluation, we already checked that this // where-clause trait-ref could be unified with the obligation // trait-ref. Repeat that unification now without any @@ -251,7 +263,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let obligations = if has_nested { let trait_def = obligation.predicate.def_id(); let conditions = match tcx.as_lang_item(trait_def) { - Some(LangItem::Sized) => self.sized_conditions(obligation), + Some(LangItem::Sized) => { + self.sizedness_conditions(obligation, SizedTraitKind::Sized) + } + Some(LangItem::MetaSized) => { + self.sizedness_conditions(obligation, SizedTraitKind::MetaSized) + } + Some(LangItem::PointeeSized) => { + bug!("`PointeeSized` is removing during lowering"); + } Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(obligation), Some(LangItem::FusedIterator) => self.fused_iterator_conditions(obligation), other => bug!("unexpected builtin trait {trait_def:?} ({other:?})"), diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 3a2f9e8ca17..9c0ccb26e53 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -27,8 +27,8 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths}; use rustc_middle::ty::{ - self, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable, - TypeVisitableExt, TypingMode, Upcast, elaborate, + self, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate, SizedTraitKind, Ty, TyCtxt, + TypeFoldable, TypeVisitableExt, TypingMode, Upcast, elaborate, }; use rustc_span::{Symbol, sym}; use tracing::{debug, instrument, trace}; @@ -1919,12 +1919,23 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // impl `impl<T: ?Sized> Any for T { .. }`. This really shouldn't exist but is // necessary due to #57893. We again arbitrarily prefer the applicable candidate // with the lowest index. + // + // We do not want to use these impls to guide inference in case a user-written impl + // may also apply. let object_bound = candidates .iter() .filter_map(|c| if let ObjectCandidate(i) = c.candidate { Some(i) } else { None }) .try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) }); match object_bound { - Some(Some(index)) => return Some(ObjectCandidate(index)), + Some(Some(index)) => { + return if has_non_region_infer + && candidates.iter().any(|c| matches!(c.candidate, ImplCandidate(_))) + { + None + } else { + Some(ObjectCandidate(index)) + }; + } Some(None) => {} None => return None, } @@ -2083,9 +2094,10 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } impl<'tcx> SelectionContext<'_, 'tcx> { - fn sized_conditions( + fn sizedness_conditions( &mut self, obligation: &PolyTraitObligation<'tcx>, + sizedness: SizedTraitKind, ) -> BuiltinImplConditions<'tcx> { use self::BuiltinImplConditions::{Ambiguous, None, Where}; @@ -2115,7 +2127,12 @@ impl<'tcx> SelectionContext<'_, 'tcx> { Where(ty::Binder::dummy(Vec::new())) } - ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => None, + ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness { + SizedTraitKind::Sized => None, + SizedTraitKind::MetaSized => Where(ty::Binder::dummy(Vec::new())), + }, + + ty::Foreign(..) => None, ty::Tuple(tys) => Where( obligation.predicate.rebind(tys.last().map_or_else(Vec::new, |&last| vec![last])), @@ -2124,11 +2141,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ty::Pat(ty, _) => Where(obligation.predicate.rebind(vec![*ty])), ty::Adt(def, args) => { - if let Some(sized_crit) = def.sized_constraint(self.tcx()) { + if let Some(crit) = def.sizedness_constraint(self.tcx(), sizedness) { // (*) binder moved here - Where( - obligation.predicate.rebind(vec![sized_crit.instantiate(self.tcx(), args)]), - ) + Where(obligation.predicate.rebind(vec![crit.instantiate(self.tcx(), args)])) } else { Where(ty::Binder::dummy(Vec::new())) } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 035fd38c48a..a05bae53566 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -1,14 +1,18 @@ -use std::collections::{BTreeMap, VecDeque}; +use std::collections::VecDeque; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir::LangItem; use rustc_hir::def_id::DefId; use rustc_infer::infer::InferCtxt; +use rustc_infer::traits::PolyTraitObligation; pub use rustc_infer::traits::util::*; use rustc_middle::bug; +use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + self, PolyTraitPredicate, SizedTraitKind, TraitPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, + TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; +pub use rustc_next_trait_solver::placeholder::BoundVarReplacer; use rustc_span::Span; use smallvec::{SmallVec, smallvec}; use tracing::debug; @@ -212,158 +216,12 @@ pub fn with_replaced_escaping_bound_vars< } } -pub struct BoundVarReplacer<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, - // These three maps track the bound variable that were replaced by placeholders. It might be - // nice to remove these since we already have the `kind` in the placeholder; we really just need - // the `var` (but we *could* bring that into scope if we were to track them as we pass them). - mapped_regions: FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion>, - mapped_types: FxIndexMap<ty::PlaceholderType, ty::BoundTy>, - mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, - // The current depth relative to *this* folding, *not* the entire normalization. In other words, - // the depth of binders we've passed here. - current_index: ty::DebruijnIndex, - // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy: - // we don't actually create a universe until we see a bound var we have to replace. - universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>, -} - -impl<'a, 'tcx> BoundVarReplacer<'a, 'tcx> { - /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that - /// use a binding level above `universe_indices.len()`, we fail. - pub fn replace_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>( - infcx: &'a InferCtxt<'tcx>, - universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>, - value: T, - ) -> ( - T, - FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion>, - FxIndexMap<ty::PlaceholderType, ty::BoundTy>, - BTreeMap<ty::PlaceholderConst, ty::BoundVar>, - ) { - let mapped_regions: FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion> = - FxIndexMap::default(); - let mapped_types: FxIndexMap<ty::PlaceholderType, ty::BoundTy> = FxIndexMap::default(); - let mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar> = BTreeMap::new(); - - let mut replacer = BoundVarReplacer { - infcx, - mapped_regions, - mapped_types, - mapped_consts, - current_index: ty::INNERMOST, - universe_indices, - }; - - let value = value.fold_with(&mut replacer); - - (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts) - } - - fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex { - let infcx = self.infcx; - let index = - self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1; - let universe = self.universe_indices[index].unwrap_or_else(|| { - for i in self.universe_indices.iter_mut().take(index + 1) { - *i = i.or_else(|| Some(infcx.create_next_universe())) - } - self.universe_indices[index].unwrap() - }); - universe - } -} - -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.infcx.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 - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match r.kind() { - ty::ReBound(debruijn, _) - if debruijn.as_usize() - >= self.current_index.as_usize() + self.universe_indices.len() => - { - bug!( - "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}", - self.universe_indices - ); - } - ty::ReBound(debruijn, br) if debruijn >= self.current_index => { - let universe = self.universe_for(debruijn); - let p = ty::PlaceholderRegion { universe, bound: br }; - self.mapped_regions.insert(p, br); - ty::Region::new_placeholder(self.infcx.tcx, p) - } - _ => r, - } - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match *t.kind() { - ty::Bound(debruijn, _) - if debruijn.as_usize() + 1 - > self.current_index.as_usize() + self.universe_indices.len() => - { - bug!( - "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}", - self.universe_indices - ); - } - ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { - let universe = self.universe_for(debruijn); - let p = ty::PlaceholderType { universe, bound: bound_ty }; - self.mapped_types.insert(p, bound_ty); - Ty::new_placeholder(self.infcx.tcx, p) - } - _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), - _ => t, - } - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - match ct.kind() { - ty::ConstKind::Bound(debruijn, _) - if debruijn.as_usize() + 1 - > self.current_index.as_usize() + self.universe_indices.len() => - { - bug!( - "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}", - self.universe_indices - ); - } - ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => { - let universe = self.universe_for(debruijn); - let p = ty::PlaceholderConst { universe, bound: bound_const }; - self.mapped_consts.insert(p, bound_const); - ty::Const::new_placeholder(self.infcx.tcx, p) - } - _ => ct.super_fold_with(self), - } - } - - fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { - if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } - } -} - /// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. pub struct PlaceholderReplacer<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, mapped_regions: FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion>, mapped_types: FxIndexMap<ty::PlaceholderType, ty::BoundTy>, - mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, + mapped_consts: FxIndexMap<ty::PlaceholderConst, ty::BoundVar>, universe_indices: &'a [Option<ty::UniverseIndex>], current_index: ty::DebruijnIndex, } @@ -373,7 +231,7 @@ impl<'a, 'tcx> PlaceholderReplacer<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, mapped_regions: FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion>, mapped_types: FxIndexMap<ty::PlaceholderType, ty::BoundTy>, - mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, + mapped_consts: FxIndexMap<ty::PlaceholderConst, ty::BoundVar>, universe_indices: &'a [Option<ty::UniverseIndex>], value: T, ) -> T { @@ -507,15 +365,19 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { } pub fn sizedness_fast_path<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>) -> bool { - // Proving `Sized` very often on "obviously sized" types like `&T`, accounts for about 60% - // percentage of the predicates we have to prove. No need to canonicalize and all that for - // such cases. + // Proving `Sized`/`MetaSized`, very often on "obviously sized" types like + // `&T`, accounts for about 60% percentage of the predicates we have to prove. No need to + // canonicalize and all that for such cases. if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) = predicate.kind().skip_binder() { - if tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) - && trait_ref.self_ty().is_trivially_sized(tcx) - { + let sizedness = match tcx.as_lang_item(trait_ref.def_id()) { + Some(LangItem::Sized) => SizedTraitKind::Sized, + Some(LangItem::MetaSized) => SizedTraitKind::MetaSized, + _ => return false, + }; + + if trait_ref.self_ty().has_trivial_sizedness(tcx, sizedness) { debug!("fast path -- trivial sizedness"); return true; } @@ -523,3 +385,39 @@ pub fn sizedness_fast_path<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc false } + +/// To improve performance, sizedness traits are not elaborated and so special-casing is required +/// in the trait solver to find a `Sized` candidate for a `MetaSized` obligation. Returns the +/// predicate to used in the candidate for such a `obligation`, given a `candidate`. +pub(crate) fn lazily_elaborate_sizedness_candidate<'tcx>( + infcx: &InferCtxt<'tcx>, + obligation: &PolyTraitObligation<'tcx>, + candidate: PolyTraitPredicate<'tcx>, +) -> PolyTraitPredicate<'tcx> { + if !infcx.tcx.is_lang_item(obligation.predicate.def_id(), LangItem::MetaSized) + || !infcx.tcx.is_lang_item(candidate.def_id(), LangItem::Sized) + { + return candidate; + } + + if obligation.predicate.polarity() != candidate.polarity() { + return candidate; + } + + let drcx = DeepRejectCtxt::relate_rigid_rigid(infcx.tcx); + if !drcx.args_may_unify( + obligation.predicate.skip_binder().trait_ref.args, + candidate.skip_binder().trait_ref.args, + ) { + return candidate; + } + + candidate.map_bound(|c| TraitPredicate { + trait_ref: TraitRef::new_from_args( + infcx.tcx, + obligation.predicate.def_id(), + c.trait_ref.args, + ), + polarity: c.polarity, + }) +} diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 416865e861e..d4e6a23f0eb 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -567,6 +567,14 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { def_id: DefId, args: GenericArgsRef<'tcx>, ) -> PredicateObligations<'tcx> { + // PERF: `Sized`'s predicates include `MetaSized`, but both are compiler implemented marker + // traits, so `MetaSized` will always be WF if `Sized` is WF and vice-versa. Determining + // the nominal obligations of `Sized` would in-effect just elaborate `MetaSized` and make + // the compiler do a bunch of work needlessly. + if self.tcx().is_lang_item(def_id, LangItem::Sized) { + return Default::default(); + } + let predicates = self.tcx().predicates_of(def_id); let mut origins = vec![def_id; predicates.predicates.len()]; let mut head = predicates; diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index bb5187e4f5c..f0ff50318ab 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -403,28 +403,18 @@ fn fn_abi_sanity_check<'tcx>( // For an unsized type we'd only pass the sized prefix, so there is no universe // in which we ever want to allow this. assert!(sized, "`PassMode::Direct` for unsized type in ABI: {:#?}", fn_abi); + // 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 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 unadjusted ABI also uses Direct for all args and is ill-specified, + // The unadjusted ABI however uses Direct for all args. It is ill-specified, // but unfortunately we need it for calling certain LLVM intrinsics. - - match spec_abi { - ExternAbi::Unadjusted => {} - ExternAbi::C { unwind: _ } - if matches!(&*tcx.sess.target.arch, "wasm32" | "wasm64") => {} - _ => { - panic!( - "`PassMode::Direct` for aggregates only allowed for \"unadjusted\" functions and on wasm\n\ - Problematic type: {:#?}", - arg.layout, - ); - } - } + assert!( + matches!(spec_abi, ExternAbi::Unadjusted), + "`PassMode::Direct` for aggregates only allowed for \"unadjusted\"\n\ + Problematic type: {:#?}", + arg.layout, + ); } } } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 79ac622df32..d996ee2b60a 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -1,13 +1,12 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_hir::LangItem; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, + self, SizedTraitKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, fold_regions, }; use rustc_span::DUMMY_SP; @@ -15,10 +14,16 @@ use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_trait_selection::traits; use tracing::instrument; +/// If `ty` implements the given `sizedness` trait, returns `None`. Otherwise, returns the type +/// that must implement the given `sizedness` for `ty` to implement it. #[instrument(level = "debug", skip(tcx), ret)] -fn sized_constraint_for_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { +fn sizedness_constraint_for_ty<'tcx>( + tcx: TyCtxt<'tcx>, + sizedness: SizedTraitKind, + ty: Ty<'tcx>, +) -> Option<Ty<'tcx>> { match ty.kind() { - // these are always sized + // Always `Sized` or `MetaSized` ty::Bool | ty::Char | ty::Int(..) @@ -36,31 +41,40 @@ fn sized_constraint_for_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<' | ty::Never | ty::Dynamic(_, _, ty::DynStar) => None, - // these are never sized - ty::Str | ty::Slice(..) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => Some(ty), - - ty::Pat(ty, _) => sized_constraint_for_ty(tcx, *ty), - - ty::Tuple(tys) => tys.last().and_then(|&ty| sized_constraint_for_ty(tcx, ty)), - - // recursive case - ty::Adt(adt, args) => adt.sized_constraint(tcx).and_then(|intermediate| { - let ty = intermediate.instantiate(tcx, args); - sized_constraint_for_ty(tcx, ty) - }), + ty::Str | ty::Slice(..) | ty::Dynamic(_, _, ty::Dyn) => match sizedness { + // Never `Sized` + SizedTraitKind::Sized => Some(ty), + // Always `MetaSized` + SizedTraitKind::MetaSized => None, + }, - // these can be sized or unsized. + // Maybe `Sized` or `MetaSized` ty::Param(..) | ty::Alias(..) | ty::Error(_) => Some(ty), // We cannot instantiate the binder, so just return the *original* type back, // but only if the inner type has a sized constraint. Thus we skip the binder, // but don't actually use the result from `sized_constraint_for_ty`. ty::UnsafeBinder(inner_ty) => { - sized_constraint_for_ty(tcx, inner_ty.skip_binder()).map(|_| ty) + sizedness_constraint_for_ty(tcx, sizedness, inner_ty.skip_binder()).map(|_| ty) + } + + // Never `MetaSized` or `Sized` + ty::Foreign(..) => Some(ty), + + // Recursive cases + ty::Pat(ty, _) => sizedness_constraint_for_ty(tcx, sizedness, *ty), + + ty::Tuple(tys) => { + tys.last().and_then(|&ty| sizedness_constraint_for_ty(tcx, sizedness, ty)) } + ty::Adt(adt, args) => adt.sizedness_constraint(tcx, sizedness).and_then(|intermediate| { + let ty = intermediate.instantiate(tcx, args); + sizedness_constraint_for_ty(tcx, sizedness, ty) + }), + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => { - bug!("unexpected type `{ty:?}` in sized_constraint_for_ty") + bug!("unexpected type `{ty:?}` in `sizedness_constraint_for_ty`") } } } @@ -76,15 +90,22 @@ fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness { } } -/// Calculates the `Sized` constraint. +/// Returns the type of the last field of a struct ("the constraint") which must implement the +/// `sizedness` trait for the whole ADT to be considered to implement that `sizedness` trait. +/// `def_id` is assumed to be the `AdtDef` of a struct and will panic otherwise. +/// +/// For `Sized`, there are only a few options for the types in the constraint: +/// - an meta-sized type (str, slices, trait objects, etc) +/// - an pointee-sized type (extern types) +/// - a type parameter or projection whose sizedness can't be known /// -/// In fact, there are only a few options for the types in the constraint: -/// - an obviously-unsized type +/// For `MetaSized`, there are only a few options for the types in the constraint: +/// - an pointee-sized type (extern types) /// - a type parameter or projection whose sizedness can't be known #[instrument(level = "debug", skip(tcx), ret)] -fn adt_sized_constraint<'tcx>( +fn adt_sizedness_constraint<'tcx>( tcx: TyCtxt<'tcx>, - def_id: DefId, + (def_id, sizedness): (DefId, SizedTraitKind), ) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> { if let Some(def_id) = def_id.as_local() { if let ty::Representability::Infinite(_) = tcx.representability(def_id) { @@ -94,21 +115,21 @@ fn adt_sized_constraint<'tcx>( let def = tcx.adt_def(def_id); if !def.is_struct() { - bug!("`adt_sized_constraint` called on non-struct type: {def:?}"); + bug!("`adt_sizedness_constraint` called on non-struct type: {def:?}"); } let tail_def = def.non_enum_variant().tail_opt()?; let tail_ty = tcx.type_of(tail_def.did).instantiate_identity(); - let constraint_ty = sized_constraint_for_ty(tcx, tail_ty)?; + let constraint_ty = sizedness_constraint_for_ty(tcx, sizedness, tail_ty)?; - // perf hack: if there is a `constraint_ty: Sized` bound, then we know + // perf hack: if there is a `constraint_ty: {Meta,}Sized` bound, then we know // that the type is sized and do not need to check it on the impl. - let sized_trait_def_id = tcx.require_lang_item(LangItem::Sized, DUMMY_SP); + let sizedness_trait_def_id = sizedness.require_lang_item(tcx); let predicates = tcx.predicates_of(def.did()).predicates; if predicates.iter().any(|(p, _)| { p.as_trait_clause().is_some_and(|trait_pred| { - trait_pred.def_id() == sized_trait_def_id + trait_pred.def_id() == sizedness_trait_def_id && trait_pred.self_ty().skip_binder() == constraint_ty }) }) { @@ -186,7 +207,7 @@ struct ImplTraitInTraitFinder<'a, 'tcx> { } impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> { - fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, binder: &ty::Binder<'tcx, T>) { + fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, binder: &ty::Binder<'tcx, T>) { self.depth.shift_in(1); binder.super_visit_with(self); self.depth.shift_out(1); @@ -370,7 +391,7 @@ fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { asyncness, - adt_sized_constraint, + adt_sizedness_constraint, param_env, typing_env_normalized_for_post_analysis, defaultness, diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index 83d3d78298e..4bd7bfe79be 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -9,7 +9,7 @@ bitflags = "2.4.1" derive-where = "1.2.7" ena = "0.14.3" indexmap = "2.0.0" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false } rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 55c0a3bba9f..927a2ce84ea 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -128,7 +128,7 @@ impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Binder<I, T> { } } -impl<I: Interner, T: TypeFoldable<I>> TypeVisitable<I> for Binder<I, T> { +impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Binder<I, T> { fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result { visitor.visit_binder(self) } @@ -147,7 +147,7 @@ impl<I: Interner, T: TypeFoldable<I>> TypeSuperFoldable<I> for Binder<I, T> { } } -impl<I: Interner, T: TypeFoldable<I>> TypeSuperVisitable<I> for Binder<I, T> { +impl<I: Interner, T: TypeVisitable<I>> TypeSuperVisitable<I> for Binder<I, T> { fn super_visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result { self.as_ref().skip_binder().visit_with(visitor) } @@ -292,7 +292,7 @@ impl<I: Interner> ValidateBoundVars<I> { impl<I: Interner> TypeVisitor<I> for ValidateBoundVars<I> { type Result = ControlFlow<()>; - fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &Binder<I, T>) -> Self::Result { + fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &Binder<I, T>) -> Self::Result { self.binder_index.shift_in(1); let result = t.super_visit_with(self); self.binder_index.shift_out(1); diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index b11bcff1d8b..852949d707b 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -4,6 +4,7 @@ use smallvec::smallvec; use crate::data_structures::HashSet; use crate::inherent::*; +use crate::lang_items::TraitSolverLangItem; use crate::outlives::{Component, push_outlives_components}; use crate::{self as ty, Interner, Upcast as _}; @@ -18,6 +19,7 @@ pub struct Elaborator<I: Interner, O> { stack: Vec<O>, visited: HashSet<ty::Binder<I, ty::PredicateKind<I>>>, mode: Filter, + elaborate_sized: ElaborateSized, } enum Filter { @@ -25,6 +27,12 @@ enum Filter { OnlySelf, } +#[derive(Eq, PartialEq)] +enum ElaborateSized { + Yes, + No, +} + /// Describes how to elaborate an obligation into a sub-obligation. pub trait Elaboratable<I: Interner> { fn predicate(&self) -> I::Predicate; @@ -77,13 +85,19 @@ pub fn elaborate<I: Interner, O: Elaboratable<I>>( cx: I, obligations: impl IntoIterator<Item = O>, ) -> Elaborator<I, O> { - let mut elaborator = - Elaborator { cx, stack: Vec::new(), visited: HashSet::default(), mode: Filter::All }; + let mut elaborator = Elaborator { + cx, + stack: Vec::new(), + visited: HashSet::default(), + mode: Filter::All, + elaborate_sized: ElaborateSized::No, + }; elaborator.extend_deduped(obligations); elaborator } impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> { + /// Adds `obligations` to the stack. fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = O>) { // Only keep those bounds that we haven't already seen. // This is necessary to prevent infinite recursion in some @@ -103,6 +117,13 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> { self } + /// Start elaborating `Sized` - reqd during coherence checking, normally skipped to improve + /// compiler performance. + pub fn elaborate_sized(mut self) -> Self { + self.elaborate_sized = ElaborateSized::Yes; + self + } + fn elaborate(&mut self, elaboratable: &O) { let cx = self.cx; @@ -111,6 +132,19 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> { return; }; + // PERF(sized-hierarchy): To avoid iterating over sizedness supertraits in + // parameter environments, as an optimisation, sizedness supertraits aren't + // elaborated, so check if a `Sized` obligation is being elaborated to a + // `MetaSized` obligation and emit it. Candidate assembly and confirmation + // are modified to check for the `Sized` subtrait when a `MetaSized` obligation + // is present. + if self.elaborate_sized == ElaborateSized::No + && let Some(did) = clause.as_trait_clause().map(|c| c.def_id()) + && self.cx.is_lang_item(did, TraitSolverLangItem::Sized) + { + return; + } + let bound_clause = clause.kind(); match bound_clause.skip_binder() { ty::ClauseKind::Trait(data) => { diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 615d07707b9..fa5e8d43702 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -237,9 +237,6 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_ } pub fn types_may_unify(self, lhs: I::Ty, rhs: I::Ty) -> bool { - if lhs == rhs { - return true; - } self.types_may_unify_inner(lhs, rhs, Self::STARTING_DEPTH) } @@ -268,6 +265,10 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_ } fn types_may_unify_inner(self, lhs: I::Ty, rhs: I::Ty, depth: usize) -> bool { + if lhs == rhs { + return true; + } + match rhs.kind() { // Start by checking whether the `rhs` type may unify with // pretty much everything. Just return `true` in that case. diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index fa88bcb891a..608efc7515c 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -11,7 +11,7 @@ use rustc_ast_ir::Mutability; use crate::elaborate::Elaboratable; use crate::fold::{TypeFoldable, TypeSuperFoldable}; use crate::relate::Relate; -use crate::solve::AdtDestructorKind; +use crate::solve::{AdtDestructorKind, SizedTraitKind}; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; use crate::{self as ty, CollectAndApply, Interner, UpcastFrom}; @@ -228,6 +228,8 @@ pub trait Region<I: Interner<Region = Self>>: fn new_static(interner: I) -> Self; + fn new_placeholder(interner: I, var: I::PlaceholderRegion) -> Self; + fn is_bound(self) -> bool { matches!(self.kind(), ty::ReBound(..)) } @@ -254,6 +256,8 @@ pub trait Const<I: Interner<Const = Self>>: fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; + fn new_placeholder(interner: I, param: I::PlaceholderConst) -> Self; + fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst<I>) -> Self; fn new_expr(interner: I, expr: I::ExprConst) -> Self; @@ -524,13 +528,14 @@ pub trait Clauses<I: Interner<Clauses = Self>>: } /// Common capabilities of placeholder kinds -pub trait PlaceholderLike: Copy + Debug + Hash + Eq { +pub trait PlaceholderLike<I: Interner>: Copy + Debug + Hash + Eq { fn universe(self) -> ty::UniverseIndex; fn var(self) -> ty::BoundVar; + type Bound: BoundVarLike<I>; + fn new(ui: ty::UniverseIndex, bound: Self::Bound) -> Self; + fn new_anon(ui: ty::UniverseIndex, var: ty::BoundVar) -> Self; fn with_updated_universe(self, ui: ty::UniverseIndex) -> Self; - - fn new(ui: ty::UniverseIndex, var: ty::BoundVar) -> Self; } pub trait IntoKind { @@ -539,13 +544,13 @@ pub trait IntoKind { fn kind(self) -> Self::Kind; } -pub trait BoundVarLike<I: Interner> { +pub trait BoundVarLike<I: Interner>: Copy + Debug + Hash + Eq { fn var(self) -> ty::BoundVar; fn assert_eq(self, var: I::BoundVarKind); } -pub trait ParamLike { +pub trait ParamLike: Copy + Debug + Hash + Eq { fn index(self) -> u32; } @@ -566,7 +571,11 @@ pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq { // FIXME: perhaps use `all_fields` and expose `FieldDef`. fn all_field_tys(self, interner: I) -> ty::EarlyBinder<I, impl IntoIterator<Item = I::Ty>>; - fn sized_constraint(self, interner: I) -> Option<ty::EarlyBinder<I, I::Ty>>; + fn sizedness_constraint( + self, + interner: I, + sizedness: SizedTraitKind, + ) -> Option<ty::EarlyBinder<I, I::Ty>>; fn is_fundamental(self) -> bool; diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index cc0925b2c32..033d2579678 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -103,9 +103,9 @@ pub trait Interner: type Ty: Ty<Self>; type Tys: Tys<Self>; type FnInputTys: Copy + Debug + Hash + Eq + SliceLike<Item = Self::Ty> + TypeVisitable<Self>; - type ParamTy: Copy + Debug + Hash + Eq + ParamLike; - type BoundTy: Copy + Debug + Hash + Eq + BoundVarLike<Self>; - type PlaceholderTy: PlaceholderLike; + type ParamTy: ParamLike; + type BoundTy: BoundVarLike<Self>; + type PlaceholderTy: PlaceholderLike<Self, Bound = Self::BoundTy>; // Things stored inside of tys type ErrorGuaranteed: Copy + Debug + Hash + Eq; @@ -131,19 +131,19 @@ pub trait Interner: // Kinds of consts type Const: Const<Self>; - type PlaceholderConst: PlaceholderLike; type ParamConst: Copy + Debug + Hash + Eq + ParamLike; - type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>; + type BoundConst: BoundVarLike<Self>; + type PlaceholderConst: PlaceholderLike<Self, Bound = Self::BoundConst>; type ValueConst: ValueConst<Self>; type ExprConst: ExprConst<Self>; type ValTree: Copy + Debug + Hash + Eq; // Kinds of regions type Region: Region<Self>; - type EarlyParamRegion: Copy + Debug + Hash + Eq + ParamLike; + type EarlyParamRegion: ParamLike; type LateParamRegion: Copy + Debug + Hash + Eq; - type BoundRegion: Copy + Debug + Hash + Eq + BoundVarLike<Self>; - type PlaceholderRegion: PlaceholderLike; + type BoundRegion: BoundVarLike<Self>; + type PlaceholderRegion: PlaceholderLike<Self, Bound = Self::BoundRegion>; // Predicates type ParamEnv: ParamEnv<Self>; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 699dd82fb22..3ee6e07b7a5 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -30,7 +30,9 @@ pub enum TraitSolverLangItem { FutureOutput, Iterator, Metadata, + MetaSized, Option, + PointeeSized, PointeeTrait, Poll, Sized, diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 792090effcf..3863a6d7c5a 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -1,3 +1,4 @@ +#![cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir")] // tidy-alphabetical-start #![allow(rustc::usage_of_ty_tykind)] #![allow(rustc::usage_of_type_ir_inherent)] @@ -7,6 +8,7 @@ feature(associated_type_defaults, never_type, rustc_attrs, negative_impls) )] #![cfg_attr(feature = "nightly", allow(internal_features))] +#![cfg_attr(not(bootstrap), allow(rustc::direct_use_of_rustc_type_ir))] // tidy-alphabetical-end extern crate self as rustc_type_ir; diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs index 0ce927b58bb..a2442660259 100644 --- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs +++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs @@ -4,7 +4,7 @@ use super::{AvailableDepth, Cx, NestedGoals}; use crate::data_structures::HashMap; struct Success<X: Cx> { - additional_depth: usize, + required_depth: usize, nested_goals: NestedGoals<X>, result: X::Tracked<X::Result>, } @@ -28,7 +28,7 @@ struct CacheEntry<X: Cx> { #[derive_where(Debug; X: Cx)] pub(super) struct CacheData<'a, X: Cx> { pub(super) result: X::Result, - pub(super) additional_depth: usize, + pub(super) required_depth: usize, pub(super) encountered_overflow: bool, pub(super) nested_goals: &'a NestedGoals<X>, } @@ -47,7 +47,7 @@ impl<X: Cx> GlobalCache<X> { origin_result: X::Result, dep_node: X::DepNodeIndex, - additional_depth: usize, + required_depth: usize, encountered_overflow: bool, nested_goals: NestedGoals<X>, ) { @@ -55,13 +55,13 @@ impl<X: Cx> GlobalCache<X> { let entry = self.map.entry(input).or_default(); if encountered_overflow { let with_overflow = WithOverflow { nested_goals, result }; - let prev = entry.with_overflow.insert(additional_depth, with_overflow); + let prev = entry.with_overflow.insert(required_depth, with_overflow); if let Some(prev) = &prev { assert!(cx.evaluation_is_concurrent()); assert_eq!(cx.get_tracked(&prev.result), origin_result); } } else { - let prev = entry.success.replace(Success { additional_depth, nested_goals, result }); + let prev = entry.success.replace(Success { required_depth, nested_goals, result }); if let Some(prev) = &prev { assert!(cx.evaluation_is_concurrent()); assert_eq!(cx.get_tracked(&prev.result), origin_result); @@ -81,13 +81,13 @@ impl<X: Cx> GlobalCache<X> { mut candidate_is_applicable: impl FnMut(&NestedGoals<X>) -> bool, ) -> Option<CacheData<'a, X>> { let entry = self.map.get(&input)?; - if let Some(Success { additional_depth, ref nested_goals, ref result }) = entry.success { - if available_depth.cache_entry_is_applicable(additional_depth) + if let Some(Success { required_depth, ref nested_goals, ref result }) = entry.success { + if available_depth.cache_entry_is_applicable(required_depth) && candidate_is_applicable(nested_goals) { return Some(CacheData { result: cx.get_tracked(&result), - additional_depth, + required_depth, encountered_overflow: false, nested_goals, }); @@ -101,7 +101,7 @@ impl<X: Cx> GlobalCache<X> { if candidate_is_applicable(nested_goals) { return Some(CacheData { result: cx.get_tracked(result), - additional_depth, + required_depth: additional_depth, encountered_overflow: true, nested_goals, }); diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 1acd5d5c2af..b59b4f92854 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -19,13 +19,14 @@ use std::hash::Hash; use std::marker::PhantomData; use derive_where::derive_where; -use rustc_index::{Idx, IndexVec}; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use tracing::debug; use crate::data_structures::HashMap; +mod stack; +use stack::{Stack, StackDepth, StackEntry}; mod global_cache; use global_cache::CacheData; pub use global_cache::GlobalCache; @@ -225,9 +226,9 @@ impl AvailableDepth { /// in case there is exponential blowup. fn allowed_depth_for_nested<D: Delegate>( root_depth: AvailableDepth, - stack: &IndexVec<StackDepth, StackEntry<D::Cx>>, + stack: &Stack<D::Cx>, ) -> Option<AvailableDepth> { - if let Some(last) = stack.raw.last() { + if let Some(last) = stack.last() { if last.available_depth.0 == 0 { return None; } @@ -433,50 +434,6 @@ impl<X: Cx> NestedGoals<X> { } } -rustc_index::newtype_index! { - #[orderable] - #[gate_rustc_only] - pub struct StackDepth {} -} - -/// Stack entries of the evaluation stack. Its fields tend to be lazily -/// when popping a child goal or completely immutable. -#[derive_where(Debug; X: Cx)] -struct StackEntry<X: Cx> { - input: X::Input, - - /// Whether proving this goal is a coinductive step. - /// - /// This is used when encountering a trait solver cycle to - /// decide whether the initial provisional result of the cycle. - step_kind_from_parent: PathKind, - - /// The available depth of a given goal, immutable. - available_depth: AvailableDepth, - - /// The maximum depth reached by this stack entry, only up-to date - /// for the top of the stack and lazily updated for the rest. - reached_depth: StackDepth, - - /// All cycle heads this goal depends on. Lazily updated and only - /// up-to date for the top of the stack. - heads: CycleHeads, - - /// Whether evaluating this goal encountered overflow. Lazily updated. - encountered_overflow: bool, - - /// Whether this goal has been used as the root of a cycle. This gets - /// eagerly updated when encountering a cycle. - has_been_used: Option<UsageKind>, - - /// The nested goals of this goal, see the doc comment of the type. - nested_goals: NestedGoals<X>, - - /// Starts out as `None` and gets set when rerunning this - /// goal in case we encounter a cycle. - provisional_result: Option<X::Result>, -} - /// A provisional result of an already computed goals which depends on other /// goals still on the stack. #[derive_where(Debug; X: Cx)] @@ -498,7 +455,7 @@ pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> { /// The stack of goals currently being computed. /// /// An element is *deeper* in the stack if its index is *lower*. - stack: IndexVec<StackDepth, StackEntry<X>>, + stack: Stack<X>, /// The provisional cache contains entries for already computed goals which /// still depend on goals higher-up in the stack. We don't move them to the /// global cache and track them locally instead. A provisional cache entry @@ -537,16 +494,16 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { /// and using existing global cache entries to make sure they /// have the same impact on the remaining evaluation. fn update_parent_goal( - stack: &mut IndexVec<StackDepth, StackEntry<X>>, + stack: &mut Stack<X>, step_kind_from_parent: PathKind, - reached_depth: StackDepth, + required_depth_for_nested: usize, heads: &CycleHeads, encountered_overflow: bool, context: UpdateParentGoalCtxt<'_, X>, ) { if let Some(parent_index) = stack.last_index() { let parent = &mut stack[parent_index]; - parent.reached_depth = parent.reached_depth.max(reached_depth); + parent.required_depth = parent.required_depth.max(required_depth_for_nested + 1); parent.encountered_overflow |= encountered_overflow; parent.heads.extend_from_child(parent_index, step_kind_from_parent, heads); @@ -588,13 +545,11 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { /// the stack which completes the cycle. This given an inductive step AB which then cycles /// coinductively with A, we need to treat this cycle as coinductive. fn cycle_path_kind( - stack: &IndexVec<StackDepth, StackEntry<X>>, + stack: &Stack<X>, step_kind_to_head: PathKind, head: StackDepth, ) -> PathKind { - stack.raw[head.index() + 1..] - .iter() - .fold(step_kind_to_head, |curr, entry| curr.extend(entry.step_kind_from_parent)) + stack.cycle_step_kinds(head).fold(step_kind_to_head, |curr, step| curr.extend(step)) } /// Probably the most involved method of the whole solver. @@ -656,20 +611,18 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { return result; } - // Unfortunate, it looks like we actually have to compute this goalrar. - let depth = self.stack.next_index(); - let entry = StackEntry { + // Unfortunate, it looks like we actually have to compute this goal. + self.stack.push(StackEntry { input, step_kind_from_parent, available_depth, - reached_depth: depth, + required_depth: 0, heads: Default::default(), encountered_overflow: false, has_been_used: None, nested_goals: Default::default(), provisional_result: None, - }; - assert_eq!(self.stack.push(entry), depth); + }); // This is for global caching, so we properly track query dependencies. // Everything that affects the `result` should be performed within this @@ -686,7 +639,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { Self::update_parent_goal( &mut self.stack, final_entry.step_kind_from_parent, - final_entry.reached_depth, + final_entry.required_depth, &final_entry.heads, final_entry.encountered_overflow, UpdateParentGoalCtxt::Ordinary(&final_entry.nested_goals), @@ -700,7 +653,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // the global cache. assert_eq!(result, expected, "input={input:?}"); } else if D::inspect_is_noop(inspect) { - self.insert_global_cache(cx, input, final_entry, result, dep_node) + self.insert_global_cache(cx, final_entry, result, dep_node) } } else if D::ENABLE_PROVISIONAL_CACHE { debug_assert!(validate_cache.is_none(), "unexpected non-root: {input:?}"); @@ -728,7 +681,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { input: X::Input, inspect: &mut D::ProofTreeBuilder, ) -> X::Result { - if let Some(last) = self.stack.raw.last_mut() { + if let Some(last) = self.stack.last_mut() { last.encountered_overflow = true; // If computing a goal `B` depends on another goal `A` and // `A` has a nested goal which overflows, then computing `B` @@ -859,7 +812,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // apply provisional cache entries which encountered overflow once the // current goal is already part of the same cycle. This check could be // improved but seems to be good enough for now. - let last = self.stack.raw.last().unwrap(); + let last = self.stack.last().unwrap(); if last.heads.opt_lowest_cycle_head().is_none_or(|lowest| lowest > head) { continue; } @@ -868,14 +821,10 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // A provisional cache entry is only valid if the current path from its // highest cycle head to the goal is the same. if path_from_head == Self::cycle_path_kind(&self.stack, step_kind_from_parent, head) { - // While we don't have to track the full depth of the provisional cache entry, - // we do have to increment the required depth by one as we'd have already failed - // with overflow otherwise - let next_index = self.stack.next_index(); Self::update_parent_goal( &mut self.stack, step_kind_from_parent, - next_index, + 0, heads, encountered_overflow, UpdateParentGoalCtxt::ProvisionalCacheHit, @@ -893,9 +842,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { /// evaluating this entry would not have ended up depending on either a goal /// already on the stack or a provisional cache entry. fn candidate_is_applicable( - stack: &IndexVec<StackDepth, StackEntry<X>>, + &self, step_kind_from_parent: PathKind, - provisional_cache: &HashMap<X::Input, Vec<ProvisionalCacheEntry<X>>>, nested_goals: &NestedGoals<X>, ) -> bool { // If the global cache entry didn't depend on any nested goals, it always @@ -906,7 +854,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // If a nested goal of the global cache entry is on the stack, we would // definitely encounter a cycle. - if stack.iter().any(|e| nested_goals.contains(e.input)) { + if self.stack.iter().any(|e| nested_goals.contains(e.input)) { debug!("cache entry not applicable due to stack"); return false; } @@ -915,7 +863,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // would apply for any of its nested goals. #[allow(rustc::potential_query_instability)] for (input, path_from_global_entry) in nested_goals.iter() { - let Some(entries) = provisional_cache.get(&input) else { + let Some(entries) = self.provisional_cache.get(&input) else { continue; }; @@ -941,7 +889,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // We check if any of the paths taken while computing the global goal // would end up with an applicable provisional cache entry. let head = heads.highest_cycle_head(); - let head_to_curr = Self::cycle_path_kind(stack, step_kind_from_parent, head); + let head_to_curr = Self::cycle_path_kind(&self.stack, step_kind_from_parent, head); let full_paths = path_from_global_entry.extend_with(head_to_curr); if full_paths.contains(head_to_provisional.into()) { debug!( @@ -969,12 +917,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { cx.with_global_cache(|cache| { cache .get(cx, input, available_depth, |nested_goals| { - Self::candidate_is_applicable( - &self.stack, - step_kind_from_parent, - &self.provisional_cache, - nested_goals, - ) + self.candidate_is_applicable(step_kind_from_parent, nested_goals) }) .map(|c| c.result) }) @@ -991,33 +934,24 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { available_depth: AvailableDepth, ) -> Option<X::Result> { cx.with_global_cache(|cache| { - let CacheData { result, additional_depth, encountered_overflow, nested_goals } = cache + let CacheData { result, required_depth, encountered_overflow, nested_goals } = cache .get(cx, input, available_depth, |nested_goals| { - Self::candidate_is_applicable( - &self.stack, - step_kind_from_parent, - &self.provisional_cache, - nested_goals, - ) + self.candidate_is_applicable(step_kind_from_parent, nested_goals) })?; - // Update the reached depth of the current goal to make sure - // its state is the same regardless of whether we've used the - // global cache or not. - let reached_depth = self.stack.next_index().plus(additional_depth); // We don't move cycle participants to the global cache, so the // cycle heads are always empty. let heads = Default::default(); Self::update_parent_goal( &mut self.stack, step_kind_from_parent, - reached_depth, + required_depth, &heads, encountered_overflow, UpdateParentGoalCtxt::Ordinary(nested_goals), ); - debug!(?additional_depth, "global cache hit"); + debug!(?required_depth, "global cache hit"); Some(result) }) } @@ -1028,7 +962,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { input: X::Input, step_kind_from_parent: PathKind, ) -> Option<X::Result> { - let (head, _stack_entry) = self.stack.iter_enumerated().find(|(_, e)| e.input == input)?; + let head = self.stack.find(input)?; // We have a nested goal which directly relies on a goal deeper in the stack. // // We start by tagging all cycle participants, as that's necessary for caching. @@ -1043,10 +977,9 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // Subtle: when encountering a cyclic goal, we still first checked for overflow, // so we have to update the reached depth. - let next_index = self.stack.next_index(); let last_index = self.stack.last_index().unwrap(); let last = &mut self.stack[last_index]; - last.reached_depth = last.reached_depth.max(next_index); + last.required_depth = last.required_depth.max(1); last.nested_goals.insert(input, step_kind_from_parent.into()); last.nested_goals.insert(last.input, PathsToNested::EMPTY); @@ -1095,7 +1028,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { let mut i = 0; loop { let result = evaluate_goal(self, inspect); - let stack_entry = self.stack.pop().unwrap(); + let stack_entry = self.stack.pop(); debug_assert_eq!(stack_entry.input, input); // If the current goal is not the root of a cycle, we are done. @@ -1176,20 +1109,18 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { fn insert_global_cache( &mut self, cx: X, - input: X::Input, final_entry: StackEntry<X>, result: X::Result, dep_node: X::DepNodeIndex, ) { - let additional_depth = final_entry.reached_depth.as_usize() - self.stack.len(); debug!(?final_entry, ?result, "insert global cache"); cx.with_global_cache(|cache| { cache.insert( cx, - input, + final_entry.input, result, dep_node, - additional_depth, + final_entry.required_depth, final_entry.encountered_overflow, final_entry.nested_goals, ) diff --git a/compiler/rustc_type_ir/src/search_graph/stack.rs b/compiler/rustc_type_ir/src/search_graph/stack.rs new file mode 100644 index 00000000000..8bb247bf055 --- /dev/null +++ b/compiler/rustc_type_ir/src/search_graph/stack.rs @@ -0,0 +1,113 @@ +use std::ops::{Index, IndexMut}; + +use derive_where::derive_where; +use rustc_index::IndexVec; + +use super::{AvailableDepth, Cx, CycleHeads, NestedGoals, PathKind, UsageKind}; + +rustc_index::newtype_index! { + #[orderable] + #[gate_rustc_only] + pub(super) struct StackDepth {} +} + +/// Stack entries of the evaluation stack. Its fields tend to be lazily +/// when popping a child goal or completely immutable. +#[derive_where(Debug; X: Cx)] +pub(super) struct StackEntry<X: Cx> { + pub input: X::Input, + + /// Whether proving this goal is a coinductive step. + /// + /// This is used when encountering a trait solver cycle to + /// decide whether the initial provisional result of the cycle. + pub step_kind_from_parent: PathKind, + + /// The available depth of a given goal, immutable. + pub available_depth: AvailableDepth, + + /// The maximum depth required while evaluating this goal. + pub required_depth: usize, + + /// All cycle heads this goal depends on. Lazily updated and only + /// up-to date for the top of the stack. + pub heads: CycleHeads, + + /// Whether evaluating this goal encountered overflow. Lazily updated. + pub encountered_overflow: bool, + + /// Whether this goal has been used as the root of a cycle. This gets + /// eagerly updated when encountering a cycle. + pub has_been_used: Option<UsageKind>, + + /// The nested goals of this goal, see the doc comment of the type. + pub nested_goals: NestedGoals<X>, + + /// Starts out as `None` and gets set when rerunning this + /// goal in case we encounter a cycle. + pub provisional_result: Option<X::Result>, +} + +#[derive_where(Default; X: Cx)] +pub(super) struct Stack<X: Cx> { + entries: IndexVec<StackDepth, StackEntry<X>>, +} + +impl<X: Cx> Stack<X> { + pub(super) fn is_empty(&self) -> bool { + self.entries.is_empty() + } + + pub(super) fn len(&self) -> usize { + self.entries.len() + } + + pub(super) fn last_index(&self) -> Option<StackDepth> { + self.entries.last_index() + } + + pub(super) fn last(&self) -> Option<&StackEntry<X>> { + self.entries.raw.last() + } + + pub(super) fn last_mut(&mut self) -> Option<&mut StackEntry<X>> { + self.entries.raw.last_mut() + } + + pub(super) fn next_index(&self) -> StackDepth { + self.entries.next_index() + } + + pub(super) fn push(&mut self, entry: StackEntry<X>) -> StackDepth { + self.entries.push(entry) + } + + pub(super) fn pop(&mut self) -> StackEntry<X> { + self.entries.pop().unwrap() + } + + pub(super) fn cycle_step_kinds(&self, head: StackDepth) -> impl Iterator<Item = PathKind> { + self.entries.raw[head.index() + 1..].iter().map(|entry| entry.step_kind_from_parent) + } + + pub(super) fn iter(&self) -> impl Iterator<Item = &StackEntry<X>> { + self.entries.iter() + } + + pub(super) fn find(&self, input: X::Input) -> Option<StackDepth> { + self.entries.iter_enumerated().find(|(_, e)| e.input == input).map(|(idx, _)| idx) + } +} + +impl<X: Cx> Index<StackDepth> for Stack<X> { + type Output = StackEntry<X>; + fn index(&self, index: StackDepth) -> &StackEntry<X> { + &self.entries[index] + } +} + +impl<X: Cx> IndexMut<StackDepth> for Stack<X> { + fn index_mut(&mut self, index: StackDepth) -> &mut Self::Output { + &mut self.entries[index] + } +} diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 2e05c23a645..bbbeaa29f84 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -8,6 +8,7 @@ use derive_where::derive_where; use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; +use crate::lang_items::TraitSolverLangItem; use crate::search_graph::PathKind; use crate::{self as ty, Canonical, CanonicalVarValues, Interner, Upcast}; @@ -236,6 +237,14 @@ pub struct ExternalConstraintsData<I: Interner> { pub normalization_nested_goals: NestedNormalizationGoals<I>, } +impl<I: Interner> ExternalConstraintsData<I> { + pub fn is_empty(&self) -> bool { + self.region_constraints.is_empty() + && self.opaque_types.is_empty() + && self.normalization_nested_goals.is_empty() + } +} + #[derive_where(Clone, Hash, PartialEq, Eq, Debug, Default; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] @@ -358,3 +367,24 @@ pub enum AdtDestructorKind { NotConst, Const, } + +/// Which sizedness trait - `Sized`, `MetaSized`? `PointeeSized` is omitted as it is removed during +/// lowering. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +pub enum SizedTraitKind { + /// `Sized` trait + Sized, + /// `MetaSized` trait + MetaSized, +} + +impl SizedTraitKind { + /// Returns `DefId` of corresponding language item. + pub fn require_lang_item<I: Interner>(self, cx: I) -> I::DefId { + cx.require_lang_item(match self { + SizedTraitKind::Sized => TraitSolverLangItem::Sized, + SizedTraitKind::MetaSized => TraitSolverLangItem::MetaSized, + }) + } +} diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index 8ba985d2d19..d1ca9bdb7fb 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -342,7 +342,7 @@ struct HasRegionsBoundAt { // FIXME: Could be optimized to not walk into components with no escaping bound vars. impl<I: Interner> TypeVisitor<I> for HasRegionsBoundAt { type Result = ControlFlow<()>; - fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result { + fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result { self.binder.shift_in(1); t.super_visit_with(self)?; self.binder.shift_out(1); diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index fc3864dd5ae..a96ac97f785 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -52,7 +52,7 @@ use smallvec::SmallVec; use thin_vec::ThinVec; use crate::inherent::*; -use crate::{self as ty, Interner, TypeFlags, TypeFoldable}; +use crate::{self as ty, Interner, TypeFlags}; /// This trait is implemented for every type that can be visited, /// providing the skeleton of the traversal. @@ -94,7 +94,7 @@ pub trait TypeVisitor<I: Interner>: Sized { #[cfg(not(feature = "nightly"))] type Result: VisitorResult; - fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result { + fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result { t.super_visit_with(self) } @@ -401,7 +401,7 @@ impl std::fmt::Debug for HasTypeFlagsVisitor { impl<I: Interner> TypeVisitor<I> for HasTypeFlagsVisitor { type Result = ControlFlow<FoundFlags>; - fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result { + fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result { // If we're looking for the HAS_BINDER_VARS flag, check if the // binder has vars. This won't be present in the binder's bound // value, so we need to check here too. @@ -510,7 +510,7 @@ struct HasEscapingVarsVisitor { impl<I: Interner> TypeVisitor<I> for HasEscapingVarsVisitor { type Result = ControlFlow<FoundEscapingVars>; - fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result { + fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result { self.outer_index.shift_in(1); let result = t.super_visit_with(self); self.outer_index.shift_out(1); |
