diff options
Diffstat (limited to 'compiler')
377 files changed, 5393 insertions, 5452 deletions
diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs index 2ecac8a9df1..872cae59a4e 100644 --- a/compiler/rustc_abi/src/callconv.rs +++ b/compiler/rustc_abi/src/callconv.rs @@ -3,18 +3,23 @@ mod abi { pub(crate) use crate::Variants; } +#[cfg(feature = "nightly")] use rustc_macros::HashStable_Generic; -use crate::{Abi, Align, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; +#[cfg(feature = "nightly")] +use crate::{Abi, FieldsShape, TyAbiInterface, TyAndLayout}; +use crate::{Align, HasDataLayout, Size}; -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[cfg_attr(feature = "nightly", derive(HashStable_Generic))] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum RegKind { Integer, Float, Vector, } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[cfg_attr(feature = "nightly", derive(HashStable_Generic))] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct Reg { pub kind: RegKind, pub size: Size, @@ -108,15 +113,8 @@ impl HomogeneousAggregate { } } +#[cfg(feature = "nightly")] impl<'a, Ty> TyAndLayout<'a, Ty> { - /// Returns `true` if this is an aggregate type (including a ScalarPair!) - pub fn is_aggregate(&self) -> bool { - match self.abi { - Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false, - Abi::ScalarPair(..) | Abi::Aggregate { .. } => true, - } - } - /// Returns `Homogeneous` if this layout is an aggregate containing fields of /// only a single type (e.g., `(u32, u32)`). Such aggregates are often /// special-cased in ABIs. diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 6e1299944a0..0340d1bd6bc 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -11,8 +11,10 @@ use crate::{ Variants, WrappingRange, }; +#[cfg(feature = "nightly")] mod ty; +#[cfg(feature = "nightly")] pub use ty::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx}; // A variant is absent if it's uninhabited and only has ZST fields. @@ -39,7 +41,7 @@ enum NicheBias { End, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum LayoutCalculatorError<F> { /// An unsized type was found in a location where a sized type was expected. /// @@ -54,6 +56,36 @@ pub enum LayoutCalculatorError<F> { /// A union had no fields. EmptyUnion, + + /// The fields or variants have irreconcilable reprs + ReprConflict, +} + +impl<F> LayoutCalculatorError<F> { + pub fn without_payload(&self) -> LayoutCalculatorError<()> { + match self { + LayoutCalculatorError::UnexpectedUnsized(_) => { + LayoutCalculatorError::UnexpectedUnsized(()) + } + LayoutCalculatorError::SizeOverflow => LayoutCalculatorError::SizeOverflow, + LayoutCalculatorError::EmptyUnion => LayoutCalculatorError::EmptyUnion, + LayoutCalculatorError::ReprConflict => LayoutCalculatorError::ReprConflict, + } + } + + /// Format an untranslated diagnostic for this type + /// + /// Intended for use by rust-analyzer, as neither it nor `rustc_abi` depend on fluent infra. + pub fn fallback_fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + LayoutCalculatorError::UnexpectedUnsized(_) => { + "an unsized type was found where a sized type was expected" + } + LayoutCalculatorError::SizeOverflow => "size overflow", + LayoutCalculatorError::EmptyUnion => "type is a union with no fields", + LayoutCalculatorError::ReprConflict => "type has an invalid repr", + }) + } } type LayoutCalculatorResult<FieldIdx, VariantIdx, F> = @@ -489,6 +521,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { } let dl = self.cx.data_layout(); + // bail if the enum has an incoherent repr that cannot be computed + if repr.packed() { + return Err(LayoutCalculatorError::ReprConflict); + } let calculate_niche_filling_layout = || -> Option<TmpLayout<FieldIdx, VariantIdx>> { if dont_niche_optimize_enum { diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 84d756b6d51..8e90130da4c 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -29,14 +29,14 @@ mod layout; mod tests; pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind}; -pub use layout::{ - FIRST_VARIANT, FieldIdx, Layout, LayoutCalculator, LayoutCalculatorError, TyAbiInterface, - TyAndLayout, VariantIdx, -}; +#[cfg(feature = "nightly")] +pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx}; +pub use layout::{LayoutCalculator, LayoutCalculatorError}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro /// instead of implementing everything in `rustc_middle`. +#[cfg(feature = "nightly")] pub trait HashStableContext {} #[derive(Clone, Copy, PartialEq, Eq, Default)] @@ -1644,6 +1644,14 @@ pub struct LayoutS<FieldIdx: Idx, VariantIdx: Idx> { } impl<FieldIdx: Idx, VariantIdx: Idx> LayoutS<FieldIdx, VariantIdx> { + /// Returns `true` if this is an aggregate type (including a ScalarPair!) + pub fn is_aggregate(&self) -> bool { + match self.abi { + Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false, + Abi::ScalarPair(..) | Abi::Aggregate { .. } => true, + } + } + pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self { let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar); let size = scalar.size(cx); diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index cecf223b961..4d8525989cc 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -23,7 +23,6 @@ #![feature(maybe_uninit_slice)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] -#![feature(strict_provenance)] #![warn(unreachable_pub)] // tidy-alphabetical-end diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 02cb6f188a7..8e4f4c8e71a 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2697,7 +2697,7 @@ impl fmt::Debug for ImplPolarity { } /// The polarity of a trait bound. -#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Hash)] #[derive(HashStable_Generic)] pub enum BoundPolarity { /// `Type: Trait` @@ -2719,7 +2719,7 @@ impl BoundPolarity { } /// The constness of a trait bound. -#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Hash)] #[derive(HashStable_Generic)] pub enum BoundConstness { /// `Type: Trait` diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 207ec710650..eb71ec5f4ec 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -135,7 +135,7 @@ pub trait Visitor<'ast>: Sized { /// or `ControlFlow<T>`. type Result: VisitorResult = (); - fn visit_ident(&mut self, _ident: Ident) -> Self::Result { + fn visit_ident(&mut self, _ident: &'ast Ident) -> Self::Result { Self::Result::output() } fn visit_foreign_item(&mut self, i: &'ast ForeignItem) -> Self::Result { @@ -173,9 +173,6 @@ pub trait Visitor<'ast>: Sized { fn visit_method_receiver_expr(&mut self, ex: &'ast Expr) -> Self::Result { self.visit_expr(ex) } - fn visit_expr_post(&mut self, _ex: &'ast Expr) -> Self::Result { - Self::Result::output() - } fn visit_ty(&mut self, t: &'ast Ty) -> Self::Result { walk_ty(self, t) } @@ -317,12 +314,12 @@ pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) -> V::R } pub fn walk_label<'a, V: Visitor<'a>>(visitor: &mut V, Label { ident }: &'a Label) -> V::Result { - visitor.visit_ident(*ident) + visitor.visit_ident(ident) } pub fn walk_lifetime<'a, V: Visitor<'a>>(visitor: &mut V, lifetime: &'a Lifetime) -> V::Result { let Lifetime { id: _, ident } = lifetime; - visitor.visit_ident(*ident) + visitor.visit_ident(ident) } pub fn walk_poly_trait_ref<'a, V>(visitor: &mut V, trait_ref: &'a PolyTraitRef) -> V::Result @@ -429,7 +426,7 @@ impl WalkItemKind for ItemKind { }) => { try_visit!(walk_qself(visitor, qself)); try_visit!(visitor.visit_path(path, *id)); - visit_opt!(visitor, visit_ident, *rename); + visit_opt!(visitor, visit_ident, rename); visit_opt!(visitor, visit_block, body); } ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { @@ -437,9 +434,9 @@ impl WalkItemKind for ItemKind { try_visit!(visitor.visit_path(prefix, *id)); if let Some(suffixes) = suffixes { for (ident, rename) in suffixes { - visitor.visit_ident(*ident); + visitor.visit_ident(ident); if let Some(rename) = rename { - visitor.visit_ident(*rename); + visitor.visit_ident(rename); } } } @@ -472,7 +469,7 @@ where let Variant { attrs, id: _, span: _, vis, ident, data, disr_expr, is_placeholder: _ } = variant; walk_list!(visitor, visit_attribute, attrs); try_visit!(visitor.visit_vis(vis)); - try_visit!(visitor.visit_ident(*ident)); + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_variant_data(data)); visit_opt!(visitor, visit_variant_discr, disr_expr); V::Result::output() @@ -481,7 +478,7 @@ where pub fn walk_expr_field<'a, V: Visitor<'a>>(visitor: &mut V, f: &'a ExprField) -> V::Result { let ExprField { attrs, id: _, span: _, ident, expr, is_shorthand: _, is_placeholder: _ } = f; walk_list!(visitor, visit_attribute, attrs); - try_visit!(visitor.visit_ident(*ident)); + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_expr(expr)); V::Result::output() } @@ -489,7 +486,7 @@ pub fn walk_expr_field<'a, V: Visitor<'a>>(visitor: &mut V, f: &'a ExprField) -> pub fn walk_pat_field<'a, V: Visitor<'a>>(visitor: &mut V, fp: &'a PatField) -> V::Result { let PatField { ident, pat, is_shorthand: _, attrs, id: _, span: _, is_placeholder: _ } = fp; walk_list!(visitor, visit_attribute, attrs); - try_visit!(visitor.visit_ident(*ident)); + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_pat(pat)); V::Result::output() } @@ -564,7 +561,7 @@ pub fn walk_use_tree<'a, V: Visitor<'a>>( match kind { UseTreeKind::Simple(rename) => { // The extra IDs are handled during AST lowering. - visit_opt!(visitor, visit_ident, *rename); + visit_opt!(visitor, visit_ident, rename); } UseTreeKind::Glob => {} UseTreeKind::Nested { ref items, span: _ } => { @@ -581,7 +578,7 @@ pub fn walk_path_segment<'a, V: Visitor<'a>>( segment: &'a PathSegment, ) -> V::Result { let PathSegment { ident, id: _, args } = segment; - try_visit!(visitor.visit_ident(*ident)); + try_visit!(visitor.visit_ident(ident)); visit_opt!(visitor, visit_generic_args, args); V::Result::output() } @@ -627,7 +624,7 @@ pub fn walk_assoc_item_constraint<'a, V: Visitor<'a>>( constraint: &'a AssocItemConstraint, ) -> V::Result { let AssocItemConstraint { id: _, ident, gen_args, kind, span: _ } = constraint; - try_visit!(visitor.visit_ident(*ident)); + try_visit!(visitor.visit_ident(ident)); visit_opt!(visitor, visit_generic_args, gen_args); match kind { AssocItemConstraintKind::Equality { term } => match term { @@ -665,7 +662,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res try_visit!(visitor.visit_pat(subpattern)); } PatKind::Ident(_bmode, ident, optional_subpattern) => { - try_visit!(visitor.visit_ident(*ident)); + try_visit!(visitor.visit_ident(ident)); visit_opt!(visitor, visit_pat, optional_subpattern); } PatKind::Lit(expression) => try_visit!(visitor.visit_expr(expression)), @@ -751,7 +748,7 @@ pub fn walk_generic_param<'a, V: Visitor<'a>>( let GenericParam { id: _, ident, attrs, bounds, is_placeholder: _, kind, colon_span: _ } = param; walk_list!(visitor, visit_attribute, attrs); - try_visit!(visitor.visit_ident(*ident)); + try_visit!(visitor.visit_ident(ident)); walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); match kind { GenericParamKind::Lifetime => (), @@ -889,7 +886,7 @@ impl WalkItemKind for AssocItemKind { }) => { try_visit!(walk_qself(visitor, qself)); try_visit!(visitor.visit_path(path, *id)); - visit_opt!(visitor, visit_ident, *rename); + visit_opt!(visitor, visit_ident, rename); visit_opt!(visitor, visit_block, body); } AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { @@ -897,9 +894,9 @@ impl WalkItemKind for AssocItemKind { try_visit!(visitor.visit_path(prefix, id)); if let Some(suffixes) = suffixes { for (ident, rename) in suffixes { - visitor.visit_ident(*ident); + visitor.visit_ident(ident); if let Some(rename) = rename { - visitor.visit_ident(*rename); + visitor.visit_ident(rename); } } } @@ -915,7 +912,7 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>( item: &'a Item<impl WalkItemKind>, ctxt: AssocCtxt, ) -> V::Result { - let &Item { id: _, span: _, ident, ref vis, ref attrs, ref kind, tokens: _ } = item; + let Item { id: _, span: _, ident, vis, attrs, kind, tokens: _ } = item; walk_list!(visitor, visit_attribute, attrs); try_visit!(visitor.visit_vis(vis)); try_visit!(visitor.visit_ident(ident)); @@ -935,7 +932,7 @@ pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _ } = field; walk_list!(visitor, visit_attribute, attrs); try_visit!(visitor.visit_vis(vis)); - visit_opt!(visitor, visit_ident, *ident); + visit_opt!(visitor, visit_ident, ident); try_visit!(visitor.visit_ty(ty)); V::Result::output() } @@ -1017,7 +1014,7 @@ pub fn walk_format_args<'a, V: Visitor<'a>>(visitor: &mut V, fmt: &'a FormatArgs for FormatArgument { kind, expr } in arguments.all_args() { match kind { FormatArgumentKind::Named(ident) | FormatArgumentKind::Captured(ident) => { - try_visit!(visitor.visit_ident(*ident)) + try_visit!(visitor.visit_ident(ident)) } FormatArgumentKind::Normal => {} } @@ -1137,7 +1134,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V } ExprKind::Field(subexpression, ident) => { try_visit!(visitor.visit_expr(subexpression)); - try_visit!(visitor.visit_ident(*ident)); + try_visit!(visitor.visit_ident(ident)); } ExprKind::Index(main_expression, index_expression, _span) => { try_visit!(visitor.visit_expr(main_expression)); @@ -1172,7 +1169,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V ExprKind::FormatArgs(f) => try_visit!(visitor.visit_format_args(f)), ExprKind::OffsetOf(container, fields) => { try_visit!(visitor.visit_ty(container)); - walk_list!(visitor, visit_ident, fields.iter().copied()); + walk_list!(visitor, visit_ident, fields.iter()); } ExprKind::Yield(optional_expression) => { visit_opt!(visitor, visit_expr, optional_expression); @@ -1185,7 +1182,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V ExprKind::Dummy => {} } - visitor.visit_expr_post(expression) + V::Result::output() } pub fn walk_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Param) -> V::Result { diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 88cdb2ec363..6585a7de245 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -49,7 +49,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { | asm::InlineAsmArch::RiscV64 | asm::InlineAsmArch::LoongArch64 ); - if !is_stable && !self.tcx.features().asm_experimental_arch { + if !is_stable && !self.tcx.features().asm_experimental_arch() { feature_err( &self.tcx.sess, sym::asm_experimental_arch, @@ -65,7 +65,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { { self.dcx().emit_err(AttSyntaxOnlyX86 { span: sp }); } - if asm.options.contains(InlineAsmOptions::MAY_UNWIND) && !self.tcx.features().asm_unwind { + if asm.options.contains(InlineAsmOptions::MAY_UNWIND) && !self.tcx.features().asm_unwind() { feature_err( &self.tcx.sess, sym::asm_unwind, @@ -237,7 +237,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } InlineAsmOperand::Label { block } => { - if !self.tcx.features().asm_goto { + if !self.tcx.features().asm_goto() { feature_err( sess, sym::asm_goto, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index ae1e1b3f8a2..a1a16d0ca26 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -575,7 +575,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } else { // Either `body.is_none()` or `is_never_pattern` here. if !is_never_pattern { - if self.tcx.features().never_patterns { + if self.tcx.features().never_patterns() { // If the feature is off we already emitted the error after parsing. let suggestion = span.shrink_to_hi(); self.dcx().emit_err(MatchArmWithNoBody { span, suggestion }); @@ -717,7 +717,7 @@ impl<'hir> LoweringContext<'_, 'hir> { outer_hir_id: HirId, inner_hir_id: HirId, ) { - if self.tcx.features().async_fn_track_caller + if self.tcx.features().async_fn_track_caller() && let Some(attrs) = self.attrs.get(&outer_hir_id.local_id) && attrs.into_iter().any(|attr| attr.has_name(sym::track_caller)) { @@ -1572,7 +1572,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); } Some(hir::CoroutineKind::Coroutine(_)) => { - if !self.tcx.features().coroutines { + if !self.tcx.features().coroutines() { rustc_session::parse::feature_err( &self.tcx.sess, sym::coroutines, @@ -1584,7 +1584,7 @@ impl<'hir> LoweringContext<'_, 'hir> { false } None => { - if !self.tcx.features().coroutines { + if !self.tcx.features().coroutines() { rustc_session::parse::feature_err( &self.tcx.sess, sym::coroutines, diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index ce744cc56e1..97fa90d340e 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -57,7 +57,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { owner: NodeId, f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>, ) { - let mut lctx = LoweringContext::new(self.tcx, self.resolver, self.ast_index); + let mut lctx = LoweringContext::new(self.tcx, self.resolver); lctx.with_hir_id_owner(owner, |lctx| f(lctx)); for (def_id, info) in lctx.children { @@ -193,8 +193,6 @@ impl<'hir> LoweringContext<'_, 'hir> { ItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => { let (generics, (ty, body_id)) = self.lower_generics( generics, - Const::No, - false, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { @@ -225,16 +223,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ); let itctx = ImplTraitContext::Universal; - let (generics, decl) = - this.lower_generics(generics, header.constness, false, id, itctx, |this| { - this.lower_fn_decl( - decl, - id, - *fn_sig_span, - FnDeclKind::Fn, - coroutine_kind, - ) - }); + let (generics, decl) = this.lower_generics(generics, id, itctx, |this| { + this.lower_fn_decl(decl, id, *fn_sig_span, FnDeclKind::Fn, coroutine_kind) + }); let sig = hir::FnSig { decl, header: this.lower_fn_header(*header, hir::Safety::Safe), @@ -269,8 +260,6 @@ impl<'hir> LoweringContext<'_, 'hir> { add_ty_alias_where_clause(&mut generics, *where_clauses, true); let (generics, ty) = self.lower_generics( &generics, - Const::No, - false, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| match ty { @@ -294,8 +283,6 @@ impl<'hir> LoweringContext<'_, 'hir> { ItemKind::Enum(enum_definition, generics) => { let (generics, variants) = self.lower_generics( generics, - Const::No, - false, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { @@ -309,8 +296,6 @@ impl<'hir> LoweringContext<'_, 'hir> { ItemKind::Struct(struct_def, generics) => { let (generics, struct_def) = self.lower_generics( generics, - Const::No, - false, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| this.lower_variant_data(hir_id, struct_def), @@ -320,8 +305,6 @@ impl<'hir> LoweringContext<'_, 'hir> { ItemKind::Union(vdata, generics) => { let (generics, vdata) = self.lower_generics( generics, - Const::No, - false, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| this.lower_variant_data(hir_id, vdata), @@ -353,7 +336,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // parent lifetime. let itctx = ImplTraitContext::Universal; let (generics, (trait_ref, lowered_ty)) = - self.lower_generics(ast_generics, Const::No, false, id, itctx, |this| { + self.lower_generics(ast_generics, id, itctx, |this| { let modifiers = TraitBoundModifiers { constness: BoundConstness::Never, asyncness: BoundAsyncness::Normal, @@ -405,8 +388,6 @@ impl<'hir> LoweringContext<'_, 'hir> { ItemKind::Trait(box Trait { is_auto, safety, generics, bounds, items }) => { let (generics, (safety, items, bounds)) = self.lower_generics( generics, - Const::No, - false, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { @@ -426,8 +407,6 @@ impl<'hir> LoweringContext<'_, 'hir> { ItemKind::TraitAlias(generics, bounds) => { let (generics, bounds) = self.lower_generics( generics, - Const::No, - false, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { @@ -604,50 +583,23 @@ impl<'hir> LoweringContext<'_, 'hir> { ctxt: AssocCtxt, parent_hir: &'hir hir::OwnerInfo<'hir>, ) -> hir::OwnerNode<'hir> { - // Evaluate with the lifetimes in `params` in-scope. - // This is used to track which lifetimes have already been defined, - // and which need to be replicated when lowering an async fn. - let parent_item = parent_hir.node().expect_item(); - let constness = match parent_item.kind { + match parent_item.kind { hir::ItemKind::Impl(impl_) => { self.is_in_trait_impl = impl_.of_trait.is_some(); - // N.B. the impl should always lower to methods that have `const host: bool` params if the trait - // is const. It doesn't matter whether the `impl` itself is const. Disallowing const fn from - // calling non-const impls are done through associated types. - if let Some(def_id) = impl_.of_trait.and_then(|tr| tr.trait_def_id()) { - if let Some(local_def) = def_id.as_local() { - match &self.ast_index[local_def] { - AstOwner::Item(ast::Item { attrs, .. }) => attrs - .iter() - .find(|attr| attr.has_name(sym::const_trait)) - .map_or(Const::No, |attr| Const::Yes(attr.span)), - _ => Const::No, - } - } else if self.tcx.is_const_trait(def_id) { - // FIXME(effects) span - Const::Yes(self.tcx.def_ident_span(def_id).unwrap()) - } else { - Const::No - } - } else { - Const::No - } } - hir::ItemKind::Trait(_, _, _, _, _) => parent_hir - .attrs - .get(parent_item.hir_id().local_id) - .iter() - .find(|attr| attr.has_name(sym::const_trait)) - .map_or(Const::No, |attr| Const::Yes(attr.span)), + hir::ItemKind::Trait(_, _, _, _, _) => {} kind => { span_bug!(item.span, "assoc item has unexpected kind of parent: {}", kind.descr()) } - }; + } + // Evaluate with the lifetimes in `params` in-scope. + // This is used to track which lifetimes have already been defined, + // and which need to be replicated when lowering an async fn. match ctxt { - AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item, constness)), - AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item, constness)), + AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item)), + AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item)), } } @@ -663,7 +615,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let fdec = &sig.decl; let itctx = ImplTraitContext::Universal; let (generics, (decl, fn_args)) = - self.lower_generics(generics, Const::No, false, i.id, itctx, |this| { + self.lower_generics(generics, i.id, itctx, |this| { ( // Disallow `impl Trait` in foreign items. this.lower_fn_decl( @@ -775,11 +727,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - fn lower_trait_item( - &mut self, - i: &AssocItem, - trait_constness: Const, - ) -> &'hir hir::TraitItem<'hir> { + fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> { let hir_id = self.lower_node_id(i.id); self.lower_attrs(hir_id, &i.attrs); let trait_item_def_id = hir_id.expect_owner(); @@ -788,8 +736,6 @@ impl<'hir> LoweringContext<'_, 'hir> { AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => { let (generics, kind) = self.lower_generics( generics, - Const::No, - false, i.id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { @@ -810,7 +756,6 @@ impl<'hir> LoweringContext<'_, 'hir> { i.id, FnDeclKind::Trait, sig.header.coroutine_kind, - trait_constness, ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false) } @@ -829,7 +774,6 @@ impl<'hir> LoweringContext<'_, 'hir> { i.id, FnDeclKind::Trait, sig.header.coroutine_kind, - trait_constness, ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true) } @@ -838,8 +782,6 @@ impl<'hir> LoweringContext<'_, 'hir> { add_ty_alias_where_clause(&mut generics, *where_clauses, false); let (generics, kind) = self.lower_generics( &generics, - Const::No, - false, i.id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { @@ -912,11 +854,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr(span, hir::ExprKind::Err(guar)) } - fn lower_impl_item( - &mut self, - i: &AssocItem, - constness_of_trait: Const, - ) -> &'hir hir::ImplItem<'hir> { + fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> { // Since `default impl` is not yet implemented, this is always true in impls. let has_value = true; let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); @@ -926,8 +864,6 @@ impl<'hir> LoweringContext<'_, 'hir> { let (generics, kind) = match &i.kind { AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics( generics, - Const::No, - false, i.id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { @@ -953,7 +889,6 @@ impl<'hir> LoweringContext<'_, 'hir> { i.id, if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent }, sig.header.coroutine_kind, - constness_of_trait, ); (generics, hir::ImplItemKind::Fn(sig, body_id)) @@ -963,8 +898,6 @@ impl<'hir> LoweringContext<'_, 'hir> { add_ty_alias_where_clause(&mut generics, *where_clauses, false); self.lower_generics( &generics, - Const::No, - false, i.id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| match ty { @@ -1370,18 +1303,12 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, kind: FnDeclKind, coroutine_kind: Option<CoroutineKind>, - parent_constness: Const, ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) { let header = self.lower_fn_header(sig.header, hir::Safety::Safe); - // Don't pass along the user-provided constness of trait associated functions; we don't want to - // synthesize a host effect param for them. We reject `const` on them during AST validation. - let constness = - if kind == FnDeclKind::Inherent { sig.header.constness } else { parent_constness }; let itctx = ImplTraitContext::Universal; - let (generics, decl) = - self.lower_generics(generics, constness, kind == FnDeclKind::Impl, id, itctx, |this| { - this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind) - }); + let (generics, decl) = self.lower_generics(generics, id, itctx, |this| { + this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind) + }); (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) }) } @@ -1460,8 +1387,6 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_generics<T>( &mut self, generics: &Generics, - constness: Const, - force_append_constness: bool, parent_node_id: NodeId, itctx: ImplTraitContext, f: impl FnOnce(&mut Self) -> T, @@ -1512,7 +1437,7 @@ impl<'hir> LoweringContext<'_, 'hir> { continue; } let is_param = *is_param.get_or_insert_with(compute_is_param); - if !is_param && !self.tcx.features().more_maybe_bounds { + if !is_param && !self.tcx.features().more_maybe_bounds() { self.tcx .sess .create_feature_err( @@ -1524,30 +1449,6 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - // Desugar `~const` bound in generics into an additional `const host: bool` param - // if the effects feature is enabled. This needs to be done before we lower where - // clauses since where clauses need to bind to the DefId of the host param - let host_param_parts = if let Const::Yes(span) = constness - // if this comes from implementing a `const` trait, we must force constness to be appended - // to the impl item, no matter whether effects is enabled. - && (self.tcx.features().effects || force_append_constness) - { - let span = self.lower_span(span); - let param_node_id = self.next_node_id(); - let hir_id = self.next_id(); - let def_id = self.create_def( - self.local_def_id(parent_node_id), - param_node_id, - sym::host, - DefKind::ConstParam, - span, - ); - self.host_param_id = Some(def_id); - Some((span, hir_id, def_id)) - } else { - None - }; - let mut predicates: SmallVec<[hir::WherePredicate<'hir>; 4]> = SmallVec::new(); predicates.extend(generics.params.iter().filter_map(|param| { self.lower_generic_bound_predicate( @@ -1574,7 +1475,7 @@ impl<'hir> LoweringContext<'_, 'hir> { .collect(); // Introduce extra lifetimes if late resolution tells us to. - let extra_lifetimes = self.resolver.take_extra_lifetime_params(parent_node_id); + let extra_lifetimes = self.resolver.extra_lifetime_params(parent_node_id); params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| { self.lifetime_res_to_generic_param( ident, @@ -1595,74 +1496,6 @@ impl<'hir> LoweringContext<'_, 'hir> { let impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds); predicates.extend(impl_trait_bounds.into_iter()); - if let Some((span, hir_id, def_id)) = host_param_parts { - let const_node_id = self.next_node_id(); - let anon_const_did = - self.create_def(def_id, const_node_id, kw::Empty, DefKind::AnonConst, span); - - let const_id = self.next_id(); - let const_expr_id = self.next_id(); - let bool_id = self.next_id(); - - self.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id))); - self.children.push((anon_const_did, hir::MaybeOwner::NonOwner(const_id))); - - let const_body = self.lower_body(|this| { - (&[], hir::Expr { - hir_id: const_expr_id, - kind: hir::ExprKind::Lit( - this.arena.alloc(hir::Lit { node: LitKind::Bool(true), span }), - ), - span, - }) - }); - - let default_ac = self.arena.alloc(hir::AnonConst { - def_id: anon_const_did, - hir_id: const_id, - body: const_body, - span, - }); - let default_ct = self.arena.alloc(hir::ConstArg { - hir_id: self.next_id(), - kind: hir::ConstArgKind::Anon(default_ac), - is_desugared_from_effects: false, - }); - let param = hir::GenericParam { - def_id, - hir_id, - name: hir::ParamName::Plain(Ident { name: sym::host, span }), - span, - kind: hir::GenericParamKind::Const { - ty: self.arena.alloc(self.ty( - span, - hir::TyKind::Path(hir::QPath::Resolved( - None, - self.arena.alloc(hir::Path { - res: Res::PrimTy(hir::PrimTy::Bool), - span, - segments: self.arena.alloc_from_iter([hir::PathSegment { - ident: Ident { name: sym::bool, span }, - hir_id: bool_id, - res: Res::PrimTy(hir::PrimTy::Bool), - args: None, - infer_args: false, - }]), - }), - )), - )), - default: Some(default_ct), - is_host_effect: true, - synthetic: true, - }, - colon_span: None, - pure_wrt_drop: false, - source: hir::GenericParamSource::Generics, - }; - - params.push(param); - } - let lowered_generics = self.arena.alloc(hir::Generics { params: self.arena.alloc_from_iter(params), predicates: self.arena.alloc_from_iter(predicates), diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 00eafeb4d84..34b8137aea8 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -154,17 +154,10 @@ struct LoweringContext<'a, 'hir> { /// defined on the TAIT, so we have type Foo<'a1> = ... and we establish a mapping in this /// field from the original parameter 'a to the new parameter 'a1. generics_def_id_map: Vec<LocalDefIdMap<LocalDefId>>, - - host_param_id: Option<LocalDefId>, - ast_index: &'a IndexSlice<LocalDefId, AstOwner<'a>>, } impl<'a, 'hir> LoweringContext<'a, 'hir> { - fn new( - tcx: TyCtxt<'hir>, - resolver: &'a mut ResolverAstLowering, - ast_index: &'a IndexSlice<LocalDefId, AstOwner<'a>>, - ) -> Self { + fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self { Self { // Pseudo-globals. tcx, @@ -193,7 +186,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { impl_trait_defs: Vec::new(), impl_trait_bounds: Vec::new(), allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(), - allow_gen_future: if tcx.features().async_fn_track_caller { + allow_gen_future: if tcx.features().async_fn_track_caller() { [sym::gen_future, sym::closure_track_caller].into() } else { [sym::gen_future].into() @@ -204,8 +197,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // interact with `gen`/`async gen` blocks allow_async_iterator: [sym::gen_future, sym::async_iterator].into(), generics_def_id_map: Default::default(), - host_param_id: None, - ast_index, } } @@ -268,8 +259,8 @@ impl ResolverAstLowering { /// /// The extra lifetimes that appear from the parenthesized `Fn`-trait desugaring /// should appear at the enclosing `PolyTraitRef`. - fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> { - self.extra_lifetime_params_map.remove(&id).unwrap_or_default() + fn extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> { + self.extra_lifetime_params_map.get(&id).cloned().unwrap_or_default() } } @@ -885,7 +876,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let mut generic_params: Vec<_> = self .lower_generic_params_mut(generic_params, hir::GenericParamSource::Binder) .collect(); - let extra_lifetimes = self.resolver.take_extra_lifetime_params(binder); + let extra_lifetimes = self.resolver.extra_lifetime_params(binder); debug!(?extra_lifetimes); generic_params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| { self.lifetime_res_to_generic_param(ident, node_id, res, hir::GenericParamSource::Binder) @@ -1035,7 +1026,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: data.inputs_span, }) }; - if !self.tcx.features().return_type_notation + if !self.tcx.features().return_type_notation() && self.tcx.sess.is_nightly_build() { add_feature_diagnostics( @@ -1160,7 +1151,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(lt)), ast::GenericArg::Type(ty) => { match &ty.kind { - TyKind::Infer if self.tcx.features().generic_arg_infer => { + TyKind::Infer if self.tcx.features().generic_arg_infer() => { return GenericArg::Infer(hir::InferArg { hir_id: self.lower_node_id(ty.id), span: self.lower_span(ty.span), @@ -1495,68 +1486,31 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // frequently opened issues show. let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None); - let captured_lifetimes_to_duplicate = if let Some(args) = - // We only look for one `use<...>` syntax since we syntactially reject more than one. - bounds.iter().find_map( - |bound| match bound { - ast::GenericBound::Use(a, _) => Some(a), - _ => None, - }, - ) { - // We'll actually validate these later on; all we need is the list of - // lifetimes to duplicate during this portion of lowering. - args.iter() - .filter_map(|arg| match arg { - PreciseCapturingArg::Lifetime(lt) => Some(*lt), - PreciseCapturingArg::Arg(..) => None, - }) - // Add in all the lifetimes mentioned in the bounds. We will error - // them out later, but capturing them here is important to make sure - // they actually get resolved in resolve_bound_vars. - .chain(lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)) - .collect() - } else { - match origin { - hir::OpaqueTyOrigin::TyAlias { .. } => { - // type alias impl trait and associated type position impl trait were - // decided to capture all in-scope lifetimes, which we collect for - // all opaques during resolution. - self.resolver - .take_extra_lifetime_params(opaque_ty_node_id) - .into_iter() - .map(|(ident, id, _)| Lifetime { id, ident }) - .collect() - } - hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => { - if in_trait_or_impl.is_some() - || self.tcx.features().lifetime_capture_rules_2024 - || span.at_least_rust_2024() - { - // return-position impl trait in trait was decided to capture all - // in-scope lifetimes, which we collect for all opaques during resolution. - self.resolver - .take_extra_lifetime_params(opaque_ty_node_id) - .into_iter() - .map(|(ident, id, _)| Lifetime { id, ident }) - .collect() - } else { - // in fn return position, like the `fn test<'a>() -> impl Debug + 'a` - // example, we only need to duplicate lifetimes that appear in the - // bounds, since those are the only ones that are captured by the opaque. - lifetime_collector::lifetimes_in_bounds(self.resolver, bounds) - } - } - hir::OpaqueTyOrigin::AsyncFn { .. } => { - unreachable!("should be using `lower_async_fn_ret_ty`") - } + // Whether this opaque always captures lifetimes in scope. + // Right now, this is all RPITIT and TAITs, and when `lifetime_capture_rules_2024` + // is enabled. We don't check the span of the edition, since this is done + // on a per-opaque basis to account for nested opaques. + let always_capture_in_scope = match origin { + _ if self.tcx.features().lifetime_capture_rules_2024() => true, + hir::OpaqueTyOrigin::TyAlias { .. } => true, + hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => in_trait_or_impl.is_some(), + hir::OpaqueTyOrigin::AsyncFn { .. } => { + unreachable!("should be using `lower_coroutine_fn_ret_ty`") } }; + let captured_lifetimes_to_duplicate = lifetime_collector::lifetimes_for_opaque( + self.resolver, + always_capture_in_scope, + opaque_ty_node_id, + bounds, + span, + ); debug!(?captured_lifetimes_to_duplicate); // Feature gate for RPITIT + use<..> match origin { rustc_hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl: Some(_), .. } => { - if !self.tcx.features().precise_capturing_in_traits + if !self.tcx.features().precise_capturing_in_traits() && let Some(span) = bounds.iter().find_map(|bound| match *bound { ast::GenericBound::Use(_, span) => Some(span), _ => None, @@ -1920,7 +1874,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let captured_lifetimes = self .resolver - .take_extra_lifetime_params(opaque_ty_node_id) + .extra_lifetime_params(opaque_ty_node_id) .into_iter() .map(|(ident, id, _)| Lifetime { id, ident }) .collect(); @@ -1993,7 +1947,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::GenericBound::Trait(hir::PolyTraitRef { bound_generic_params: &[], - modifiers: hir::TraitBoundModifier::None, + modifiers: hir::TraitBoundModifiers::NONE, trait_ref: hir::TraitRef { path: self.make_lang_item_path(trait_lang_item, opaque_ty_span, Some(bound_args)), hir_ref_id: self.next_id(), @@ -2091,11 +2045,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { param: &GenericParam, source: hir::GenericParamSource, ) -> hir::GenericParam<'hir> { - let (name, kind) = self.lower_generic_param_kind( - param, - source, - attr::contains_name(¶m.attrs, sym::rustc_runtime), - ); + let (name, kind) = self.lower_generic_param_kind(param, source); let hir_id = self.lower_node_id(param.id); self.lower_attrs(hir_id, ¶m.attrs); @@ -2115,7 +2065,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, param: &GenericParam, source: hir::GenericParamSource, - is_host_effect: bool, ) -> (hir::ParamName, hir::GenericParamKind<'hir>) { match ¶m.kind { GenericParamKind::Lifetime => { @@ -2181,7 +2130,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ( hir::ParamName::Plain(self.lower_ident(param.ident)), - hir::GenericParamKind::Const { ty, default, is_host_effect, synthetic: false }, + hir::GenericParamKind::Const { ty, default, synthetic: false }, ) } } @@ -2307,7 +2256,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_array_length(&mut self, c: &AnonConst) -> hir::ArrayLen<'hir> { match c.value.kind { ExprKind::Underscore => { - if self.tcx.features().generic_arg_infer { + if self.tcx.features().generic_arg_infer() { hir::ArrayLen::Infer(hir::InferArg { hir_id: self.lower_node_id(c.id), span: self.lower_span(c.value.span), @@ -2482,22 +2431,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_trait_bound_modifiers( &mut self, modifiers: TraitBoundModifiers, - ) -> hir::TraitBoundModifier { - // Invalid modifier combinations will cause an error during AST validation. - // Arbitrarily pick a placeholder for them to make compilation proceed. - match (modifiers.constness, modifiers.polarity) { - (BoundConstness::Never, BoundPolarity::Positive) => hir::TraitBoundModifier::None, - (_, BoundPolarity::Maybe(_)) => hir::TraitBoundModifier::Maybe, - (BoundConstness::Never, BoundPolarity::Negative(_)) => { - if self.tcx.features().negative_bounds { - hir::TraitBoundModifier::Negative - } else { - hir::TraitBoundModifier::None - } - } - (BoundConstness::Always(_), _) => hir::TraitBoundModifier::Const, - (BoundConstness::Maybe(_), _) => hir::TraitBoundModifier::MaybeConst, - } + ) -> hir::TraitBoundModifiers { + hir::TraitBoundModifiers { constness: modifiers.constness, polarity: modifiers.polarity } } // Helper methods for building HIR. @@ -2663,7 +2598,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { Res::Def(DefKind::Trait | DefKind::TraitAlias, _) => { let principal = hir::PolyTraitRef { bound_generic_params: &[], - modifiers: hir::TraitBoundModifier::None, + modifiers: hir::TraitBoundModifiers::NONE, trait_ref: hir::TraitRef { path, hir_ref_id: hir_id }, span: self.lower_span(span), }; diff --git a/compiler/rustc_ast_lowering/src/lifetime_collector.rs b/compiler/rustc_ast_lowering/src/lifetime_collector.rs index fe64160fb4d..8d47c856bdd 100644 --- a/compiler/rustc_ast_lowering/src/lifetime_collector.rs +++ b/compiler/rustc_ast_lowering/src/lifetime_collector.rs @@ -1,5 +1,7 @@ use rustc_ast::visit::{self, BoundKind, LifetimeCtxt, Visitor}; -use rustc_ast::{GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind}; +use rustc_ast::{ + GenericBound, GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind, +}; use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def::{DefKind, LifetimeRes, Res}; use rustc_middle::span_bug; @@ -10,14 +12,41 @@ use rustc_span::symbol::{Ident, kw}; use super::ResolverAstLoweringExt; struct LifetimeCollectVisitor<'ast> { - resolver: &'ast ResolverAstLowering, + resolver: &'ast mut ResolverAstLowering, + always_capture_in_scope: bool, current_binders: Vec<NodeId>, collected_lifetimes: FxIndexSet<Lifetime>, } impl<'ast> LifetimeCollectVisitor<'ast> { - fn new(resolver: &'ast ResolverAstLowering) -> Self { - Self { resolver, current_binders: Vec::new(), collected_lifetimes: FxIndexSet::default() } + fn new(resolver: &'ast mut ResolverAstLowering, always_capture_in_scope: bool) -> Self { + Self { + resolver, + always_capture_in_scope, + current_binders: Vec::new(), + collected_lifetimes: FxIndexSet::default(), + } + } + + fn visit_opaque(&mut self, opaque_ty_node_id: NodeId, bounds: &'ast GenericBounds, span: Span) { + // If we're edition 2024 or within a TAIT or RPITIT, *and* there is no + // `use<>` statement to override the default capture behavior, then + // capture all of the in-scope lifetimes. + if (self.always_capture_in_scope || span.at_least_rust_2024()) + && bounds.iter().all(|bound| !matches!(bound, GenericBound::Use(..))) + { + for (ident, id, _) in self.resolver.extra_lifetime_params(opaque_ty_node_id) { + self.record_lifetime_use(Lifetime { id, ident }); + } + } + + // We also recurse on the bounds to make sure we capture all the lifetimes + // mentioned in the bounds. These may disagree with the `use<>` list, in which + // case we will error on these later. We will also recurse to visit any + // nested opaques, which may *implicitly* capture lifetimes. + for bound in bounds { + self.visit_param_bound(bound, BoundKind::Bound); + } } fn record_lifetime_use(&mut self, lifetime: Lifetime) { @@ -99,6 +128,9 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> { self.record_elided_anchor(t.id, t.span); visit::walk_ty(self, t); } + TyKind::ImplTrait(opaque_ty_node_id, bounds) => { + self.visit_opaque(*opaque_ty_node_id, bounds, t.span) + } _ => { visit::walk_ty(self, t); } @@ -106,13 +138,14 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> { } } -pub(crate) fn lifetimes_in_bounds( - resolver: &ResolverAstLowering, +pub(crate) fn lifetimes_for_opaque( + resolver: &mut ResolverAstLowering, + always_capture_in_scope: bool, + opaque_ty_node_id: NodeId, bounds: &GenericBounds, + span: Span, ) -> FxIndexSet<Lifetime> { - let mut visitor = LifetimeCollectVisitor::new(resolver); - for bound in bounds { - visitor.visit_param_bound(bound, BoundKind::Bound); - } + let mut visitor = LifetimeCollectVisitor::new(resolver, always_capture_in_scope); + visitor.visit_opaque(opaque_ty_node_id, bounds, span); visitor.collected_lifetimes } diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index e60488fdc8c..258655de486 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -268,7 +268,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: data.inputs_span, }) }; - if !self.tcx.features().return_type_notation + if !self.tcx.features().return_type_notation() && self.tcx.sess.is_nightly_build() { add_feature_diagnostics( @@ -496,7 +496,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // // disallowed --^^^^^^^^^^ allowed --^^^^^^^^^^ // ``` FnRetTy::Ty(ty) if matches!(itctx, ImplTraitContext::OpaqueTy { .. }) => { - if self.tcx.features().impl_trait_in_fn_trait_return { + if self.tcx.features().impl_trait_in_fn_trait_return() { self.lower_ty(ty, itctx) } else { self.lower_ty( diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 20a4f2120dc..1d149e91b85 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -249,7 +249,7 @@ impl<'a> AstValidator<'a> { } fn visit_struct_field_def(&mut self, field: &'a FieldDef) { - if let Some(ident) = field.ident + if let Some(ref ident) = field.ident && ident.name == kw::Underscore { self.visit_vis(&field.vis); @@ -295,7 +295,8 @@ impl<'a> AstValidator<'a> { return; }; - let make_impl_const_sugg = if self.features.const_trait_impl + let const_trait_impl = self.features.const_trait_impl(); + let make_impl_const_sugg = if const_trait_impl && let TraitOrTraitImpl::TraitImpl { constness: Const::No, polarity: ImplPolarity::Positive, @@ -308,13 +309,12 @@ impl<'a> AstValidator<'a> { None }; - let make_trait_const_sugg = if self.features.const_trait_impl - && let TraitOrTraitImpl::Trait { span, constness: None } = parent - { - Some(span.shrink_to_lo()) - } else { - None - }; + let make_trait_const_sugg = + if const_trait_impl && let TraitOrTraitImpl::Trait { span, constness: None } = parent { + Some(span.shrink_to_lo()) + } else { + None + }; let parent_constness = parent.constness(); self.dcx().emit_err(errors::TraitFnConst { @@ -899,7 +899,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } this.visit_vis(&item.vis); - this.visit_ident(item.ident); + this.visit_ident(&item.ident); let disallowed = matches!(constness, Const::No) .then(|| TildeConstReason::TraitImpl { span: item.span }); this.with_tilde_const(disallowed, |this| this.visit_generics(generics)); @@ -953,7 +953,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } this.visit_vis(&item.vis); - this.visit_ident(item.ident); + this.visit_ident(&item.ident); this.with_tilde_const( Some(TildeConstReason::Impl { span: item.span }), |this| this.visit_generics(generics), @@ -991,7 +991,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } self.visit_vis(&item.vis); - self.visit_ident(item.ident); + self.visit_ident(&item.ident); let kind = FnKind::Fn(FnCtxt::Free, item.ident, sig, &item.vis, generics, body.as_deref()); self.visit_fn(kind, item.span, item.id); @@ -1058,7 +1058,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound // context for the supertraits. this.visit_vis(&item.vis); - this.visit_ident(item.ident); + this.visit_ident(&item.ident); let disallowed = is_const_trait .is_none() .then(|| TildeConstReason::Trait { span: item.span }); @@ -1085,7 +1085,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ItemKind::Struct(vdata, generics) => match vdata { VariantData::Struct { fields, .. } => { self.visit_vis(&item.vis); - self.visit_ident(item.ident); + self.visit_ident(&item.ident); self.visit_generics(generics); // Permit `Anon{Struct,Union}` as field type. walk_list!(self, visit_struct_field_def, fields); @@ -1101,7 +1101,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { match vdata { VariantData::Struct { fields, .. } => { self.visit_vis(&item.vis); - self.visit_ident(item.ident); + self.visit_ident(&item.ident); self.visit_generics(generics); // Permit `Anon{Struct,Union}` as field type. walk_list!(self, visit_struct_field_def, fields); @@ -1145,7 +1145,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } self.check_type_no_bounds(bounds, "this context"); - if self.features.lazy_type_alias { + if self.features.lazy_type_alias() { if let Err(err) = self.check_type_alias_where_clause_location(ty_alias) { self.dcx().emit_err(err); } @@ -1286,7 +1286,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { GenericBound::Trait(trait_ref) => { match (ctxt, trait_ref.modifiers.constness, trait_ref.modifiers.polarity) { (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) - if !self.features.more_maybe_bounds => + if !self.features.more_maybe_bounds() => { self.sess .create_feature_err( @@ -1299,7 +1299,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .emit(); } (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) - if !self.features.more_maybe_bounds => + if !self.features.more_maybe_bounds() => { self.sess .create_feature_err( @@ -1521,7 +1521,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { || matches!(sig.header.constness, Const::Yes(_)) => { self.visit_vis(&item.vis); - self.visit_ident(item.ident); + self.visit_ident(&item.ident); let kind = FnKind::Fn( FnCtxt::Assoc(ctxt), item.ident, diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index f2773dcdc60..d646150a620 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -15,13 +15,13 @@ use crate::errors; /// The common case. macro_rules! gate { ($visitor:expr, $feature:ident, $span:expr, $explain:expr) => {{ - if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) { + if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) { #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable feature_err(&$visitor.sess, sym::$feature, $span, $explain).emit(); } }}; ($visitor:expr, $feature:ident, $span:expr, $explain:expr, $help:expr) => {{ - if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) { + if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) { // FIXME: make this translatable #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] @@ -43,7 +43,7 @@ macro_rules! gate_alt { /// The case involving a multispan. macro_rules! gate_multi { ($visitor:expr, $feature:ident, $spans:expr, $explain:expr) => {{ - if !$visitor.features.$feature { + if !$visitor.features.$feature() { let spans: Vec<_> = $spans.filter(|span| !span.allows_unstable(sym::$feature)).collect(); if !spans.is_empty() { @@ -56,7 +56,7 @@ macro_rules! gate_multi { /// The legacy case. macro_rules! gate_legacy { ($visitor:expr, $feature:ident, $span:expr, $explain:expr) => {{ - if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) { + if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) { feature_warn(&$visitor.sess, sym::$feature, $span, $explain); } }}; @@ -150,7 +150,7 @@ impl<'a> PostExpansionVisitor<'a> { // FIXME(non_lifetime_binders): Const bound params are pretty broken. // Let's keep users from using this feature accidentally. - if self.features.non_lifetime_binders { + if self.features.non_lifetime_binders() { let const_param_spans: Vec<_> = params .iter() .filter_map(|param| match param.kind { @@ -210,7 +210,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } // Emit errors for non-staged-api crates. - if !self.features.staged_api { + if !self.features.staged_api() { if attr.has_name(sym::unstable) || attr.has_name(sym::stable) || attr.has_name(sym::rustc_const_unstable) @@ -470,7 +470,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { // Limit `min_specialization` to only specializing functions. gate_alt!( &self, - self.features.specialization || (is_fn && self.features.min_specialization), + self.features.specialization() || (is_fn && self.features.min_specialization()), sym::specialization, i.span, "specialization is unstable" @@ -548,7 +548,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(return_type_notation, "return type notation is experimental"); gate_all!(pin_ergonomics, "pinned reference syntax is experimental"); - if !visitor.features.never_patterns { + if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { for &span in spans { if span.allows_unstable(sym::never_patterns) { @@ -572,7 +572,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { } } - if !visitor.features.negative_bounds { + if !visitor.features.negative_bounds() { for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() { sess.dcx().emit_err(errors::NegativeBoundUnsupported { span }); } @@ -600,59 +600,61 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { } fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) { - // checks if `#![feature]` has been used to enable any lang feature - // does not check the same for lib features unless there's at least one - // declared lang feature - if !sess.opts.unstable_features.is_nightly_build() { - if features.declared_features.is_empty() { - return; - } - for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) { - let mut err = errors::FeatureOnNonNightly { - span: attr.span, - channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"), - stable_features: vec![], - sugg: None, - }; - - let mut all_stable = true; - for ident in - attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident()) - { - let name = ident.name; - let stable_since = features - .declared_lang_features - .iter() - .flat_map(|&(feature, _, since)| if feature == name { since } else { None }) - .next(); - if let Some(since) = stable_since { - err.stable_features.push(errors::StableFeature { name, since }); - } else { - all_stable = false; - } - } - if all_stable { - err.sugg = Some(attr.span); + // checks if `#![feature]` has been used to enable any feature. + if sess.opts.unstable_features.is_nightly_build() { + return; + } + if features.enabled_features().is_empty() { + return; + } + let mut errored = false; + for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) { + // `feature(...)` used on non-nightly. This is definitely an error. + let mut err = errors::FeatureOnNonNightly { + span: attr.span, + channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"), + stable_features: vec![], + sugg: None, + }; + + let mut all_stable = true; + for ident in attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident()) { + let name = ident.name; + let stable_since = features + .enabled_lang_features() + .iter() + .find(|feat| feat.gate_name == name) + .map(|feat| feat.stable_since) + .flatten(); + if let Some(since) = stable_since { + err.stable_features.push(errors::StableFeature { name, since }); + } else { + all_stable = false; } - sess.dcx().emit_err(err); } + if all_stable { + err.sugg = Some(attr.span); + } + sess.dcx().emit_err(err); + errored = true; } + // Just make sure we actually error if anything is listed in `enabled_features`. + assert!(errored); } fn check_incompatible_features(sess: &Session, features: &Features) { - let declared_features = features - .declared_lang_features - .iter() - .copied() - .map(|(name, span, _)| (name, span)) - .chain(features.declared_lib_features.iter().copied()); + let enabled_lang_features = + features.enabled_lang_features().iter().map(|feat| (feat.gate_name, feat.attr_sp)); + let enabled_lib_features = + features.enabled_lib_features().iter().map(|feat| (feat.gate_name, feat.attr_sp)); + let enabled_features = enabled_lang_features.chain(enabled_lib_features); for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES .iter() - .filter(|&&(f1, f2)| features.active(f1) && features.active(f2)) + .filter(|(f1, f2)| features.enabled(*f1) && features.enabled(*f2)) { - if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) { - if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2) + if let Some((f1_name, f1_span)) = enabled_features.clone().find(|(name, _)| name == f1) { + if let Some((f2_name, f2_span)) = enabled_features.clone().find(|(name, _)| name == f2) { let spans = vec![f1_span, f2_span]; sess.dcx().emit_err(errors::IncompatibleFeatures { @@ -671,10 +673,11 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) { } // Ban GCE with the new solver, because it does not implement GCE correctly. - if let Some(&(_, gce_span, _)) = features - .declared_lang_features + if let Some(gce_span) = features + .enabled_lang_features() .iter() - .find(|&&(feat, _, _)| feat == sym::generic_const_exprs) + .find(|feat| feat.gate_name == sym::generic_const_exprs) + .map(|feat| feat.attr_sp) { sess.dcx().emit_err(errors::IncompatibleFeatures { spans: vec![gce_span], diff --git a/compiler/rustc_ast_passes/src/node_count.rs b/compiler/rustc_ast_passes/src/node_count.rs index e22e99f6e4d..9e7204df8ad 100644 --- a/compiler/rustc_ast_passes/src/node_count.rs +++ b/compiler/rustc_ast_passes/src/node_count.rs @@ -16,7 +16,7 @@ impl NodeCounter { } impl<'ast> Visitor<'ast> for NodeCounter { - fn visit_ident(&mut self, _ident: Ident) { + fn visit_ident(&mut self, _ident: &Ident) { self.count += 1; } fn visit_foreign_item(&mut self, i: &ForeignItem) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 2cdec2138ad..1e18f0779f0 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -627,6 +627,13 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) { self.ibox(0); + match item.unsafety { + ast::Safety::Unsafe(_) => { + self.word("unsafe"); + self.popen(); + } + ast::Safety::Default | ast::Safety::Safe(_) => {} + } match &item.args { AttrArgs::Delimited(DelimArgs { dspan: _, delim, tokens }) => self.print_mac_common( Some(MacHeader::Path(&item.path)), @@ -655,6 +662,10 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere self.word(token_str); } } + match item.unsafety { + ast::Safety::Unsafe(_) => self.pclose(), + ast::Safety::Default | ast::Safety::Safe(_) => {} + } self.end(); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 8217b6df5b4..8279c66836c 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -38,7 +38,6 @@ impl<'a> State<'a> { self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); } ast::ForeignItemKind::Static(box ast::StaticItem { ty, mutability, expr, safety }) => { - self.print_safety(*safety); self.print_item_const( ident, Some(*mutability), @@ -46,6 +45,7 @@ impl<'a> State<'a> { ty, expr.as_deref(), vis, + *safety, ast::Defaultness::Final, ) } @@ -84,10 +84,12 @@ impl<'a> State<'a> { ty: &ast::Ty, body: Option<&ast::Expr>, vis: &ast::Visibility, + safety: ast::Safety, defaultness: ast::Defaultness, ) { self.head(""); self.print_visibility(vis); + self.print_safety(safety); self.print_defaultness(defaultness); let leading = match mutbl { None => "const", @@ -181,6 +183,7 @@ impl<'a> State<'a> { ty, body.as_deref(), &item.vis, + ast::Safety::Default, ast::Defaultness::Final, ); } @@ -192,6 +195,7 @@ impl<'a> State<'a> { ty, expr.as_deref(), &item.vis, + ast::Safety::Default, *defaultness, ); } @@ -549,6 +553,7 @@ impl<'a> State<'a> { ty, expr.as_deref(), vis, + ast::Safety::Default, *defaultness, ); } diff --git a/compiler/rustc_attr/messages.ftl b/compiler/rustc_attr/messages.ftl index adabf18ca85..235ab7572c4 100644 --- a/compiler/rustc_attr/messages.ftl +++ b/compiler/rustc_attr/messages.ftl @@ -91,6 +91,9 @@ attr_non_ident_feature = attr_rustc_allowed_unstable_pairing = `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute +attr_rustc_const_stable_indirect_pairing = + `const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied + attr_rustc_promotable_pairing = `rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index bbb17497684..6af75bc94bb 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -16,9 +16,9 @@ use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::UNEXPECTED_CFGS; use rustc_session::parse::feature_err; use rustc_session::{RustcVersion, Session}; -use rustc_span::Span; use rustc_span::hygiene::Transparency; use rustc_span::symbol::{Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, Span}; use crate::fluent_generated; use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; @@ -92,7 +92,11 @@ impl Stability { #[derive(HashStable_Generic)] pub struct ConstStability { pub level: StabilityLevel, - pub feature: Symbol, + /// This can be `None` for functions that do not have an explicit const feature. + /// We still track them for recursive const stability checks. + pub feature: Option<Symbol>, + /// This is true iff the `const_stable_indirect` attribute is present. + pub const_stable_indirect: bool, /// whether the function has a `#[rustc_promotable]` attribute pub promotable: bool, } @@ -268,17 +272,23 @@ pub fn find_stability( /// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable` /// attributes in `attrs`. Returns `None` if no stability attributes are found. +/// +/// `is_const_fn` indicates whether this is a function marked as `const`. It will always +/// be false for intrinsics in an `extern` block! pub fn find_const_stability( sess: &Session, attrs: &[Attribute], item_sp: Span, + is_const_fn: bool, ) -> Option<(ConstStability, Span)> { let mut const_stab: Option<(ConstStability, Span)> = None; let mut promotable = false; + let mut const_stable_indirect = None; for attr in attrs { match attr.name_or_empty() { sym::rustc_promotable => promotable = true, + sym::rustc_const_stable_indirect => const_stable_indirect = Some(attr.span), sym::rustc_const_unstable => { if const_stab.is_some() { sess.dcx() @@ -287,8 +297,15 @@ pub fn find_const_stability( } if let Some((feature, level)) = parse_unstability(sess, attr) { - const_stab = - Some((ConstStability { level, feature, promotable: false }, attr.span)); + const_stab = Some(( + ConstStability { + level, + feature: Some(feature), + const_stable_indirect: false, + promotable: false, + }, + attr.span, + )); } } sym::rustc_const_stable => { @@ -298,15 +315,22 @@ pub fn find_const_stability( break; } if let Some((feature, level)) = parse_stability(sess, attr) { - const_stab = - Some((ConstStability { level, feature, promotable: false }, attr.span)); + const_stab = Some(( + ConstStability { + level, + feature: Some(feature), + const_stable_indirect: false, + promotable: false, + }, + attr.span, + )); } } _ => {} } } - // Merge the const-unstable info into the stability info + // Merge promotable and not_exposed_on_stable into stability info if promotable { match &mut const_stab { Some((stab, _)) => stab.promotable = promotable, @@ -317,6 +341,46 @@ pub fn find_const_stability( } } } + if const_stable_indirect.is_some() { + match &mut const_stab { + Some((stab, _)) => { + if stab.is_const_unstable() { + stab.const_stable_indirect = true; + } else { + _ = sess.dcx().emit_err(session_diagnostics::RustcConstStableIndirectPairing { + span: item_sp, + }) + } + } + _ => { + // We ignore the `#[rustc_const_stable_indirect]` here, it should be picked up by + // the `default_const_unstable` logic. + } + } + } + // Make sure if `const_stable_indirect` is present, that is recorded. Also make sure all `const + // fn` get *some* marker, since we are a staged_api crate and therefore will do recursive const + // stability checks for them. We need to do this because the default for whether an unmarked + // function enforces recursive stability differs between staged-api crates and force-unmarked + // crates: in force-unmarked crates, only functions *explicitly* marked `const_stable_indirect` + // enforce recursive stability. Therefore when `lookup_const_stability` is `None`, we have to + // assume the function does not have recursive stability. All functions that *do* have recursive + // stability must explicitly record this, and so that's what we do for all `const fn` in a + // staged_api crate. + if (is_const_fn || const_stable_indirect.is_some()) && const_stab.is_none() { + let c = ConstStability { + feature: None, + const_stable_indirect: const_stable_indirect.is_some(), + promotable: false, + level: StabilityLevel::Unstable { + reason: UnstableReason::Default, + issue: None, + is_soft: false, + implied_by: None, + }, + }; + const_stab = Some((c, const_stable_indirect.unwrap_or(DUMMY_SP))); + } const_stab } @@ -619,11 +683,11 @@ pub fn eval_condition( // we can't use `try_gate_cfg` as symbols don't differentiate between `r#true` // and `true`, and we want to keep the former working without feature gate gate_cfg( - &(( + &( if *b { kw::True } else { kw::False }, sym::cfg_boolean_literals, - |features: &Features| features.cfg_boolean_literals, - )), + |features: &Features| features.cfg_boolean_literals(), + ), cfg.span(), sess, features, @@ -711,7 +775,7 @@ pub fn eval_condition( } sym::target => { if let Some(features) = features - && !features.cfg_target_compact + && !features.cfg_target_compact() { feature_err( sess, @@ -831,7 +895,7 @@ pub fn find_deprecation( attrs: &[Attribute], ) -> Option<(Deprecation, Span)> { let mut depr: Option<(Deprecation, Span)> = None; - let is_rustc = features.staged_api; + let is_rustc = features.staged_api(); 'outer: for attr in attrs { if !attr.has_name(sym::deprecated) { @@ -891,7 +955,7 @@ pub fn find_deprecation( } } sym::suggestion => { - if !features.deprecated_suggestion { + if !features.deprecated_suggestion() { sess.dcx().emit_err( session_diagnostics::DeprecatedItemSuggestion { span: mi.span, @@ -909,7 +973,7 @@ pub fn find_deprecation( sess.dcx().emit_err(session_diagnostics::UnknownMetaItem { span: meta.span(), item: pprust::path_to_string(&mi.path), - expected: if features.deprecated_suggestion { + expected: if features.deprecated_suggestion() { &["since", "note", "suggestion"] } else { &["since", "note"] diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr/src/session_diagnostics.rs index 626840aa6a3..9d08a9f5754 100644 --- a/compiler/rustc_attr/src/session_diagnostics.rs +++ b/compiler/rustc_attr/src/session_diagnostics.rs @@ -319,6 +319,13 @@ pub(crate) struct RustcPromotablePairing { } #[derive(Diagnostic)] +#[diag(attr_rustc_const_stable_indirect_pairing)] +pub(crate) struct RustcConstStableIndirectPairing { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(attr_rustc_allowed_unstable_pairing, code = E0789)] pub(crate) struct RustcAllowedUnstablePairing { #[primary_span] diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 2437a43bd5a..2fa752384a1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -65,6 +65,7 @@ impl<'tcx> UniverseInfo<'tcx> { UniverseInfoInner::RelateTys { expected, found } => { let err = mbcx.infcx.err_ctxt().report_mismatched_types( &cause, + mbcx.param_env, expected, found, TypeError::RegionsPlaceholderMismatch, @@ -480,12 +481,11 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>( .try_report_from_nll() .or_else(|| { if let SubregionOrigin::Subtype(trace) = cause { - Some( - infcx.err_ctxt().report_and_explain_type_error( - *trace, - TypeError::RegionsPlaceholderMismatch, - ), - ) + Some(infcx.err_ctxt().report_and_explain_type_error( + *trace, + infcx.tcx.param_env(generic_param_scope), + TypeError::RegionsPlaceholderMismatch, + )) } else { None } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 20ecc665b1e..315851729b1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -959,13 +959,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { None } } - hir::ExprKind::MethodCall(_, _, args, span) => { - if let Some(def_id) = typeck_results.type_dependent_def_id(*hir_id) { - Some((def_id, *span, *args)) - } else { - None - } - } + hir::ExprKind::MethodCall(_, _, args, span) => typeck_results + .type_dependent_def_id(*hir_id) + .map(|def_id| (def_id, *span, *args)), _ => None, } }; @@ -1146,6 +1142,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } // don't create labels for compiler-generated spans Some(_) => None, + // don't create labels for the span not from user's code + None if opt_assignment_rhs_span + .is_some_and(|span| self.infcx.tcx.sess.source_map().is_imported(span)) => + { + None + } None => { let (has_sugg, decl_span, sugg) = if name != kw::SelfLower { suggest_ampmut( @@ -1198,18 +1200,21 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { sugg.push(s); } - err.multipart_suggestion_verbose( - format!( - "consider changing this to be a mutable {pointer_desc}{}", - if is_trait_sig { - " in the `impl` method and the `trait` definition" - } else { - "" - } - ), - sugg, - Applicability::MachineApplicable, - ); + if sugg.iter().all(|(span, _)| !self.infcx.tcx.sess.source_map().is_imported(*span)) + { + err.multipart_suggestion_verbose( + format!( + "consider changing this to be a mutable {pointer_desc}{}", + if is_trait_sig { + " in the `impl` method and the `trait` definition" + } else { + "" + } + ), + sugg, + Applicability::MachineApplicable, + ); + } } Some((false, err_label_span, message, _)) => { let def_id = self.body.source.def_id(); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index cbf8aa313c5..698fdafc936 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -187,10 +187,10 @@ fn do_mir_borrowck<'tcx>( let location_table = LocationTable::new(body); - let move_data = MoveData::gather_moves(body, tcx, param_env, |_| true); + let move_data = MoveData::gather_moves(body, tcx, |_| true); let promoted_move_data = promoted .iter_enumerated() - .map(|(idx, body)| (idx, MoveData::gather_moves(body, tcx, param_env, |_| true))); + .map(|(idx, body)| (idx, MoveData::gather_moves(body, tcx, |_| true))); let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) .into_engine(tcx, body) diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index a16c1931a55..abce98265b3 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -360,6 +360,7 @@ fn check_opaque_type_well_formed<'tcx>( .err_ctxt() .report_mismatched_types( &ObligationCause::misc(definition_span, def_id), + param_env, opaque_ty, definition_ty, err, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 238d7d0749a..a544d88e832 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1035,7 +1035,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 + features.unsized_locals() || features.unsized_fn_params() } /// Equate the inferred type and the annotated type for user type annotations @@ -1128,7 +1128,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } let projected_ty = curr_projected_ty.projection_ty_core( tcx, - self.param_env, proj, |this, field, ()| { let ty = this.field_ty(tcx, field); @@ -1919,7 +1918,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // than 1. // If the length is larger than 1, the repeat expression will need to copy the // element, so we require the `Copy` trait. - if len.try_eval_target_usize(tcx, self.param_env).map_or(true, |len| len > 1) { + if len.try_to_target_usize(tcx).is_none_or(|len| len > 1) { match operand { Operand::Copy(..) | Operand::Constant(..) => { // These are always okay: direct use of a const, or a value that can evidently be copied. diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index 71449350985..599b180f879 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -69,7 +69,7 @@ pub(crate) fn expand_assert<'cx>( // If `generic_assert` is enabled, generates rich captured outputs // // FIXME(c410-f3r) See https://github.com/rust-lang/rust/issues/96949 - else if cx.ecfg.features.generic_assert { + else if cx.ecfg.features.generic_assert() { context::Context::new(cx, call_site_span).build(cond_expr, panic_path()) } // If `generic_assert` is not enabled, only outputs a literal "assertion failed: ..." diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 652e6f7740f..d4befd12190 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -222,7 +222,7 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, ' rustc_ast::visit::walk_attribute(self, attr); } fn visit_variant(&mut self, v: &'a rustc_ast::Variant) { - self.visit_ident(v.ident); + self.visit_ident(&v.ident); self.visit_vis(&v.vis); self.visit_variant_data(&v.data); visit_opt!(self, visit_anon_const, &v.disr_expr); diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index d6603af101a..707c36d5046 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -313,14 +313,23 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> { match m { ProcMacro::Derive(cd) => { cx.resolver.declare_proc_macro(cd.id); - cx.expr_call(span, proc_macro_ty_method_path(cx, custom_derive), thin_vec![ - cx.expr_str(span, cd.trait_name), - cx.expr_array_ref( - span, - cd.attrs.iter().map(|&s| cx.expr_str(span, s)).collect::<ThinVec<_>>(), - ), - local_path(cx, cd.function_name), - ]) + // The call needs to use `harness_span` so that the const stability checker + // accepts it. + cx.expr_call( + harness_span, + proc_macro_ty_method_path(cx, custom_derive), + thin_vec![ + cx.expr_str(span, cd.trait_name), + cx.expr_array_ref( + span, + cd.attrs + .iter() + .map(|&s| cx.expr_str(span, s)) + .collect::<ThinVec<_>>(), + ), + local_path(cx, cd.function_name), + ], + ) } ProcMacro::Attr(ca) | ProcMacro::Bang(ca) => { cx.resolver.declare_proc_macro(ca.id); @@ -330,7 +339,9 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> { ProcMacro::Derive(_) => unreachable!(), }; - cx.expr_call(span, proc_macro_ty_method_path(cx, ident), thin_vec![ + // The call needs to use `harness_span` so that the const stability checker + // accepts it. + cx.expr_call(harness_span, proc_macro_ty_method_path(cx, ident), thin_vec![ cx.expr_str(span, ca.function_name.name), local_path(cx, ca.function_name), ]) diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 9fc0318df5d..42c7f5f0dc6 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -47,12 +47,12 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {} 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> {} -#[lang = "receiver"] -pub trait Receiver {} +#[lang = "legacy_receiver"] +pub trait LegacyReceiver {} -impl<T: ?Sized> Receiver for &T {} -impl<T: ?Sized> Receiver for &mut T {} -impl<T: ?Sized> Receiver for Box<T> {} +impl<T: ?Sized> LegacyReceiver for &T {} +impl<T: ?Sized> LegacyReceiver for &mut T {} +impl<T: ?Sized> LegacyReceiver for Box<T> {} #[lang = "copy"] pub unsafe trait Copy {} diff --git a/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch index 646928893e9..3c81b04c0ea 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch @@ -38,7 +38,7 @@ diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index d9de37e..8293fce 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs -@@ -2996,42 +2996,6 @@ atomic_int! { +@@ -2996,44 +2996,6 @@ atomic_int! { 8, u64 AtomicU64 } @@ -52,7 +52,8 @@ index d9de37e..8293fce 100644 - unstable(feature = "integer_atomics", issue = "99069"), - unstable(feature = "integer_atomics", issue = "99069"), - unstable(feature = "integer_atomics", issue = "99069"), -- rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), +- rustc_const_unstable(feature = "integer_atomics", issue = "99069"), +- rustc_const_unstable(feature = "integer_atomics", issue = "99069"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicI128"), - "i128", - "#![feature(integer_atomics)]\n\n", @@ -70,7 +71,8 @@ index d9de37e..8293fce 100644 - unstable(feature = "integer_atomics", issue = "99069"), - unstable(feature = "integer_atomics", issue = "99069"), - unstable(feature = "integer_atomics", issue = "99069"), -- rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), +- rustc_const_unstable(feature = "integer_atomics", issue = "99069"), +- rustc_const_unstable(feature = "integer_atomics", issue = "99069"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicU128"), - "u128", - "#![feature(integer_atomics)]\n\n", diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index f0b78e5d7c6..79d76925df9 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -210,7 +210,6 @@ impl DebugContext { type_names::push_generic_params( tcx, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), args), - enclosing_fn_def_id, &mut name, ); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index cbe411d78d5..e7f9f894381 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -133,6 +133,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( .expect_const() .try_to_valtree() .expect("expected monomorphic const in codegen") + .0 .unwrap_branch(); assert_eq!(x.layout(), y.layout()); @@ -806,8 +807,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => m.load_scalar(fx), ty::Array(elem, len) if matches!(elem.kind(), ty::Uint(ty::UintTy::U8)) - && len.try_eval_target_usize(fx.tcx, ty::ParamEnv::reveal_all()) - == Some(expected_bytes) => + && len + .try_to_target_usize(fx.tcx) + .expect("expected monomorphic const in codegen") + == expected_bytes => { m.force_stack(fx).0.load( fx, @@ -907,8 +910,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => {} ty::Array(elem, len) if matches!(elem.kind(), ty::Uint(ty::UintTy::U8)) - && len.try_eval_target_usize(fx.tcx, ty::ParamEnv::reveal_all()) - == Some(expected_bytes) => {} + && len + .try_to_target_usize(fx.tcx) + .expect("expected monomorphic const in codegen") + == expected_bytes => {} _ => { fx.tcx.dcx().span_fatal( span, diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 5c297ebfadb..336934354e1 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -34,7 +34,9 @@ pub(crate) fn unsized_info<'tcx>( { let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); - if data_a.principal_def_id() == data_b.principal_def_id() { + let b_principal_def_id = data_b.principal_def_id(); + if data_a.principal_def_id() == b_principal_def_id || b_principal_def_id.is_none() { + // A NOP cast that doesn't actually change anything, should be allowed even with invalid vtables. debug_assert!( validate_trivial_unsize(fx.tcx, data_a, data_b), "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}" diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index f47bfdad131..0576b64ef6f 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -44,12 +44,12 @@ impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {} 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, ()> {} -#[lang = "receiver"] -pub trait Receiver {} +#[lang = "legacy_receiver"] +pub trait LegacyReceiver {} -impl<T: ?Sized> Receiver for &T {} -impl<T: ?Sized> Receiver for &mut T {} -impl<T: ?Sized, A: Allocator> Receiver for Box<T, A> {} +impl<T: ?Sized> LegacyReceiver for &T {} +impl<T: ?Sized> LegacyReceiver for &mut T {} +impl<T: ?Sized, A: Allocator> LegacyReceiver for Box<T, A> {} #[lang = "copy"] pub unsafe trait Copy {} diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index b611f9ba8bc..7c52cba096b 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::{Instance, ParamEnv, Ty, TyCtxt}; use rustc_span::Span; use rustc_span::def_id::DefId; use rustc_target::abi::call::FnAbi; -use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, WasmCAbi}; +use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, WasmCAbi, X86Abi}; use crate::common::{SignType, TypeReflection, type_is_pointer}; use crate::context::CodegenCx; @@ -1725,16 +1725,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn fptosi_sat(&mut self, val: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { self.fptoint_sat(true, val, dest_ty) } - - fn instrprof_increment( - &mut self, - _fn_name: RValue<'gcc>, - _hash: RValue<'gcc>, - _num_counters: RValue<'gcc>, - _index: RValue<'gcc>, - ) { - unimplemented!(); - } } impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { @@ -2347,6 +2337,12 @@ impl<'tcx> HasWasmCAbiOpt for Builder<'_, '_, 'tcx> { } } +impl<'tcx> HasX86AbiOpt for Builder<'_, '_, 'tcx> { + fn x86_abi_opt(&self) -> X86Abi { + self.cx.x86_abi_opt() + } +} + pub trait ToGccComp { fn to_gcc_comparison(&self) -> ComparisonOp; } diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs index 9ad2e90122f..65972a03e83 100644 --- a/compiler/rustc_codegen_gcc/src/callee.rs +++ b/compiler/rustc_codegen_gcc/src/callee.rs @@ -98,8 +98,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) // whether we are sharing generics or not. The important thing here is // that the visibility we apply to the declaration is the same one that // has been applied to the definition (wherever that definition may be). - let is_generic = - instance.args.non_erasable_generics(tcx, instance.def_id()).next().is_some(); + let is_generic = instance.args.non_erasable_generics().next().is_some(); if is_generic { // This is a monomorphization. Its expected visibility depends diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 7cb49bf7991..707b35967a6 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -19,7 +19,9 @@ use rustc_session::Session; use rustc_span::source_map::respan; use rustc_span::{DUMMY_SP, Span}; use rustc_target::abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; -use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, TlsModel, WasmCAbi}; +use rustc_target::spec::{ + HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, TlsModel, WasmCAbi, X86Abi, +}; use crate::callee::get_fn; use crate::common::SignType; @@ -538,6 +540,12 @@ impl<'gcc, 'tcx> HasWasmCAbiOpt for CodegenCx<'gcc, 'tcx> { } } +impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> { + fn x86_abi_opt(&self) -> X86Abi { + X86Abi { regparm: self.tcx.sess.opts.unstable_opts.regparm } + } +} + impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 4e1b99fdebf..43dbfafa871 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -76,8 +76,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(), ty::Array(elem, len) if matches!(*elem.kind(), ty::Uint(ty::UintTy::U8)) - && len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all()) - == Some(expected_bytes) => + && len + .try_to_target_usize(bx.tcx) + .expect("expected monomorphic const in codegen") + == expected_bytes => { let place = PlaceRef::alloca(bx, args[0].layout); args[0].val.store(bx, place); @@ -696,8 +698,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( } ty::Array(elem, len) if matches!(*elem.kind(), ty::Uint(ty::UintTy::U8)) - && len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all()) - == Some(expected_bytes) => + && len + .try_to_target_usize(bx.tcx) + .expect("expected monomorphic const in codegen") + == expected_bytes => { // Zero-extend iN to the array length: let ze = bx.zext(result, bx.type_ix(expected_bytes * 8)); diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 183e9ddf8bf..db874afe1ab 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -197,7 +197,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { /// `[T]` becomes `T`, while `str` and `Trait` turn into `i8` - this /// is useful for indexing slices, as `&[T]`'s data pointer is `T*`. /// If the type is an unsized struct, the regular layout is generated, - /// with the inner-most trailing unsized field using the "minimal unit" + /// with the innermost trailing unsized field using the "minimal unit" /// of that field's type - this is useful for taking the address of /// that field and ensuring the struct has the right alignment. fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index dbf5298d64b..8702532c36e 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1165,39 +1165,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size); } - fn instrprof_increment( - &mut self, - fn_name: &'ll Value, - hash: &'ll Value, - num_counters: &'ll Value, - index: &'ll Value, - ) { - debug!( - "instrprof_increment() with args ({:?}, {:?}, {:?}, {:?})", - fn_name, hash, num_counters, index - ); - - let llfn = unsafe { llvm::LLVMRustGetInstrProfIncrementIntrinsic(self.cx().llmod) }; - let llty = self.cx.type_func( - &[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32(), self.cx.type_i32()], - self.cx.type_void(), - ); - let args = &[fn_name, hash, num_counters, index]; - let args = self.check_call("call", llty, llfn, args); - - unsafe { - let _ = llvm::LLVMRustBuildCall( - self.llbuilder, - llty, - llfn, - args.as_ptr() as *const &llvm::Value, - args.len() as c_uint, - [].as_ptr(), - 0 as c_uint, - ); - } - } - fn call( &mut self, llty: &'ll Type, @@ -1667,6 +1634,18 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { kcfi_bundle } + /// Emits a call to `llvm.instrprof.increment`. Used by coverage instrumentation. + #[instrument(level = "debug", skip(self))] + pub(crate) fn instrprof_increment( + &mut self, + fn_name: &'ll Value, + hash: &'ll Value, + num_counters: &'ll Value, + index: &'ll Value, + ) { + self.call_intrinsic("llvm.instrprof.increment", &[fn_name, hash, num_counters, index]); + } + /// Emits a call to `llvm.instrprof.mcdc.parameters`. /// /// This doesn't produce any code directly, but is used as input by @@ -1676,40 +1655,21 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { /// /// [`CodeGenPGO::emitMCDCParameters`]: /// https://github.com/rust-lang/llvm-project/blob/5399a24/clang/lib/CodeGen/CodeGenPGO.cpp#L1124 + #[instrument(level = "debug", skip(self))] pub(crate) fn mcdc_parameters( &mut self, fn_name: &'ll Value, hash: &'ll Value, bitmap_bits: &'ll Value, ) { - debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bits); - assert!( crate::llvm_util::get_version() >= (19, 0, 0), "MCDC intrinsics require LLVM 19 or later" ); - - let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCParametersIntrinsic(self.cx().llmod) }; - let llty = self.cx.type_func( - &[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32()], - self.cx.type_void(), - ); - let args = &[fn_name, hash, bitmap_bits]; - let args = self.check_call("call", llty, llfn, args); - - unsafe { - let _ = llvm::LLVMRustBuildCall( - self.llbuilder, - llty, - llfn, - args.as_ptr() as *const &llvm::Value, - args.len() as c_uint, - [].as_ptr(), - 0 as c_uint, - ); - } + self.call_intrinsic("llvm.instrprof.mcdc.parameters", &[fn_name, hash, bitmap_bits]); } + #[instrument(level = "debug", skip(self))] pub(crate) fn mcdc_tvbitmap_update( &mut self, fn_name: &'ll Value, @@ -1717,39 +1677,21 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { bitmap_index: &'ll Value, mcdc_temp: &'ll Value, ) { - debug!( - "mcdc_tvbitmap_update() with args ({:?}, {:?}, {:?}, {:?})", - fn_name, hash, bitmap_index, mcdc_temp - ); assert!( crate::llvm_util::get_version() >= (19, 0, 0), "MCDC intrinsics require LLVM 19 or later" ); - - let llfn = - unsafe { llvm::LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(self.cx().llmod) }; - let llty = self.cx.type_func( - &[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32(), self.cx.type_ptr()], - self.cx.type_void(), - ); let args = &[fn_name, hash, bitmap_index, mcdc_temp]; - let args = self.check_call("call", llty, llfn, args); - unsafe { - let _ = llvm::LLVMRustBuildCall( - self.llbuilder, - llty, - llfn, - args.as_ptr() as *const &llvm::Value, - args.len() as c_uint, - [].as_ptr(), - 0 as c_uint, - ); - } + self.call_intrinsic("llvm.instrprof.mcdc.tvbitmap.update", args); + } + + #[instrument(level = "debug", skip(self))] + pub(crate) fn mcdc_condbitmap_reset(&mut self, mcdc_temp: &'ll Value) { self.store(self.const_i32(0), mcdc_temp, self.tcx.data_layout.i32_align.abi); } + #[instrument(level = "debug", skip(self))] pub(crate) fn mcdc_condbitmap_update(&mut self, cond_index: &'ll Value, mcdc_temp: &'ll Value) { - debug!("mcdc_condbitmap_update() with args ({:?}, {:?})", cond_index, mcdc_temp); assert!( crate::llvm_util::get_version() >= (19, 0, 0), "MCDC intrinsics require LLVM 19 or later" diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 206a7069792..a51ef8d7b85 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -98,8 +98,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::ExternalLinkage); - let is_generic = - instance.args.non_erasable_generics(tcx, instance.def_id()).next().is_some(); + let is_generic = instance.args.non_erasable_generics().next().is_some(); let is_hidden = if is_generic { // This is a monomorphization of a generic function. diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 2f830d6f941..3fc153c6cd4 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -80,6 +80,7 @@ pub(crate) struct CodegenCx<'ll, 'tcx> { pub isize_ty: &'ll Type, + /// Extra codegen state needed when coverage instrumentation is enabled. pub coverage_cx: Option<coverageinfo::CrateCoverageContext<'ll, 'tcx>>, pub dbg_cx: Option<debuginfo::CodegenUnitDebugContext<'ll, 'tcx>>, @@ -592,11 +593,10 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { &self.statics_to_rauw } + /// Extra state that is only available when coverage instrumentation is enabled. #[inline] - pub(crate) fn coverage_context( - &self, - ) -> Option<&coverageinfo::CrateCoverageContext<'ll, 'tcx>> { - self.coverage_cx.as_ref() + pub(crate) fn coverage_cx(&self) -> &coverageinfo::CrateCoverageContext<'ll, 'tcx> { + self.coverage_cx.as_ref().expect("only called when coverage instrumentation is enabled") } pub(crate) fn create_used_variable_impl(&self, name: &'static CStr, values: &[&'ll Value]) { @@ -1099,6 +1099,10 @@ impl<'ll> CodegenCx<'ll, '_> { if self.sess().instrument_coverage() { ifn!("llvm.instrprof.increment", fn(ptr, t_i64, t_i32, t_i32) -> void); + if crate::llvm_util::get_version() >= (19, 0, 0) { + 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); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index 90f7dd733ca..feac97f3e2b 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -1,11 +1,9 @@ -use rustc_middle::mir::coverage::{ - ConditionInfo, CounterId, CovTerm, DecisionInfo, ExpressionId, MappingKind, SourceRegion, -}; +use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId, SourceRegion}; /// Must match the layout of `LLVMRustCounterKind`. #[derive(Copy, Clone, Debug)] #[repr(C)] -pub enum CounterKind { +pub(crate) enum CounterKind { Zero = 0, CounterValueReference = 1, Expression = 2, @@ -25,9 +23,9 @@ pub enum CounterKind { /// Must match the layout of `LLVMRustCounter`. #[derive(Copy, Clone, Debug)] #[repr(C)] -pub struct Counter { +pub(crate) struct Counter { // Important: The layout (order and types of fields) must match its C++ counterpart. - pub kind: CounterKind, + pub(crate) kind: CounterKind, id: u32, } @@ -36,7 +34,7 @@ impl Counter { pub(crate) const ZERO: Self = Self { kind: CounterKind::Zero, id: 0 }; /// Constructs a new `Counter` of kind `CounterValueReference`. - pub fn counter_value_reference(counter_id: CounterId) -> Self { + pub(crate) fn counter_value_reference(counter_id: CounterId) -> Self { Self { kind: CounterKind::CounterValueReference, id: counter_id.as_u32() } } @@ -59,7 +57,7 @@ impl Counter { /// Must match the layout of `LLVMRustCounterExprKind`. #[derive(Copy, Clone, Debug)] #[repr(C)] -pub enum ExprKind { +pub(crate) enum ExprKind { Subtract = 0, Add = 1, } @@ -69,48 +67,13 @@ pub enum ExprKind { /// Must match the layout of `LLVMRustCounterExpression`. #[derive(Copy, Clone, Debug)] #[repr(C)] -pub struct CounterExpression { - pub kind: ExprKind, - pub lhs: Counter, - pub rhs: Counter, +pub(crate) struct CounterExpression { + pub(crate) kind: ExprKind, + pub(crate) lhs: Counter, + pub(crate) rhs: Counter, } -/// Corresponds to enum `llvm::coverage::CounterMappingRegion::RegionKind`. -/// -/// Must match the layout of `LLVMRustCounterMappingRegionKind`. -#[derive(Copy, Clone, Debug)] -#[repr(C)] -enum RegionKind { - /// A CodeRegion associates some code with a counter - CodeRegion = 0, - - /// An ExpansionRegion represents a file expansion region that associates - /// a source range with the expansion of a virtual source file, such as - /// for a macro instantiation or #include file. - ExpansionRegion = 1, - - /// A SkippedRegion represents a source range with code that was skipped - /// by a preprocessor or similar means. - SkippedRegion = 2, - - /// A GapRegion is like a CodeRegion, but its count is only set as the - /// line execution count when its the only region in the line. - GapRegion = 3, - - /// A BranchRegion represents leaf-level boolean expressions and is - /// associated with two counters, each representing the number of times the - /// expression evaluates to true or false. - BranchRegion = 4, - - /// A DecisionRegion represents a top-level boolean expression and is - /// associated with a variable length bitmap index and condition number. - MCDCDecisionRegion = 5, - - /// A Branch Region can be extended to include IDs to facilitate MC/DC. - MCDCBranchRegion = 6, -} - -mod mcdc { +pub(crate) mod mcdc { use rustc_middle::mir::coverage::{ConditionId, ConditionInfo, DecisionInfo}; /// Must match the layout of `LLVMRustMCDCDecisionParameters`. @@ -121,8 +84,6 @@ mod mcdc { num_conditions: u16, } - // ConditionId in llvm is `unsigned int` at 18 while `int16_t` at - // [19](https://github.com/llvm/llvm-project/pull/81257). type LLVMConditionId = i16; /// Must match the layout of `LLVMRustMCDCBranchParameters`. @@ -133,38 +94,6 @@ mod mcdc { condition_ids: [LLVMConditionId; 2], } - #[repr(C)] - #[derive(Clone, Copy, Debug)] - enum ParameterTag { - None = 0, - Decision = 1, - Branch = 2, - } - /// Same layout with `LLVMRustMCDCParameters` - #[repr(C)] - #[derive(Clone, Copy, Debug)] - pub(crate) struct Parameters { - tag: ParameterTag, - decision_params: DecisionParameters, - branch_params: BranchParameters, - } - - impl Parameters { - pub(crate) fn none() -> Self { - Self { - tag: ParameterTag::None, - decision_params: Default::default(), - branch_params: Default::default(), - } - } - pub(crate) fn decision(decision_params: DecisionParameters) -> Self { - Self { tag: ParameterTag::Decision, decision_params, branch_params: Default::default() } - } - pub(crate) fn branch(branch_params: BranchParameters) -> Self { - Self { tag: ParameterTag::Branch, decision_params: Default::default(), branch_params } - } - } - impl From<ConditionInfo> for BranchParameters { fn from(value: ConditionInfo) -> Self { let to_llvm_cond_id = |cond_id: Option<ConditionId>| { @@ -186,267 +115,68 @@ mod mcdc { } } -/// This struct provides LLVM's representation of a "CoverageMappingRegion", encoded into the -/// coverage map, in accordance with the -/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format). -/// The struct composes fields representing the `Counter` type and value(s) (injected counter -/// ID, or expression type and operands), the source file (an indirect index into a "filenames -/// array", encoded separately), and source location (start and end positions of the represented -/// code region). +/// A span of source code coordinates to be embedded in coverage metadata. /// -/// Corresponds to struct `llvm::coverage::CounterMappingRegion`. -/// -/// Must match the layout of `LLVMRustCounterMappingRegion`. -#[derive(Copy, Clone, Debug)] +/// Must match the layout of `LLVMRustCoverageSpan`. +#[derive(Clone, Debug)] #[repr(C)] -pub struct CounterMappingRegion { - /// The counter type and type-dependent counter data, if any. - counter: Counter, - - /// If the `RegionKind` is a `BranchRegion`, this represents the counter - /// for the false branch of the region. - false_counter: Counter, - - mcdc_params: mcdc::Parameters, - /// An indirect reference to the source filename. In the LLVM Coverage Mapping Format, the - /// file_id is an index into a function-specific `virtual_file_mapping` array of indexes - /// that, in turn, are used to look up the filename for this region. +pub(crate) struct CoverageSpan { + /// Local index into the function's local-to-global file ID table. + /// The value at that index is itself an index into the coverage filename + /// table in the CGU's `__llvm_covmap` section. file_id: u32, - /// If the `RegionKind` is an `ExpansionRegion`, the `expanded_file_id` can be used to find - /// the mapping regions created as a result of macro expansion, by checking if their file id - /// matches the expanded file id. - expanded_file_id: u32, - - /// 1-based starting line of the mapping region. + /// 1-based starting line of the source code span. start_line: u32, - - /// 1-based starting column of the mapping region. + /// 1-based starting column of the source code span. start_col: u32, - - /// 1-based ending line of the mapping region. + /// 1-based ending line of the source code span. end_line: u32, - - /// 1-based ending column of the mapping region. If the high bit is set, the current - /// mapping region is a gap area. + /// 1-based ending column of the source code span. High bit must be unset. end_col: u32, - - kind: RegionKind, } -impl CounterMappingRegion { - pub(crate) fn from_mapping( - mapping_kind: &MappingKind, - local_file_id: u32, - source_region: &SourceRegion, - ) -> Self { - let &SourceRegion { file_name: _, start_line, start_col, end_line, end_col } = - source_region; - match *mapping_kind { - MappingKind::Code(term) => Self::code_region( - Counter::from_term(term), - local_file_id, - start_line, - start_col, - end_line, - end_col, - ), - MappingKind::Branch { true_term, false_term } => Self::branch_region( - Counter::from_term(true_term), - Counter::from_term(false_term), - local_file_id, - start_line, - start_col, - end_line, - end_col, - ), - MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => { - Self::mcdc_branch_region( - Counter::from_term(true_term), - Counter::from_term(false_term), - mcdc_params, - local_file_id, - start_line, - start_col, - end_line, - end_col, - ) - } - MappingKind::MCDCDecision(decision_info) => Self::decision_region( - decision_info, - local_file_id, - start_line, - start_col, - end_line, - end_col, - ), - } - } - - pub(crate) fn code_region( - counter: Counter, - file_id: u32, - start_line: u32, - start_col: u32, - end_line: u32, - end_col: u32, - ) -> Self { - Self { - counter, - false_counter: Counter::ZERO, - mcdc_params: mcdc::Parameters::none(), - file_id, - expanded_file_id: 0, - start_line, - start_col, - end_line, - end_col, - kind: RegionKind::CodeRegion, - } +impl CoverageSpan { + pub(crate) fn from_source_region(file_id: u32, code_region: &SourceRegion) -> Self { + let &SourceRegion { file_name: _, start_line, start_col, end_line, end_col } = code_region; + // Internally, LLVM uses the high bit of `end_col` to distinguish between + // code regions and gap regions, so it can't be used by the column number. + assert!(end_col & (1u32 << 31) == 0, "high bit of `end_col` must be unset: {end_col:#X}"); + Self { file_id, start_line, start_col, end_line, end_col } } +} - pub(crate) fn branch_region( - counter: Counter, - false_counter: Counter, - file_id: u32, - start_line: u32, - start_col: u32, - end_line: u32, - end_col: u32, - ) -> Self { - Self { - counter, - false_counter, - mcdc_params: mcdc::Parameters::none(), - file_id, - expanded_file_id: 0, - start_line, - start_col, - end_line, - end_col, - kind: RegionKind::BranchRegion, - } - } - - pub(crate) fn mcdc_branch_region( - counter: Counter, - false_counter: Counter, - condition_info: ConditionInfo, - file_id: u32, - start_line: u32, - start_col: u32, - end_line: u32, - end_col: u32, - ) -> Self { - Self { - counter, - false_counter, - mcdc_params: mcdc::Parameters::branch(condition_info.into()), - file_id, - expanded_file_id: 0, - start_line, - start_col, - end_line, - end_col, - kind: RegionKind::MCDCBranchRegion, - } - } - - pub(crate) fn decision_region( - decision_info: DecisionInfo, - file_id: u32, - start_line: u32, - start_col: u32, - end_line: u32, - end_col: u32, - ) -> Self { - let mcdc_params = mcdc::Parameters::decision(decision_info.into()); - - Self { - counter: Counter::ZERO, - false_counter: Counter::ZERO, - mcdc_params, - file_id, - expanded_file_id: 0, - start_line, - start_col, - end_line, - end_col, - kind: RegionKind::MCDCDecisionRegion, - } - } +/// Must match the layout of `LLVMRustCoverageCodeRegion`. +#[derive(Clone, Debug)] +#[repr(C)] +pub(crate) struct CodeRegion { + pub(crate) span: CoverageSpan, + pub(crate) counter: Counter, +} - // This function might be used in the future; the LLVM API is still evolving, as is coverage - // support. - #[allow(dead_code)] - pub(crate) fn expansion_region( - file_id: u32, - expanded_file_id: u32, - start_line: u32, - start_col: u32, - end_line: u32, - end_col: u32, - ) -> Self { - Self { - counter: Counter::ZERO, - false_counter: Counter::ZERO, - mcdc_params: mcdc::Parameters::none(), - file_id, - expanded_file_id, - start_line, - start_col, - end_line, - end_col, - kind: RegionKind::ExpansionRegion, - } - } +/// Must match the layout of `LLVMRustCoverageBranchRegion`. +#[derive(Clone, Debug)] +#[repr(C)] +pub(crate) struct BranchRegion { + pub(crate) span: CoverageSpan, + pub(crate) true_counter: Counter, + pub(crate) false_counter: Counter, +} - // This function might be used in the future; the LLVM API is still evolving, as is coverage - // support. - #[allow(dead_code)] - pub(crate) fn skipped_region( - file_id: u32, - start_line: u32, - start_col: u32, - end_line: u32, - end_col: u32, - ) -> Self { - Self { - counter: Counter::ZERO, - false_counter: Counter::ZERO, - mcdc_params: mcdc::Parameters::none(), - file_id, - expanded_file_id: 0, - start_line, - start_col, - end_line, - end_col, - kind: RegionKind::SkippedRegion, - } - } +/// Must match the layout of `LLVMRustCoverageMCDCBranchRegion`. +#[derive(Clone, Debug)] +#[repr(C)] +pub(crate) struct MCDCBranchRegion { + pub(crate) span: CoverageSpan, + pub(crate) true_counter: Counter, + pub(crate) false_counter: Counter, + pub(crate) mcdc_branch_params: mcdc::BranchParameters, +} - // This function might be used in the future; the LLVM API is still evolving, as is coverage - // support. - #[allow(dead_code)] - pub(crate) fn gap_region( - counter: Counter, - file_id: u32, - start_line: u32, - start_col: u32, - end_line: u32, - end_col: u32, - ) -> Self { - Self { - counter, - false_counter: Counter::ZERO, - mcdc_params: mcdc::Parameters::none(), - file_id, - expanded_file_id: 0, - start_line, - start_col, - end_line, - end_col: (1_u32 << 31) | end_col, - kind: RegionKind::GapRegion, - } - } +/// Must match the layout of `LLVMRustCoverageMCDCDecisionRegion`. +#[derive(Clone, Debug)] +#[repr(C)] +pub(crate) struct MCDCDecisionRegion { + pub(crate) span: CoverageSpan, + pub(crate) mcdc_decision_params: mcdc::DecisionParameters, } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index c2c261da79b..8edd788ee36 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -1,16 +1,23 @@ +use std::ffi::CString; + use itertools::Itertools as _; -use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods}; +use rustc_abi::Align; +use rustc_codegen_ssa::traits::{ + BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, +}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::IndexVec; +use rustc_middle::mir::coverage::MappingKind; use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::Symbol; use rustc_span::def_id::DefIdSet; +use rustc_target::spec::HasTargetSpec; use tracing::debug; use crate::common::CodegenCx; -use crate::coverageinfo::ffi::CounterMappingRegion; +use crate::coverageinfo::ffi; use crate::coverageinfo::map_data::{FunctionCoverage, FunctionCoverageCollector}; use crate::{coverageinfo, llvm}; @@ -47,11 +54,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { add_unused_functions(cx); } - let function_coverage_map = match cx.coverage_context() { - Some(ctx) => ctx.take_function_coverage_map(), - None => return, - }; - + let function_coverage_map = cx.coverage_cx().take_function_coverage_map(); if function_coverage_map.is_empty() { // This module has no functions with coverage instrumentation return; @@ -75,11 +78,9 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { // Generate the coverage map header, which contains the filenames used by // this CGU's coverage mappings, and store it in a well-known global. - let cov_data_val = generate_coverage_map(cx, covmap_version, filenames_size, filenames_val); - coverageinfo::save_cov_data_to_mod(cx, cov_data_val); + generate_covmap_record(cx, covmap_version, filenames_size, filenames_val); let mut unused_function_names = Vec::new(); - let covfun_section_name = coverageinfo::covfun_section_name(cx); // Encode coverage mappings and generate function records for (instance, function_coverage) in function_coverage_entries { @@ -108,9 +109,8 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { unused_function_names.push(mangled_function_name); } - save_function_record( + generate_covfun_record( cx, - &covfun_section_name, mangled_function_name, source_hash, filenames_ref, @@ -132,7 +132,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { .collect::<Vec<_>>(); let initializer = cx.const_array(cx.type_ptr(), &name_globals); - let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), "__llvm_coverage_names"); + let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), c"__llvm_coverage_names"); llvm::set_global_constant(array, true); llvm::set_linkage(array, llvm::Linkage::InternalLinkage); llvm::set_initializer(array, initializer); @@ -235,7 +235,10 @@ fn encode_mappings_for_function( let expressions = function_coverage.counter_expressions().collect::<Vec<_>>(); let mut virtual_file_mapping = VirtualFileMapping::default(); - let mut mapping_regions = Vec::with_capacity(counter_regions.len()); + let mut code_regions = vec![]; + let mut branch_regions = vec![]; + let mut mcdc_branch_regions = vec![]; + let mut mcdc_decision_regions = vec![]; // Group mappings into runs with the same filename, preserving the order // yielded by `FunctionCoverage`. @@ -255,11 +258,36 @@ fn encode_mappings_for_function( // form suitable for FFI. for (mapping_kind, region) in counter_regions_for_file { debug!("Adding counter {mapping_kind:?} to map for {region:?}"); - mapping_regions.push(CounterMappingRegion::from_mapping( - &mapping_kind, - local_file_id.as_u32(), - region, - )); + let span = ffi::CoverageSpan::from_source_region(local_file_id.as_u32(), region); + match mapping_kind { + MappingKind::Code(term) => { + code_regions + .push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) }); + } + MappingKind::Branch { true_term, false_term } => { + branch_regions.push(ffi::BranchRegion { + span, + true_counter: ffi::Counter::from_term(true_term), + false_counter: ffi::Counter::from_term(false_term), + }); + } + MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => { + mcdc_branch_regions.push(ffi::MCDCBranchRegion { + span, + true_counter: ffi::Counter::from_term(true_term), + false_counter: ffi::Counter::from_term(false_term), + mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params), + }); + } + MappingKind::MCDCDecision(mcdc_decision_params) => { + mcdc_decision_regions.push(ffi::MCDCDecisionRegion { + span, + mcdc_decision_params: ffi::mcdc::DecisionParameters::from( + mcdc_decision_params, + ), + }); + } + } } } @@ -268,21 +296,24 @@ fn encode_mappings_for_function( coverageinfo::write_mapping_to_buffer( virtual_file_mapping.into_vec(), expressions, - mapping_regions, + &code_regions, + &branch_regions, + &mcdc_branch_regions, + &mcdc_decision_regions, buffer, ); }) } -/// Construct coverage map header and the array of function records, and combine them into the -/// coverage map. Save the coverage map data into the LLVM IR as a static global using a -/// specific, well-known section and name. -fn generate_coverage_map<'ll>( +/// Generates the contents of the covmap record for this CGU, which mostly +/// consists of a header and a list of filenames. The record is then stored +/// as a global variable in the `__llvm_covmap` section. +fn generate_covmap_record<'ll>( cx: &CodegenCx<'ll, '_>, version: u32, filenames_size: usize, filenames_val: &'ll llvm::Value, -) -> &'ll llvm::Value { +) { debug!("cov map: filenames_size = {}, 0-based version = {}", filenames_size, version); // Create the coverage data header (Note, fields 0 and 2 are now always zero, @@ -297,15 +328,37 @@ fn generate_coverage_map<'ll>( ); // Create the complete LLVM coverage data value to add to the LLVM IR - cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false) + let covmap_data = + cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false); + + let covmap_var_name = CString::new(llvm::build_byte_buffer(|s| unsafe { + llvm::LLVMRustCoverageWriteMappingVarNameToString(s); + })) + .unwrap(); + debug!("covmap var name: {:?}", covmap_var_name); + + let covmap_section_name = CString::new(llvm::build_byte_buffer(|s| unsafe { + llvm::LLVMRustCoverageWriteMapSectionNameToString(cx.llmod, s); + })) + .expect("covmap section name should not contain NUL"); + debug!("covmap section name: {:?}", covmap_section_name); + + let llglobal = llvm::add_global(cx.llmod, cx.val_ty(covmap_data), &covmap_var_name); + llvm::set_initializer(llglobal, covmap_data); + llvm::set_global_constant(llglobal, true); + llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage); + llvm::set_section(llglobal, &covmap_section_name); + // LLVM's coverage mapping format specifies 8-byte alignment for items in this section. + // <https://llvm.org/docs/CoverageMappingFormat.html> + llvm::set_alignment(llglobal, Align::EIGHT); + cx.add_used_global(llglobal); } -/// Construct a function record and combine it with the function's coverage mapping data. -/// Save the function record into the LLVM IR as a static global using a -/// specific, well-known section and name. -fn save_function_record( +/// Generates the contents of the covfun record for this function, which +/// contains the function's coverage mapping data. The record is then stored +/// as a global variable in the `__llvm_covfun` section. +fn generate_covfun_record( cx: &CodegenCx<'_, '_>, - covfun_section_name: &str, mangled_function_name: &str, source_hash: u64, filenames_ref: u64, @@ -332,13 +385,28 @@ fn save_function_record( /*packed=*/ true, ); - coverageinfo::save_func_record_to_mod( - cx, - covfun_section_name, - func_name_hash, - func_record_val, - is_used, - ); + // Choose a variable name to hold this function's covfun data. + // Functions that are used have a suffix ("u") to distinguish them from + // unused copies of the same function (from different CGUs), so that if a + // linker sees both it won't discard the used copy's data. + let func_record_var_name = + CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" })) + .unwrap(); + debug!("function record var name: {:?}", func_record_var_name); + + let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name); + llvm::set_initializer(llglobal, func_record_val); + llvm::set_global_constant(llglobal, true); + llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage); + llvm::set_visibility(llglobal, llvm::Visibility::Hidden); + llvm::set_section(llglobal, cx.covfun_section_name()); + // LLVM's coverage mapping format specifies 8-byte alignment for items in this section. + // <https://llvm.org/docs/CoverageMappingFormat.html> + llvm::set_alignment(llglobal, Align::EIGHT); + if cx.target_spec().supports_comdat() { + llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name); + } + cx.add_used_global(llglobal); } /// Each CGU will normally only emit coverage metadata for the functions that it actually generates. @@ -470,9 +538,5 @@ fn add_unused_function_coverage<'tcx>( // zero, because none of its counters/expressions are marked as seen. let function_coverage = FunctionCoverageCollector::unused(instance, function_coverage_info); - if let Some(coverage_context) = cx.coverage_context() { - coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage); - } else { - bug!("Could not get the `coverage_context`"); - } + cx.coverage_cx().function_coverage_map.borrow_mut().insert(instance, function_coverage); } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index d7d29eebf85..c6b2a623ea6 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -1,22 +1,20 @@ -use std::cell::RefCell; +use std::cell::{OnceCell, RefCell}; +use std::ffi::{CStr, CString}; use libc::c_uint; use rustc_codegen_ssa::traits::{ - BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, - MiscCodegenMethods, StaticCodegenMethods, + BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods, }; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_llvm::RustString; -use rustc_middle::bug; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; use rustc_middle::ty::layout::HasTyCtxt; -use rustc_target::abi::{Align, Size}; +use rustc_target::abi::Size; use tracing::{debug, instrument}; use crate::builder::Builder; use crate::common::CodegenCx; -use crate::coverageinfo::ffi::{CounterExpression, CounterMappingRegion}; use crate::coverageinfo::map_data::FunctionCoverageCollector; use crate::llvm; @@ -31,6 +29,8 @@ pub(crate) struct CrateCoverageContext<'ll, 'tcx> { RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>, pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>, pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>, + + covfun_section_name: OnceCell<CString>, } impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> { @@ -39,6 +39,7 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> { function_coverage_map: Default::default(), pgo_func_name_var_map: Default::default(), mcdc_condition_bitmap_map: Default::default(), + covfun_section_name: Default::default(), } } @@ -65,27 +66,38 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> { } } -// These methods used to be part of trait `CoverageInfoMethods`, which no longer -// exists after most coverage code was moved out of SSA. impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { pub(crate) fn coverageinfo_finalize(&self) { mapgen::finalize(self) } + /// Returns the section name to use when embedding per-function coverage information + /// in the object file, according to the target's object file format. LLVM's coverage + /// tools use information from this section when producing coverage reports. + /// + /// Typical values are: + /// - `__llvm_covfun` on Linux + /// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix) + /// - `.lcovfun$M` on Windows (includes `$M` sorting suffix) + fn covfun_section_name(&self) -> &CStr { + self.coverage_cx().covfun_section_name.get_or_init(|| { + CString::new(llvm::build_byte_buffer(|s| unsafe { + llvm::LLVMRustCoverageWriteFuncSectionNameToString(self.llmod, s); + })) + .expect("covfun section name should not contain NUL") + }) + } + /// For LLVM codegen, returns a function-specific `Value` for a global /// string, to hold the function name passed to LLVM intrinsic /// `instrprof.increment()`. The `Value` is only created once per instance. /// Multiple invocations with the same instance return the same `Value`. fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value { - if let Some(coverage_context) = self.coverage_context() { - debug!("getting pgo_func_name_var for instance={:?}", instance); - let mut pgo_func_name_var_map = coverage_context.pgo_func_name_var_map.borrow_mut(); - pgo_func_name_var_map - .entry(instance) - .or_insert_with(|| create_pgo_func_name_var(self, instance)) - } else { - bug!("Could not get the `coverage_context`"); - } + debug!("getting pgo_func_name_var for instance={:?}", instance); + let mut pgo_func_name_var_map = self.coverage_cx().pgo_func_name_var_map.borrow_mut(); + pgo_func_name_var_map + .entry(instance) + .or_insert_with(|| create_pgo_func_name_var(self, instance)) } } @@ -119,11 +131,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { cond_bitmaps.push(cond_bitmap); } - self.coverage_context() - .expect("always present when coverage is enabled") - .mcdc_condition_bitmap_map - .borrow_mut() - .insert(instance, cond_bitmaps); + self.coverage_cx().mcdc_condition_bitmap_map.borrow_mut().insert(instance, cond_bitmaps); } #[instrument(level = "debug", skip(self))] @@ -144,8 +152,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { return; }; - let Some(coverage_context) = bx.coverage_context() else { return }; - let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); + let mut coverage_map = bx.coverage_cx().function_coverage_map.borrow_mut(); let func_coverage = coverage_map .entry(instance) .or_insert_with(|| FunctionCoverageCollector::new(instance, function_coverage_info)); @@ -187,7 +194,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { } CoverageKind::CondBitmapUpdate { index, decision_depth } => { drop(coverage_map); - let cond_bitmap = coverage_context + let cond_bitmap = bx + .coverage_cx() .try_get_mcdc_condition_bitmap(&instance, decision_depth) .expect("mcdc cond bitmap should have been allocated for updating"); let cond_index = bx.const_i32(index as i32); @@ -195,7 +203,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { } CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth } => { drop(coverage_map); - let cond_bitmap = coverage_context + let cond_bitmap = bx.coverage_cx() .try_get_mcdc_condition_bitmap(&instance, decision_depth) .expect("mcdc cond bitmap should have been allocated for merging into the global bitmap"); assert!( @@ -207,6 +215,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { let hash = bx.const_u64(function_coverage_info.function_source_hash); let bitmap_index = bx.const_u32(bitmap_idx); bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_index, cond_bitmap); + bx.mcdc_condbitmap_reset(cond_bitmap); } } } @@ -255,8 +264,11 @@ pub(crate) fn write_filenames_section_to_buffer<'a>( pub(crate) fn write_mapping_to_buffer( virtual_file_mapping: Vec<u32>, - expressions: Vec<CounterExpression>, - mapping_regions: Vec<CounterMappingRegion>, + expressions: Vec<ffi::CounterExpression>, + code_regions: &[ffi::CodeRegion], + branch_regions: &[ffi::BranchRegion], + mcdc_branch_regions: &[ffi::MCDCBranchRegion], + mcdc_decision_regions: &[ffi::MCDCDecisionRegion], buffer: &RustString, ) { unsafe { @@ -265,8 +277,14 @@ pub(crate) fn write_mapping_to_buffer( virtual_file_mapping.len() as c_uint, expressions.as_ptr(), expressions.len() as c_uint, - mapping_regions.as_ptr(), - mapping_regions.len() as c_uint, + code_regions.as_ptr(), + code_regions.len() as c_uint, + branch_regions.as_ptr(), + branch_regions.len() as c_uint, + mcdc_branch_regions.as_ptr(), + mcdc_branch_regions.len() as c_uint, + mcdc_decision_regions.as_ptr(), + mcdc_decision_regions.len() as c_uint, buffer, ); } @@ -279,79 +297,3 @@ pub(crate) fn hash_bytes(bytes: &[u8]) -> u64 { pub(crate) fn mapping_version() -> u32 { unsafe { llvm::LLVMRustCoverageMappingVersion() } } - -pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - cov_data_val: &'ll llvm::Value, -) { - let covmap_var_name = llvm::build_string(|s| unsafe { - llvm::LLVMRustCoverageWriteMappingVarNameToString(s); - }) - .expect("Rust Coverage Mapping var name failed UTF-8 conversion"); - debug!("covmap var name: {:?}", covmap_var_name); - - let covmap_section_name = llvm::build_string(|s| unsafe { - llvm::LLVMRustCoverageWriteMapSectionNameToString(cx.llmod, s); - }) - .expect("Rust Coverage section name failed UTF-8 conversion"); - debug!("covmap section name: {:?}", covmap_section_name); - - let llglobal = llvm::add_global(cx.llmod, cx.val_ty(cov_data_val), &covmap_var_name); - llvm::set_initializer(llglobal, cov_data_val); - llvm::set_global_constant(llglobal, true); - llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage); - llvm::set_section(llglobal, &covmap_section_name); - // LLVM's coverage mapping format specifies 8-byte alignment for items in this section. - llvm::set_alignment(llglobal, Align::EIGHT); - cx.add_used_global(llglobal); -} - -pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - covfun_section_name: &str, - func_name_hash: u64, - func_record_val: &'ll llvm::Value, - is_used: bool, -) { - // Assign a name to the function record. This is used to merge duplicates. - // - // In LLVM, a "translation unit" (effectively, a `Crate` in Rust) can describe functions that - // are included-but-not-used. If (or when) Rust generates functions that are - // included-but-not-used, note that a dummy description for a function included-but-not-used - // in a Crate can be replaced by full description provided by a different Crate. The two kinds - // of descriptions play distinct roles in LLVM IR; therefore, assign them different names (by - // appending "u" to the end of the function record var name, to prevent `linkonce_odr` merging. - let func_record_var_name = - format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }); - debug!("function record var name: {:?}", func_record_var_name); - debug!("function record section name: {:?}", covfun_section_name); - - let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name); - llvm::set_initializer(llglobal, func_record_val); - llvm::set_global_constant(llglobal, true); - llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage); - llvm::set_visibility(llglobal, llvm::Visibility::Hidden); - llvm::set_section(llglobal, covfun_section_name); - // LLVM's coverage mapping format specifies 8-byte alignment for items in this section. - llvm::set_alignment(llglobal, Align::EIGHT); - llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name); - cx.add_used_global(llglobal); -} - -/// Returns the section name string to pass through to the linker when embedding -/// per-function coverage information in the object file, according to the target -/// platform's object file format. -/// -/// LLVM's coverage tools read coverage mapping details from this section when -/// producing coverage reports. -/// -/// Typical values are: -/// - `__llvm_covfun` on Linux -/// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix) -/// - `.lcovfun$M` on Windows (includes `$M` sorting suffix) -pub(crate) fn covfun_section_name(cx: &CodegenCx<'_, '_>) -> String { - llvm::build_string(|s| unsafe { - llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s); - }) - .expect("Rust Coverage function record section name failed UTF-8 conversion") -} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 1a8153a54e8..3de4ca77e7d 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -350,7 +350,6 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { type_names::push_generic_params( tcx, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), args), - enclosing_fn_def_id, &mut name, ); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index bfe623e7fc3..c9a17c9852d 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -787,7 +787,9 @@ fn codegen_msvc_try<'ll>( let tydesc = bx.declare_global("__rust_panic_type_info", bx.val_ty(type_info)); unsafe { llvm::LLVMRustSetLinkage(tydesc, llvm::Linkage::LinkOnceODRLinkage); - llvm::SetUniqueComdat(bx.llmod, tydesc); + if bx.cx.tcx.sess.target.supports_comdat() { + llvm::SetUniqueComdat(bx.llmod, tydesc); + } llvm::LLVMSetInitializer(tydesc, type_info); } @@ -1177,8 +1179,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(), ty::Array(elem, len) if matches!(elem.kind(), ty::Uint(ty::UintTy::U8)) - && len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all()) - == Some(expected_bytes) => + && len + .try_to_target_usize(bx.tcx) + .expect("expected monomorphic const in codegen") + == expected_bytes => { let place = PlaceRef::alloca(bx, args[0].layout); args[0].val.store(bx, place); @@ -1243,12 +1247,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } if name == sym::simd_shuffle_generic { - let idx = fn_args[2] - .expect_const() - .eval(tcx, ty::ParamEnv::reveal_all(), span) - .unwrap() - .1 - .unwrap_branch(); + let idx = fn_args[2].expect_const().try_to_valtree().unwrap().0.unwrap_branch(); let n = idx.len() as u64; let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn); @@ -1467,8 +1466,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ty::Array(elem, len) if matches!(elem.kind(), ty::Uint(ty::UintTy::U8)) - && len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all()) - == Some(expected_bytes) => + && len + .try_to_target_usize(bx.tcx) + .expect("expected monomorphic const in codegen") + == expected_bytes => { // Zero-extend iN to the array length: let ze = bx.zext(i_, bx.type_ix(expected_bytes * 8)); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 661debbb9f1..10e55a4f7f6 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -646,6 +646,7 @@ unsafe extern "C" { pub type Attribute; pub type Metadata; pub type BasicBlock; + pub type Comdat; } #[repr(C)] pub struct Builder<'a>(InvariantOpaque<'a>); @@ -1490,6 +1491,9 @@ unsafe extern "C" { pub fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr); pub fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&ConstantInt>; + + pub fn LLVMGetOrInsertComdat(M: &Module, Name: *const c_char) -> &Comdat; + pub fn LLVMSetComdat(V: &Value, C: &Comdat); } #[link(name = "llvm-wrapper", kind = "static")] @@ -1611,10 +1615,6 @@ unsafe extern "C" { pub fn LLVMRustSetAllowReassoc(Instr: &Value); // Miscellaneous instructions - pub fn LLVMRustGetInstrProfIncrementIntrinsic(M: &Module) -> &Value; - pub fn LLVMRustGetInstrProfMCDCParametersIntrinsic(M: &Module) -> &Value; - pub fn LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(M: &Module) -> &Value; - pub fn LLVMRustBuildCall<'a>( B: &Builder<'a>, Ty: &'a Type, @@ -1740,7 +1740,7 @@ unsafe extern "C" { ) -> bool; #[allow(improper_ctypes)] - pub fn LLVMRustCoverageWriteFilenamesSectionToBuffer( + pub(crate) fn LLVMRustCoverageWriteFilenamesSectionToBuffer( Filenames: *const *const c_char, FilenamesLen: size_t, Lengths: *const size_t, @@ -1749,33 +1749,39 @@ unsafe extern "C" { ); #[allow(improper_ctypes)] - pub fn LLVMRustCoverageWriteMappingToBuffer( + pub(crate) fn LLVMRustCoverageWriteMappingToBuffer( VirtualFileMappingIDs: *const c_uint, NumVirtualFileMappingIDs: c_uint, Expressions: *const crate::coverageinfo::ffi::CounterExpression, NumExpressions: c_uint, - MappingRegions: *const crate::coverageinfo::ffi::CounterMappingRegion, - NumMappingRegions: c_uint, + CodeRegions: *const crate::coverageinfo::ffi::CodeRegion, + NumCodeRegions: c_uint, + BranchRegions: *const crate::coverageinfo::ffi::BranchRegion, + NumBranchRegions: c_uint, + MCDCBranchRegions: *const crate::coverageinfo::ffi::MCDCBranchRegion, + NumMCDCBranchRegions: c_uint, + MCDCDecisionRegions: *const crate::coverageinfo::ffi::MCDCDecisionRegion, + NumMCDCDecisionRegions: c_uint, BufferOut: &RustString, ); - pub fn LLVMRustCoverageCreatePGOFuncNameVar( + pub(crate) fn LLVMRustCoverageCreatePGOFuncNameVar( F: &Value, FuncName: *const c_char, FuncNameLen: size_t, ) -> &Value; - pub fn LLVMRustCoverageHashByteArray(Bytes: *const c_char, NumBytes: size_t) -> u64; + pub(crate) fn LLVMRustCoverageHashByteArray(Bytes: *const c_char, NumBytes: size_t) -> u64; #[allow(improper_ctypes)] - pub fn LLVMRustCoverageWriteMapSectionNameToString(M: &Module, Str: &RustString); + pub(crate) fn LLVMRustCoverageWriteMapSectionNameToString(M: &Module, Str: &RustString); #[allow(improper_ctypes)] - pub fn LLVMRustCoverageWriteFuncSectionNameToString(M: &Module, Str: &RustString); + pub(crate) fn LLVMRustCoverageWriteFuncSectionNameToString(M: &Module, Str: &RustString); #[allow(improper_ctypes)] - pub fn LLVMRustCoverageWriteMappingVarNameToString(Str: &RustString); + pub(crate) fn LLVMRustCoverageWriteMappingVarNameToString(Str: &RustString); - pub fn LLVMRustCoverageMappingVersion() -> u32; + pub(crate) fn LLVMRustCoverageMappingVersion() -> u32; pub fn LLVMRustDebugMetadataVersion() -> u32; pub fn LLVMRustVersionMajor() -> u32; pub fn LLVMRustVersionMinor() -> u32; @@ -2320,7 +2326,6 @@ unsafe extern "C" { pub fn LLVMRustPositionBuilderAtStart<'a>(B: &Builder<'a>, BB: &'a BasicBlock); - pub fn LLVMRustSetComdat<'a>(M: &'a Module, V: &'a Value, Name: *const c_char, NameLen: size_t); pub fn LLVMRustSetModulePICLevel(M: &Module); pub fn LLVMRustSetModulePIELevel(M: &Module); pub fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index d0db350a149..e837022044e 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -178,10 +178,10 @@ pub fn SetFunctionCallConv(fn_: &Value, cc: CallConv) { // function. // For more details on COMDAT sections see e.g., https://www.airs.com/blog/archives/52 pub fn SetUniqueComdat(llmod: &Module, val: &Value) { - unsafe { - let name = get_value_name(val); - LLVMRustSetComdat(llmod, val, name.as_ptr().cast(), name.len()); - } + let name_buf = get_value_name(val).to_vec(); + let name = + CString::from_vec_with_nul(name_buf).or_else(|buf| CString::new(buf.into_bytes())).unwrap(); + set_comdat(llmod, val, &name); } pub fn SetUnnamedAddress(global: &Value, unnamed: UnnamedAddr) { @@ -210,15 +210,13 @@ impl MemoryEffects { } } -pub fn set_section(llglobal: &Value, section_name: &str) { - let section_name_cstr = CString::new(section_name).expect("unexpected CString error"); +pub fn set_section(llglobal: &Value, section_name: &CStr) { unsafe { - LLVMSetSection(llglobal, section_name_cstr.as_ptr()); + LLVMSetSection(llglobal, section_name.as_ptr()); } } -pub fn add_global<'a>(llmod: &'a Module, ty: &'a Type, name: &str) -> &'a Value { - let name_cstr = CString::new(name).expect("unexpected CString error"); +pub fn add_global<'a>(llmod: &'a Module, ty: &'a Type, name_cstr: &CStr) -> &'a Value { unsafe { LLVMAddGlobal(llmod, ty, name_cstr.as_ptr()) } } @@ -252,9 +250,14 @@ pub fn set_alignment(llglobal: &Value, align: Align) { } } -pub fn set_comdat(llmod: &Module, llglobal: &Value, name: &str) { +/// Get the `name`d comdat from `llmod` and assign it to `llglobal`. +/// +/// Inserts the comdat into `llmod` if it does not exist. +/// It is an error to call this if the target does not support comdat. +pub fn set_comdat(llmod: &Module, llglobal: &Value, name: &CStr) { unsafe { - LLVMRustSetComdat(llmod, llglobal, name.as_ptr().cast(), name.len()); + let comdat = LLVMGetOrInsertComdat(llmod, name.as_ptr()); + LLVMSetComdat(llglobal, comdat); } } diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 02e1995620b..bf6ef219873 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -64,7 +64,9 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { unsafe { llvm::LLVMRustSetLinkage(lldecl, base::linkage_to_llvm(linkage)) }; let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); base::set_link_section(lldecl, attrs); - if linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR { + if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR) + && self.tcx.sess.target.supports_comdat() + { llvm::SetUniqueComdat(self.llmod, lldecl); } diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 1af666f818b..6be4c3f034f 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -191,7 +191,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { /// `[T]` becomes `T`, while `str` and `Trait` turn into `i8` - this /// is useful for indexing slices, as `&[T]`'s data pointer is `T*`. /// If the type is an unsized struct, the regular layout is generated, - /// with the inner-most trailing unsized field using the "minimal unit" + /// with the innermost trailing unsized field using the "minimal unit" /// of that field's type - this is useful for taking the address of /// that field and ensuring the struct has the right alignment. fn llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> &'a Type { diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 77c35a1fe79..d9669453f5a 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -312,7 +312,7 @@ fn exported_symbols_provider_local( match *mono_item { MonoItem::Fn(Instance { def: InstanceKind::Item(def), args }) => { - if args.non_erasable_generics(tcx, def).next().is_some() { + if args.non_erasable_generics().next().is_some() { let symbol = ExportedSymbol::Generic(def, args); symbols.push((symbol, SymbolExportInfo { level: SymbolExportLevel::Rust, @@ -321,12 +321,9 @@ fn exported_symbols_provider_local( })); } } - MonoItem::Fn(Instance { def: InstanceKind::DropGlue(def_id, Some(ty)), args }) => { + MonoItem::Fn(Instance { def: InstanceKind::DropGlue(_, Some(ty)), args }) => { // A little sanity-check - assert_eq!( - args.non_erasable_generics(tcx, def_id).next(), - Some(GenericArgKind::Type(ty)) - ); + assert_eq!(args.non_erasable_generics().next(), Some(GenericArgKind::Type(ty))); symbols.push((ExportedSymbol::DropGlue(ty), SymbolExportInfo { level: SymbolExportLevel::Rust, kind: SymbolExportKind::Text, @@ -334,14 +331,11 @@ fn exported_symbols_provider_local( })); } MonoItem::Fn(Instance { - def: InstanceKind::AsyncDropGlueCtorShim(def_id, Some(ty)), + def: InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)), args, }) => { // A little sanity-check - assert_eq!( - args.non_erasable_generics(tcx, def_id).next(), - Some(GenericArgKind::Type(ty)) - ); + assert_eq!(args.non_erasable_generics().next(), Some(GenericArgKind::Type(ty))); symbols.push((ExportedSymbol::AsyncDropGlueCtorShim(ty), SymbolExportInfo { level: SymbolExportLevel::Rust, kind: SymbolExportKind::Text, diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index d91c0f0790d..a726ee73aaa 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -147,7 +147,7 @@ pub fn validate_trivial_unsize<'tcx>( infcx.leak_check(universe, None).is_ok() }) } - (None, None) => true, + (_, None) => true, _ => false, } } @@ -175,7 +175,8 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( { let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); - if data_a.principal_def_id() == data_b.principal_def_id() { + let b_principal_def_id = data_b.principal_def_id(); + if data_a.principal_def_id() == b_principal_def_id || b_principal_def_id.is_none() { // Codegen takes advantage of the additional assumption, where if the // principal trait def id of what's being casted doesn't change, // then we don't need to adjust the vtable at all. This @@ -887,7 +888,7 @@ impl CrateInfo { // below. // // In order to get this left-to-right dependency ordering, we use the reverse - // postorder of all crates putting the leaves at the right-most positions. + // postorder of all crates putting the leaves at the rightmost positions. let mut compiler_builtins = None; let mut used_crates: Vec<_> = tcx .postorder_cnums(()) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index d536419ab3c..a5bd3adbcdd 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -137,7 +137,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let inner = attr.meta_item_list(); match inner.as_deref() { Some([item]) if item.has_name(sym::linker) => { - if !tcx.features().used_with_arg { + if !tcx.features().used_with_arg() { feature_err( &tcx.sess, sym::used_with_arg, @@ -149,7 +149,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER; } Some([item]) if item.has_name(sym::compiler) => { - if !tcx.features().used_with_arg { + if !tcx.features().used_with_arg() { feature_err( &tcx.sess, sym::used_with_arg, @@ -213,7 +213,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { .emit(); } if is_closure - && !tcx.features().closure_track_caller + && !tcx.features().closure_track_caller() && !attr.span.allows_unstable(sym::closure_track_caller) { feature_err( @@ -268,7 +268,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // // This exception needs to be kept in sync with allowing // `#[target_feature]` on `main` and `start`. - } else if !tcx.features().target_feature_11 { + } else if !tcx.features().target_feature_11() { feature_err( &tcx.sess, sym::target_feature_11, @@ -584,7 +584,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // its parent function, which effectively inherits the features anyway. Boxing this closure // would result in this closure being compiled without the inherited target features, but this // is probably a poor usage of `#[inline(always)]` and easily avoided by not using the attribute. - if tcx.features().target_feature_11 + if tcx.features().target_feature_11() && tcx.is_closure_like(did.to_def_id()) && codegen_fn_attrs.inline != InlineAttr::Always { diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 369ab387bea..1e5b4f3433d 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -23,7 +23,6 @@ use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{ self, ExistentialProjection, GenericArgKind, GenericArgsRef, ParamEnv, Ty, TyCtxt, }; -use rustc_span::DUMMY_SP; use rustc_target::abi::Integer; use smallvec::SmallVec; @@ -111,14 +110,14 @@ fn push_debuginfo_type_name<'tcx>( ty_and_layout, &|output, visited| { push_item_name(tcx, def.did(), true, output); - push_generic_params_internal(tcx, args, def.did(), output, visited); + push_generic_params_internal(tcx, args, output, visited); }, output, visited, ); } else { push_item_name(tcx, def.did(), qualified, output); - push_generic_params_internal(tcx, args, def.did(), output, visited); + push_generic_params_internal(tcx, args, output, visited); } } ty::Tuple(component_types) => { @@ -252,13 +251,8 @@ fn push_debuginfo_type_name<'tcx>( let principal = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), principal); push_item_name(tcx, principal.def_id, qualified, output); - let principal_has_generic_params = push_generic_params_internal( - tcx, - principal.args, - principal.def_id, - output, - visited, - ); + let principal_has_generic_params = + push_generic_params_internal(tcx, principal.args, output, visited); let projection_bounds: SmallVec<[_; 4]> = trait_data .projection_bounds() @@ -539,13 +533,7 @@ pub fn compute_debuginfo_vtable_name<'tcx>( tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref); push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name); visited.clear(); - push_generic_params_internal( - tcx, - trait_ref.args, - trait_ref.def_id, - &mut vtable_name, - &mut visited, - ); + push_generic_params_internal(tcx, trait_ref.args, &mut vtable_name, &mut visited); } else { vtable_name.push('_'); } @@ -648,12 +636,11 @@ fn push_unqualified_item_name( fn push_generic_params_internal<'tcx>( tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>, - def_id: DefId, output: &mut String, visited: &mut FxHashSet<Ty<'tcx>>, ) -> bool { assert_eq!(args, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), args)); - let mut args = args.non_erasable_generics(tcx, def_id).peekable(); + let mut args = args.non_erasable_generics().peekable(); if args.peek().is_none() { return false; } @@ -685,21 +672,25 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S ty::ConstKind::Param(param) => { write!(output, "{}", param.name) } - ty::ConstKind::Value(ty, _) => { + ty::ConstKind::Value(ty, valtree) => { match ty.kind() { ty::Int(ity) => { // FIXME: directly extract the bits from a valtree instead of evaluating an // already evaluated `Const` in order to get the bits. - let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all()); + let bits = ct + .try_to_bits(tcx, ty::ParamEnv::reveal_all()) + .expect("expected monomorphic const in codegen"); let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; write!(output, "{val}") } ty::Uint(_) => { - let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all()); + let val = ct + .try_to_bits(tcx, ty::ParamEnv::reveal_all()) + .expect("expected monomorphic const in codegen"); write!(output, "{val}") } ty::Bool => { - let val = ct.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap(); + let val = ct.try_to_bool().expect("expected monomorphic const in codegen"); write!(output, "{val}") } _ => { @@ -711,8 +702,9 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S // avoiding collisions and will make the emitted type names shorter. let hash_short = tcx.with_stable_hashing_context(|mut hcx| { let mut hasher = StableHasher::new(); - let ct = ct.eval(tcx, ty::ParamEnv::reveal_all(), DUMMY_SP).unwrap(); - hcx.while_hashing_spans(false, |hcx| ct.hash_stable(hcx, &mut hasher)); + hcx.while_hashing_spans(false, |hcx| { + (ty, valtree).hash_stable(hcx, &mut hasher) + }); hasher.finish::<Hash64>() }); @@ -732,12 +724,11 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S pub fn push_generic_params<'tcx>( tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>, - def_id: DefId, output: &mut String, ) { let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name"); let mut visited = FxHashSet::default(); - push_generic_params_internal(tcx, args, def_id, output, &mut visited); + push_generic_params_internal(tcx, args, output, &mut visited); } fn push_closure_or_coroutine_name<'tcx>( @@ -782,7 +773,7 @@ fn push_closure_or_coroutine_name<'tcx>( // FIXME(async_closures): This is probably not going to be correct w.r.t. // multiple coroutine flavors. Maybe truncate to (parent + 1)? let args = args.truncate_to(tcx, generics); - push_generic_params_internal(tcx, args, enclosing_fn_def_id, output, visited); + push_generic_params_internal(tcx, args, output, visited); } fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) { diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index cbd95146294..73bfa9dbd10 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -11,7 +11,6 @@ #![feature(let_chains)] #![feature(negative_impls)] #![feature(rustdoc_internals)] -#![feature(strict_provenance)] #![feature(trait_alias)] #![feature(try_blocks)] #![warn(unreachable_pub)] diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index a132ca69540..82fea4c58e1 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -361,12 +361,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty), (Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty), (Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm), - (Pointer(..), Int(..)) => bx.ptrtoint(imm, to_backend_ty), + (Pointer(..), Int(..)) => { + // FIXME: this exposes the provenance, which shouldn't be necessary. + bx.ptrtoint(imm, to_backend_ty) + } (Float(_), Pointer(..)) => { let int_imm = bx.bitcast(imm, bx.cx().type_isize()); bx.ptradd(bx.const_null(bx.type_ptr()), int_imm) } (Pointer(..), Float(_)) => { + // FIXME: this exposes the provenance, which shouldn't be necessary. let int_imm = bx.ptrtoint(imm, bx.cx().type_isize()); bx.bitcast(int_imm, to_backend_ty) } diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index dfe8fd616e4..0845bcc5749 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -5,7 +5,6 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; -use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; @@ -61,30 +60,9 @@ pub(crate) fn from_target_feature( return None; }; - // Only allow features whose feature gates have been enabled. + // Only allow target features whose feature gates have been enabled. let allowed = match feature_gate.as_ref().copied() { - Some(sym::arm_target_feature) => rust_features.arm_target_feature, - Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature, - Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature, - Some(sym::mips_target_feature) => rust_features.mips_target_feature, - Some(sym::riscv_target_feature) => rust_features.riscv_target_feature, - Some(sym::avx512_target_feature) => rust_features.avx512_target_feature, - Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature, - Some(sym::tbm_target_feature) => rust_features.tbm_target_feature, - Some(sym::wasm_target_feature) => rust_features.wasm_target_feature, - Some(sym::rtm_target_feature) => rust_features.rtm_target_feature, - Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature, - Some(sym::bpf_target_feature) => rust_features.bpf_target_feature, - Some(sym::aarch64_ver_target_feature) => rust_features.aarch64_ver_target_feature, - Some(sym::csky_target_feature) => rust_features.csky_target_feature, - Some(sym::loongarch_target_feature) => rust_features.loongarch_target_feature, - Some(sym::lahfsahf_target_feature) => rust_features.lahfsahf_target_feature, - Some(sym::prfchw_target_feature) => rust_features.prfchw_target_feature, - Some(sym::sha512_sm_x86) => rust_features.sha512_sm_x86, - Some(sym::x86_amx_intrinsics) => rust_features.x86_amx_intrinsics, - Some(sym::xop_target_feature) => rust_features.xop_target_feature, - Some(sym::s390x_target_feature) => rust_features.s390x_target_feature, - Some(name) => bug!("unknown target feature gate {}", name), + Some(name) => rust_features.enabled(name), None => true, }; if !allowed { diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index c0c1085e949..50a51714146 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -437,14 +437,6 @@ pub trait BuilderMethods<'a, 'tcx>: /// Called for `StorageDead` fn lifetime_end(&mut self, ptr: Self::Value, size: Size); - fn instrprof_increment( - &mut self, - fn_name: Self::Value, - hash: Self::Value, - num_counters: Self::Value, - index: Self::Value, - ); - fn call( &mut self, llty: Self::Type, diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 24dbe688f36..3e4f83c8242 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -41,8 +41,6 @@ const_eval_const_context = {$kind -> *[other] {""} } -const_eval_const_stable = const-stable functions can only call other const-stable functions - const_eval_copy_nonoverlapping_overlapping = `copy_nonoverlapping` called on overlapping ranges @@ -259,6 +257,9 @@ const_eval_non_const_fn_call = const_eval_non_const_impl = impl defined here, but it is not `const` +const_eval_non_const_intrinsic = + cannot call non-const intrinsic `{$name}` in {const_eval_const_context}s + const_eval_not_enough_caller_args = calling a function with fewer arguments than it requires @@ -397,17 +398,29 @@ const_eval_uninhabited_enum_variant_read = read discriminant of an uninhabited enum variant const_eval_uninhabited_enum_variant_written = writing discriminant of an uninhabited enum variant + +const_eval_unmarked_const_fn_exposed = `{$def_path}` cannot be (indirectly) exposed to stable + .help = either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]` +const_eval_unmarked_intrinsic_exposed = intrinsic `{$def_path}` cannot be (indirectly) exposed to stable + .help = mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_const_stable_indirect]` (but this requires team approval) + const_eval_unreachable = entering unreachable code const_eval_unreachable_unwind = unwinding past a stack frame that does not allow unwinding const_eval_unsized_local = unsized locals are not supported const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn - -const_eval_unstable_in_stable = - const-stable function cannot use `#[feature({$gate})]` - .unstable_sugg = if the function is not (yet) meant to be stable, make this function unstably const - .bypass_sugg = otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (but requires team approval) +const_eval_unstable_in_stable_exposed = + const function that might be (indirectly) exposed to stable cannot use `#[feature({$gate})]` + .is_function_call = mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + .unstable_sugg = if the {$is_function_call2 -> + [true] caller + *[false] function + } is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + .bypass_sugg = otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + +const_eval_unstable_intrinsic = `{$name}` is not yet stable as a const intrinsic + .help = add `#![feature({$feature})]` to the crate attributes to enable const_eval_unterminated_c_string = reading a null-terminated string starting at {$pointer} with no null found before end of allocation diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 463a66d4e2e..004fb12419f 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -5,6 +5,7 @@ use std::borrow::Cow; use std::mem; use std::ops::Deref; +use rustc_attr::{ConstStability, StabilityLevel}; use rustc_errors::{Diag, ErrorGuaranteed}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; @@ -28,8 +29,8 @@ use super::ops::{self, NonConstOp, Status}; use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop}; use super::resolver::FlowSensitiveAnalysis; use super::{ConstCx, Qualif}; -use crate::const_eval::is_unstable_const_fn; -use crate::errors::UnstableInStable; +use crate::check_consts::is_safe_to_expose_on_stable_const_fn; +use crate::errors; type QualifResults<'mir, 'tcx, Q> = rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>; @@ -274,19 +275,22 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { /// context. pub fn check_op_spanned<O: NonConstOp<'tcx>>(&mut self, op: O, span: Span) { let gate = match op.status_in_item(self.ccx) { - Status::Allowed => return, - - Status::Unstable(gate) if self.tcx.features().active(gate) => { - let unstable_in_stable = self.ccx.is_const_stable_const_fn() - && !super::rustc_allow_const_fn_unstable(self.tcx, self.def_id(), gate); - if unstable_in_stable { - emit_unstable_in_stable_error(self.ccx, span, gate); + Status::Unstable { gate, safe_to_expose_on_stable, is_function_call } + if self.tcx.features().enabled(gate) => + { + // Generally this is allowed since the feature gate is enabled -- except + // if this function wants to be safe-to-expose-on-stable. + if !safe_to_expose_on_stable + && self.enforce_recursive_const_stability() + && !super::rustc_allow_const_fn_unstable(self.tcx, self.def_id(), gate) + { + emit_unstable_in_stable_exposed_error(self.ccx, span, gate, is_function_call); } return; } - Status::Unstable(gate) => Some(gate), + Status::Unstable { gate, .. } => Some(gate), Status::Forbidden => None, }; @@ -304,7 +308,13 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { self.error_emitted = Some(reported); } - ops::DiagImportance::Secondary => self.secondary_errors.push(err), + ops::DiagImportance::Secondary => { + self.secondary_errors.push(err); + self.tcx.dcx().span_delayed_bug( + span, + "compilation must fail when there is a secondary const checker error", + ); + } } } @@ -569,6 +579,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { ty::FnPtr(..) => { self.check_op(ops::FnCallIndirect); + // We can get here without an error in miri-unleashed mode... might as well + // skip the rest of the checks as well then. return; } _ => { @@ -604,14 +616,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { let mut is_trait = false; // Attempting to call a trait method? - if tcx.trait_of_item(callee).is_some() { + if let Some(trait_did) = tcx.trait_of_item(callee) { trace!("attempting to call a trait method"); + + let trait_is_const = tcx.is_const_trait(trait_did); // trait method calls are only permitted when `effects` is enabled. - // we don't error, since that is handled by typeck. We try to resolve - // the trait into the concrete method, and uses that for const stability - // checks. + // typeck ensures the conditions for calling a const trait method are met, + // so we only error if the trait isn't const. We try to resolve the trait + // into the concrete method, and uses that for const stability checks. // FIXME(effects) we might consider moving const stability checks to typeck as well. - if tcx.features().effects { + if tcx.features().effects() && trait_is_const { + // This skips the check below that ensures we only call `const fn`. is_trait = true; if let Ok(Some(instance)) = @@ -625,18 +640,27 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { callee = def; } } else { + // if the trait is const but the user has not enabled the feature(s), + // suggest them. + let feature = if trait_is_const { + Some(if tcx.features().const_trait_impl() { + sym::effects + } else { + sym::const_trait_impl + }) + } else { + None + }; self.check_op(ops::FnCallNonConst { caller, callee, args: fn_args, span: *fn_span, call_source, - feature: Some(if tcx.features().const_trait_impl { - sym::effects - } else { - sym::const_trait_impl - }), + feature, }); + // If we allowed this, we're in miri-unleashed mode, so we might + // as well skip the remaining checks. return; } } @@ -650,29 +674,73 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // const-eval of the `begin_panic` fn assumes the argument is `&str` if tcx.is_lang_item(callee, LangItem::BeginPanic) { match args[0].node.ty(&self.ccx.body.local_decls, tcx).kind() { - ty::Ref(_, ty, _) if ty.is_str() => return, + ty::Ref(_, ty, _) if ty.is_str() => {} _ => self.check_op(ops::PanicNonStr), } + // Allow this call, skip all the checks below. + return; } // const-eval of `#[rustc_const_panic_str]` functions assumes the argument is `&&str` if tcx.has_attr(callee, sym::rustc_const_panic_str) { match args[0].node.ty(&self.ccx.body.local_decls, tcx).kind() { ty::Ref(_, ty, _) if matches!(ty.kind(), ty::Ref(_, ty, _) if ty.is_str()) => - { - return; + {} + _ => { + self.check_op(ops::PanicNonStr); } - _ => self.check_op(ops::PanicNonStr), } + // Allow this call, skip all the checks below. + return; } // This can be called on stable via the `vec!` macro. if tcx.is_lang_item(callee, LangItem::ExchangeMalloc) { self.check_op(ops::HeapAllocation); + // Allow this call, skip all the checks below. + return; + } + + // Intrinsics are language primitives, not regular calls, so treat them separately. + if let Some(intrinsic) = tcx.intrinsic(callee) { + match tcx.lookup_const_stability(callee) { + None => { + // Non-const intrinsic. + self.check_op(ops::IntrinsicNonConst { name: intrinsic.name }); + } + Some(ConstStability { feature: None, const_stable_indirect, .. }) => { + // Intrinsic does not need a separate feature gate (we rely on the + // regular stability checker). However, we have to worry about recursive + // const stability. + if !const_stable_indirect && self.enforce_recursive_const_stability() { + self.dcx().emit_err(errors::UnmarkedIntrinsicExposed { + span: self.span, + def_path: self.tcx.def_path_str(callee), + }); + } + } + Some(ConstStability { + feature: Some(feature), + level: StabilityLevel::Unstable { .. }, + const_stable_indirect, + .. + }) => { + self.check_op(ops::IntrinsicUnstable { + name: intrinsic.name, + feature, + const_stable_indirect, + }); + } + Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { + // All good. + } + } + // This completes the checks for intrinsics. return; } - if !tcx.is_const_fn_raw(callee) && !is_trait { + // Trait functions are not `const fn` so we have to skip them here. + if !tcx.is_const_fn(callee) && !is_trait { self.check_op(ops::FnCallNonConst { caller, callee, @@ -681,66 +749,68 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { call_source, feature: None, }); + // If we allowed this, we're in miri-unleashed mode, so we might + // as well skip the remaining checks. return; } - // If the `const fn` we are trying to call is not const-stable, ensure that we have - // the proper feature gate enabled. - if let Some((gate, implied_by)) = is_unstable_const_fn(tcx, callee) { - trace!(?gate, "calling unstable const fn"); - if self.span.allows_unstable(gate) { - return; - } - if let Some(implied_by_gate) = implied_by - && self.span.allows_unstable(implied_by_gate) - { - return; - } - - // Calling an unstable function *always* requires that the corresponding gate - // (or implied gate) be enabled, even if the function has - // `#[rustc_allow_const_fn_unstable(the_gate)]`. - let gate_declared = |gate| tcx.features().declared(gate); - let feature_gate_declared = gate_declared(gate); - let implied_gate_declared = implied_by.is_some_and(gate_declared); - if !feature_gate_declared && !implied_gate_declared { - self.check_op(ops::FnCallUnstable(callee, Some(gate))); - return; + // Finally, stability for regular function calls -- this is the big one. + match tcx.lookup_const_stability(callee) { + Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { + // All good. } - - // If this crate is not using stability attributes, or the caller is not claiming to be a - // stable `const fn`, that is all that is required. - if !self.ccx.is_const_stable_const_fn() { - trace!("crate not using stability attributes or caller not stably const"); - return; - } - - // Otherwise, we are something const-stable calling a const-unstable fn. - if super::rustc_allow_const_fn_unstable(tcx, caller, gate) { - trace!("rustc_allow_const_fn_unstable gate active"); - return; + None | Some(ConstStability { feature: None, .. }) => { + // This doesn't need a separate const-stability check -- const-stability equals + // regular stability, and regular stability is checked separately. + // However, we *do* have to worry about *recursive* const stability. + if self.enforce_recursive_const_stability() + && !is_safe_to_expose_on_stable_const_fn(tcx, callee) + { + self.dcx().emit_err(errors::UnmarkedConstFnExposed { + span: self.span, + def_path: self.tcx.def_path_str(callee), + }); + } } + Some(ConstStability { + feature: Some(feature), + level: StabilityLevel::Unstable { implied_by: implied_feature, .. }, + .. + }) => { + // An unstable const fn with a feature gate. + let callee_safe_to_expose_on_stable = + is_safe_to_expose_on_stable_const_fn(tcx, callee); + + // We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if + // the callee is safe to expose, to avoid bypassing recursive stability. + if (self.span.allows_unstable(feature) + || implied_feature.is_some_and(|f| self.span.allows_unstable(f))) + && callee_safe_to_expose_on_stable + { + return; + } - self.check_op(ops::FnCallUnstable(callee, Some(gate))); - return; - } - - // FIXME(ecstaticmorse); For compatibility, we consider `unstable` callees that - // have no `rustc_const_stable` attributes to be const-unstable as well. This - // should be fixed later. - let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none() - && tcx.lookup_stability(callee).is_some_and(|s| s.is_unstable()); - if callee_is_unstable_unmarked { - trace!("callee_is_unstable_unmarked"); - // We do not use `const` modifiers for intrinsic "functions", as intrinsics are - // `extern` functions, and these have no way to get marked `const`. So instead we - // use `rustc_const_(un)stable` attributes to mean that the intrinsic is `const` - if self.ccx.is_const_stable_const_fn() || tcx.intrinsic(callee).is_some() { - self.check_op(ops::FnCallUnstable(callee, None)); - return; + // We can't use `check_op` to check whether the feature is enabled because + // the logic is a bit different than elsewhere: local functions don't need + // the feature gate, and there might be an "implied" gate that also suffices + // to allow this. + let feature_enabled = callee.is_local() + || tcx.features().enabled(feature) + || implied_feature.is_some_and(|f| tcx.features().enabled(f)); + // We do *not* honor this if we are in the "danger zone": we have to enforce + // recursive const-stability and the callee is not safe-to-expose. In that + // case we need `check_op` to do the check. + let danger_zone = !callee_safe_to_expose_on_stable + && self.enforce_recursive_const_stability(); + if danger_zone || !feature_enabled { + self.check_op(ops::FnCallUnstable { + def_id: callee, + feature, + safe_to_expose_on_stable: callee_safe_to_expose_on_stable, + }); + } } } - trace!("permitting call"); } // Forbid all `Drop` terminators unless the place being dropped is a local with no @@ -785,11 +855,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { TerminatorKind::InlineAsm { .. } => self.check_op(ops::InlineAsm), - TerminatorKind::Yield { .. } => self.check_op(ops::Coroutine( - self.tcx - .coroutine_kind(self.body.source.def_id()) - .expect("Only expected to have a yield in a coroutine"), - )), + TerminatorKind::Yield { .. } => { + self.check_op(ops::Coroutine( + self.tcx + .coroutine_kind(self.body.source.def_id()) + .expect("Only expected to have a yield in a coroutine"), + )); + } TerminatorKind::CoroutineDrop => { span_bug!( @@ -819,8 +891,19 @@ fn is_int_bool_float_or_char(ty: Ty<'_>) -> bool { ty.is_bool() || ty.is_integral() || ty.is_char() || ty.is_floating_point() } -fn emit_unstable_in_stable_error(ccx: &ConstCx<'_, '_>, span: Span, gate: Symbol) { +fn emit_unstable_in_stable_exposed_error( + ccx: &ConstCx<'_, '_>, + span: Span, + gate: Symbol, + is_function_call: bool, +) -> ErrorGuaranteed { let attr_span = ccx.tcx.def_span(ccx.def_id()).shrink_to_lo(); - ccx.dcx().emit_err(UnstableInStable { gate: gate.to_string(), span, attr_span }); + ccx.dcx().emit_err(errors::UnstableInStableExposed { + gate: gate.to_string(), + span, + attr_span, + is_function_call, + is_function_call2: is_function_call, + }) } diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index 15ac4cedcc3..56da6791847 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -59,10 +59,12 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> { self.const_kind.expect("`const_kind` must not be called on a non-const fn") } - pub fn is_const_stable_const_fn(&self) -> bool { + pub fn enforce_recursive_const_stability(&self) -> bool { + // We can skip this if `staged_api` is not enabled, since in such crates + // `lookup_const_stability` will always be `None`. self.const_kind == Some(hir::ConstContext::ConstFn) - && self.tcx.features().staged_api - && is_const_stable_const_fn(self.tcx, self.def_id().to_def_id()) + && self.tcx.features().staged_api() + && is_safe_to_expose_on_stable_const_fn(self.tcx, self.def_id().to_def_id()) } fn is_async(&self) -> bool { @@ -90,50 +92,38 @@ pub fn rustc_allow_const_fn_unstable( attr::rustc_allow_const_fn_unstable(tcx.sess, attrs).any(|name| name == feature_gate) } -/// Returns `true` if the given `const fn` is "const-stable". +/// Returns `true` if the given `const fn` is "safe to expose on stable". /// /// Panics if the given `DefId` does not refer to a `const fn`. /// -/// Const-stability is only relevant for `const fn` within a `staged_api` crate. Only "const-stable" -/// functions can be called in a const-context by users of the stable compiler. "const-stable" -/// functions are subject to more stringent restrictions than "const-unstable" functions: They -/// cannot use unstable features and can only call other "const-stable" functions. -pub fn is_const_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - // A default body in a `#[const_trait]` is not const-stable because const - // trait fns currently cannot be const-stable. We shouldn't - // restrict default bodies to only call const-stable functions. +/// This is relevant within a `staged_api` crate. Unlike with normal features, the use of unstable +/// const features *recursively* taints the functions that use them. This is to avoid accidentally +/// exposing e.g. the implementation of an unstable const intrinsic on stable. So we partition the +/// world into two functions: those that are safe to expose on stable (and hence may not use +/// unstable features, not even recursively), and those that are not. +pub fn is_safe_to_expose_on_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + // A default body in a `#[const_trait]` is not const-stable because const trait fns currently + // cannot be const-stable. These functions can't be called from anything stable, so we shouldn't + // restrict them to only call const-stable functions. if tcx.is_const_default_method(def_id) { + // FIXME(const_trait_impl): we have to eventually allow some of these if these things can ever be stable. + // They should probably behave like regular `const fn` for that... return false; } // Const-stability is only relevant for `const fn`. - assert!(tcx.is_const_fn_raw(def_id)); + assert!(tcx.is_const_fn(def_id)); - // A function is only const-stable if it has `#[rustc_const_stable]` or it the trait it belongs - // to is const-stable. match tcx.lookup_const_stability(def_id) { - Some(stab) => stab.is_const_stable(), - None if is_parent_const_stable_trait(tcx, def_id) => { - // Remove this when `#![feature(const_trait_impl)]` is stabilized, - // returning `true` unconditionally. - tcx.dcx().span_delayed_bug( - tcx.def_span(def_id), - "trait implementations cannot be const stable yet", - ); - true + None => { + // Only marked functions can be trusted. Note that this may be a function in a + // non-staged-API crate where no recursive checks were done! + false + } + Some(stab) => { + // We consider things safe-to-expose if they are stable, if they don't have any explicit + // const stability attribute, or if they are marked as `const_stable_indirect`. + stab.is_const_stable() || stab.feature.is_none() || stab.const_stable_indirect } - None => false, // By default, items are not const stable. - } -} - -fn is_parent_const_stable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - let local_def_id = def_id.expect_local(); - let hir_id = tcx.local_def_id_to_hir_id(local_def_id); - - let parent_owner_id = tcx.parent_hir_id(hir_id).owner; - if !tcx.is_const_trait_impl_raw(parent_owner_id.to_def_id()) { - return false; } - - tcx.lookup_const_stability(parent_owner_id).is_some_and(|stab| stab.is_const_stable()) } diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 5c4a899f28a..3ac06ae6491 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -26,8 +26,16 @@ use crate::{errors, fluent_generated}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Status { - Allowed, - Unstable(Symbol), + Unstable { + /// The feature that must be enabled to use this operation. + gate: Symbol, + /// Whether it is allowed to use this operation from stable `const fn`. + /// This will usually be `false`. + safe_to_expose_on_stable: bool, + /// We indicate whether this is a function call, since we can use targeted + /// diagnostics for "callee is not safe to expose om stable". + is_function_call: bool, + }, Forbidden, } @@ -40,9 +48,9 @@ pub enum DiagImportance { Secondary, } -/// An operation that is not *always* allowed in a const context. +/// An operation that is *not allowed* in a const context. pub trait NonConstOp<'tcx>: std::fmt::Debug { - /// Returns an enum indicating whether this operation is allowed within the given item. + /// Returns an enum indicating whether this operation can be enabled with a feature gate. fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Forbidden } @@ -114,7 +122,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { if let Ok(Some(ImplSource::UserDefined(data))) = implsrc { // FIXME(effects) revisit this - if !tcx.is_const_trait_impl_raw(data.impl_def_id) { + if !tcx.is_const_trait_impl(data.impl_def_id) { let span = tcx.def_span(data.impl_def_id); err.subdiagnostic(errors::NonConstImplNote { span }); } @@ -166,7 +174,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { let note = match self_ty.kind() { FnDef(def_id, ..) => { let span = tcx.def_span(*def_id); - if ccx.tcx.is_const_fn_raw(*def_id) { + if ccx.tcx.is_const_fn(*def_id) { span_bug!(span, "calling const FnDef errored when it shouldn't"); } @@ -298,30 +306,78 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { /// /// Contains the name of the feature that would allow the use of this function. #[derive(Debug)] -pub(crate) struct FnCallUnstable(pub DefId, pub Option<Symbol>); +pub(crate) struct FnCallUnstable { + pub def_id: DefId, + pub feature: Symbol, + pub safe_to_expose_on_stable: bool, +} impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { - fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { - let FnCallUnstable(def_id, feature) = *self; - - let mut err = ccx - .dcx() - .create_err(errors::UnstableConstFn { span, def_path: ccx.tcx.def_path_str(def_id) }); + fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { + Status::Unstable { + gate: self.feature, + safe_to_expose_on_stable: self.safe_to_expose_on_stable, + is_function_call: true, + } + } + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { + let mut err = ccx.dcx().create_err(errors::UnstableConstFn { + span, + def_path: ccx.tcx.def_path_str(self.def_id), + }); // FIXME: make this translatable #[allow(rustc::untranslatable_diagnostic)] - if ccx.is_const_stable_const_fn() { - err.help(fluent_generated::const_eval_const_stable); - } else if ccx.tcx.sess.is_nightly_build() { - if let Some(feature) = feature { - err.help(format!("add `#![feature({feature})]` to the crate attributes to enable")); - } - } + err.help(format!("add `#![feature({})]` to the crate attributes to enable", self.feature)); err } } +/// A call to an intrinsic that is just not const-callable at all. +#[derive(Debug)] +pub(crate) struct IntrinsicNonConst { + pub name: Symbol, +} + +impl<'tcx> NonConstOp<'tcx> for IntrinsicNonConst { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { + ccx.dcx().create_err(errors::NonConstIntrinsic { + span, + name: self.name, + kind: ccx.const_kind(), + }) + } +} + +/// A call to an intrinsic that is just not const-callable at all. +#[derive(Debug)] +pub(crate) struct IntrinsicUnstable { + pub name: Symbol, + pub feature: Symbol, + pub const_stable_indirect: bool, +} + +impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable { + fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { + Status::Unstable { + gate: self.feature, + safe_to_expose_on_stable: self.const_stable_indirect, + // We do *not* want to suggest to mark the intrinsic as `const_stable_indirect`, + // that's not a trivial change! + is_function_call: false, + } + } + + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { + ccx.dcx().create_err(errors::UnstableIntrinsic { + span, + name: self.name, + feature: self.feature, + }) + } +} + #[derive(Debug)] pub(crate) struct Coroutine(pub hir::CoroutineKind); impl<'tcx> NonConstOp<'tcx> for Coroutine { @@ -331,7 +387,11 @@ impl<'tcx> NonConstOp<'tcx> for Coroutine { hir::CoroutineSource::Block, ) = self.0 { - Status::Unstable(sym::const_async_blocks) + Status::Unstable { + gate: sym::const_async_blocks, + safe_to_expose_on_stable: false, + is_function_call: false, + } } else { Status::Forbidden } diff --git a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs index f98234ce7f3..0173a528c22 100644 --- a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs @@ -15,7 +15,7 @@ use crate::check_consts::rustc_allow_const_fn_unstable; /// elaboration. pub fn checking_enabled(ccx: &ConstCx<'_, '_>) -> bool { // Const-stable functions must always use the stable live drop checker... - if ccx.is_const_stable_const_fn() { + if ccx.enforce_recursive_const_stability() { // ...except if they have the feature flag set via `rustc_allow_const_fn_unstable`. return rustc_allow_const_fn_unstable( ccx.tcx, @@ -24,7 +24,7 @@ pub fn checking_enabled(ccx: &ConstCx<'_, '_>) -> bool { ); } - ccx.tcx.features().const_precise_live_drops + ccx.tcx.features().const_precise_live_drops() } /// Look for live drops in a const context. diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 7358a6c6d22..547030a1854 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -6,13 +6,10 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::LangItem; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::*; -use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty}; use rustc_middle::{bug, mir}; -use rustc_trait_selection::traits::{ - ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext, -}; -use tracing::{instrument, trace}; +use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; +use tracing::instrument; use super::ConstCx; @@ -195,50 +192,8 @@ impl Qualif for NeedsNonConstDrop { return false; } - // FIXME(effects): If `destruct` is not a `const_trait`, - // or effects are disabled in this crate, then give up. - let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span)); - if !cx.tcx.has_host_param(destruct_def_id) || !cx.tcx.features().effects { - return NeedsDrop::in_any_value_of_ty(cx, ty); - } - - let obligation = Obligation::new( - cx.tcx, - ObligationCause::dummy_with_span(cx.body.span), - cx.param_env, - ty::TraitRef::new(cx.tcx, destruct_def_id, [ - ty::GenericArg::from(ty), - ty::GenericArg::from(cx.tcx.expected_host_effect_param_for_body(cx.def_id())), - ]), - ); - - let infcx = cx.tcx.infer_ctxt().build(); - let mut selcx = SelectionContext::new(&infcx); - let Some(impl_src) = selcx.select(&obligation).ok().flatten() else { - // If we couldn't select a const destruct candidate, then it's bad - return true; - }; - - trace!(?impl_src); - - if !matches!( - impl_src, - ImplSource::Builtin(BuiltinImplSource::Misc, _) | ImplSource::Param(_) - ) { - // If our const destruct candidate is not ConstDestruct or implied by the param env, - // then it's bad - return true; - } - - if impl_src.borrow_nested_obligations().is_empty() { - return false; - } - - // If we had any errors, then it's bad - let ocx = ObligationCtxt::new(&infcx); - ocx.register_obligations(impl_src.nested_obligations()); - let errors = ocx.select_all_or_error(); - !errors.is_empty() + // FIXME(effects): Reimplement const drop checking. + NeedsDrop::in_any_value_of_ty(cx, ty) } fn in_adt_inherently<'tcx>( diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index fd05664e2f2..6686413bf02 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -11,7 +11,8 @@ use rustc_span::{Span, Symbol}; use super::CompileTimeMachine; use crate::errors::{self, FrameNote, ReportErrorExt}; use crate::interpret::{ - ErrorHandled, Frame, InterpError, InterpErrorInfo, MachineStopType, err_inval, err_machine_stop, + ErrorHandled, Frame, InterpErrorInfo, InterpErrorKind, MachineStopType, err_inval, + err_machine_stop, }; /// The CTFE machine has some custom error kinds. @@ -57,7 +58,7 @@ impl MachineStopType for ConstEvalErrKind { } } -/// The errors become [`InterpError::MachineStop`] when being raised. +/// The errors become [`InterpErrorKind::MachineStop`] when being raised. impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind { fn into(self) -> InterpErrorInfo<'tcx> { err_machine_stop!(self).into() @@ -124,7 +125,7 @@ pub fn get_span_and_frames<'tcx>( /// `get_span_and_frames`. pub(super) fn report<'tcx, C, F, E>( tcx: TyCtxt<'tcx>, - error: InterpError<'tcx>, + error: InterpErrorKind<'tcx>, span: Span, get_span_and_frames: C, mk: F, diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 672353e629d..7319c251bbd 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -18,7 +18,7 @@ use tracing::{debug, instrument, trace}; use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine}; use crate::const_eval::CheckAlignment; use crate::interpret::{ - CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpError, + CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpErrorKind, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup, create_static_alloc, eval_nullary_intrinsic, intern_const_alloc_recursive, interp_ok, throw_exhaust, }; @@ -463,7 +463,7 @@ fn report_validation_error<'tcx>( error: InterpErrorInfo<'tcx>, alloc_id: AllocId, ) -> ErrorHandled { - if !matches!(error.kind(), InterpError::UndefinedBehavior(_)) { + if !matches!(error.kind(), InterpErrorKind::UndefinedBehavior(_)) { // Some other error happened during validation, e.g. an unsupported operation. return report_eval_error(ecx, cid, error); } diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index ca0993f0580..037fdcbcf9b 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -1,25 +1,8 @@ +use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_span::symbol::Symbol; -use {rustc_attr as attr, rustc_hir as hir}; - -/// Whether the `def_id` is an unstable const fn and what feature gate(s) are necessary to enable -/// it. -pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<(Symbol, Option<Symbol>)> { - if tcx.is_const_fn_raw(def_id) { - let const_stab = tcx.lookup_const_stability(def_id)?; - match const_stab.level { - attr::StabilityLevel::Unstable { implied_by, .. } => { - Some((const_stab.feature, implied_by)) - } - attr::StabilityLevel::Stable { .. } => None, - } - } else { - None - } -} pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { let parent_id = tcx.local_parent(def_id); diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 2db43a0f787..d54c5b750f0 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -219,7 +219,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { } /// "Intercept" a function call, because we have something special to do for it. - /// All `#[rustc_do_not_const_check]` functions should be hooked here. + /// All `#[rustc_do_not_const_check]` functions MUST be hooked here. /// If this returns `Some` function, which may be `instance` or a different function with /// compatible arguments, then evaluation should continue with that function. /// If this returns `None`, the function call has been handled and the function has returned. @@ -431,8 +431,8 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // sensitive check here. But we can at least rule out functions that are not const at // all. That said, we have to allow calling functions inside a trait marked with // #[const_trait]. These *are* const-checked! - // FIXME: why does `is_const_fn_raw` not classify them as const? - if (!ecx.tcx.is_const_fn_raw(def) && !ecx.tcx.is_const_default_method(def)) + // FIXME(effects): why does `is_const_fn` not classify them as const? + if (!ecx.tcx.is_const_fn(def) && !ecx.tcx.is_const_default_method(def)) || ecx.tcx.has_attr(def, sym::rustc_do_not_const_check) { // We certainly do *not* want to actually call the fn diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index ba19f642795..e2e4754a45c 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -43,7 +43,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( // We go to `usize` as we cannot allocate anything bigger anyway. let (field_count, variant, down) = match ty.kind() { - ty::Array(_, len) => (len.eval_target_usize(tcx.tcx, param_env) as usize, None, op), + ty::Array(_, len) => (len.try_to_target_usize(tcx.tcx)? as usize, None, op), ty::Adt(def, _) if def.variants().is_empty() => { return None; } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index c943236affc..38b87b72634 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -9,12 +9,12 @@ use rustc_errors::{ use rustc_hir::ConstContext; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::mir::interpret::{ - CheckInAllocMsg, CtfeProvenance, ExpectedKind, InterpError, InvalidMetaKind, + CheckInAllocMsg, CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind, InvalidProgramInfo, Misalignment, Pointer, PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, }; use rustc_middle::ty::{self, Mutability, Ty}; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use rustc_target::abi::WrappingRange; use rustc_target::abi::call::AdjustForForeignAbiError; @@ -44,11 +44,15 @@ pub(crate) struct MutablePtrInFinal { } #[derive(Diagnostic)] -#[diag(const_eval_unstable_in_stable)] -pub(crate) struct UnstableInStable { +#[diag(const_eval_unstable_in_stable_exposed)] +pub(crate) struct UnstableInStableExposed { pub gate: String, #[primary_span] pub span: Span, + #[help(const_eval_is_function_call)] + pub is_function_call: bool, + /// Need to duplicate the field so that fluent also provides it as a variable... + pub is_function_call2: bool, #[suggestion( const_eval_unstable_sugg, code = "#[rustc_const_unstable(feature = \"...\", issue = \"...\")]\n", @@ -118,6 +122,34 @@ pub(crate) struct UnstableConstFn { } #[derive(Diagnostic)] +#[diag(const_eval_unstable_intrinsic)] +#[help] +pub(crate) struct UnstableIntrinsic { + #[primary_span] + pub span: Span, + pub name: Symbol, + pub feature: Symbol, +} + +#[derive(Diagnostic)] +#[diag(const_eval_unmarked_const_fn_exposed)] +#[help] +pub(crate) struct UnmarkedConstFnExposed { + #[primary_span] + pub span: Span, + pub def_path: String, +} + +#[derive(Diagnostic)] +#[diag(const_eval_unmarked_intrinsic_exposed)] +#[help] +pub(crate) struct UnmarkedIntrinsicExposed { + #[primary_span] + pub span: Span, + pub def_path: String, +} + +#[derive(Diagnostic)] #[diag(const_eval_mutable_ref_escaping, code = E0764)] pub(crate) struct MutableRefEscaping { #[primary_span] @@ -154,6 +186,15 @@ pub(crate) struct NonConstFnCall { } #[derive(Diagnostic)] +#[diag(const_eval_non_const_intrinsic)] +pub(crate) struct NonConstIntrinsic { + #[primary_span] + pub span: Span, + pub name: Symbol, + pub kind: ConstContext, +} + +#[derive(Diagnostic)] #[diag(const_eval_unallowed_op_in_const_context)] pub(crate) struct UnallowedOpInConstContext { #[primary_span] @@ -835,23 +876,23 @@ impl ReportErrorExt for UnsupportedOpInfo { } } -impl<'tcx> ReportErrorExt for InterpError<'tcx> { +impl<'tcx> ReportErrorExt for InterpErrorKind<'tcx> { fn diagnostic_message(&self) -> DiagMessage { match self { - InterpError::UndefinedBehavior(ub) => ub.diagnostic_message(), - InterpError::Unsupported(e) => e.diagnostic_message(), - InterpError::InvalidProgram(e) => e.diagnostic_message(), - InterpError::ResourceExhaustion(e) => e.diagnostic_message(), - InterpError::MachineStop(e) => e.diagnostic_message(), + InterpErrorKind::UndefinedBehavior(ub) => ub.diagnostic_message(), + InterpErrorKind::Unsupported(e) => e.diagnostic_message(), + InterpErrorKind::InvalidProgram(e) => e.diagnostic_message(), + InterpErrorKind::ResourceExhaustion(e) => e.diagnostic_message(), + InterpErrorKind::MachineStop(e) => e.diagnostic_message(), } } fn add_args<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) { match self { - InterpError::UndefinedBehavior(ub) => ub.add_args(diag), - InterpError::Unsupported(e) => e.add_args(diag), - InterpError::InvalidProgram(e) => e.add_args(diag), - InterpError::ResourceExhaustion(e) => e.add_args(diag), - InterpError::MachineStop(e) => e.add_args(&mut |name, value| { + InterpErrorKind::UndefinedBehavior(ub) => ub.add_args(diag), + InterpErrorKind::Unsupported(e) => e.add_args(diag), + InterpErrorKind::InvalidProgram(e) => e.add_args(diag), + InterpErrorKind::ResourceExhaustion(e) => e.add_args(diag), + InterpErrorKind::MachineStop(e) => e.add_args(&mut |name, value| { diag.arg(name, value); }), } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 4945563f4a4..85d99900c6c 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -471,7 +471,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Don't forget to mark "initially live" locals as live. self.storage_live_for_always_live_locals()?; }; - res.inspect_err(|_| { + res.inspect_err_kind(|_| { // Don't show the incomplete stack frame in the error stacktrace. self.stack_mut().pop(); }) diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 1def3d08328..64b15611316 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -391,7 +391,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let ptr = self.read_pointer(src)?; let val = Immediate::new_slice( ptr, - length.eval_target_usize(*self.tcx, self.param_env), + length + .try_to_target_usize(*self.tcx) + .expect("expected monomorphic const in const eval"), self, ); self.write_immediate(val, dest) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 02dd7821ef6..a1c773a4b80 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -19,7 +19,7 @@ use rustc_trait_selection::traits::ObligationCtxt; use tracing::{debug, instrument, trace}; use super::{ - Frame, FrameInfo, GlobalId, InterpError, InterpErrorInfo, InterpResult, MPlaceTy, Machine, + Frame, FrameInfo, GlobalId, InterpErrorInfo, InterpErrorKind, InterpResult, MPlaceTy, Machine, MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, err_inval, interp_ok, throw_inval, throw_ub, throw_ub_custom, }; @@ -73,7 +73,7 @@ where } impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { - type LayoutOfResult = Result<TyAndLayout<'tcx>, InterpError<'tcx>>; + type LayoutOfResult = Result<TyAndLayout<'tcx>, InterpErrorKind<'tcx>>; #[inline] fn layout_tcx_at_span(&self) -> Span { @@ -82,20 +82,25 @@ impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { } #[inline] - fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> InterpError<'tcx> { + fn handle_layout_err( + &self, + err: LayoutError<'tcx>, + _: Span, + _: Ty<'tcx>, + ) -> InterpErrorKind<'tcx> { err_inval!(Layout(err)) } } impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> { - type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpError<'tcx>>; + type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpErrorKind<'tcx>>; fn handle_fn_abi_err( &self, err: FnAbiError<'tcx>, _span: Span, _fn_abi_request: FnAbiRequest<'tcx>, - ) -> InterpError<'tcx> { + ) -> InterpErrorKind<'tcx> { match err { FnAbiError::Layout(err) => err_inval!(Layout(err)), FnAbiError::AdjustForForeignAbi(err) => { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 540898ec645..4e603f57c56 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -324,13 +324,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { dist.checked_neg().unwrap(), // i64::MIN is impossible as no allocation can be that large CheckInAllocMsg::OffsetFromTest, ) - .map_err(|_| { + .map_err_kind(|_| { // Make the error more specific. err_ub_custom!( fluent::const_eval_offset_from_different_allocations, name = intrinsic_name, ) - .into() })?; // Perform division by size to compute return value. diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 13641ef2bd3..b6120ce82fe 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -17,8 +17,8 @@ use rustc_hir as hir; use rustc_middle::bug; use rustc_middle::mir::interpret::ValidationErrorKind::{self, *}; use rustc_middle::mir::interpret::{ - ExpectedKind, InterpError, InterpErrorInfo, InvalidMetaKind, Misalignment, PointerKind, - Provenance, UnsupportedOpInfo, ValidationErrorInfo, alloc_range, interp_ok, + ExpectedKind, InterpErrorKind, InvalidMetaKind, Misalignment, PointerKind, Provenance, + UnsupportedOpInfo, ValidationErrorInfo, alloc_range, interp_ok, }; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; @@ -37,8 +37,8 @@ use super::{ // for the validation errors #[rustfmt::skip] -use super::InterpError::UndefinedBehavior as Ub; -use super::InterpError::Unsupported as Unsup; +use super::InterpErrorKind::UndefinedBehavior as Ub; +use super::InterpErrorKind::Unsupported as Unsup; use super::UndefinedBehaviorInfo::*; use super::UnsupportedOpInfo::*; @@ -97,20 +97,19 @@ macro_rules! try_validation { ($e:expr, $where:expr, $( $( $p:pat_param )|+ => $kind: expr ),+ $(,)? ) => {{ - $e.map_err(|e| { + $e.map_err_kind(|e| { // We catch the error and turn it into a validation failure. We are okay with // allocation here as this can only slow down builds that fail anyway. - let (kind, backtrace) = e.into_parts(); - match kind { + match e { $( $($p)|+ => { err_validation_failure!( $where, $kind - ).into() + ) } ),+, - _ => InterpErrorInfo::from_parts(kind, backtrace), + e => e, } })? }}; @@ -1230,11 +1229,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // No need for an alignment check here, this is not an actual memory access. let alloc = self.ecx.get_ptr_alloc(mplace.ptr(), size)?.expect("we already excluded size 0"); - alloc.get_bytes_strip_provenance().map_err(|err| { + alloc.get_bytes_strip_provenance().map_err_kind(|kind| { // Some error happened, try to provide a more detailed description. // For some errors we might be able to provide extra information. // (This custom logic does not fit the `try_validation!` macro.) - let (kind, backtrace) = err.into_parts(); match kind { Ub(InvalidUninitBytes(Some((_alloc_id, access)))) | Unsup(ReadPointerAsInt(Some((_alloc_id, access)))) => { // Some byte was uninitialized, determine which @@ -1247,14 +1245,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.path.push(PathElem::ArrayElem(i)); if matches!(kind, Ub(InvalidUninitBytes(_))) { - err_validation_failure!(self.path, Uninit { expected }).into() + err_validation_failure!(self.path, Uninit { expected }) } else { - err_validation_failure!(self.path, PointerAsInt { expected }).into() + err_validation_failure!(self.path, PointerAsInt { expected }) } } // Propagate upwards (that will also check for unexpected errors). - _ => return InterpErrorInfo::from_parts(kind, backtrace), + err => err, } })?; @@ -1368,12 +1366,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { v.reset_padding(val)?; interp_ok(()) }) - .map_err(|err| { + .map_err_info(|err| { if !matches!( err.kind(), err_ub!(ValidationError { .. }) - | InterpError::InvalidProgram(_) - | InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField) + | InterpErrorKind::InvalidProgram(_) + | InterpErrorKind::Unsupported(UnsupportedOpInfo::ExternTypeField) ) { bug!( "Unexpected error during validation: {}", diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 39e2d3b4ebb..0490195caf4 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -10,7 +10,6 @@ #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(slice_ptr_get)] -#![feature(strict_provenance)] #![feature(trait_alias)] #![feature(try_blocks)] #![feature(unqualified_local_imports)] diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index d73cf11ee64..5a477143a62 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -13,7 +13,7 @@ ena = "0.14.3" indexmap = { version = "2.4.0" } jobserver_crate = { version = "0.1.28", package = "jobserver" } measureme = "11" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" rustc-rayon = { version = "0.5.0", optional = true } rustc-stable-hash = { version = "0.1.0", features = ["nightly"] } rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 7cb013fdbd8..53ff67f60e3 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -2,15 +2,13 @@ //! //! Algorithm based on Loukas Georgiadis, //! "Linear-Time Algorithms for Dominators and Related Problems", -//! <ftp://ftp.cs.princeton.edu/techreports/2005/737.pdf> +//! <https://www.cs.princeton.edu/techreports/2005/737.pdf> //! //! Additionally useful is the original Lengauer-Tarjan paper on this subject, //! "A Fast Algorithm for Finding Dominators in a Flowgraph" //! Thomas Lengauer and Robert Endre Tarjan. //! <https://www.cs.princeton.edu/courses/archive/spr03/cs423/download/dominators.pdf> -use std::cmp::Ordering; - use rustc_index::{Idx, IndexSlice, IndexVec}; use super::ControlFlowGraph; @@ -64,9 +62,6 @@ fn is_small_path_graph<G: ControlFlowGraph>(g: &G) -> bool { } fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> { - // compute the post order index (rank) for each node - let mut post_order_rank = IndexVec::from_elem_n(0, graph.num_nodes()); - // We allocate capacity for the full set of nodes, because most of the time // most of the nodes *are* reachable. let mut parent: IndexVec<PreorderIndex, PreorderIndex> = @@ -83,12 +78,10 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> { pre_order_to_real.push(graph.start_node()); parent.push(PreorderIndex::ZERO); // the parent of the root node is the root for now. real_to_pre_order[graph.start_node()] = Some(PreorderIndex::ZERO); - let mut post_order_idx = 0; // Traverse the graph, collecting a number of things: // // * Preorder mapping (to it, and back to the actual ordering) - // * Postorder mapping (used exclusively for `cmp_in_dominator_order` on the final product) // * Parents for each vertex in the preorder tree // // These are all done here rather than through one of the 'standard' @@ -104,8 +97,6 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> { continue 'recurse; } } - post_order_rank[pre_order_to_real[frame.pre_order_idx]] = post_order_idx; - post_order_idx += 1; stack.pop(); } @@ -282,7 +273,7 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> { let time = compute_access_time(start_node, &immediate_dominators); - Inner { post_order_rank, immediate_dominators, time } + Inner { immediate_dominators, time } } /// Evaluate the link-eval virtual forest, providing the currently minimum semi @@ -348,7 +339,6 @@ fn compress( /// Tracks the list of dominators for each node. #[derive(Clone, Debug)] struct Inner<N: Idx> { - post_order_rank: IndexVec<N, usize>, // Even though we track only the immediate dominator of each node, it's // possible to get its full list of dominators by looking up the dominator // of each dominator. @@ -379,17 +369,6 @@ impl<Node: Idx> Dominators<Node> { } } - /// Provide deterministic ordering of nodes such that, if any two nodes have a dominator - /// relationship, the dominator will always precede the dominated. (The relative ordering - /// of two unrelated nodes will also be consistent, but otherwise the order has no - /// meaning.) This method cannot be used to determine if either Node dominates the other. - pub fn cmp_in_dominator_order(&self, lhs: Node, rhs: Node) -> Ordering { - match &self.kind { - Kind::Path => lhs.index().cmp(&rhs.index()), - Kind::General(g) => g.post_order_rank[rhs].cmp(&g.post_order_rank[lhs]), - } - } - /// Returns true if `a` dominates `b`. /// /// # Panics diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index fba2707922b..afac08ae6f8 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -33,7 +33,6 @@ #![feature(ptr_alignment_type)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] -#![feature(strict_provenance)] #![feature(test)] #![feature(thread_id_value)] #![feature(type_alias_impl_trait)] diff --git a/compiler/rustc_error_codes/src/error_codes/E0636.md b/compiler/rustc_error_codes/src/error_codes/E0636.md index 57cf72db556..41fd701a8ed 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0636.md +++ b/compiler/rustc_error_codes/src/error_codes/E0636.md @@ -1,9 +1,9 @@ -A `#![feature]` attribute was declared multiple times. +The same feature is enabled multiple times with `#![feature]` attributes Erroneous code example: ```compile_fail,E0636 #![allow(stable_features)] #![feature(rust1)] -#![feature(rust1)] // error: the feature `rust1` has already been declared +#![feature(rust1)] // error: the feature `rust1` has already been enabled ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0705.md b/compiler/rustc_error_codes/src/error_codes/E0705.md index 317f3a47eff..e7d7d74cc4c 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0705.md +++ b/compiler/rustc_error_codes/src/error_codes/E0705.md @@ -1,6 +1,6 @@ #### Note: this error code is no longer emitted by the compiler. -A `#![feature]` attribute was declared for a feature that is stable in the +A `#![feature]` attribute was used for a feature that is stable in the current edition, but not in all editions. Erroneous code example: diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 1adb6b9dcfe..0ccc71ae06c 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -58,9 +58,9 @@ impl HumanReadableErrorType { struct Margin { /// The available whitespace in the left that can be consumed when centering. pub whitespace_left: usize, - /// The column of the beginning of left-most span. + /// The column of the beginning of leftmost span. pub span_left: usize, - /// The column of the end of right-most span. + /// The column of the end of rightmost span. pub span_right: usize, /// The beginning of the line to be displayed. pub computed_left: usize, @@ -128,7 +128,7 @@ impl Margin { } else { 0 }; - // We want to show as much as possible, max_line_len is the right-most boundary for the + // We want to show as much as possible, max_line_len is the rightmost boundary for the // relevant code. self.computed_right = max(max_line_len, self.computed_left); @@ -685,7 +685,7 @@ impl HumanEmitter { buffer.puts(line_offset, code_offset, "...", Style::LineNumber); } if margin.was_cut_right(line_len) { - // We have stripped some code after the right-most span end, make it clear we did so. + // We have stripped some code after the rightmost span end, make it clear we did so. buffer.puts(line_offset, code_offset + taken - 3, "...", Style::LineNumber); } buffer.puts(line_offset, 0, &self.maybe_anonymized(line_index), Style::LineNumber); diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 57bac9d09d5..fcf3352bfc5 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -25,7 +25,7 @@ expand_collapse_debuginfo_illegal = illegal value for attribute #[collapse_debuginfo(no|external|yes)] expand_count_repetition_misplaced = - `count` can not be placed inside the inner-most repetition + `count` can not be placed inside the innermost repetition expand_crate_name_in_cfg_attr = `crate_name` within an `#![cfg_attr]` attribute is forbidden diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index f0cfe133a49..7e4bc508e5c 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1,5 +1,6 @@ use std::default::Default; use std::iter; +use std::path::Component::Prefix; use std::path::{Path, PathBuf}; use std::rc::Rc; @@ -865,7 +866,9 @@ impl SyntaxExtension { }) .unwrap_or_else(|| (None, helper_attrs)); let stability = attr::find_stability(sess, attrs, span); - let const_stability = attr::find_const_stability(sess, attrs, span); + // We set `is_const_fn` false to avoid getting any implicit const stability. + let const_stability = + attr::find_const_stability(sess, attrs, span, /* is_const_fn */ false); let body_stability = attr::find_body_stability(sess, attrs); if let Some((_, sp)) = const_stability { sess.dcx().emit_err(errors::MacroConstStability { @@ -1293,7 +1296,12 @@ pub fn resolve_path(sess: &Session, path: impl Into<PathBuf>, span: Span) -> PRe base_path.push(path); Ok(base_path) } else { - Ok(path) + // This ensures that Windows verbatim paths are fixed if mixed path separators are used, + // which can happen when `concat!` is used to join paths. + match path.components().next() { + Some(Prefix(prefix)) if prefix.kind().is_verbatim() => Ok(path.components().collect()), + _ => Ok(path), + } } } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index ea06415801f..dc6aa110f45 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -11,7 +11,8 @@ use rustc_ast::{ use rustc_attr as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_feature::{ - ACCEPTED_FEATURES, AttributeSafety, Features, REMOVED_FEATURES, UNSTABLE_FEATURES, + ACCEPTED_LANG_FEATURES, AttributeSafety, EnabledLangFeature, EnabledLibFeature, Features, + REMOVED_LANG_FEATURES, UNSTABLE_LANG_FEATURES, }; use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::validate_attr; @@ -52,7 +53,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - let mut features = Features::default(); - // Process all features declared in the code. + // Process all features enabled in the code. for attr in krate_attrs { for mi in feature_list(attr) { let name = match mi.ident() { @@ -76,8 +77,8 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - } }; - // If the declared feature has been removed, issue an error. - if let Some(f) = REMOVED_FEATURES.iter().find(|f| name == f.feature.name) { + // If the enabled feature has been removed, issue an error. + if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| name == f.feature.name) { sess.dcx().emit_err(FeatureRemoved { span: mi.span(), reason: f.reason.map(|reason| FeatureRemovedReason { reason }), @@ -85,14 +86,17 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - continue; } - // If the declared feature is stable, record it. - if let Some(f) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) { - let since = Some(Symbol::intern(f.since)); - features.set_declared_lang_feature(name, mi.span(), since); + // If the enabled feature is stable, record it. + if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| name == f.name) { + features.set_enabled_lang_feature(EnabledLangFeature { + gate_name: name, + attr_sp: mi.span(), + stable_since: Some(Symbol::intern(f.since)), + }); continue; } - // If `-Z allow-features` is used and the declared feature is + // If `-Z allow-features` is used and the enabled feature is // unstable and not also listed as one of the allowed features, // issue an error. if let Some(allowed) = sess.opts.unstable_opts.allow_features.as_ref() { @@ -102,9 +106,8 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - } } - // If the declared feature is unstable, record it. - if let Some(f) = UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name) { - (f.set_enabled)(&mut features); + // If the enabled feature is unstable, record it. + if UNSTABLE_LANG_FEATURES.iter().find(|f| name == f.name).is_some() { // When the ICE comes from core, alloc or std (approximation of the standard // library), there's a chance that the person hitting the ICE may be using // -Zbuild-std or similar with an untested target. The bug is probably in the @@ -115,13 +118,19 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - { sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed); } - features.set_declared_lang_feature(name, mi.span(), None); + + features.set_enabled_lang_feature(EnabledLangFeature { + gate_name: name, + attr_sp: mi.span(), + stable_since: None, + }); continue; } - // Otherwise, the feature is unknown. Record it as a lib feature. - // It will be checked later. - features.set_declared_lib_feature(name, mi.span()); + // Otherwise, the feature is unknown. Enable it as a lib feature. + // It will be checked later whether the feature really exists. + features + .set_enabled_lib_feature(EnabledLibFeature { gate_name: name, attr_sp: mi.span() }); // Similar to above, detect internal lib features to suppress // the ICE message that asks for a report. @@ -396,7 +405,7 @@ impl<'a> StripUnconfigured<'a> { /// If attributes are not allowed on expressions, emit an error for `attr` #[instrument(level = "trace", skip(self))] pub(crate) fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { - if self.features.is_some_and(|features| !features.stmt_expr_attributes) + if self.features.is_some_and(|features| !features.stmt_expr_attributes()) && !attr.span.allows_unstable(sym::stmt_expr_attributes) { let mut err = feature_err( diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index a872b12e744..5ffafcaa542 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -867,7 +867,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { | Annotatable::FieldDef(..) | Annotatable::Variant(..) => panic!("unexpected annotatable"), }; - if self.cx.ecfg.features.proc_macro_hygiene { + if self.cx.ecfg.features.proc_macro_hygiene() { return; } feature_err( @@ -905,7 +905,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } } - if !self.cx.ecfg.features.proc_macro_hygiene { + if !self.cx.ecfg.features.proc_macro_hygiene() { annotatable.visit_with(&mut GateProcMacroInput { sess: &self.cx.sess }); } } diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index c4ba98f581e..810a5d30c7e 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -23,11 +23,11 @@ pub(crate) enum MetaVarExpr { /// Ignore a meta-variable for repetition without expansion. Ignore(Ident), - /// The index of the repetition at a particular depth, where 0 is the inner-most + /// The index of the repetition at a particular depth, where 0 is the innermost /// repetition. The `usize` is the depth. Index(usize), - /// The length of the repetition at a particular depth, where 0 is the inner-most + /// The length of the repetition at a particular depth, where 0 is the innermost /// repetition. The `usize` is the depth. Len(usize), } diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 2edd289625e..1345f06d5ac 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -119,16 +119,16 @@ pub(super) fn parse( result } -/// Asks for the `macro_metavar_expr` feature if it is not already declared +/// Asks for the `macro_metavar_expr` feature if it is not enabled fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &Session, span: Span) { - if !features.macro_metavar_expr { + if !features.macro_metavar_expr() { let msg = "meta-variable expressions are unstable"; feature_err(sess, sym::macro_metavar_expr, span, msg).emit(); } } fn maybe_emit_macro_metavar_expr_concat_feature(features: &Features, sess: &Session, span: Span) { - if !features.macro_metavar_expr_concat { + if !features.macro_metavar_expr_concat() { let msg = "the `concat` meta-variable expression is unstable"; feature_err(sess, sym::macro_metavar_expr_concat, span, msg).emit(); } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index fb6fe0bb1d7..34811ca2b35 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -570,7 +570,7 @@ fn lockstep_iter_size( } } -/// Used solely by the `count` meta-variable expression, counts the outer-most repetitions at a +/// Used solely by the `count` meta-variable expression, counts the outermost repetitions at a /// given optional nested depth. /// /// For example, a macro parameter of `$( { $( $foo:ident ),* } )*` called with `{ a, b } { c }`: diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 0c5fe6e8d8b..ac922f184dd 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -9,7 +9,7 @@ macro_rules! declare_features { $(#[doc = $doc:tt])* (accepted, $feature:ident, $ver:expr, $issue:expr), )+) => { /// Formerly unstable features that have now been accepted (stabilized). - pub const ACCEPTED_FEATURES: &[Feature] = &[ + pub const ACCEPTED_LANG_FEATURES: &[Feature] = &[ $(Feature { name: sym::$feature, since: $ver, @@ -353,6 +353,9 @@ declare_features! ( (accepted, repr_packed, "1.33.0", Some(33158)), /// Allows `#[repr(transparent)]` attribute on newtype structs. (accepted, repr_transparent, "1.28.0", Some(43036)), + /// Allows enums like Result<T, E> to be used across FFI, if T's niche value can + /// be used to describe E or vice-versa. + (accepted, result_ffi_guarantees, "CURRENT_RUSTC_VERSION", Some(110503)), /// Allows return-position `impl Trait` in traits. (accepted, return_position_impl_trait_in_trait, "1.75.0", Some(91611)), /// Allows code like `let x: &'static u32 = &42` to work (RFC 1414). @@ -361,6 +364,8 @@ declare_features! ( (accepted, self_in_typedefs, "1.32.0", Some(49303)), /// Allows `Self` struct constructor (RFC 2302). (accepted, self_struct_ctor, "1.32.0", Some(51994)), + /// Shortern the tail expression lifetime + (accepted, shorter_tail_lifetimes, "CURRENT_RUSTC_VERSION", Some(123739)), /// Allows using subslice patterns, `[a, .., b]` and `[a, xs @ .., b]`. (accepted, slice_patterns, "1.42.0", Some(62254)), /// Allows use of `&foo[a..b]` as a slicing syntax. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 477760a4597..0069b07ad62 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -12,33 +12,31 @@ use crate::{Features, Stability}; type GateFn = fn(&Features) -> bool; -macro_rules! cfg_fn { - ($field: ident) => { - (|features| features.$field) as GateFn - }; -} - pub type GatedCfg = (Symbol, Symbol, GateFn); /// `cfg(...)`'s that are feature gated. const GATED_CFGS: &[GatedCfg] = &[ // (name in cfg, feature, function to check if the feature is enabled) - (sym::overflow_checks, sym::cfg_overflow_checks, cfg_fn!(cfg_overflow_checks)), - (sym::ub_checks, sym::cfg_ub_checks, cfg_fn!(cfg_ub_checks)), - (sym::target_thread_local, sym::cfg_target_thread_local, cfg_fn!(cfg_target_thread_local)), + (sym::overflow_checks, sym::cfg_overflow_checks, Features::cfg_overflow_checks), + (sym::ub_checks, sym::cfg_ub_checks, Features::cfg_ub_checks), + (sym::target_thread_local, sym::cfg_target_thread_local, Features::cfg_target_thread_local), ( sym::target_has_atomic_equal_alignment, sym::cfg_target_has_atomic_equal_alignment, - cfg_fn!(cfg_target_has_atomic_equal_alignment), - ), - (sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)), - (sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)), - (sym::version, sym::cfg_version, cfg_fn!(cfg_version)), - (sym::relocation_model, sym::cfg_relocation_model, cfg_fn!(cfg_relocation_model)), - (sym::sanitizer_cfi_generalize_pointers, sym::cfg_sanitizer_cfi, cfg_fn!(cfg_sanitizer_cfi)), - (sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, cfg_fn!(cfg_sanitizer_cfi)), + Features::cfg_target_has_atomic_equal_alignment, + ), + ( + sym::target_has_atomic_load_store, + sym::cfg_target_has_atomic, + Features::cfg_target_has_atomic, + ), + (sym::sanitize, sym::cfg_sanitize, Features::cfg_sanitize), + (sym::version, sym::cfg_version, Features::cfg_version), + (sym::relocation_model, sym::cfg_relocation_model, Features::cfg_relocation_model), + (sym::sanitizer_cfi_generalize_pointers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi), + (sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi), // this is consistent with naming of the compiler flag it's for - (sym::fmt_debug, sym::fmt_debug, cfg_fn!(fmt_debug)), + (sym::fmt_debug, sym::fmt_debug, Features::fmt_debug), ]; /// Find a gated cfg determined by the `pred`icate which is given the cfg's name. @@ -220,7 +218,7 @@ macro_rules! gated { safety: AttributeSafety::Unsafe, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)), + gate: Gated(Stability::Unstable, sym::$gate, $msg, Features::$gate), } }; (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => { @@ -231,7 +229,7 @@ macro_rules! gated { safety: AttributeSafety::Unsafe, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)), + gate: Gated(Stability::Unstable, sym::$attr, $msg, Features::$attr), } }; ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => { @@ -242,7 +240,7 @@ macro_rules! gated { safety: AttributeSafety::Normal, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)), + gate: Gated(Stability::Unstable, sym::$gate, $msg, Features::$gate), } }; ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => { @@ -253,7 +251,7 @@ macro_rules! gated { safety: AttributeSafety::Normal, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)), + gate: Gated(Stability::Unstable, sym::$attr, $msg, Features::$attr), } }; } @@ -282,7 +280,7 @@ macro_rules! rustc_attr { safety: AttributeSafety::Normal, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, cfg_fn!(rustc_attrs)), + gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, Features::rustc_attrs), } }; } @@ -620,11 +618,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "allow_internal_unstable side-steps feature gating and stability checks", ), gated!( - rustc_allow_const_fn_unstable, Normal, - template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, EncodeCrossCrate::No, - "rustc_allow_const_fn_unstable side-steps feature gating and stability checks" - ), - gated!( allow_internal_unsafe, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint", ), @@ -841,8 +834,13 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::Yes, INTERNAL_UNSTABLE ), rustc_attr!( - rustc_runtime, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, INTERNAL_UNSTABLE + rustc_const_stable_indirect, Normal, + template!(Word), WarnFollowing, EncodeCrossCrate::No, IMPL_DETAIL, + ), + gated!( + rustc_allow_const_fn_unstable, Normal, + template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, EncodeCrossCrate::No, + "rustc_allow_const_fn_unstable side-steps feature gating and stability checks" ), // ========================================================================== @@ -935,7 +933,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ Stability::Unstable, sym::rustc_attrs, "diagnostic items compiler internal support for linting", - cfg_fn!(rustc_attrs), + Features::rustc_attrs, ), }, gated!( @@ -1193,7 +1191,7 @@ pub static BUILTIN_ATTRIBUTE_MAP: LazyLock<FxHashMap<Symbol, &BuiltinAttribute>> pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool { match sym { sym::on_unimplemented => true, - sym::do_not_recommend => features.do_not_recommend, + sym::do_not_recommend => features.do_not_recommend(), _ => false, } } diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 8f4c0b0ac95..9f42d3ec45c 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -94,13 +94,13 @@ impl UnstableFeatures { fn find_lang_feature_issue(feature: Symbol) -> Option<NonZero<u32>> { // Search in all the feature lists. - if let Some(f) = UNSTABLE_FEATURES.iter().find(|f| f.feature.name == feature) { - return f.feature.issue; + if let Some(f) = UNSTABLE_LANG_FEATURES.iter().find(|f| f.name == feature) { + return f.issue; } - if let Some(f) = ACCEPTED_FEATURES.iter().find(|f| f.name == feature) { + if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature) { return f.issue; } - if let Some(f) = REMOVED_FEATURES.iter().find(|f| f.feature.name == feature) { + if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| f.feature.name == feature) { return f.feature.issue; } panic!("feature `{feature}` is not declared anywhere"); @@ -127,12 +127,14 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZero<u } } -pub use accepted::ACCEPTED_FEATURES; +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, }; -pub use removed::REMOVED_FEATURES; -pub use unstable::{Features, INCOMPATIBLE_FEATURES, UNSTABLE_FEATURES}; +pub use removed::REMOVED_LANG_FEATURES; +pub use unstable::{ + EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES, UNSTABLE_LANG_FEATURES, +}; diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index d797fee000d..fe3a67fd667 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -14,7 +14,7 @@ macro_rules! declare_features { $(#[doc = $doc:tt])* (removed, $feature:ident, $ver:expr, $issue:expr, $reason:expr), )+) => { /// Formerly unstable features that have now been removed. - pub const REMOVED_FEATURES: &[RemovedFeature] = &[ + pub const REMOVED_LANG_FEATURES: &[RemovedFeature] = &[ $(RemovedFeature { feature: Feature { name: sym::$feature, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 1067156958d..a81058e6ea1 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -6,11 +6,6 @@ use rustc_span::symbol::{Symbol, sym}; use super::{Feature, to_nonzero}; -pub struct UnstableFeature { - pub feature: Feature, - pub set_enabled: fn(&mut Features), -} - #[derive(PartialEq)] enum FeatureStatus { Default, @@ -30,86 +25,98 @@ macro_rules! status_to_enum { }; } +/// A set of features to be used by later passes. +/// +/// There are two ways to check if a language feature `foo` is enabled: +/// - Directly with the `foo` method, e.g. `if tcx.features().foo() { ... }`. +/// - With the `enabled` method, e.g. `if tcx.features.enabled(sym::foo) { ... }`. +/// +/// The former is preferred. `enabled` should only be used when the feature symbol is not a +/// constant, e.g. a parameter, or when the feature is a library feature. +#[derive(Clone, Default, Debug)] +pub struct Features { + /// `#![feature]` attrs for language features, for error reporting. + enabled_lang_features: Vec<EnabledLangFeature>, + /// `#![feature]` attrs for non-language (library) features. + enabled_lib_features: Vec<EnabledLibFeature>, + /// `enabled_lang_features` + `enabled_lib_features`. + enabled_features: FxHashSet<Symbol>, +} + +/// Information about an enabled language feature. +#[derive(Debug, Copy, Clone)] +pub struct EnabledLangFeature { + /// Name of the feature gate guarding the language feature. + pub gate_name: Symbol, + /// Span of the `#[feature(...)]` attribute. + pub attr_sp: Span, + /// If the lang feature is stable, the version number when it was stabilized. + pub stable_since: Option<Symbol>, +} + +/// Information abhout an enabled library feature. +#[derive(Debug, Copy, Clone)] +pub struct EnabledLibFeature { + pub gate_name: Symbol, + pub attr_sp: Span, +} + +impl Features { + /// `since` should be set for stable features that are nevertheless enabled with a `#[feature]` + /// attribute, indicating since when they are stable. + pub fn set_enabled_lang_feature(&mut self, lang_feat: EnabledLangFeature) { + self.enabled_lang_features.push(lang_feat); + self.enabled_features.insert(lang_feat.gate_name); + } + + pub fn set_enabled_lib_feature(&mut self, lib_feat: EnabledLibFeature) { + self.enabled_lib_features.push(lib_feat); + self.enabled_features.insert(lib_feat.gate_name); + } + + /// Returns a list of [`EnabledLangFeature`] with info about: + /// + /// - Feature gate name. + /// - The span of the `#[feature]` attribute. + /// - For stable language features, version info for when it was stabilized. + pub fn enabled_lang_features(&self) -> &Vec<EnabledLangFeature> { + &self.enabled_lang_features + } + + pub fn enabled_lib_features(&self) -> &Vec<EnabledLibFeature> { + &self.enabled_lib_features + } + + pub fn enabled_features(&self) -> &FxHashSet<Symbol> { + &self.enabled_features + } + + /// Is the given feature enabled (via `#[feature(...)]`)? + pub fn enabled(&self, feature: Symbol) -> bool { + self.enabled_features.contains(&feature) + } +} + macro_rules! declare_features { ($( $(#[doc = $doc:tt])* ($status:ident, $feature:ident, $ver:expr, $issue:expr), )+) => { /// Unstable language features that are being implemented or being /// considered for acceptance (stabilization) or removal. - pub const UNSTABLE_FEATURES: &[UnstableFeature] = &[ - $(UnstableFeature { - feature: Feature { - name: sym::$feature, - since: $ver, - issue: to_nonzero($issue), - }, - // Sets this feature's corresponding bool within `features`. - set_enabled: |features| features.$feature = true, + pub const UNSTABLE_LANG_FEATURES: &[Feature] = &[ + $(Feature { + name: sym::$feature, + since: $ver, + issue: to_nonzero($issue), }),+ ]; - const NUM_FEATURES: usize = UNSTABLE_FEATURES.len(); - - /// A set of features to be used by later passes. - #[derive(Clone, Default, Debug)] - pub struct Features { - /// `#![feature]` attrs for language features, for error reporting. - /// "declared" here means that the feature is actually enabled in the current crate. - pub declared_lang_features: Vec<(Symbol, Span, Option<Symbol>)>, - /// `#![feature]` attrs for non-language (library) features. - /// "declared" here means that the feature is actually enabled in the current crate. - pub declared_lib_features: Vec<(Symbol, Span)>, - /// `declared_lang_features` + `declared_lib_features`. - pub declared_features: FxHashSet<Symbol>, - /// Active state of individual features (unstable only). - $( - $(#[doc = $doc])* - pub $feature: bool - ),+ - } - impl Features { - pub fn set_declared_lang_feature( - &mut self, - symbol: Symbol, - span: Span, - since: Option<Symbol> - ) { - self.declared_lang_features.push((symbol, span, since)); - self.declared_features.insert(symbol); - } - - pub fn set_declared_lib_feature(&mut self, symbol: Symbol, span: Span) { - self.declared_lib_features.push((symbol, span)); - self.declared_features.insert(symbol); - } - - /// This is intended for hashing the set of active features. - /// - /// The expectation is that this produces much smaller code than other alternatives. - /// - /// Note that the total feature count is pretty small, so this is not a huge array. - #[inline] - pub fn all_features(&self) -> [u8; NUM_FEATURES] { - [$(self.$feature as u8),+] - } - - /// Is the given feature explicitly declared, i.e. named in a - /// `#![feature(...)]` within the code? - pub fn declared(&self, feature: Symbol) -> bool { - self.declared_features.contains(&feature) - } - - /// Is the given feature active (enabled by the user)? - /// - /// Panics if the symbol doesn't correspond to a declared feature. - pub fn active(&self, feature: Symbol) -> bool { - match feature { - $( sym::$feature => self.$feature, )* - - _ => panic!("`{}` was not listed in `declare_features`", feature), + $( + pub fn $feature(&self) -> bool { + self.enabled_features.contains(&sym::$feature) } - } + )* /// Some features are known to be incomplete and using them is likely to have /// unanticipated results, such as compiler crashes. We warn the user about these @@ -119,8 +126,11 @@ macro_rules! declare_features { $( sym::$feature => status_to_enum!($status) == FeatureStatus::Incomplete, )* - // Accepted/removed features aren't in this file but are never incomplete. - _ if self.declared_features.contains(&feature) => false, + _ if self.enabled_features.contains(&feature) => { + // Accepted/removed features and library features aren't in this file but + // are never incomplete. + false + } _ => panic!("`{}` was not listed in `declare_features`", feature), } } @@ -132,7 +142,7 @@ macro_rules! declare_features { $( sym::$feature => status_to_enum!($status) == FeatureStatus::Internal, )* - _ if self.declared_features.contains(&feature) => { + _ if self.enabled_features.contains(&feature) => { // This could be accepted/removed, or a libs feature. // Accepted/removed features aren't in this file but are never internal // (a removed feature might have been internal, but that's now irrelevant). @@ -580,17 +590,12 @@ declare_features! ( (incomplete, repr128, "1.16.0", Some(56071)), /// Allows `repr(simd)` and importing the various simd intrinsics. (unstable, repr_simd, "1.4.0", Some(27731)), - /// Allows enums like Result<T, E> to be used across FFI, if T's niche value can - /// be used to describe E or vise-versa. - (unstable, result_ffi_guarantees, "1.80.0", Some(110503)), /// Allows bounding the return type of AFIT/RPITIT. (unstable, return_type_notation, "1.70.0", Some(109417)), /// Allows `extern "rust-cold"`. (unstable, rust_cold_cc, "1.63.0", Some(97544)), /// Allows use of x86 SHA512, SM3 and SM4 target-features and intrinsics (unstable, sha512_sm_x86, "1.82.0", Some(126624)), - /// Shortern the tail expression lifetime - (unstable, shorter_tail_lifetimes, "1.79.0", Some(123739)), /// Allows the use of SIMD types in functions declared in `extern` blocks. (unstable, simd_ffi, "1.0.0", Some(27731)), /// Allows specialization of implementations (RFC 1210). @@ -598,7 +603,7 @@ declare_features! ( /// Allows attributes on expressions and non-item statements. (unstable, stmt_expr_attributes, "1.6.0", Some(15701)), /// Allows lints part of the strict provenance effort. - (unstable, strict_provenance, "1.61.0", Some(95228)), + (unstable, strict_provenance_lints, "1.61.0", Some(130351)), /// Allows string patterns to dereference values to match them. (unstable, string_deref_patterns, "1.67.0", Some(87121)), /// Allows the use of `#[target_feature]` on safe functions. diff --git a/compiler/rustc_fluent_macro/src/fluent.rs b/compiler/rustc_fluent_macro/src/fluent.rs index ead72e2a0e1..d4604c27e6d 100644 --- a/compiler/rustc_fluent_macro/src/fluent.rs +++ b/compiler/rustc_fluent_macro/src/fluent.rs @@ -8,6 +8,7 @@ use fluent_syntax::ast::{ Attribute, Entry, Expression, Identifier, InlineExpression, Message, Pattern, PatternElement, }; use fluent_syntax::parser::ParserError; +use proc_macro::tracked_path::path; use proc_macro::{Diagnostic, Level, Span}; use proc_macro2::TokenStream; use quote::quote; @@ -99,8 +100,7 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok let crate_name = Ident::new(&crate_name, resource_str.span()); - // As this macro also outputs an `include_str!` for this file, the macro will always be - // re-executed when the file changes. + path(absolute_ftl_path.to_str().unwrap()); let resource_contents = match read_to_string(absolute_ftl_path) { Ok(resource_contents) => resource_contents, Err(e) => { diff --git a/compiler/rustc_fluent_macro/src/lib.rs b/compiler/rustc_fluent_macro/src/lib.rs index 6e5add24bcc..3ad51fa1e64 100644 --- a/compiler/rustc_fluent_macro/src/lib.rs +++ b/compiler/rustc_fluent_macro/src/lib.rs @@ -6,6 +6,7 @@ #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] #![feature(rustdoc_internals)] +#![feature(track_path)] #![warn(unreachable_pub)] // tidy-alphabetical-end diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 009c6c4aea5..1c268c8bbe0 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -6,8 +6,8 @@ use rustc_ast::{ LitKind, TraitObjectSyntax, UintTy, }; pub use rustc_ast::{ - BinOp, BinOpKind, BindingMode, BorrowKind, ByRef, CaptureBy, ImplPolarity, IsAuto, Movability, - Mutability, UnOp, + BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy, + ImplPolarity, IsAuto, Movability, Mutability, UnOp, }; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; @@ -502,19 +502,16 @@ pub enum GenericArgsParentheses { ParenSugar, } -/// A modifier on a trait bound. +/// The modifiers on a trait bound. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] -pub enum TraitBoundModifier { - /// `Type: Trait` - None, - /// `Type: !Trait` - Negative, - /// `Type: ?Trait` - Maybe, - /// `Type: const Trait` - Const, - /// `Type: ~const Trait` - MaybeConst, +pub struct TraitBoundModifiers { + pub constness: BoundConstness, + pub polarity: BoundPolarity, +} + +impl TraitBoundModifiers { + pub const NONE: Self = + TraitBoundModifiers { constness: BoundConstness::Never, polarity: BoundPolarity::Positive }; } #[derive(Clone, Copy, Debug, HashStable_Generic)] @@ -583,7 +580,6 @@ pub enum GenericParamKind<'hir> { ty: &'hir Ty<'hir>, /// Optional default value for the const generic param default: Option<&'hir ConstArg<'hir>>, - is_host_effect: bool, synthetic: bool, }, } @@ -3180,7 +3176,7 @@ pub struct PolyTraitRef<'hir> { /// The constness and polarity of the trait ref. /// /// The `async` modifier is lowered directly into a different trait for now. - pub modifiers: TraitBoundModifier, + pub modifiers: TraitBoundModifiers, /// The `Foo<&'a T>` in `for<'a> Foo<&'a T>`. pub trait_ref: TraitRef<'hir>, @@ -4085,7 +4081,7 @@ mod size_asserts { static_assert_size!(ForeignItem<'_>, 88); static_assert_size!(ForeignItemKind<'_>, 56); static_assert_size!(GenericArg<'_>, 16); - static_assert_size!(GenericBound<'_>, 48); + static_assert_size!(GenericBound<'_>, 64); static_assert_size!(Generics<'_>, 56); static_assert_size!(Impl<'_>, 80); static_assert_size!(ImplItem<'_>, 88); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index ffe519b0e7d..322f8e2a517 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -935,7 +935,7 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>( match param.kind { GenericParamKind::Lifetime { .. } => {} GenericParamKind::Type { ref default, .. } => visit_opt!(visitor, visit_ty, default), - GenericParamKind::Const { ref ty, ref default, is_host_effect: _, synthetic: _ } => { + GenericParamKind::Const { ref ty, ref default, synthetic: _ } => { try_visit!(visitor.visit_ty(ty)); if let Some(ref default) = default { try_visit!(visitor.visit_const_param_default(param.hir_id, default)); diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index b161e6ba0fa..7d81f977c22 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -241,7 +241,7 @@ language_item_table! { DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); DerefPure, sym::deref_pure, deref_pure_trait, Target::Trait, GenericRequirement::Exact(0); DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None; - Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None; + LegacyReceiver, sym::legacy_receiver, legacy_receiver_trait, Target::Trait, GenericRequirement::None; Fn, kw::Fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); @@ -415,14 +415,6 @@ language_item_table! { String, sym::String, string, Target::Struct, GenericRequirement::None; CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None; - - EffectsRuntime, sym::EffectsRuntime, effects_runtime, Target::Struct, GenericRequirement::None; - EffectsNoRuntime, sym::EffectsNoRuntime, effects_no_runtime, Target::Struct, GenericRequirement::None; - EffectsMaybe, sym::EffectsMaybe, effects_maybe, Target::Struct, GenericRequirement::None; - EffectsIntersection, sym::EffectsIntersection, effects_intersection, Target::Trait, GenericRequirement::None; - EffectsIntersectionOutput, sym::EffectsIntersectionOutput, effects_intersection_output, Target::AssocTy, GenericRequirement::None; - EffectsCompat, sym::EffectsCompat, effects_compat, Target::Trait, GenericRequirement::Exact(1); - EffectsTyCompat, sym::EffectsTyCompat, effects_ty_compat, Target::Trait, GenericRequirement::Exact(1); } pub enum GenericRequirement { diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index 8947e7a2216..09ddc6ca9de 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -1,15 +1,9 @@ //! Bounds are restrictions applied to some types after they've been lowered from the HIR to the //! [`rustc_middle::ty`] form. -use rustc_data_structures::fx::FxIndexMap; use rustc_hir::LangItem; -use rustc_hir::def::DefKind; -use rustc_middle::ty::fold::FnMutDelegate; use rustc_middle::ty::{self, Ty, TyCtxt, Upcast}; use rustc_span::Span; -use rustc_span::def_id::DefId; - -use crate::hir_ty_lowering::OnlySelfBounds; /// Collects together a list of type bounds. These lists of bounds occur in many places /// in Rust's syntax: @@ -30,7 +24,6 @@ use crate::hir_ty_lowering::OnlySelfBounds; #[derive(Default, PartialEq, Eq, Clone, Debug)] pub(crate) struct Bounds<'tcx> { clauses: Vec<(ty::Clause<'tcx>, Span)>, - effects_min_tys: FxIndexMap<Ty<'tcx>, Span>, } impl<'tcx> Bounds<'tcx> { @@ -47,12 +40,9 @@ impl<'tcx> Bounds<'tcx> { pub(crate) fn push_trait_bound( &mut self, tcx: TyCtxt<'tcx>, - defining_def_id: DefId, bound_trait_ref: ty::PolyTraitRef<'tcx>, span: Span, polarity: ty::PredicatePolarity, - constness: ty::BoundConstness, - only_self_bounds: OnlySelfBounds, ) { let clause = ( bound_trait_ref @@ -68,127 +58,6 @@ impl<'tcx> Bounds<'tcx> { } else { self.clauses.push(clause); } - - // FIXME(effects): Lift this out of `push_trait_bound`, and move it somewhere else. - // Perhaps moving this into `lower_poly_trait_ref`, just like we lower associated - // type bounds. - if !tcx.features().effects || only_self_bounds.0 { - return; - } - // For `T: ~const Tr` or `T: const Tr`, we need to add an additional bound on the - // associated type of `<T as Tr>` and make sure that the effect is compatible. - let compat_val = match (tcx.def_kind(defining_def_id), constness) { - // FIXME(effects): revisit the correctness of this - (_, ty::BoundConstness::Const) => tcx.consts.false_, - // body owners that can have trait bounds - (DefKind::Const | DefKind::Fn | DefKind::AssocFn, ty::BoundConstness::ConstIfConst) => { - tcx.expected_host_effect_param_for_body(defining_def_id) - } - - (_, ty::BoundConstness::NotConst) => { - if !tcx.is_const_trait(bound_trait_ref.def_id()) { - return; - } - tcx.consts.true_ - } - (DefKind::Trait, ty::BoundConstness::ConstIfConst) => { - // we are in a trait, where `bound_trait_ref` could be: - // (1) a super trait `trait Foo: ~const Bar`. - // - This generates `<Self as Foo>::Effects: TyCompat<<Self as Bar>::Effects>` - // - // (2) a where clause `where for<..> Something: ~const Bar`. - // - This generates `for<..> <Self as Foo>::Effects: TyCompat<<Something as Bar>::Effects>` - let Some(own_fx) = tcx.associated_type_for_effects(defining_def_id) else { - tcx.dcx().span_delayed_bug(span, "should not have allowed `~const` on a trait that doesn't have `#[const_trait]`"); - return; - }; - let own_fx_ty = Ty::new_projection( - tcx, - own_fx, - ty::GenericArgs::identity_for_item(tcx, own_fx), - ); - let Some(their_fx) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) - else { - tcx.dcx().span_delayed_bug(span, "`~const` on trait without Effects assoc"); - return; - }; - let their_fx_ty = - Ty::new_projection(tcx, their_fx, bound_trait_ref.skip_binder().args); - let compat = tcx.require_lang_item(LangItem::EffectsTyCompat, Some(span)); - let clause = bound_trait_ref - .map_bound(|_| { - let trait_ref = ty::TraitRef::new(tcx, compat, [own_fx_ty, their_fx_ty]); - ty::ClauseKind::Trait(ty::TraitPredicate { - trait_ref, - polarity: ty::PredicatePolarity::Positive, - }) - }) - .upcast(tcx); - - self.clauses.push((clause, span)); - return; - } - - (DefKind::Impl { of_trait: true }, ty::BoundConstness::ConstIfConst) => { - // this is a where clause on an impl header. - // push `<T as Tr>::Effects` into the set for the `Min` bound. - let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else { - tcx.dcx().span_delayed_bug(span, "`~const` on trait without Effects assoc"); - return; - }; - - let ty = bound_trait_ref - .map_bound(|trait_ref| Ty::new_projection(tcx, assoc, trait_ref.args)); - - // When the user has written `for<'a, T> X<'a, T>: ~const Foo`, replace the - // binders to dummy ones i.e. `X<'static, ()>` so they can be referenced in - // the `Min` associated type properly (which doesn't allow using `for<>`) - // This should work for any bound variables as long as they don't have any - // bounds e.g. `for<T: Trait>`. - // FIXME(effects) reconsider this approach to allow compatibility with `for<T: Tr>` - let ty = tcx.replace_bound_vars_uncached(ty, FnMutDelegate { - regions: &mut |_| tcx.lifetimes.re_static, - types: &mut |_| tcx.types.unit, - consts: &mut |_| unimplemented!("`~const` does not support const binders"), - }); - - self.effects_min_tys.insert(ty, span); - return; - } - // for - // ``` - // trait Foo { type Bar: ~const Trait } - // ``` - // ensure that `<Self::Bar as Trait>::Effects: TyCompat<Self::Effects>`. - // - // FIXME(effects) this is equality for now, which wouldn't be helpful for a non-const implementor - // that uses a `Bar` that implements `Trait` with `Maybe` effects. - (DefKind::AssocTy, ty::BoundConstness::ConstIfConst) => { - // FIXME(effects): implement this - return; - } - // probably illegal in this position. - (_, ty::BoundConstness::ConstIfConst) => { - tcx.dcx().span_delayed_bug(span, "invalid `~const` encountered"); - return; - } - }; - // create a new projection type `<T as Tr>::Effects` - let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else { - tcx.dcx().span_delayed_bug( - span, - "`~const` trait bound has no effect assoc yet no errors encountered?", - ); - return; - }; - let self_ty = Ty::new_projection(tcx, assoc, bound_trait_ref.skip_binder().args); - // make `<T as Tr>::Effects: Compat<runtime>` - let new_trait_ref = - ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::EffectsCompat, Some(span)), [ - ty::GenericArg::from(self_ty), - compat_val.into(), - ]); - self.clauses.push((bound_trait_ref.rebind(new_trait_ref).upcast(tcx), span)); } pub(crate) fn push_projection_bound( @@ -210,15 +79,22 @@ impl<'tcx> Bounds<'tcx> { self.clauses.insert(0, (trait_ref.upcast(tcx), span)); } - pub(crate) fn clauses( - &self, - // FIXME(effects): remove tcx - _tcx: TyCtxt<'tcx>, - ) -> impl Iterator<Item = (ty::Clause<'tcx>, Span)> + '_ { - self.clauses.iter().cloned() + /// Push a `const` or `~const` bound as a `HostEffect` predicate. + pub(crate) fn push_const_bound( + &mut self, + tcx: TyCtxt<'tcx>, + bound_trait_ref: ty::PolyTraitRef<'tcx>, + host: ty::HostPolarity, + span: Span, + ) { + if tcx.is_const_trait(bound_trait_ref.def_id()) { + self.clauses.push((bound_trait_ref.to_host_effect_clause(tcx, host), span)); + } else { + tcx.dcx().span_delayed_bug(span, "tried to lower {host:?} bound for non-const trait"); + } } - pub(crate) fn effects_min_tys(&self) -> impl Iterator<Item = Ty<'tcx>> + '_ { - self.effects_min_tys.keys().copied() + pub(crate) fn clauses(&self) -> impl Iterator<Item = (ty::Clause<'tcx>, Span)> + '_ { + self.clauses.iter().cloned() } } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 94da3d4ea84..97f3f1c8ef2 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -22,7 +22,7 @@ use rustc_middle::ty::{ AdtDef, GenericArgKind, ParamEnv, RegionKind, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; -use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS}; +use rustc_session::lint::builtin::UNINHABITED_STATIC; use rustc_target::abi::FieldIdx; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; @@ -36,36 +36,25 @@ use super::compare_impl_item::{check_type_bounds, compare_impl_method, compare_i use super::*; use crate::check::intrinsicck::InlineAsmCtxt; -pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) { - match tcx.sess.target.is_abi_supported(abi) { - Some(true) => (), - Some(false) => { - struct_span_code_err!( - tcx.dcx(), - span, - E0570, - "`{abi}` is not a supported ABI for the current target", - ) - .emit(); - } - None => { - tcx.node_span_lint(UNSUPPORTED_CALLING_CONVENTIONS, hir_id, span, |lint| { - lint.primary_message("use of calling convention not supported on this target"); - }); - } +pub fn check_abi(tcx: TyCtxt<'_>, span: Span, abi: Abi) { + if !tcx.sess.target.is_abi_supported(abi) { + struct_span_code_err!( + tcx.dcx(), + span, + E0570, + "`{abi}` is not a supported ABI for the current target", + ) + .emit(); } } pub fn check_abi_fn_ptr(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) { - match tcx.sess.target.is_abi_supported(abi) { - Some(true) => (), - Some(false) | None => { - tcx.node_span_lint(UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, hir_id, span, |lint| { - lint.primary_message(format!( - "the calling convention {abi} is not supported on this target" - )); - }); - } + if !tcx.sess.target.is_abi_supported(abi) { + tcx.node_span_lint(UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, hir_id, span, |lint| { + lint.primary_message(format!( + "the calling convention {abi} is not supported on this target" + )); + }); } } @@ -705,7 +694,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { let hir::ItemKind::ForeignMod { abi, items } = it.kind else { return; }; - check_abi(tcx, it.hir_id(), it.span, abi); + check_abi(tcx, it.span, abi); match abi { Abi::RustIntrinsic => { @@ -1037,7 +1026,11 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { return; } - if let Some(len) = len_const.try_eval_target_usize(tcx, tcx.param_env(def.did())) { + // FIXME(repr_simd): This check is nice, but perhaps unnecessary due to the fact + // we do not expect users to implement their own `repr(simd)` types. If they could, + // this check is easily side-steppable by hiding the const behind normalization. + // The consequence is that the error is, in general, only observable post-mono. + if let Some(len) = len_const.try_to_target_usize(tcx) { if len == 0 { struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit(); return; @@ -1173,7 +1166,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) return; } - if adt.is_union() && !tcx.features().transparent_unions { + if adt.is_union() && !tcx.features().transparent_unions() { feature_err( &tcx.sess, sym::transparent_unions, @@ -1308,7 +1301,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { let repr_type_ty = def.repr().discr_type().to_ty(tcx); if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 { - if !tcx.features().repr128 { + if !tcx.features().repr128() { feature_err( &tcx.sess, sym::repr128, diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 75956165e87..02cf1a502f1 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -43,14 +43,13 @@ mod refine; /// - `impl_m`: type of the method we are checking /// - `trait_m`: the method in the trait /// - `impl_trait_ref`: the TraitRef corresponding to the trait implementation +#[instrument(level = "debug", skip(tcx))] pub(super) fn compare_impl_method<'tcx>( tcx: TyCtxt<'tcx>, impl_m: ty::AssocItem, trait_m: ty::AssocItem, impl_trait_ref: ty::TraitRef<'tcx>, ) { - debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref); - let _: Result<_, ErrorGuaranteed> = try { check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, false)?; compare_method_predicate_entailment(tcx, impl_m, trait_m, impl_trait_ref)?; @@ -167,8 +166,6 @@ fn compare_method_predicate_entailment<'tcx>( trait_m: ty::AssocItem, impl_trait_ref: ty::TraitRef<'tcx>, ) -> Result<(), ErrorGuaranteed> { - let trait_to_impl_args = impl_trait_ref.args; - // This node-id should be used for the `body_id` field on each // `ObligationCause` (and the `FnCtxt`). // @@ -183,27 +180,18 @@ fn compare_method_predicate_entailment<'tcx>( kind: impl_m.kind, }); - // Create mapping from impl to placeholder. - let impl_to_placeholder_args = GenericArgs::identity_for_item(tcx, impl_m.def_id); - - // Create mapping from trait to placeholder. - let trait_to_placeholder_args = - impl_to_placeholder_args.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_args); - debug!("compare_impl_method: trait_to_placeholder_args={:?}", trait_to_placeholder_args); + // Create mapping from trait method to impl method. + let impl_def_id = impl_m.container_id(tcx); + let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_m.def_id).rebase_onto( + tcx, + impl_m.container_id(tcx), + impl_trait_ref.args, + ); + debug!(?trait_to_impl_args); let impl_m_predicates = tcx.predicates_of(impl_m.def_id); let trait_m_predicates = tcx.predicates_of(trait_m.def_id); - // Create obligations for each predicate declared by the impl - // definition in the context of the trait's parameter - // environment. We can't just use `impl_env.caller_bounds`, - // however, because we want to replace all late-bound regions with - // region variables. - let impl_predicates = tcx.predicates_of(impl_m_predicates.parent.unwrap()); - let mut hybrid_preds = impl_predicates.instantiate_identity(tcx); - - debug!("compare_impl_method: impl_bounds={:?}", hybrid_preds); - // This is the only tricky bit of the new way we check implementation methods // We need to build a set of predicates where only the method-level bounds // are from the trait and we assume all other bounds from the implementation @@ -211,25 +199,43 @@ fn compare_method_predicate_entailment<'tcx>( // // We then register the obligations from the impl_m and check to see // if all constraints hold. - hybrid_preds.predicates.extend( - trait_m_predicates - .instantiate_own(tcx, trait_to_placeholder_args) - .map(|(predicate, _)| predicate), + let impl_predicates = tcx.predicates_of(impl_m_predicates.parent.unwrap()); + let mut hybrid_preds = impl_predicates.instantiate_identity(tcx).predicates; + hybrid_preds.extend( + trait_m_predicates.instantiate_own(tcx, trait_to_impl_args).map(|(predicate, _)| predicate), ); - // Construct trait parameter environment and then shift it into the placeholder viewpoint. - // The key step here is to update the caller_bounds's predicates to be - // the new hybrid bounds we computed. + // FIXME(effects): This should be replaced with a more dedicated method. + let is_conditionally_const = tcx.is_conditionally_const(impl_def_id); + if is_conditionally_const { + // Augment the hybrid param-env with the const conditions + // of the impl header and the trait method. + hybrid_preds.extend( + tcx.const_conditions(impl_def_id) + .instantiate_identity(tcx) + .into_iter() + .chain( + tcx.const_conditions(trait_m.def_id).instantiate_own(tcx, trait_to_impl_args), + ) + .map(|(trait_ref, _)| { + trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe) + }), + ); + } + let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id); - let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing); + let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds), Reveal::UserFacing); let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); + debug!(caller_bounds=?param_env.caller_bounds()); let infcx = &tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new_with_diagnostics(infcx); - debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds()); - - let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_args); + // Create obligations for each predicate declared by the impl + // definition in the context of the hybrid param-env. This makes + // sure that the impl's method's where clauses are not more + // restrictive than the trait's method (and the impl itself). + let impl_m_own_bounds = impl_m_predicates.instantiate_own_identity(); for (predicate, span) in impl_m_own_bounds { let normalize_cause = traits::ObligationCause::misc(span, impl_m_def_id); let predicate = ocx.normalize(&normalize_cause, param_env, predicate); @@ -243,6 +249,34 @@ fn compare_method_predicate_entailment<'tcx>( ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate)); } + // If we're within a const implementation, we need to make sure that the method + // does not assume stronger `~const` bounds than the trait definition. + // + // This registers the `~const` bounds of the impl method, which we will prove + // using the hybrid param-env that we earlier augmented with the const conditions + // from the impl header and trait method declaration. + if is_conditionally_const { + for (const_condition, span) in + tcx.const_conditions(impl_m.def_id).instantiate_own_identity() + { + let normalize_cause = traits::ObligationCause::misc(span, impl_m_def_id); + let const_condition = ocx.normalize(&normalize_cause, param_env, const_condition); + + let cause = + ObligationCause::new(span, impl_m_def_id, ObligationCauseCode::CompareImplItem { + impl_item_def_id: impl_m_def_id, + trait_item_def_id: trait_m.def_id, + kind: impl_m.kind, + }); + ocx.register_obligation(traits::Obligation::new( + tcx, + cause, + param_env, + const_condition.to_host_effect_clause(tcx, ty::HostPolarity::Maybe), + )); + } + } + // We now need to check that the signature of the impl method is // compatible with that of the trait method. We do this by // checking that `impl_fty <: trait_fty`. @@ -256,7 +290,6 @@ fn compare_method_predicate_entailment<'tcx>( // any associated types appearing in the fn arguments or return // type. - // Compute placeholder form of impl and trait method tys. let mut wf_tys = FxIndexSet::default(); let unnormalized_impl_sig = infcx.instantiate_binder_with_fresh_vars( @@ -267,9 +300,9 @@ fn compare_method_predicate_entailment<'tcx>( let norm_cause = ObligationCause::misc(impl_m_span, impl_m_def_id); let impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_impl_sig); - debug!("compare_impl_method: impl_fty={:?}", impl_sig); + debug!(?impl_sig); - let trait_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_to_placeholder_args); + let trait_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_to_impl_args); let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig); // Next, add all inputs and output as well-formed tys. Importantly, @@ -280,9 +313,7 @@ fn compare_method_predicate_entailment<'tcx>( // We also have to add the normalized trait signature // as we don't normalize during implied bounds computation. wf_tys.extend(trait_sig.inputs_and_output.iter()); - let trait_fty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(trait_sig)); - - debug!("compare_impl_method: trait_fty={:?}", trait_fty); + debug!(?trait_sig); // FIXME: We'd want to keep more accurate spans than "the method signature" when // processing the comparison between the trait and impl fn, but we sadly lose them @@ -298,6 +329,7 @@ fn compare_method_predicate_entailment<'tcx>( let emitted = report_trait_method_mismatch( infcx, cause, + param_env, terr, (trait_m, trait_sig), (impl_m, impl_sig), @@ -455,8 +487,6 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // just so we don't ICE during instantiation later. check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, true)?; - let trait_to_impl_args = impl_trait_ref.args; - let impl_m_hir_id = tcx.local_def_id_to_hir_id(impl_m_def_id); let return_span = tcx.hir().fn_decl_by_hir_id(impl_m_hir_id).unwrap().output.span(); let cause = @@ -466,18 +496,18 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( kind: impl_m.kind, }); - // Create mapping from impl to placeholder. - let impl_to_placeholder_args = GenericArgs::identity_for_item(tcx, impl_m.def_id); - - // Create mapping from trait to placeholder. - let trait_to_placeholder_args = - impl_to_placeholder_args.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_args); + // Create mapping from trait to impl (i.e. impl trait header + impl method identity args). + let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_m.def_id).rebase_onto( + tcx, + impl_m.container_id(tcx), + impl_trait_ref.args, + ); let hybrid_preds = tcx .predicates_of(impl_m.container_id(tcx)) .instantiate_identity(tcx) .into_iter() - .chain(tcx.predicates_of(trait_m.def_id).instantiate_own(tcx, trait_to_placeholder_args)) + .chain(tcx.predicates_of(trait_m.def_id).instantiate_own(tcx, trait_to_impl_args)) .map(|(clause, _)| clause); let param_env = ty::ParamEnv::new(tcx.mk_clauses_from_iter(hybrid_preds), Reveal::UserFacing); let param_env = traits::normalize_param_env_or_error( @@ -511,7 +541,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( .instantiate_binder_with_fresh_vars( return_span, infer::HigherRankedType, - tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_to_placeholder_args), + tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_to_impl_args), ) .fold_with(&mut collector); @@ -593,10 +623,10 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( hir.get_if_local(impl_m.def_id) .and_then(|node| node.fn_decl()) .map(|decl| (decl.output.span(), Cow::from("return type in trait"), false)), - Some(infer::ValuePairs::Terms(ExpectedFound { + Some(param_env.and(infer::ValuePairs::Terms(ExpectedFound { expected: trait_return_ty.into(), found: impl_return_ty.into(), - })), + }))), terr, false, ); @@ -620,6 +650,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( let emitted = report_trait_method_mismatch( infcx, cause, + param_env, terr, (trait_m, trait_sig), (impl_m, impl_sig), @@ -705,7 +736,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // Also, we only need to account for a difference in trait and impl args, // since we previously enforce that the trait method and impl method have the // same generics. - let num_trait_args = trait_to_impl_args.len(); + let num_trait_args = impl_trait_ref.args.len(); let num_impl_args = tcx.generics_of(impl_m.container_id(tcx)).own_params.len(); let ty = match ty.try_fold_with(&mut RemapHiddenTyRegions { tcx, @@ -933,6 +964,7 @@ impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> { fn report_trait_method_mismatch<'tcx>( infcx: &InferCtxt<'tcx>, mut cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, terr: TypeError<'tcx>, (trait_m, trait_sig): (ty::AssocItem, ty::FnSig<'tcx>), (impl_m, impl_sig): (ty::AssocItem, ty::FnSig<'tcx>), @@ -1018,10 +1050,10 @@ fn report_trait_method_mismatch<'tcx>( &mut diag, &cause, trait_err_span.map(|sp| (sp, Cow::from("type in trait"), false)), - Some(infer::ValuePairs::PolySigs(ExpectedFound { + Some(param_env.and(infer::ValuePairs::PolySigs(ExpectedFound { expected: ty::Binder::dummy(trait_sig), found: ty::Binder::dummy(impl_sig), - })), + }))), terr, false, ); @@ -1041,12 +1073,7 @@ fn check_region_bounds_on_impl_item<'tcx>( let trait_generics = tcx.generics_of(trait_m.def_id); let trait_params = trait_generics.own_counts().lifetimes; - debug!( - "check_region_bounds_on_impl_item: \ - trait_generics={:?} \ - impl_generics={:?}", - trait_generics, impl_generics - ); + debug!(?trait_generics, ?impl_generics); // Must have same number of early-bound lifetime parameters. // Unfortunately, if the user screws up the bounds, then this @@ -1710,8 +1737,7 @@ pub(super) fn compare_impl_const_raw( let trait_const_item = tcx.associated_item(trait_const_item_def); let impl_trait_ref = tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap().instantiate_identity(); - - debug!("compare_impl_const(impl_trait_ref={:?})", impl_trait_ref); + debug!(?impl_trait_ref); compare_number_of_generics(tcx, impl_const_item, trait_const_item, false)?; compare_generic_param_kinds(tcx, impl_const_item, trait_const_item, false)?; @@ -1722,6 +1748,7 @@ pub(super) fn compare_impl_const_raw( /// The equivalent of [compare_method_predicate_entailment], but for associated constants /// instead of associated functions. // FIXME(generic_const_items): If possible extract the common parts of `compare_{type,const}_predicate_entailment`. +#[instrument(level = "debug", skip(tcx))] fn compare_const_predicate_entailment<'tcx>( tcx: TyCtxt<'tcx>, impl_ct: ty::AssocItem, @@ -1736,13 +1763,14 @@ fn compare_const_predicate_entailment<'tcx>( // because we shouldn't really have to deal with lifetimes or // predicates. In fact some of this should probably be put into // shared functions because of DRY violations... - let impl_args = GenericArgs::identity_for_item(tcx, impl_ct.def_id); - let trait_to_impl_args = - impl_args.rebase_onto(tcx, impl_ct.container_id(tcx), impl_trait_ref.args); + let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_ct.def_id).rebase_onto( + tcx, + impl_ct.container_id(tcx), + impl_trait_ref.args, + ); // Create a parameter environment that represents the implementation's - // method. - // Compute placeholder form of impl and trait const tys. + // associated const. let impl_ty = tcx.type_of(impl_ct_def_id).instantiate_identity(); let trait_ty = tcx.type_of(trait_ct.def_id).instantiate(tcx, trait_to_impl_args); @@ -1759,14 +1787,14 @@ fn compare_const_predicate_entailment<'tcx>( // The predicates declared by the impl definition, the trait and the // associated const in the trait are assumed. let impl_predicates = tcx.predicates_of(impl_ct_predicates.parent.unwrap()); - let mut hybrid_preds = impl_predicates.instantiate_identity(tcx); - hybrid_preds.predicates.extend( + let mut hybrid_preds = impl_predicates.instantiate_identity(tcx).predicates; + hybrid_preds.extend( trait_ct_predicates .instantiate_own(tcx, trait_to_impl_args) .map(|(predicate, _)| predicate), ); - let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing); + let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds), Reveal::UserFacing); let param_env = traits::normalize_param_env_or_error( tcx, param_env, @@ -1776,7 +1804,7 @@ fn compare_const_predicate_entailment<'tcx>( let infcx = tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let impl_ct_own_bounds = impl_ct_predicates.instantiate_own(tcx, impl_args); + let impl_ct_own_bounds = impl_ct_predicates.instantiate_own_identity(); for (predicate, span) in impl_ct_own_bounds { let cause = ObligationCause::misc(span, impl_ct_def_id); let predicate = ocx.normalize(&cause, param_env, predicate); @@ -1787,20 +1815,15 @@ fn compare_const_predicate_entailment<'tcx>( // There is no "body" here, so just pass dummy id. let impl_ty = ocx.normalize(&cause, param_env, impl_ty); - - debug!("compare_const_impl: impl_ty={:?}", impl_ty); + debug!(?impl_ty); let trait_ty = ocx.normalize(&cause, param_env, trait_ty); - - debug!("compare_const_impl: trait_ty={:?}", trait_ty); + debug!(?trait_ty); let err = ocx.sup(&cause, param_env, trait_ty, impl_ty); if let Err(terr) = err { - debug!( - "checking associated const for compatibility: impl ty {:?}, trait ty {:?}", - impl_ty, trait_ty - ); + debug!(?impl_ty, ?trait_ty); // Locate the Span containing just the type of the offending impl let (ty, _) = tcx.hir().expect_impl_item(impl_ct_def_id).expect_const(); @@ -1824,10 +1847,10 @@ fn compare_const_predicate_entailment<'tcx>( &mut diag, &cause, trait_c_span.map(|span| (span, Cow::from("type in trait"), false)), - Some(infer::ValuePairs::Terms(ExpectedFound { + Some(param_env.and(infer::ValuePairs::Terms(ExpectedFound { expected: trait_ty.into(), found: impl_ty.into(), - })), + }))), terr, false, ); @@ -1845,14 +1868,13 @@ fn compare_const_predicate_entailment<'tcx>( ocx.resolve_regions_and_report_errors(impl_ct_def_id, &outlives_env) } +#[instrument(level = "debug", skip(tcx))] pub(super) fn compare_impl_ty<'tcx>( tcx: TyCtxt<'tcx>, impl_ty: ty::AssocItem, trait_ty: ty::AssocItem, impl_trait_ref: ty::TraitRef<'tcx>, ) { - debug!("compare_impl_type(impl_trait_ref={:?})", impl_trait_ref); - let _: Result<(), ErrorGuaranteed> = try { compare_number_of_generics(tcx, impl_ty, trait_ty, false)?; compare_generic_param_kinds(tcx, impl_ty, trait_ty, false)?; @@ -1864,20 +1886,25 @@ pub(super) fn compare_impl_ty<'tcx>( /// The equivalent of [compare_method_predicate_entailment], but for associated types /// instead of associated functions. +#[instrument(level = "debug", skip(tcx))] fn compare_type_predicate_entailment<'tcx>( tcx: TyCtxt<'tcx>, impl_ty: ty::AssocItem, trait_ty: ty::AssocItem, impl_trait_ref: ty::TraitRef<'tcx>, ) -> Result<(), ErrorGuaranteed> { - let impl_args = GenericArgs::identity_for_item(tcx, impl_ty.def_id); - let trait_to_impl_args = - impl_args.rebase_onto(tcx, impl_ty.container_id(tcx), impl_trait_ref.args); + let impl_def_id = impl_ty.container_id(tcx); + let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_ty.def_id).rebase_onto( + tcx, + impl_def_id, + impl_trait_ref.args, + ); let impl_ty_predicates = tcx.predicates_of(impl_ty.def_id); let trait_ty_predicates = tcx.predicates_of(trait_ty.def_id); - let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_args); + let impl_ty_own_bounds = impl_ty_predicates.instantiate_own_identity(); + // If there are no bounds, then there are no const conditions, so no need to check that here. if impl_ty_own_bounds.len() == 0 { // Nothing to check. return Ok(()); @@ -1887,29 +1914,46 @@ fn compare_type_predicate_entailment<'tcx>( // `ObligationCause` (and the `FnCtxt`). This is what // `regionck_item` expects. let impl_ty_def_id = impl_ty.def_id.expect_local(); - debug!("compare_type_predicate_entailment: trait_to_impl_args={:?}", trait_to_impl_args); + debug!(?trait_to_impl_args); // The predicates declared by the impl definition, the trait and the // associated type in the trait are assumed. let impl_predicates = tcx.predicates_of(impl_ty_predicates.parent.unwrap()); - let mut hybrid_preds = impl_predicates.instantiate_identity(tcx); - hybrid_preds.predicates.extend( + let mut hybrid_preds = impl_predicates.instantiate_identity(tcx).predicates; + hybrid_preds.extend( trait_ty_predicates .instantiate_own(tcx, trait_to_impl_args) .map(|(predicate, _)| predicate), ); - - debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds); + debug!(?hybrid_preds); let impl_ty_span = tcx.def_span(impl_ty_def_id); let normalize_cause = ObligationCause::misc(impl_ty_span, impl_ty_def_id); - let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing); + + let is_conditionally_const = tcx.is_conditionally_const(impl_ty.def_id); + if is_conditionally_const { + // Augment the hybrid param-env with the const conditions + // of the impl header and the trait assoc type. + hybrid_preds.extend( + tcx.const_conditions(impl_ty_predicates.parent.unwrap()) + .instantiate_identity(tcx) + .into_iter() + .chain( + tcx.const_conditions(trait_ty.def_id).instantiate_own(tcx, trait_to_impl_args), + ) + .map(|(trait_ref, _)| { + trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe) + }), + ); + } + + let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds), Reveal::UserFacing); let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); + debug!(caller_bounds=?param_env.caller_bounds()); + let infcx = tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds()); - for (predicate, span) in impl_ty_own_bounds { let cause = ObligationCause::misc(span, impl_ty_def_id); let predicate = ocx.normalize(&cause, param_env, predicate); @@ -1923,6 +1967,29 @@ fn compare_type_predicate_entailment<'tcx>( ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate)); } + if is_conditionally_const { + // Validate the const conditions of the impl associated type. + let impl_ty_own_const_conditions = + tcx.const_conditions(impl_ty.def_id).instantiate_own_identity(); + for (const_condition, span) in impl_ty_own_const_conditions { + let normalize_cause = traits::ObligationCause::misc(span, impl_ty_def_id); + let const_condition = ocx.normalize(&normalize_cause, param_env, const_condition); + + let cause = + ObligationCause::new(span, impl_ty_def_id, ObligationCauseCode::CompareImplItem { + impl_item_def_id: impl_ty_def_id, + trait_item_def_id: trait_ty.def_id, + kind: impl_ty.kind, + }); + ocx.register_obligation(traits::Obligation::new( + tcx, + cause, + param_env, + const_condition.to_host_effect_clause(tcx, ty::HostPolarity::Maybe), + )); + } + } + // Check that all obligations are satisfied by the implementation's // version. let errors = ocx.select_all_or_error(); @@ -2005,15 +2072,31 @@ pub(super) fn check_type_bounds<'tcx>( ObligationCause::new(impl_ty_span, impl_ty_def_id, code) }; - let obligations: Vec<_> = tcx + let mut obligations: Vec<_> = tcx .explicit_item_bounds(trait_ty.def_id) .iter_instantiated_copied(tcx, rebased_args) .map(|(concrete_ty_bound, span)| { - debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound); + debug!(?concrete_ty_bound); traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound) }) .collect(); - debug!("check_type_bounds: item_bounds={:?}", obligations); + + // Only in a const implementation do we need to check that the `~const` item bounds hold. + if tcx.is_conditionally_const(impl_ty_def_id) { + obligations.extend( + tcx.implied_const_bounds(trait_ty.def_id) + .iter_instantiated_copied(tcx, rebased_args) + .map(|(c, span)| { + traits::Obligation::new( + tcx, + mk_cause(span), + param_env, + c.to_host_effect_clause(tcx, ty::HostPolarity::Maybe), + ) + }), + ); + } + debug!(item_bounds=?obligations); // Normalize predicates with the assumption that the GAT may always normalize // to its definition type. This should be the param-env we use to *prove* the @@ -2032,7 +2115,7 @@ pub(super) fn check_type_bounds<'tcx>( } else { ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate) }; - debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate); + debug!(?normalized_predicate); obligation.predicate = normalized_predicate; ocx.register_obligation(obligation); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 06317a3b304..c75bdcec388 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -58,15 +58,9 @@ fn equate_intrinsic_type<'tcx>( // the host effect param should be invisible as it shouldn't matter // whether effects is enabled for the intrinsic provider crate. - let consts_count = if generics.host_effect_index.is_some() { - own_counts.consts - 1 - } else { - own_counts.consts - }; - if gen_count_ok(own_counts.lifetimes, n_lts, "lifetime") && gen_count_ok(own_counts.types, n_tps, "type") - && gen_count_ok(consts_count, n_cts, "const") + && gen_count_ok(own_counts.consts, n_cts, "const") { let _ = check_function_signature( tcx, diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index 71eb368185e..bbff00cd3b3 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -76,9 +76,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { let (size, ty) = match elem_ty.kind() { ty::Array(ty, len) => { - if let Some(len) = - len.try_eval_target_usize(self.tcx, self.tcx.param_env(adt.did())) - { + if let Some(len) = len.try_to_target_usize(self.tcx) { (len, *ty) } else { return None; diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 959b17b2d40..f2f9c69e49f 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -646,10 +646,10 @@ pub fn check_function_signature<'tcx>( &mut diag, &cause, None, - Some(infer::ValuePairs::PolySigs(ExpectedFound { + Some(param_env.and(infer::ValuePairs::PolySigs(ExpectedFound { expected: expected_sig, found: actual_sig, - })), + }))), err, false, ); diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 1a5f4659812..bfa088fdefc 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -167,9 +167,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h } } if let Some(tail_expr) = blk.expr { - if visitor.tcx.features().shorter_tail_lifetimes - && blk.span.edition().at_least_rust_2024() - { + if blk.span.edition().at_least_rust_2024() { visitor.terminating_scopes.insert(tail_expr.hir_id.local_id); } visitor.visit_expr(tail_expr); @@ -466,7 +464,8 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h hir::ExprKind::If(cond, then, Some(otherwise)) => { let expr_cx = visitor.cx; - let data = if expr.span.at_least_rust_2024() && visitor.tcx.features().if_let_rescope { + let data = if expr.span.at_least_rust_2024() && visitor.tcx.features().if_let_rescope() + { ScopeData::IfThenRescope } else { ScopeData::IfThen @@ -481,7 +480,8 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h hir::ExprKind::If(cond, then, None) => { let expr_cx = visitor.cx; - let data = if expr.span.at_least_rust_2024() && visitor.tcx.features().if_let_rescope { + let data = if expr.span.at_least_rust_2024() && visitor.tcx.features().if_let_rescope() + { ScopeData::IfThenRescope } else { ScopeData::IfThen diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index f788456d4e9..499e42d31c9 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -32,7 +32,8 @@ use rustc_trait_selection::traits::misc::{ use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ - self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc, + self, FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt, + WellFormedLoc, }; use rustc_type_ir::TypeFlags; use rustc_type_ir::solve::NoSolution; @@ -86,7 +87,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { self.body_def_id, ObligationCauseCode::WellFormed(loc), ); - self.ocx.register_obligation(traits::Obligation::new( + self.ocx.register_obligation(Obligation::new( self.tcx(), cause, self.param_env, @@ -110,7 +111,7 @@ where let mut wfcx = WfCheckingCtxt { ocx, span, body_def_id, param_env }; - if !tcx.features().trivial_bounds { + if !tcx.features().trivial_bounds() { wfcx.check_false_global_bounds() } f(&mut wfcx)?; @@ -913,15 +914,10 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => Ok(()), // Const parameters are well formed if their type is structural match. - hir::GenericParamKind::Const { - ty: hir_ty, - default: _, - is_host_effect: _, - synthetic: _, - } => { + hir::GenericParamKind::Const { ty: hir_ty, default: _, synthetic: _ } => { let ty = tcx.type_of(param.def_id).instantiate_identity(); - if tcx.features().unsized_const_params { + if tcx.features().unsized_const_params() { enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| { wfcx.register_bound( ObligationCause::new( @@ -935,7 +931,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), ); Ok(()) }) - } else if tcx.features().adt_const_params { + } else if tcx.features().adt_const_params() { enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| { wfcx.register_bound( ObligationCause::new( @@ -1178,7 +1174,7 @@ fn check_type_defn<'tcx>( wfcx.body_def_id, ObligationCauseCode::Misc, ); - wfcx.register_obligation(traits::Obligation::new( + wfcx.register_obligation(Obligation::new( tcx, cause, wfcx.param_env, @@ -1374,6 +1370,30 @@ fn check_impl<'tcx>( obligation.cause.span = hir_self_ty.span; } } + + // Ensure that the `~const` where clauses of the trait hold for the impl. + if tcx.is_conditionally_const(item.owner_id.def_id) { + for (bound, _) in + tcx.const_conditions(trait_ref.def_id).instantiate(tcx, trait_ref.args) + { + let bound = wfcx.normalize( + item.span, + Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)), + bound, + ); + wfcx.register_obligation(Obligation::new( + tcx, + ObligationCause::new( + hir_self_ty.span, + wfcx.body_def_id, + ObligationCauseCode::WellFormed(None), + ), + wfcx.param_env, + bound.to_host_effect_clause(tcx, ty::HostPolarity::Maybe), + )) + } + } + debug!(?obligations); wfcx.register_obligations(obligations); } @@ -1566,7 +1586,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id wfcx.body_def_id, ObligationCauseCode::WhereClause(def_id.to_def_id(), DUMMY_SP), ); - traits::Obligation::new(tcx, cause, wfcx.param_env, pred) + Obligation::new(tcx, cause, wfcx.param_env, pred) }); let predicates = predicates.instantiate_identity(tcx); @@ -1698,9 +1718,9 @@ fn check_method_receiver<'tcx>( return Ok(()); } - let arbitrary_self_types_level = if tcx.features().arbitrary_self_types_pointers { + let arbitrary_self_types_level = if tcx.features().arbitrary_self_types_pointers() { Some(ArbitrarySelfTypesLevel::WithPointers) - } else if tcx.features().arbitrary_self_types { + } else if tcx.features().arbitrary_self_types() { Some(ArbitrarySelfTypesLevel::Basic) } else { None @@ -1801,7 +1821,7 @@ fn receiver_is_valid<'tcx>( autoderef = autoderef.include_raw_pointers(); } - let receiver_trait_def_id = tcx.require_lang_item(LangItem::Receiver, Some(span)); + let receiver_trait_def_id = tcx.require_lang_item(LangItem::LegacyReceiver, Some(span)); // Keep dereferencing `receiver_ty` until we get to `self_ty`. while let Some((potential_self_ty, _)) = autoderef.next() { @@ -1857,7 +1877,7 @@ fn receiver_is_implemented<'tcx>( let tcx = wfcx.tcx(); let trait_ref = ty::TraitRef::new(tcx, receiver_trait_def_id, [receiver_ty]); - let obligation = traits::Obligation::new(tcx, cause, wfcx.param_env, trait_ref); + let obligation = Obligation::new(tcx, cause, wfcx.param_env, trait_ref); if wfcx.infcx.predicate_must_hold_modulo_regions(&obligation) { true @@ -2193,7 +2213,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { .unwrap_or(obligation_span); } - let obligation = traits::Obligation::new( + let obligation = Obligation::new( tcx, traits::ObligationCause::new( span, diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 76c75d976ee..b4f6b5a9dd2 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -364,6 +364,7 @@ pub(crate) fn coerce_unsized_info<'tcx>( .err_ctxt() .report_mismatched_types( &cause, + param_env, mk_ptr(mt_b.ty), target, ty::error::TypeError::Mutability, diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index dfb3c088afb..2afc2aec1ba 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -77,7 +77,7 @@ impl<'tcx> InherentCollect<'tcx> { return Ok(()); } - if self.tcx.features().rustc_attrs { + if self.tcx.features().rustc_attrs() { let items = self.tcx.associated_item_def_ids(impl_def_id); if !self.tcx.has_attr(ty_def_id, sym::rustc_has_incoherent_inherent_impls) { @@ -115,7 +115,7 @@ impl<'tcx> InherentCollect<'tcx> { ) -> Result<(), ErrorGuaranteed> { let items = self.tcx.associated_item_def_ids(impl_def_id); if !self.tcx.hir().rustc_coherence_is_core() { - if self.tcx.features().rustc_attrs { + if self.tcx.features().rustc_attrs() { for &impl_item in items { if !self.tcx.has_attr(impl_item, sym::rustc_allow_incoherent_impl) { let span = self.tcx.def_span(impl_def_id); diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index eea5a16ac6f..3aad4bafeb5 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -53,7 +53,7 @@ fn enforce_trait_manually_implementable( ) -> Result<(), ErrorGuaranteed> { let impl_header_span = tcx.def_span(impl_def_id); - if tcx.is_lang_item(trait_def_id, LangItem::Freeze) && !tcx.features().freeze_impls { + if tcx.is_lang_item(trait_def_id, LangItem::Freeze) && !tcx.features().freeze_impls() { feature_err( &tcx.sess, sym::freeze_impls, @@ -86,8 +86,8 @@ fn enforce_trait_manually_implementable( if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable = trait_def.specialization_kind { - if !tcx.features().specialization - && !tcx.features().min_specialization + if !tcx.features().specialization() + && !tcx.features().min_specialization() && !impl_header_span.allows_unstable(sym::specialization) && !impl_header_span.allows_unstable(sym::min_specialization) { diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index f63e2d40e39..3add801cf56 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -77,6 +77,8 @@ pub fn provide(providers: &mut Providers) { explicit_supertraits_containing_assoc_item: predicates_of::explicit_supertraits_containing_assoc_item, trait_explicit_predicates_and_bounds: predicates_of::trait_explicit_predicates_and_bounds, + const_conditions: predicates_of::const_conditions, + implied_const_bounds: predicates_of::implied_const_bounds, type_param_predicates: predicates_of::type_param_predicates, trait_def, adt_def, @@ -1129,7 +1131,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { }; let paren_sugar = tcx.has_attr(def_id, sym::rustc_paren_sugar); - if paren_sugar && !tcx.features().unboxed_closures { + if paren_sugar && !tcx.features().unboxed_closures() { tcx.dcx().emit_err(errors::ParenSugarAttribute { span: item.span }); } @@ -1595,7 +1597,7 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai impl_.of_trait.as_ref().map(|ast_trait_ref| { let selfty = tcx.type_of(def_id).instantiate_identity(); - check_impl_constness(tcx, tcx.is_const_trait_impl_raw(def_id.to_def_id()), ast_trait_ref); + check_impl_constness(tcx, tcx.is_const_trait_impl(def_id.to_def_id()), ast_trait_ref); let trait_ref = icx.lowerer().lower_impl_trait_ref(ast_trait_ref, selfty); @@ -1696,7 +1698,7 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( // Feature gate SIMD types in FFI, since I am not sure that the // ABIs are handled at all correctly. -huonw - if abi != abi::Abi::RustIntrinsic && !tcx.features().simd_ffi { + if abi != abi::Abi::RustIntrinsic && !tcx.features().simd_ffi() { let check = |hir_ty: &hir::Ty<'_>, ty: Ty<'_>| { if ty.is_simd() { let snip = tcx diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 14b6b17ed18..3eec0e12665 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -53,7 +53,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { param_def_id_to_index, has_self: opaque_ty_generics.has_self, has_late_bound_regions: opaque_ty_generics.has_late_bound_regions, - host_effect_index: parent_generics.host_effect_index, }; } @@ -109,7 +108,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // We do not allow generic parameters in anon consts if we are inside // of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed. None - } else if tcx.features().generic_const_exprs { + } else if tcx.features().generic_const_exprs() { let parent_node = tcx.parent_hir_node(hir_id); debug!(?parent_node); if let Node::Variant(Variant { disr_expr: Some(constant), .. }) = parent_node @@ -161,7 +160,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { param_def_id_to_index, has_self: generics.has_self, has_late_bound_regions: generics.has_late_bound_regions, - host_effect_index: None, }; } else { // HACK(eddyb) this provides the correct generics when @@ -292,12 +290,10 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { let has_self = opt_self.is_some(); let mut parent_has_self = false; let mut own_start = has_self as u32; - let mut host_effect_index = None; let parent_count = parent_def_id.map_or(0, |def_id| { let generics = tcx.generics_of(def_id); assert!(!has_self); parent_has_self = generics.has_self; - host_effect_index = generics.host_effect_index; own_start = generics.count() as u32; generics.parent_count + generics.own_params.len() }); @@ -361,12 +357,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { kind, }) } - GenericParamKind::Const { ty: _, default, is_host_effect, synthetic } => { - if !matches!(allow_defaults, Defaults::Allowed) - && default.is_some() - // `host` effect params are allowed to have defaults. - && !is_host_effect - { + GenericParamKind::Const { ty: _, default, synthetic } => { + if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() { tcx.dcx().span_err( param.span, "defaults for const parameters are only allowed in \ @@ -376,27 +368,12 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { let index = next_index(); - if is_host_effect { - if let Some(idx) = host_effect_index { - tcx.dcx().span_delayed_bug( - param.span, - format!("parent also has host effect param? index: {idx}, def: {def_id:?}"), - ); - } - - host_effect_index = Some(index as usize); - } - Some(ty::GenericParamDef { index, name: param.name.ident().name, def_id: param.def_id.to_def_id(), pure_wrt_drop: param.pure_wrt_drop, - kind: ty::GenericParamDefKind::Const { - has_default: default.is_some(), - is_host_effect, - synthetic, - }, + kind: ty::GenericParamDefKind::Const { has_default: default.is_some(), synthetic }, }) } })); @@ -459,7 +436,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { param_def_id_to_index, has_self: has_self || parent_has_self, has_late_bound_regions: has_late_bound_regions(tcx, node), - host_effect_index, } } @@ -540,8 +516,7 @@ impl<'v> Visitor<'v> for AnonConstInParamTyDetector { type Result = ControlFlow<()>; fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) -> Self::Result { - if let GenericParamKind::Const { ty, default: _, is_host_effect: _, synthetic: _ } = p.kind - { + if let GenericParamKind::Const { ty, default: _, synthetic: _ } = p.kind { let prev = self.in_param_ty; self.in_param_ty = true; let res = self.visit_ty(ty); diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 4346504450d..b2ad42be6c7 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -13,6 +13,7 @@ use tracing::{debug, instrument}; use super::ItemCtxt; use super::predicates_of::assert_only_contains_predicates_from; +use crate::bounds::Bounds; use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter}; /// For associated types we include both bounds written on the type @@ -36,9 +37,19 @@ fn associated_type_bounds<'tcx>( ); let icx = ItemCtxt::new(tcx, assoc_item_def_id); - let mut bounds = icx.lowerer().lower_mono_bounds(item_ty, hir_bounds, filter); + let mut bounds = Bounds::default(); + icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter); // Associated types are implicitly sized unless a `?Sized` bound is found - icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); + match filter { + PredicateFilter::All + | PredicateFilter::SelfOnly + | PredicateFilter::SelfThatDefines(_) + | PredicateFilter::SelfAndAssociatedTypeBounds => { + icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); + } + // `ConstIfConst` is only interested in `~const` bounds. + PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {} + } let trait_def_id = tcx.local_parent(assoc_item_def_id); let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id); @@ -56,7 +67,7 @@ fn associated_type_bounds<'tcx>( ) }); - let all_bounds = tcx.arena.alloc_from_iter(bounds.clauses(tcx).chain(bounds_from_parent)); + let all_bounds = tcx.arena.alloc_from_iter(bounds.clauses().chain(bounds_from_parent)); debug!( "associated_type_bounds({}) = {:?}", tcx.def_path_str(assoc_item_def_id.to_def_id()), @@ -107,10 +118,19 @@ fn remap_gat_vars_and_recurse_into_nested_projections<'tcx>( } else { // Only collect *self* type bounds if the filter is for self. match filter { - PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => { + PredicateFilter::All => {} + PredicateFilter::SelfOnly => { return None; } - PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => {} + PredicateFilter::SelfThatDefines(_) + | PredicateFilter::SelfConstIfConst + | PredicateFilter::SelfAndAssociatedTypeBounds + | PredicateFilter::ConstIfConst => { + unreachable!( + "invalid predicate filter for \ + `remap_gat_vars_and_recurse_into_nested_projections`" + ) + } } clause_ty = alias_ty.self_ty(); @@ -303,12 +323,23 @@ fn opaque_type_bounds<'tcx>( ) -> &'tcx [(ty::Clause<'tcx>, Span)] { ty::print::with_reduced_queries!({ let icx = ItemCtxt::new(tcx, opaque_def_id); - let mut bounds = icx.lowerer().lower_mono_bounds(item_ty, hir_bounds, filter); + let mut bounds = Bounds::default(); + icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter); // Opaque types are implicitly sized unless a `?Sized` bound is found - icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); + match filter { + PredicateFilter::All + | PredicateFilter::SelfOnly + | PredicateFilter::SelfThatDefines(_) + | PredicateFilter::SelfAndAssociatedTypeBounds => { + // Associated types are implicitly sized unless a `?Sized` bound is found + icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); + } + //`ConstIfConst` is only interested in `~const` bounds. + PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {} + } debug!(?bounds); - tcx.arena.alloc_from_iter(bounds.clauses(tcx)) + tcx.arena.alloc_from_iter(bounds.clauses()) }) } diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index a87b29b3093..644ff0c667c 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -12,11 +12,12 @@ use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument, trace}; +use super::item_bounds::explicit_item_bounds_with_filter; use crate::bounds::Bounds; use crate::collect::ItemCtxt; use crate::constrained_generic_params as cgp; use crate::delegation::inherit_predicates_for_delegation_item; -use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter, RegionInferReason}; +use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter, RegionInferReason}; /// Returns a list of all type predicates (explicit and implicit) for the definition with /// ID `def_id`. This includes all predicates returned by `explicit_predicates_of`, plus @@ -78,7 +79,6 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic #[instrument(level = "trace", skip(tcx), ret)] fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::GenericPredicates<'_> { use rustc_hir::*; - use rustc_middle::ty::Ty; match tcx.opt_rpitit_info(def_id.to_def_id()) { Some(ImplTraitInTraitData::Trait { fn_def_id, .. }) => { @@ -106,7 +106,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen return ty::GenericPredicates { parent: Some(tcx.parent(def_id.to_def_id())), predicates: tcx.arena.alloc_from_iter(predicates), - effects_min_tys: ty::List::empty(), }; } @@ -128,7 +127,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen return ty::GenericPredicates { parent: Some(impl_def_id), predicates: tcx.arena.alloc_from_iter(impl_predicates), - effects_min_tys: ty::List::empty(), }; } @@ -154,7 +152,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // We use an `IndexSet` to preserve order of insertion. // Preserving the order of insertion is important here so as not to break UI tests. let mut predicates: FxIndexSet<(ty::Clause<'_>, Span)> = FxIndexSet::default(); - let mut effects_min_tys = Vec::new(); let hir_generics = node.generics().unwrap_or(NO_GENERICS); if let Node::Item(item) = node { @@ -181,13 +178,15 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // 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 { - let bounds = icx.lowerer().lower_mono_bounds( + let mut bounds = Bounds::default(); + icx.lowerer().lower_bounds( tcx.types.self_param, self_bounds, + &mut bounds, + ty::List::empty(), PredicateFilter::All, ); - predicates.extend(bounds.clauses(tcx)); - effects_min_tys.extend(bounds.effects_min_tys()); + predicates.extend(bounds.clauses()); } // In default impls, we can assume that the self type implements @@ -220,7 +219,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen param.span, ); trace!(?bounds); - predicates.extend(bounds.clauses(tcx)); + predicates.extend(bounds.clauses()); trace!(?predicates); } hir::GenericParamKind::Const { .. } => { @@ -265,15 +264,14 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } let mut bounds = Bounds::default(); - icx.lowerer().lower_poly_bounds( + icx.lowerer().lower_bounds( ty, - bound_pred.bounds.iter(), + bound_pred.bounds, &mut bounds, bound_vars, - OnlySelfBounds(false), + PredicateFilter::All, ); - predicates.extend(bounds.clauses(tcx)); - effects_min_tys.extend(bounds.effects_min_tys()); + predicates.extend(bounds.clauses()); } hir::WherePredicate::RegionPredicate(region_pred) => { @@ -305,7 +303,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } } - if tcx.features().generic_const_exprs { + if tcx.features().generic_const_exprs() { predicates.extend(const_evaluatable_predicates_of(tcx, def_id)); } @@ -342,30 +340,9 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen debug!(?predicates); } - // add `Self::Effects: Compat<HOST>` to ensure non-const impls don't get called - // in const contexts. - if let Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(..), .. }) = node - && let Some(host_effect_index) = generics.host_effect_index - { - let parent = generics.parent.unwrap(); - let Some(assoc_def_id) = tcx.associated_type_for_effects(parent) else { - bug!("associated_type_for_effects returned None when there is host effect in generics"); - }; - let effects = - Ty::new_projection(tcx, assoc_def_id, ty::GenericArgs::identity_for_item(tcx, parent)); - let param = generics.param_at(host_effect_index, tcx); - let span = tcx.def_span(param.def_id); - let host = ty::Const::new_param(tcx, ty::ParamConst::for_def(param)); - let compat = tcx.require_lang_item(LangItem::EffectsCompat, Some(span)); - let trait_ref = - ty::TraitRef::new(tcx, compat, [ty::GenericArg::from(effects), host.into()]); - predicates.push((ty::Binder::dummy(trait_ref).upcast(tcx), span)); - } - ty::GenericPredicates { parent: generics.parent, predicates: tcx.arena.alloc_from_iter(predicates), - effects_min_tys: tcx.mk_type_list(&effects_min_tys), } } @@ -516,12 +493,11 @@ pub(super) fn explicit_predicates_of<'tcx>( ty::GenericPredicates { parent: predicates_and_bounds.parent, predicates: tcx.arena.alloc_slice(&predicates), - effects_min_tys: predicates_and_bounds.effects_min_tys, } } } else { if matches!(def_kind, DefKind::AnonConst) - && tcx.features().generic_const_exprs + && tcx.features().generic_const_exprs() && let Some(defaulted_param_def_id) = tcx.hir().opt_const_param_default_param_def_id(tcx.local_def_id_to_hir_id(def_id)) { @@ -568,7 +544,6 @@ pub(super) fn explicit_predicates_of<'tcx>( return GenericPredicates { parent: parent_preds.parent, predicates: { tcx.arena.alloc_from_iter(filtered_predicates) }, - effects_min_tys: parent_preds.effects_min_tys, }; } gather_explicit_predicates_of(tcx, def_id) @@ -626,7 +601,7 @@ pub(super) fn implied_predicates_with_filter<'tcx>( bug!("trait_def_id {trait_def_id:?} is not an item"); }; - let (generics, bounds) = match item.kind { + let (generics, superbounds) = match item.kind { hir::ItemKind::Trait(.., generics, supertraits, _) => (generics, supertraits), hir::ItemKind::TraitAlias(generics, supertraits) => (generics, supertraits), _ => span_bug!(item.span, "super_predicates invoked on non-trait"), @@ -635,7 +610,8 @@ pub(super) fn implied_predicates_with_filter<'tcx>( let icx = ItemCtxt::new(tcx, trait_def_id); let self_param_ty = tcx.types.self_param; - let superbounds = icx.lowerer().lower_mono_bounds(self_param_ty, bounds, filter); + let mut bounds = Bounds::default(); + icx.lowerer().lower_bounds(self_param_ty, superbounds, &mut bounds, ty::List::empty(), filter); let where_bounds_that_match = icx.probe_ty_param_bounds_in_generics( generics, @@ -646,7 +622,7 @@ pub(super) fn implied_predicates_with_filter<'tcx>( // Combine the two lists to form the complete set of superbounds: let implied_bounds = - &*tcx.arena.alloc_from_iter(superbounds.clauses(tcx).chain(where_bounds_that_match)); + &*tcx.arena.alloc_from_iter(bounds.clauses().chain(where_bounds_that_match)); debug!(?implied_bounds); // Now require that immediate supertraits are lowered, which will, in @@ -702,29 +678,76 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>( assert_eq!( trait_predicate.self_ty(), ty, - "expected `Self` predicate when computing `{filter:?}` implied bounds: {clause:?}" + "expected `Self` predicate when computing \ + `{filter:?}` implied bounds: {clause:?}" ); } ty::ClauseKind::Projection(projection_predicate) => { assert_eq!( projection_predicate.self_ty(), ty, - "expected `Self` predicate when computing `{filter:?}` implied bounds: {clause:?}" + "expected `Self` predicate when computing \ + `{filter:?}` implied bounds: {clause:?}" ); } ty::ClauseKind::TypeOutlives(outlives_predicate) => { assert_eq!( outlives_predicate.0, ty, - "expected `Self` predicate when computing `{filter:?}` implied bounds: {clause:?}" + "expected `Self` predicate when computing \ + `{filter:?}` implied bounds: {clause:?}" ); } ty::ClauseKind::RegionOutlives(_) | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) - | ty::ClauseKind::ConstEvaluatable(_) => { + | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::HostEffect(..) => { + bug!( + "unexpected non-`Self` predicate when computing \ + `{filter:?}` implied bounds: {clause:?}" + ); + } + } + } + } + PredicateFilter::ConstIfConst => { + for (clause, _) in bounds { + match clause.kind().skip_binder() { + ty::ClauseKind::HostEffect(ty::HostEffectPredicate { + trait_ref: _, + host: ty::HostPolarity::Maybe, + }) => {} + _ => { bug!( - "unexpected non-`Self` predicate when computing `{filter:?}` implied bounds: {clause:?}" + "unexpected non-`HostEffect` predicate when computing \ + `{filter:?}` implied bounds: {clause:?}" + ); + } + } + } + } + PredicateFilter::SelfConstIfConst => { + for (clause, _) in bounds { + match clause.kind().skip_binder() { + ty::ClauseKind::HostEffect(pred) => { + assert_eq!( + pred.host, + ty::HostPolarity::Maybe, + "expected `~const` predicate when computing `{filter:?}` \ + implied bounds: {clause:?}", + ); + assert_eq!( + pred.trait_ref.self_ty(), + ty, + "expected `Self` predicate when computing `{filter:?}` \ + implied bounds: {clause:?}" + ); + } + _ => { + bug!( + "unexpected non-`HostEffect` predicate when computing \ + `{filter:?}` implied bounds: {clause:?}" ); } } @@ -825,20 +848,6 @@ impl<'tcx> ItemCtxt<'tcx> { continue; }; - // Subtle: If we're collecting `SelfAndAssociatedTypeBounds`, then we - // want to only consider predicates with `Self: ...`, but we don't want - // `OnlySelfBounds(true)` since we want to collect the nested associated - // type bound as well. - let (only_self_bounds, assoc_name) = match filter { - PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { - (OnlySelfBounds(false), None) - } - PredicateFilter::SelfOnly => (OnlySelfBounds(true), None), - PredicateFilter::SelfThatDefines(assoc_name) => { - (OnlySelfBounds(true), Some(assoc_name)) - } - }; - let bound_ty = if predicate.is_param_bound(param_def_id.to_def_id()) { ty } else if matches!(filter, PredicateFilter::All) { @@ -848,33 +857,156 @@ impl<'tcx> ItemCtxt<'tcx> { }; let bound_vars = self.tcx.late_bound_vars(predicate.hir_id); - self.lowerer().lower_poly_bounds( + self.lowerer().lower_bounds( bound_ty, - predicate.bounds.iter().filter(|bound| { - assoc_name - .map_or(true, |assoc_name| self.bound_defines_assoc_item(bound, assoc_name)) - }), + predicate.bounds, &mut bounds, bound_vars, - only_self_bounds, + filter, ); } - bounds.clauses(self.tcx).collect() + bounds.clauses().collect() } +} - #[instrument(level = "trace", skip(self))] - fn bound_defines_assoc_item(&self, b: &hir::GenericBound<'_>, assoc_name: Ident) -> bool { - match b { - hir::GenericBound::Trait(poly_trait_ref) => { - let trait_ref = &poly_trait_ref.trait_ref; - if let Some(trait_did) = trait_ref.trait_def_id() { - self.tcx.trait_may_define_assoc_item(trait_did, assoc_name) - } else { - false - } +/// Compute the conditions that need to hold for a conditionally-const item to be const. +/// That is, compute the set of `~const` where clauses for a given item. +/// +/// This query also computes the `~const` where clauses for associated types, which are +/// not "const", but which have item bounds which may be `~const`. These must hold for +/// the `~const` item bound to hold. +pub(super) fn const_conditions<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> ty::ConstConditions<'tcx> { + if !tcx.is_conditionally_const(def_id) { + bug!("const_conditions invoked for item that is not conditionally const: {def_id:?}"); + } + + let (generics, trait_def_id_and_supertraits, has_parent) = match tcx.hir_node_by_def_id(def_id) + { + Node::Item(item) => match item.kind { + hir::ItemKind::Impl(impl_) => (impl_.generics, None, false), + hir::ItemKind::Fn(_, generics, _) => (generics, None, false), + hir::ItemKind::Trait(_, _, generics, supertraits, _) => { + (generics, Some((item.owner_id.def_id, supertraits)), false) } - _ => false, + _ => bug!("const_conditions called on wrong item: {def_id:?}"), + }, + // While associated types are not really const, we do allow them to have `~const` + // bounds and where clauses. `const_conditions` is responsible for gathering + // these up so we can check them in `compare_type_predicate_entailment`, and + // in `HostEffect` goal computation. + Node::TraitItem(item) => match item.kind { + hir::TraitItemKind::Fn(_, _) | hir::TraitItemKind::Type(_, _) => { + (item.generics, None, true) + } + _ => bug!("const_conditions called on wrong item: {def_id:?}"), + }, + Node::ImplItem(item) => match item.kind { + hir::ImplItemKind::Fn(_, _) | hir::ImplItemKind::Type(_) => { + (item.generics, None, tcx.is_conditionally_const(tcx.local_parent(def_id))) + } + _ => bug!("const_conditions called on wrong item: {def_id:?}"), + }, + Node::ForeignItem(item) => match item.kind { + hir::ForeignItemKind::Fn(_, _, generics) => (generics, None, false), + _ => bug!("const_conditions called on wrong item: {def_id:?}"), + }, + // N.B. Tuple ctors are unconditionally constant. + Node::Ctor(hir::VariantData::Tuple { .. }) => return Default::default(), + _ => bug!("const_conditions called on wrong item: {def_id:?}"), + }; + + let icx = ItemCtxt::new(tcx, def_id); + let mut bounds = Bounds::default(); + + for pred in generics.predicates { + match pred { + hir::WherePredicate::BoundPredicate(bound_pred) => { + let ty = icx.lowerer().lower_ty_maybe_return_type_notation(bound_pred.bounded_ty); + let bound_vars = tcx.late_bound_vars(bound_pred.hir_id); + icx.lowerer().lower_bounds( + ty, + bound_pred.bounds.iter(), + &mut bounds, + bound_vars, + PredicateFilter::ConstIfConst, + ); + } + _ => {} } } + + if let Some((def_id, supertraits)) = trait_def_id_and_supertraits { + bounds.push_const_bound( + tcx, + ty::Binder::dummy(ty::TraitRef::identity(tcx, def_id.to_def_id())), + ty::HostPolarity::Maybe, + DUMMY_SP, + ); + + icx.lowerer().lower_bounds( + tcx.types.self_param, + supertraits.into_iter(), + &mut bounds, + ty::List::empty(), + PredicateFilter::ConstIfConst, + ); + } + + ty::ConstConditions { + parent: has_parent.then(|| tcx.local_parent(def_id).to_def_id()), + predicates: tcx.arena.alloc_from_iter(bounds.clauses().map(|(clause, span)| { + ( + clause.kind().map_bound(|clause| match clause { + ty::ClauseKind::HostEffect(ty::HostEffectPredicate { + trait_ref, + host: ty::HostPolarity::Maybe, + }) => trait_ref, + _ => bug!("converted {clause:?}"), + }), + span, + ) + })), + } +} + +pub(super) fn implied_const_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> ty::EarlyBinder<'tcx, &'tcx [(ty::PolyTraitRef<'tcx>, Span)]> { + if !tcx.is_conditionally_const(def_id) { + bug!("const_conditions invoked for item that is not conditionally const: {def_id:?}"); + } + + let bounds = match tcx.hir_node_by_def_id(def_id) { + Node::Item(hir::Item { kind: hir::ItemKind::Trait(..), .. }) => { + implied_predicates_with_filter( + tcx, + def_id.to_def_id(), + PredicateFilter::SelfConstIfConst, + ) + } + Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(..), .. }) => { + explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::ConstIfConst) + } + _ => bug!("implied_const_bounds called on wrong item: {def_id:?}"), + }; + + bounds.map_bound(|bounds| { + &*tcx.arena.alloc_from_iter(bounds.iter().copied().map(|(clause, span)| { + ( + clause.kind().map_bound(|clause| match clause { + ty::ClauseKind::HostEffect(ty::HostEffectPredicate { + trait_ref, + host: ty::HostPolarity::Maybe, + }) => trait_ref, + _ => bug!("converted {clause:?}"), + }), + span, + ) + })) + }) } diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index cb7f0901c7e..95e07244a6b 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1161,7 +1161,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { && let Some(param) = generics.params.iter().find(|p| p.def_id == param_id) && param.is_elided_lifetime() && !self.tcx.asyncness(lifetime_ref.hir_id.owner.def_id).is_async() - && !self.tcx.features().anonymous_lifetime_in_impl_trait + && !self.tcx.features().anonymous_lifetime_in_impl_trait() { let mut diag: rustc_errors::Diag<'_> = rustc_session::parse::feature_err( &self.tcx.sess, @@ -2239,7 +2239,7 @@ fn deny_non_region_late_bound( format!("late-bound {what} parameter not allowed on {where_}"), ); - let guar = diag.emit_unless(!tcx.features().non_lifetime_binders || !first); + let guar = diag.emit_unless(!tcx.features().non_lifetime_binders() || !first); first = false; *arg = ResolvedArg::Error(guar); diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 470bcaeded1..84161ec7648 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -699,7 +699,7 @@ fn infer_placeholder_type<'tcx>( } fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) { - if !tcx.features().inherent_associated_types { + if !tcx.features().inherent_associated_types() { use rustc_session::parse::feature_err; use rustc_span::symbol::sym; feature_err( @@ -714,7 +714,7 @@ fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) { pub(crate) fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool { use hir::intravisit::Visitor; - if tcx.features().lazy_type_alias { + if tcx.features().lazy_type_alias() { return true; } struct HasTait; diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index 1ccb7faaf30..e65420ea8bf 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -186,7 +186,6 @@ impl<'tcx> GenericsBuilder<'tcx> { param_def_id_to_index, has_self, has_late_bound_regions: sig_generics.has_late_bound_regions, - host_effect_index: sig_generics.host_effect_index, } } } @@ -279,8 +278,6 @@ impl<'tcx> PredicatesBuilder<'tcx> { ty::GenericPredicates { parent: self.parent, predicates: self.tcx.arena.alloc_from_iter(preds), - // FIXME(fn_delegation): Support effects. - effects_min_tys: ty::List::empty(), } } } @@ -472,10 +469,6 @@ fn check_constraints<'tcx>( })); }; - if tcx.has_host_param(sig_id) { - emit("delegation to a function with effect parameter is not supported yet"); - } - if let Some(local_sig_id) = sig_id.as_local() && tcx.hir().opt_delegation_sig_id(local_sig_id).is_some() { 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 8f7ca089c91..c902e85c267 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -19,9 +19,7 @@ use tracing::{debug, instrument}; use super::errors::GenericsArgsErrExtend; use crate::bounds::Bounds; use crate::errors; -use crate::hir_ty_lowering::{ - AssocItemQSelf, HirTyLowerer, OnlySelfBounds, PredicateFilter, RegionInferReason, -}; +use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer, PredicateFilter, RegionInferReason}; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Add a `Sized` bound to the `bounds` if appropriate. @@ -47,23 +45,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let hir::GenericBound::Trait(ptr) = hir_bound else { continue; }; - match ptr.modifiers { - hir::TraitBoundModifier::Maybe => unbounds.push(ptr), - hir::TraitBoundModifier::Negative => { + match ptr.modifiers.polarity { + hir::BoundPolarity::Maybe(_) => unbounds.push(ptr), + hir::BoundPolarity::Negative(_) => { if let Some(sized_def_id) = sized_def_id && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id) { seen_negative_sized_bound = true; } } - hir::TraitBoundModifier::None => { + hir::BoundPolarity::Positive => { if let Some(sized_def_id) = sized_def_id && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id) { seen_positive_sized_bound = true; } } - _ => {} } } }; @@ -91,7 +88,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; if seen_repeat { self.dcx().emit_err(err); - } else if !tcx.features().more_maybe_bounds { + } else if !tcx.features().more_maybe_bounds() { self.tcx().sess.create_feature_err(err, sym::more_maybe_bounds).emit(); }; } @@ -144,34 +141,46 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// There is an implied binder around `param_ty` and `hir_bounds`. /// See `lower_poly_trait_ref` for more details. #[instrument(level = "debug", skip(self, hir_bounds, bounds))] - pub(crate) fn lower_poly_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'tcx>>>( + pub(crate) fn lower_bounds<'hir, I: IntoIterator<Item = &'hir hir::GenericBound<'tcx>>>( &self, param_ty: Ty<'tcx>, hir_bounds: I, bounds: &mut Bounds<'tcx>, bound_vars: &'tcx ty::List<ty::BoundVariableKind>, - only_self_bounds: OnlySelfBounds, + predicate_filter: PredicateFilter, ) where 'tcx: 'hir, { for hir_bound in hir_bounds { + // In order to avoid cycles, when we're lowering `SelfThatDefines`, + // we skip over any traits that don't define the given associated type. + if let PredicateFilter::SelfThatDefines(assoc_name) = predicate_filter { + if let Some(trait_ref) = hir_bound.trait_ref() + && let Some(trait_did) = trait_ref.trait_def_id() + && self.tcx().trait_may_define_assoc_item(trait_did, assoc_name) + { + // Okay + } else { + continue; + } + } + match hir_bound { hir::GenericBound::Trait(poly_trait_ref) => { - let (constness, polarity) = match poly_trait_ref.modifiers { - hir::TraitBoundModifier::Const => { - (ty::BoundConstness::Const, ty::PredicatePolarity::Positive) - } - hir::TraitBoundModifier::MaybeConst => { - (ty::BoundConstness::ConstIfConst, ty::PredicatePolarity::Positive) - } - hir::TraitBoundModifier::None => { - (ty::BoundConstness::NotConst, ty::PredicatePolarity::Positive) - } - hir::TraitBoundModifier::Negative => { - (ty::BoundConstness::NotConst, ty::PredicatePolarity::Negative) - } - hir::TraitBoundModifier::Maybe => continue, + let hir::TraitBoundModifiers { constness, polarity } = poly_trait_ref.modifiers; + // FIXME: We could pass these directly into `lower_poly_trait_ref` + // so that we could use these spans in diagnostics within that function... + let constness = match constness { + hir::BoundConstness::Never => None, + hir::BoundConstness::Always(_) => Some(ty::BoundConstness::Const), + hir::BoundConstness::Maybe(_) => Some(ty::BoundConstness::ConstIfConst), }; + let polarity = match polarity { + rustc_ast::BoundPolarity::Positive => ty::PredicatePolarity::Positive, + rustc_ast::BoundPolarity::Negative(_) => ty::PredicatePolarity::Negative, + rustc_ast::BoundPolarity::Maybe(_) => continue, + }; + let _ = self.lower_poly_trait_ref( &poly_trait_ref.trait_ref, poly_trait_ref.span, @@ -179,10 +188,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { polarity, param_ty, bounds, - only_self_bounds, + predicate_filter, ); } hir::GenericBound::Outlives(lifetime) => { + // `ConstIfConst` is only interested in `~const` bounds. + if matches!( + predicate_filter, + PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst + ) { + continue; + } + let region = self.lower_lifetime(lifetime, RegionInferReason::OutlivesBound); bounds.push_region_bound( self.tcx(), @@ -200,56 +217,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - /// Lower HIR bounds into `bounds` given the self type `param_ty` and *no* overarching late-bound vars. - /// - /// ### Example - /// - /// ```ignore (illustrative) - /// fn foo<T: Bar + Baz>() { } - /// // ^ ^^^^^^^^^ hir_bounds - /// // param_ty - /// ``` - pub(crate) fn lower_mono_bounds( - &self, - param_ty: Ty<'tcx>, - hir_bounds: &[hir::GenericBound<'tcx>], - filter: PredicateFilter, - ) -> Bounds<'tcx> { - let mut bounds = Bounds::default(); - - let only_self_bounds = match filter { - PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { - OnlySelfBounds(false) - } - PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => OnlySelfBounds(true), - }; - - self.lower_poly_bounds( - param_ty, - hir_bounds.iter().filter(|bound| match filter { - PredicateFilter::All - | PredicateFilter::SelfOnly - | PredicateFilter::SelfAndAssociatedTypeBounds => true, - PredicateFilter::SelfThatDefines(assoc_name) => { - if let Some(trait_ref) = bound.trait_ref() - && let Some(trait_did) = trait_ref.trait_def_id() - && self.tcx().trait_may_define_assoc_item(trait_did, assoc_name) - { - true - } else { - false - } - } - }), - &mut bounds, - ty::List::empty(), - only_self_bounds, - ); - debug!(?bounds); - - bounds - } - /// Lower an associated item constraint from the HIR into `bounds`. /// /// ### A Note on Binders @@ -267,7 +234,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bounds: &mut Bounds<'tcx>, duplicates: &mut FxIndexMap<DefId, Span>, path_span: Span, - only_self_bounds: OnlySelfBounds, + predicate_filter: PredicateFilter, ) -> Result<(), ErrorGuaranteed> { let tcx = self.tcx(); @@ -432,33 +399,48 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }, ); - bounds.push_projection_bound( - tcx, - projection_term.map_bound(|projection_term| ty::ProjectionPredicate { - projection_term, - term, - }), - constraint.span, - ); + match predicate_filter { + PredicateFilter::All + | PredicateFilter::SelfOnly + | PredicateFilter::SelfThatDefines(_) + | PredicateFilter::SelfAndAssociatedTypeBounds => { + bounds.push_projection_bound( + tcx, + projection_term.map_bound(|projection_term| ty::ProjectionPredicate { + projection_term, + term, + }), + constraint.span, + ); + } + // `ConstIfConst` is only interested in `~const` bounds. + PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {} + } } // Lower a constraint like `Item: Debug` as found in HIR bound `T: Iterator<Item: Debug>` // to a bound involving a projection: `<T as Iterator>::Item: Debug`. hir::AssocItemConstraintKind::Bound { bounds: hir_bounds } => { - // NOTE: If `only_self_bounds` is true, do NOT expand this associated type bound into - // a trait predicate, since we only want to add predicates for the `Self` type. - if !only_self_bounds.0 { - let projection_ty = projection_term - .map_bound(|projection_term| projection_term.expect_ty(self.tcx())); - // Calling `skip_binder` is okay, because `lower_bounds` expects the `param_ty` - // parameter to have a skipped binder. - let param_ty = Ty::new_alias(tcx, ty::Projection, projection_ty.skip_binder()); - self.lower_poly_bounds( - param_ty, - hir_bounds.iter(), - bounds, - projection_ty.bound_vars(), - only_self_bounds, - ); + match predicate_filter { + PredicateFilter::All + | PredicateFilter::SelfAndAssociatedTypeBounds + | PredicateFilter::ConstIfConst => { + let projection_ty = projection_term + .map_bound(|projection_term| projection_term.expect_ty(self.tcx())); + // Calling `skip_binder` is okay, because `lower_bounds` expects the `param_ty` + // parameter to have a skipped binder. + let param_ty = + Ty::new_alias(tcx, ty::Projection, projection_ty.skip_binder()); + self.lower_bounds( + param_ty, + hir_bounds, + bounds, + projection_ty.bound_vars(), + predicate_filter, + ); + } + PredicateFilter::SelfOnly + | PredicateFilter::SelfThatDefines(_) + | PredicateFilter::SelfConstIfConst => {} } } } @@ -516,7 +498,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty, trait_segment, false, - ty::BoundConstness::NotConst, ); // SUBTLE: As noted at the end of `try_append_return_type_notation_params` 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 98822eec2ac..890e8fa99e6 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 @@ -20,7 +20,7 @@ use tracing::{debug, instrument}; use super::HirTyLowerer; use crate::bounds::Bounds; use crate::hir_ty_lowering::{ - GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds, RegionInferReason, + GenericArgCountMismatch, GenericArgCountResult, PredicateFilter, RegionInferReason, }; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { @@ -40,8 +40,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut potential_assoc_types = Vec::new(); let dummy_self = self.tcx().types.trait_object_dummy_self; for trait_bound in hir_trait_bounds.iter().rev() { - // FIXME: This doesn't handle `? const`. - if trait_bound.modifiers == hir::TraitBoundModifier::Maybe { + if let hir::BoundPolarity::Maybe(_) = trait_bound.modifiers.polarity { continue; } if let GenericArgCountResult { @@ -51,13 +50,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } = self.lower_poly_trait_ref( &trait_bound.trait_ref, trait_bound.span, - ty::BoundConstness::NotConst, + None, ty::PredicatePolarity::Positive, dummy_self, &mut bounds, - // True so we don't populate `bounds` with associated type bounds, even - // though they're disallowed from object types. - OnlySelfBounds(true), + PredicateFilter::SelfOnly, ) { potential_assoc_types.extend(cur_potential_assoc_types); } @@ -65,7 +62,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut trait_bounds = vec![]; let mut projection_bounds = vec![]; - for (pred, span) in bounds.clauses(tcx) { + for (pred, span) in bounds.clauses() { let bound_pred = pred.kind(); match bound_pred.skip_binder() { ty::ClauseKind::Trait(trait_pred) => { @@ -81,7 +78,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::ClauseKind::RegionOutlives(_) | ty::ClauseKind::ConstArgHasType(..) | ty::ClauseKind::WellFormed(_) - | ty::ClauseKind::ConstEvaluatable(_) => { + | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::HostEffect(..) => { span_bug!(span, "did not expect {pred} clause in object bounds"); } } @@ -261,7 +259,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } }) .collect(); - let args = tcx.mk_args(&args); let span = i.bottom().1; let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| { @@ -294,7 +291,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .emit(); } - ty::ExistentialTraitRef { def_id: trait_ref.def_id, args } + ty::ExistentialTraitRef::new(tcx, trait_ref.def_id, args) }) }); 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 01768c89cca..dd0f250a8e2 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -63,7 +63,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_segment: &'_ hir::PathSegment<'_>, is_impl: bool, ) { - if self.tcx().features().unboxed_closures { + if self.tcx().features().unboxed_closures() { return; } @@ -343,7 +343,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { && let Some(hir_ty) = constraint.ty() && let ty = self.lower_ty(hir_ty) && (ty.is_enum() || ty.references_error()) - && tcx.features().associated_const_equality + && tcx.features().associated_const_equality() { Some(errors::AssocKindMismatchWrapInBracesSugg { lo: hir_ty.span.shrink_to_lo(), 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 d760acf53bd..863c077a9e0 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -65,9 +65,6 @@ use crate::require_c_abi_if_c_variadic; pub struct GenericPathSegment(pub DefId, pub usize); #[derive(Copy, Clone, Debug)] -pub struct OnlySelfBounds(pub bool); - -#[derive(Copy, Clone, Debug)] pub enum PredicateFilter { /// All predicates may be implied by the trait. All, @@ -76,13 +73,20 @@ pub enum PredicateFilter { SelfOnly, /// Only traits that reference `Self: ..` and define an associated type - /// with the given ident are implied by the trait. + /// with the given ident are implied by the trait. This mode exists to + /// side-step query cycles when lowering associated types. SelfThatDefines(Ident), /// Only traits that reference `Self: ..` and their associated type bounds. /// For example, given `Self: Tr<A: B>`, this would expand to `Self: Tr` /// and `<Self as Tr>::A: B`. SelfAndAssociatedTypeBounds, + + /// Filter only the `~const` bounds, which are lowered into `HostEffect` clauses. + ConstIfConst, + + /// Filter only the `~const` bounds which are *also* in the supertrait position. + SelfConstIfConst, } #[derive(Debug)] @@ -336,14 +340,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { def_id: DefId, item_segment: &hir::PathSegment<'tcx>, ) -> GenericArgsRef<'tcx> { - let (args, _) = self.lower_generic_args_of_path( - span, - def_id, - &[], - item_segment, - None, - ty::BoundConstness::NotConst, - ); + let (args, _) = self.lower_generic_args_of_path(span, def_id, &[], item_segment, None); if let Some(c) = item_segment.args().constraints.first() { prohibit_assoc_item_constraint(self, c, Some((def_id, item_segment, span))); } @@ -392,7 +389,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { parent_args: &[ty::GenericArg<'tcx>], segment: &hir::PathSegment<'tcx>, self_ty: Option<Ty<'tcx>>, - constness: ty::BoundConstness, ) -> (GenericArgsRef<'tcx>, GenericArgCountResult) { // If the type is parameterized by this region, then replace this // region with the current anon region binding (in other words, @@ -415,7 +411,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assert!(self_ty.is_none()); } - let mut arg_count = check_generic_arg_count( + let arg_count = check_generic_arg_count( self, def_id, segment, @@ -573,16 +569,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } } - if let ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst = constness - && generics.has_self - && !tcx.is_const_trait(def_id) - { - let reported = self.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait { - span, - modifier: constness.as_str(), - }); - arg_count.correct = Err(GenericArgCountMismatch { reported, invalid_args: vec![] }); - } let mut args_ctx = GenericArgsCtxt { lowerer: self, @@ -614,14 +600,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { parent_args: GenericArgsRef<'tcx>, ) -> GenericArgsRef<'tcx> { debug!(?span, ?item_def_id, ?item_segment); - let (args, _) = self.lower_generic_args_of_path( - span, - item_def_id, - parent_args, - item_segment, - None, - ty::BoundConstness::NotConst, - ); + let (args, _) = + self.lower_generic_args_of_path(span, item_def_id, parent_args, item_segment, None); if let Some(c) = item_segment.args().constraints.first() { prohibit_assoc_item_constraint(self, c, Some((item_def_id, item_segment, span))); } @@ -647,7 +627,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty, trait_ref.path.segments.last().unwrap(), true, - ty::BoundConstness::NotConst, ) } @@ -679,11 +658,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, trait_ref: &hir::TraitRef<'tcx>, span: Span, - constness: ty::BoundConstness, + constness: Option<ty::BoundConstness>, polarity: ty::PredicatePolarity, self_ty: Ty<'tcx>, bounds: &mut Bounds<'tcx>, - only_self_bounds: OnlySelfBounds, + predicate_filter: PredicateFilter, ) -> GenericArgCountResult { let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()); let trait_segment = trait_ref.path.segments.last().unwrap(); @@ -700,9 +679,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &[], trait_segment, Some(self_ty), - constness, ); + if let Some(constness) = constness + && !self.tcx().is_const_trait(trait_def_id) + { + self.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait { + span: trait_ref.path.span, + modifier: constness.as_str(), + }); + } + let tcx = self.tcx(); let bound_vars = tcx.late_bound_vars(trait_ref.hir_ref_id); debug!(?bound_vars); @@ -712,16 +699,49 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bound_vars, ); - debug!(?poly_trait_ref); - bounds.push_trait_bound( - tcx, - self.item_def_id().to_def_id(), - poly_trait_ref, - span, - polarity, - constness, - only_self_bounds, - ); + match predicate_filter { + PredicateFilter::All + | PredicateFilter::SelfOnly + | PredicateFilter::SelfThatDefines(..) + | PredicateFilter::SelfAndAssociatedTypeBounds => { + debug!(?poly_trait_ref); + bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity); + + match constness { + Some(ty::BoundConstness::Const) => { + if polarity == ty::PredicatePolarity::Positive { + bounds.push_const_bound( + tcx, + poly_trait_ref, + ty::HostPolarity::Const, + span, + ); + } + } + Some(ty::BoundConstness::ConstIfConst) => { + // We don't emit a const bound here, since that would mean that we + // unconditionally need to prove a `HostEffect` predicate, even when + // the predicates are being instantiated in a non-const context. This + // is instead handled in the `const_conditions` query. + } + None => {} + } + } + // On the flip side, when filtering `ConstIfConst` bounds, we only need to convert + // `~const` bounds. All other predicates are handled in their respective queries. + // + // Note that like `PredicateFilter::SelfOnly`, we don't need to do any filtering + // here because we only call this on self bounds, and deal with the recursive case + // in `lower_assoc_item_constraint`. + PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => match constness { + Some(ty::BoundConstness::ConstIfConst) => { + if polarity == ty::PredicatePolarity::Positive { + bounds.push_const_bound(tcx, poly_trait_ref, ty::HostPolarity::Maybe, span); + } + } + None | Some(ty::BoundConstness::Const) => {} + }, + } let mut dup_constraints = FxIndexMap::default(); for constraint in trait_segment.args().constraints { @@ -744,7 +764,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bounds, &mut dup_constraints, constraint.span, - only_self_bounds, + predicate_filter, ); // Okay to ignore `Err` because of `ErrorGuaranteed` (see above). } @@ -762,19 +782,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty: Ty<'tcx>, trait_segment: &hir::PathSegment<'tcx>, is_impl: bool, - // FIXME(effects): Move all host param things in HIR ty lowering to AST lowering. - constness: ty::BoundConstness, ) -> ty::TraitRef<'tcx> { self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl); - let (generic_args, _) = self.lower_generic_args_of_path( - span, - trait_def_id, - &[], - trait_segment, - Some(self_ty), - constness, - ); + let (generic_args, _) = + self.lower_generic_args_of_path(span, trait_def_id, &[], trait_segment, Some(self_ty)); if let Some(c) = trait_segment.args().constraints.first() { prohibit_assoc_item_constraint(self, c, Some((trait_def_id, trait_segment, span))); } @@ -1268,7 +1280,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // selection during HIR ty lowering instead of in the trait solver), IATs can lead to cycle // errors (#108491) which mask the feature-gate error, needlessly confusing users // who use IATs by accident (#113265). - if !tcx.features().inherent_associated_types { + if !tcx.features().inherent_associated_types() { return Ok(None); } @@ -1542,7 +1554,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { item_def_id: DefId, trait_segment: &hir::PathSegment<'tcx>, item_segment: &hir::PathSegment<'tcx>, - constness: ty::BoundConstness, ) -> Ty<'tcx> { let tcx = self.tcx(); @@ -1555,7 +1566,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { debug!(?self_ty); let trait_ref = - self.lower_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false, constness); + self.lower_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false); debug!(?trait_ref); let item_args = @@ -1918,7 +1929,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { def_id, &path.segments[path.segments.len() - 2], path.segments.last().unwrap(), - ty::BoundConstness::NotConst, ) } Res::PrimTy(prim_ty) => { @@ -2151,7 +2161,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &[], &hir::PathSegment::invalid(), None, - ty::BoundConstness::NotConst, ); tcx.at(span).type_of(def_id).instantiate(tcx, args) } diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index 7f183324f04..d9c70c3cee6 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -57,7 +57,7 @@ pub(crate) fn check_impl_wf( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, ) -> Result<(), ErrorGuaranteed> { - let min_specialization = tcx.features().min_specialization; + let min_specialization = tcx.features().min_specialization(); let mut res = Ok(()); debug_assert_matches!(tcx.def_kind(impl_def_id), DefKind::Impl { .. }); res = res.and(enforce_impl_params_are_constrained(tcx, impl_def_id)); diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 0355adfcb11..a394fc2fbb1 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -530,6 +530,7 @@ fn trait_specialization_kind<'tcx>( | ty::ClauseKind::Projection(_) | ty::ClauseKind::ConstArgHasType(..) | ty::ClauseKind::WellFormed(_) - | ty::ClauseKind::ConstEvaluatable(..) => None, + | ty::ClauseKind::ConstEvaluatable(..) + | ty::ClauseKind::HostEffect(..) => None, } } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 71ee77f8f61..3ad35163191 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -116,7 +116,7 @@ fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi return; } - let extended_abi_support = tcx.features().extended_varargs_abi_support; + let extended_abi_support = tcx.features().extended_varargs_abi_support(); let conventions = match (extended_abi_support, abi.supports_varargs()) { // User enabled additional ABI support for varargs and function ABI matches those ones. (true, true) => return, @@ -155,7 +155,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) { // FIXME(effects): remove once effects is implemented in old trait solver // or if the next solver is stabilized. - if tcx.features().effects && !tcx.next_trait_solver_globally() { + if tcx.features().effects() && !tcx.next_trait_solver_globally() { tcx.dcx().emit_err(errors::EffectsWithoutNextSolver); } @@ -172,7 +172,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) { let _ = tcx.ensure().crate_inherent_impls_overlap_check(()); }); - if tcx.features().rustc_attrs { + if tcx.features().rustc_attrs() { tcx.sess.time("outlives_dumping", || outlives::dump::inferred_outlives(tcx)); tcx.sess.time("variance_dumping", || variance::dump::variances(tcx)); collect::dump::opaque_hidden_types(tcx); diff --git a/compiler/rustc_hir_analysis/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs index f576499ecac..2c1d443f951 100644 --- a/compiler/rustc_hir_analysis/src/outlives/explicit.rs +++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs @@ -53,7 +53,8 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { | ty::ClauseKind::Projection(_) | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) - | ty::ClauseKind::ConstEvaluatable(_) => {} + | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::HostEffect(..) => {} } } diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs index e3cdb1bf5f7..c43917649de 100644 --- a/compiler/rustc_hir_analysis/src/outlives/mod.rs +++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs @@ -23,7 +23,7 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clau let crate_map = tcx.inferred_outlives_crate(()); crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]) } - DefKind::AnonConst if tcx.features().generic_const_exprs => { + DefKind::AnonConst if tcx.features().generic_const_exprs() => { let id = tcx.local_def_id_to_hir_id(item_def_id); if tcx.hir().opt_const_param_default_param_def_id(id).is_some() { // In `generics_of` we set the generics' parent to be our parent's parent which means that diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 9ebfd4f15ab..61214b99215 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -16,7 +16,6 @@ use rustc_ast_pretty::pprust::{Comments, PrintState}; use rustc_hir::{ BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, - TraitBoundModifier, }; use rustc_span::FileName; use rustc_span::source_map::SourceMap; @@ -676,9 +675,16 @@ impl<'a> State<'a> { } fn print_poly_trait_ref(&mut self, t: &hir::PolyTraitRef<'_>) { - // FIXME: This isn't correct! - if t.modifiers == TraitBoundModifier::Maybe { - self.word("?"); + let hir::TraitBoundModifiers { constness, polarity } = t.modifiers; + match constness { + hir::BoundConstness::Never => {} + hir::BoundConstness::Always(_) => self.word("const"), + hir::BoundConstness::Maybe(_) => self.word("~const"), + } + match polarity { + hir::BoundPolarity::Positive => {} + hir::BoundPolarity::Negative(_) => self.word("!"), + hir::BoundPolarity::Maybe(_) => self.word("?"), } self.print_formal_generic_params(t.bound_generic_params); self.print_trait_ref(&t.trait_ref); @@ -2128,7 +2134,7 @@ impl<'a> State<'a> { self.print_type(default); } } - GenericParamKind::Const { ty, ref default, is_host_effect: _, synthetic: _ } => { + GenericParamKind::Const { ty, ref default, synthetic: _ } => { self.word_space(":"); self.print_type(ty); if let Some(default) = default { diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 13ba615d4c9..ed56bb9c455 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -20,7 +20,7 @@ use rustc_target::spec::abi; use rustc_trait_selection::error_reporting::traits::DefIdOrName; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use tracing::{debug, instrument, trace}; +use tracing::{debug, instrument}; use super::method::MethodCallee; use super::method::probe::ProbeScope; @@ -537,40 +537,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // This check is here because there is currently no way to express a trait bound for `FnDef` types only. if let ty::FnDef(def_id, _args) = *arg_ty.kind() { - let fn_once_def_id = - self.tcx.require_lang_item(hir::LangItem::FnOnce, Some(span)); - let fn_once_output_def_id = - self.tcx.require_lang_item(hir::LangItem::FnOnceOutput, Some(span)); - if self.tcx.has_host_param(fn_once_def_id) { - let const_param: ty::GenericArg<'tcx> = - ([self.tcx.consts.false_, self.tcx.consts.true_])[idx].into(); - self.register_predicate(traits::Obligation::new( - self.tcx, - self.misc(span), - self.param_env, - ty::TraitRef::new(self.tcx, fn_once_def_id, [ - arg_ty.into(), - fn_sig.inputs()[0].into(), - const_param, - ]), - )); - - self.register_predicate(traits::Obligation::new( - self.tcx, - self.misc(span), - self.param_env, - ty::ProjectionPredicate { - projection_term: ty::AliasTerm::new( - self.tcx, - fn_once_output_def_id, - [arg_ty.into(), fn_sig.inputs()[0].into(), const_param], - ), - term: fn_sig.output().into(), - }, - )); - - self.select_obligations_where_possible(|_| {}); - } else if idx == 0 && !self.tcx.is_const_fn_raw(def_id) { + if idx == 0 && !self.tcx.is_const_fn(def_id) { self.dcx().emit_err(errors::ConstSelectMustBeConst { span }); } } else { @@ -876,27 +843,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { callee_did: DefId, callee_args: GenericArgsRef<'tcx>, ) { - let tcx = self.tcx; - - // fast-reject if callee doesn't have the host effect param (non-const) - let generics = tcx.generics_of(callee_did); - let Some(host_effect_index) = generics.host_effect_index else { return }; - - let effect = tcx.expected_host_effect_param_for_body(self.body_id); - - trace!(?effect, ?generics, ?callee_args); + // FIXME(effects): We should be enforcing these effects unconditionally. + // This can be done as soon as we convert the standard library back to + // using const traits, since if we were to enforce these conditions now, + // we'd fail on basically every builtin trait call (i.e. `1 + 2`). + if !self.tcx.features().effects() { + return; + } - let param = callee_args.const_at(host_effect_index); - let cause = self.misc(span); - // We know the type of `effect` to be `bool`, there will be no opaque type inference. - match self.at(&cause, self.param_env).eq(infer::DefineOpaqueTypes::Yes, effect, param) { - Ok(infer::InferOk { obligations, value: () }) => { - self.register_predicates(obligations); + let host = match self.tcx.hir().body_const_context(self.body_id) { + Some(hir::ConstContext::Const { .. } | hir::ConstContext::Static(_)) => { + ty::HostPolarity::Const } - Err(e) => { - // FIXME(effects): better diagnostic - self.err_ctxt().report_mismatched_consts(&cause, effect, param, e).emit(); + Some(hir::ConstContext::ConstFn) => ty::HostPolarity::Maybe, + None => return, + }; + + // FIXME(effects): Should this be `is_const_fn_raw`? It depends on if we move + // const stability checking here too, I guess. + if self.tcx.is_conditionally_const(callee_did) { + let q = self.tcx.const_conditions(callee_did); + // FIXME(effects): Use this span with a better cause code. + for (cond, _) in q.instantiate(self.tcx, callee_args) { + self.register_predicate(Obligation::new( + self.tcx, + self.misc(span), + self.param_env, + cond.to_host_effect_clause(self.tcx, host), + )); } + } else { + // FIXME(effects): This should eventually be caught here. + // For now, though, we defer some const checking to MIR. } } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 8fa6ab8503d..483a8d1d9a9 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -721,7 +721,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { use rustc_middle::ty::cast::IntTy::*; if self.cast_ty.is_dyn_star() { - if fcx.tcx.features().dyn_star { + if fcx.tcx.features().dyn_star() { span_bug!(self.span, "should be handled by `coerce`"); } else { // Report "casting is invalid" rather than "non-primitive cast" @@ -876,20 +876,14 @@ impl<'a, 'tcx> CastCheck<'tcx> { // A<dyn Src<...> + SrcAuto> -> B<dyn Dst<...> + DstAuto>. need to make sure // - `Src` and `Dst` traits are the same // - traits have the same generic arguments - // - `SrcAuto` is a superset of `DstAuto` - (Some(src_principal), Some(dst_principal)) => { + // - projections are the same + // - `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto` + // + // Note that trait upcasting goes through a different mechanism (`coerce_unsized`) + // and is unaffected by this check. + (Some(src_principal), Some(_)) => { let tcx = fcx.tcx; - // Check that the traits are actually the same. - // The `dyn Src = dyn Dst` check below would suffice, - // but this may produce a better diagnostic. - // - // Note that trait upcasting goes through a different mechanism (`coerce_unsized`) - // and is unaffected by this check. - if src_principal.def_id() != dst_principal.def_id() { - return Err(CastError::DifferingKinds { src_kind, dst_kind }); - } - // We need to reconstruct trait object types. // `m_src` and `m_dst` won't work for us here because they will potentially // contain wrappers, which we do not care about. @@ -912,8 +906,8 @@ impl<'a, 'tcx> CastCheck<'tcx> { ty::Dyn, )); - // `dyn Src = dyn Dst`, this checks for matching traits/generics - // This is `demand_eqtype`, but inlined to give a better error. + // `dyn Src = dyn Dst`, this checks for matching traits/generics/projections + // This is `fcx.demand_eqtype`, but inlined to give a better error. let cause = fcx.misc(self.span); if fcx .at(&cause, fcx.param_env) @@ -965,8 +959,35 @@ impl<'a, 'tcx> CastCheck<'tcx> { // dyn Auto -> dyn Auto'? ok. (None, None) => Ok(CastKind::PtrPtrCast), - // dyn Trait -> dyn Auto? should be ok, but we used to not allow it. - // FIXME: allow this + // dyn Trait -> dyn Auto? not ok (for now). + // + // Although dropping the principal is already allowed for unsizing coercions + // (e.g. `*const (dyn Trait + Auto)` to `*const dyn Auto`), dropping it is + // currently **NOT** allowed for (non-coercion) ptr-to-ptr casts (e.g + // `*const Foo` to `*const Bar` where `Foo` has a `dyn Trait + Auto` tail + // and `Bar` has a `dyn Auto` tail), because the underlying MIR operations + // currently work very differently: + // + // * A MIR unsizing coercion on raw pointers to trait objects (`*const dyn Src` + // to `*const dyn Dst`) is currently equivalent to downcasting the source to + // the concrete sized type that it was originally unsized from first (via a + // ptr-to-ptr cast from `*const Src` to `*const T` with `T: Sized`) and then + // unsizing this thin pointer to the target type (unsizing `*const T` to + // `*const Dst`). In particular, this means that the pointer's metadata + // (vtable) will semantically change, e.g. for const eval and miri, even + // though the vtables will always be merged for codegen. + // + // * A MIR ptr-to-ptr cast is currently equivalent to a transmute and does not + // change the pointer metadata (vtable) at all. + // + // In addition to this potentially surprising difference between coercion and + // non-coercion casts, casting away the principal with a MIR ptr-to-ptr cast + // is currently considered undefined behavior: + // + // As a validity invariant of pointers to trait objects, we currently require + // that the principal of the vtable in the pointer metadata exactly matches + // the principal of the pointee type, where "no principal" is also considered + // a kind of principal. (Some(_), None) => Err(CastError::DifferingKinds { src_kind, dst_kind }), // dyn Auto -> dyn Trait? not ok. diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 406d668e7a5..6fa958d9496 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -223,11 +223,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::Ref(r_b, _, mutbl_b) => { return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b); } - ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star => { + ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star() => { return self.coerce_dyn_star(a, b, predicates, region); } ty::Adt(pin, _) - if self.tcx.features().pin_ergonomics + if self.tcx.features().pin_ergonomics() && self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) => { return self.coerce_pin(a, b); @@ -698,7 +698,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } if let Some((sub, sup)) = has_trait_upcasting_coercion - && !self.tcx().features().trait_upcasting + && !self.tcx().features().trait_upcasting() { // Renders better when we erase regions, since they're not really the point here. let (sub, sup) = self.tcx.erase_regions((sub, sup)); @@ -712,7 +712,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { err.emit(); } - if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion { + if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion() { feature_err( &self.tcx.sess, sym::unsized_tuple_coercion, @@ -732,7 +732,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, b_region: ty::Region<'tcx>, ) -> CoerceResult<'tcx> { - if !self.tcx.features().dyn_star { + if !self.tcx.features().dyn_star() { return Err(TypeError::Mismatch); } @@ -1695,7 +1695,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { blk_id, expression, ); - if !fcx.tcx.features().unsized_locals { + if !fcx.tcx.features().unsized_locals() { unsized_return = self.is_return_ty_definitely_unsized(fcx); } } @@ -1709,7 +1709,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { return_expr_id, expression, ); - if !fcx.tcx.features().unsized_locals { + if !fcx.tcx.features().unsized_locals() { unsized_return = self.is_return_ty_definitely_unsized(fcx); } } @@ -1723,6 +1723,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { }) => { err = fcx.err_ctxt().report_mismatched_types( cause, + fcx.param_env, expected, found, coercion_error, @@ -1752,6 +1753,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { }) => { err = fcx.err_ctxt().report_mismatched_types( cause, + fcx.param_env, expected, found, coercion_error, @@ -1787,6 +1789,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { _ => { err = fcx.err_ctxt().report_mismatched_types( cause, + fcx.param_env, expected, found, coercion_error, @@ -1897,7 +1900,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { block_or_return_id: hir::HirId, expression: Option<&'tcx hir::Expr<'tcx>>, ) -> Diag<'infcx> { - let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err); + let mut err = + fcx.err_ctxt().report_mismatched_types(cause, fcx.param_env, expected, found, ty_err); let due_to_block = matches!(fcx.tcx.hir_node(block_or_return_id), hir::Node::Block(..)); diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 777248ff873..3399a9fe880 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -191,7 +191,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.at(cause, self.param_env) .sup(DefineOpaqueTypes::Yes, expected, actual) .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) - .map_err(|e| self.err_ctxt().report_mismatched_types(cause, expected, actual, e)) + .map_err(|e| { + self.err_ctxt().report_mismatched_types(cause, self.param_env, expected, actual, e) + }) } pub(crate) fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { @@ -218,7 +220,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.at(cause, self.param_env) .eq(DefineOpaqueTypes::Yes, expected, actual) .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) - .map_err(|e| self.err_ctxt().report_mismatched_types(cause, expected, actual, e)) + .map_err(|e| { + self.err_ctxt().report_mismatched_types(cause, self.param_env, expected, actual, e) + }) } pub(crate) fn demand_coerce( @@ -271,7 +275,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr = expr.peel_drop_temps(); let cause = self.misc(expr.span); let expr_ty = self.resolve_vars_if_possible(checked_ty); - let mut err = self.err_ctxt().report_mismatched_types(&cause, expected, expr_ty, e); + let mut err = + self.err_ctxt().report_mismatched_types(&cause, self.param_env, expected, expr_ty, e); self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr, Some(e)); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index f4d7b59e9c8..92c2a906055 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -10,7 +10,8 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::unord::UnordMap; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, ErrorGuaranteed, StashKey, Subdiagnostic, pluralize, struct_span_code_err, + Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, Subdiagnostic, pluralize, + struct_span_code_err, }; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -734,7 +735,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // be known if explicitly specified via turbofish). self.deferred_transmute_checks.borrow_mut().push((*from, to, expr.hir_id)); } - if !tcx.features().unsized_fn_params { + if !tcx.features().unsized_fn_params() { // We want to remove some Sized bounds from std functions, // but don't want to expose the removal to stable Rust. // i.e., we don't want to allow @@ -1750,7 +1751,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // to tell them that in the diagnostic. Does not affect typeck. let is_constable = match element.kind { hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() { - ty::FnDef(def_id, _) if tcx.is_const_fn(def_id) => traits::IsConstable::Fn, + ty::FnDef(def_id, _) if tcx.is_stable_const_fn(def_id) => traits::IsConstable::Fn, _ => traits::IsConstable::No, }, hir::ExprKind::Path(qpath) => { @@ -1995,7 +1996,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(base_expr) = base_expr { // FIXME: We are currently creating two branches here in order to maintain // consistency. But they should be merged as much as possible. - let fru_tys = if self.tcx.features().type_changing_struct_update { + let fru_tys = if self.tcx.features().type_changing_struct_update() { if adt.is_struct() { // Make some fresh generic parameters for our ADT type. let fresh_args = self.fresh_args_for_item(base_expr.span, adt.did()); @@ -2763,12 +2764,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field_ident.span, "field not available in `impl Future`, but it is available in its `Output`", ); - err.span_suggestion_verbose( - base.span.shrink_to_hi(), - "consider `await`ing on the `Future` and access the field of its `Output`", - ".await", - Applicability::MaybeIncorrect, - ); + match self.tcx.coroutine_kind(self.body_id) { + Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => { + err.span_suggestion_verbose( + base.span.shrink_to_hi(), + "consider `await`ing on the `Future` to access the field", + ".await", + Applicability::MaybeIncorrect, + ); + } + _ => { + let mut span: MultiSpan = base.span.into(); + span.push_span_label(self.tcx.def_span(self.body_id), "this is not `async`"); + err.span_note( + span, + "this implements `Future` and its output type has the field, \ + but the future cannot be awaited in a synchronous function", + ); + } + } } fn ban_nonexisting_field( @@ -3538,7 +3552,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (ident, _def_scope) = self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block); - if !self.tcx.features().offset_of_enum { + if !self.tcx.features().offset_of_enum() { rustc_session::parse::feature_err( &self.tcx.sess, sym::offset_of_enum, @@ -3628,7 +3642,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let field_ty = self.field_ty(expr.span, field, args); - if self.tcx.features().offset_of_slice { + if self.tcx.features().offset_of_slice() { self.require_type_has_static_alignment( field_ty, expr.span, @@ -3661,7 +3675,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && field.name == sym::integer(index) { if let Some(&field_ty) = tys.get(index) { - if self.tcx.features().offset_of_slice { + if self.tcx.features().offset_of_slice() { self.require_type_has_static_alignment( field_ty, expr.span, diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 4fd508ab896..68776c52555 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -7,8 +7,6 @@ use rustc_data_structures::unord::{UnordBag, UnordMap, UnordSet}; use rustc_hir as hir; use rustc_hir::HirId; use rustc_hir::intravisit::Visitor; -use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; -use rustc_middle::bug; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; @@ -48,7 +46,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { self.fulfillment_cx.borrow_mut().pending_obligations() ); - let fallback_occurred = self.fallback_types() | self.fallback_effects(); + let fallback_occurred = self.fallback_types(); if !fallback_occurred { return; @@ -103,31 +101,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> { fallback_occurred } - fn fallback_effects(&self) -> bool { - let unsolved_effects = self.unsolved_effects(); - - if unsolved_effects.is_empty() { - return false; - } - - // not setting the `fallback_has_occurred` field here because - // that field is only used for type fallback diagnostics. - for effect in unsolved_effects { - let expected = self.tcx.consts.true_; - let cause = self.misc(DUMMY_SP); - match self.at(&cause, self.param_env).eq(DefineOpaqueTypes::Yes, expected, effect) { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations); - } - Err(e) => { - bug!("cannot eq unsolved effect: {e:?}") - } - } - } - - true - } - // Tries to apply a fallback to `ty` if it is an unsolved variable. // // - Unconstrained ints are replaced with `i32`. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index eccb18ad6c4..0fc566c58f7 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1262,15 +1262,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { self.fcx.ty_infer(Some(param), inf.span).into() } - ( - &GenericParamDefKind::Const { has_default, is_host_effect, .. }, - GenericArg::Infer(inf), - ) => { - if has_default && is_host_effect { - self.fcx.var_for_effect(param) - } else { - self.fcx.ct_infer(Some(param), inf.span).into() - } + (&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { + self.fcx.ct_infer(Some(param), inf.span).into() } _ => unreachable!(), } @@ -1305,20 +1298,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.fcx.var_for_def(self.span, param) } } - GenericParamDefKind::Const { has_default, is_host_effect, .. } => { + GenericParamDefKind::Const { has_default, .. } => { if has_default { - // N.B. this is a bit of a hack. `infer_args` is passed depending on - // whether the user has provided generic args. E.g. for `Vec::new` - // we would have to infer the generic types. However, for `Vec::<T>::new` - // where the allocator param `A` has a default we will *not* infer. But - // for effect params this is a different story: if the user has not written - // anything explicit for the effect param, we always need to try to infer - // it before falling back to default, such that a `const fn` such as - // `needs_drop::<()>` can still be called in const contexts. (if we defaulted - // instead of inferred, typeck would error) - if is_host_effect { - return self.fcx.var_for_effect(param); - } else if !infer_args { + if !infer_args { return tcx .const_param_default(param.def_id) .instantiate(tcx, preceding_args) @@ -1486,8 +1468,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return ty::Const::new_error(self.tcx, guar); } } - } else if self.tcx.features().generic_const_exprs { - ct.normalize(self.tcx, self.param_env) + } else if self.tcx.features().generic_const_exprs() { + ct.normalize_internal(self.tcx, self.param_env) } else { ct } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index bf8cc462189..a6c249da103 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -827,6 +827,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { formal_and_expected_inputs[mismatch_idx.into()], provided_arg_tys[mismatch_idx.into()].0, ), + self.param_env, terr, ); err.span_label( @@ -912,7 +913,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let trace = mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) { - let mut err = self.err_ctxt().report_and_explain_type_error(trace, *e); + let mut err = + self.err_ctxt().report_and_explain_type_error(trace, self.param_env, *e); suggest_confusable(&mut err); reported = Some(err.emit()); return false; @@ -940,7 +942,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx]; let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx]; let trace = mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty); - let mut err = self.err_ctxt().report_and_explain_type_error(trace, *err); + let mut err = + self.err_ctxt().report_and_explain_type_error(trace, self.param_env, *err); self.emit_coerce_suggestions( &mut err, provided_args[*provided_idx], @@ -1097,6 +1100,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut only_extras_so_far = errors .peek() .is_some_and(|first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); + let mut prev_extra_idx = None; let mut suggestions = vec![]; while let Some(error) = errors.next() { only_extras_so_far &= matches!(error, Error::Extra(_)); @@ -1112,7 +1116,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &mut err, &trace.cause, None, - Some(trace.values), + Some(self.param_env.and(trace.values)), e, true, ); @@ -1165,11 +1169,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // fn f() {} // - f(0, 1,) // + f() - if only_extras_so_far - && !errors - .peek() - .is_some_and(|next_error| matches!(next_error, Error::Extra(_))) - { + let trim_next_comma = match errors.peek() { + Some(Error::Extra(provided_idx)) + if only_extras_so_far + && provided_idx.index() > arg_idx.index() + 1 => + // If the next Error::Extra ("next") doesn't next to current ("current"), + // fn foo(_: (), _: u32) {} + // - foo("current", (), 1u32, "next") + // + foo((), 1u32) + // If the previous error is not a `Error::Extra`, then do not trim the next comma + // - foo((), "current", 42u32, "next") + // + foo((), 42u32) + { + prev_extra_idx.map_or(true, |prev_extra_idx| { + prev_extra_idx + 1 == arg_idx.index() + }) + } + // If no error left, we need to delete the next comma + None if only_extras_so_far => true, + // Not sure if other error type need to be handled as well + _ => false, + }; + + if trim_next_comma { let next = provided_arg_tys .get(arg_idx + 1) .map(|&(_, sp)| sp) @@ -1192,6 +1214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { SuggestionText::Remove(_) => SuggestionText::Remove(true), _ => SuggestionText::DidYouMean, }; + prev_extra_idx = Some(arg_idx.index()) } } Error::Missing(expected_idx) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index 693cb4465cc..eb5fe3a86e4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -52,6 +52,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) | ty::PredicateKind::Ambiguous => false, } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index e60bd1a312a..3940d138deb 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -247,12 +247,6 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { fn ct_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> { // FIXME ideally this shouldn't use unwrap match param { - Some( - param @ ty::GenericParamDef { - kind: ty::GenericParamDefKind::Const { is_host_effect: true, .. }, - .. - }, - ) => self.var_for_effect(param).as_const().unwrap(), Some(param) => self.var_for_def(span, param).as_const().unwrap(), None => self.next_const_var(span), } @@ -404,7 +398,7 @@ fn default_fallback(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior { } // `feature(never_type_fallback)`: fallback to `!` or `()` trying to not break stuff - if tcx.features().never_type_fallback { + if tcx.features().never_type_fallback() { return DivergingFallbackBehavior::ContextDependent; } diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 5ae8d2230f8..f427b0b805e 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -141,7 +141,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { let var_ty = self.assign(p.span, p.hir_id, None); if let Some((ty_span, hir_id)) = self.outermost_fn_param_pat { - if !self.fcx.tcx.features().unsized_fn_params { + if !self.fcx.tcx.features().unsized_fn_params() { self.fcx.require_type_is_sized( var_ty, ty_span, @@ -158,7 +158,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { ), ); } - } else if !self.fcx.tcx.features().unsized_locals { + } else if !self.fcx.tcx.features().unsized_locals() { 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 6b0a897faba..85e11ff6745 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -155,13 +155,13 @@ fn typeck_with_fallback<'tcx>( tcx.fn_sig(def_id).instantiate_identity() }; - check_abi(tcx, id, span, fn_sig.abi()); + check_abi(tcx, span, fn_sig.abi()); // Compute the function signature from point of view of inside the fn. let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); let fn_sig = fcx.normalize(body.value.span, fn_sig); - check_fn(&mut fcx, fn_sig, None, decl, def_id, body, tcx.features().unsized_fn_params); + check_fn(&mut fcx, fn_sig, None, decl, def_id, body, tcx.features().unsized_fn_params()); } else { let expected_type = infer_type_if_missing(&fcx, node); let expected_type = expected_type.unwrap_or_else(fallback); @@ -419,7 +419,7 @@ fn report_unexpected_variant_res( } } - err.multipart_suggestion_verbose(descr, suggestion, Applicability::MaybeIncorrect); + err.multipart_suggestion_verbose(descr, suggestion, Applicability::HasPlaceholders); err } Res::Def(DefKind::Variant, _) if expr.is_none() => { diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 1d7b3433fe5..96784fcb61b 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -536,9 +536,15 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // FIXME(arbitrary_self_types): We probably should limit the // situations where this can occur by adding additional restrictions // to the feature, like the self type can't reference method args. - if self.tcx.features().arbitrary_self_types { + if self.tcx.features().arbitrary_self_types() { self.err_ctxt() - .report_mismatched_types(&cause, method_self_ty, self_ty, terr) + .report_mismatched_types( + &cause, + self.param_env, + method_self_ty, + self_ty, + terr, + ) .emit(); } else { // This has/will have errored in wfcheck, which we cannot depend on from here, as typeck on functions diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 69b9be00276..e20a0cb67c3 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -369,17 +369,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { let (obligation, args) = self.obligation_for_method(cause, trait_def_id, self_ty, opt_input_types); - // FIXME(effects) find a better way to do this - // Operators don't have generic methods, but making them `#[const_trait]` gives them - // `const host: bool`. - let args = if self.tcx.is_const_trait(trait_def_id) { - self.tcx.mk_args_from_iter( - args.iter() - .chain([self.tcx.expected_host_effect_param_for_body(self.body_id).into()]), - ) - } else { - args - }; self.construct_obligation_for_trait(m_name, trait_def_id, obligation, args) } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 1be711887d9..569fdea11ce 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -404,7 +404,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mode, })); } else if bad_ty.reached_raw_pointer - && !self.tcx.features().arbitrary_self_types_pointers + && !self.tcx.features().arbitrary_self_types_pointers() && !self.tcx.sess.at_least_rust_2018() { // this case used to be allowed by the compiler, @@ -429,8 +429,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.resolve_vars_if_possible(ty.value); let guar = match *ty.kind() { ty::Infer(ty::TyVar(_)) => { - let raw_ptr_call = - bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types; + let raw_ptr_call = bad_ty.reached_raw_pointer + && !self.tcx.features().arbitrary_self_types(); let mut err = self.err_ctxt().emit_inference_failure_err( self.body_id, span, @@ -813,7 +813,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::ClauseKind::Projection(_) | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) - | ty::ClauseKind::ConstEvaluatable(_) => None, + | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::HostEffect(..) => None, } }); @@ -1146,7 +1147,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } ty::Adt(def, args) - if self.tcx.features().pin_ergonomics + if self.tcx.features().pin_ergonomics() && self.tcx.is_lang_item(def.did(), hir::LangItem::Pin) => { // make sure this is a pinned reference (and not a `Pin<Box>` or something) @@ -1195,7 +1196,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self_ty: Ty<'tcx>, unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, ) -> Option<PickResult<'tcx>> { - if !self.tcx.features().pin_ergonomics { + if !self.tcx.features().pin_ergonomics() { return None; } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 9dd59541148..eaf40a193a6 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -537,7 +537,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && binding_id != self.binding_id { if self.check_and_add_sugg_binding(LetStmt { - ty_hir_id_opt: if let Some(ty) = ty { Some(ty.hir_id) } else { None }, + ty_hir_id_opt: ty.map(|ty| ty.hir_id), binding_id, span: pat.span, init_hir_id: init.hir_id, diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index e961752b24c..cba6586f01d 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -442,7 +442,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let features = self.tcx.features(); - if features.ref_pat_eat_one_layer_2024 || features.ref_pat_eat_one_layer_2024_structural { + if features.ref_pat_eat_one_layer_2024() || features.ref_pat_eat_one_layer_2024_structural() + { def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl()); if def_br == ByRef::Yes(Mutability::Not) { max_ref_mutbl = MutblCap::Not; @@ -490,7 +491,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - if self.tcx.features().string_deref_patterns + if self.tcx.features().string_deref_patterns() && let hir::ExprKind::Lit(Spanned { node: ast::LitKind::Str(..), .. }) = lt.kind { let tcx = self.tcx; @@ -675,10 +676,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let bm = match user_bind_annot { BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => { if pat.span.at_least_rust_2024() - && (self.tcx.features().ref_pat_eat_one_layer_2024 - || self.tcx.features().ref_pat_eat_one_layer_2024_structural) + && (self.tcx.features().ref_pat_eat_one_layer_2024() + || self.tcx.features().ref_pat_eat_one_layer_2024_structural()) { - if !self.tcx.features().mut_ref { + if !self.tcx.features().mut_ref() { feature_err( &self.tcx.sess, sym::mut_ref, @@ -2152,8 +2153,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let tcx = self.tcx; let features = tcx.features(); - let ref_pat_eat_one_layer_2024 = features.ref_pat_eat_one_layer_2024; - let ref_pat_eat_one_layer_2024_structural = features.ref_pat_eat_one_layer_2024_structural; + let ref_pat_eat_one_layer_2024 = features.ref_pat_eat_one_layer_2024(); + let ref_pat_eat_one_layer_2024_structural = + features.ref_pat_eat_one_layer_2024_structural(); let no_ref_mut_behind_and = ref_pat_eat_one_layer_2024 || ref_pat_eat_one_layer_2024_structural; diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 63cf483aa22..88982661c8f 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -488,7 +488,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_locals() || self.tcx.features().unsized_fn_params() { for capture in self.typeck_results.borrow().closure_min_captures_flattened(closure_def_id) { @@ -2457,7 +2457,7 @@ fn truncate_capture_for_optimization( ) -> (Place<'_>, ty::UpvarCapture) { let is_shared_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)); - // Find the right-most deref (if any). All the projections that come after this + // Find the rightmost deref (if any). All the projections that come after this // are fields or other "in-place pointer adjustments"; these refer therefore to // data owned by whatever pointer is being dereferenced here. let idx = place.projections.iter().rposition(|proj| ProjectionKind::Deref == proj.kind); diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index d3d092be222..391e640f8bc 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -830,7 +830,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { value = tcx.fold_regions(value, |_, _| tcx.lifetimes.re_erased); // Normalize consts in writeback, because GCE doesn't normalize eagerly. - if tcx.features().generic_const_exprs { + if tcx.features().generic_const_exprs() { value = value.fold_with(&mut EagerlyNormalizeConsts { tcx, param_env: self.fcx.param_env }); } diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index a006786aa75..1f46155abc8 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -68,7 +68,7 @@ pub(crate) fn assert_dep_graph(tcx: TyCtxt<'_>) { // if the `rustc_attrs` feature is not enabled, then the // attributes we are interested in cannot be present anyway, so // skip the walk. - if !tcx.features().rustc_attrs { + if !tcx.features().rustc_attrs() { return; } diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index d25fe4219b5..2075d4214c1 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -140,7 +140,7 @@ pub(crate) fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) { } // can't add `#[rustc_clean]` etc without opting into this feature - if !tcx.features().rustc_attrs { + if !tcx.features().rustc_attrs() { return; } diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index e3519dfb028..90d07964fda 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -489,17 +489,6 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> { } } } - ty::ConstKind::Infer(InferConst::EffectVar(vid)) => { - match self.infcx.unwrap().probe_effect_var(vid) { - Some(value) => return self.fold_const(value), - None => { - return self.canonicalize_const_var( - CanonicalVarInfo { kind: CanonicalVarKind::Effect }, - ct, - ); - } - } - } ty::ConstKind::Infer(InferConst::Fresh(_)) => { bug!("encountered a fresh const during canonicalization") } @@ -700,8 +689,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { .iter() .map(|v| CanonicalVarInfo { kind: match v.kind { - CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) - | CanonicalVarKind::Effect => { + CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { return *v; } CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => { diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 8caedcd4053..fb5fc3a53fe 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -24,7 +24,6 @@ pub use instantiate::CanonicalExt; use rustc_index::IndexVec; pub use rustc_middle::infer::canonical::*; -use rustc_middle::infer::unify_key::EffectVarValue; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, GenericArg, List, Ty, TyCtxt}; use rustc_span::Span; @@ -145,15 +144,6 @@ impl<'tcx> InferCtxt<'tcx> { CanonicalVarKind::Const(ui) => { self.next_const_var_in_universe(span, universe_map(ui)).into() } - CanonicalVarKind::Effect => { - let vid = self - .inner - .borrow_mut() - .effect_unification_table() - .new_key(EffectVarValue::Unknown) - .vid; - ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(vid)).into() - } CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst { universe, bound }) => { let universe_mapped = universe_map(universe); let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, bound }; diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 57007752cad..0c151a11ad4 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -1,6 +1,5 @@ ///! Definition of `InferCtxtLike` from the librarified type layer. use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::infer::unify_key::EffectVarValue; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::solve::SolverMode; use rustc_middle::ty::fold::TypeFoldable; @@ -88,15 +87,6 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { } } - fn opportunistic_resolve_effect_var(&self, vid: ty::EffectVid) -> ty::Const<'tcx> { - match self.probe_effect_var(vid) { - Some(ct) => ct, - None => { - ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(self.root_effect_var(vid))) - } - } - } - fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> ty::Region<'tcx> { self.inner.borrow_mut().unwrap_region_constraints().opportunistic_resolve_var(self.tcx, vid) } @@ -152,10 +142,6 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.inner.borrow_mut().const_unification_table().union(a, b); } - fn equate_effect_vids_raw(&self, a: rustc_type_ir::EffectVid, b: rustc_type_ir::EffectVid) { - self.inner.borrow_mut().effect_unification_table().union(a, b); - } - fn instantiate_ty_var_raw<R: PredicateEmittingRelation<Self>>( &self, relation: &mut R, @@ -189,13 +175,6 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.inner.borrow_mut().float_unification_table().union_value(vid, value); } - fn instantiate_effect_var_raw(&self, vid: rustc_type_ir::EffectVid, value: ty::Const<'tcx>) { - self.inner - .borrow_mut() - .effect_unification_table() - .union_value(vid, EffectVarValue::Known(value)); - } - fn instantiate_const_var_raw<R: PredicateEmittingRelation<Self>>( &self, relation: &mut R, diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index c4294111ebe..28eac5b7496 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -153,15 +153,6 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> { drop(inner); self.freshen_const(input, ty::InferConst::Fresh) } - ty::ConstKind::Infer(ty::InferConst::EffectVar(v)) => { - let mut inner = self.infcx.inner.borrow_mut(); - let input = - inner.effect_unification_table().probe_value(v).known().ok_or_else(|| { - ty::InferConst::EffectVar(inner.effect_unification_table().find(v).vid) - }); - drop(inner); - self.freshen_const(input, ty::InferConst::Fresh) - } ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => { if i >= self.const_freshen_count { bug!( diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index f1195d0d4c0..be43cba97f0 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -26,9 +26,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::extension; pub use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues}; -use rustc_middle::infer::unify_key::{ - ConstVariableOrigin, ConstVariableValue, ConstVidKey, EffectVarValue, EffectVidKey, -}; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; use rustc_middle::traits::select; @@ -39,7 +37,7 @@ use rustc_middle::ty::fold::{ }; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{ - self, ConstVid, EffectVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, GenericArgsRef, + self, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, GenericArgsRef, GenericParamDefKind, InferConst, IntVid, Ty, TyCtxt, TyVid, }; use rustc_middle::{bug, span_bug}; @@ -117,9 +115,6 @@ pub struct InferCtxtInner<'tcx> { /// Map from floating variable to the kind of float it represents. float_unification_storage: ut::UnificationTableStorage<ty::FloatVid>, - /// Map from effect variable to the effect param it represents. - effect_unification_storage: ut::UnificationTableStorage<EffectVidKey<'tcx>>, - /// Tracks the set of region variables and the constraints between them. /// /// This is initially `Some(_)` but when @@ -176,7 +171,6 @@ impl<'tcx> InferCtxtInner<'tcx> { const_unification_storage: Default::default(), int_unification_storage: Default::default(), float_unification_storage: Default::default(), - effect_unification_storage: Default::default(), region_constraint_storage: Some(Default::default()), region_obligations: vec![], opaque_type_storage: Default::default(), @@ -228,10 +222,6 @@ impl<'tcx> InferCtxtInner<'tcx> { self.const_unification_storage.with_log(&mut self.undo_log) } - fn effect_unification_table(&mut self) -> UnificationTable<'_, 'tcx, EffectVidKey<'tcx>> { - self.effect_unification_storage.with_log(&mut self.undo_log) - } - #[inline] pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'_, 'tcx> { self.region_constraint_storage @@ -524,7 +514,6 @@ impl fmt::Display for FixupError { ), Ty(_) => write!(f, "unconstrained type"), Const(_) => write!(f, "unconstrained const value"), - Effect(_) => write!(f, "unconstrained effect value"), } } } @@ -726,17 +715,6 @@ impl<'tcx> InferCtxt<'tcx> { vars } - pub fn unsolved_effects(&self) -> Vec<ty::Const<'tcx>> { - let mut inner = self.inner.borrow_mut(); - let mut table = inner.effect_unification_table(); - - (0..table.len()) - .map(|i| ty::EffectVid::from_usize(i)) - .filter(|&vid| table.probe_value(vid).is_unknown()) - .map(|v| ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(v))) - .collect() - } - #[instrument(skip(self), level = "debug")] pub fn sub_regions( &self, @@ -984,10 +962,7 @@ impl<'tcx> InferCtxt<'tcx> { Ty::new_var(self.tcx, ty_var_id).into() } - GenericParamDefKind::Const { is_host_effect, .. } => { - if is_host_effect { - return self.var_for_effect(param); - } + GenericParamDefKind::Const { .. } => { let origin = ConstVariableOrigin { param_def_id: Some(param.def_id), span }; let const_var_id = self .inner @@ -1000,18 +975,6 @@ impl<'tcx> InferCtxt<'tcx> { } } - pub fn var_for_effect(&self, param: &ty::GenericParamDef) -> GenericArg<'tcx> { - let effect_vid = - self.inner.borrow_mut().effect_unification_table().new_key(EffectVarValue::Unknown).vid; - let ty = self - .tcx - .type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"); - debug_assert_eq!(self.tcx.types.bool, ty); - ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(effect_vid)).into() - } - /// Given a set of generics defined on a type or impl, returns the generic parameters mapping /// each type/region parameter to a fresh inference variable. pub fn fresh_args_for_item(&self, span: Span, def_id: DefId) -> GenericArgsRef<'tcx> { @@ -1137,13 +1100,6 @@ impl<'tcx> InferCtxt<'tcx> { .probe_value(vid) .known() .unwrap_or(ct), - InferConst::EffectVar(vid) => self - .inner - .borrow_mut() - .effect_unification_table() - .probe_value(vid) - .known() - .unwrap_or(ct), InferConst::Fresh(_) => ct, }, ty::ConstKind::Param(_) @@ -1164,10 +1120,6 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().const_unification_table().find(var).vid } - pub fn root_effect_var(&self, var: ty::EffectVid) -> ty::EffectVid { - self.inner.borrow_mut().effect_unification_table().find(var).vid - } - /// Resolves an int var to a rigid int type, if it was constrained to one, /// or else the root int var in the unification table. pub fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> { @@ -1233,10 +1185,6 @@ impl<'tcx> InferCtxt<'tcx> { } } - pub fn probe_effect_var(&self, vid: EffectVid) -> Option<ty::Const<'tcx>> { - self.inner.borrow_mut().effect_unification_table().probe_value(vid).known() - } - /// Attempts to resolve all type/region/const variables in /// `value`. Region inference must have been run already (e.g., /// by calling `resolve_regions_and_report_errors`). If some @@ -1506,14 +1454,6 @@ impl<'tcx> InferCtxt<'tcx> { ConstVariableValue::Known { .. } => true, } } - - TyOrConstInferVar::Effect(v) => { - // If `probe_value` returns `Some`, it never equals - // `ty::ConstKind::Infer(ty::InferConst::Effect(v))`. - // - // Not `inlined_probe_value(v)` because this call site is colder. - self.probe_effect_var(v).is_some() - } } } @@ -1540,8 +1480,6 @@ pub enum TyOrConstInferVar { /// Equivalent to `ty::ConstKind::Infer(ty::InferConst::Var(_))`. Const(ConstVid), - /// Equivalent to `ty::ConstKind::Infer(ty::InferConst::EffectVar(_))`. - Effect(EffectVid), } impl<'tcx> TyOrConstInferVar { @@ -1572,7 +1510,6 @@ impl<'tcx> TyOrConstInferVar { fn maybe_from_const(ct: ty::Const<'tcx>) -> Option<Self> { match ct.kind() { ty::ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)), - ty::ConstKind::Infer(InferConst::EffectVar(v)) => Some(TyOrConstInferVar::Effect(v)), _ => None, } } diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index e23bb1aaa56..1afe50e336d 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -24,19 +24,9 @@ pub fn explicit_outlives_bounds<'tcx>( param_env .caller_bounds() .into_iter() - .map(ty::Clause::kind) + .filter_map(ty::Clause::as_region_outlives_clause) .filter_map(ty::Binder::no_bound_vars) - .filter_map(move |kind| match kind { - ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => { - Some(OutlivesBound::RegionSubRegion(r_b, r_a)) - } - ty::ClauseKind::Trait(_) - | ty::ClauseKind::TypeOutlives(_) - | ty::ClauseKind::Projection(_) - | ty::ClauseKind::ConstArgHasType(_, _) - | ty::ClauseKind::WellFormed(_) - | ty::ClauseKind::ConstEvaluatable(_) => None, - }) + .map(|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a)) } impl<'tcx> InferCtxt<'tcx> { diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 7049444db9b..32817dbcb21 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -660,7 +660,6 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Generalizer<'_, 'tcx> { } } } - ty::ConstKind::Infer(InferConst::EffectVar(_)) => Ok(c), // FIXME: Unevaluated constants are also not rigid, so the current // approach of always relating them structurally is incomplete. // diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 64cc76f827e..6ec2e0152f0 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -176,9 +176,6 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for FullTypeResolver<'a, 'tcx> { ty::ConstKind::Infer(InferConst::Fresh(_)) => { bug!("Unexpected const in full const resolver: {:?}", c); } - ty::ConstKind::Infer(InferConst::EffectVar(evid)) => { - return Err(FixupError { unresolved: super::TyOrConstInferVar::Effect(evid) }); - } _ => {} } c.try_super_fold_with(self) diff --git a/compiler/rustc_infer/src/infer/snapshot/fudge.rs b/compiler/rustc_infer/src/infer/snapshot/fudge.rs index 8e330a084c6..394e07a81e7 100644 --- a/compiler/rustc_infer/src/infer/snapshot/fudge.rs +++ b/compiler/rustc_infer/src/infer/snapshot/fudge.rs @@ -4,9 +4,11 @@ use rustc_data_structures::{snapshot_vec as sv, unify as ut}; use rustc_middle::infer::unify_key::{ConstVariableValue, ConstVidKey}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid}; +use rustc_type_ir::visit::TypeVisitableExt; use tracing::instrument; use ut::UnifyKey; +use super::VariableLengths; use crate::infer::type_variable::TypeVariableOrigin; use crate::infer::{ConstVariableOrigin, InferCtxt, RegionVariableOrigin, UnificationTable}; @@ -40,26 +42,7 @@ fn const_vars_since_snapshot<'tcx>( ) } -struct VariableLengths { - type_var_len: usize, - const_var_len: usize, - int_var_len: usize, - float_var_len: usize, - region_constraints_len: usize, -} - impl<'tcx> InferCtxt<'tcx> { - fn variable_lengths(&self) -> VariableLengths { - let mut inner = self.inner.borrow_mut(); - VariableLengths { - type_var_len: inner.type_variables().num_vars(), - const_var_len: inner.const_unification_table().len(), - int_var_len: inner.int_unification_table().len(), - float_var_len: inner.float_unification_table().len(), - region_constraints_len: inner.unwrap_region_constraints().num_region_vars(), - } - } - /// This rather funky routine is used while processing expected /// types. What happens here is that we want to propagate a /// coercion through the return type of a fn to its @@ -106,148 +89,167 @@ impl<'tcx> InferCtxt<'tcx> { T: TypeFoldable<TyCtxt<'tcx>>, { let variable_lengths = self.variable_lengths(); - let (mut fudger, value) = self.probe(|_| { - match f() { - Ok(value) => { - let value = self.resolve_vars_if_possible(value); - - // At this point, `value` could in principle refer - // to inference variables that have been created during - // the snapshot. Once we exit `probe()`, those are - // going to be popped, so we will have to - // eliminate any references to them. - - let mut inner = self.inner.borrow_mut(); - let type_vars = - inner.type_variables().vars_since_snapshot(variable_lengths.type_var_len); - let int_vars = vars_since_snapshot( - &inner.int_unification_table(), - variable_lengths.int_var_len, - ); - let float_vars = vars_since_snapshot( - &inner.float_unification_table(), - variable_lengths.float_var_len, - ); - let region_vars = inner - .unwrap_region_constraints() - .vars_since_snapshot(variable_lengths.region_constraints_len); - let const_vars = const_vars_since_snapshot( - &mut inner.const_unification_table(), - variable_lengths.const_var_len, - ); - - let fudger = InferenceFudger { - infcx: self, - type_vars, - int_vars, - float_vars, - region_vars, - const_vars, - }; - - Ok((fudger, value)) - } - Err(e) => Err(e), - } + let (snapshot_vars, value) = self.probe(|_| { + let value = f()?; + // At this point, `value` could in principle refer + // to inference variables that have been created during + // the snapshot. Once we exit `probe()`, those are + // going to be popped, so we will have to + // eliminate any references to them. + let snapshot_vars = SnapshotVarData::new(self, variable_lengths); + Ok((snapshot_vars, self.resolve_vars_if_possible(value))) })?; // At this point, we need to replace any of the now-popped // type/region variables that appear in `value` with a fresh // variable of the appropriate kind. We can't do this during // the probe because they would just get popped then too. =) + Ok(self.fudge_inference(snapshot_vars, value)) + } + fn fudge_inference<T: TypeFoldable<TyCtxt<'tcx>>>( + &self, + snapshot_vars: SnapshotVarData, + value: T, + ) -> T { // Micro-optimization: if no variables have been created, then // `value` can't refer to any of them. =) So we can just return it. - if fudger.type_vars.0.is_empty() - && fudger.int_vars.is_empty() - && fudger.float_vars.is_empty() - && fudger.region_vars.0.is_empty() - && fudger.const_vars.0.is_empty() - { - Ok(value) + if snapshot_vars.is_empty() { + value } else { - Ok(value.fold_with(&mut fudger)) + value.fold_with(&mut InferenceFudger { infcx: self, snapshot_vars }) } } } -struct InferenceFudger<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, +struct SnapshotVarData { + region_vars: (Range<RegionVid>, Vec<RegionVariableOrigin>), type_vars: (Range<TyVid>, Vec<TypeVariableOrigin>), int_vars: Range<IntVid>, float_vars: Range<FloatVid>, - region_vars: (Range<RegionVid>, Vec<RegionVariableOrigin>), const_vars: (Range<ConstVid>, Vec<ConstVariableOrigin>), } +impl SnapshotVarData { + fn new(infcx: &InferCtxt<'_>, vars_pre_snapshot: VariableLengths) -> SnapshotVarData { + let mut inner = infcx.inner.borrow_mut(); + let region_vars = inner + .unwrap_region_constraints() + .vars_since_snapshot(vars_pre_snapshot.region_constraints_len); + let type_vars = inner.type_variables().vars_since_snapshot(vars_pre_snapshot.type_var_len); + let int_vars = + vars_since_snapshot(&inner.int_unification_table(), vars_pre_snapshot.int_var_len); + let float_vars = + vars_since_snapshot(&inner.float_unification_table(), vars_pre_snapshot.float_var_len); + + let const_vars = const_vars_since_snapshot( + &mut inner.const_unification_table(), + vars_pre_snapshot.const_var_len, + ); + SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars } + } + + fn is_empty(&self) -> bool { + let SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars } = self; + region_vars.0.is_empty() + && type_vars.0.is_empty() + && int_vars.is_empty() + && float_vars.is_empty() + && const_vars.0.is_empty() + } +} + +struct InferenceFudger<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, + snapshot_vars: SnapshotVarData, +} + impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for InferenceFudger<'a, 'tcx> { fn cx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match *ty.kind() { - ty::Infer(ty::InferTy::TyVar(vid)) => { - if self.type_vars.0.contains(&vid) { - // This variable was created during the fudging. - // Recreate it with a fresh variable here. - let idx = vid.as_usize() - self.type_vars.0.start.as_usize(); - let origin = self.type_vars.1[idx]; - self.infcx.next_ty_var_with_origin(origin) - } else { - // This variable was created before the - // "fudging". Since we refresh all type - // variables to their binding anyhow, we know - // that it is unbound, so we can just return - // it. - debug_assert!( - self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown() - ); - ty + if let &ty::Infer(infer_ty) = ty.kind() { + match infer_ty { + ty::TyVar(vid) => { + if self.snapshot_vars.type_vars.0.contains(&vid) { + // This variable was created during the fudging. + // Recreate it with a fresh variable here. + let idx = vid.as_usize() - self.snapshot_vars.type_vars.0.start.as_usize(); + let origin = self.snapshot_vars.type_vars.1[idx]; + self.infcx.next_ty_var_with_origin(origin) + } else { + // This variable was created before the + // "fudging". Since we refresh all type + // variables to their binding anyhow, we know + // that it is unbound, so we can just return + // it. + debug_assert!( + self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown() + ); + ty + } } - } - ty::Infer(ty::InferTy::IntVar(vid)) => { - if self.int_vars.contains(&vid) { - self.infcx.next_int_var() - } else { - ty + ty::IntVar(vid) => { + if self.snapshot_vars.int_vars.contains(&vid) { + self.infcx.next_int_var() + } else { + ty + } } - } - ty::Infer(ty::InferTy::FloatVar(vid)) => { - if self.float_vars.contains(&vid) { - self.infcx.next_float_var() - } else { - ty + ty::FloatVar(vid) => { + if self.snapshot_vars.float_vars.contains(&vid) { + self.infcx.next_float_var() + } else { + ty + } + } + ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => { + unreachable!("unexpected fresh infcx var") } } - _ => ty.super_fold_with(self), + } else if ty.has_infer() { + ty.super_fold_with(self) + } else { + ty } } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - if let ty::ReVar(vid) = *r - && self.region_vars.0.contains(&vid) - { - let idx = vid.index() - self.region_vars.0.start.index(); - let origin = self.region_vars.1[idx]; - return self.infcx.next_region_var(origin); + if let ty::ReVar(vid) = r.kind() { + if self.snapshot_vars.region_vars.0.contains(&vid) { + let idx = vid.index() - self.snapshot_vars.region_vars.0.start.index(); + let origin = self.snapshot_vars.region_vars.1[idx]; + self.infcx.next_region_var(origin) + } else { + r + } + } else { + r } - r } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.kind() { - if self.const_vars.0.contains(&vid) { - // This variable was created during the fudging. - // Recreate it with a fresh variable here. - let idx = vid.index() - self.const_vars.0.start.index(); - let origin = self.const_vars.1[idx]; - self.infcx.next_const_var_with_origin(origin) - } else { - ct + if let ty::ConstKind::Infer(infer_ct) = ct.kind() { + match infer_ct { + ty::InferConst::Var(vid) => { + if self.snapshot_vars.const_vars.0.contains(&vid) { + let idx = vid.index() - self.snapshot_vars.const_vars.0.start.index(); + let origin = self.snapshot_vars.const_vars.1[idx]; + self.infcx.next_const_var_with_origin(origin) + } else { + ct + } + } + ty::InferConst::Fresh(_) => { + unreachable!("unexpected fresh infcx var") + } } - } else { + } else if ct.has_infer() { ct.super_fold_with(self) + } else { + ct } } } diff --git a/compiler/rustc_infer/src/infer/snapshot/mod.rs b/compiler/rustc_infer/src/infer/snapshot/mod.rs index 07a482c2f9a..b16c80cf201 100644 --- a/compiler/rustc_infer/src/infer/snapshot/mod.rs +++ b/compiler/rustc_infer/src/infer/snapshot/mod.rs @@ -17,7 +17,26 @@ pub struct CombinedSnapshot<'tcx> { universe: ty::UniverseIndex, } +struct VariableLengths { + region_constraints_len: usize, + type_var_len: usize, + int_var_len: usize, + float_var_len: usize, + const_var_len: usize, +} + impl<'tcx> InferCtxt<'tcx> { + fn variable_lengths(&self) -> VariableLengths { + let mut inner = self.inner.borrow_mut(); + VariableLengths { + region_constraints_len: inner.unwrap_region_constraints().num_region_vars(), + type_var_len: inner.type_variables().num_vars(), + int_var_len: inner.int_unification_table().len(), + float_var_len: inner.float_unification_table().len(), + const_var_len: inner.const_unification_table().len(), + } + } + pub fn in_snapshot(&self) -> bool { UndoLogs::<UndoLog<'tcx>>::in_snapshot(&self.inner.borrow_mut().undo_log) } diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index 79ea0915c9c..713389f4618 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; use rustc_data_structures::undo_log::{Rollback, UndoLogs}; use rustc_data_structures::{snapshot_vec as sv, unify as ut}; -use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey, RegionVidKey}; +use rustc_middle::infer::unify_key::{ConstVidKey, RegionVidKey}; use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey}; use tracing::debug; @@ -22,7 +22,6 @@ pub(crate) enum UndoLog<'tcx> { ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>), IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>), FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>), - EffectUnificationTable(sv::UndoLog<ut::Delegate<EffectVidKey<'tcx>>>), RegionConstraintCollector(region_constraints::UndoLog<'tcx>), RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>), ProjectionCache(traits::UndoLog<'tcx>), @@ -50,7 +49,6 @@ impl_from! { FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>), ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>), - EffectUnificationTable(sv::UndoLog<ut::Delegate<EffectVidKey<'tcx>>>), RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>), ProjectionCache(traits::UndoLog<'tcx>), @@ -65,7 +63,6 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> { UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo), UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo), UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo), - UndoLog::EffectUnificationTable(undo) => self.effect_unification_storage.reverse(undo), UndoLog::RegionConstraintCollector(undo) => { self.region_constraint_storage.as_mut().unwrap().reverse(undo) } diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 779ce976bec..2086483b94a 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -187,10 +187,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { value_count: usize, ) -> (Range<TyVid>, Vec<TypeVariableOrigin>) { let range = TyVid::from_usize(value_count)..TyVid::from_usize(self.num_vars()); - ( - range.start..range.end, - (range.start..range.end).map(|index| self.var_origin(index)).collect(), - ) + (range.clone(), range.map(|index| self.var_origin(index)).collect()) } /// Returns indices of all variables that are not yet diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 3189620e969..d3762e739db 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -836,6 +836,7 @@ fn test_unstable_options_tracking_hash() { tracked!(profile_emit, Some(PathBuf::from("abc"))); tracked!(profile_sample_use, Some(PathBuf::from("abc"))); tracked!(profiler_runtime, "abc".to_string()); + tracked!(regparm, Some(3)); tracked!(relax_elf_relocations, Some(true)); tracked!(remap_cwd_prefix, Some(PathBuf::from("abc"))); tracked!(sanitizer, SanitizerSet::ADDRESS); diff --git a/compiler/rustc_lint/src/async_fn_in_trait.rs b/compiler/rustc_lint/src/async_fn_in_trait.rs index 63a8a949e96..9923f05df3c 100644 --- a/compiler/rustc_lint/src/async_fn_in_trait.rs +++ b/compiler/rustc_lint/src/async_fn_in_trait.rs @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for AsyncFnInTrait { && let hir::IsAsync::Async(async_span) = sig.header.asyncness { // RTN can be used to bound `async fn` in traits in a better way than "always" - if cx.tcx.features().return_type_notation { + if cx.tcx.features().return_type_notation() { return; } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 8bd9c899a62..70d51c92750 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1235,7 +1235,7 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller { def_id: LocalDefId, ) { if fn_kind.asyncness().is_async() - && !cx.tcx.features().async_fn_track_caller + && !cx.tcx.features().async_fn_track_caller() // Now, check if the function has the `#[track_caller]` attribute && let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller) { @@ -1424,7 +1424,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`. let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) - && cx.tcx.features().generic_const_exprs + && cx.tcx.features().generic_const_exprs() { return; } @@ -1538,7 +1538,7 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { use rustc_middle::ty::ClauseKind; - if cx.tcx.features().trivial_bounds { + if cx.tcx.features().trivial_bounds() { let predicates = cx.tcx.predicates_of(item.owner_id); for &(predicate, span) in predicates.predicates { let predicate_kind_name = match predicate.kind().skip_binder() { @@ -1554,7 +1554,9 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { // Ignore bounds that a user can't type | ClauseKind::WellFormed(..) // FIXME(generic_const_exprs): `ConstEvaluatable` can be written - | ClauseKind::ConstEvaluatable(..) => continue, + | ClauseKind::ConstEvaluatable(..) + // Users don't write this directly, only via another trait ref. + | ty::ClauseKind::HostEffect(..) => continue, }; if predicate.is_global() { cx.emit_span_lint(TRIVIAL_BOUNDS, span, BuiltinTrivialBounds { @@ -1892,11 +1894,11 @@ impl EarlyLintPass for KeywordIdents { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { self.check_tokens(cx, &mac.args.tokens); } - fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) { + fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: &Ident) { if ident.name.as_str().starts_with('\'') { self.check_ident_token(cx, UnderMacro(false), ident.without_first_quote(), "'"); } else { - self.check_ident_token(cx, UnderMacro(false), ident, ""); + self.check_ident_token(cx, UnderMacro(false), *ident, ""); } } } @@ -2287,13 +2289,15 @@ declare_lint_pass!( impl EarlyLintPass for IncompleteInternalFeatures { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { let features = cx.builder.features(); - features - .declared_lang_features - .iter() - .map(|(name, span, _)| (name, span)) - .chain(features.declared_lib_features.iter().map(|(name, span)| (name, span))) - .filter(|(&name, _)| features.incomplete(name) || features.internal(name)) - .for_each(|(&name, &span)| { + let lang_features = + features.enabled_lang_features().iter().map(|feat| (feat.gate_name, feat.attr_sp)); + let lib_features = + features.enabled_lib_features().iter().map(|feat| (feat.gate_name, feat.attr_sp)); + + lang_features + .chain(lib_features) + .filter(|(name, _)| features.incomplete(*name) || features.internal(*name)) + .for_each(|(name, span)| { if features.incomplete(name) { let note = rustc_feature::find_feature_issue(name, GateIssue::Language) .map(|n| BuiltinFeatureIssueNote { n }); @@ -2601,7 +2605,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { ty.tuple_fields().iter().find_map(|field| ty_find_init_error(cx, field, init)) } Array(ty, len) => { - if matches!(len.try_eval_target_usize(cx.tcx, cx.param_env), Some(v) if v > 0) { + if matches!(len.try_to_target_usize(cx.tcx), Some(v) if v > 0) { // Array length known at array non-empty -- recurse. ty_find_init_error(cx, *ty, init) } else { @@ -2657,8 +2661,8 @@ declare_lint! { /// /// ### Explanation /// - /// Dereferencing a null pointer causes [undefined behavior] even as a place expression, - /// like `&*(0 as *const i32)` or `addr_of!(*(0 as *const i32))`. + /// Dereferencing a null pointer causes [undefined behavior] if it is accessed + /// (loaded from or stored to). /// /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html pub DEREF_NULLPTR, @@ -2673,14 +2677,14 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { /// test if expression is a null ptr fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { match &expr.kind { - rustc_hir::ExprKind::Cast(expr, ty) => { - if let rustc_hir::TyKind::Ptr(_) = ty.kind { + hir::ExprKind::Cast(expr, ty) => { + if let hir::TyKind::Ptr(_) = ty.kind { return is_zero(expr) || is_null_ptr(cx, expr); } } // check for call to `core::ptr::null` or `core::ptr::null_mut` - rustc_hir::ExprKind::Call(path, _) => { - if let rustc_hir::ExprKind::Path(ref qpath) = path.kind { + hir::ExprKind::Call(path, _) => { + if let hir::ExprKind::Path(ref qpath) = path.kind { if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() { return matches!( cx.tcx.get_diagnostic_name(def_id), @@ -2697,7 +2701,7 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { /// test if expression is the literal `0` fn is_zero(expr: &hir::Expr<'_>) -> bool { match &expr.kind { - rustc_hir::ExprKind::Lit(lit) => { + hir::ExprKind::Lit(lit) => { if let LitKind::Int(a, _) = lit.node { return a == 0; } @@ -2707,8 +2711,16 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { false } - if let rustc_hir::ExprKind::Unary(rustc_hir::UnOp::Deref, expr_deref) = expr.kind { - if is_null_ptr(cx, expr_deref) { + if let hir::ExprKind::Unary(hir::UnOp::Deref, expr_deref) = expr.kind + && is_null_ptr(cx, expr_deref) + { + if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::AddrOf(hir::BorrowKind::Raw, ..), + .. + }) = cx.tcx.parent_hir_node(expr.hir_id) + { + // `&raw *NULL` is ok. + } else { cx.emit_span_lint(DEREF_NULLPTR, expr.span, BuiltinDerefNullptr { label: expr.span, }); diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 2285877c9ef..a6210aa520f 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -121,6 +121,18 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> self.with_lint_attrs(e.id, &e.attrs, |cx| { lint_callback!(cx, check_expr, e); ast_visit::walk_expr(cx, e); + // Explicitly check for lints associated with 'closure_id', since + // it does not have a corresponding AST node + match e.kind { + ast::ExprKind::Closure(box ast::Closure { + coroutine_kind: Some(coroutine_kind), + .. + }) => { + cx.check_id(coroutine_kind.closure_id()); + } + _ => {} + } + lint_callback!(cx, check_expr_post, e); }) } @@ -190,7 +202,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> ast_visit::walk_ty(self, t); } - fn visit_ident(&mut self, ident: Ident) { + fn visit_ident(&mut self, ident: &Ident) { lint_callback!(self, check_ident, ident); } @@ -214,21 +226,6 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> }) } - fn visit_expr_post(&mut self, e: &'a ast::Expr) { - // Explicitly check for lints associated with 'closure_id', since - // it does not have a corresponding AST node - match e.kind { - ast::ExprKind::Closure(box ast::Closure { - coroutine_kind: Some(coroutine_kind), - .. - }) => { - self.check_id(coroutine_kind.closure_id()); - } - _ => {} - } - lint_callback!(self, check_expr_post, e); - } - fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) { lint_callback!(self, check_generic_arg, arg); ast_visit::walk_generic_arg(self, arg); diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 58fd11fcc29..bdfcc2c0a10 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -243,7 +243,7 @@ impl_lint_pass!( impl<'tcx> LateLintPass<'tcx> for IfLetRescope { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - if expr.span.edition().at_least_rust_2024() || !cx.tcx.features().if_let_rescope { + if expr.span.edition().at_least_rust_2024() || !cx.tcx.features().if_let_rescope() { return; } if let (Level::Allow, _) = cx.tcx.lint_level_at_node(IF_LET_RESCOPE, expr.hir_id) { diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index d029ad93407..cc40b67ab27 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -263,8 +263,8 @@ where && parent == self.parent_def_id { let opaque_span = self.tcx.def_span(opaque_def_id); - let new_capture_rules = - opaque_span.at_least_rust_2024() || self.tcx.features().lifetime_capture_rules_2024; + let new_capture_rules = opaque_span.at_least_rust_2024() + || self.tcx.features().lifetime_capture_rules_2024(); if !new_capture_rules && !opaque.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Use(..))) { diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 89a67fc0d89..ff2ae69e1db 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -493,7 +493,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { // // This means that this only errors if we're truly lowering the lint // level from forbid. - if self.lint_added_lints && level != Level::Forbid && old_level == Level::Forbid { + if self.lint_added_lints && level == Level::Deny && old_level == Level::Forbid { + // Having a deny inside a forbid is fine and is ignored, so we skip this check. + return; + } else if self.lint_added_lints && level != Level::Forbid && old_level == Level::Forbid { // Backwards compatibility check: // // We used to not consider `forbid(lint_group)` @@ -855,7 +858,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { #[track_caller] fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool { let feature = if let Some(feature) = lint_id.lint.feature_gate - && !self.features.active(feature) + && !self.features.enabled(feature) { // Lint is behind a feature that is not enabled; eventually return false. feature diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 81352af3d48..a7faab0868d 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -598,6 +598,7 @@ fn register_builtins(store: &mut LintStore) { "converted into hard error, see PR #125380 \ <https://github.com/rust-lang/rust/pull/125380> for more information", ); + store.register_removed("unsupported_calling_conventions", "converted into hard error"); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 83a8ca4307e..1c27e1daa90 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -542,11 +542,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { } fn check_generic_param(&mut self, cx: &LateContext<'_>, param: &hir::GenericParam<'_>) { - if let GenericParamKind::Const { is_host_effect, .. } = param.kind { - // `host` params are explicitly allowed to be lowercase. - if is_host_effect { - return; - } + if let GenericParamKind::Const { .. } = param.kind { NonUpperCaseGlobals::check_upper_case(cx, "const parameter", ¶m.name.ident()); } } diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index a1d436e0d3d..75ae994a86b 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -133,7 +133,7 @@ macro_rules! early_lint_methods { ($macro:path, $args:tt) => ( $macro!($args, [ fn check_param(a: &rustc_ast::Param); - fn check_ident(a: rustc_span::symbol::Ident); + fn check_ident(a: &rustc_span::symbol::Ident); fn check_crate(a: &rustc_ast::Crate); fn check_crate_post(a: &rustc_ast::Crate); fn check_item(a: &rustc_ast::Item); diff --git a/compiler/rustc_lint/src/tail_expr_drop_order.rs b/compiler/rustc_lint/src/tail_expr_drop_order.rs index 44a36142ed4..89763059877 100644 --- a/compiler/rustc_lint/src/tail_expr_drop_order.rs +++ b/compiler/rustc_lint/src/tail_expr_drop_order.rs @@ -14,15 +14,14 @@ use rustc_span::edition::Edition; use crate::{LateContext, LateLintPass}; declare_lint! { - /// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location, that of type - /// with a significant `Drop` implementation, such as locks. - /// In case there are also local variables of type with significant `Drop` implementation as well, - /// this lint warns you of a potential transposition in the drop order. - /// Your discretion on the new drop order introduced by Edition 2024 is required. + /// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location, + /// that runs a custom `Drop` destructor. + /// Some of them may be dropped earlier in Edition 2024 that they used to in Edition 2021 and prior. + /// This lint detects those cases and provides you information on those values and their custom destructor implementations. + /// Your discretion on this information is required. /// /// ### Example - /// ```rust,edition2024 - /// #![feature(shorter_tail_lifetimes)] + /// ```rust,edition2021 /// #![warn(tail_expr_drop_order)] /// struct Droppy(i32); /// impl Droppy { @@ -37,12 +36,12 @@ declare_lint! { /// println!("loud drop {}", self.0); /// } /// } - /// fn edition_2024() -> i32 { + /// fn edition_2021() -> i32 { /// let another_droppy = Droppy(0); /// Droppy(1).get() /// } /// fn main() { - /// edition_2024(); + /// edition_2021(); /// } /// ``` /// @@ -137,7 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for TailExprDropOrder { _: Span, def_id: rustc_span::def_id::LocalDefId, ) { - if cx.tcx.sess.at_least_rust_2024() && cx.tcx.features().shorter_tail_lifetimes { + if !body.value.span.edition().at_least_rust_2024() { Self::check_fn_or_closure(cx, fn_kind, body, def_id); } } @@ -185,8 +184,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LintVisitor<'a, 'tcx> { impl<'a, 'tcx> LintVisitor<'a, 'tcx> { fn check_block_inner(&mut self, block: &Block<'tcx>) { - if !block.span.at_least_rust_2024() { - // We only lint for Edition 2024 onwards + if block.span.at_least_rust_2024() { + // We only lint up to Edition 2021 return; } let Some(tail_expr) = block.expr else { return }; diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index 5a3666dcbd4..b793ec6a493 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -114,10 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return }; for bound in &bounds[..] { let def_id = bound.trait_ref.trait_def_id(); - if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop)) - // FIXME: ?Drop is not a thing. - && bound.modifiers != hir::TraitBoundModifier::Maybe - { + if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop)) { let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return }; cx.emit_span_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id }); } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index db4413149a4..60ff925b40e 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -18,6 +18,8 @@ use rustc_target::spec::abi::Abi as SpecAbi; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; +mod improper_ctypes; + use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, @@ -728,7 +730,6 @@ fn is_niche_optimization_candidate<'tcx>( /// can, return the type that `ty` can be safely converted to, otherwise return `None`. /// Currently restricted to function pointers, boxes, references, `core::num::NonZero`, /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes. -/// FIXME: This duplicates code in codegen. pub(crate) fn repr_nullable_ptr<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -741,10 +742,6 @@ pub(crate) fn repr_nullable_ptr<'tcx>( [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) { ([], [field]) | ([field], []) => field.ty(tcx, args), ([field1], [field2]) => { - if !tcx.features().result_ffi_guarantees { - return None; - } - let ty1 = field1.ty(tcx, args); let ty2 = field2.ty(tcx, args); @@ -983,15 +980,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Empty enums are okay... although sort of useless. return FfiSafe; } - - if def.is_variant_list_non_exhaustive() && !def.did().is_local() { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_non_exhaustive, - help: None, - }; - } - // Check for a repr() attribute to specify the size of the // discriminant. if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() @@ -1010,21 +998,23 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }; } + use improper_ctypes::{ + check_non_exhaustive_variant, non_local_and_non_exhaustive, + }; + + let non_local_def = non_local_and_non_exhaustive(def); // Check the contained variants. - for variant in def.variants() { - let is_non_exhaustive = variant.is_field_list_non_exhaustive(); - if is_non_exhaustive && !variant.def_id.is_local() { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_non_exhaustive_variant, - help: None, - }; - } + let ret = def.variants().iter().try_for_each(|variant| { + check_non_exhaustive_variant(non_local_def, variant) + .map_break(|reason| FfiUnsafe { ty, reason, help: None })?; match self.check_variant_for_ffi(acc, ty, def, variant, args) { - FfiSafe => (), - r => return r, + FfiSafe => ControlFlow::Continue(()), + r => ControlFlow::Break(r), } + }); + if let ControlFlow::Break(result) = ret { + return result; } FfiSafe diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs new file mode 100644 index 00000000000..1030101c545 --- /dev/null +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -0,0 +1,51 @@ +use std::ops::ControlFlow; + +use rustc_errors::DiagMessage; +use rustc_hir::def::CtorKind; +use rustc_middle::ty; + +use crate::fluent_generated as fluent; + +/// Check a variant of a non-exhaustive enum for improper ctypes +/// +/// We treat `#[non_exhaustive] enum` as "ensure that code will compile if new variants are added". +/// This includes linting, on a best-effort basis. There are valid additions that are unlikely. +/// +/// Adding a data-carrying variant to an existing C-like enum that is passed to C is "unlikely", +/// so we don't need the lint to account for it. +/// e.g. going from enum Foo { A, B, C } to enum Foo { A, B, C, D(u32) }. +pub(crate) fn check_non_exhaustive_variant( + non_local_def: bool, + variant: &ty::VariantDef, +) -> ControlFlow<DiagMessage, ()> { + // non_exhaustive suggests it is possible that someone might break ABI + // see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344 + // so warn on complex enums being used outside their crate + if non_local_def { + // which is why we only warn about really_tagged_union reprs from https://rust.tf/rfc2195 + // with an enum like `#[repr(u8)] enum Enum { A(DataA), B(DataB), }` + // but exempt enums with unit ctors like C's (e.g. from rust-bindgen) + if variant_has_complex_ctor(variant) { + return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive); + } + } + + let non_exhaustive_variant_fields = variant.is_field_list_non_exhaustive(); + if non_exhaustive_variant_fields && !variant.def_id.is_local() { + return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive_variant); + } + + ControlFlow::Continue(()) +} + +fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool { + // CtorKind::Const means a "unit" ctor + !matches!(variant.ctor_kind(), Some(CtorKind::Const)) +} + +// non_exhaustive suggests it is possible that someone might break ABI +// see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344 +// so warn on complex enums being used outside their crate +pub(crate) fn non_local_and_non_exhaustive(def: ty::AdtDef<'_>) -> bool { + def.is_variant_list_non_exhaustive() && !def.did().is_local() +} diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 1a007250961..ddc18c755a8 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -346,7 +346,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { None } } - ty::Array(ty, len) => match len.try_eval_target_usize(cx.tcx, cx.param_env) { + ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) { // If the array is empty we don't lint, to avoid false positives Some(0) | None => None, // If the array is definitely non-empty, we can do `#[must_use]` checking. diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 45a5ce0ca20..a4c49a15905 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -123,7 +123,6 @@ declare_lint_pass! { UNSAFE_OP_IN_UNSAFE_FN, UNSTABLE_NAME_COLLISIONS, UNSTABLE_SYNTAX_PRE_EXPANSION, - UNSUPPORTED_CALLING_CONVENTIONS, UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, UNUSED_ASSIGNMENTS, UNUSED_ASSOCIATED_TYPE_BOUNDS, @@ -156,7 +155,7 @@ declare_lint! { /// /// ```rust /// #![forbid(warnings)] - /// #![deny(bad_style)] + /// #![warn(bad_style)] /// /// fn main() {} /// ``` @@ -2667,7 +2666,6 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![feature(strict_provenance)] /// #![warn(fuzzy_provenance_casts)] /// /// fn main() { @@ -2701,7 +2699,7 @@ declare_lint! { pub FUZZY_PROVENANCE_CASTS, Allow, "a fuzzy integer to pointer cast is used", - @feature_gate = strict_provenance; + @feature_gate = strict_provenance_lints; } declare_lint! { @@ -2711,7 +2709,6 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![feature(strict_provenance)] /// #![warn(lossy_provenance_casts)] /// /// fn main() { @@ -2747,7 +2744,7 @@ declare_lint! { pub LOSSY_PROVENANCE_CASTS, Allow, "a lossy pointer to integer cast is used", - @feature_gate = strict_provenance; + @feature_gate = strict_provenance_lints; } declare_lint! { @@ -3790,53 +3787,6 @@ declare_lint! { } declare_lint! { - /// The `unsupported_calling_conventions` lint is output whenever there is a use of the - /// `stdcall`, `fastcall`, `thiscall`, `vectorcall` calling conventions (or their unwind - /// variants) on targets that cannot meaningfully be supported for the requested target. - /// - /// For example `stdcall` does not make much sense for a x86_64 or, more apparently, powerpc - /// code, because this calling convention was never specified for those targets. - /// - /// Historically MSVC toolchains have fallen back to the regular C calling convention for - /// targets other than x86, but Rust doesn't really see a similar need to introduce a similar - /// hack across many more targets. - /// - /// ### Example - /// - /// ```rust,ignore (needs specific targets) - /// extern "stdcall" fn stdcall() {} - /// ``` - /// - /// This will produce: - /// - /// ```text - /// warning: use of calling convention not supported on this target - /// --> $DIR/unsupported.rs:39:1 - /// | - /// LL | extern "stdcall" fn stdcall() {} - /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - /// | - /// = note: `#[warn(unsupported_calling_conventions)]` on by default - /// = 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 ... - /// ``` - /// - /// ### Explanation - /// - /// On most of the targets the behaviour of `stdcall` and similar calling conventions is not - /// defined at all, but was previously accepted due to a bug in the implementation of the - /// compiler. - pub UNSUPPORTED_CALLING_CONVENTIONS, - Warn, - "use of unsupported calling convention", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, - reference: "issue #87678 <https://github.com/rust-lang/rust/issues/87678>", - }; -} - -declare_lint! { /// The `unsupported_fn_ptr_calling_conventions` lint is output whenever there is a use of /// a target dependent calling convention on a target that does not support this calling /// convention on a function pointer. diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index cda81d4a9b5..b32af5e5e75 100644 --- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -33,45 +33,6 @@ static coverage::Counter fromRust(LLVMRustCounter Counter) { report_fatal_error("Bad LLVMRustCounterKind!"); } -// FFI equivalent of enum `llvm::coverage::CounterMappingRegion::RegionKind` -// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L213-L234 -enum class LLVMRustCounterMappingRegionKind { - CodeRegion = 0, - ExpansionRegion = 1, - SkippedRegion = 2, - GapRegion = 3, - BranchRegion = 4, - MCDCDecisionRegion = 5, - MCDCBranchRegion = 6 -}; - -static coverage::CounterMappingRegion::RegionKind -fromRust(LLVMRustCounterMappingRegionKind Kind) { - switch (Kind) { - case LLVMRustCounterMappingRegionKind::CodeRegion: - return coverage::CounterMappingRegion::CodeRegion; - case LLVMRustCounterMappingRegionKind::ExpansionRegion: - return coverage::CounterMappingRegion::ExpansionRegion; - case LLVMRustCounterMappingRegionKind::SkippedRegion: - return coverage::CounterMappingRegion::SkippedRegion; - case LLVMRustCounterMappingRegionKind::GapRegion: - return coverage::CounterMappingRegion::GapRegion; - case LLVMRustCounterMappingRegionKind::BranchRegion: - return coverage::CounterMappingRegion::BranchRegion; - case LLVMRustCounterMappingRegionKind::MCDCDecisionRegion: - return coverage::CounterMappingRegion::MCDCDecisionRegion; - case LLVMRustCounterMappingRegionKind::MCDCBranchRegion: - return coverage::CounterMappingRegion::MCDCBranchRegion; - } - report_fatal_error("Bad LLVMRustCounterMappingRegionKind!"); -} - -enum LLVMRustMCDCParametersTag { - None = 0, - Decision = 1, - Branch = 2, -}; - struct LLVMRustMCDCDecisionParameters { uint32_t BitmapIdx; uint16_t NumConditions; @@ -82,47 +43,58 @@ struct LLVMRustMCDCBranchParameters { int16_t ConditionIDs[2]; }; -struct LLVMRustMCDCParameters { - LLVMRustMCDCParametersTag Tag; - LLVMRustMCDCDecisionParameters DecisionParameters; - LLVMRustMCDCBranchParameters BranchParameters; -}; - #if LLVM_VERSION_GE(19, 0) -static coverage::mcdc::Parameters fromRust(LLVMRustMCDCParameters Params) { - switch (Params.Tag) { - case LLVMRustMCDCParametersTag::None: - return std::monostate(); - case LLVMRustMCDCParametersTag::Decision: - return coverage::mcdc::DecisionParameters( - Params.DecisionParameters.BitmapIdx, - Params.DecisionParameters.NumConditions); - case LLVMRustMCDCParametersTag::Branch: - return coverage::mcdc::BranchParameters( - static_cast<coverage::mcdc::ConditionID>( - Params.BranchParameters.ConditionID), - {static_cast<coverage::mcdc::ConditionID>( - Params.BranchParameters.ConditionIDs[0]), - static_cast<coverage::mcdc::ConditionID>( - Params.BranchParameters.ConditionIDs[1])}); - } - report_fatal_error("Bad LLVMRustMCDCParametersTag!"); +static coverage::mcdc::BranchParameters +fromRust(LLVMRustMCDCBranchParameters Params) { + return coverage::mcdc::BranchParameters( + Params.ConditionID, {Params.ConditionIDs[0], Params.ConditionIDs[1]}); +} + +static coverage::mcdc::DecisionParameters +fromRust(LLVMRustMCDCDecisionParameters Params) { + return coverage::mcdc::DecisionParameters(Params.BitmapIdx, + Params.NumConditions); } #endif -// FFI equivalent of struct `llvm::coverage::CounterMappingRegion` -// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L211-L304 -struct LLVMRustCounterMappingRegion { - LLVMRustCounter Count; - LLVMRustCounter FalseCount; - LLVMRustMCDCParameters MCDCParameters; +// Must match the layout of +// `rustc_codegen_llvm::coverageinfo::ffi::CoverageSpan`. +struct LLVMRustCoverageSpan { uint32_t FileID; - uint32_t ExpandedFileID; uint32_t LineStart; uint32_t ColumnStart; uint32_t LineEnd; uint32_t ColumnEnd; - LLVMRustCounterMappingRegionKind Kind; +}; + +// Must match the layout of `rustc_codegen_llvm::coverageinfo::ffi::CodeRegion`. +struct LLVMRustCoverageCodeRegion { + LLVMRustCoverageSpan Span; + LLVMRustCounter Count; +}; + +// Must match the layout of +// `rustc_codegen_llvm::coverageinfo::ffi::BranchRegion`. +struct LLVMRustCoverageBranchRegion { + LLVMRustCoverageSpan Span; + LLVMRustCounter TrueCount; + LLVMRustCounter FalseCount; +}; + +// Must match the layout of +// `rustc_codegen_llvm::coverageinfo::ffi::MCDCBranchRegion`. +struct LLVMRustCoverageMCDCBranchRegion { + LLVMRustCoverageSpan Span; + LLVMRustCounter TrueCount; + LLVMRustCounter FalseCount; + LLVMRustMCDCBranchParameters MCDCBranchParams; +}; + +// Must match the layout of +// `rustc_codegen_llvm::coverageinfo::ffi::MCDCDecisionRegion`. +struct LLVMRustCoverageMCDCDecisionRegion { + LLVMRustCoverageSpan Span; + LLVMRustMCDCDecisionParameters MCDCDecisionParams; }; // FFI equivalent of enum `llvm::coverage::CounterExpression::ExprKind` @@ -174,28 +146,16 @@ extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer( extern "C" void LLVMRustCoverageWriteMappingToBuffer( const unsigned *VirtualFileMappingIDs, unsigned NumVirtualFileMappingIDs, const LLVMRustCounterExpression *RustExpressions, unsigned NumExpressions, - const LLVMRustCounterMappingRegion *RustMappingRegions, - unsigned NumMappingRegions, RustStringRef BufferOut) { + const LLVMRustCoverageCodeRegion *CodeRegions, unsigned NumCodeRegions, + const LLVMRustCoverageBranchRegion *BranchRegions, + unsigned NumBranchRegions, + const LLVMRustCoverageMCDCBranchRegion *MCDCBranchRegions, + unsigned NumMCDCBranchRegions, + const LLVMRustCoverageMCDCDecisionRegion *MCDCDecisionRegions, + unsigned NumMCDCDecisionRegions, RustStringRef BufferOut) { // Convert from FFI representation to LLVM representation. - SmallVector<coverage::CounterMappingRegion, 0> MappingRegions; - MappingRegions.reserve(NumMappingRegions); - for (const auto &Region : ArrayRef<LLVMRustCounterMappingRegion>( - RustMappingRegions, NumMappingRegions)) { - MappingRegions.emplace_back( - fromRust(Region.Count), fromRust(Region.FalseCount), -#if LLVM_VERSION_LT(19, 0) - coverage::CounterMappingRegion::MCDCParameters{}, -#endif - Region.FileID, Region.ExpandedFileID, // File IDs, then region info. - Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd, - fromRust(Region.Kind) -#if LLVM_VERSION_GE(19, 0) - , - fromRust(Region.MCDCParameters) -#endif - ); - } + // Expressions: std::vector<coverage::CounterExpression> Expressions; Expressions.reserve(NumExpressions); for (const auto &Expression : @@ -205,6 +165,46 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer( fromRust(Expression.RHS)); } + std::vector<coverage::CounterMappingRegion> MappingRegions; + MappingRegions.reserve(NumCodeRegions + NumBranchRegions + + NumMCDCBranchRegions + NumMCDCDecisionRegions); + + // Code regions: + for (const auto &Region : ArrayRef(CodeRegions, NumCodeRegions)) { + MappingRegions.push_back(coverage::CounterMappingRegion::makeRegion( + fromRust(Region.Count), Region.Span.FileID, Region.Span.LineStart, + Region.Span.ColumnStart, Region.Span.LineEnd, Region.Span.ColumnEnd)); + } + + // Branch regions: + for (const auto &Region : ArrayRef(BranchRegions, NumBranchRegions)) { + MappingRegions.push_back(coverage::CounterMappingRegion::makeBranchRegion( + fromRust(Region.TrueCount), fromRust(Region.FalseCount), + Region.Span.FileID, Region.Span.LineStart, Region.Span.ColumnStart, + Region.Span.LineEnd, Region.Span.ColumnEnd)); + } + +#if LLVM_VERSION_GE(19, 0) + // MC/DC branch regions: + for (const auto &Region : ArrayRef(MCDCBranchRegions, NumMCDCBranchRegions)) { + MappingRegions.push_back(coverage::CounterMappingRegion::makeBranchRegion( + fromRust(Region.TrueCount), fromRust(Region.FalseCount), + Region.Span.FileID, Region.Span.LineStart, Region.Span.ColumnStart, + Region.Span.LineEnd, Region.Span.ColumnEnd, + fromRust(Region.MCDCBranchParams))); + } + + // MC/DC decision regions: + for (const auto &Region : + ArrayRef(MCDCDecisionRegions, NumMCDCDecisionRegions)) { + MappingRegions.push_back(coverage::CounterMappingRegion::makeDecisionRegion( + fromRust(Region.MCDCDecisionParams), Region.Span.FileID, + Region.Span.LineStart, Region.Span.ColumnStart, Region.Span.LineEnd, + Region.Span.ColumnEnd)); + } +#endif + + // Write the converted expressions and mappings to a byte buffer. auto CoverageMappingWriter = coverage::CoverageMappingWriter( ArrayRef<unsigned>(VirtualFileMappingIDs, NumVirtualFileMappingIDs), Expressions, MappingRegions); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 72b03fa0560..cb75888abd7 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1531,45 +1531,6 @@ extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, ArrayRef<OperandBundleDef>(OpBundles))); } -extern "C" LLVMValueRef -LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) { -#if LLVM_VERSION_GE(20, 0) - return wrap(llvm::Intrinsic::getOrInsertDeclaration( - unwrap(M), llvm::Intrinsic::instrprof_increment)); -#else - return wrap(llvm::Intrinsic::getDeclaration( - unwrap(M), llvm::Intrinsic::instrprof_increment)); -#endif -} - -extern "C" LLVMValueRef -LLVMRustGetInstrProfMCDCParametersIntrinsic(LLVMModuleRef M) { -#if LLVM_VERSION_LT(19, 0) - report_fatal_error("LLVM 19.0 is required for mcdc intrinsic functions"); -#endif -#if LLVM_VERSION_GE(20, 0) - return wrap(llvm::Intrinsic::getOrInsertDeclaration( - unwrap(M), llvm::Intrinsic::instrprof_mcdc_parameters)); -#else - return wrap(llvm::Intrinsic::getDeclaration( - unwrap(M), llvm::Intrinsic::instrprof_mcdc_parameters)); -#endif -} - -extern "C" LLVMValueRef -LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(LLVMModuleRef M) { -#if LLVM_VERSION_LT(19, 0) - report_fatal_error("LLVM 19.0 is required for mcdc intrinsic functions"); -#endif -#if LLVM_VERSION_GE(20, 0) - return wrap(llvm::Intrinsic::getOrInsertDeclaration( - unwrap(M), llvm::Intrinsic::instrprof_mcdc_tvbitmap_update)); -#else - return wrap(llvm::Intrinsic::getDeclaration( - unwrap(M), llvm::Intrinsic::instrprof_mcdc_tvbitmap_update)); -#endif -} - extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Src, unsigned SrcAlign, @@ -1658,16 +1619,6 @@ extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B, unwrap(B)->SetInsertPoint(unwrap(BB), Point); } -extern "C" void LLVMRustSetComdat(LLVMModuleRef M, LLVMValueRef V, - const char *Name, size_t NameLen) { - Triple TargetTriple = Triple(unwrap(M)->getTargetTriple()); - GlobalObject *GV = unwrap<GlobalObject>(V); - if (TargetTriple.supportsCOMDAT()) { - StringRef NameRef(Name, NameLen); - GV->setComdat(unwrap(M)->getOrInsertComdat(NameRef)); - } -} - enum class LLVMRustLinkage { ExternalLinkage = 0, AvailableExternallyLinkage = 1, diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index 39fa23766b5..641d1d8e798 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -170,7 +170,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { let mut upstream_in_dylibs = FxHashSet::default(); - if tcx.features().rustc_private { + if tcx.features().rustc_private() { // We need this to prevent users of `rustc_driver` from linking dynamically to `std` // which does not work as `std` is also statically linked into `rustc_driver`. diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 99c673b021a..a4a69ae9514 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -499,8 +499,11 @@ impl<'a> CrateLocator<'a> { dylibs: FxIndexMap<PathBuf, PathKind>, ) -> Result<Option<(Svh, Library)>, CrateError> { let mut slot = None; - // Order here matters, rmeta should come first. See comment in - // `extract_one` below. + // Order here matters, rmeta should come first. + // + // Make sure there's at most one rlib and at most one dylib. + // + // See comment in `extract_one` below. let source = CrateSource { rmeta: self.extract_one(rmetas, CrateFlavor::Rmeta, &mut slot)?, rlib: self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot)?, @@ -706,54 +709,58 @@ impl<'a> CrateLocator<'a> { let mut rmetas = FxIndexMap::default(); let mut dylibs = FxIndexMap::default(); for loc in &self.exact_paths { - if !loc.canonicalized().exists() { - return Err(CrateError::ExternLocationNotExist( - self.crate_name, - loc.original().clone(), - )); + let loc_canon = loc.canonicalized(); + let loc_orig = loc.original(); + if !loc_canon.exists() { + return Err(CrateError::ExternLocationNotExist(self.crate_name, loc_orig.clone())); } - if !loc.original().is_file() { - return Err(CrateError::ExternLocationNotFile( - self.crate_name, - loc.original().clone(), - )); + if !loc_orig.is_file() { + return Err(CrateError::ExternLocationNotFile(self.crate_name, loc_orig.clone())); } - let Some(file) = loc.original().file_name().and_then(|s| s.to_str()) else { - return Err(CrateError::ExternLocationNotFile( - self.crate_name, - loc.original().clone(), - )); + // Note to take care and match against the non-canonicalized name: + // some systems save build artifacts into content-addressed stores + // that do not preserve extensions, and then link to them using + // e.g. symbolic links. If we canonicalize too early, we resolve + // the symlink, the file type is lost and we might treat rlibs and + // rmetas as dylibs. + let Some(file) = loc_orig.file_name().and_then(|s| s.to_str()) else { + return Err(CrateError::ExternLocationNotFile(self.crate_name, loc_orig.clone())); }; - - if file.starts_with("lib") && (file.ends_with(".rlib") || file.ends_with(".rmeta")) - || file.starts_with(self.target.dll_prefix.as_ref()) - && file.ends_with(self.target.dll_suffix.as_ref()) - { - // Make sure there's at most one rlib and at most one dylib. - // Note to take care and match against the non-canonicalized name: - // some systems save build artifacts into content-addressed stores - // that do not preserve extensions, and then link to them using - // e.g. symbolic links. If we canonicalize too early, we resolve - // the symlink, the file type is lost and we might treat rlibs and - // rmetas as dylibs. - let loc_canon = loc.canonicalized().clone(); - let loc = loc.original(); - if loc.file_name().unwrap().to_str().unwrap().ends_with(".rlib") { - rlibs.insert(loc_canon, PathKind::ExternFlag); - } else if loc.file_name().unwrap().to_str().unwrap().ends_with(".rmeta") { - rmetas.insert(loc_canon, PathKind::ExternFlag); - } else { - dylibs.insert(loc_canon, PathKind::ExternFlag); + // FnMut cannot return reference to captured value, so references + // must be taken outside the closure. + let rlibs = &mut rlibs; + let rmetas = &mut rmetas; + let dylibs = &mut dylibs; + let type_via_filename = (|| { + if file.starts_with("lib") { + if file.ends_with(".rlib") { + return Some(rlibs); + } + if file.ends_with(".rmeta") { + return Some(rmetas); + } + } + let dll_prefix = self.target.dll_prefix.as_ref(); + let dll_suffix = self.target.dll_suffix.as_ref(); + if file.starts_with(dll_prefix) && file.ends_with(dll_suffix) { + return Some(dylibs); + } + None + })(); + match type_via_filename { + Some(type_via_filename) => { + type_via_filename.insert(loc_canon.clone(), PathKind::ExternFlag); + } + None => { + self.crate_rejections + .via_filename + .push(CrateMismatch { path: loc_orig.clone(), got: String::new() }); } - } else { - self.crate_rejections - .via_filename - .push(CrateMismatch { path: loc.original().clone(), got: String::new() }); } } // Extract the dylib/rlib/rmeta triple. - Ok(self.extract_lib(rlibs, rmetas, dylibs)?.map(|(_, lib)| lib)) + self.extract_lib(rlibs, rmetas, dylibs).map(|opt| opt.map(|(_, lib)| lib)) } pub(crate) fn into_error(self, root: Option<CratePaths>) -> CrateError { diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index c7953d50406..1e6bc413118 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -265,7 +265,7 @@ impl<'tcx> Collector<'tcx> { NativeLibKind::RawDylib } "link-arg" => { - if !features.link_arg_attribute { + if !features.link_arg_attribute() { feature_err( sess, sym::link_arg_attribute, @@ -314,7 +314,7 @@ impl<'tcx> Collector<'tcx> { .emit_err(errors::LinkCfgSinglePredicate { span: item.span() }); continue; }; - if !features.link_cfg { + if !features.link_cfg() { feature_err( sess, sym::link_cfg, @@ -384,7 +384,7 @@ impl<'tcx> Collector<'tcx> { }; macro report_unstable_modifier($feature: ident) { - if !features.$feature { + if !features.$feature() { // FIXME: make this translatable #[expect(rustc::untranslatable_diagnostic)] feature_err( diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 69707fdbe8f..71c7231a788 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -275,6 +275,8 @@ provide! { tcx, def_id, other, cdata, impl_parent => { table } defaultness => { table_direct } constness => { table_direct } + const_conditions => { table } + implied_const_bounds => { table_defaulted_array } coerce_unsized_info => { Ok(cdata .root @@ -330,7 +332,6 @@ provide! { tcx, def_id, other, cdata, .process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys"))) } - associated_type_for_effects => { table } associated_types_for_impl_traits_in_associated_fn => { table_defaulted_array } visibility => { cdata.get_visibility(def_id.index) } @@ -437,9 +438,6 @@ provide! { tcx, def_id, other, cdata, pub(in crate::rmeta) fn provide(providers: &mut Providers) { provide_cstore_hooks(providers); - // FIXME(#44234) - almost all of these queries have no sub-queries and - // therefore no actual inputs, they're just reading tables calculated in - // resolve! Does this work? Unsure! That's what the issue is about providers.queries = rustc_middle::query::Providers { allocator_kind: |tcx, ()| CStore::from_tcx(tcx).allocator_kind(), alloc_error_handler_kind: |tcx, ()| CStore::from_tcx(tcx).alloc_error_handler_kind(), diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index afe03531861..47f7a8b7c20 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1081,7 +1081,7 @@ fn should_encode_mir( && (generics.requires_monomorphization(tcx) || tcx.cross_crate_inlinable(def_id))); // The function has a `const` modifier or is in a `#[const_trait]`. - let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id()) + let is_const_fn = tcx.is_const_fn(def_id.to_def_id()) || tcx.is_const_default_method(def_id.to_def_id()); (is_const_fn, opt) } @@ -1433,6 +1433,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } } + if tcx.is_conditionally_const(def_id) { + record!(self.tables.const_conditions[def_id] <- self.tcx.const_conditions(def_id)); + } if should_encode_type(tcx, local_id, def_kind) { record!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id)); } @@ -1456,10 +1459,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.tcx.explicit_super_predicates_of(def_id).skip_binder()); record_defaulted_array!(self.tables.explicit_implied_predicates_of[def_id] <- self.tcx.explicit_implied_predicates_of(def_id).skip_binder()); - let module_children = self.tcx.module_children_local(local_id); record_array!(self.tables.module_children_non_reexports[def_id] <- module_children.iter().map(|child| child.res.def_id().index)); + if self.tcx.is_const_trait(def_id) { + record_defaulted_array!(self.tables.implied_const_bounds[def_id] + <- self.tcx.implied_const_bounds(def_id).skip_binder()); + } } if let DefKind::TraitAlias = def_kind { record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); @@ -1479,9 +1485,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { for &def_id in associated_item_def_ids { self.encode_info_for_assoc_item(def_id); } - if let Some(assoc_def_id) = self.tcx.associated_type_for_effects(def_id) { - record!(self.tables.associated_type_for_effects[def_id] <- assoc_def_id); - } } if let DefKind::Closure | DefKind::SyntheticCoroutineBody = def_kind && let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id) @@ -1652,6 +1655,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let ty::AssocKind::Type = item.kind { self.encode_explicit_item_bounds(def_id); self.encode_explicit_item_super_predicates(def_id); + if tcx.is_conditionally_const(def_id) { + record_defaulted_array!(self.tables.implied_const_bounds[def_id] + <- self.tcx.implied_const_bounds(def_id).skip_binder()); + } } } AssocItemContainer::ImplContainer => { @@ -1765,7 +1772,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_stability(&mut self, def_id: DefId) { // The query lookup can take a measurable amount of time in crates with many items. Check if // the stability attributes are even enabled before using their queries. - if self.feat.staged_api || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { + if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_stability(def_id) { record!(self.tables.lookup_stability[def_id] <- stab) } @@ -1776,7 +1783,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_const_stability(&mut self, def_id: DefId) { // The query lookup can take a measurable amount of time in crates with many items. Check if // the stability attributes are even enabled before using their queries. - if self.feat.staged_api || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { + if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_const_stability(def_id) { record!(self.tables.lookup_const_stability[def_id] <- stab) } @@ -1787,7 +1794,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_default_body_stability(&mut self, def_id: DefId) { // The query lookup can take a measurable amount of time in crates with many items. Check if // the stability attributes are even enabled before using their queries. - if self.feat.staged_api || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { + if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_default_body_stability(def_id) { record!(self.tables.lookup_default_body_stability[def_id] <- stab) } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 79bd1c13b12..a00ca27aacc 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -392,9 +392,9 @@ define_tables! { inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, explicit_super_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, explicit_implied_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, + implied_const_bounds: Table<DefIndex, LazyArray<(ty::PolyTraitRef<'static>, Span)>>, inherent_impls: Table<DefIndex, LazyArray<DefIndex>>, associated_types_for_impl_traits_in_associated_fn: Table<DefIndex, LazyArray<DefId>>, - associated_type_for_effects: Table<DefIndex, Option<LazyValue<DefId>>>, opt_rpitit_info: Table<DefIndex, Option<LazyValue<ty::ImplTraitInTraitData>>>, is_effects_desugaring: Table<DefIndex, bool>, unused_generic_params: Table<DefIndex, UnusedGenericParams>, @@ -436,6 +436,7 @@ define_tables! { thir_abstract_const: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, ty::Const<'static>>>>, impl_parent: Table<DefIndex, RawDefId>, constness: Table<DefIndex, hir::Constness>, + const_conditions: Table<DefIndex, LazyValue<ty::ConstConditions<'static>>>, defaultness: Table<DefIndex, hir::Defaultness>, // FIXME(eddyb) perhaps compute this on the fly if cheap enough? coerce_unsized_info: Table<DefIndex, LazyValue<ty::adjustment::CoerceUnsizedInfo>>, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 8fd5ff1f369..926691013dd 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -329,7 +329,7 @@ impl<'hir> Map<'hir> { BodyOwnerKind::Static(mutability) => ConstContext::Static(mutability), BodyOwnerKind::Fn if self.tcx.is_constructor(def_id) => return None, - BodyOwnerKind::Fn | BodyOwnerKind::Closure if self.tcx.is_const_fn_raw(def_id) => { + BodyOwnerKind::Fn | BodyOwnerKind::Closure if self.tcx.is_const_fn(def_id) => { ConstContext::ConstFn } BodyOwnerKind::Fn if self.tcx.is_const_default_method(def_id) => ConstContext::ConstFn, diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index cf692b145b8..7f9211043d6 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -172,70 +172,3 @@ impl<'tcx> UnifyValue for ConstVariableValue<'tcx> { } } } - -/// values for the effect inference variable -#[derive(Clone, Copy, Debug)] -pub enum EffectVarValue<'tcx> { - Unknown, - Known(ty::Const<'tcx>), -} - -impl<'tcx> EffectVarValue<'tcx> { - pub fn known(self) -> Option<ty::Const<'tcx>> { - match self { - EffectVarValue::Unknown => None, - EffectVarValue::Known(value) => Some(value), - } - } - - pub fn is_unknown(self) -> bool { - match self { - EffectVarValue::Unknown => true, - EffectVarValue::Known(_) => false, - } - } -} - -impl<'tcx> UnifyValue for EffectVarValue<'tcx> { - type Error = NoError; - - fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> { - match (*value1, *value2) { - (EffectVarValue::Unknown, EffectVarValue::Unknown) => Ok(EffectVarValue::Unknown), - (EffectVarValue::Unknown, EffectVarValue::Known(val)) - | (EffectVarValue::Known(val), EffectVarValue::Unknown) => { - Ok(EffectVarValue::Known(val)) - } - (EffectVarValue::Known(_), EffectVarValue::Known(_)) => { - bug!("equating known inference variables: {value1:?} {value2:?}") - } - } - } -} - -#[derive(PartialEq, Copy, Clone, Debug)] -pub struct EffectVidKey<'tcx> { - pub vid: ty::EffectVid, - pub phantom: PhantomData<ty::Const<'tcx>>, -} - -impl<'tcx> From<ty::EffectVid> for EffectVidKey<'tcx> { - fn from(vid: ty::EffectVid) -> Self { - EffectVidKey { vid, phantom: PhantomData } - } -} - -impl<'tcx> UnifyKey for EffectVidKey<'tcx> { - type Value = EffectVarValue<'tcx>; - #[inline] - fn index(&self) -> u32 { - self.vid.as_u32() - } - #[inline] - fn from_index(i: u32) -> Self { - EffectVidKey::from(ty::EffectVid::from_u32(i)) - } - fn tag() -> &'static str { - "EffectVidKey" - } -} diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index e9b73d25ba2..04a06ba7464 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -56,7 +56,6 @@ #![feature(ptr_alignment_type)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] -#![feature(strict_provenance)] #![feature(trait_upcasting)] #![feature(trusted_len)] #![feature(try_blocks)] diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index ee34ccd889f..c0688aff183 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -422,15 +422,15 @@ impl<'tcx> TyCtxt<'tcx> { debug!("stability: skipping span={:?} since it is internal", span); return EvalResult::Allow; } - if self.features().declared(feature) { + if self.features().enabled(feature) { return EvalResult::Allow; } // If this item was previously part of a now-stabilized feature which is still - // active (i.e. the user hasn't removed the attribute for the stabilized feature + // enabled (i.e. the user hasn't removed the attribute for the stabilized feature // yet) then allow use of this item. if let Some(implied_by) = implied_by - && self.features().declared(implied_by) + && self.features().enabled(implied_by) { return EvalResult::Allow; } @@ -509,7 +509,7 @@ impl<'tcx> TyCtxt<'tcx> { debug!("body stability: skipping span={:?} since it is internal", span); return EvalResult::Allow; } - if self.features().declared(feature) { + if self.features().enabled(feature) { return EvalResult::Allow; } diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 4262460d928..465aa0eee03 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -1,5 +1,6 @@ use std::fmt::{self, Debug, Display, Formatter}; +use either::Either; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_session::RemapFileNameExt; @@ -320,8 +321,14 @@ impl<'tcx> Const<'tcx> { Const::Ty(_, c) => { // We want to consistently have a "clean" value for type system constants (i.e., no // data hidden in the padding), so we always go through a valtree here. - let (ty, val) = c.eval(tcx, param_env, span)?; - Ok(tcx.valtree_to_const_val((ty, val))) + match c.eval_valtree(tcx, param_env, span) { + Ok((ty, val)) => Ok(tcx.valtree_to_const_val((ty, val))), + Err(Either::Left(_bad_ty)) => Err(tcx + .dcx() + .delayed_bug("`mir::Const::eval` called on a non-valtree-compatible type") + .into()), + Err(Either::Right(e)) => Err(e), + } } Const::Unevaluated(uneval, _) => { // FIXME: We might want to have a `try_eval`-like function on `Unevaluated` diff --git a/compiler/rustc_middle/src/mir/graphviz.rs b/compiler/rustc_middle/src/mir/graphviz.rs index a3fe8f9cffa..7bb41193d5c 100644 --- a/compiler/rustc_middle/src/mir/graphviz.rs +++ b/compiler/rustc_middle/src/mir/graphviz.rs @@ -17,7 +17,7 @@ where let mirs = def_ids .iter() .flat_map(|def_id| { - if tcx.is_const_fn_raw(*def_id) { + if tcx.is_const_fn(*def_id) { vec![tcx.optimized_mir(*def_id), tcx.mir_for_ctfe(*def_id)] } else { vec![tcx.instance_mir(ty::InstanceKind::Item(*def_id))] diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 04d035e27ba..ac3baf74ca7 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -18,9 +18,9 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_target::abi::{Align, HasDataLayout, Size}; use super::{ - AllocId, BadBytesAccess, CtfeProvenance, InterpError, InterpResult, Pointer, PointerArithmetic, - Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, UndefinedBehaviorInfo, - UnsupportedOpInfo, interp_ok, read_target_uint, write_target_uint, + AllocId, BadBytesAccess, CtfeProvenance, InterpErrorKind, InterpResult, Pointer, + PointerArithmetic, Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, + UndefinedBehaviorInfo, UnsupportedOpInfo, interp_ok, read_target_uint, write_target_uint, }; use crate::ty; @@ -199,22 +199,22 @@ impl From<ScalarSizeMismatch> for AllocError { } impl AllocError { - pub fn to_interp_error<'tcx>(self, alloc_id: AllocId) -> InterpError<'tcx> { + pub fn to_interp_error<'tcx>(self, alloc_id: AllocId) -> InterpErrorKind<'tcx> { use AllocError::*; match self { ScalarSizeMismatch(s) => { - InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ScalarSizeMismatch(s)) + InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ScalarSizeMismatch(s)) } - ReadPointerAsInt(info) => InterpError::Unsupported( + ReadPointerAsInt(info) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPointerAsInt(info.map(|b| (alloc_id, b))), ), - OverwritePartialPointer(offset) => InterpError::Unsupported( + OverwritePartialPointer(offset) => InterpErrorKind::Unsupported( UnsupportedOpInfo::OverwritePartialPointer(Pointer::new(alloc_id, offset)), ), - ReadPartialPointer(offset) => InterpError::Unsupported( + ReadPartialPointer(offset) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPartialPointer(Pointer::new(alloc_id, offset)), ), - InvalidUninitBytes(info) => InterpError::UndefinedBehavior( + InvalidUninitBytes(info) => InterpErrorKind::UndefinedBehavior( UndefinedBehaviorInfo::InvalidUninitBytes(info.map(|b| (alloc_id, b))), ), } @@ -318,7 +318,7 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> { pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> { Self::uninit_inner(size, align, || { ty::tls::with(|tcx| tcx.dcx().delayed_bug("exhausted memory during interpretation")); - InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) + InterpErrorKind::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) }) .into() } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index fcb87e19435..b520f21ce20 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -113,7 +113,7 @@ pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>); #[derive(Debug)] struct InterpErrorInfoInner<'tcx> { - kind: InterpError<'tcx>, + kind: InterpErrorKind<'tcx>, backtrace: InterpErrorBacktrace, } @@ -154,21 +154,21 @@ impl InterpErrorBacktrace { } impl<'tcx> InterpErrorInfo<'tcx> { - pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) { + pub fn into_parts(self) -> (InterpErrorKind<'tcx>, InterpErrorBacktrace) { let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self; (kind, backtrace) } - pub fn into_kind(self) -> InterpError<'tcx> { + pub fn into_kind(self) -> InterpErrorKind<'tcx> { self.0.kind } - pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self { + pub fn from_parts(kind: InterpErrorKind<'tcx>, backtrace: InterpErrorBacktrace) -> Self { Self(Box::new(InterpErrorInfoInner { kind, backtrace })) } #[inline] - pub fn kind(&self) -> &InterpError<'tcx> { + pub fn kind(&self) -> &InterpErrorKind<'tcx> { &self.0.kind } } @@ -179,13 +179,13 @@ fn print_backtrace(backtrace: &Backtrace) { impl From<ErrorGuaranteed> for InterpErrorInfo<'_> { fn from(err: ErrorGuaranteed) -> Self { - InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err.into())).into() + InterpErrorKind::InvalidProgram(InvalidProgramInfo::AlreadyReported(err.into())).into() } } impl From<ErrorHandled> for InterpErrorInfo<'_> { fn from(err: ErrorHandled) -> Self { - InterpError::InvalidProgram(match err { + InterpErrorKind::InvalidProgram(match err { ErrorHandled::Reported(r, _span) => InvalidProgramInfo::AlreadyReported(r), ErrorHandled::TooGeneric(_span) => InvalidProgramInfo::TooGeneric, }) @@ -193,8 +193,8 @@ impl From<ErrorHandled> for InterpErrorInfo<'_> { } } -impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> { - fn from(kind: InterpError<'tcx>) -> Self { +impl<'tcx> From<InterpErrorKind<'tcx>> for InterpErrorInfo<'tcx> { + fn from(kind: InterpErrorKind<'tcx>) -> Self { InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace: InterpErrorBacktrace::new(), @@ -590,7 +590,7 @@ impl dyn MachineStopType { } #[derive(Debug)] -pub enum InterpError<'tcx> { +pub enum InterpErrorKind<'tcx> { /// The program caused undefined behavior. UndefinedBehavior(UndefinedBehaviorInfo<'tcx>), /// The program did something the interpreter does not support (some of these *might* be UB @@ -606,25 +606,25 @@ pub enum InterpError<'tcx> { MachineStop(Box<dyn MachineStopType>), } -impl InterpError<'_> { +impl InterpErrorKind<'_> { /// Some errors do string formatting even if the error is never printed. /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors, /// so this method lets us detect them and `bug!` on unexpected errors. pub fn formatted_string(&self) -> bool { matches!( self, - InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { .. }) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) + InterpErrorKind::Unsupported(UnsupportedOpInfo::Unsupported(_)) + | InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { .. }) + | InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) ) } } -// Macros for constructing / throwing `InterpError` +// Macros for constructing / throwing `InterpErrorKind` #[macro_export] macro_rules! err_unsup { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::Unsupported( + $crate::mir::interpret::InterpErrorKind::Unsupported( $crate::mir::interpret::UnsupportedOpInfo::$($tt)* ) }; @@ -638,7 +638,7 @@ macro_rules! err_unsup_format { #[macro_export] macro_rules! err_inval { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::InvalidProgram( + $crate::mir::interpret::InterpErrorKind::InvalidProgram( $crate::mir::interpret::InvalidProgramInfo::$($tt)* ) }; @@ -647,7 +647,7 @@ macro_rules! err_inval { #[macro_export] macro_rules! err_ub { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::UndefinedBehavior( + $crate::mir::interpret::InterpErrorKind::UndefinedBehavior( $crate::mir::interpret::UndefinedBehaviorInfo::$($tt)* ) }; @@ -680,7 +680,7 @@ macro_rules! err_ub_custom { #[macro_export] macro_rules! err_exhaust { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::ResourceExhaustion( + $crate::mir::interpret::InterpErrorKind::ResourceExhaustion( $crate::mir::interpret::ResourceExhaustionInfo::$($tt)* ) }; @@ -689,7 +689,7 @@ macro_rules! err_exhaust { #[macro_export] macro_rules! err_machine_stop { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::MachineStop(Box::new($($tt)*)) + $crate::mir::interpret::InterpErrorKind::MachineStop(Box::new($($tt)*)) }; } @@ -792,9 +792,9 @@ impl<'tcx, T> ops::FromResidual for InterpResult_<'tcx, T> { } // Allow `yeet`ing `InterpError` in functions returning `InterpResult_`. -impl<'tcx, T> ops::FromResidual<ops::Yeet<InterpError<'tcx>>> for InterpResult_<'tcx, T> { +impl<'tcx, T> ops::FromResidual<ops::Yeet<InterpErrorKind<'tcx>>> for InterpResult_<'tcx, T> { #[inline] - fn from_residual(ops::Yeet(e): ops::Yeet<InterpError<'tcx>>) -> Self { + fn from_residual(ops::Yeet(e): ops::Yeet<InterpErrorKind<'tcx>>) -> Self { Self::new(Err(e.into())) } } @@ -856,7 +856,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> { } #[inline] - pub fn map_err( + pub fn map_err_info( self, f: impl FnOnce(InterpErrorInfo<'tcx>) -> InterpErrorInfo<'tcx>, ) -> InterpResult<'tcx, T> { @@ -864,8 +864,19 @@ impl<'tcx, T> InterpResult_<'tcx, T> { } #[inline] - pub fn inspect_err(self, f: impl FnOnce(&InterpErrorInfo<'tcx>)) -> InterpResult<'tcx, T> { - InterpResult_::new(self.disarm().inspect_err(f)) + pub fn map_err_kind( + self, + f: impl FnOnce(InterpErrorKind<'tcx>) -> InterpErrorKind<'tcx>, + ) -> InterpResult<'tcx, T> { + InterpResult_::new(self.disarm().map_err(|mut e| { + e.0.kind = f(e.0.kind); + e + })) + } + + #[inline] + pub fn inspect_err_kind(self, f: impl FnOnce(&InterpErrorKind<'tcx>)) -> InterpResult<'tcx, T> { + InterpResult_::new(self.disarm().inspect_err(|e| f(&e.0.kind))) } #[inline] diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 115bcdbc589..790ff3e2fe0 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -36,7 +36,7 @@ pub use self::allocation::{ pub use self::error::{ BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, - InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, InvalidProgramInfo, + InterpErrorInfo, InterpErrorKind, InterpResult, InvalidMetaKind, InvalidProgramInfo, MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind, interp_ok, diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 59bc55269a3..2ecf1d0bcf8 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -116,7 +116,7 @@ impl<'tcx> TyCtxt<'tcx> { // @lcnr believes that successfully evaluating even though there are // used generic parameters is a bug of evaluation, so checking for it // here does feel somewhat sensible. - if !self.features().generic_const_exprs && ct.args.has_non_region_param() { + if !self.features().generic_const_exprs() && ct.args.has_non_region_param() { let def_kind = self.def_kind(instance.def_id()); assert!( matches!( diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 56ca9167d4d..d8d99deeb2c 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -86,11 +86,9 @@ impl<'tcx> MonoItem<'tcx> { } } - pub fn is_generic_fn(&self, tcx: TyCtxt<'tcx>) -> bool { + pub fn is_generic_fn(&self) -> bool { match self { - MonoItem::Fn(instance) => { - instance.args.non_erasable_generics(tcx, instance.def_id()).next().is_some() - } + MonoItem::Fn(instance) => instance.args.non_erasable_generics().next().is_some(), MonoItem::Static(..) | MonoItem::GlobalAsm(..) => false, } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 2a3a070a6e7..e690bf74b6b 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -277,9 +277,9 @@ pub fn create_dump_file<'tcx>( ) })?; } - Ok(fs::File::create_buffered(&file_path).map_err(|e| { + fs::File::create_buffered(&file_path).map_err(|e| { io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}")) - })?) + }) } /////////////////////////////////////////////////////////////////////////// @@ -317,7 +317,7 @@ pub fn write_mir_pretty<'tcx>( }; // For `const fn` we want to render both the optimized MIR and the MIR for ctfe. - if tcx.is_const_fn_raw(def_id) { + if tcx.is_const_fn(def_id) { render_body(w, tcx.optimized_mir(def_id))?; writeln!(w)?; writeln!(w, "// MIR FOR CTFE")?; diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 8d57d0d8654..476e352ed92 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -55,7 +55,7 @@ impl<'tcx> PlaceTy<'tcx> { /// `PlaceElem`, where we can just use the `Ty` that is already /// stored inline on field projection elems. pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> { - self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty, |_, ty| ty) + self.projection_ty_core(tcx, &elem, |_, _, ty| ty, |_, ty| ty) } /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })` @@ -66,7 +66,6 @@ impl<'tcx> PlaceTy<'tcx> { pub fn projection_ty_core<V, T>( self, tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, elem: &ProjectionElem<V, T>, mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>, mut handle_opaque_cast_and_subtype: impl FnMut(&Self, T) -> Ty<'tcx>, @@ -93,7 +92,9 @@ impl<'tcx> PlaceTy<'tcx> { ty::Slice(..) => self.ty, ty::Array(inner, _) if !from_end => Ty::new_array(tcx, *inner, to - from), ty::Array(inner, size) if from_end => { - let size = size.eval_target_usize(tcx, param_env); + let size = size + .try_to_target_usize(tcx) + .expect("expected subslice projection on fixed-size array"); let len = size - from - to; Ty::new_array(tcx, *inner, len) } diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 48bf4ffced0..5f8427bd707 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -370,6 +370,7 @@ tcx_lifetime! { rustc_middle::ty::FnSig, rustc_middle::ty::GenericArg, rustc_middle::ty::GenericPredicates, + rustc_middle::ty::ConstConditions, rustc_middle::ty::inhabitedness::InhabitedPredicate, rustc_middle::ty::Instance, rustc_middle::ty::InstanceKind, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 33128bdbf1e..94bdb913528 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -570,6 +570,7 @@ rustc_queries! { /// either `#[coverage(on)]` or no coverage attribute was found. query coverage_attr_on(key: LocalDefId) -> bool { desc { |tcx| "checking for `#[coverage(..)]` on `{}`", tcx.def_path_str(key) } + feedable } /// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass @@ -682,6 +683,24 @@ rustc_queries! { } } + query const_conditions( + key: DefId + ) -> ty::ConstConditions<'tcx> { + desc { |tcx| "computing the conditions for `{}` to be considered const", + tcx.def_path_str(key) + } + separate_provide_extern + } + + query implied_const_bounds( + key: DefId + ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::PolyTraitRef<'tcx>, Span)]> { + desc { |tcx| "computing the implied `~const` bounds for `{}`", + tcx.def_path_str(key) + } + separate_provide_extern + } + /// To avoid cycles within the predicates of a single item we compute /// per-type-parameter predicates for resolving `T::AssocTy`. query type_param_predicates( @@ -722,12 +741,11 @@ rustc_queries! { desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) } } - /// Returns `true` if this is a const fn, use the `is_const_fn` to know whether your crate - /// actually sees it as const fn (e.g., the const-fn-ness might be unstable and you might - /// not have the feature gate active). + /// Returns `true` if this is a const fn / const impl. /// /// **Do not call this function manually.** It is only meant to cache the base data for the - /// `is_const_fn` function. Consider using `is_const_fn` or `is_const_fn_raw` instead. + /// higher-level functions. Consider using `is_const_fn` or `is_const_trait_impl` instead. + /// Also note that neither of them takes into account feature gates and stability. query constness(key: DefId) -> hir::Constness { desc { |tcx| "checking if item is const: `{}`", tcx.def_path_str(key) } separate_provide_extern @@ -853,12 +871,6 @@ rustc_queries! { separate_provide_extern } - query associated_type_for_effects(def_id: DefId) -> Option<DefId> { - desc { |tcx| "creating associated items for effects in `{}`", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } - separate_provide_extern - } - /// Given an impl trait in trait `opaque_ty_def_id`, create and return the corresponding /// associated item. query associated_type_for_impl_trait_in_trait(opaque_ty_def_id: LocalDefId) -> LocalDefId { diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 66035464fa5..05556ae38a8 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -161,9 +161,7 @@ pub enum SelectionCandidate<'tcx> { /// Implementation of a `Fn`-family trait by one of the anonymous /// types generated for a fn pointer type (e.g., `fn(int) -> int`) - FnPointerCandidate { - fn_host_effect: ty::Const<'tcx>, - }, + FnPointerCandidate, TraitAliasCandidate, @@ -180,9 +178,6 @@ pub enum SelectionCandidate<'tcx> { BuiltinObjectCandidate, BuiltinUnsizeCandidate, - - /// Implementation of `const Destruct`, optionally from a custom `impl const Drop`. - ConstDestructCandidate(Option<DefId>), } /// The result of trait evaluation. The order is important diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index 26dcae001e0..515aabbe2fc 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -61,7 +61,7 @@ pub enum OverlapMode { impl OverlapMode { pub fn get(tcx: TyCtxt<'_>, trait_id: DefId) -> OverlapMode { - let with_negative_coherence = tcx.features().with_negative_coherence; + let with_negative_coherence = tcx.features().with_negative_coherence(); let strict_coherence = tcx.has_attr(trait_id, sym::rustc_strict_coherence); if with_negative_coherence { diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 7e533bc4291..ef9dfdd2f96 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -387,6 +387,17 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for [(ty::Claus } impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> + for [(ty::PolyTraitRef<'tcx>, Span)] +{ + fn decode(decoder: &mut D) -> &'tcx Self { + decoder + .interner() + .arena + .alloc_from_iter((0..decoder.read_usize()).map(|_| Decodable::decode(decoder))) + } +} + +impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<ty::BoundVariableKind> { fn decode(decoder: &mut D) -> &'tcx Self { diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 389d20f315f..5ab85a69ce6 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -109,6 +109,7 @@ impl<'tcx> Const<'tcx> { #[inline] pub fn new_unevaluated(tcx: TyCtxt<'tcx>, uv: ty::UnevaluatedConst<'tcx>) -> Const<'tcx> { + tcx.debug_assert_args_compatible(uv.def, uv.args); Const::new(tcx, ty::ConstKind::Unevaluated(uv)) } @@ -398,133 +399,65 @@ impl<'tcx> Const<'tcx> { } } - /// Returns the evaluated constant - #[inline] - pub fn eval( - self, - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - span: Span, - ) -> Result<(Ty<'tcx>, ValTree<'tcx>), ErrorHandled> { - self.eval_valtree(tcx, param_env, span).map_err(|err| { - match err { - Either::Right(err) => err, - Either::Left(_bad_ty) => { - // This can happen when we run on ill-typed code. - let e = tcx.dcx().span_delayed_bug( - span, - "`ty::Const::eval` called on a non-valtree-compatible type", - ); - e.into() - } - } - }) - } - /// Normalizes the constant to a value or an error if possible. #[inline] - pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { - match self.eval(tcx, param_env, DUMMY_SP) { + pub fn normalize_internal(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { + match self.eval_valtree(tcx, param_env, DUMMY_SP) { Ok((ty, val)) => Self::new_value(tcx, val, ty), - Err(ErrorHandled::Reported(r, _span)) => Self::new_error(tcx, r.into()), - Err(ErrorHandled::TooGeneric(_span)) => self, + Err(Either::Left(_bad_ty)) => { + // This can happen when we run on ill-typed code. + Self::new_error( + tcx, + tcx.dcx() + .delayed_bug("`ty::Const::eval` called on a non-valtree-compatible type"), + ) + } + Err(Either::Right(ErrorHandled::Reported(r, _span))) => Self::new_error(tcx, r.into()), + Err(Either::Right(ErrorHandled::TooGeneric(_span))) => self, } } - #[inline] - pub fn try_eval_scalar( - self, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> Option<(Ty<'tcx>, Scalar)> { - let (ty, val) = self.eval(tcx, param_env, DUMMY_SP).ok()?; - let val = val.try_to_scalar()?; - Some((ty, val)) - } - - #[inline] - /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of - /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it - /// contains const generic parameters or pointers). - pub fn try_eval_scalar_int( - self, - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - ) -> Option<(Ty<'tcx>, ScalarInt)> { - let (ty, scalar) = self.try_eval_scalar(tcx, param_env)?; - let val = scalar.try_to_scalar_int().ok()?; - Some((ty, val)) - } - - #[inline] - /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of - /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it - /// contains const generic parameters or pointers). - pub fn try_eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<u128> { - let (ty, scalar) = self.try_eval_scalar_int(tcx, param_env)?; - let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size; - // if `ty` does not depend on generic parameters, use an empty param_env - Some(scalar.to_bits(size)) - } - - #[inline] - /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type. - pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u128 { - self.try_eval_bits(tcx, param_env) - .unwrap_or_else(|| bug!("failed to evalate {:#?} to bits", self)) - } - - #[inline] - pub fn try_eval_target_usize( - self, - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - ) -> Option<u64> { - let (_, scalar) = self.try_eval_scalar_int(tcx, param_env)?; - Some(scalar.to_target_usize(tcx)) - } - - #[inline] - pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> { - let (_, scalar) = self.try_eval_scalar_int(tcx, param_env)?; - scalar.try_into().ok() - } - - #[inline] - /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`. - pub fn eval_target_usize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 { - self.try_eval_target_usize(tcx, param_env) - .unwrap_or_else(|| bug!("expected usize, got {:#?}", self)) - } - /// Panics if self.kind != ty::ConstKind::Value - pub fn to_valtree(self) -> ty::ValTree<'tcx> { + pub fn to_valtree(self) -> (ty::ValTree<'tcx>, Ty<'tcx>) { match self.kind() { - ty::ConstKind::Value(_, valtree) => valtree, + ty::ConstKind::Value(ty, valtree) => (valtree, ty), _ => bug!("expected ConstKind::Value, got {:?}", self.kind()), } } /// Attempts to convert to a `ValTree` - pub fn try_to_valtree(self) -> Option<ty::ValTree<'tcx>> { + pub fn try_to_valtree(self) -> Option<(ty::ValTree<'tcx>, Ty<'tcx>)> { match self.kind() { - ty::ConstKind::Value(_, valtree) => Some(valtree), + ty::ConstKind::Value(ty, valtree) => Some((valtree, ty)), _ => None, } } #[inline] - pub fn try_to_scalar(self) -> Option<Scalar> { - self.try_to_valtree()?.try_to_scalar() + pub fn try_to_scalar(self) -> Option<(Scalar, Ty<'tcx>)> { + let (valtree, ty) = self.try_to_valtree()?; + Some((valtree.try_to_scalar()?, ty)) } pub fn try_to_bool(self) -> Option<bool> { - self.try_to_valtree()?.try_to_scalar_int()?.try_to_bool().ok() + self.try_to_valtree()?.0.try_to_scalar_int()?.try_to_bool().ok() } #[inline] pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> { - self.try_to_valtree()?.try_to_target_usize(tcx) + self.try_to_valtree()?.0.try_to_target_usize(tcx) + } + + #[inline] + /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of + /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it + /// contains const generic parameters or pointers). + pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<u128> { + let (scalar, ty) = self.try_to_scalar()?; + let scalar = scalar.try_to_scalar_int().ok()?; + let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size; + // if `ty` does not depend on generic parameters, use an empty param_env + Some(scalar.to_bits(size)) } pub fn is_ct_infer(self) -> bool { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index c2a55060490..a6a0a6dc222 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -78,10 +78,10 @@ use crate::traits::solve::{ use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, GenericArg, GenericArgs, - GenericArgsRef, GenericParamDefKind, ImplPolarity, List, ListWithCachedTypeInfo, ParamConst, - ParamTy, Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, - PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, - Visibility, + GenericArgsRef, GenericParamDefKind, HostPolarity, ImplPolarity, List, ListWithCachedTypeInfo, + ParamConst, ParamTy, Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, + PredicateKind, PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, + TyKind, TyVid, Visibility, }; #[allow(rustc::usage_of_ty_tykind)] @@ -279,6 +279,26 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.debug_assert_args_compatible(def_id, args); } + /// Assert that the args from an `ExistentialTraitRef` or `ExistentialProjection` + /// are compatible with the `DefId`. Since we're missing a `Self` type, stick on + /// a dummy self type and forward to `debug_assert_args_compatible`. + fn debug_assert_existential_args_compatible( + self, + def_id: Self::DefId, + args: Self::GenericArgs, + ) { + // FIXME: We could perhaps add a `skip: usize` to `debug_assert_args_compatible` + // to avoid needing to reintern the set of args... + if cfg!(debug_assertions) { + self.debug_assert_args_compatible( + def_id, + self.mk_args_from_iter( + [self.types.trait_object_dummy_self.into()].into_iter().chain(args.iter()), + ), + ); + } + } + fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output where I: Iterator<Item = T>, @@ -363,6 +383,28 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.explicit_implied_predicates_of(def_id).map_bound(|preds| preds.into_iter().copied()) } + fn is_const_impl(self, def_id: DefId) -> bool { + self.is_conditionally_const(def_id) + } + + fn const_conditions( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Binder<'tcx, ty::TraitRef<'tcx>>>> { + ty::EarlyBinder::bind( + self.const_conditions(def_id).instantiate_identity(self).into_iter().map(|(c, _)| c), + ) + } + + fn implied_const_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Binder<'tcx, ty::TraitRef<'tcx>>>> { + ty::EarlyBinder::bind( + self.implied_const_bounds(def_id).iter_identity_copied().map(|(c, _)| c), + ) + } + fn has_target_features(self, def_id: DefId) -> bool { !self.codegen_fn_attrs(def_id).target_features.is_empty() } @@ -626,13 +668,6 @@ bidirectional_lang_item_map! { Destruct, DiscriminantKind, DynMetadata, - EffectsCompat, - EffectsIntersection, - EffectsIntersectionOutput, - EffectsMaybe, - EffectsNoRuntime, - EffectsRuntime, - EffectsTyCompat, Fn, FnMut, FnOnce, @@ -690,15 +725,15 @@ impl<'tcx> rustc_type_ir::inherent::Safety<TyCtxt<'tcx>> for hir::Safety { impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_feature::Features { fn generic_const_exprs(self) -> bool { - self.generic_const_exprs + self.generic_const_exprs() } fn coroutine_clone(self) -> bool { - self.coroutine_clone + self.coroutine_clone() } fn associated_const_equality(self) -> bool { - self.associated_const_equality + self.associated_const_equality() } } @@ -2176,7 +2211,7 @@ macro_rules! nop_slice_lift { nop_slice_lift! {ty::ValTree<'a> => ty::ValTree<'tcx>} TrivialLiftImpls! { - ImplPolarity, PredicatePolarity, Promoted + ImplPolarity, PredicatePolarity, Promoted, HostPolarity, } macro_rules! sty_debug_print { @@ -3085,38 +3120,24 @@ impl<'tcx> TyCtxt<'tcx> { } } - /// Whether the `def_id` counts as const fn in the current crate, considering all active - /// feature gates - pub fn is_const_fn(self, def_id: DefId) -> bool { - if self.is_const_fn_raw(def_id) { - match self.lookup_const_stability(def_id) { - Some(stability) if stability.is_const_unstable() => { - // has a `rustc_const_unstable` attribute, check whether the user enabled the - // corresponding feature gate. - self.features().declared(stability.feature) - } - // functions without const stability are either stable user written - // const fn or the user is using feature gates and we thus don't - // care what they do - _ => true, + /// Whether `def_id` is a stable const fn (i.e., doesn't need any feature gates to be called). + /// + /// When this is `false`, the function may still be callable as a `const fn` due to features + /// being enabled! + pub fn is_stable_const_fn(self, def_id: DefId) -> bool { + self.is_const_fn(def_id) + && match self.lookup_const_stability(def_id) { + None => true, // a fn in a non-staged_api crate + Some(stability) if stability.is_const_stable() => true, + _ => false, } - } else { - false - } } + // FIXME(effects): Please remove this. It's a footgun. /// Whether the trait impl is marked const. This does not consider stability or feature gates. - pub fn is_const_trait_impl_raw(self, def_id: DefId) -> bool { - let Some(local_def_id) = def_id.as_local() else { return false }; - let node = self.hir_node_by_def_id(local_def_id); - - matches!( - node, - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { constness, .. }), - .. - }) if matches!(constness, hir::Constness::Const) - ) + pub fn is_const_trait_impl(self, def_id: DefId) -> bool { + self.def_kind(def_id) == DefKind::Impl { of_trait: true } + && self.constness(def_id) == hir::Constness::Const } pub fn intrinsic(self, def_id: impl IntoQueryParam<DefId> + Copy) -> Option<ty::IntrinsicDef> { diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 4f408ee1574..84f52bfe48f 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -70,7 +70,7 @@ impl<'tcx> Ty<'tcx> { /// ADTs with no type arguments. pub fn is_simple_text(self, tcx: TyCtxt<'tcx>) -> bool { match self.kind() { - Adt(def, args) => args.non_erasable_generics(tcx, def.did()).next().is_none(), + Adt(_, args) => args.non_erasable_generics().next().is_none(), Ref(_, ty, _) => ty.is_simple_text(tcx), _ => self.is_simple_ty(), } @@ -193,7 +193,7 @@ fn suggest_changing_unsized_bound( .enumerate() .filter(|(_, bound)| { if let hir::GenericBound::Trait(poly) = bound - && poly.modifiers == hir::TraitBoundModifier::Maybe + && let hir::BoundPolarity::Maybe(_) = poly.modifiers.polarity && poly.trait_ref.trait_def_id() == def_id { true @@ -592,9 +592,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IsSuggestableVisitor<'tcx> { match c.kind() { ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => {} - // effect variables are always suggestable, because they are not visible - ConstKind::Infer(InferConst::EffectVar(_)) => {} - ConstKind::Infer(..) | ConstKind::Bound(..) | ConstKind::Placeholder(..) diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index b02eff3bfd6..c49824bb418 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -35,9 +35,6 @@ impl<'tcx> TypeError<'tcx> { TypeError::CyclicTy(_) => "cyclic type of infinite size".into(), TypeError::CyclicConst(_) => "encountered a self-referencing constant".into(), TypeError::Mismatch => "types differ".into(), - TypeError::ConstnessMismatch(values) => { - format!("expected {} bound, found {} bound", values.expected, values.found).into() - } TypeError::PolarityMismatch(values) => { format!("expected {} polarity, found {} polarity", values.expected, values.found) .into() diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 92a975c028e..704a197aa49 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -265,6 +265,12 @@ impl FlagComputation { ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => { self.add_args(trait_pred.trait_ref.args); } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(ty::HostEffectPredicate { + trait_ref, + host: _, + })) => { + self.add_args(trait_ref.args); + } ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate( a, b, @@ -354,9 +360,7 @@ impl FlagComputation { self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); match infer { InferConst::Fresh(_) => self.add_flags(TypeFlags::HAS_CT_FRESH), - InferConst::Var(_) | InferConst::EffectVar(_) => { - self.add_flags(TypeFlags::HAS_CT_INFER) - } + InferConst::Var(_) => self.add_flags(TypeFlags::HAS_CT_INFER), } } ty::ConstKind::Bound(debruijn, _) => { diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index daf1362e25c..737f1362b34 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -501,12 +501,8 @@ impl<'tcx> GenericArgs<'tcx> { #[inline] pub fn non_erasable_generics( &'tcx self, - tcx: TyCtxt<'tcx>, - def_id: DefId, ) -> impl DoubleEndedIterator<Item = GenericArgKind<'tcx>> + 'tcx { - let generics = tcx.generics_of(def_id); - self.iter().enumerate().filter_map(|(i, k)| match k.unpack() { - _ if Some(i) == generics.host_effect_index => None, + self.iter().filter_map(|k| match k.unpack() { ty::GenericArgKind::Lifetime(_) => None, generic => Some(generic), }) diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 660686f4aa2..19779740227 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -14,7 +14,7 @@ use crate::ty::{EarlyBinder, GenericArgsRef}; pub enum GenericParamDefKind { Lifetime, Type { has_default: bool, synthetic: bool }, - Const { has_default: bool, is_host_effect: bool, synthetic: bool }, + Const { has_default: bool, synthetic: bool }, } impl GenericParamDefKind { @@ -81,10 +81,6 @@ impl GenericParamDef { } } - pub fn is_host_effect(&self) -> bool { - matches!(self.kind, GenericParamDefKind::Const { is_host_effect: true, .. }) - } - pub fn default_value<'tcx>( &self, tcx: TyCtxt<'tcx>, @@ -133,9 +129,6 @@ pub struct Generics { pub has_self: bool, pub has_late_bound_regions: Option<Span>, - - // The index of the host effect when instantiated. (i.e. might be index to parent args) - pub host_effect_index: Option<usize>, } impl<'tcx> rustc_type_ir::inherent::GenericsOf<TyCtxt<'tcx>> for &'tcx Generics { @@ -216,12 +209,10 @@ impl<'tcx> Generics { pub fn own_requires_monomorphization(&self) -> bool { for param in &self.own_params { match param.kind { - GenericParamDefKind::Type { .. } - | GenericParamDefKind::Const { is_host_effect: false, .. } => { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { return true; } - GenericParamDefKind::Lifetime - | GenericParamDefKind::Const { is_host_effect: true, .. } => {} + GenericParamDefKind::Lifetime => {} } } false @@ -300,8 +291,6 @@ impl<'tcx> Generics { own_params.start = 1; } - let verbose = tcx.sess.verbose_internals(); - // Filter the default arguments. // // This currently uses structural equality instead @@ -316,8 +305,6 @@ impl<'tcx> Generics { param.default_value(tcx).is_some_and(|default| { default.instantiate(tcx, args) == args[param.index as usize] }) - // filter out trailing effect params, if we're not in `-Zverbose-internals`. - || (!verbose && matches!(param.kind, GenericParamDefKind::Const { is_host_effect: true, .. })) }) .count(); @@ -373,7 +360,6 @@ impl<'tcx> Generics { pub struct GenericPredicates<'tcx> { pub parent: Option<DefId>, pub predicates: &'tcx [(Clause<'tcx>, Span)], - pub effects_min_tys: &'tcx ty::List<Ty<'tcx>>, } impl<'tcx> GenericPredicates<'tcx> { @@ -395,7 +381,9 @@ impl<'tcx> GenericPredicates<'tcx> { EarlyBinder::bind(self.predicates).iter_instantiated_copied(tcx, args) } - pub fn instantiate_own_identity(self) -> impl Iterator<Item = (Clause<'tcx>, Span)> { + pub fn instantiate_own_identity( + self, + ) -> impl Iterator<Item = (Clause<'tcx>, Span)> + DoubleEndedIterator + ExactSizeIterator { EarlyBinder::bind(self.predicates).iter_identity_copied() } @@ -433,3 +421,73 @@ impl<'tcx> GenericPredicates<'tcx> { instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s)); } } + +/// `~const` bounds for a given item. This is represented using a struct much like +/// `GenericPredicates`, where you can either choose to only instantiate the "own" +/// bounds or all of the bounds including those from the parent. This distinction +/// is necessary for code like `compare_method_predicate_entailment`. +#[derive(Copy, Clone, Default, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct ConstConditions<'tcx> { + pub parent: Option<DefId>, + pub predicates: &'tcx [(ty::PolyTraitRef<'tcx>, Span)], +} + +impl<'tcx> ConstConditions<'tcx> { + pub fn instantiate( + self, + tcx: TyCtxt<'tcx>, + args: GenericArgsRef<'tcx>, + ) -> Vec<(ty::PolyTraitRef<'tcx>, Span)> { + let mut instantiated = vec![]; + self.instantiate_into(tcx, &mut instantiated, args); + instantiated + } + + pub fn instantiate_own( + self, + tcx: TyCtxt<'tcx>, + args: GenericArgsRef<'tcx>, + ) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, Span)> + DoubleEndedIterator + ExactSizeIterator + { + EarlyBinder::bind(self.predicates).iter_instantiated_copied(tcx, args) + } + + pub fn instantiate_own_identity( + self, + ) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, Span)> + DoubleEndedIterator + ExactSizeIterator + { + EarlyBinder::bind(self.predicates).iter_identity_copied() + } + + #[instrument(level = "debug", skip(self, tcx))] + fn instantiate_into( + self, + tcx: TyCtxt<'tcx>, + instantiated: &mut Vec<(ty::PolyTraitRef<'tcx>, Span)>, + args: GenericArgsRef<'tcx>, + ) { + if let Some(def_id) = self.parent { + tcx.const_conditions(def_id).instantiate_into(tcx, instantiated, args); + } + instantiated.extend( + self.predicates.iter().map(|&(p, s)| (EarlyBinder::bind(p).instantiate(tcx, args), s)), + ); + } + + pub fn instantiate_identity(self, tcx: TyCtxt<'tcx>) -> Vec<(ty::PolyTraitRef<'tcx>, Span)> { + let mut instantiated = vec![]; + self.instantiate_identity_into(tcx, &mut instantiated); + instantiated + } + + fn instantiate_identity_into( + self, + tcx: TyCtxt<'tcx>, + instantiated: &mut Vec<(ty::PolyTraitRef<'tcx>, Span)>, + ) { + if let Some(def_id) = self.parent { + tcx.const_conditions(def_id).instantiate_identity_into(tcx, instantiated); + } + instantiated.extend(self.predicates.iter().copied()); + } +} diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs index 54b8507babf..bf741f63a3d 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs @@ -85,7 +85,7 @@ impl<'tcx> InhabitedPredicate<'tcx> { match self { Self::False => Ok(false), Self::True => Ok(true), - Self::ConstIsZero(const_) => match const_.try_eval_target_usize(tcx, param_env) { + Self::ConstIsZero(const_) => match const_.try_to_target_usize(tcx) { None | Some(0) => Ok(true), Some(1..) => Ok(false), }, diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index b1c5ff50fdc..e237d382900 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -204,7 +204,7 @@ impl<'tcx> Instance<'tcx> { } // If this a non-generic instance, it cannot be a shared monomorphization. - self.args.non_erasable_generics(tcx, self.def_id()).next()?; + self.args.non_erasable_generics().next()?; // compiler_builtins cannot use upstream monomorphizations. if tcx.is_compiler_builtins(LOCAL_CRATE) { @@ -476,7 +476,6 @@ impl<'tcx> Instance<'tcx> { pub fn mono(tcx: TyCtxt<'tcx>, def_id: DefId) -> Instance<'tcx> { let args = GenericArgs::for_item(tcx, def_id, |param, _| match param.kind { ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - ty::GenericParamDefKind::Const { is_host_effect: true, .. } => tcx.consts.true_.into(), ty::GenericParamDefKind::Type { .. } => { bug!("Instance::mono: {:?} has type parameters", def_id) } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 6c12b691c26..7e65df6b27c 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -21,7 +21,9 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{FieldIdx, TyAbiInterface, VariantIdx, call}; use rustc_target::spec::abi::Abi as SpecAbi; -use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, PanicStrategy, Target, WasmCAbi}; +use rustc_target::spec::{ + HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, PanicStrategy, Target, WasmCAbi, X86Abi, +}; use tracing::debug; use {rustc_abi as abi, rustc_hir as hir}; @@ -396,8 +398,8 @@ impl<'tcx> SizeSkeleton<'tcx> { ), } } - ty::Array(inner, len) if tcx.features().transmute_generic_consts => { - let len_eval = len.try_eval_target_usize(tcx, param_env); + ty::Array(inner, len) if tcx.features().transmute_generic_consts() => { + let len_eval = len.try_to_target_usize(tcx); if len_eval == Some(0) { return Ok(SizeSkeleton::Known(Size::from_bytes(0), None)); } @@ -544,6 +546,12 @@ impl<'tcx> HasWasmCAbiOpt for TyCtxt<'tcx> { } } +impl<'tcx> HasX86AbiOpt for TyCtxt<'tcx> { + fn x86_abi_opt(&self) -> X86Abi { + X86Abi { regparm: self.sess.opts.unstable_opts.regparm } + } +} + impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> { #[inline] fn tcx(&self) -> TyCtxt<'tcx> { @@ -595,6 +603,12 @@ impl<'tcx> HasWasmCAbiOpt for LayoutCx<'tcx> { } } +impl<'tcx> HasX86AbiOpt for LayoutCx<'tcx> { + fn x86_abi_opt(&self) -> X86Abi { + self.calc.cx.x86_abi_opt() + } +} + impl<'tcx> HasTyCtxt<'tcx> for LayoutCx<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.calc.cx diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index ed24fcc7eb8..b92fc864b49 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -84,12 +84,13 @@ pub use self::parameterized::ParameterizedOverTcx; pub use self::pattern::{Pattern, PatternKind}; pub use self::predicate::{ AliasTerm, Clause, ClauseKind, CoercePredicate, ExistentialPredicate, - ExistentialPredicateStableCmpExt, ExistentialProjection, ExistentialTraitRef, NormalizesTo, - OutlivesPredicate, PolyCoercePredicate, PolyExistentialPredicate, PolyExistentialProjection, - PolyExistentialTraitRef, PolyProjectionPredicate, PolyRegionOutlivesPredicate, - PolySubtypePredicate, PolyTraitPredicate, PolyTraitRef, PolyTypeOutlivesPredicate, Predicate, - PredicateKind, ProjectionPredicate, RegionOutlivesPredicate, SubtypePredicate, ToPolyTraitRef, - TraitPredicate, TraitRef, TypeOutlivesPredicate, + ExistentialPredicateStableCmpExt, ExistentialProjection, ExistentialTraitRef, + HostEffectPredicate, NormalizesTo, OutlivesPredicate, PolyCoercePredicate, + PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef, + PolyProjectionPredicate, PolyRegionOutlivesPredicate, PolySubtypePredicate, PolyTraitPredicate, + PolyTraitRef, PolyTypeOutlivesPredicate, Predicate, PredicateKind, ProjectionPredicate, + RegionOutlivesPredicate, SubtypePredicate, ToPolyTraitRef, TraitPredicate, TraitRef, + TypeOutlivesPredicate, }; pub use self::region::BoundRegionKind::*; pub use self::region::{ @@ -1994,14 +1995,82 @@ impl<'tcx> TyCtxt<'tcx> { (ident, scope) } + /// Checks whether this is a `const fn`. Returns `false` for non-functions. + /// + /// Even if this returns `true`, constness may still be unstable! #[inline] - pub fn is_const_fn_raw(self, def_id: DefId) -> bool { + pub fn is_const_fn(self, def_id: DefId) -> bool { matches!( self.def_kind(def_id), - DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) | DefKind::Closure + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Closure ) && self.constness(def_id) == hir::Constness::Const } + /// Whether this item is conditionally constant for the purposes of the + /// effects implementation. + /// + /// This roughly corresponds to all const functions and other callable + /// items, along with const impls and traits, and associated types within + /// those impls and traits. + pub fn is_conditionally_const(self, def_id: impl Into<DefId>) -> bool { + let def_id: DefId = def_id.into(); + match self.def_kind(def_id) { + DefKind::Impl { of_trait: true } => { + self.constness(def_id) == hir::Constness::Const + && self.is_const_trait( + self.trait_id_of_impl(def_id) + .expect("expected trait for trait implementation"), + ) + } + DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) => { + self.constness(def_id) == hir::Constness::Const + } + DefKind::Trait => self.is_const_trait(def_id), + DefKind::AssocTy | DefKind::AssocFn => { + let parent_def_id = self.parent(def_id); + match self.def_kind(parent_def_id) { + DefKind::Impl { of_trait: false } => { + self.constness(def_id) == hir::Constness::Const + } + DefKind::Impl { of_trait: true } | DefKind::Trait => { + self.is_conditionally_const(parent_def_id) + } + _ => bug!("unexpected parent item of associated item: {parent_def_id:?}"), + } + } + DefKind::Closure | DefKind::OpaqueTy => { + // Closures and RPITs will eventually have const conditions + // for `~const` bounds. + false + } + DefKind::Ctor(_, CtorKind::Const) + | DefKind::Impl { of_trait: false } + | DefKind::Mod + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::TyParam + | DefKind::Const + | DefKind::ConstParam + | DefKind::Static { .. } + | DefKind::AssocConst + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::InlineConst + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::SyntheticCoroutineBody => false, + } + } + #[inline] pub fn is_const_trait(self, def_id: DefId) -> bool { self.trait_def(def_id).constness == hir::Constness::Const diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 7e1255f606c..43bdce5b576 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -132,6 +132,7 @@ parameterized_over_tcx! { ty::Ty, ty::FnSig, ty::GenericPredicates, + ty::ConstConditions, ty::TraitRef, ty::Const, ty::Predicate, diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index d20cb368278..3ecaa3e22d3 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -19,6 +19,7 @@ pub type ExistentialPredicate<'tcx> = ir::ExistentialPredicate<TyCtxt<'tcx>>; pub type ExistentialTraitRef<'tcx> = ir::ExistentialTraitRef<TyCtxt<'tcx>>; pub type ExistentialProjection<'tcx> = ir::ExistentialProjection<TyCtxt<'tcx>>; pub type TraitPredicate<'tcx> = ir::TraitPredicate<TyCtxt<'tcx>>; +pub type HostEffectPredicate<'tcx> = ir::HostEffectPredicate<TyCtxt<'tcx>>; pub type ClauseKind<'tcx> = ir::ClauseKind<TyCtxt<'tcx>>; pub type PredicateKind<'tcx> = ir::PredicateKind<TyCtxt<'tcx>>; pub type NormalizesTo<'tcx> = ir::NormalizesTo<TyCtxt<'tcx>>; @@ -143,6 +144,7 @@ impl<'tcx> Predicate<'tcx> { | PredicateKind::AliasRelate(..) | PredicateKind::NormalizesTo(..) => false, PredicateKind::Clause(ClauseKind::Trait(_)) + | PredicateKind::Clause(ClauseKind::HostEffect(..)) | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) | PredicateKind::Clause(ClauseKind::TypeOutlives(_)) | PredicateKind::Clause(ClauseKind::Projection(_)) @@ -644,6 +646,7 @@ impl<'tcx> Predicate<'tcx> { match predicate.skip_binder() { PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)), PredicateKind::Clause(ClauseKind::Projection(..)) + | PredicateKind::Clause(ClauseKind::HostEffect(..)) | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) | PredicateKind::NormalizesTo(..) | PredicateKind::AliasRelate(..) @@ -664,6 +667,7 @@ impl<'tcx> Predicate<'tcx> { match predicate.skip_binder() { PredicateKind::Clause(ClauseKind::Projection(t)) => Some(predicate.rebind(t)), PredicateKind::Clause(ClauseKind::Trait(..)) + | PredicateKind::Clause(ClauseKind::HostEffect(..)) | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) | PredicateKind::NormalizesTo(..) | PredicateKind::AliasRelate(..) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 7ada5fd93ba..0248aad53e2 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1208,7 +1208,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } } - if self.tcx().features().return_type_notation + if self.tcx().features().return_type_notation() && let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) = self.tcx().opt_rpitit_info(def_id) && let ty::Alias(_, alias_ty) = @@ -1956,7 +1956,6 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { define_scoped_cx!(self); match constness { - ty::BoundConstness::NotConst => {} ty::BoundConstness::Const => { p!("const "); } @@ -2948,7 +2947,10 @@ impl<'tcx> ty::TraitPredicate<'tcx> { } #[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift)] -pub struct TraitPredPrintWithBoundConstness<'tcx>(ty::TraitPredicate<'tcx>, ty::BoundConstness); +pub struct TraitPredPrintWithBoundConstness<'tcx>( + ty::TraitPredicate<'tcx>, + Option<ty::BoundConstness>, +); impl<'tcx> fmt::Debug for TraitPredPrintWithBoundConstness<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -2966,7 +2968,7 @@ impl<'tcx> ty::PolyTraitPredicate<'tcx> { fn print_with_bound_constness( self, - constness: ty::BoundConstness, + constness: Option<ty::BoundConstness>, ) -> ty::Binder<'tcx, TraitPredPrintWithBoundConstness<'tcx>> { self.map_bound(|trait_pred| TraitPredPrintWithBoundConstness(trait_pred, constness)) } @@ -3073,6 +3075,15 @@ define_print! { p!(print(self.trait_ref.print_trait_sugared())) } + ty::HostEffectPredicate<'tcx> { + let constness = match self.host { + ty::HostPolarity::Const => { "const" } + ty::HostPolarity::Maybe => { "~const" } + }; + p!(print(self.trait_ref.self_ty()), ": {constness} "); + p!(print(self.trait_ref.print_trait_sugared())) + } + ty::TypeAndMut<'tcx> { p!(write("{}", self.mutbl.prefix_str()), print(self.ty)) } @@ -3085,6 +3096,7 @@ define_print! { ty::ClauseKind::RegionOutlives(predicate) => p!(print(predicate)), ty::ClauseKind::TypeOutlives(predicate) => p!(print(predicate)), ty::ClauseKind::Projection(predicate) => p!(print(predicate)), + ty::ClauseKind::HostEffect(predicate) => p!(print(predicate)), ty::ClauseKind::ConstArgHasType(ct, ty) => { p!("the constant `", print(ct), "` has type `", print(ty), "`") }, @@ -3206,7 +3218,9 @@ define_print_and_forward_display! { TraitPredPrintWithBoundConstness<'tcx> { p!(print(self.0.trait_ref.self_ty()), ": "); - p!(pretty_print_bound_constness(self.1)); + if let Some(constness) = self.1 { + p!(pretty_print_bound_constness(constness)); + } if let ty::PredicatePolarity::Negative = self.0.polarity { p!("!"); } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 4c7bcb1bf2e..504a3c8a6d8 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -1,7 +1,5 @@ use std::iter; -use rustc_hir as hir; -use rustc_target::spec::abi; pub use rustc_type_ir::relate::*; use crate::ty::error::{ExpectedFound, TypeError}; @@ -121,26 +119,6 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate< } } -impl<'tcx> Relate<TyCtxt<'tcx>> for hir::Safety { - fn relate<R: TypeRelation<TyCtxt<'tcx>>>( - _relation: &mut R, - a: hir::Safety, - b: hir::Safety, - ) -> RelateResult<'tcx, hir::Safety> { - if a != b { Err(TypeError::SafetyMismatch(ExpectedFound::new(true, a, b))) } else { Ok(a) } - } -} - -impl<'tcx> Relate<TyCtxt<'tcx>> for abi::Abi { - fn relate<R: TypeRelation<TyCtxt<'tcx>>>( - _relation: &mut R, - a: abi::Abi, - b: abi::Abi, - ) -> RelateResult<'tcx, abi::Abi> { - if a == b { Ok(a) } else { Err(TypeError::AbiMismatch(ExpectedFound::new(true, a, b))) } - } -} - impl<'tcx> Relate<TyCtxt<'tcx>> for ty::GenericArgsRef<'tcx> { fn relate<R: TypeRelation<TyCtxt<'tcx>>>( relation: &mut R, diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index cd9ff9b60d8..4872d8c89eb 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -264,8 +264,6 @@ TrivialTypeTraversalImpls! { // interners). TrivialTypeTraversalAndLiftImpls! { ::rustc_hir::def_id::DefId, - ::rustc_hir::Safety, - ::rustc_target::spec::abi::Abi, crate::ty::ClosureKind, crate::ty::ParamConst, crate::ty::ParamTy, @@ -276,6 +274,11 @@ TrivialTypeTraversalAndLiftImpls! { rustc_target::abi::Size, } +TrivialLiftImpls! { + ::rustc_hir::Safety, + ::rustc_target::spec::abi::Abi, +} + /////////////////////////////////////////////////////////////////////////// // Lift implementations diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 3f00458d195..d8362ccc0a9 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -750,7 +750,7 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn new_diverging_default(tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - if tcx.features().never_type_fallback { tcx.types.never } else { tcx.types.unit } + if tcx.features().never_type_fallback() { tcx.types.never } else { tcx.types.unit } } // lang and diagnostic tys @@ -1117,7 +1117,12 @@ impl<'tcx> Ty<'tcx> { // The way we evaluate the `N` in `[T; N]` here only works since we use // `simd_size_and_type` post-monomorphization. It will probably start to ICE // if we use it in generic code. See the `simd-array-trait` ui test. - (f0_len.eval_target_usize(tcx, ParamEnv::empty()), *f0_elem_ty) + ( + f0_len + .try_to_target_usize(tcx) + .expect("expected SIMD field to have definite array size"), + *f0_elem_ty, + ) } #[inline] diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 0a917120b3b..7fd7e463acf 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -17,10 +17,10 @@ use rustc_span::sym; use rustc_target::abi::{Float, Integer, IntegerType, Size}; use rustc_target::spec::abi::Abi; use smallvec::{SmallVec, smallvec}; -use tracing::{debug, instrument, trace}; +use tracing::{debug, instrument}; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use crate::query::{IntoQueryParam, Providers}; +use crate::query::Providers; use crate::ty::layout::{FloatExt, IntegerExt}; use crate::ty::{ self, Asyncness, FallibleTypeFolder, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeFoldable, @@ -865,66 +865,6 @@ impl<'tcx> TyCtxt<'tcx> { || self.extern_crate(key).is_some_and(|e| e.is_direct()) } - /// Whether the item has a host effect param. This is different from `TyCtxt::is_const`, - /// because the item must also be "maybe const", and the crate where the item is - /// defined must also have the effects feature enabled. - pub fn has_host_param(self, def_id: impl IntoQueryParam<DefId>) -> bool { - self.generics_of(def_id).host_effect_index.is_some() - } - - pub fn expected_host_effect_param_for_body(self, def_id: impl Into<DefId>) -> ty::Const<'tcx> { - let def_id = def_id.into(); - // FIXME(effects): This is suspicious and should probably not be done, - // especially now that we enforce host effects and then properly handle - // effect vars during fallback. - let mut host_always_on = - !self.features().effects || self.sess.opts.unstable_opts.unleash_the_miri_inside_of_you; - - // Compute the constness required by the context. - let const_context = self.hir().body_const_context(def_id); - - let kind = self.def_kind(def_id); - debug_assert_ne!(kind, DefKind::ConstParam); - - if self.has_attr(def_id, sym::rustc_do_not_const_check) { - trace!("do not const check this context"); - host_always_on = true; - } - - match const_context { - _ if host_always_on => self.consts.true_, - Some(hir::ConstContext::Static(_) | hir::ConstContext::Const { .. }) => { - self.consts.false_ - } - Some(hir::ConstContext::ConstFn) => { - let host_idx = self - .generics_of(def_id) - .host_effect_index - .expect("ConstContext::Maybe must have host effect param"); - ty::GenericArgs::identity_for_item(self, def_id).const_at(host_idx) - } - None => self.consts.true_, - } - } - - /// Constructs generic args for an item, optionally appending a const effect param type - pub fn with_opt_host_effect_param( - self, - caller_def_id: LocalDefId, - callee_def_id: DefId, - args: impl IntoIterator<Item: Into<ty::GenericArg<'tcx>>>, - ) -> ty::GenericArgsRef<'tcx> { - let generics = self.generics_of(callee_def_id); - assert_eq!(generics.parent, None); - - let opt_const_param = generics - .host_effect_index - .is_some() - .then(|| ty::GenericArg::from(self.expected_host_effect_param_for_body(caller_def_id))); - - self.mk_args_from_iter(args.into_iter().map(|arg| arg.into()).chain(opt_const_param)) - } - /// Expand any [weak alias types][weak] contained within the given `value`. /// /// This should be used over other normalization routines in situations where @@ -1844,8 +1784,8 @@ pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { /// cause an ICE that we otherwise may want to prevent. pub fn intrinsic_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::IntrinsicDef> { if (matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic) - && tcx.features().intrinsics) - || (tcx.has_attr(def_id, sym::rustc_intrinsic) && tcx.features().rustc_attrs) + && tcx.features().intrinsics()) + || (tcx.has_attr(def_id, sym::rustc_intrinsic) && tcx.features().rustc_attrs()) { Some(ty::IntrinsicDef { name: tcx.item_name(def_id.into()), diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/build/expr/as_operand.rs index 1e67e759aa2..112eac32264 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs @@ -163,7 +163,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let tcx = this.tcx; - if tcx.features().unsized_fn_params { + if tcx.features().unsized_fn_params() { let ty = expr.ty; let param_env = this.param_env; diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 020c202f965..37cedd8cf5c 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -149,7 +149,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let ty::Adt(def, _) = ty.kind() && tcx.is_lang_item(def.did(), LangItem::String) { - if !tcx.features().string_deref_patterns { + if !tcx.features().string_deref_patterns() { span_bug!( test.span, "matching on `String` went through without enabling string_deref_patterns" @@ -454,12 +454,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, Some(source_info.span)); - let method = trait_method( - self.tcx, - eq_def_id, - sym::eq, - self.tcx.with_opt_host_effect_param(self.def_id, eq_def_id, [compare_ty, compare_ty]), - ); + let method = trait_method(self.tcx, eq_def_id, sym::eq, [compare_ty, compare_ty]); let bool_ty = self.tcx.types.bool; let eq_result = self.temp(bool_ty, source_info.span); diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index dfc82f705a8..a7e56b8f589 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -1048,8 +1048,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // | +------------|outer_scope cache|--+ | // +------------------------------|middle_scope cache|------+ // - // Now, a new, inner-most scope is added along with a new drop into - // both inner-most and outer-most scopes: + // Now, a new, innermost scope is added along with a new drop into + // both innermost and outermost scopes: // // +------------------------------------------------------------+ // | +----------------------------------+ | @@ -1061,11 +1061,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // +----=----------------|invalid middle_scope cache|-----------+ // // If, when adding `drop(new)` we do not invalidate the cached blocks for both - // outer_scope and middle_scope, then, when building drops for the inner (right-most) + // outer_scope and middle_scope, then, when building drops for the inner (rightmost) // scope, the old, cached blocks, without `drop(new)` will get used, producing the // wrong results. // - // Note that this code iterates scopes from the inner-most to the outer-most, + // Note that this code iterates scopes from the innermost to the outermost, // invalidating caches of each scope visited. This way bare minimum of the // caches gets invalidated. i.e., if a new drop is added into the middle scope, the // cache of outer scope stays intact. diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 8512763a595..f3e6301d9d1 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -509,20 +509,12 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } ExprKind::RawBorrow { arg, .. } => { if let ExprKind::Scope { value: arg, .. } = self.thir[arg].kind - // THIR desugars UNSAFE_STATIC into *UNSAFE_STATIC_REF, where - // UNSAFE_STATIC_REF holds the addr of the UNSAFE_STATIC, so: take two steps && let ExprKind::Deref { arg } = self.thir[arg].kind - // FIXME(workingjubiee): we lack a clear reason to reject ThreadLocalRef here, - // but we also have no conclusive reason to allow it either! - && let ExprKind::StaticRef { .. } = self.thir[arg].kind { - // A raw ref to a place expr, even an "unsafe static", is okay! - // We short-circuit to not recursively traverse this expression. + // Taking a raw ref to a deref place expr is always safe. + // Make sure the expression we're deref'ing is safe, though. + visit::walk_expr(self, &self.thir[arg]); return; - // note: const_mut_refs enables this code, and it currently remains unsafe: - // static mut BYTE: u8 = 0; - // static mut BYTE_PTR: *mut u8 = unsafe { addr_of_mut!(BYTE) }; - // static mut DEREF_BYTE_PTR: *mut u8 = unsafe { addr_of_mut!(*BYTE_PTR) }; } } ExprKind::Deref { arg } => { diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 5995d60e7e0..e2823456477 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -281,7 +281,14 @@ impl<'tcx> Cx<'tcx> { .unwrap_or_else(|e| panic!("could not compute layout for {param_env_ty:?}: {e:?}")) .size; - let lit = ScalarInt::try_from_uint(discr_offset as u128, size).unwrap(); + let (lit, overflowing) = ScalarInt::truncate_from_uint(discr_offset as u128, size); + if overflowing { + // An erroneous enum with too many variants for its repr will emit E0081 and E0370 + self.tcx.dcx().span_delayed_bug( + source.span, + "overflowing enum wasn't rejected by hir analysis", + ); + } let kind = ExprKind::NonHirLiteral { lit, user_ty: None }; let offset = self.thir.exprs.push(Expr { temp_lifetime, ty: discr_ty, span, kind }); @@ -777,7 +784,7 @@ impl<'tcx> Cx<'tcx> { if_then_scope: region::Scope { id: then.hir_id.local_id, data: { - if expr.span.at_least_rust_2024() && tcx.features().if_let_rescope { + if expr.span.at_least_rust_2024() && tcx.features().if_let_rescope() { region::ScopeData::IfThenRescope } else { region::ScopeData::IfThen diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index ae77bce6bb1..8498df59ce6 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1126,7 +1126,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( .map(|witness| cx.print_witness_pat(witness)) .collect::<Vec<String>>() .join(" | "); - if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns { + if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns() { // Arms with a never pattern don't take a body. pattern } else { diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 2c3611afca9..0dfa9168f7c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -43,7 +43,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } struct ConstToPat<'tcx> { - id: hir::HirId, span: Span, param_env: ty::ParamEnv<'tcx>, @@ -62,7 +61,6 @@ impl<'tcx> ConstToPat<'tcx> { ) -> Self { trace!(?pat_ctxt.typeck_results.hir_owner); ConstToPat { - id, span, infcx, param_env: pat_ctxt.param_env, @@ -149,15 +147,7 @@ impl<'tcx> ConstToPat<'tcx> { tcx, ObligationCause::dummy(), self.param_env, - ty::TraitRef::new_from_args( - tcx, - partial_eq_trait_id, - tcx.with_opt_host_effect_param( - tcx.hir().enclosing_body_owner(self.id), - partial_eq_trait_id, - [ty, ty], - ), - ), + ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]), ); // This *could* accept a type that isn't actually `PartialEq`, because region bounds get diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index a3b117a3f19..7f2a07e2f5e 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -863,7 +863,7 @@ where ty::Adt(def, args) => self.open_drop_for_adt(*def, args), ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind), ty::Array(ety, size) => { - let size = size.try_eval_target_usize(self.tcx(), self.elaborator.param_env()); + let size = size.try_to_target_usize(self.tcx()); self.open_drop_for_array(*ety, size) } ty::Slice(ety) => self.drop_loop_pair(*ety), diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 162245cb950..fd8e403ebc2 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -18,18 +18,12 @@ struct MoveDataBuilder<'a, 'tcx, F> { body: &'a Body<'tcx>, loc: Location, tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, data: MoveData<'tcx>, filter: F, } impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { - fn new( - body: &'a Body<'tcx>, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - filter: F, - ) -> Self { + fn new(body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, filter: F) -> Self { let mut move_paths = IndexVec::new(); let mut path_map = IndexVec::new(); let mut init_path_map = IndexVec::new(); @@ -59,7 +53,6 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { body, loc: Location::START, tcx, - param_env, data: MoveData { moves: IndexVec::new(), loc_map: LocationMap::new(body), @@ -308,10 +301,9 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { pub(super) fn gather_moves<'tcx>( body: &Body<'tcx>, tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, filter: impl Fn(Ty<'tcx>) -> bool, ) -> MoveData<'tcx> { - let mut builder = MoveDataBuilder::new(body, tcx, param_env, filter); + let mut builder = MoveDataBuilder::new(body, tcx, filter); builder.gather_args(); @@ -550,7 +542,9 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { }; let base_ty = base_place.ty(self.body, self.tcx).ty; let len: u64 = match base_ty.kind() { - ty::Array(_, size) => size.eval_target_usize(self.tcx, self.param_env), + ty::Array(_, size) => size + .try_to_target_usize(self.tcx) + .expect("expected subslice projection on fixed-size array"), _ => bug!("from_end: false slice pattern of non-array type"), }; for offset in from..to { diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index bc1177976b5..926bd187431 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -4,7 +4,7 @@ use std::ops::{Index, IndexMut}; use rustc_data_structures::fx::FxHashMap; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::*; -use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_span::Span; use smallvec::SmallVec; @@ -352,10 +352,9 @@ impl<'tcx> MoveData<'tcx> { pub fn gather_moves( body: &Body<'tcx>, tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, filter: impl Fn(Ty<'tcx>) -> bool, ) -> MoveData<'tcx> { - builder::gather_moves(body, tcx, param_env, filter) + builder::gather_moves(body, tcx, filter) } /// For the move path `mpi`, returns the root local variable that starts the path. diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 75732b19cd0..5727517bd61 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -40,8 +40,7 @@ pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); } - let param_env = tcx.param_env(def_id); - let move_data = MoveData::gather_moves(body, tcx, param_env, |_| true); + let move_data = MoveData::gather_moves(body, tcx, |_| true); if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() { let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index faee40faa3f..d0f62bd82d1 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -1177,7 +1177,7 @@ struct PlaceInfo<'tcx> { /// The projection used to go from parent to this node (only None for root). proj_elem: Option<TrackElem>, - /// The left-most child. + /// The leftmost child. first_child: Option<PlaceIndex>, /// Index of the sibling to the right of this node. diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 8f032728f6b..cd291058977 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1497,7 +1497,7 @@ fn check_field_tys_sized<'tcx>( ) { // No need to check if unsized_locals/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_locals() && !tcx.features().unsized_fn_params() { return; } @@ -1957,7 +1957,8 @@ fn check_must_not_suspend_ty<'tcx>( let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix); check_must_not_suspend_ty(tcx, ty, hir_id, param_env, SuspendCheckData { descr_pre, - plural_len: len.try_eval_target_usize(tcx, param_env).unwrap_or(0) as usize + 1, + // FIXME(must_not_suspend): This is wrong. We should handle printing unevaluated consts. + plural_len: len.try_to_target_usize(tcx).unwrap_or(0) as usize + 1, ..data }) } diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index cc4b7689d40..2c622b1927e 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -223,6 +223,7 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( // Inherited from the by-ref coroutine. body_def.codegen_fn_attrs(tcx.codegen_fn_attrs(coroutine_def_id).clone()); + body_def.coverage_attr_on(tcx.coverage_attr_on(coroutine_def_id)); body_def.constness(tcx.constness(coroutine_def_id)); body_def.coroutine_kind(tcx.coroutine_kind(coroutine_def_id)); body_def.def_ident_span(tcx.def_ident_span(coroutine_def_id)); diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index 94088156756..9a533ea024d 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -5,7 +5,6 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph::DirectedGraph; use rustc_index::IndexVec; use rustc_index::bit_set::BitSet; -use rustc_middle::bug; use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op}; use tracing::{debug, debug_span, instrument}; @@ -58,13 +57,13 @@ pub(super) struct CoverageCounters { counter_increment_sites: IndexVec<CounterId, CounterIncrementSite>, /// Coverage counters/expressions that are associated with individual BCBs. - bcb_counters: IndexVec<BasicCoverageBlock, Option<BcbCounter>>, + node_counters: IndexVec<BasicCoverageBlock, Option<BcbCounter>>, /// Coverage counters/expressions that are associated with the control-flow /// edge between two BCBs. /// /// We currently don't iterate over this map, but if we do in the future, /// switch it back to `FxIndexMap` to avoid query stability hazards. - bcb_edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>, + edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>, /// Table of expression data, associating each expression ID with its /// corresponding operator (+ or -) and its LHS/RHS operands. @@ -78,20 +77,20 @@ impl CoverageCounters { /// Ensures that each BCB node needing a counter has one, by creating physical /// counters or counter expressions for nodes and edges as required. pub(super) fn make_bcb_counters( - basic_coverage_blocks: &CoverageGraph, + graph: &CoverageGraph, bcb_needs_counter: &BitSet<BasicCoverageBlock>, ) -> Self { - let mut counters = MakeBcbCounters::new(basic_coverage_blocks, bcb_needs_counter); - counters.make_bcb_counters(); + let mut builder = CountersBuilder::new(graph, bcb_needs_counter); + builder.make_bcb_counters(); - counters.coverage_counters + builder.counters } fn with_num_bcbs(num_bcbs: usize) -> Self { Self { counter_increment_sites: IndexVec::new(), - bcb_counters: IndexVec::from_elem_n(None, num_bcbs), - bcb_edge_counters: FxHashMap::default(), + node_counters: IndexVec::from_elem_n(None, num_bcbs), + edge_counters: FxHashMap::default(), expressions: IndexVec::new(), expressions_memo: FxHashMap::default(), } @@ -104,24 +103,18 @@ impl CoverageCounters { BcbCounter::Counter { id } } - /// Creates a new physical counter attached a BCB node. - /// The node must not already have a counter. + /// Creates a new physical counter for a BCB node. fn make_phys_node_counter(&mut self, bcb: BasicCoverageBlock) -> BcbCounter { - let counter = self.make_counter_inner(CounterIncrementSite::Node { bcb }); - debug!(?bcb, ?counter, "node gets a physical counter"); - self.set_bcb_counter(bcb, counter) + self.make_counter_inner(CounterIncrementSite::Node { bcb }) } - /// Creates a new physical counter attached to a BCB edge. - /// The edge must not already have a counter. + /// Creates a new physical counter for a BCB edge. fn make_phys_edge_counter( &mut self, from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, ) -> BcbCounter { - let counter = self.make_counter_inner(CounterIncrementSite::Edge { from_bcb, to_bcb }); - debug!(?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter"); - self.set_bcb_edge_counter(from_bcb, to_bcb, counter) + self.make_counter_inner(CounterIncrementSite::Edge { from_bcb, to_bcb }) } fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter { @@ -193,35 +186,31 @@ impl CoverageCounters { self.counter_increment_sites.len() } - fn set_bcb_counter(&mut self, bcb: BasicCoverageBlock, counter_kind: BcbCounter) -> BcbCounter { - if let Some(replaced) = self.bcb_counters[bcb].replace(counter_kind) { - bug!( - "attempt to set a BasicCoverageBlock coverage counter more than once; \ - {bcb:?} already had counter {replaced:?}", - ); - } else { - counter_kind - } + fn set_node_counter(&mut self, bcb: BasicCoverageBlock, counter: BcbCounter) -> BcbCounter { + let existing = self.node_counters[bcb].replace(counter); + assert!( + existing.is_none(), + "node {bcb:?} already has a counter: {existing:?} => {counter:?}" + ); + counter } - fn set_bcb_edge_counter( + fn set_edge_counter( &mut self, from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, - counter_kind: BcbCounter, + counter: BcbCounter, ) -> BcbCounter { - if let Some(replaced) = self.bcb_edge_counters.insert((from_bcb, to_bcb), counter_kind) { - bug!( - "attempt to set an edge counter more than once; from_bcb: \ - {from_bcb:?} already had counter {replaced:?}", - ); - } else { - counter_kind - } + let existing = self.edge_counters.insert((from_bcb, to_bcb), counter); + assert!( + existing.is_none(), + "edge ({from_bcb:?} -> {to_bcb:?}) already has a counter: {existing:?} => {counter:?}" + ); + counter } pub(super) fn term_for_bcb(&self, bcb: BasicCoverageBlock) -> Option<CovTerm> { - self.bcb_counters[bcb].map(|counter| counter.as_term()) + self.node_counters[bcb].map(|counter| counter.as_term()) } /// Returns an iterator over all the nodes/edges in the coverage graph that @@ -238,7 +227,7 @@ impl CoverageCounters { pub(super) fn bcb_nodes_with_coverage_expressions( &self, ) -> impl Iterator<Item = (BasicCoverageBlock, ExpressionId)> + Captures<'_> { - self.bcb_counters.iter_enumerated().filter_map(|(bcb, &counter_kind)| match counter_kind { + self.node_counters.iter_enumerated().filter_map(|(bcb, &counter)| match counter { // Yield the BCB along with its associated expression ID. Some(BcbCounter::Expression { id }) => Some((bcb, id)), // This BCB is associated with a counter or nothing, so skip it. @@ -265,22 +254,20 @@ impl CoverageCounters { } } -/// Helper struct that allows counter creation to inspect the BCB graph. -struct MakeBcbCounters<'a> { - coverage_counters: CoverageCounters, - basic_coverage_blocks: &'a CoverageGraph, +/// Helper struct that allows counter creation to inspect the BCB graph, and +/// the set of nodes that need counters. +struct CountersBuilder<'a> { + counters: CoverageCounters, + graph: &'a CoverageGraph, bcb_needs_counter: &'a BitSet<BasicCoverageBlock>, } -impl<'a> MakeBcbCounters<'a> { - fn new( - basic_coverage_blocks: &'a CoverageGraph, - bcb_needs_counter: &'a BitSet<BasicCoverageBlock>, - ) -> Self { - assert_eq!(basic_coverage_blocks.num_nodes(), bcb_needs_counter.domain_size()); +impl<'a> CountersBuilder<'a> { + fn new(graph: &'a CoverageGraph, bcb_needs_counter: &'a BitSet<BasicCoverageBlock>) -> Self { + assert_eq!(graph.num_nodes(), bcb_needs_counter.domain_size()); Self { - coverage_counters: CoverageCounters::with_num_bcbs(basic_coverage_blocks.num_nodes()), - basic_coverage_blocks, + counters: CoverageCounters::with_num_bcbs(graph.num_nodes()), + graph, bcb_needs_counter, } } @@ -295,7 +282,7 @@ impl<'a> MakeBcbCounters<'a> { // nodes within the loop are visited before visiting any nodes outside // the loop. It also keeps track of which loop(s) the traversal is // currently inside. - let mut traversal = TraverseCoverageGraphWithLoops::new(self.basic_coverage_blocks); + let mut traversal = TraverseCoverageGraphWithLoops::new(self.graph); while let Some(bcb) = traversal.next() { let _span = debug_span!("traversal", ?bcb).entered(); if self.bcb_needs_counter.contains(bcb) { @@ -322,25 +309,35 @@ impl<'a> MakeBcbCounters<'a> { // We might also use that counter to compute one of the out-edge counters. let node_counter = self.get_or_make_node_counter(from_bcb); - let successors = self.basic_coverage_blocks.successors[from_bcb].as_slice(); + let successors = self.graph.successors[from_bcb].as_slice(); // If this node's out-edges won't sum to the node's counter, // then there's no reason to create edge counters here. - if !self.basic_coverage_blocks[from_bcb].is_out_summable { + if !self.graph[from_bcb].is_out_summable { return; } - // Determine the set of out-edges that don't yet have edge counters. - let candidate_successors = self.basic_coverage_blocks.successors[from_bcb] + // When choosing which out-edge should be given a counter expression, ignore edges that + // already have counters, or could use the existing counter of their target node. + let out_edge_has_counter = |to_bcb| { + if self.counters.edge_counters.contains_key(&(from_bcb, to_bcb)) { + return true; + } + self.graph.sole_predecessor(to_bcb) == Some(from_bcb) + && self.counters.node_counters[to_bcb].is_some() + }; + + // Determine the set of out-edges that could benefit from being given an expression. + let candidate_successors = self.graph.successors[from_bcb] .iter() .copied() - .filter(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb)) + .filter(|&to_bcb| !out_edge_has_counter(to_bcb)) .collect::<Vec<_>>(); debug!(?candidate_successors); // If there are out-edges without counters, choose one to be given an expression // (computed from this node and the other out-edges) instead of a physical counter. - let Some(expression_to_bcb) = + let Some(target_bcb) = self.choose_out_edge_for_expression(traversal, &candidate_successors) else { return; @@ -353,43 +350,44 @@ impl<'a> MakeBcbCounters<'a> { .iter() .copied() // Skip the chosen edge, since we'll calculate its count from this sum. - .filter(|&to_bcb| to_bcb != expression_to_bcb) + .filter(|&edge_target_bcb| edge_target_bcb != target_bcb) .map(|to_bcb| self.get_or_make_edge_counter(from_bcb, to_bcb)) .collect::<Vec<_>>(); - let Some(sum_of_all_other_out_edges) = - self.coverage_counters.make_sum(&other_out_edge_counters) + let Some(sum_of_all_other_out_edges) = self.counters.make_sum(&other_out_edge_counters) else { return; }; // Now create an expression for the chosen edge, by taking the counter // for its source node and subtracting the sum of its sibling out-edges. - let expression = self.coverage_counters.make_expression( - node_counter, - Op::Subtract, - sum_of_all_other_out_edges, - ); + let expression = + self.counters.make_expression(node_counter, Op::Subtract, sum_of_all_other_out_edges); - debug!("{expression_to_bcb:?} gets an expression: {expression:?}"); - if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(expression_to_bcb) { - // This edge normally wouldn't get its own counter, so attach the expression - // to its target node instead, so that `edge_has_no_counter` can see it. - assert_eq!(sole_pred, from_bcb); - self.coverage_counters.set_bcb_counter(expression_to_bcb, expression); - } else { - self.coverage_counters.set_bcb_edge_counter(from_bcb, expression_to_bcb, expression); - } + debug!("{target_bcb:?} gets an expression: {expression:?}"); + self.counters.set_edge_counter(from_bcb, target_bcb, expression); } #[instrument(level = "debug", skip(self))] fn get_or_make_node_counter(&mut self, bcb: BasicCoverageBlock) -> BcbCounter { // If the BCB already has a counter, return it. - if let Some(counter_kind) = self.coverage_counters.bcb_counters[bcb] { - debug!("{bcb:?} already has a counter: {counter_kind:?}"); - return counter_kind; + if let Some(counter) = self.counters.node_counters[bcb] { + debug!("{bcb:?} already has a counter: {counter:?}"); + return counter; + } + + let counter = self.make_node_counter_inner(bcb); + self.counters.set_node_counter(bcb, counter) + } + + fn make_node_counter_inner(&mut self, bcb: BasicCoverageBlock) -> BcbCounter { + // If the node's sole in-edge already has a counter, use that. + if let Some(sole_pred) = self.graph.sole_predecessor(bcb) + && let Some(&edge_counter) = self.counters.edge_counters.get(&(sole_pred, bcb)) + { + return edge_counter; } - let predecessors = self.basic_coverage_blocks.predecessors[bcb].as_slice(); + let predecessors = self.graph.predecessors[bcb].as_slice(); // Handle cases where we can't compute a node's count from its in-edges: // - START_BCB has no in-edges, so taking the sum would panic (or be wrong). @@ -398,7 +396,9 @@ impl<'a> MakeBcbCounters<'a> { // leading to infinite recursion. if predecessors.len() <= 1 || predecessors.contains(&bcb) { debug!(?bcb, ?predecessors, "node has <=1 predecessors or is its own predecessor"); - return self.coverage_counters.make_phys_node_counter(bcb); + let counter = self.counters.make_phys_node_counter(bcb); + debug!(?bcb, ?counter, "node gets a physical counter"); + return counter; } // A BCB with multiple incoming edges can compute its count by ensuring that counters @@ -408,13 +408,11 @@ impl<'a> MakeBcbCounters<'a> { .copied() .map(|from_bcb| self.get_or_make_edge_counter(from_bcb, bcb)) .collect::<Vec<_>>(); - let sum_of_in_edges: BcbCounter = self - .coverage_counters - .make_sum(&in_edge_counters) - .expect("there must be at least one in-edge"); + let sum_of_in_edges: BcbCounter = + self.counters.make_sum(&in_edge_counters).expect("there must be at least one in-edge"); debug!("{bcb:?} gets a new counter (sum of predecessor counters): {sum_of_in_edges:?}"); - self.coverage_counters.set_bcb_counter(bcb, sum_of_in_edges) + sum_of_in_edges } #[instrument(level = "debug", skip(self))] @@ -423,9 +421,24 @@ impl<'a> MakeBcbCounters<'a> { from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, ) -> BcbCounter { + // If the edge already has a counter, return it. + if let Some(&counter) = self.counters.edge_counters.get(&(from_bcb, to_bcb)) { + debug!("Edge {from_bcb:?}->{to_bcb:?} already has a counter: {counter:?}"); + return counter; + } + + let counter = self.make_edge_counter_inner(from_bcb, to_bcb); + self.counters.set_edge_counter(from_bcb, to_bcb, counter) + } + + fn make_edge_counter_inner( + &mut self, + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, + ) -> BcbCounter { // If the target node has exactly one in-edge (i.e. this one), then just // use the node's counter, since it will have the same value. - if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(to_bcb) { + if let Some(sole_pred) = self.graph.sole_predecessor(to_bcb) { assert_eq!(sole_pred, from_bcb); // This call must take care not to invoke `get_or_make_edge` for // this edge, since that would result in infinite recursion! @@ -434,21 +447,15 @@ impl<'a> MakeBcbCounters<'a> { // If the source node has exactly one out-edge (i.e. this one) and would have // the same execution count as that edge, then just use the node's counter. - if let Some(simple_succ) = self.basic_coverage_blocks.simple_successor(from_bcb) { + if let Some(simple_succ) = self.graph.simple_successor(from_bcb) { assert_eq!(simple_succ, to_bcb); return self.get_or_make_node_counter(from_bcb); } - // If the edge already has a counter, return it. - if let Some(&counter_kind) = - self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb)) - { - debug!("Edge {from_bcb:?}->{to_bcb:?} already has a counter: {counter_kind:?}"); - return counter_kind; - } - // Make a new counter to count this edge. - self.coverage_counters.make_phys_edge_counter(from_bcb, to_bcb) + let counter = self.counters.make_phys_edge_counter(from_bcb, to_bcb); + debug!(?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter"); + counter } /// Given a set of candidate out-edges (represented by their successor node), @@ -493,9 +500,9 @@ impl<'a> MakeBcbCounters<'a> { for &target_bcb in candidate_successors { // An edge is a reloop edge if its target dominates any BCB that has // an edge back to the loop header. (Otherwise it's an exit edge.) - let is_reloop_edge = reloop_bcbs.iter().any(|&reloop_bcb| { - self.basic_coverage_blocks.dominates(target_bcb, reloop_bcb) - }); + let is_reloop_edge = reloop_bcbs + .iter() + .any(|&reloop_bcb| self.graph.dominates(target_bcb, reloop_bcb)); if is_reloop_edge { // We found a good out-edge to be given an expression. return Some(target_bcb); @@ -508,21 +515,4 @@ impl<'a> MakeBcbCounters<'a> { None } - - #[inline] - fn edge_has_no_counter( - &self, - from_bcb: BasicCoverageBlock, - to_bcb: BasicCoverageBlock, - ) -> bool { - let edge_counter = - if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(to_bcb) { - assert_eq!(sole_pred, from_bcb); - self.coverage_counters.bcb_counters[to_bcb] - } else { - self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb)).copied() - }; - - edge_counter.is_none() - } } diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index d839f46cfbd..930fa129ef2 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -21,6 +21,10 @@ pub(crate) struct CoverageGraph { pub(crate) successors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>, pub(crate) predecessors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>, dominators: Option<Dominators<BasicCoverageBlock>>, + /// Allows nodes to be compared in some total order such that _if_ + /// `a` dominates `b`, then `a < b`. If neither node dominates the other, + /// their relative order is consistent but arbitrary. + dominator_order_rank: IndexVec<BasicCoverageBlock, u32>, } impl CoverageGraph { @@ -54,10 +58,27 @@ impl CoverageGraph { } } - let mut this = Self { bcbs, bb_to_bcb, successors, predecessors, dominators: None }; + let num_nodes = bcbs.len(); + let mut this = Self { + bcbs, + bb_to_bcb, + successors, + predecessors, + dominators: None, + dominator_order_rank: IndexVec::from_elem_n(0, num_nodes), + }; + assert_eq!(num_nodes, this.num_nodes()); this.dominators = Some(dominators::dominators(&this)); + // The dominator rank of each node is just its index in a reverse-postorder traversal. + let reverse_post_order = graph::iterate::reverse_post_order(&this, this.start_node()); + // The coverage graph is created by traversal, so all nodes are reachable. + assert_eq!(reverse_post_order.len(), this.num_nodes()); + for (rank, bcb) in (0u32..).zip(reverse_post_order) { + this.dominator_order_rank[bcb] = rank; + } + // The coverage graph's entry-point node (bcb0) always starts with bb0, // which never has predecessors. Any other blocks merged into bcb0 can't // have multiple (coverage-relevant) predecessors, so bcb0 always has @@ -162,7 +183,7 @@ impl CoverageGraph { a: BasicCoverageBlock, b: BasicCoverageBlock, ) -> Ordering { - self.dominators.as_ref().unwrap().cmp_in_dominator_order(a, b) + self.dominator_order_rank[a].cmp(&self.dominator_order_rank[b]) } /// Returns the source of this node's sole in-edge, if it has exactly one. diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index bc86ae22a0d..2db7c6cf1d6 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -363,7 +363,7 @@ fn calc_test_vectors_index(conditions: &mut Vec<MCDCBranch>) -> usize { let ConditionInfo { condition_id, true_next_id, false_next_id } = branch.condition_info; [true_next_id, false_next_id] .into_iter() - .filter_map(std::convert::identity) + .flatten() .for_each(|next_id| indegree_stats[next_id] += 1); (condition_id, branch) }) diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index d0f30314e79..2e4c503f3ce 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -524,6 +524,11 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir // FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back // to HIR for it. + // HACK: For synthetic MIR bodies (async closures), use the def id of the HIR body. + if tcx.is_synthetic_mir(def_id) { + return extract_hir_info(tcx, tcx.local_parent(def_id)); + } + let hir_node = tcx.hir_node_by_def_id(def_id); let fn_body_id = hir_node.body_id().expect("HIR node is a function with body"); let hir_body = tcx.hir().body(fn_body_id); diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index da7d20cf19a..085c738f1f9 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -87,7 +87,7 @@ fn remove_unwanted_expansion_spans(covspans: &mut Vec<SpanFromMir>) { covspans.retain(|covspan| { match covspan.expn_kind { // Retain only the first await-related or macro-expanded covspan with this span. - Some(ExpnKind::Desugaring(kind)) if kind == DesugaringKind::Await => { + Some(ExpnKind::Desugaring(DesugaringKind::Await)) => { deduplicated_spans.insert(covspan.span) } Some(ExpnKind::Macro(MacroKind::Bang, _)) => deduplicated_spans.insert(covspan.span), diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index f4ac4d9fee6..30e1ac05e03 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -58,8 +58,7 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops { let param_env = tcx.param_env_reveal_all_normalized(def_id); // For types that do not need dropping, the behaviour is trivial. So we only need to track // init/uninit for types that do need dropping. - let move_data = - MoveData::gather_moves(body, tcx, param_env, |ty| ty.needs_drop(tcx, param_env)); + let move_data = MoveData::gather_moves(body, tcx, |ty| ty.needs_drop(tcx, param_env)); let elaborate_patch = { let env = MoveDataParamEnv { move_data, param_env }; diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index daf868559bc..79c62372df0 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -288,7 +288,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()), evaluated: IndexVec::with_capacity(num_values), next_opaque: Some(1), - feature_unsized_locals: tcx.features().unsized_locals, + feature_unsized_locals: tcx.features().unsized_locals(), ssa, dominators, reused_locals: BitSet::new_empty(local_decls.len()), diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index c9f24764cc2..42d6bdf6cee 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -439,12 +439,7 @@ impl<'tcx> Inliner<'tcx> { // Reachability pass defines which functions are eligible for inlining. Generally inlining // other functions is incorrect because they could reference symbols that aren't exported. - let is_generic = callsite - .callee - .args - .non_erasable_generics(self.tcx, callsite.callee.def_id()) - .next() - .is_some(); + let is_generic = callsite.callee.args.non_erasable_generics().next().is_some(); if !is_generic && !cross_crate_inlinable { return Err("not exported"); } diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 8f490094d60..08923748eb2 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -232,7 +232,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { F: FnOnce(&mut Self) -> InterpResult<'tcx, T>, { f(self) - .map_err(|err| { + .map_err_info(|err| { trace!("InterpCx operation failed: {:?}", err); // Some errors shouldn't come up because creating them causes // an allocation, which we should avoid. When that happens, @@ -602,7 +602,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { Len(place) => { let len = if let ty::Array(_, n) = place.ty(self.local_decls(), self.tcx).ty.kind() { - n.try_eval_target_usize(self.tcx, self.param_env)? + n.try_to_target_usize(self.tcx)? } else { match self.get_const(place)? { Value::Immediate(src) => src.len(&self.ecx).discard_err()?, diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index d963ca5c485..fa9a6bfcf7c 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -329,7 +329,7 @@ impl<'tcx> Validator<'_, 'tcx> { // Determine the type of the thing we are indexing. && let ty::Array(_, len) = place_base.ty(self.body, self.tcx).ty.kind() // It's an array; determine its length. - && let Some(len) = len.try_eval_target_usize(self.tcx, self.param_env) + && let Some(len) = len.try_to_target_usize(self.tcx) // If the index is in-bounds, go ahead. && idx < len { @@ -407,7 +407,7 @@ impl<'tcx> Validator<'_, 'tcx> { // mutably without consequences. However, only &mut [] // is allowed right now. if let ty::Array(_, len) = ty.kind() { - match len.try_eval_target_usize(self.tcx, self.param_env) { + match len.try_to_target_usize(self.tcx) { Some(0) => {} _ => return Err(Unpromotable), } @@ -673,7 +673,7 @@ impl<'tcx> Validator<'_, 'tcx> { } // Make sure the callee is a `const fn`. let is_const_fn = match *fn_ty.kind() { - ty::FnDef(def_id, _) => self.tcx.is_const_fn_raw(def_id), + ty::FnDef(def_id, _) => self.tcx.is_const_fn(def_id), _ => false, }; if !is_const_fn { diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index e6647edf3f5..09969a4c7cc 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -19,8 +19,7 @@ pub(super) struct RemoveUninitDrops; impl<'tcx> crate::MirPass<'tcx> for RemoveUninitDrops { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let param_env = tcx.param_env(body.source.def_id()); - let move_data = - MoveData::gather_moves(body, tcx, param_env, |ty| ty.needs_drop(tcx, param_env)); + let move_data = MoveData::gather_moves(body, tcx, |ty| ty.needs_drop(tcx, param_env)); let mut maybe_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) .into_engine(tcx, body) diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index b4d084d4dff..8df6e63deeb 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -504,7 +504,7 @@ fn collect_items_rec<'tcx>( // Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the // mono item graph. if tcx.dcx().err_count() > error_count - && starting_item.node.is_generic_fn(tcx) + && starting_item.node.is_generic_fn() && starting_item.node.is_user_defined() { let formatted_item = with_no_trimmed_paths!(starting_item.node.to_string()); @@ -1522,7 +1522,6 @@ fn create_mono_items_for_default_impls<'tcx>( // it, to validate whether or not the impl is legal to instantiate at all. let only_region_params = |param: &ty::GenericParamDef, _: &_| match param.kind { GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Const { is_host_effect: true, .. } => tcx.consts.true_.into(), GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { unreachable!( "`own_requires_monomorphization` check means that \ diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 9bf7e67417e..e2a6d392ca0 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -229,7 +229,7 @@ where } let characteristic_def_id = characteristic_def_id_of_mono_item(cx.tcx, mono_item); - let is_volatile = is_incremental_build && mono_item.is_generic_fn(cx.tcx); + let is_volatile = is_incremental_build && mono_item.is_generic_fn(); let cgu_name = match characteristic_def_id { Some(def_id) => compute_codegen_unit_name( @@ -822,7 +822,7 @@ fn mono_item_visibility<'tcx>( return Visibility::Hidden; } - let is_generic = instance.args.non_erasable_generics(tcx, def_id).next().is_some(); + let is_generic = instance.args.non_erasable_generics().next().is_some(); // Upstream `DefId` instances get different handling than local ones. let Some(def_id) = def_id.as_local() else { diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 23634d35c07..63608f9e856 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -431,7 +431,6 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz ); CanonicalVarKind::Const(self.delegate.universe_of_ct(vid).unwrap()) } - ty::InferConst::EffectVar(_) => CanonicalVarKind::Effect, ty::InferConst::Fresh(_) => todo!(), }, ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode { diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs index f2654f7534e..71c87714745 100644 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -76,9 +76,6 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for EagerResolv resolved } } - ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)) => { - self.delegate.opportunistic_resolve_effect_var(vid) - } _ => { if c.has_infer() { c.super_fold_with(self) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index c9c0d6391fc..f6a5f20a639 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -100,6 +100,15 @@ where }) } + /// Assemble additional assumptions for an alias that are not included + /// in the item bounds of the alias. For now, this is limited to the + /// `implied_const_bounds` for an associated type. + fn consider_additional_alias_assumptions( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal<I, Self>, + alias_ty: ty::AliasTy<I>, + ) -> Vec<Candidate<I>>; + fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, @@ -270,11 +279,6 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, ) -> Vec<Candidate<I>>; - - fn consider_builtin_effects_intersection_candidate( - ecx: &mut EvalCtxt<'_, D>, - goal: Goal<I, Self>, - ) -> Result<Candidate<I>, NoSolution>; } impl<D, I> EvalCtxt<'_, D> @@ -481,9 +485,6 @@ where Some(TraitSolverLangItem::TransmuteTrait) => { G::consider_builtin_transmute_candidate(self, goal) } - Some(TraitSolverLangItem::EffectsIntersection) => { - G::consider_builtin_effects_intersection_candidate(self, goal) - } _ => Err(NoSolution), } }; @@ -602,6 +603,8 @@ where )); } + candidates.extend(G::consider_additional_alias_assumptions(self, goal, alias_ty)); + if kind != ty::Projection { return; } diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs new file mode 100644 index 00000000000..8d57ad8f255 --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -0,0 +1,345 @@ +//! Dealing with host effect goals, i.e. enforcing the constness in +//! `T: const Trait` or `T: ~const Trait`. + +use rustc_type_ir::fast_reject::DeepRejectCtxt; +use rustc_type_ir::inherent::*; +use rustc_type_ir::{self as ty, Interner, elaborate}; +use tracing::instrument; + +use super::assembly::Candidate; +use crate::delegate::SolverDelegate; +use crate::solve::assembly::{self}; +use crate::solve::{ + BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, NoSolution, + QueryResult, +}; + +impl<D, I> assembly::GoalKind<D> for ty::HostEffectPredicate<I> +where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + fn self_ty(self) -> I::Ty { + self.self_ty() + } + + fn trait_ref(self, _: I) -> ty::TraitRef<I> { + self.trait_ref + } + + fn with_self_ty(self, cx: I, self_ty: I::Ty) -> Self { + self.with_self_ty(cx, self_ty) + } + + fn trait_def_id(self, _: I) -> I::DefId { + self.def_id() + } + + fn probe_and_match_goal_against_assumption( + ecx: &mut EvalCtxt<'_, D>, + source: rustc_type_ir::solve::CandidateSource<I>, + goal: Goal<I, Self>, + assumption: <I as Interner>::Clause, + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>, + ) -> Result<Candidate<I>, NoSolution> { + if let Some(host_clause) = assumption.as_host_effect_clause() { + if host_clause.def_id() == goal.predicate.def_id() + && host_clause.host().satisfies(goal.predicate.host) + { + if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( + goal.predicate.trait_ref.args, + host_clause.skip_binder().trait_ref.args, + ) { + return Err(NoSolution); + } + + ecx.probe_trait_candidate(source).enter(|ecx| { + let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause); + ecx.eq( + goal.param_env, + goal.predicate.trait_ref, + assumption_trait_pred.trait_ref, + )?; + then(ecx) + }) + } else { + Err(NoSolution) + } + } else { + Err(NoSolution) + } + } + + /// Register additional assumptions for aliases corresponding to `~const` item bounds. + /// + /// Unlike item bounds, they are not simply implied by the well-formedness of the alias. + /// Instead, they only hold if the const conditons on the alias also hold. This is why + /// we also register the const conditions of the alias after matching the goal against + /// the assumption. + fn consider_additional_alias_assumptions( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal<I, Self>, + alias_ty: ty::AliasTy<I>, + ) -> Vec<Candidate<I>> { + let cx = ecx.cx(); + let mut candidates = vec![]; + + // FIXME(effects): We elaborate here because the implied const bounds + // aren't necessarily elaborated. We probably should prefix this query + // with `explicit_`... + for clause in elaborate::elaborate( + cx, + cx.implied_const_bounds(alias_ty.def_id) + .iter_instantiated(cx, alias_ty.args) + .map(|trait_ref| trait_ref.to_host_effect_clause(cx, goal.predicate.host)), + ) { + candidates.extend(Self::probe_and_match_goal_against_assumption( + ecx, + CandidateSource::AliasBound, + goal, + clause, + |ecx| { + // Const conditions must hold for the implied const bound to hold. + ecx.add_goals( + GoalSource::Misc, + cx.const_conditions(alias_ty.def_id) + .iter_instantiated(cx, alias_ty.args) + .map(|trait_ref| { + goal.with( + cx, + trait_ref.to_host_effect_clause(cx, goal.predicate.host), + ) + }), + ); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }, + )); + } + + candidates + } + + fn consider_impl_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal<I, Self>, + impl_def_id: <I as Interner>::DefId, + ) -> Result<Candidate<I>, NoSolution> { + let cx = ecx.cx(); + + let impl_trait_ref = cx.impl_trait_ref(impl_def_id); + if !DeepRejectCtxt::relate_rigid_infer(ecx.cx()) + .args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args) + { + return Err(NoSolution); + } + + let impl_polarity = cx.impl_polarity(impl_def_id); + match impl_polarity { + ty::ImplPolarity::Negative => return Err(NoSolution), + ty::ImplPolarity::Reservation => { + unimplemented!("reservation impl for const trait: {:?}", goal) + } + ty::ImplPolarity::Positive => {} + }; + + if !cx.is_const_impl(impl_def_id) { + return Err(NoSolution); + } + + ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { + let impl_args = ecx.fresh_args_for_item(impl_def_id); + ecx.record_impl_args(impl_args); + let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args); + + ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?; + let where_clause_bounds = cx + .predicates_of(impl_def_id) + .iter_instantiated(cx, impl_args) + .map(|pred| goal.with(cx, pred)); + ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); + + // For this impl to be `const`, we need to check its `~const` bounds too. + let const_conditions = cx + .const_conditions(impl_def_id) + .iter_instantiated(cx, impl_args) + .map(|bound_trait_ref| { + goal.with(cx, bound_trait_ref.to_host_effect_clause(cx, goal.predicate.host)) + }); + ecx.add_goals(GoalSource::ImplWhereBound, const_conditions); + + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + fn consider_error_guaranteed_candidate( + ecx: &mut EvalCtxt<'_, D>, + _guar: <I as Interner>::ErrorGuaranteed, + ) -> Result<Candidate<I>, NoSolution> { + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) + } + + fn consider_auto_trait_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("auto traits are never const") + } + + fn consider_trait_alias_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("trait aliases are never const") + } + + fn consider_builtin_sized_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("Sized is never const") + } + + fn consider_builtin_copy_clone_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + todo!("Copy/Clone is not yet const") + } + + fn consider_builtin_pointer_like_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("PointerLike is not const") + } + + fn consider_builtin_fn_ptr_trait_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + todo!("Fn* are not yet const") + } + + fn consider_builtin_fn_trait_candidates( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + _kind: rustc_type_ir::ClosureKind, + ) -> Result<Candidate<I>, NoSolution> { + todo!("Fn* are not yet const") + } + + fn consider_builtin_async_fn_trait_candidates( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + _kind: rustc_type_ir::ClosureKind, + ) -> Result<Candidate<I>, NoSolution> { + todo!("AsyncFn* are not yet const") + } + + fn consider_builtin_async_fn_kind_helper_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("AsyncFnKindHelper is not const") + } + + fn consider_builtin_tuple_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("Tuple trait is not const") + } + + fn consider_builtin_pointee_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("Pointee is not const") + } + + fn consider_builtin_future_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("Future is not const") + } + + fn consider_builtin_iterator_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + todo!("Iterator is not yet const") + } + + fn consider_builtin_fused_iterator_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("FusedIterator is not const") + } + + fn consider_builtin_async_iterator_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("AsyncIterator is not const") + } + + fn consider_builtin_coroutine_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("Coroutine is not const") + } + + fn consider_builtin_discriminant_kind_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("DiscriminantKind is not const") + } + + fn consider_builtin_async_destruct_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("AsyncDestruct is not const") + } + + fn consider_builtin_destruct_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("Destruct is not const") + } + + fn consider_builtin_transmute_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("TransmuteFrom is not const") + } + + fn consider_structural_builtin_unsize_candidates( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Vec<Candidate<I>> { + unreachable!("Unsize is not const") + } +} + +impl<D, I> EvalCtxt<'_, D> +where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + #[instrument(level = "trace", skip(self))] + pub(super) fn compute_host_effect_goal( + &mut self, + goal: Goal<I, ty::HostEffectPredicate<I>>, + ) -> QueryResult<I> { + let candidates = self.assemble_and_evaluate_candidates(goal); + self.merge_candidates(candidates) + } +} diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index f49f3a1a3bf..e2fd0dd2a25 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -16,7 +16,7 @@ use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner}; -use tracing::{instrument, trace}; +use tracing::{debug, instrument, trace}; use crate::canonicalizer::{CanonicalizeMode, Canonicalizer}; use crate::delegate::SolverDelegate; @@ -165,12 +165,22 @@ where // HACK: We bail with overflow if the response would have too many non-region // inference variables. This tends to only happen if we encounter a lot of // ambiguous alias types which get replaced with fresh inference variables - // during generalization. This prevents a hang in nalgebra. - let num_non_region_vars = canonical.variables.iter().filter(|c| !c.is_region()).count(); - if num_non_region_vars > self.cx().recursion_limit() { - return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow { - suggest_increasing_limit: true, - })); + // during generalization. This prevents hangs caused by an exponential blowup, + // see tests/ui/traits/next-solver/coherence-alias-hang.rs. + // + // We don't do so for `NormalizesTo` goals as we erased the expected term and + // bailing with overflow here would prevent us from detecting a type-mismatch, + // causing a coherence error in diesel, see #131969. We still bail with overflow + // when later returning from the parent AliasRelate goal. + if !self.is_normalizes_to_goal { + let num_non_region_vars = + canonical.variables.iter().filter(|c| !c.is_region() && c.is_existential()).count(); + if num_non_region_vars > self.cx().recursion_limit() { + debug!(?num_non_region_vars, "too many inference variables -> overflow"); + return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow { + suggest_increasing_limit: true, + })); + } } Ok(canonical) 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 cbefc826fb7..7608253882a 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 @@ -291,7 +291,7 @@ where search_graph, nested_goals: NestedGoals::new(), tainted: Ok(()), - inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values, input), + inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values), }; for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { @@ -443,6 +443,9 @@ where ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => { self.compute_trait_goal(Goal { param_env, predicate }) } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => { + self.compute_host_effect_goal(Goal { param_env, predicate }) + } ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => { self.compute_projection_goal(Goal { param_env, predicate }) } diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index 85474bf37b4..1607fbb1b6a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -13,7 +13,7 @@ use rustc_type_ir::{self as ty, Interner}; use crate::delegate::SolverDelegate; use crate::solve::eval_ctxt::canonical; use crate::solve::{ - CanonicalInput, Certainty, GenerateProofTree, Goal, GoalEvaluationKind, GoalSource, QueryInput, + CanonicalInput, Certainty, GenerateProofTree, Goal, GoalEvaluationKind, GoalSource, QueryResult, inspect, }; @@ -119,6 +119,9 @@ impl<I: Interner> WipCanonicalGoalEvaluation<I> { } } +/// This only exists during proof tree building and does not have +/// a corresponding struct in `inspect`. We need this to track a +/// bunch of metadata about the current evaluation. #[derive_where(PartialEq, Eq, Debug; I: Interner)] struct WipCanonicalGoalEvaluationStep<I: Interner> { /// Unlike `EvalCtxt::var_values`, we append a new @@ -128,7 +131,6 @@ struct WipCanonicalGoalEvaluationStep<I: Interner> { /// This is necessary as we otherwise don't unify these /// vars when instantiating multiple `CanonicalState`. var_values: Vec<I::GenericArg>, - instantiated_goal: QueryInput<I, I::Predicate>, probe_depth: usize, evaluation: WipProbe<I>, } @@ -145,16 +147,12 @@ impl<I: Interner> WipCanonicalGoalEvaluationStep<I> { current } - fn finalize(self) -> inspect::CanonicalGoalEvaluationStep<I> { + fn finalize(self) -> inspect::Probe<I> { let evaluation = self.evaluation.finalize(); match evaluation.kind { - inspect::ProbeKind::Root { .. } => (), + inspect::ProbeKind::Root { .. } => evaluation, _ => unreachable!("unexpected root evaluation: {evaluation:?}"), } - inspect::CanonicalGoalEvaluationStep { - instantiated_goal: self.instantiated_goal, - evaluation, - } } } @@ -328,11 +326,9 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> { pub(crate) fn new_goal_evaluation_step( &mut self, var_values: ty::CanonicalVarValues<I>, - instantiated_goal: QueryInput<I, I::Predicate>, ) -> ProofTreeBuilder<D> { self.nested(|| WipCanonicalGoalEvaluationStep { var_values: var_values.var_values.to_vec(), - instantiated_goal, evaluation: WipProbe { initial_num_var_values: var_values.len(), steps: vec![], diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index ff91fa13fd0..6793779b205 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -13,6 +13,7 @@ mod alias_relate; mod assembly; +mod effect_goals; mod eval_ctxt; pub mod inspect; mod normalizes_to; @@ -182,12 +183,6 @@ where let (ct, ty) = goal.predicate; let ct_ty = match ct.kind() { - // FIXME: Ignore effect vars because canonicalization doesn't handle them correctly - // and if we stall on the var then we wind up creating ambiguity errors in a probe - // for this goal which contains an effect var. Which then ends up ICEing. - ty::ConstKind::Infer(ty::InferConst::EffectVar(_)) => { - return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); - } ty::ConstKind::Infer(_) => { return self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); } @@ -295,6 +290,37 @@ where Ok(ty) } } + + /// Normalize a const for when it is structurally matched on, or more likely + /// when it needs `.try_to_*` called on it (e.g. to turn it into a usize). + /// + /// This function is necessary in nearly all cases before matching on a const. + /// Not doing so is likely to be incomplete and therefore unsound during + /// coherence. + #[instrument(level = "trace", skip(self, param_env), ret)] + fn structurally_normalize_const( + &mut self, + param_env: I::ParamEnv, + ct: I::Const, + ) -> Result<I::Const, NoSolution> { + if let ty::ConstKind::Unevaluated(..) = ct.kind() { + let normalized_ct = self.next_const_infer(); + let alias_relate_goal = Goal::new( + self.cx(), + param_env, + ty::PredicateKind::AliasRelate( + ct.into(), + normalized_ct.into(), + ty::AliasRelationDirection::Equate, + ), + ); + self.add_goal(GoalSource::Misc, alias_relate_goal); + self.try_evaluate_added_goals()?; + Ok(self.resolve_vars_if_possible(normalized_ct)) + } else { + Ok(ct) + } + } } fn response_no_constraints_raw<I: Interner>( diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 4d8b193ee49..7287cdf74bf 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 @@ -193,6 +193,14 @@ where } } + fn consider_additional_alias_assumptions( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + _alias_ty: ty::AliasTy<I>, + ) -> Vec<Candidate<I>> { + vec![] + } + fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, NormalizesTo<I>>, @@ -911,68 +919,6 @@ where ) -> Result<Candidate<I>, NoSolution> { panic!("`TransmuteFrom` does not have an associated type: {:?}", goal) } - - fn consider_builtin_effects_intersection_candidate( - ecx: &mut EvalCtxt<'_, D>, - goal: Goal<I, Self>, - ) -> Result<Candidate<I>, NoSolution> { - let ty::Tuple(types) = goal.predicate.self_ty().kind() else { - return Err(NoSolution); - }; - - let cx = ecx.cx(); - - let mut first_non_maybe = None; - let mut non_maybe_count = 0; - for ty in types.iter() { - if !matches!(ty::EffectKind::try_from_ty(cx, ty), Some(ty::EffectKind::Maybe)) { - first_non_maybe.get_or_insert(ty); - non_maybe_count += 1; - } - } - - match non_maybe_count { - 0 => { - let ty = ty::EffectKind::Maybe.to_ty(cx); - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - ecx.instantiate_normalizes_to_term(goal, ty.into()); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } - 1 => { - let ty = first_non_maybe.unwrap(); - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - ecx.instantiate_normalizes_to_term(goal, ty.into()); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } - _ => { - let mut min = ty::EffectKind::Maybe; - - for ty in types.iter() { - // We can't find the intersection if the types used are generic. - // - // FIXME(effects): do we want to look at where clauses to get some - // clue for the case where generic types are being used? - let Some(kind) = ty::EffectKind::try_from_ty(cx, ty) else { - return Err(NoSolution); - }; - - let Some(result) = ty::EffectKind::intersection(min, kind) else { - return Err(NoSolution); - }; - - min = result; - } - - let ty = min.to_ty(cx); - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - ecx.instantiate_normalizes_to_term(goal, ty.into()); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } - } - } } impl<D, I> EvalCtxt<'_, D> 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 5828b2ecf34..08cc89d950e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -39,6 +39,14 @@ where self.def_id() } + fn consider_additional_alias_assumptions( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + _alias_ty: ty::AliasTy<I>, + ) -> Vec<Candidate<I>> { + vec![] + } + fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, TraitPredicate<I>>, @@ -627,11 +635,16 @@ where } ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + let assume = ecx.structurally_normalize_const( + goal.param_env, + goal.predicate.trait_ref.args.const_at(2), + )?; + let certainty = ecx.is_transmutable( goal.param_env, goal.predicate.trait_ref.args.type_at(0), goal.predicate.trait_ref.args.type_at(1), - goal.predicate.trait_ref.args.const_at(2), + assume, )?; ecx.evaluate_added_goals_and_make_canonical_response(certainty) }) @@ -714,47 +727,6 @@ where } }) } - - fn consider_builtin_effects_intersection_candidate( - ecx: &mut EvalCtxt<'_, D>, - goal: Goal<I, Self>, - ) -> Result<Candidate<I>, NoSolution> { - if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); - } - - let ty::Tuple(types) = goal.predicate.self_ty().kind() else { - return Err(NoSolution); - }; - - let cx = ecx.cx(); - let maybe_count = types - .iter() - .filter_map(|ty| ty::EffectKind::try_from_ty(cx, ty)) - .filter(|&ty| ty == ty::EffectKind::Maybe) - .count(); - - // Don't do concrete type check unless there are more than one type that will influence the result. - // This would allow `(Maybe, T): Min` pass even if we know nothing about `T`. - if types.len() - maybe_count > 1 { - let mut min = ty::EffectKind::Maybe; - - for ty in types.iter() { - let Some(kind) = ty::EffectKind::try_from_ty(ecx.cx(), ty) else { - return Err(NoSolution); - }; - - let Some(result) = ty::EffectKind::intersection(min, kind) else { - return Err(NoSolution); - }; - - min = result; - } - } - - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) - .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) - } } impl<D, I> EvalCtxt<'_, D> @@ -785,7 +757,8 @@ where let mut responses = vec![]; // If the principal def ids match (or are both none), then we're not doing // trait upcasting. We're just removing auto traits (or shortening the lifetime). - if a_data.principal_def_id() == b_data.principal_def_id() { + let b_principal_def_id = b_data.principal_def_id(); + if a_data.principal_def_id() == b_principal_def_id || b_principal_def_id.is_none() { responses.extend(self.consider_builtin_upcast_to_principal( goal, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs index 41108c91f2e..e1f19beb53a 100644 --- a/compiler/rustc_parse/src/lexer/diagnostics.rs +++ b/compiler/rustc_parse/src/lexer/diagnostics.rs @@ -85,7 +85,7 @@ pub(super) fn report_suspicious_mismatch_block( } } - // Find the inner-most span candidate for final report + // Find the innermost span candidate for final report let candidate_span = matched_spans.into_iter().rev().find(|&(_, same_ident)| !same_ident).map(|(span, _)| span); diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index e5a14f6a156..f8ef423a9b0 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -99,6 +99,10 @@ passes_collapse_debuginfo = passes_confusables = attribute should be applied to an inherent method .label = not an inherent method +passes_const_stable_not_stable = + attribute `#[rustc_const_stable]` can only be applied to functions that are declared `#[stable]` + .label = attribute specified here + passes_continue_labeled_block = `continue` pointing to a labeled block .label = labeled blocks cannot be `continue`'d @@ -245,6 +249,19 @@ passes_doc_test_unknown_include = unknown `doc` attribute `{$path}` .suggestion = use `doc = include_str!` instead +passes_doc_test_unknown_passes = + unknown `doc` attribute `{$path}` + .note = `doc` attribute `{$path}` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136> + .label = no longer functions + .help = you may want to use `doc(document_private_items)` + .no_op_note = `doc({$path})` is now a no-op + +passes_doc_test_unknown_plugins = + unknown `doc` attribute `{$path}` + .note = `doc` attribute `{$path}` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136> and CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622> + .label = no longer functions + .no_op_note = `doc({$path})` is now a no-op + passes_doc_test_unknown_spotlight = unknown `doc` attribute `{$path}` .note = `doc(spotlight)` was renamed to `doc(notable_trait)` @@ -256,7 +273,7 @@ passes_duplicate_diagnostic_item_in_crate = .note = the diagnostic item is first defined in crate `{$orig_crate_name}` passes_duplicate_feature_err = - the feature `{$feature}` has already been declared + the feature `{$feature}` has already been enabled passes_duplicate_lang_item = found duplicate lang item `{$lang_item_name}` @@ -452,10 +469,10 @@ passes_may_dangle = `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl passes_maybe_string_interpolation = you might have meant to use string interpolation in this string literal + passes_missing_const_err = - attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` + attributes `#[rustc_const_unstable]`, `#[rustc_const_stable]` and `#[rustc_const_stable_indirect]` require the function or method to be `const` .help = make the function or method const - .label = attribute specified here passes_missing_const_stab_attr = {$descr} has missing const stability attribute @@ -553,9 +570,9 @@ passes_only_has_effect_on = *[unspecified] (unspecified--this is a compiler bug) } -passes_optimize_not_fn_or_closure = - attribute should be applied to function or closure - .label = not a function or closure +passes_optimize_invalid_target = + attribute applied to an invalid target + .label = invalid target passes_outer_crate_level_attr = crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs index d0cc123c41a..b1267562f7b 100644 --- a/compiler/rustc_passes/src/abi_test.rs +++ b/compiler/rustc_passes/src/abi_test.rs @@ -12,7 +12,7 @@ use super::layout_test::ensure_wf; use crate::errors::{AbiInvalidAttribute, AbiNe, AbiOf, UnrecognizedField}; pub fn test_abi(tcx: TyCtxt<'_>) { - if !tcx.features().rustc_attrs { + if !tcx.features().rustc_attrs() { // if the `rustc_attrs` feature is not enabled, don't bother testing ABI return; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4516ea94cad..ed0d7ed8acc 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -124,7 +124,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } [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, target), + [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target), [sym::no_sanitize, ..] => { self.check_applied_to_fn_or_method(hir_id, attr, span, target) } @@ -433,23 +433,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks that `#[optimize(..)]` is applied to a function/closure/method, /// or to an impl block or module. - // FIXME(#128488): this should probably be elevated to an error? - fn check_optimize(&self, hir_id: HirId, attr: &Attribute, target: Target) { - match target { + fn check_optimize(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + let is_valid = matches!( + target, Target::Fn - | Target::Closure - | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) - | Target::Impl - | Target::Mod => {} - - _ => { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr.span, - errors::OptimizeNotFnOrClosure, - ); - } + | Target::Closure + | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) + ); + if !is_valid { + self.dcx().emit_err(errors::OptimizeInvalidTarget { + attr_span: attr.span, + defn_span: span, + on_crate: hir_id == CRATE_HIR_ID, + }); } } @@ -922,12 +918,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }; match item_kind { Some(ItemKind::Impl(i)) => { - let is_valid = matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) - || if let hir::TyKind::BareFn(bare_fn_ty) = &i.self_ty.kind { - bare_fn_ty.decl.inputs.len() == 1 - } else { - false - } + let is_valid = doc_fake_variadic_is_allowed_self_ty(i.self_ty) || if let Some(&[hir::GenericArg::Type(ty)]) = i .of_trait .as_ref() @@ -1187,19 +1178,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { sym::masked => self.check_doc_masked(attr, meta, hir_id, target), - // no_default_passes: deprecated - // passes: deprecated - // plugins: removed, but rustdoc warns about it itself - sym::cfg - | sym::hidden - | sym::no_default_passes - | sym::notable_trait - | sym::passes - | sym::plugins => {} + sym::cfg | sym::hidden | sym::notable_trait => {} sym::rust_logo => { if self.check_attr_crate_level(attr, meta, hir_id) - && !self.tcx.features().rustdoc_internals + && !self.tcx.features().rustdoc_internals() { feature_err( &self.tcx.sess, @@ -1244,6 +1227,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> { sugg: (attr.meta().unwrap().span, applicability), }, ); + } else if i_meta.has_name(sym::passes) + || i_meta.has_name(sym::no_default_passes) + { + self.tcx.emit_node_span_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + i_meta.span, + errors::DocTestUnknownPasses { path, span: i_meta.span }, + ); + } else if i_meta.has_name(sym::plugins) { + self.tcx.emit_node_span_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + i_meta.span, + errors::DocTestUnknownPlugins { path, span: i_meta.span }, + ); } else { self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, @@ -1770,7 +1769,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } sym::align => { if let (Target::Fn | Target::Method(MethodKind::Inherent), false) = - (target, self.tcx.features().fn_align) + (target, self.tcx.features().fn_align()) { feature_err( &self.tcx.sess, @@ -1998,7 +1997,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ) { match target { Target::Fn | Target::Method(_) - if self.tcx.is_const_fn_raw(hir_id.expect_owner().to_def_id()) => {} + if self.tcx.is_const_fn(hir_id.expect_owner().to_def_id()) => {} // FIXME(#80564): We permit struct fields and match arms to have an // `#[allow_internal_unstable]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible @@ -2296,10 +2295,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &mut diag, &cause, None, - Some(ValuePairs::PolySigs(ExpectedFound { + Some(param_env.and(ValuePairs::PolySigs(ExpectedFound { expected: ty::Binder::dummy(expected_sig), found: ty::Binder::dummy(sig), - })), + }))), terr, false, ); @@ -2626,3 +2625,20 @@ fn check_duplicates( }, } } + +fn doc_fake_variadic_is_allowed_self_ty(self_ty: &hir::Ty<'_>) -> bool { + matches!(&self_ty.kind, hir::TyKind::Tup([_])) + || if let hir::TyKind::BareFn(bare_fn_ty) = &self_ty.kind { + bare_fn_ty.decl.inputs.len() == 1 + } else { + false + } + || (if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &self_ty.kind + && let Some(&[hir::GenericArg::Type(ty)]) = + path.segments.last().map(|last| last.args().args) + { + doc_fake_variadic_is_allowed_self_ty(ty) + } else { + false + }) +} diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 0dad94a9939..f5ece513956 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -87,7 +87,7 @@ impl<'tcx> CheckConstVisitor<'tcx> { let is_feature_allowed = |feature_gate| { // All features require that the corresponding gate be enabled, // even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`. - if !tcx.features().active(feature_gate) { + if !tcx.features().enabled(feature_gate) { return false; } @@ -105,7 +105,7 @@ impl<'tcx> CheckConstVisitor<'tcx> { // If this crate is not using stability attributes, or this function is not claiming to be a // stable `const fn`, that is all that is required. - if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) { + if !tcx.features().staged_api() || tcx.has_attr(def_id, sym::rustc_const_unstable) { return true; } @@ -135,7 +135,7 @@ impl<'tcx> CheckConstVisitor<'tcx> { let required_gates = required_gates.unwrap_or(&[]); let missing_gates: Vec<_> = - required_gates.iter().copied().filter(|&g| !features.active(g)).collect(); + required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect(); match missing_gates.as_slice() { [] => { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 6dc3dfba58f..b5f1eac1cba 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -76,9 +76,15 @@ pub(crate) struct CoverageNotFnOrClosure { pub defn_span: Span, } -#[derive(LintDiagnostic)] -#[diag(passes_optimize_not_fn_or_closure)] -pub(crate) struct OptimizeNotFnOrClosure; +#[derive(Diagnostic)] +#[diag(passes_optimize_invalid_target)] +pub(crate) struct OptimizeInvalidTarget { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, + pub on_crate: bool, +} #[derive(Diagnostic)] #[diag(passes_should_be_applied_to_fn)] @@ -318,6 +324,27 @@ pub(crate) struct DocTestUnknownSpotlight { } #[derive(LintDiagnostic)] +#[diag(passes_doc_test_unknown_passes)] +#[note] +#[help] +#[note(passes_no_op_note)] +pub(crate) struct DocTestUnknownPasses { + pub path: String, + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(passes_doc_test_unknown_plugins)] +#[note] +#[note(passes_no_op_note)] +pub(crate) struct DocTestUnknownPlugins { + pub path: String, + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] #[diag(passes_doc_test_unknown_include)] pub(crate) struct DocTestUnknownInclude { pub path: String, @@ -1547,12 +1574,20 @@ pub(crate) struct DuplicateFeatureErr { pub span: Span, pub feature: Symbol, } + #[derive(Diagnostic)] #[diag(passes_missing_const_err)] pub(crate) struct MissingConstErr { #[primary_span] #[help] pub fn_sig_span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_const_stable_not_stable)] +pub(crate) struct ConstStableNotStable { + #[primary_span] + pub fn_sig_span: Span, #[label] pub const_span: Span, } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index a4c3d789176..27714a0fdcc 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -122,7 +122,9 @@ impl<'k> StatCollector<'k> { // We will soon sort, so the initial order does not matter. #[allow(rustc::potential_query_instability)] let mut nodes: Vec<_> = self.nodes.iter().collect(); - nodes.sort_by_key(|(_, node)| node.stats.count * node.stats.size); + nodes.sort_by_cached_key(|(label, node)| { + (node.stats.count * node.stats.size, label.to_owned()) + }); let total_size = nodes.iter().map(|(_, node)| node.stats.count * node.stats.size).sum(); diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index 3aef88e771f..93729a7f6df 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -18,7 +18,7 @@ use crate::errors::{ }; pub fn test_layout(tcx: TyCtxt<'_>) { - if !tcx.features().rustc_attrs { + if !tcx.features().rustc_attrs() { // if the `rustc_attrs` feature is not enabled, don't bother testing layout return; } diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 91ba2fa7548..8a360c017ad 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -144,7 +144,7 @@ impl<'tcx> Visitor<'tcx> for LibFeatureCollector<'tcx> { fn lib_features(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> LibFeatures { // If `staged_api` is not enabled then we aren't allowed to define lib // features; there is no point collecting them. - if !tcx.features().staged_api { + if !tcx.features().staged_api() { return LibFeatures::default(); } diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 056318fbcb7..0ec151ceb45 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -322,7 +322,7 @@ impl<'tcx> ReachableContext<'tcx> { self.visit(ty); // Manually visit to actually see the trait's `DefId`. Type visitors won't see it if let Some(trait_ref) = dyn_ty.principal() { - let ExistentialTraitRef { def_id, args } = trait_ref.skip_binder(); + let ExistentialTraitRef { def_id, args, .. } = trait_ref.skip_binder(); self.visit_def_id(def_id, "", &""); self.visit(args); } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 751c87a9fe5..466ea32735b 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -10,13 +10,13 @@ use rustc_attr::{ }; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; -use rustc_feature::ACCEPTED_FEATURES; +use rustc_feature::{ACCEPTED_LANG_FEATURES, EnabledLangFeature, EnabledLibFeature}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; +use rustc_hir::{Constness, FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; use rustc_middle::hir::nested_filter; use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures}; use rustc_middle::middle::privacy::EffectiveVisibilities; @@ -27,7 +27,6 @@ use rustc_session::lint; use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED}; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; -use rustc_target::spec::abi::Abi; use tracing::{debug, info}; use crate::errors; @@ -107,6 +106,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { def_id: LocalDefId, item_sp: Span, fn_sig: Option<&'tcx hir::FnSig<'tcx>>, + is_foreign_item: bool, kind: AnnotationKind, inherit_deprecation: InheritDeprecation, inherit_const_stability: InheritConstStability, @@ -144,7 +144,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } - if !self.tcx.features().staged_api { + if !self.tcx.features().staged_api() { // Propagate unstability. This can happen even for non-staged-api crates in case // -Zforce-unstable-if-unmarked is set. if let Some(stab) = self.parent_stab { @@ -163,30 +163,65 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } let stab = attr::find_stability(self.tcx.sess, attrs, item_sp); - let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item_sp); + let const_stab = attr::find_const_stability( + self.tcx.sess, + attrs, + item_sp, + fn_sig.is_some_and(|s| s.header.is_const()), + ); let body_stab = attr::find_body_stability(self.tcx.sess, attrs); - let mut const_span = None; - let const_stab = const_stab.map(|(const_stab, const_span_node)| { - self.index.const_stab_map.insert(def_id, const_stab); - const_span = Some(const_span_node); - const_stab - }); - - // If the current node is a function, has const stability attributes and if it doesn not have an intrinsic ABI, - // check if the function/method is const or the parent impl block is const - if let (Some(const_span), Some(fn_sig)) = (const_span, fn_sig) - && fn_sig.header.abi != Abi::RustIntrinsic + // If the current node is a function with const stability attributes (directly given or + // implied), check if the function/method is const or the parent impl block is const. + if let Some(fn_sig) = fn_sig && !fn_sig.header.is_const() - && (!self.in_trait_impl || !self.tcx.is_const_fn_raw(def_id.to_def_id())) + // We have to exclude foreign items as they might be intrinsics. Sadly we can't check + // their ABI; `fn_sig.abi` is *not* correct for foreign functions. + && !is_foreign_item + && const_stab.is_some() + && (!self.in_trait_impl || !self.tcx.is_const_fn(def_id.to_def_id())) + { + self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span }); + } + + // If this is marked const *stable*, it must also be regular-stable. + if let Some((const_stab, const_span)) = const_stab + && let Some(fn_sig) = fn_sig + && const_stab.is_const_stable() + && !stab.is_some_and(|(s, _)| s.is_stable()) + // FIXME: we skip this check targets until + // <https://github.com/rust-lang/stdarch/pull/1654> propagates. + && false { self.tcx .dcx() - .emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span, const_span }); + .emit_err(errors::ConstStableNotStable { fn_sig_span: fn_sig.span, const_span }); + } + + // Stable *language* features shouldn't be used as unstable library features. + // (Not doing this for stable library features is checked by tidy.) + if let Some(( + ConstStability { level: Unstable { .. }, feature: Some(feature), .. }, + const_span, + )) = const_stab + { + if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() { + self.tcx.dcx().emit_err(errors::UnstableAttrForAlreadyStableFeature { + span: const_span, + item_sp, + }); + } } + let const_stab = const_stab.map(|(const_stab, _span)| { + self.index.const_stab_map.insert(def_id, const_stab); + const_stab + }); + // `impl const Trait for Type` items forward their const stability to their // immediate children. + // FIXME(effects): how is this supposed to interact with `#[rustc_const_stable_indirect]`? + // Currently, once that is set, we do not inherit anything from the parent any more. if const_stab.is_none() { debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab); if let Some(parent) = self.parent_const_stab { @@ -247,8 +282,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } + // Stable *language* features shouldn't be used as unstable library features. + // (Not doing this for stable library features is checked by tidy.) if let Stability { level: Unstable { .. }, feature } = stab { - if ACCEPTED_FEATURES.iter().find(|f| f.name == feature).is_some() { + if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() { self.tcx .dcx() .emit_err(errors::UnstableAttrForAlreadyStableFeature { span, item_sp }); @@ -260,21 +297,13 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { self.index.implications.insert(implied_by, feature); } - if let Some(ConstStability { level: Unstable { .. }, feature, .. }) = const_stab { - if ACCEPTED_FEATURES.iter().find(|f| f.name == feature).is_some() { - self.tcx.dcx().emit_err(errors::UnstableAttrForAlreadyStableFeature { - span: const_span.unwrap(), // If const_stab contains Some(..), same is true for const_span - item_sp, - }); - } - } if let Some(ConstStability { level: Unstable { implied_by: Some(implied_by), .. }, feature, .. }) = const_stab { - self.index.implications.insert(implied_by, feature); + self.index.implications.insert(implied_by, feature.unwrap()); } self.index.stab_map.insert(def_id, stab); @@ -372,6 +401,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { ctor_def_id, i.span, None, + /* is_foreign_item */ false, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -390,6 +420,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { i.owner_id.def_id, i.span, fn_sig, + /* is_foreign_item */ false, kind, InheritDeprecation::Yes, const_stab_inherit, @@ -409,6 +440,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { ti.owner_id.def_id, ti.span, fn_sig, + /* is_foreign_item */ false, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -432,6 +464,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { ii.owner_id.def_id, ii.span, fn_sig, + /* is_foreign_item */ false, kind, InheritDeprecation::Yes, InheritConstStability::No, @@ -447,6 +480,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { var.def_id, var.span, None, + /* is_foreign_item */ false, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -457,6 +491,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { ctor_def_id, var.span, None, + /* is_foreign_item */ false, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -475,6 +510,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { s.def_id, s.span, None, + /* is_foreign_item */ false, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -486,10 +522,15 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { } fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) { + let fn_sig = match &i.kind { + rustc_hir::ForeignItemKind::Fn(fn_sig, ..) => Some(fn_sig), + _ => None, + }; self.annotate( i.owner_id.def_id, i.span, - None, + fn_sig, + /* is_foreign_item */ true, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -512,6 +553,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { p.def_id, p.span, None, + /* is_foreign_item */ false, kind, InheritDeprecation::No, InheritConstStability::No, @@ -540,8 +582,10 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { } } - fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) { - if !self.tcx.features().staged_api { + fn check_missing_or_wrong_const_stability(&self, def_id: LocalDefId, span: Span) { + // The visitor runs for "unstable-if-unmarked" crates, but we don't yet support + // that on the const side. + if !self.tcx.features().staged_api() { return; } @@ -554,10 +598,11 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { } let is_const = self.tcx.is_const_fn(def_id.to_def_id()) - || self.tcx.is_const_trait_impl_raw(def_id.to_def_id()); + || self.tcx.is_const_trait_impl(def_id.to_def_id()); let is_stable = self.tcx.lookup_stability(def_id).is_some_and(|stability| stability.level.is_stable()); - let missing_const_stability_attribute = self.tcx.lookup_const_stability(def_id).is_none(); + let missing_const_stability_attribute = + self.tcx.lookup_const_stability(def_id).is_none_or(|s| s.feature.is_none()); if is_const && is_stable && missing_const_stability_attribute { let descr = self.tcx.def_descr(def_id.to_def_id()); @@ -587,7 +632,7 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { } // Ensure stable `const fn` have a const stability attribute. - self.check_missing_const_stability(i.owner_id.def_id, i.span); + self.check_missing_or_wrong_const_stability(i.owner_id.def_id, i.span); intravisit::walk_item(self, i) } @@ -601,7 +646,7 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { let impl_def_id = self.tcx.hir().get_parent_item(ii.hir_id()); if self.tcx.impl_trait_ref(impl_def_id).is_none() { self.check_missing_stability(ii.owner_id.def_id, ii.span); - self.check_missing_const_stability(ii.owner_id.def_id, ii.span); + self.check_missing_or_wrong_const_stability(ii.owner_id.def_id, ii.span); } intravisit::walk_impl_item(self, ii); } @@ -670,6 +715,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID), None, + /* is_foreign_item */ false, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -732,12 +778,23 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // For implementations of traits, check the stability of each item // individually as it's possible to have a stable trait with unstable // items. - hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref t), self_ty, items, .. }) => { + hir::ItemKind::Impl(hir::Impl { + constness, + of_trait: Some(ref t), + self_ty, + items, + .. + }) => { let features = self.tcx.features(); - if features.staged_api { + if features.staged_api() { let attrs = self.tcx.hir().attrs(item.hir_id()); let stab = attr::find_stability(self.tcx.sess, attrs, item.span); - let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item.span); + let const_stab = attr::find_const_stability( + self.tcx.sess, + attrs, + item.span, + matches!(constness, Constness::Const), + ); // If this impl block has an #[unstable] attribute, give an // error if all involved types and traits are stable, because @@ -762,8 +819,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable // needs to have an error emitted. - if features.const_trait_impl - && self.tcx.is_const_trait_impl_raw(item.owner_id.to_def_id()) + if features.const_trait_impl() + && self.tcx.is_const_trait_impl(item.owner_id.to_def_id()) && const_stab.is_some_and(|(stab, _)| stab.is_const_stable()) { self.tcx.dcx().emit_err(errors::TraitImplConstStable { span: item.span }); @@ -926,7 +983,7 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> { /// libraries, identify activated features that don't exist and error about them. pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { let is_staged_api = - tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api; + tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api(); if is_staged_api { let effective_visibilities = &tcx.effective_visibilities(()); let mut missing = MissingStabilityAnnotations { tcx, effective_visibilities }; @@ -935,33 +992,33 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { tcx.hir().visit_all_item_likes_in_crate(&mut missing); } - let declared_lang_features = &tcx.features().declared_lang_features; + let enabled_lang_features = tcx.features().enabled_lang_features(); let mut lang_features = UnordSet::default(); - for &(feature, span, since) in declared_lang_features { - if let Some(since) = since { + for EnabledLangFeature { gate_name, attr_sp, stable_since } in enabled_lang_features { + if let Some(version) = stable_since { // Warn if the user has enabled an already-stable lang feature. - unnecessary_stable_feature_lint(tcx, span, feature, since); + unnecessary_stable_feature_lint(tcx, *attr_sp, *gate_name, *version); } - if !lang_features.insert(feature) { + if !lang_features.insert(gate_name) { // Warn if the user enables a lang feature multiple times. - tcx.dcx().emit_err(errors::DuplicateFeatureErr { span, feature }); + tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *attr_sp, feature: *gate_name }); } } - let declared_lib_features = &tcx.features().declared_lib_features; + let enabled_lib_features = tcx.features().enabled_lib_features(); let mut remaining_lib_features = FxIndexMap::default(); - for (feature, span) in declared_lib_features { - if remaining_lib_features.contains_key(&feature) { + for EnabledLibFeature { gate_name, attr_sp } in enabled_lib_features { + if remaining_lib_features.contains_key(gate_name) { // Warn if the user enables a lib feature multiple times. - tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *span, feature: *feature }); + tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *attr_sp, feature: *gate_name }); } - remaining_lib_features.insert(feature, *span); + remaining_lib_features.insert(*gate_name, *attr_sp); } // `stdbuild` has special handling for `libc`, so we need to // recognise the feature when building std. // Likewise, libtest is handled specially, so `test` isn't // available as we'd like it to be. - // FIXME: only remove `libc` when `stdbuild` is active. + // FIXME: only remove `libc` when `stdbuild` is enabled. // FIXME: remove special casing for `test`. // FIXME(#120456) - is `swap_remove` correct? remaining_lib_features.swap_remove(&sym::libc); @@ -987,7 +1044,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { /// time, less loading from metadata is performed and thus compiler performance is improved. fn check_features<'tcx>( tcx: TyCtxt<'tcx>, - remaining_lib_features: &mut FxIndexMap<&Symbol, Span>, + remaining_lib_features: &mut FxIndexMap<Symbol, Span>, remaining_implications: &mut UnordMap<Symbol, Symbol>, defined_features: &LibFeatures, all_implications: &UnordMap<Symbol, Symbol>, @@ -1021,7 +1078,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { // All local crate implications need to have the feature that implies it confirmed to exist. let mut remaining_implications = tcx.stability_implications(LOCAL_CRATE).clone(); - // We always collect the lib features declared in the current crate, even if there are + // We always collect the lib features enabled in the current crate, even if there are // no unknown features, because the collection also does feature attribute validation. let local_defined_features = tcx.lib_features(LOCAL_CRATE); if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() { @@ -1057,7 +1114,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { } for (feature, span) in remaining_lib_features { - tcx.dcx().emit_err(errors::UnknownFeature { span, feature: *feature }); + tcx.dcx().emit_err(errors::UnknownFeature { span, feature }); } for (&implied_by, &feature) in remaining_implications.to_sorted_stable_ord() { diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml index 0cb47e03441..34fb1bdf6fa 100644 --- a/compiler/rustc_pattern_analysis/Cargo.toml +++ b/compiler/rustc_pattern_analysis/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena", optional = true } rustc_data_structures = { path = "../rustc_data_structures", optional = true } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 70a9319d666..0e132b27fb4 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -891,7 +891,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { print::write_slice_like(&mut s, &prefix, has_dot_dot, &suffix).unwrap(); s } - Never if self.tcx.features().never_patterns => "!".to_string(), + Never if self.tcx.features().never_patterns() => "!".to_string(), Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => "_".to_string(), Missing { .. } => bug!( "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, @@ -915,7 +915,7 @@ fn would_print_as_wildcard(tcx: TyCtxt<'_>, p: &WitnessPat<'_, '_>) -> bool { | Constructor::NonExhaustive | Constructor::Hidden | Constructor::PrivateUninhabited => true, - Constructor::Never if !tcx.features().never_patterns => true, + Constructor::Never if !tcx.features().never_patterns() => true, _ => false, } } @@ -929,7 +929,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { type PatData = &'p Pat<'tcx>; fn is_exhaustive_patterns_feature_on(&self) -> bool { - self.tcx.features().exhaustive_patterns + self.tcx.features().exhaustive_patterns() } fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: &Self::Ty) -> usize { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index f67c4cb922c..05954143aee 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -32,8 +32,8 @@ use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, use rustc_middle::query::Providers; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{ - self, Const, GenericArgs, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitor, + self, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitor, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -137,6 +137,10 @@ where ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity: _ }) => { self.visit_trait(trait_ref) } + ty::ClauseKind::HostEffect(pred) => { + try_visit!(self.visit_trait(pred.trait_ref)); + pred.host.visit_with(self) + } ty::ClauseKind::Projection(ty::ProjectionPredicate { projection_term: projection_ty, term, @@ -246,10 +250,10 @@ where ty::ExistentialPredicate::Trait(trait_ref) => trait_ref, ty::ExistentialPredicate::Projection(proj) => proj.trait_ref(tcx), ty::ExistentialPredicate::AutoTrait(def_id) => { - ty::ExistentialTraitRef { def_id, args: GenericArgs::empty() } + ty::ExistentialTraitRef::new(tcx, def_id, ty::GenericArgs::empty()) } }; - let ty::ExistentialTraitRef { def_id, args: _ } = trait_ref; + let ty::ExistentialTraitRef { def_id, .. } = trait_ref; try_visit!(self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref)); } } diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index 5e450979273..5a72e80a0a5 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -111,13 +111,25 @@ impl<'a> HashStable<StableHashingContext<'a>> for SourceFile { impl<'tcx> HashStable<StableHashingContext<'tcx>> for rustc_feature::Features { fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { // Unfortunately we cannot exhaustively list fields here, since the - // struct is macro generated. - self.declared_lang_features.hash_stable(hcx, hasher); - self.declared_lib_features.hash_stable(hcx, hasher); + // struct has private fields (to ensure its invariant is maintained) + self.enabled_lang_features().hash_stable(hcx, hasher); + self.enabled_lib_features().hash_stable(hcx, hasher); + } +} - self.all_features()[..].hash_stable(hcx, hasher); - for feature in rustc_feature::UNSTABLE_FEATURES.iter() { - feature.feature.name.hash_stable(hcx, hasher); - } +impl<'tcx> HashStable<StableHashingContext<'tcx>> for rustc_feature::EnabledLangFeature { + fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { + let rustc_feature::EnabledLangFeature { gate_name, attr_sp, stable_since } = self; + gate_name.hash_stable(hcx, hasher); + attr_sp.hash_stable(hcx, hasher); + stable_since.hash_stable(hcx, hasher); + } +} + +impl<'tcx> HashStable<StableHashingContext<'tcx>> for rustc_feature::EnabledLibFeature { + fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { + let rustc_feature::EnabledLibFeature { gate_name, attr_sp } = self; + gate_name.hash_stable(hcx, hasher); + attr_sp.hash_stable(hcx, hasher); } } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 0145ffd3a4b..031ffaed808 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1193,7 +1193,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { if !ident.as_str().starts_with('_') { self.r.unused_macros.insert(def_id, (node_id, ident)); for (rule_i, rule_span) in &self.r.macro_map[&def_id.to_def_id()].rule_spans { - self.r.unused_macro_rules.insert((def_id, *rule_i), (ident, *rule_span)); + self.r + .unused_macro_rules + .entry(def_id) + .or_default() + .insert(*rule_i, (ident, *rule_span)); } } } @@ -1317,7 +1321,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> { // Visit attributes after items for backward compatibility. // This way they can use `macro_rules` defined later. self.visit_vis(&item.vis); - self.visit_ident(item.ident); + self.visit_ident(&item.ident); item.kind.walk(item, AssocCtxt::Trait, self); visit::walk_list!(self, visit_attribute, &item.attrs); } diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 8ae77902bce..b80e0e196ca 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -606,7 +606,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Scope::BuiltinTypes => match this.builtin_types_bindings.get(&ident.name) { Some(binding) => { if matches!(ident.name, sym::f16) - && !this.tcx.features().f16 + && !this.tcx.features().f16() && !ident.span.allows_unstable(sym::f16) && finalize.is_some() && innermost_result.is_none() @@ -620,7 +620,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .emit(); } if matches!(ident.name, sym::f128) - && !this.tcx.features().f128 + && !this.tcx.features().f128() && !ident.span.allows_unstable(sym::f128) && finalize.is_some() && innermost_result.is_none() diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index b84cbf9c629..adb0ba7c820 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1205,7 +1205,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r } fn visit_assoc_item_constraint(&mut self, constraint: &'ast AssocItemConstraint) { - self.visit_ident(constraint.ident); + self.visit_ident(&constraint.ident); if let Some(ref gen_args) = constraint.gen_args { // Forbid anonymous lifetimes in GAT parameters until proper semantics are decided. self.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { @@ -2683,7 +2683,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.with_generic_param_rib( &generics.params, RibKind::Item( - if self.r.tcx.features().generic_const_items { + if self.r.tcx.features().generic_const_items() { HasGenericParams::Yes(generics.span) } else { HasGenericParams::No @@ -2888,7 +2888,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { RibKind::Normal => { // FIXME(non_lifetime_binders): Stop special-casing // const params to error out here. - if self.r.tcx.features().non_lifetime_binders + if self.r.tcx.features().non_lifetime_binders() && matches!(param.kind, GenericParamKind::Type { .. }) { Res::Def(def_kind, def_id.to_def_id()) @@ -4011,6 +4011,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let instead = res.is_some(); let suggestion = if let Some((start, end)) = this.diag_metadata.in_range && path[0].ident.span.lo() == end.span.lo() + && !matches!(start.kind, ExprKind::Lit(_)) { let mut sugg = "."; let mut span = start.span.between(end.span); @@ -4410,10 +4411,10 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let tcx = self.r.tcx(); let gate_err_sym_msg = match prim { - PrimTy::Float(FloatTy::F16) if !tcx.features().f16 => { + PrimTy::Float(FloatTy::F16) if !tcx.features().f16() => { Some((sym::f16, "the type `f16` is unstable")) } - PrimTy::Float(FloatTy::F128) if !tcx.features().f128 => { + PrimTy::Float(FloatTy::F128) if !tcx.features().f128() => { Some((sym::f128, "the type `f128` is unstable")) } _ => None, @@ -4564,7 +4565,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } AnonConstKind::InlineConst => ConstantHasGenerics::Yes, AnonConstKind::ConstArg(_) => { - if self.r.tcx.features().generic_const_exprs || is_trivial_const_arg { + if self.r.tcx.features().generic_const_exprs() || is_trivial_const_arg { ConstantHasGenerics::Yes } else { ConstantHasGenerics::No(NoConstantGenericsReason::NonTrivialConstArg) @@ -4581,7 +4582,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { fn resolve_expr_field(&mut self, f: &'ast ExprField, e: &'ast Expr) { self.resolve_expr(&f.expr, Some(e)); - self.visit_ident(f.ident); + self.visit_ident(&f.ident); walk_list!(self, visit_attribute, f.attrs.iter()); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 50fbdcaf9dc..f0632a21091 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1198,7 +1198,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // const generics. Of course, `Struct` and `Enum` may contain ty params, too, but the // benefits of including them here outweighs the small number of false positives. Some(Res::Def(DefKind::Struct | DefKind::Enum, _)) - if self.r.tcx.features().adt_const_params => + if self.r.tcx.features().adt_const_params() => { Applicability::MaybeIncorrect } @@ -2773,7 +2773,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // Avoid suggesting placing lifetime parameters on constant items unless the relevant // feature is enabled. Suggest the parent item as a possible location if applicable. if let LifetimeBinderKind::ConstItem = kind - && !self.r.tcx().features().generic_const_items + && !self.r.tcx().features().generic_const_items() { continue; } @@ -2934,7 +2934,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .emit(); } NoConstantGenericsReason::NonTrivialConstArg => { - assert!(!self.r.tcx.features().generic_const_exprs); + assert!(!self.r.tcx.features().generic_const_exprs()); self.r .dcx() .create_err(errors::ParamInNonTrivialAnonConst { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 24a0c252e55..35d491cfc18 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1122,7 +1122,8 @@ pub struct Resolver<'ra, 'tcx> { local_macro_def_scopes: FxHashMap<LocalDefId, Module<'ra>>, ast_transform_scopes: FxHashMap<LocalExpnId, Module<'ra>>, unused_macros: FxHashMap<LocalDefId, (NodeId, Ident)>, - unused_macro_rules: FxHashMap<(LocalDefId, usize), (Ident, Span)>, + /// A map from the macro to all its potentially unused arms. + unused_macro_rules: FxIndexMap<LocalDefId, FxHashMap<usize, (Ident, Span)>>, proc_macro_stubs: FxHashSet<LocalDefId>, /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index fa2be8216c7..a9ebffea8a1 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -340,7 +340,9 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) { let did = self.local_def_id(id); - self.unused_macro_rules.remove(&(did, rule_i)); + if let Some(rules) = self.unused_macro_rules.get_mut(&did) { + rules.remove(&rule_i); + } } fn check_unused_macros(&mut self) { @@ -352,18 +354,24 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { BuiltinLintDiag::UnusedMacroDefinition(ident.name), ); } - for (&(def_id, arm_i), &(ident, rule_span)) in self.unused_macro_rules.iter() { - if self.unused_macros.contains_key(&def_id) { - // We already lint the entire macro as unused - continue; + + for (&def_id, unused_arms) in self.unused_macro_rules.iter() { + let mut unused_arms = unused_arms.iter().collect::<Vec<_>>(); + unused_arms.sort_by_key(|&(&arm_i, _)| arm_i); + + for (&arm_i, &(ident, rule_span)) in unused_arms { + if self.unused_macros.contains_key(&def_id) { + // We already lint the entire macro as unused + continue; + } + let node_id = self.def_id_to_node_id[def_id]; + self.lint_buffer.buffer_lint( + UNUSED_MACRO_RULES, + node_id, + rule_span, + BuiltinLintDiag::MacroRuleNeverUsed(arm_i, ident.name), + ); } - let node_id = self.def_id_to_node_id[def_id]; - self.lint_buffer.buffer_lint( - UNUSED_MACRO_RULES, - node_id, - rule_span, - BuiltinLintDiag::MacroRuleNeverUsed(arm_i, ident.name), - ); } } @@ -653,7 +661,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // We are trying to avoid reporting this error if other related errors were reported. - if res != Res::Err && inner_attr && !self.tcx.features().custom_inner_attributes { + if res != Res::Err && inner_attr && !self.tcx.features().custom_inner_attributes() { let is_macro = match res { Res::Def(..) => true, Res::NonMacroAttr(..) => false, @@ -682,7 +690,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && namespace.ident.name == sym::diagnostic && !(attribute.ident.name == sym::on_unimplemented || (attribute.ident.name == sym::do_not_recommend - && self.tcx.features().do_not_recommend)) + && self.tcx.features().do_not_recommend())) { let distance = edit_distance(attribute.ident.name.as_str(), sym::on_unimplemented.as_str(), 5); @@ -999,10 +1007,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { { let feature = stability.feature; - let is_allowed = |feature| { - self.tcx.features().declared_features.contains(&feature) - || span.allows_unstable(feature) - }; + let is_allowed = + |feature| self.tcx.features().enabled(feature) || span.allows_unstable(feature); let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature)); if !is_allowed(feature) && !allowed_by_implication { let lint_buffer = &mut self.lint_buffer; diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 53834198f63..ca75952fe3d 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -133,7 +133,9 @@ fn encode_const<'tcx>( // bool value false is encoded as 0 and true as 1. match ct_ty.kind() { ty::Int(ity) => { - let bits = c.eval_bits(tcx, ty::ParamEnv::reveal_all()); + let bits = c + .try_to_bits(tcx, ty::ParamEnv::reveal_all()) + .expect("expected monomorphic const in cfi"); let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; if val < 0 { s.push('n'); @@ -141,7 +143,9 @@ fn encode_const<'tcx>( let _ = write!(s, "{val}"); } ty::Uint(_) => { - let val = c.eval_bits(tcx, ty::ParamEnv::reveal_all()); + let val = c + .try_to_bits(tcx, ty::ParamEnv::reveal_all()) + .expect("expected monomorphic const in cfi"); let _ = write!(s, "{val}"); } ty::Bool => { diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 83dcceeaa84..cba79a02f8b 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -245,11 +245,15 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc alias_ty.to_ty(tcx), ); debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx)); - ty::ExistentialPredicate::Projection(ty::ExistentialProjection { - def_id: assoc_ty.def_id, - args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args, - term: resolved.into(), - }) + ty::ExistentialPredicate::Projection( + ty::ExistentialProjection::erase_self_ty( + tcx, + ty::ProjectionPredicate { + projection_term: alias_ty.into(), + term: resolved.into(), + }, + ), + ) }) }) }) @@ -318,10 +322,11 @@ pub(crate) fn transform_instance<'tcx>( .lang_items() .drop_trait() .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item")); - let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { + let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::new_from_args( + tcx, def_id, - args: List::empty(), - }); + ty::List::empty(), + )); let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn); instance.args = tcx.mk_args_trait(self_ty, List::empty()); diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 1816d1278fe..893c532f1fb 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -136,3 +136,6 @@ session_unsupported_crate_type_for_target = dropping unsupported crate type `{$crate_type}` for target `{$target_triple}` session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is greater than 5 + +session_unsupported_regparm = `-Zregparm={$regparm}` is unsupported (valid values 0-3) +session_unsupported_regparm_arch = `-Zregparm=N` is only supported on x86 diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index dbb74d1e244..20e8fb38b88 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -486,6 +486,16 @@ pub(crate) struct FunctionReturnRequiresX86OrX8664; pub(crate) struct FunctionReturnThunkExternRequiresNonLargeCodeModel; #[derive(Diagnostic)] +#[diag(session_unsupported_regparm)] +pub(crate) struct UnsupportedRegparm { + pub(crate) regparm: u32, +} + +#[derive(Diagnostic)] +#[diag(session_unsupported_regparm_arch)] +pub(crate) struct UnsupportedRegparmArch; + +#[derive(Diagnostic)] #[diag(session_failed_to_create_profiler)] pub(crate) struct FailedToCreateProfiler { pub(crate) err: String, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index f9964b59a94..54a4621db24 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2000,6 +2000,11 @@ options! { "enable queries of the dependency graph for regression testing (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], "randomize the layout of types (default: no)"), + regparm: Option<u32> = (None, parse_opt_number, [TRACKED], + "On x86-32 targets, setting this to N causes the compiler to pass N arguments \ + in registers EAX, EDX, and ECX instead of on the stack for\ + \"C\", \"cdecl\", and \"stdcall\" fn.\ + It is UNSOUND to link together crates that use different values for this flag!"), relax_elf_relocations: Option<bool> = (None, parse_opt_bool, [TRACKED], "whether ELF relocations can be relaxed"), remap_cwd_prefix: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 27879d817b2..1963cf4eb7c 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1337,6 +1337,15 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } + if let Some(regparm) = sess.opts.unstable_opts.regparm { + if regparm > 3 { + sess.dcx().emit_err(errors::UnsupportedRegparm { regparm }); + } + if sess.target.arch != "x86" { + sess.dcx().emit_err(errors::UnsupportedRegparmArch); + } + } + // The code model check applies to `thunk` and `thunk-extern`, but not `thunk-inline`, so it is // kept as a `match` to force a change if new ones are added, even if we currently only support // `thunk-extern` like Clang. diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index e9c3b3ffc1d..7be7db1fd3d 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -380,11 +380,12 @@ impl RustcInternal for ExistentialProjection { type T<'tcx> = rustc_ty::ExistentialProjection<'tcx>; fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { - rustc_ty::ExistentialProjection { - def_id: self.def_id.0.internal(tables, tcx), - args: self.generic_args.internal(tables, tcx), - term: self.term.internal(tables, tcx), - } + rustc_ty::ExistentialProjection::new_from_args( + tcx, + self.def_id.0.internal(tables, tcx), + self.generic_args.internal(tables, tcx), + self.term.internal(tables, tcx), + ) } } @@ -403,10 +404,11 @@ impl RustcInternal for ExistentialTraitRef { type T<'tcx> = rustc_ty::ExistentialTraitRef<'tcx>; fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { - rustc_ty::ExistentialTraitRef { - def_id: self.def_id.0.internal(tables, tcx), - args: self.generic_args.internal(tables, tcx), - } + rustc_ty::ExistentialTraitRef::new_from_args( + tcx, + self.def_id.0.internal(tables, tcx), + self.generic_args.internal(tables, tcx), + ) } } diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index ed5d2d1e799..9514ec883ae 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -161,8 +161,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { fn predicates_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::GenericPredicates { let mut tables = self.0.borrow_mut(); let def_id = tables[def_id]; - let GenericPredicates { parent, predicates, effects_min_tys: _ } = - tables.tcx.predicates_of(def_id); + let GenericPredicates { parent, predicates } = tables.tcx.predicates_of(def_id); stable_mir::ty::GenericPredicates { parent: parent.map(|did| tables.trait_def(did)), predicates: predicates @@ -183,8 +182,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { ) -> stable_mir::ty::GenericPredicates { let mut tables = self.0.borrow_mut(); let def_id = tables[def_id]; - let GenericPredicates { parent, predicates, effects_min_tys: _ } = - tables.tcx.explicit_predicates_of(def_id); + let GenericPredicates { parent, predicates } = tables.tcx.explicit_predicates_of(def_id); stable_mir::ty::GenericPredicates { parent: parent.map(|did| tables.trait_def(did)), predicates: predicates @@ -406,7 +404,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let tcx = tables.tcx; let mir_const = cnst.internal(&mut *tables, tcx); mir_const - .try_eval_target_usize(tables.tcx, ParamEnv::empty()) + .try_to_target_usize(tables.tcx) .ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64"))) } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index b9372283feb..8f05f859c07 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -68,7 +68,7 @@ impl<'tcx> Stable<'tcx> for ty::ExistentialTraitRef<'tcx> { type T = stable_mir::ty::ExistentialTraitRef; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { - let ty::ExistentialTraitRef { def_id, args } = self; + let ty::ExistentialTraitRef { def_id, args, .. } = self; stable_mir::ty::ExistentialTraitRef { def_id: tables.trait_def(*def_id), generic_args: args.stable(tables), @@ -95,7 +95,7 @@ impl<'tcx> Stable<'tcx> for ty::ExistentialProjection<'tcx> { type T = stable_mir::ty::ExistentialProjection; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { - let ty::ExistentialProjection { def_id, args, term } = self; + let ty::ExistentialProjection { def_id, args, term, .. } = self; stable_mir::ty::ExistentialProjection { def_id: tables.trait_def(*def_id), generic_args: args.stable(tables), @@ -588,7 +588,6 @@ impl<'tcx> Stable<'tcx> for ty::Generics { .has_late_bound_regions .as_ref() .map(|late_bound_regions| late_bound_regions.stable(tables)), - host_effect_index: self.host_effect_index, } } } @@ -603,7 +602,7 @@ impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDefKind { ty::GenericParamDefKind::Type { has_default, synthetic } => { GenericParamDefKind::Type { has_default: *has_default, synthetic: *synthetic } } - ty::GenericParamDefKind::Const { has_default, is_host_effect: _, synthetic: _ } => { + ty::GenericParamDefKind::Const { has_default, synthetic: _ } => { GenericParamDefKind::Const { has_default: *has_default } } } @@ -690,6 +689,9 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> { ClauseKind::ConstEvaluatable(const_) => { stable_mir::ty::ClauseKind::ConstEvaluatable(const_.stable(tables)) } + ClauseKind::HostEffect(..) => { + todo!() + } } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 6f62b4f82d7..134a1a1db30 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -195,14 +195,6 @@ symbols! { Display, DoubleEndedIterator, Duration, - EffectsCompat, - EffectsIntersection, - EffectsIntersectionOutput, - EffectsMaybe, - EffectsNoRuntime, - EffectsRuntime, - EffectsTyCompat, - Effects__, Encodable, Encoder, Enumerate, @@ -1133,6 +1125,7 @@ symbols! { lazy_normalization_consts, lazy_type_alias, le, + legacy_receiver, len, let_chains, let_else, @@ -1573,7 +1566,6 @@ symbols! { readonly, realloc, reason, - receiver, recursion_limit, reexport_test_harness_main, ref_pat_eat_one_layer_2024, @@ -1668,6 +1660,7 @@ symbols! { rustc_confusables, rustc_const_panic_str, rustc_const_stable, + rustc_const_stable_indirect, rustc_const_unstable, rustc_conversion_suggestion, rustc_deallocator, @@ -1737,7 +1730,6 @@ symbols! { rustc_reallocator, rustc_regions, rustc_reservation_impl, - rustc_runtime, rustc_safe_intrinsic, rustc_serialize, rustc_skip_during_method_dispatch, @@ -1913,7 +1905,7 @@ symbols! { str_trim, str_trim_end, str_trim_start, - strict_provenance, + strict_provenance_lints, string_as_mut_str, string_as_str, string_deref_patterns, diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 78e6b9ec6e8..5c5ab435dbd 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -135,7 +135,7 @@ fn symbol_name_provider<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty // This closure determines the instantiating crate for instances that // need an instantiating-crate-suffix for their symbol name, in order // to differentiate between local copies. - if is_generic(instance, tcx) { + if is_generic(instance) { // For generics we might find re-usable upstream instances. If there // is one, we rely on the symbol being instantiated locally. instance.upstream_monomorphization(tcx).unwrap_or(LOCAL_CRATE) @@ -241,7 +241,7 @@ fn compute_symbol_name<'tcx>( // the ID of the instantiating crate. This avoids symbol conflicts // in case the same instances is emitted in two crates of the same // project. - let avoid_cross_crate_conflicts = is_generic(instance, tcx) || is_globally_shared_function; + let avoid_cross_crate_conflicts = is_generic(instance) || is_globally_shared_function; let instantiating_crate = avoid_cross_crate_conflicts.then(compute_instantiating_crate); @@ -276,6 +276,6 @@ fn compute_symbol_name<'tcx>( symbol } -fn is_generic<'tcx>(instance: Instance<'tcx>, tcx: TyCtxt<'tcx>) -> bool { - instance.args.non_erasable_generics(tcx, instance.def_id()).next().is_some() +fn is_generic<'tcx>(instance: Instance<'tcx>) -> bool { + instance.args.non_erasable_generics().next().is_some() } diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs index 8cfb65c1c50..73bd8d7555b 100644 --- a/compiler/rustc_symbol_mangling/src/test.rs +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -18,7 +18,7 @@ pub fn report_symbol_names(tcx: TyCtxt<'_>) { // if the `rustc_attrs` feature is not enabled, then the // attributes we are interested in cannot be present anyway, so // skip the walk. - if !tcx.features().rustc_attrs { + if !tcx.features().rustc_attrs() { return; } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index f3da7ff1ca7..d092fa8f082 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -555,7 +555,6 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> { // We only mangle a typed value if the const can be evaluated. - let ct = ct.normalize(self.tcx, ty::ParamEnv::reveal_all()); let (ct_ty, valtree) = match ct.kind() { ty::ConstKind::Value(ty, val) => (ty, val), @@ -592,7 +591,9 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => { ct_ty.print(self)?; - let mut bits = ct.eval_bits(self.tcx, ty::ParamEnv::reveal_all()); + let mut bits = ct + .try_to_bits(self.tcx, ty::ParamEnv::reveal_all()) + .expect("expected const to be monomorphic"); // Negative integer values are mangled using `n` as a "sign prefix". if let ty::Int(ity) = ct_ty.kind() { diff --git a/compiler/rustc_target/src/callconv/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs index 4a21935623b..ffec76370d0 100644 --- a/compiler/rustc_target/src/callconv/loongarch.rs +++ b/compiler/rustc_target/src/callconv/loongarch.rs @@ -1,6 +1,7 @@ use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform}; use crate::abi::{self, Abi, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; use crate::spec::HasTargetSpec; +use crate::spec::abi::Abi as SpecAbi; #[derive(Copy, Clone)] enum RegPassKind { @@ -359,3 +360,30 @@ where ); } } + +pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: SpecAbi) +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec, +{ + if abi == SpecAbi::RustIntrinsic { + return; + } + + let grlen = cx.data_layout().pointer_size.bits(); + + for arg in fn_abi.args.iter_mut() { + if arg.is_ignore() { + continue; + } + + // LLVM integers types do not differentiate between signed or unsigned integers. + // Some LoongArch instructions do not have a `.w` suffix version, they use all the + // GRLEN bits. By explicitly setting the `signext` or `zeroext` attribute + // according to signedness to avoid unnecessary integer extending instructions. + // + // This is similar to the RISC-V case, see + // https://github.com/rust-lang/rust/issues/114508 for details. + extend_integer_width(arg, grlen); + } +} diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 832246495bc..25b001b57e8 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -1,12 +1,15 @@ -use std::fmt; use std::str::FromStr; +use std::{fmt, iter}; pub use rustc_abi::{Reg, RegKind}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; -use crate::abi::{self, Abi, Align, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; -use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt, WasmCAbi}; +use crate::abi::{ + self, Abi, AddressSpace, Align, HasDataLayout, Pointer, Size, TyAbiInterface, TyAndLayout, +}; +use crate::spec::abi::Abi as SpecAbi; +use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, WasmCAbi}; mod aarch64; mod amdgpu; @@ -631,7 +634,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { ) -> Result<(), AdjustForForeignAbiError> where Ty: TyAbiInterface<'a, C> + Copy, - C: HasDataLayout + HasTargetSpec + HasWasmCAbiOpt, + C: HasDataLayout + HasTargetSpec + HasWasmCAbiOpt + HasX86AbiOpt, { if abi == spec::abi::Abi::X86Interrupt { if let Some(arg) = self.args.first_mut() { @@ -643,14 +646,18 @@ impl<'a, Ty> FnAbi<'a, Ty> { let spec = cx.target_spec(); match &spec.arch[..] { "x86" => { - let flavor = if let spec::abi::Abi::Fastcall { .. } - | spec::abi::Abi::Vectorcall { .. } = abi - { - x86::Flavor::FastcallOrVectorcall - } else { - x86::Flavor::General + let (flavor, regparm) = match abi { + spec::abi::Abi::Fastcall { .. } | spec::abi::Abi::Vectorcall { .. } => { + (x86::Flavor::FastcallOrVectorcall, None) + } + spec::abi::Abi::C { .. } + | spec::abi::Abi::Cdecl { .. } + | spec::abi::Abi::Stdcall { .. } => { + (x86::Flavor::General, cx.x86_abi_opt().regparm) + } + _ => (x86::Flavor::General, None), }; - x86::compute_abi_info(cx, self, flavor); + x86::compute_abi_info(cx, self, x86::X86Options { flavor, regparm }); } "x86_64" => match abi { spec::abi::Abi::SysV64 { .. } => x86_64::compute_abi_info(cx, self), @@ -716,6 +723,118 @@ impl<'a, Ty> FnAbi<'a, Ty> { Ok(()) } + + pub fn adjust_for_rust_abi<C>(&mut self, cx: &C, abi: SpecAbi) + where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec, + { + let spec = cx.target_spec(); + match &spec.arch[..] { + "x86" => x86::compute_rust_abi_info(cx, self, abi), + "riscv32" | "riscv64" => riscv::compute_rust_abi_info(cx, self, abi), + "loongarch64" => loongarch::compute_rust_abi_info(cx, self, abi), + _ => {} + }; + + for (arg_idx, arg) in self + .args + .iter_mut() + .enumerate() + .map(|(idx, arg)| (Some(idx), arg)) + .chain(iter::once((None, &mut self.ret))) + { + if arg.is_ignore() { + continue; + } + + if arg_idx.is_none() && arg.layout.size > Pointer(AddressSpace::DATA).size(cx) * 2 { + // Return values larger than 2 registers using a return area + // pointer. LLVM and Cranelift disagree about how to return + // values that don't fit in the registers designated for return + // values. LLVM will force the entire return value to be passed + // by return area pointer, while Cranelift will look at each IR level + // return value independently and decide to pass it in a + // register or not, which would result in the return value + // being passed partially in registers and partially through a + // return area pointer. + // + // While Cranelift may need to be fixed as the LLVM behavior is + // generally more correct with respect to the surface language, + // forcing this behavior in rustc itself makes it easier for + // other backends to conform to the Rust ABI and for the C ABI + // rustc already handles this behavior anyway. + // + // In addition LLVM's decision to pass the return value in + // registers or using a return area pointer depends on how + // exactly the return type is lowered to an LLVM IR type. For + // example `Option<u128>` can be lowered as `{ i128, i128 }` + // in which case the x86_64 backend would use a return area + // pointer, or it could be passed as `{ i32, i128 }` in which + // case the x86_64 backend would pass it in registers by taking + // advantage of an LLVM ABI extension that allows using 3 + // registers for the x86_64 sysv call conv rather than the + // officially specified 2 registers. + // + // FIXME: Technically we should look at the amount of available + // return registers rather than guessing that there are 2 + // registers for return values. In practice only a couple of + // architectures have less than 2 return registers. None of + // which supported by Cranelift. + // + // NOTE: This adjustment is only necessary for the Rust ABI as + // for other ABI's the calling convention implementations in + // rustc_target already ensure any return value which doesn't + // fit in the available amount of return registers is passed in + // the right way for the current target. + arg.make_indirect(); + continue; + } + + match arg.layout.abi { + Abi::Aggregate { .. } => {} + + // This is a fun case! The gist of what this is doing is + // that we want callers and callees to always agree on the + // ABI of how they pass SIMD arguments. If we were to *not* + // make these arguments indirect then they'd be immediates + // in LLVM, which means that they'd used whatever the + // appropriate ABI is for the callee and the caller. That + // means, for example, if the caller doesn't have AVX + // enabled but the callee does, then passing an AVX argument + // across this boundary would cause corrupt data to show up. + // + // This problem is fixed by unconditionally passing SIMD + // arguments through memory between callers and callees + // which should get them all to agree on ABI regardless of + // target feature sets. Some more information about this + // issue can be found in #44367. + // + // Note that the intrinsic ABI is exempt here as + // that's how we connect up to LLVM and it's unstable + // anyway, we control all calls to it in libstd. + Abi::Vector { .. } if abi != SpecAbi::RustIntrinsic && spec.simd_types_indirect => { + arg.make_indirect(); + continue; + } + + _ => continue, + } + // Compute `Aggregate` ABI. + + let is_indirect_not_on_stack = + matches!(arg.mode, PassMode::Indirect { on_stack: false, .. }); + assert!(is_indirect_not_on_stack); + + let size = arg.layout.size; + if !arg.layout.is_unsized() && size <= Pointer(AddressSpace::DATA).size(cx) { + // We want to pass small aggregates as immediates, but using + // an LLVM aggregate type for this leads to bad optimizations, + // so we pick an appropriately sized integer type instead. + arg.cast_to(Reg { kind: RegKind::Integer, size }); + } + } + } } impl FromStr for Conv { diff --git a/compiler/rustc_target/src/callconv/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs index be6bc701b49..f96169e6a61 100644 --- a/compiler/rustc_target/src/callconv/riscv.rs +++ b/compiler/rustc_target/src/callconv/riscv.rs @@ -7,6 +7,7 @@ use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform}; use crate::abi::{self, Abi, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; use crate::spec::HasTargetSpec; +use crate::spec::abi::Abi as SpecAbi; #[derive(Copy, Clone)] enum RegPassKind { @@ -365,3 +366,29 @@ where ); } } + +pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: SpecAbi) +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec, +{ + if abi == SpecAbi::RustIntrinsic { + return; + } + + let xlen = cx.data_layout().pointer_size.bits(); + + for arg in fn_abi.args.iter_mut() { + if arg.is_ignore() { + continue; + } + + // LLVM integers types do not differentiate between signed or unsigned integers. + // Some RISC-V instructions do not have a `.w` suffix version, they use all the + // XLEN bits. By explicitly setting the `signext` or `zeroext` attribute + // according to signedness to avoid unnecessary integer extending instructions. + // + // See https://github.com/rust-lang/rust/issues/114508 for details. + extend_integer_width(arg, xlen); + } +} diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index d9af83d3205..e907beecb38 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -1,6 +1,9 @@ use crate::abi::call::{ArgAttribute, FnAbi, PassMode, Reg, RegKind}; -use crate::abi::{Abi, Align, HasDataLayout, TyAbiInterface, TyAndLayout}; +use crate::abi::{ + Abi, AddressSpace, Align, Float, HasDataLayout, Pointer, TyAbiInterface, TyAndLayout, +}; use crate::spec::HasTargetSpec; +use crate::spec::abi::Abi as SpecAbi; #[derive(PartialEq)] pub(crate) enum Flavor { @@ -8,7 +11,12 @@ pub(crate) enum Flavor { FastcallOrVectorcall, } -pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, flavor: Flavor) +pub(crate) struct X86Options { + pub flavor: Flavor, + pub regparm: Option<u32>, +} + +pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, @@ -128,58 +136,109 @@ where } } - if flavor == Flavor::FastcallOrVectorcall { - // Mark arguments as InReg like clang does it, - // so our fastcall/vectorcall is compatible with C/C++ fastcall/vectorcall. + fill_inregs(cx, fn_abi, opts, false); +} - // Clang reference: lib/CodeGen/TargetInfo.cpp - // See X86_32ABIInfo::shouldPrimitiveUseInReg(), X86_32ABIInfo::updateFreeRegs() +pub(crate) fn fill_inregs<'a, Ty, C>( + cx: &C, + fn_abi: &mut FnAbi<'a, Ty>, + opts: X86Options, + rust_abi: bool, +) where + Ty: TyAbiInterface<'a, C> + Copy, +{ + if opts.flavor != Flavor::FastcallOrVectorcall && opts.regparm.is_none_or(|x| x == 0) { + return; + } + // Mark arguments as InReg like clang does it, + // so our fastcall/vectorcall is compatible with C/C++ fastcall/vectorcall. - // IsSoftFloatABI is only set to true on ARM platforms, - // which in turn can't be x86? + // Clang reference: lib/CodeGen/TargetInfo.cpp + // See X86_32ABIInfo::shouldPrimitiveUseInReg(), X86_32ABIInfo::updateFreeRegs() - let mut free_regs = 2; + // IsSoftFloatABI is only set to true on ARM platforms, + // which in turn can't be x86? - for arg in fn_abi.args.iter_mut() { - let attrs = match arg.mode { - PassMode::Ignore - | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { - continue; - } - PassMode::Direct(ref mut attrs) => attrs, - PassMode::Pair(..) - | PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } - | PassMode::Cast { .. } => { - unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode) - } - }; + // 2 for fastcall/vectorcall, regparm limited by 3 otherwise + let mut free_regs = opts.regparm.unwrap_or(2).into(); - // At this point we know this must be a primitive of sorts. - let unit = arg.layout.homogeneous_aggregate(cx).unwrap().unit().unwrap(); - assert_eq!(unit.size, arg.layout.size); - if unit.kind == RegKind::Float { + // For types generating PassMode::Cast, InRegs will not be set. + // Maybe, this is a FIXME + let has_casts = fn_abi.args.iter().any(|arg| matches!(arg.mode, PassMode::Cast { .. })); + if has_casts && rust_abi { + return; + } + + for arg in fn_abi.args.iter_mut() { + let attrs = match arg.mode { + PassMode::Ignore | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { continue; } + PassMode::Direct(ref mut attrs) => attrs, + PassMode::Pair(..) + | PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } + | PassMode::Cast { .. } => { + unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode) + } + }; - let size_in_regs = (arg.layout.size.bits() + 31) / 32; + // At this point we know this must be a primitive of sorts. + let unit = arg.layout.homogeneous_aggregate(cx).unwrap().unit().unwrap(); + assert_eq!(unit.size, arg.layout.size); + if matches!(unit.kind, RegKind::Float | RegKind::Vector) { + continue; + } - if size_in_regs == 0 { - continue; - } + let size_in_regs = (arg.layout.size.bits() + 31) / 32; - if size_in_regs > free_regs { - break; - } + if size_in_regs == 0 { + continue; + } - free_regs -= size_in_regs; + if size_in_regs > free_regs { + break; + } - if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer { - attrs.set(ArgAttribute::InReg); - } + free_regs -= size_in_regs; - if free_regs == 0 { - break; + if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer { + attrs.set(ArgAttribute::InReg); + } + + if free_regs == 0 { + break; + } + } +} + +pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: SpecAbi) +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec, +{ + // Avoid returning floats in x87 registers on x86 as loading and storing from x87 + // registers will quiet signalling NaNs. Also avoid using SSE registers since they + // are not always available (depending on target features). + if !fn_abi.ret.is_ignore() + // Intrinsics themselves are not actual "real" functions, so theres no need to change their ABIs. + && abi != SpecAbi::RustIntrinsic + { + let has_float = match fn_abi.ret.layout.abi { + Abi::Scalar(s) => matches!(s.primitive(), Float(_)), + Abi::ScalarPair(s1, s2) => { + matches!(s1.primitive(), Float(_)) || matches!(s2.primitive(), Float(_)) + } + _ => false, // anyway not passed via registers on x86 + }; + if has_float { + if fn_abi.ret.layout.size <= Pointer(AddressSpace::DATA).size(cx) { + // Same size or smaller than pointer, return in a register. + fn_abi.ret.cast_to(Reg { kind: RegKind::Integer, size: fn_abi.ret.layout.size }); + } else { + // Larger than a pointer, return indirectly. + fn_abi.ret.make_indirect(); } + return; } } } diff --git a/compiler/rustc_target/src/spec/abi/mod.rs b/compiler/rustc_target/src/spec/abi/mod.rs index cac0cf9959d..c2095155afa 100644 --- a/compiler/rustc_target/src/spec/abi/mod.rs +++ b/compiler/rustc_target/src/spec/abi/mod.rs @@ -184,7 +184,7 @@ pub fn is_enabled( ) -> Result<(), AbiDisabled> { let s = is_stable(name); if let Err(AbiDisabled::Unstable { feature, .. }) = s { - if features.active(feature) || span.allows_unstable(feature) { + if features.enabled(feature) || span.allows_unstable(feature) { return Ok(()); } } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 82e11a3afce..f4cbe47e0f3 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1803,6 +1803,7 @@ supported_targets! { ("wasm32-unknown-emscripten", wasm32_unknown_emscripten), ("wasm32-unknown-unknown", wasm32_unknown_unknown), + ("wasm32v1-none", wasm32v1_none), ("wasm32-wasi", wasm32_wasi), ("wasm32-wasip1", wasm32_wasip1), ("wasm32-wasip2", wasm32_wasip2), @@ -2096,6 +2097,18 @@ 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 { + /// On x86-32 targets, the regparm N causes the compiler to pass arguments + /// in registers EAX, EDX, and ECX instead of on the stack. + pub regparm: Option<u32>, +} + +pub trait HasX86AbiOpt { + fn x86_abi_opt(&self) -> X86Abi; +} + type StaticCow<T> = Cow<'static, T>; /// Optional aspects of a target specification. @@ -2515,6 +2528,13 @@ fn add_link_args(link_args: &mut LinkArgs, flavor: LinkerFlavor, args: &[&'stati } impl TargetOptions { + pub fn supports_comdat(&self) -> bool { + // XCOFF and MachO don't support COMDAT. + !self.is_like_aix && !self.is_like_osx + } +} + +impl TargetOptions { fn link_args(flavor: LinkerFlavor, args: &[&'static str]) -> LinkArgs { let mut link_args = LinkArgs::new(); add_link_args(&mut link_args, flavor, args); @@ -2750,10 +2770,9 @@ impl Target { } } - /// Returns a None if the UNSUPPORTED_CALLING_CONVENTIONS lint should be emitted - pub fn is_abi_supported(&self, abi: Abi) -> Option<bool> { + pub fn is_abi_supported(&self, abi: Abi) -> bool { use Abi::*; - Some(match abi { + match abi { Rust | C { .. } | System { .. } @@ -2812,9 +2831,9 @@ impl Target { // architectures for which these calling conventions are actually well defined. Stdcall { .. } | Fastcall { .. } if self.arch == "x86" => true, Vectorcall { .. } if ["x86", "x86_64"].contains(&&self.arch[..]) => true, - // Return a `None` for other cases so that we know to emit a future compat lint. - Stdcall { .. } | Fastcall { .. } | Vectorcall { .. } => return None, - }) + // Reject these calling conventions everywhere else. + Stdcall { .. } | Fastcall { .. } | Vectorcall { .. } => false, + } } /// Minimum integer size in bits that this target can perform atomic diff --git a/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs b/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs index 2519b935c03..7648f81fd4d 100644 --- a/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs +++ b/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs @@ -21,6 +21,7 @@ pub(crate) fn target() -> Target { "-Vgcc_ntox86_cxx", ]), env: "nto70".into(), + vendor: "pc".into(), stack_probes: StackProbeType::Inline, ..base::nto_qnx::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs index 68d51193564..603c0f99314 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { code_model: Some(CodeModel::Medium), cpu: "generic".into(), - features: "+f,+d".into(), + features: "+f,+d,+lsx".into(), llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), supported_sanitizers: SanitizerSet::ADDRESS diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs index 25d3559d920..d7044dde0f1 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { code_model: Some(CodeModel::Medium), cpu: "generic".into(), - features: "+f,+d".into(), + features: "+f,+d,+lsx".into(), llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), crt_static_default: false, diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs index 785c58f3ab7..12e026294cf 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs @@ -1,4 +1,4 @@ -use crate::spec::{SanitizerSet, Target, TargetOptions, base}; +use crate::spec::{CodeModel, SanitizerSet, Target, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,6 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), arch: "loongarch64".into(), options: TargetOptions { + code_model: Some(CodeModel::Medium), cpu: "generic".into(), features: "+f,+d".into(), llvm_abiname: "lp64d".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32i_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32i_unknown_none_elf.rs index 1995de9dd2d..0e0e13fd1d8 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32i_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32i_unknown_none_elf.rs @@ -20,6 +20,7 @@ pub(crate) fn target() -> Target { max_atomic_width: Some(32), atomic_cas: false, features: "+forced-atomics".into(), + llvm_abiname: "ilp32".into(), panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs index bd37cf80b48..669c1702fda 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs @@ -29,6 +29,7 @@ pub(crate) fn target() -> Target { atomic_cas: true, features: "+m".into(), + llvm_abiname: "ilp32".into(), executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/spec/targets/riscv32im_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32im_unknown_none_elf.rs index 32df30ddb5e..477a6c0e9eb 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32im_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32im_unknown_none_elf.rs @@ -20,6 +20,7 @@ pub(crate) fn target() -> Target { max_atomic_width: Some(32), atomic_cas: false, features: "+m,+forced-atomics".into(), + llvm_abiname: "ilp32".into(), panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/targets/riscv32ima_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32ima_unknown_none_elf.rs index 61c887031bc..68146788d20 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32ima_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32ima_unknown_none_elf.rs @@ -19,6 +19,7 @@ pub(crate) fn target() -> Target { cpu: "generic-rv32".into(), max_atomic_width: Some(32), features: "+m,+a".into(), + llvm_abiname: "ilp32".into(), panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/targets/riscv32imac_esp_espidf.rs b/compiler/rustc_target/src/spec/targets/riscv32imac_esp_espidf.rs index 9795af56569..e12c3af6f8f 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imac_esp_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imac_esp_espidf.rs @@ -27,6 +27,7 @@ pub(crate) fn target() -> Target { atomic_cas: true, features: "+m,+a,+c".into(), + llvm_abiname: "ilp32".into(), panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_none_elf.rs index cc28198e3ff..adc76f3cdb5 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_none_elf.rs @@ -19,6 +19,7 @@ pub(crate) fn target() -> Target { cpu: "generic-rv32".into(), max_atomic_width: Some(32), features: "+m,+a,+c".into(), + llvm_abiname: "ilp32".into(), panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs index 62397dcae9a..31c9180c509 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs @@ -21,6 +21,7 @@ pub(crate) fn target() -> Target { cpu: "generic-rv32".into(), max_atomic_width: Some(32), features: "+m,+a,+c".into(), + llvm_abiname: "ilp32".into(), panic_strategy: PanicStrategy::Unwind, relocation_model: RelocModel::Static, ..Default::default() diff --git a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_xous_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_xous_elf.rs index 0eaabc60cbc..88d112a012d 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_xous_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_xous_elf.rs @@ -20,6 +20,7 @@ pub(crate) fn target() -> Target { cpu: "generic-rv32".into(), max_atomic_width: Some(32), features: "+m,+a,+c".into(), + llvm_abiname: "ilp32".into(), panic_strategy: PanicStrategy::Unwind, relocation_model: RelocModel::Static, ..Default::default() diff --git a/compiler/rustc_target/src/spec/targets/riscv32imc_esp_espidf.rs b/compiler/rustc_target/src/spec/targets/riscv32imc_esp_espidf.rs index 9ae84ace457..cec97f86538 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imc_esp_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imc_esp_espidf.rs @@ -30,6 +30,7 @@ pub(crate) fn target() -> Target { atomic_cas: true, features: "+m,+c".into(), + llvm_abiname: "ilp32".into(), panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_none_elf.rs index 0ae49debc3a..0e00fc69b41 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_none_elf.rs @@ -20,6 +20,7 @@ pub(crate) fn target() -> Target { max_atomic_width: Some(32), atomic_cas: false, features: "+m,+c,+forced-atomics".into(), + llvm_abiname: "ilp32".into(), panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs index 5f3917edf70..e86549806dd 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs @@ -21,6 +21,7 @@ pub(crate) fn target() -> Target { cpu: "generic-rv32".into(), max_atomic_width: Some(32), features: "+m,+c".into(), + llvm_abiname: "ilp32".into(), panic_strategy: PanicStrategy::Unwind, relocation_model: RelocModel::Static, ..Default::default() diff --git a/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_none_elf.rs index d514f8efcd0..d62ecc07a5d 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_none_elf.rs @@ -22,6 +22,7 @@ pub(crate) fn target() -> Target { cpu: "generic-rv64".into(), max_atomic_width: Some(64), features: "+m,+a,+c".into(), + llvm_abiname: "lp64".into(), panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, code_model: Some(CodeModel::Medium), diff --git a/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs index a737e46de1b..9c181665581 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs @@ -24,6 +24,7 @@ pub(crate) fn target() -> Target { cpu: "generic-rv64".into(), max_atomic_width: Some(64), features: "+m,+a,+c".into(), + llvm_abiname: "lp64".into(), panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, code_model: Some(CodeModel::Medium), diff --git a/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs b/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs new file mode 100644 index 00000000000..bf35ae009c6 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs @@ -0,0 +1,51 @@ +//! A "bare wasm" target representing a WebAssembly output that does not import +//! anything from its environment and also specifies an _upper_ bound on the set +//! of WebAssembly proposals that are supported. +//! +//! It's equivalent to the `wasm32-unknown-unknown` target with the additional +//! flags `-Ctarget-cpu=mvp` and `-Ctarget-feature=+mutable-globals`. This +//! enables just the features specified in <https://www.w3.org/TR/wasm-core-1/> +//! +//! This is a _separate target_ because using `wasm32-unknown-unknown` with +//! those target flags doesn't automatically rebuild libcore / liballoc with +//! them, and in order to get those libraries rebuilt you need to use the +//! nightly Rust feature `-Zbuild-std`. This target is for people who want to +//! use stable Rust, and target a stable set pf WebAssembly features. + +use crate::spec::{Cc, LinkerFlavor, Target, base}; + +pub(crate) fn target() -> Target { + let mut options = base::wasm::options(); + options.os = "none".into(); + + // WebAssembly 1.0 shipped in 2019 and included exactly one proposal + // after the initial "MVP" feature set: "mutable-globals". + options.cpu = "mvp".into(); + options.features = "+mutable-globals".into(); + + options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::No), &[ + // For now this target just never has an entry symbol no matter the output + // type, so unconditionally pass this. + "--no-entry", + ]); + options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::Yes), &[ + // Make sure clang uses LLD as its linker and is configured appropriately + // otherwise + "--target=wasm32-unknown-unknown", + "-Wl,--no-entry", + ]); + + Target { + llvm_target: "wasm32-unknown-unknown".into(), + metadata: crate::spec::TargetMetadata { + description: Some("WebAssembly".into()), + tier: Some(2), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(), + arch: "wasm32".into(), + options, + } +} diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs index 1aa82494a49..245a5f06765 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs @@ -21,6 +21,7 @@ pub(crate) fn target() -> Target { "-Vgcc_ntox86_64_cxx", ]), env: "nto71".into(), + vendor: "pc".into(), ..base::nto_qnx::opts() }, } diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index fc5bd846e02..cc5931be860 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -121,7 +121,13 @@ impl Target { // Check dynamic linking stuff // BPF: when targeting user space vms (like rbpf), those can load dynamic libraries. // hexagon: when targeting QuRT, that OS can load dynamic libraries. - if self.os == "none" && (self.arch != "bpf" && self.arch != "hexagon") { + // wasm{32,64}: dynamic linking is inherent in the definition of the VM. + if self.os == "none" + && (self.arch != "bpf" + && self.arch != "hexagon" + && self.arch != "wasm32" + && self.arch != "wasm64") + { assert!(!self.dynamic_linking); } if self.only_cdylib @@ -152,6 +158,17 @@ impl Target { if self.crt_static_default || self.crt_static_allows_dylibs { assert!(self.crt_static_respected); } + + // Check that RISC-V targets always specify which ABI they use. + match &*self.arch { + "riscv32" => { + assert_matches!(&*self.llvm_abiname, "ilp32" | "ilp32f" | "ilp32d" | "ilp32e") + } + "riscv64" => { + assert_matches!(&*self.llvm_abiname, "lp64" | "lp64f" | "lp64d" | "lp64q") + } + _ => {} + } } // Add your target to the whitelist if it has `std` library diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index e92366d5c5c..910cafbdf3b 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -316,7 +316,7 @@ const X86_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), ("lzcnt", Stable, &[]), ("movbe", Stable, &[]), - ("pclmulqdq", Stable, &[]), + ("pclmulqdq", Stable, &["sse2"]), ("popcnt", Stable, &[]), ("prfchw", Unstable(sym::prfchw_target_feature), &[]), ("rdrand", Stable, &[]), diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index df5800ab58a..b9a569d25e3 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -75,6 +75,7 @@ use crate::errors::{ObligationCauseFailureCode, TypeErrorAdditionalDiags}; use crate::infer; use crate::infer::relate::{self, RelateResult, TypeRelation}; use crate::infer::{InferCtxt, TypeTrace, ValuePairs}; +use crate::solve::deeply_normalize_for_diagnostics; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, }; @@ -145,21 +146,31 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { pub fn report_mismatched_types( &self, cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, expected: Ty<'tcx>, actual: Ty<'tcx>, err: TypeError<'tcx>, ) -> Diag<'a> { - self.report_and_explain_type_error(TypeTrace::types(cause, true, expected, actual), err) + self.report_and_explain_type_error( + TypeTrace::types(cause, true, expected, actual), + param_env, + err, + ) } pub fn report_mismatched_consts( &self, cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, expected: ty::Const<'tcx>, actual: ty::Const<'tcx>, err: TypeError<'tcx>, ) -> Diag<'a> { - self.report_and_explain_type_error(TypeTrace::consts(cause, true, expected, actual), err) + self.report_and_explain_type_error( + TypeTrace::consts(cause, true, expected, actual), + param_env, + err, + ) } pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { @@ -1133,7 +1144,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { diag: &mut Diag<'_>, cause: &ObligationCause<'tcx>, secondary_span: Option<(Span, Cow<'static, str>, bool)>, - mut values: Option<ValuePairs<'tcx>>, + mut values: Option<ty::ParamEnvAnd<'tcx, ValuePairs<'tcx>>>, terr: TypeError<'tcx>, prefer_label: bool, ) { @@ -1241,8 +1252,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } let (expected_found, exp_found, is_simple_error, values) = match values { None => (None, Mismatch::Fixed("type"), false, None), - Some(values) => { - let values = self.resolve_vars_if_possible(values); + Some(ty::ParamEnvAnd { param_env, value: values }) => { + let mut values = self.resolve_vars_if_possible(values); + if self.next_trait_solver() { + values = deeply_normalize_for_diagnostics(self, param_env, values); + } let (is_simple_error, exp_found) = match values { ValuePairs::Terms(ExpectedFound { expected, found }) => { match (expected.unpack(), found.unpack()) { @@ -1773,6 +1787,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { pub fn report_and_explain_type_error( &self, trace: TypeTrace<'tcx>, + param_env: ty::ParamEnv<'tcx>, terr: TypeError<'tcx>, ) -> Diag<'a> { debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr); @@ -1784,7 +1799,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.type_error_additional_suggestions(&trace, terr), ); let mut diag = self.dcx().create_err(failure_code); - self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false); + self.note_type_err( + &mut diag, + &trace.cause, + None, + Some(param_env.and(trace.values)), + terr, + false, + ); diag } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 62204f63dd0..0cf7c43beb5 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -894,7 +894,7 @@ fn foo(&self) -> Self::T { String::new() } // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting. let trait_bounds = bounds.iter().filter_map(|bound| match bound { - hir::GenericBound::Trait(ptr) if ptr.modifiers == hir::TraitBoundModifier::None => { + hir::GenericBound::Trait(ptr) if ptr.modifiers == hir::TraitBoundModifiers::NONE => { Some(ptr) } _ => None, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 94610a9e0e6..833358b2e14 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -295,7 +295,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut err = match origin { infer::Subtype(box trace) => { let terr = TypeError::RegionsDoesNotOutlive(sup, sub); - let mut err = self.report_and_explain_type_error(trace, terr); + let mut err = self.report_and_explain_type_error( + trace, + self.tcx.param_env(generic_param_scope), + terr, + ); match (*sub, *sup) { (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} (ty::RePlaceholder(_), _) => { @@ -646,7 +650,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } infer::Subtype(box trace) => { let terr = TypeError::RegionsPlaceholderMismatch; - return self.report_and_explain_type_error(trace, terr); + return self.report_and_explain_type_error( + trace, + self.tcx.param_env(generic_param_scope), + terr, + ); } _ => { return self.report_concrete_failure( diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 5076152dbff..b7e2ed391cd 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -156,8 +156,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (leaf_trait_predicate, &obligation) }; - let (main_trait_predicate, leaf_trait_predicate, predicate_constness) = self.get_effects_trait_pred_override(main_trait_predicate, leaf_trait_predicate, span); - let main_trait_ref = main_trait_predicate.to_poly_trait_ref(); let leaf_trait_ref = leaf_trait_predicate.to_poly_trait_ref(); @@ -228,7 +226,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let err_msg = self.get_standard_error_message( main_trait_predicate, message, - predicate_constness, + None, append_const_msg, post_message, ); @@ -289,13 +287,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } - if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Drop) - && matches!(predicate_constness, ty::BoundConstness::ConstIfConst | ty::BoundConstness::Const) - { - err.note("`~const Drop` was renamed to `~const Destruct`"); - err.note("See <https://github.com/rust-lang/rust/pull/94901> for more details"); - } - let explanation = get_explanation_based_on_obligation( self.tcx, &obligation, @@ -541,6 +532,29 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => { + // FIXME(effects): We should recompute the predicate with `~const` + // if it's `const`, and if it holds, explain that this bound only + // *conditionally* holds. If that fails, we should also do selection + // to drill this down to an impl or built-in source, so we can + // point at it and explain that while the trait *is* implemented, + // that implementation is not const. + let err_msg = self.get_standard_error_message( + bound_predicate.rebind(ty::TraitPredicate { + trait_ref: predicate.trait_ref, + polarity: ty::PredicatePolarity::Positive, + }), + None, + Some(match predicate.host { + ty::HostPolarity::Maybe => ty::BoundConstness::ConstIfConst, + ty::HostPolarity::Const => ty::BoundConstness::Const, + }), + None, + String::new(), + ); + struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg) + } + ty::PredicateKind::Subtype(predicate) => { // Errors for Subtype predicates show up as // `FulfillmentErrorCode::SubtypeError`, @@ -1277,19 +1291,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let normalized_term = ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); - let is_normalized_term_expected = !matches!( - obligation.cause.code().peel_derives(), - ObligationCauseCode::WhereClause(..) - | ObligationCauseCode::WhereClauseInExpr(..) - | ObligationCauseCode::Coercion { .. } - ); - - let (expected, actual) = if is_normalized_term_expected { - (normalized_term, data.term) - } else { - (data.term, normalized_term) - }; - // constrain inference variables a bit more to nested obligations from normalize so // we can have more helpful errors. // @@ -1298,12 +1299,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let _ = ocx.select_where_possible(); if let Err(new_err) = - ocx.eq(&obligation.cause, obligation.param_env, expected, actual) + ocx.eq(&obligation.cause, obligation.param_env, data.term, normalized_term) { ( Some(( data.projection_term, - is_normalized_term_expected, self.resolve_vars_if_possible(normalized_term), data.term, )), @@ -1348,7 +1348,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { derive_better_type_error(lhs, rhs) { ( - Some((lhs, true, self.resolve_vars_if_possible(expected_term), rhs)), + Some((lhs, self.resolve_vars_if_possible(expected_term), rhs)), better_type_err, ) } else if let Some(rhs) = rhs.to_alias_term() @@ -1356,7 +1356,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { derive_better_type_error(rhs, lhs) { ( - Some((rhs, true, self.resolve_vars_if_possible(expected_term), lhs)), + Some((rhs, self.resolve_vars_if_possible(expected_term), lhs)), better_type_err, ) } else { @@ -1367,7 +1367,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; let msg = values - .and_then(|(predicate, _, normalized_term, expected_term)| { + .and_then(|(predicate, normalized_term, expected_term)| { self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term) }) .unwrap_or_else(|| { @@ -1444,12 +1444,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &mut diag, &obligation.cause, secondary_span, - values.map(|(_, is_normalized_ty_expected, normalized_ty, expected_ty)| { - infer::ValuePairs::Terms(ExpectedFound::new( - is_normalized_ty_expected, - normalized_ty, + values.map(|(_, normalized_ty, expected_ty)| { + obligation.param_env.and(infer::ValuePairs::Terms(ExpectedFound::new( + true, expected_ty, - )) + normalized_ty, + ))) }), err, false, @@ -2209,7 +2209,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, trait_predicate: ty::PolyTraitPredicate<'tcx>, message: Option<String>, - predicate_constness: ty::BoundConstness, + predicate_constness: Option<ty::BoundConstness>, append_const_msg: Option<AppendConstMessage>, post_message: String, ) -> String { @@ -2217,19 +2217,21 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .and_then(|cannot_do_this| { match (predicate_constness, append_const_msg) { // do nothing if predicate is not const - (ty::BoundConstness::NotConst, _) => Some(cannot_do_this), + (None, _) => Some(cannot_do_this), // suggested using default post message ( - ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, + Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst), Some(AppendConstMessage::Default), ) => Some(format!("{cannot_do_this} in const contexts")), // overridden post message ( - ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, + Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst), Some(AppendConstMessage::Custom(custom_msg, _)), ) => Some(format!("{cannot_do_this}{custom_msg}")), // fallback to generic message - (ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, None) => None, + (Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst), None) => { + None + } } }) .unwrap_or_else(|| { @@ -2247,169 +2249,143 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span: Span, ) -> GetSafeTransmuteErrorAndReason { use rustc_transmute::Answer; + self.probe(|_| { + // We don't assemble a transmutability candidate for types that are generic + // and we should have ambiguity for types that still have non-region infer. + if obligation.predicate.has_non_region_param() || obligation.has_non_region_infer() { + return GetSafeTransmuteErrorAndReason::Default; + } - // We don't assemble a transmutability candidate for types that are generic - // and we should have ambiguity for types that still have non-region infer. - if obligation.predicate.has_non_region_param() || obligation.has_non_region_infer() { - return GetSafeTransmuteErrorAndReason::Default; - } + // Erase regions because layout code doesn't particularly care about regions. + let trait_ref = + self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref)); - // Erase regions because layout code doesn't particularly care about regions. - let trait_ref = - self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref)); + let src_and_dst = rustc_transmute::Types { + dst: trait_ref.args.type_at(0), + src: trait_ref.args.type_at(1), + }; - let src_and_dst = rustc_transmute::Types { - dst: trait_ref.args.type_at(0), - src: trait_ref.args.type_at(1), - }; - let Some(assume) = rustc_transmute::Assume::from_const( - self.infcx.tcx, - obligation.param_env, - trait_ref.args.const_at(2), - ) else { - self.dcx().span_delayed_bug( - span, - "Unable to construct rustc_transmute::Assume where it was previously possible", - ); - return GetSafeTransmuteErrorAndReason::Silent; - }; + let ocx = ObligationCtxt::new(self); + let Ok(assume) = ocx.structurally_normalize_const( + &obligation.cause, + obligation.param_env, + trait_ref.args.const_at(2), + ) else { + self.dcx().span_delayed_bug( + span, + "Unable to construct rustc_transmute::Assume where it was previously possible", + ); + return GetSafeTransmuteErrorAndReason::Silent; + }; - let dst = trait_ref.args.type_at(0); - let src = trait_ref.args.type_at(1); + let Some(assume) = + rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, assume) + else { + self.dcx().span_delayed_bug( + span, + "Unable to construct rustc_transmute::Assume where it was previously possible", + ); + return GetSafeTransmuteErrorAndReason::Silent; + }; - let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`"); + let dst = trait_ref.args.type_at(0); + let src = trait_ref.args.type_at(1); + let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`"); - match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( - obligation.cause, - src_and_dst, - assume, - ) { - Answer::No(reason) => { - let safe_transmute_explanation = match reason { - rustc_transmute::Reason::SrcIsNotYetSupported => { - format!("analyzing the transmutability of `{src}` is not yet supported") - } + match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( + obligation.cause, + src_and_dst, + assume, + ) { + Answer::No(reason) => { + let safe_transmute_explanation = match reason { + rustc_transmute::Reason::SrcIsNotYetSupported => { + format!("analyzing the transmutability of `{src}` is not yet supported") + } - rustc_transmute::Reason::DstIsNotYetSupported => { - format!("analyzing the transmutability of `{dst}` is not yet supported") - } + rustc_transmute::Reason::DstIsNotYetSupported => { + format!("analyzing the transmutability of `{dst}` is not yet supported") + } - rustc_transmute::Reason::DstIsBitIncompatible => { - format!("at least one value of `{src}` isn't a bit-valid value of `{dst}`") - } + rustc_transmute::Reason::DstIsBitIncompatible => { + format!( + "at least one value of `{src}` isn't a bit-valid value of `{dst}`" + ) + } - rustc_transmute::Reason::DstUninhabited => { - format!("`{dst}` is uninhabited") - } + rustc_transmute::Reason::DstUninhabited => { + format!("`{dst}` is uninhabited") + } - rustc_transmute::Reason::DstMayHaveSafetyInvariants => { - format!("`{dst}` may carry safety invariants") - } - rustc_transmute::Reason::DstIsTooBig => { - format!("the size of `{src}` is smaller than the size of `{dst}`") - } - rustc_transmute::Reason::DstRefIsTooBig { src, dst } => { - let src_size = src.size; - let dst_size = dst.size; - format!( - "the referent size of `{src}` ({src_size} bytes) is smaller than that of `{dst}` ({dst_size} bytes)" - ) - } - rustc_transmute::Reason::SrcSizeOverflow => { - format!( - "values of the type `{src}` are too big for the target architecture" - ) - } - rustc_transmute::Reason::DstSizeOverflow => { - format!( - "values of the type `{dst}` are too big for the target architecture" - ) - } - rustc_transmute::Reason::DstHasStricterAlignment { - src_min_align, - dst_min_align, - } => { - format!( - "the minimum alignment of `{src}` ({src_min_align}) should be greater than that of `{dst}` ({dst_min_align})" - ) - } - rustc_transmute::Reason::DstIsMoreUnique => { - format!("`{src}` is a shared reference, but `{dst}` is a unique reference") - } - // Already reported by rustc - rustc_transmute::Reason::TypeError => { - return GetSafeTransmuteErrorAndReason::Silent; - } - rustc_transmute::Reason::SrcLayoutUnknown => { - format!("`{src}` has an unknown layout") - } - rustc_transmute::Reason::DstLayoutUnknown => { - format!("`{dst}` has an unknown layout") + rustc_transmute::Reason::DstMayHaveSafetyInvariants => { + format!("`{dst}` may carry safety invariants") + } + rustc_transmute::Reason::DstIsTooBig => { + format!("the size of `{src}` is smaller than the size of `{dst}`") + } + rustc_transmute::Reason::DstRefIsTooBig { src, dst } => { + let src_size = src.size; + let dst_size = dst.size; + format!( + "the referent size of `{src}` ({src_size} bytes) \ + is smaller than that of `{dst}` ({dst_size} bytes)" + ) + } + rustc_transmute::Reason::SrcSizeOverflow => { + format!( + "values of the type `{src}` are too big for the target architecture" + ) + } + rustc_transmute::Reason::DstSizeOverflow => { + format!( + "values of the type `{dst}` are too big for the target architecture" + ) + } + rustc_transmute::Reason::DstHasStricterAlignment { + src_min_align, + dst_min_align, + } => { + format!( + "the minimum alignment of `{src}` ({src_min_align}) should \ + be greater than that of `{dst}` ({dst_min_align})" + ) + } + rustc_transmute::Reason::DstIsMoreUnique => { + format!( + "`{src}` is a shared reference, but `{dst}` is a unique reference" + ) + } + // Already reported by rustc + rustc_transmute::Reason::TypeError => { + return GetSafeTransmuteErrorAndReason::Silent; + } + rustc_transmute::Reason::SrcLayoutUnknown => { + format!("`{src}` has an unknown layout") + } + rustc_transmute::Reason::DstLayoutUnknown => { + format!("`{dst}` has an unknown layout") + } + }; + GetSafeTransmuteErrorAndReason::Error { + err_msg, + safe_transmute_explanation: Some(safe_transmute_explanation), } - }; - GetSafeTransmuteErrorAndReason::Error { - err_msg, - safe_transmute_explanation: Some(safe_transmute_explanation), } + // Should never get a Yes at this point! We already ran it before, and did not get a Yes. + Answer::Yes => span_bug!( + span, + "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes", + ), + // Reached when a different obligation (namely `Freeze`) causes the + // transmutability analysis to fail. In this case, silence the + // transmutability error message in favor of that more specific + // error. + Answer::If(_) => GetSafeTransmuteErrorAndReason::Error { + err_msg, + safe_transmute_explanation: None, + }, } - // Should never get a Yes at this point! We already ran it before, and did not get a Yes. - Answer::Yes => span_bug!( - span, - "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes", - ), - // Reached when a different obligation (namely `Freeze`) causes the - // transmutability analysis to fail. In this case, silence the - // transmutability error message in favor of that more specific - // error. - Answer::If(_) => { - GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation: None } - } - } - } - - /// For effects predicates such as `<u32 as Add>::Effects: Compat<host>`, pretend that the - /// predicate that failed was `u32: Add`. Return the constness of such predicate to later - /// print as `u32: ~const Add`. - fn get_effects_trait_pred_override( - &self, - p: ty::PolyTraitPredicate<'tcx>, - leaf: ty::PolyTraitPredicate<'tcx>, - span: Span, - ) -> (ty::PolyTraitPredicate<'tcx>, ty::PolyTraitPredicate<'tcx>, ty::BoundConstness) { - let trait_ref = p.to_poly_trait_ref(); - if !self.tcx.is_lang_item(trait_ref.def_id(), LangItem::EffectsCompat) { - return (p, leaf, ty::BoundConstness::NotConst); - } - - let Some(ty::Alias(ty::AliasTyKind::Projection, projection)) = - trait_ref.self_ty().no_bound_vars().map(Ty::kind) - else { - return (p, leaf, ty::BoundConstness::NotConst); - }; - - let constness = trait_ref.skip_binder().args.const_at(1); - - let constness = if constness == self.tcx.consts.true_ || constness.is_ct_infer() { - ty::BoundConstness::NotConst - } else if constness == self.tcx.consts.false_ { - ty::BoundConstness::Const - } else if matches!(constness.kind(), ty::ConstKind::Param(_)) { - ty::BoundConstness::ConstIfConst - } else { - self.dcx().span_bug(span, format!("Unknown constness argument: {constness:?}")); - }; - - let new_pred = p.map_bound(|mut trait_pred| { - trait_pred.trait_ref = projection.trait_ref(self.tcx); - trait_pred - }); - - let new_leaf = leaf.map_bound(|mut trait_pred| { - trait_pred.trait_ref = projection.trait_ref(self.tcx); - trait_pred - }); - - (new_pred, new_leaf, constness) + }) } fn add_tuple_trait_message( @@ -2649,6 +2625,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; self.report_and_explain_type_error( TypeTrace::trait_refs(&cause, true, expected_trait_ref, found_trait_ref), + obligation.param_env, terr, ) } @@ -2739,6 +2716,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { return Ok(self.report_and_explain_type_error( TypeTrace::trait_refs(&obligation.cause, true, expected_trait_ref, found_trait_ref), + obligation.param_env, ty::error::TypeError::Mismatch, )); } @@ -3021,7 +2999,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation: &PredicateObligation<'tcx>, span: Span, ) -> Result<Diag<'a>, ErrorGuaranteed> { - if !self.tcx.features().generic_const_exprs { + if !self.tcx.features().generic_const_exprs() { let guar = self .dcx() .struct_span_err(span, "constant expression depends on a generic parameter") 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 ba57909fc23..ca23f776581 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -287,6 +287,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { FulfillmentErrorCode::Subtype(ref expected_found, ref err) => self .report_mismatched_types( &error.obligation.cause, + error.obligation.param_env, expected_found.expected, expected_found.found, *err, @@ -295,6 +296,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { FulfillmentErrorCode::ConstEquate(ref expected_found, ref err) => { let mut diag = self.report_mismatched_consts( &error.obligation.cause, + error.obligation.param_env, expected_found.expected, expected_found.found, *err, 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 cd41ab9fa6c..7f42c932fcf 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 @@ -231,7 +231,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]` if let ty::Array(aty, len) = self_ty.kind() { flags.push((sym::_Self, Some("[]".to_string()))); - let len = len.try_to_valtree().and_then(|v| v.try_to_target_usize(self.tcx)); + let len = len.try_to_target_usize(self.tcx); flags.push((sym::_Self, Some(format!("[{aty}; _]")))); if let Some(n) = len { flags.push((sym::_Self, Some(format!("[{aty}; {n}]")))); 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 733baaa99e5..1fe93cb017a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3044,7 +3044,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if local { err.note("all local variables must have a statically known size"); } - if !tcx.features().unsized_locals { + if !tcx.features().unsized_locals() { err.help("unsized locals are gated as an unstable feature"); } } @@ -3125,7 +3125,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.note("all function arguments must have a statically known size"); } if tcx.sess.opts.unstable_features.is_nightly_build() - && !tcx.features().unsized_fn_params + && !tcx.features().unsized_fn_params() { err.help("unsized fn params are gated as an unstable feature"); } @@ -3594,52 +3594,64 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, span: Span, ) { - if let Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) = - self.tcx.coroutine_kind(obligation.cause.body_id) - { - let future_trait = self.tcx.require_lang_item(LangItem::Future, None); + let future_trait = self.tcx.require_lang_item(LangItem::Future, None); - let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); - let impls_future = self.type_implements_trait( - future_trait, - [self.tcx.instantiate_bound_regions_with_erased(self_ty)], - obligation.param_env, - ); - if !impls_future.must_apply_modulo_regions() { - return; - } + let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); + let impls_future = self.type_implements_trait( + future_trait, + [self.tcx.instantiate_bound_regions_with_erased(self_ty)], + obligation.param_env, + ); + if !impls_future.must_apply_modulo_regions() { + return; + } - let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; - // `<T as Future>::Output` - let projection_ty = trait_pred.map_bound(|trait_pred| { - Ty::new_projection( - self.tcx, - item_def_id, - // Future::Output has no args - [trait_pred.self_ty()], - ) - }); - let InferOk { value: projection_ty, .. } = - self.at(&obligation.cause, obligation.param_env).normalize(projection_ty); + let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; + // `<T as Future>::Output` + let projection_ty = trait_pred.map_bound(|trait_pred| { + Ty::new_projection( + self.tcx, + item_def_id, + // Future::Output has no args + [trait_pred.self_ty()], + ) + }); + let InferOk { value: projection_ty, .. } = + self.at(&obligation.cause, obligation.param_env).normalize(projection_ty); - debug!( - normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty) - ); - let try_obligation = self.mk_trait_obligation_with_new_self_ty( - obligation.param_env, - trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())), - ); - debug!(try_trait_obligation = ?try_obligation); - if self.predicate_may_hold(&try_obligation) - && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) - && snippet.ends_with('?') - { - err.span_suggestion_verbose( - span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(), - "consider `await`ing on the `Future`", - ".await", - Applicability::MaybeIncorrect, - ); + debug!( + normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty) + ); + let try_obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())), + ); + debug!(try_trait_obligation = ?try_obligation); + if self.predicate_may_hold(&try_obligation) + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) + && snippet.ends_with('?') + { + match self.tcx.coroutine_kind(obligation.cause.body_id) { + Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => { + err.span_suggestion_verbose( + span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await", + Applicability::MaybeIncorrect, + ); + } + _ => { + let mut span: MultiSpan = span.with_lo(span.hi() - BytePos(1)).into(); + span.push_span_label( + self.tcx.def_span(obligation.cause.body_id), + "this is not `async`", + ); + err.span_note( + span, + "this implements `Future` and its output type supports \ + `?`, but the future cannot be awaited in a synchronous function", + ); + } } } } @@ -3701,12 +3713,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } _ => None, }; - // Also add host param, if present - let host = self.tcx.generics_of(trait_pred.def_id()).host_effect_index.map(|idx| trait_pred.skip_binder().trait_ref.args[idx]); let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate { trait_ref: ty::TraitRef::new(self.tcx, trait_pred.def_id(), - [field_ty].into_iter().chain(trait_args).chain(host), + [field_ty].into_iter().chain(trait_args), ), ..*tr }); @@ -4500,7 +4510,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_ref: ty::PolyTraitRef<'tcx>, ) { // Don't suggest if RTN is active -- we should prefer a where-clause bound instead. - if self.tcx.features().return_type_notation { + if self.tcx.features().return_type_notation() { return; } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 4975a9ce0c7..e735020a63e 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -344,7 +344,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { }; let mut nested_goals = vec![]; - self.candidates_recur(&mut candidates, &mut nested_goals, &last_eval_step.evaluation); + self.candidates_recur(&mut candidates, &mut nested_goals, &last_eval_step); candidates } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 12aeee0d02f..934fe9ec47c 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -806,7 +806,8 @@ impl<'tcx> AutoTraitFinder<'tcx> { | ty::PredicateKind::Subtype(..) // FIXME(generic_const_exprs): you can absolutely add this as a where clauses | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) - | ty::PredicateKind::Coerce(..) => {} + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => {} ty::PredicateKind::Ambiguous => return false, }; } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index f4a2483cebf..f8fb297e36c 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -298,6 +298,7 @@ fn equate_impl_headers<'tcx>( } /// The result of [fn impl_intersection_has_impossible_obligation]. +#[derive(Debug)] enum IntersectionHasImpossibleObligations<'tcx> { Yes, No { @@ -328,6 +329,7 @@ enum IntersectionHasImpossibleObligations<'tcx> { /// of the two impls above to be empty. /// /// Importantly, this works even if there isn't a `impl !Error for MyLocalType`. +#[instrument(level = "debug", skip(selcx), ret)] fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligations: &'a [PredicateObligation<'tcx>], diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index c258832bf2b..1be3c964454 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -40,7 +40,7 @@ pub fn is_const_evaluatable<'tcx>( ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer), }; - if tcx.features().generic_const_exprs { + if tcx.features().generic_const_exprs() { let ct = tcx.expand_abstract_consts(unexpanded_ct); let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() { diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 364a13b3a75..a068f25fe35 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -245,6 +245,7 @@ fn predicate_references_self<'tcx>( | ty::ClauseKind::RegionOutlives(..) // FIXME(generic_const_exprs): this can mention `Self` | ty::ClauseKind::ConstEvaluatable(..) + | ty::ClauseKind::HostEffect(..) => None, } } @@ -254,7 +255,7 @@ fn super_predicates_have_non_lifetime_binders( trait_def_id: DefId, ) -> SmallVec<[Span; 1]> { // If non_lifetime_binders is disabled, then exit early - if !tcx.features().non_lifetime_binders { + if !tcx.features().non_lifetime_binders() { return SmallVec::new(); } tcx.explicit_super_predicates_of(trait_def_id) @@ -284,7 +285,8 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { | ty::ClauseKind::Projection(_) | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) - | ty::ClauseKind::ConstEvaluatable(_) => false, + | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::HostEffect(..) => false, }) } @@ -327,7 +329,7 @@ pub fn dyn_compatibility_violations_for_assoc_item( .collect(), // Associated types can only be dyn-compatible if they have `Self: Sized` bounds. ty::AssocKind::Type => { - if !tcx.features().generic_associated_types_extended + if !tcx.features().generic_associated_types_extended() && !tcx.generics_of(item.def_id).is_own_empty() && !item.is_impl_trait_in_trait() { diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index a45ec8b3c44..5e270b62b00 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -329,4 +329,15 @@ where .at(cause, param_env) .structurally_normalize(value, &mut **self.engine.borrow_mut()) } + + pub fn structurally_normalize_const( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: ty::Const<'tcx>, + ) -> Result<ty::Const<'tcx>, Vec<E>> { + self.infcx + .at(cause, param_env) + .structurally_normalize_const(value, &mut **self.engine.borrow_mut()) + } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index e56f1866970..1754418156d 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -372,7 +372,11 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { | ty::PredicateKind::Subtype(_) | ty::PredicateKind::Coerce(_) | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) - | ty::PredicateKind::ConstEquate(..) => { + | ty::PredicateKind::ConstEquate(..) + // FIXME(effects): We may need to do this using the higher-ranked + // pred instead of just instantiating it with placeholders b/c of + // higher-ranked implied bound issues in the old solver. + | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => { let pred = ty::Binder::dummy(infcx.enter_forall_and_leak_universe(binder)); let mut obligations = PredicateObligations::with_capacity(1); obligations.push(obligation.with(infcx.tcx, pred)); @@ -398,6 +402,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ) } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => { + ProcessResult::Changed(Default::default()) + } + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(data)) => { if infcx.considering_regions { infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)); @@ -450,7 +458,6 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::ConstKind::Infer(var) => { let var = match var { ty::InferConst::Var(vid) => TyOrConstInferVar::Const(vid), - ty::InferConst::EffectVar(vid) => TyOrConstInferVar::Effect(vid), ty::InferConst::Fresh(_) => { bug!("encountered fresh const in fulfill") } @@ -598,7 +605,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::PredicateKind::ConstEquate(c1, c2) => { let tcx = self.selcx.tcx(); assert!( - tcx.features().generic_const_exprs, + tcx.features().generic_const_exprs(), "`ConstEquate` without a feature gate: {c1:?} {c2:?}", ); // FIXME: we probably should only try to unify abstract constants diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 0fb795fc184..cdf24887e76 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -346,7 +346,7 @@ pub fn normalize_param_env_or_error<'tcx>( let mut predicates: Vec<_> = util::elaborate( tcx, unnormalized_env.caller_bounds().into_iter().map(|predicate| { - if tcx.features().generic_const_exprs { + if tcx.features().generic_const_exprs() { return predicate; } @@ -368,7 +368,7 @@ pub fn normalize_param_env_or_error<'tcx>( // should actually be okay since without `feature(generic_const_exprs)` the only // const arguments that have a non-empty param env are array repeat counts. These // do not appear in the type system though. - c.normalize(self.0, ty::ParamEnv::empty()) + c.normalize_internal(self.0, ty::ParamEnv::empty()) } } @@ -562,11 +562,20 @@ fn is_impossible_associated_item( let generics = tcx.generics_of(trait_item_def_id); let predicates = tcx.predicates_of(trait_item_def_id); + + // Be conservative in cases where we have `W<T: ?Sized>` and a method like `Self: Sized`, + // since that method *may* have some substitutions where the predicates hold. + // + // This replicates the logic we use in coherence. + let infcx = + tcx.infer_ctxt().ignoring_regions().with_next_trait_solver(true).intercrate(true).build(); + let param_env = ty::ParamEnv::empty(); + let fresh_args = infcx.fresh_args_for_item(tcx.def_span(impl_def_id), impl_def_id); + let impl_trait_ref = tcx .impl_trait_ref(impl_def_id) .expect("expected impl to correspond to trait") - .instantiate_identity(); - let param_env = tcx.param_env(impl_def_id); + .instantiate(tcx, fresh_args); let mut visitor = ReferencesOnlyParentGenerics { tcx, generics, trait_item_def_id }; let predicates_for_trait = predicates.predicates.iter().filter_map(|(pred, span)| { @@ -580,16 +589,9 @@ fn is_impossible_associated_item( }) }); - let infcx = tcx.infer_ctxt().ignoring_regions().build(); - for obligation in predicates_for_trait { - // Ignore overflow error, to be conservative. - if let Ok(result) = infcx.evaluate_obligation(&obligation) - && !result.may_apply() - { - return true; - } - } - false + let ocx = ObligationCtxt::new(&infcx); + ocx.register_obligations(predicates_for_trait); + !ocx.select_where_possible().is_empty() } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 7eac3559348..12e00ec79ac 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -402,7 +402,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx #[instrument(skip(self), level = "debug")] fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { let tcx = self.selcx.tcx(); - if tcx.features().generic_const_exprs + if tcx.features().generic_const_exprs() || !needs_normalization(&constant, self.param_env.reveal()) { constant @@ -413,7 +413,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx self.selcx.infcx, &mut self.universes, constant, - |constant| constant.normalize(tcx, self.param_env), + |constant| constant.normalize_internal(tcx, self.param_env), ) } } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 7f7c9bced18..38722c0ff7c 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -189,7 +189,7 @@ pub(super) fn poly_project_and_unify_term<'cx, 'tcx>( ProjectAndUnifyResult::MismatchedProjectionTypes(e) => Err(e), ProjectAndUnifyResult::Holds(obligations) if old_universe != new_universe - && selcx.tcx().features().generic_associated_types_extended => + && selcx.tcx().features().generic_associated_types_extended() => { // If the `generic_associated_types_extended` feature is active, then we ignore any // obligations references lifetimes from any universe greater than or equal to the @@ -1642,24 +1642,9 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>( sig, ); - let host_effect_param = match *fn_type.kind() { - ty::FnDef(def_id, args) => tcx - .generics_of(def_id) - .host_effect_index - .map_or(tcx.consts.true_, |idx| args.const_at(idx)), - ty::FnPtr(..) => tcx.consts.true_, - _ => unreachable!("only expected FnPtr or FnDef in `confirm_fn_pointer_candidate`"), - }; - - confirm_callable_candidate( - selcx, - obligation, - sig, - util::TupleArgumentsFlag::Yes, - host_effect_param, - ) - .with_addl_obligations(nested) - .with_addl_obligations(obligations) + confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) } fn confirm_closure_candidate<'cx, 'tcx>( @@ -1739,16 +1724,9 @@ fn confirm_closure_candidate<'cx, 'tcx>( debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate"); - confirm_callable_candidate( - selcx, - obligation, - closure_sig, - util::TupleArgumentsFlag::No, - // FIXME(effects): This doesn't handle const closures correctly! - selcx.tcx().consts.true_, - ) - .with_addl_obligations(nested) - .with_addl_obligations(obligations) + confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) } fn confirm_callable_candidate<'cx, 'tcx>( @@ -1756,7 +1734,6 @@ fn confirm_callable_candidate<'cx, 'tcx>( obligation: &ProjectionTermObligation<'tcx>, fn_sig: ty::PolyFnSig<'tcx>, flag: util::TupleArgumentsFlag, - fn_host_effect: ty::Const<'tcx>, ) -> Progress<'tcx> { let tcx = selcx.tcx(); @@ -1771,7 +1748,6 @@ fn confirm_callable_candidate<'cx, 'tcx>( obligation.predicate.self_ty(), fn_sig, flag, - fn_host_effect, ) .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate { projection_term: ty::AliasTerm::new_from_args(tcx, fn_once_output_def_id, trait_ref.args), diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 18412b844ff..01e6516302c 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -340,7 +340,7 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> { self.infcx, &mut self.universes, constant, - |constant| constant.normalize(self.infcx.tcx, self.param_env), + |constant| constant.normalize_internal(self.infcx.tcx, self.param_env), ); debug!(?constant, ?self.param_env); constant.try_super_fold_with(self) diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index dfd0cab6905..c6e41e57f0c 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -96,6 +96,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( // FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound // if we ever support that ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) @@ -200,6 +201,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( // FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound // if we ever support that ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) 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 52048ca79f9..e027586563e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -244,7 +244,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .param_env .caller_bounds() .iter() - .filter(|p| !p.references_error()) .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()) @@ -394,7 +393,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let self_ty = obligation.self_ty().skip_binder(); match *self_ty.kind() { ty::Closure(def_id, _) => { - let is_const = self.tcx().is_const_fn_raw(def_id); + let is_const = self.tcx().is_const_fn(def_id); debug!(?kind, ?obligation, "assemble_unboxed_candidates"); match self.infcx.closure_kind(self_ty) { Some(closure_kind) => { @@ -414,7 +413,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::CoroutineClosure(def_id, args) => { let args = args.as_coroutine_closure(); - let is_const = self.tcx().is_const_fn_raw(def_id); + let is_const = self.tcx().is_const_fn(def_id); if let Some(closure_kind) = self.infcx.closure_kind(self_ty) // Ambiguity if upvars haven't been constrained yet && !args.tupled_upvars_ty().is_ty_var() @@ -543,23 +542,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Provide an impl, but only for suitable `fn` pointers. ty::FnPtr(sig_tys, hdr) => { if sig_tys.with(hdr).is_fn_trait_compatible() { - candidates - .vec - .push(FnPointerCandidate { fn_host_effect: self.tcx().consts.true_ }); + candidates.vec.push(FnPointerCandidate); } } // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). - ty::FnDef(def_id, args) => { + ty::FnDef(def_id, _) => { let tcx = self.tcx(); if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible() && tcx.codegen_fn_attrs(def_id).target_features.is_empty() { - candidates.vec.push(FnPointerCandidate { - fn_host_effect: tcx - .generics_of(def_id) - .host_effect_index - .map_or(tcx.consts.true_, |idx| args.const_at(idx)), - }); + candidates.vec.push(FnPointerCandidate); } } _ => {} @@ -883,7 +875,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } if let Some(principal) = data.principal() { - if !self.infcx.tcx.features().dyn_compatible_for_dispatch { + if !self.infcx.tcx.features().dyn_compatible_for_dispatch() { principal.with_self_ty(self.tcx(), self_ty) } else if self.tcx().is_dyn_compatible(principal.def_id()) { principal.with_self_ty(self.tcx(), self_ty) @@ -943,7 +935,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } let tcx = self.tcx(); - if tcx.features().trait_upcasting { + if tcx.features().trait_upcasting() { return None; } @@ -1018,7 +1010,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // #2 (region bounds). let principal_def_id_a = a_data.principal_def_id(); let principal_def_id_b = b_data.principal_def_id(); - if principal_def_id_a == principal_def_id_b { + if principal_def_id_a == principal_def_id_b || principal_def_id_b.is_none() { // We may upcast to auto traits that are either explicitly listed in // the object type's bounds, or implied by the principal trait ref's // supertraits. @@ -1170,103 +1162,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn assemble_const_destruct_candidates( &mut self, - obligation: &PolyTraitObligation<'tcx>, + _obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - // If the predicate is `~const Destruct` in a non-const environment, we don't actually need - // to check anything. We'll short-circuit checking any obligations in confirmation, too. - let Some(host_effect_index) = - self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index - else { - candidates.vec.push(BuiltinCandidate { has_nested: false }); - return; - }; - // If the obligation has `host = true`, then the obligation is non-const and it's always - // trivially implemented. - if obligation.predicate.skip_binder().trait_ref.args.const_at(host_effect_index) - == self.tcx().consts.true_ - { - candidates.vec.push(BuiltinCandidate { has_nested: false }); - return; - } - - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); - match self_ty.kind() { - ty::Alias(..) - | ty::Dynamic(..) - | ty::Error(_) - | ty::Bound(..) - | ty::Param(_) - | ty::Placeholder(_) => { - // We don't know if these are `~const Destruct`, at least - // not structurally... so don't push a candidate. - } - - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::Str - | ty::RawPtr(_, _) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Never - | ty::Foreign(_) - | ty::Array(..) - | ty::Pat(..) - | ty::Slice(_) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::Tuple(_) - | ty::CoroutineWitness(..) => { - // These are built-in, and cannot have a custom `impl const Destruct`. - candidates.vec.push(ConstDestructCandidate(None)); - } - - ty::Adt(..) => { - let mut relevant_impl = None; - self.tcx().for_each_relevant_impl( - self.tcx().require_lang_item(LangItem::Drop, None), - obligation.predicate.skip_binder().trait_ref.self_ty(), - |impl_def_id| { - if let Some(old_impl_def_id) = relevant_impl { - self.tcx() - .dcx() - .struct_span_err( - self.tcx().def_span(impl_def_id), - "multiple drop impls found", - ) - .with_span_note( - self.tcx().def_span(old_impl_def_id), - "other impl here", - ) - .delay_as_bug(); - } - - relevant_impl = Some(impl_def_id); - }, - ); - - if let Some(impl_def_id) = relevant_impl { - // Check that `impl Drop` is actually const, if there is a custom impl - if self.tcx().constness(impl_def_id) == hir::Constness::Const { - candidates.vec.push(ConstDestructCandidate(Some(impl_def_id))); - } - } else { - // Otherwise check the ADT like a built-in type (structurally) - candidates.vec.push(ConstDestructCandidate(None)); - } - } - - ty::Infer(_) => { - candidates.ambiguous = true; - } - } + // FIXME(effects): Destruct is not const yet, and it is implemented + // by all types today in non-const setting. + candidates.vec.push(BuiltinCandidate { has_nested: false }); } fn assemble_candidate_for_tuple( diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index cc5c7532b50..e7d3004aa20 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -28,9 +28,9 @@ use super::{BuiltinImplConditions, PredicateObligations, SelectionContext}; use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::util::{self, closure_trait_ref_and_return_type}; use crate::traits::{ - ImplDerivedCause, ImplSource, ImplSourceUserDefinedData, Normalized, Obligation, - ObligationCause, PolyTraitObligation, PredicateObligation, Selection, SelectionError, - SignatureMismatch, TraitDynIncompatible, TraitObligation, Unimplemented, + ImplSource, ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, + PolyTraitObligation, PredicateObligation, Selection, SelectionError, SignatureMismatch, + TraitDynIncompatible, TraitObligation, Unimplemented, }; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { @@ -109,8 +109,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator) } - FnPointerCandidate { fn_host_effect } => { - let data = self.confirm_fn_pointer_candidate(obligation, fn_host_effect)?; + FnPointerCandidate => { + let data = self.confirm_fn_pointer_candidate(obligation)?; ImplSource::Builtin(BuiltinImplSource::Misc, data) } @@ -131,11 +131,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { TraitUpcastingUnsizeCandidate(idx) => { self.confirm_trait_upcasting_unsize_candidate(obligation, idx)? } - - ConstDestructCandidate(def_id) => { - let data = self.confirm_const_destruct_candidate(obligation, def_id)?; - ImplSource::Builtin(BuiltinImplSource::Misc, data) - } }; // The obligations returned by confirmation are recursively evaluated @@ -405,11 +400,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let predicate = obligation.predicate.skip_binder(); - let Some(assume) = rustc_transmute::Assume::from_const( - self.infcx.tcx, - obligation.param_env, - predicate.trait_ref.args.const_at(2), - ) else { + let mut assume = predicate.trait_ref.args.const_at(2); + // FIXME(min_generic_const_exprs): We should shallowly normalize this. + if self.tcx().features().generic_const_exprs() { + assume = assume.normalize_internal(self.tcx(), obligation.param_env); + } + let Some(assume) = + rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, assume) + else { return Err(Unimplemented); }; @@ -628,7 +626,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { for assoc_type in assoc_types { let defs: &ty::Generics = tcx.generics_of(assoc_type); - if !defs.own_params.is_empty() && !tcx.features().generic_associated_types_extended { + if !defs.own_params.is_empty() && !tcx.features().generic_associated_types_extended() { tcx.dcx().span_delayed_bug( obligation.cause.span, "GATs in trait object shouldn't have been considered", @@ -708,7 +706,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_fn_pointer_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, - fn_host_effect: ty::Const<'tcx>, ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> { debug!(?obligation, "confirm_fn_pointer_candidate"); let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); @@ -722,7 +719,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self_ty, sig, util::TupleArgumentsFlag::Yes, - fn_host_effect, ) .map_bound(|(trait_ref, _)| trait_ref); @@ -904,11 +900,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let self_ty: Ty<'_> = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let trait_ref = match *self_ty.kind() { - ty::Closure(..) => self.closure_trait_ref_unnormalized( - self_ty, - obligation.predicate.def_id(), - self.tcx().consts.true_, - ), + ty::Closure(..) => { + self.closure_trait_ref_unnormalized(self_ty, obligation.predicate.def_id()) + } ty::CoroutineClosure(_, args) => { args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| { ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [ @@ -1153,6 +1147,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`. let iter = data_a .principal() + .filter(|_| { + // optionally drop the principal, if we're unsizing to no principal + data_b.principal().is_some() + }) .map(|b| b.map_bound(ty::ExistentialPredicate::Trait)) .into_iter() .chain( @@ -1337,170 +1335,4 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => bug!("source: {source}, target: {target}"), }) } - - fn confirm_const_destruct_candidate( - &mut self, - obligation: &PolyTraitObligation<'tcx>, - impl_def_id: Option<DefId>, - ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> { - let Some(host_effect_index) = - self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index - else { - bug!() - }; - let host_effect_param: ty::GenericArg<'tcx> = - obligation.predicate.skip_binder().trait_ref.args.const_at(host_effect_index).into(); - - let drop_trait = self.tcx().require_lang_item(LangItem::Drop, None); - - let tcx = self.tcx(); - let self_ty = obligation.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty)); - - let mut nested = PredicateObligations::new(); - let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived); - - // If we have a custom `impl const Drop`, then - // first check it like a regular impl candidate. - // This is copied from confirm_impl_candidate but remaps the predicate to `~const Drop` beforehand. - if let Some(impl_def_id) = impl_def_id { - let mut new_obligation = obligation.clone(); - new_obligation.predicate = new_obligation.predicate.map_bound(|mut trait_pred| { - trait_pred.trait_ref.def_id = drop_trait; - trait_pred - }); - let args = self.rematch_impl(impl_def_id, &new_obligation); - debug!(?args, "impl args"); - - let cause = obligation.derived_cause(|derived| { - ObligationCauseCode::ImplDerived(Box::new(ImplDerivedCause { - derived, - impl_or_alias_def_id: impl_def_id, - impl_def_predicate_index: None, - span: obligation.cause.span, - })) - }); - let obligations = ensure_sufficient_stack(|| { - self.vtable_impl( - impl_def_id, - args, - &cause, - new_obligation.recursion_depth + 1, - new_obligation.param_env, - obligation.predicate, - ) - }); - nested.extend(obligations.nested); - } - - // We want to confirm the ADT's fields if we have an ADT - let mut stack = match *self_ty.skip_binder().kind() { - ty::Adt(def, args) => def.all_fields().map(|f| f.ty(tcx, args)).collect(), - _ => vec![self_ty.skip_binder()], - }; - - while let Some(nested_ty) = stack.pop() { - match *nested_ty.kind() { - // We know these types are trivially drop - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::Str - | ty::RawPtr(_, _) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Never - | ty::Foreign(_) => {} - - // `ManuallyDrop` is trivially drop - ty::Adt(def, _) if def.is_manually_drop() => {} - - // These types are built-in, so we can fast-track by registering - // nested predicates for their constituent type(s) - ty::Array(ty, _) | ty::Slice(ty) | ty::Pat(ty, _) => { - stack.push(ty); - } - ty::Tuple(tys) => { - stack.extend(tys.iter()); - } - ty::Closure(_, args) => { - stack.push(args.as_closure().tupled_upvars_ty()); - } - ty::Coroutine(_, args) => { - let coroutine = args.as_coroutine(); - stack.extend([coroutine.tupled_upvars_ty(), coroutine.witness()]); - } - ty::CoroutineWitness(def_id, args) => { - let tcx = self.tcx(); - stack.extend(tcx.bound_coroutine_hidden_types(def_id).map(|bty| { - self.infcx.enter_forall_and_leak_universe(bty.instantiate(tcx, args)) - })) - } - - // If we have a projection type, make sure to normalize it so we replace it - // with a fresh infer variable - ty::Alias(ty::Projection | ty::Inherent, ..) => { - let predicate = normalize_with_depth_to( - self, - obligation.param_env, - cause.clone(), - obligation.recursion_depth + 1, - self_ty.rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef::new( - self.tcx(), - self.tcx().require_lang_item(LangItem::Destruct, Some(cause.span)), - [nested_ty.into(), host_effect_param], - ), - polarity: ty::PredicatePolarity::Positive, - }), - &mut nested, - ); - - nested.push(Obligation::with_depth( - tcx, - cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - predicate, - )); - } - - // If we have any other type (e.g. an ADT), just register a nested obligation - // since it's either not `const Drop` (and we raise an error during selection), - // or it's an ADT (and we need to check for a custom impl during selection) - ty::Error(_) - | ty::Dynamic(..) - | ty::CoroutineClosure(..) - | ty::Param(_) - | ty::Bound(..) - | ty::Adt(..) - | ty::Alias(ty::Opaque | ty::Weak, _) - | ty::Infer(_) - | ty::Placeholder(_) => { - let predicate = self_ty.rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef::new( - self.tcx(), - self.tcx().require_lang_item(LangItem::Destruct, Some(cause.span)), - [nested_ty.into(), host_effect_param], - ), - polarity: ty::PredicatePolarity::Positive, - }); - - nested.push(Obligation::with_depth( - tcx, - cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - predicate, - )); - } - } - } - - Ok(nested) - } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 621babe9104..ec4114fd9d7 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -645,6 +645,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.evaluate_trait_predicate_recursively(previous_stack, obligation) } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => { + // FIXME(effects): It should be relatively straightforward to implement + // old trait solver support for `HostEffect` bounds; or at least basic + // support for them. + todo!() + } + ty::PredicateKind::Subtype(p) => { let p = bound_predicate.rebind(p); // Does this code ever run? @@ -865,7 +872,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::PredicateKind::ConstEquate(c1, c2) => { let tcx = self.tcx(); assert!( - tcx.features().generic_const_exprs, + tcx.features().generic_const_exprs(), "`ConstEquate` without a feature gate: {c1:?} {c2:?}", ); @@ -1821,8 +1828,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { |cand: ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_bound_vars(); // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`, - // `DiscriminantKindCandidate`, `ConstDestructCandidate` - // to anything else. + // or `DiscriminantKindCandidate` to anything else. // // This is a fix for #53123 and prevents winnowing from accidentally extending the // lifetime of a variable. @@ -1831,12 +1837,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No, // (*) - (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => { - DropVictim::Yes - } - (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => { - DropVictim::No - } + (BuiltinCandidate { has_nested: false }, _) => DropVictim::Yes, + (_, BuiltinCandidate { has_nested: false }) => DropVictim::No, (ParamCandidate(other), ParamCandidate(victim)) => { let same_except_bound_vars = other.skip_binder().trait_ref @@ -1855,11 +1857,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } - // Drop otherwise equivalent non-const fn pointer candidates - (FnPointerCandidate { .. }, FnPointerCandidate { fn_host_effect }) => { - DropVictim::drop_if(*fn_host_effect == self.tcx().consts.true_) - } - ( ParamCandidate(other_cand), ImplCandidate(..) @@ -2204,7 +2201,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { match self.tcx().coroutine_movability(coroutine_def_id) { hir::Movability::Static => None, hir::Movability::Movable => { - if self.tcx().features().coroutine_clone { + if self.tcx().features().coroutine_clone() { let resolved_upvars = self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty()); let resolved_witness = @@ -2766,7 +2763,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { &mut self, self_ty: Ty<'tcx>, fn_trait_def_id: DefId, - fn_host_effect: ty::Const<'tcx>, ) -> ty::PolyTraitRef<'tcx> { let ty::Closure(_, args) = *self_ty.kind() else { bug!("expected closure, found {self_ty}"); @@ -2779,7 +2775,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { self_ty, closure_sig, util::TupleArgumentsFlag::No, - fn_host_effect, ) .map_bound(|(trait_ref, _)| trait_ref) } diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index b82a3433645..0e45f7a195f 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -136,7 +136,7 @@ pub fn translate_args_with_cause<'tcx>( } pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool { - tcx.features().specialization || tcx.features().min_specialization + tcx.features().specialization() || tcx.features().min_specialization() } /// Is `impl1` a specialization of `impl2`? diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index 3814f8112e9..23b5f62b5ca 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -82,6 +82,8 @@ impl<'tcx> At<'_, 'tcx> { } Ok(self.infcx.resolve_vars_if_possible(new_infer_ct)) + } else if self.infcx.tcx.features().generic_const_exprs() { + Ok(ct.normalize_internal(self.infcx.tcx, self.param_env)) } else { Ok(self.normalize(ct).into_value_registering_obligations(self.infcx, fulfill_cx)) } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index aed2e3d61aa..b7a2f20b769 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -215,22 +215,13 @@ pub(crate) fn closure_trait_ref_and_return_type<'tcx>( self_ty: Ty<'tcx>, sig: ty::PolyFnSig<'tcx>, tuple_arguments: TupleArgumentsFlag, - fn_host_effect: ty::Const<'tcx>, ) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> { assert!(!self_ty.has_escaping_bound_vars()); let arguments_tuple = match tuple_arguments { TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()), }; - let trait_ref = if tcx.has_host_param(fn_trait_def_id) { - ty::TraitRef::new(tcx, fn_trait_def_id, [ - ty::GenericArg::from(self_ty), - ty::GenericArg::from(arguments_tuple), - ty::GenericArg::from(fn_host_effect), - ]) - } else { - ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]) - }; + let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]); sig.map_bound(|sig| (trait_ref, sig.output())) } diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 6e6f948a2cd..ed221e2a183 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -154,18 +154,17 @@ fn prepare_vtable_segments_inner<'tcx, T>( // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() { + let has_entries = + has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id()); + segment_visitor(VtblSegment::TraitOwnEntries { trait_ref: inner_most_trait_ref, - emit_vptr: emit_vptr && !tcx.sess.opts.unstable_opts.no_trait_vptr, + emit_vptr: emit_vptr && has_entries && !tcx.sess.opts.unstable_opts.no_trait_vptr, })?; // If we've emitted (fed to `segment_visitor`) a trait that has methods present in the vtable, // we'll need to emit vptrs from now on. - if !emit_vptr_on_new_entry - && has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id()) - { - emit_vptr_on_new_entry = true; - } + emit_vptr_on_new_entry |= has_entries; if let Some(next_inner_most_trait_ref) = siblings.find(|&sibling| visited.insert(sibling.upcast(tcx))) diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 07e68e5a3e8..437343b569c 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -170,6 +170,10 @@ pub fn clause_obligations<'tcx>( ty::ClauseKind::Trait(t) => { wf.compute_trait_pred(t, Elaborate::None); } + ty::ClauseKind::HostEffect(..) => { + // Technically the well-formedness of this predicate is implied by + // the corresponding trait predicate it should've been generated beside. + } ty::ClauseKind::RegionOutlives(..) => {} ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { wf.compute(ty.into()); @@ -836,7 +840,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { // obligations that don't refer to Self and // checking those - let defer_to_coercion = tcx.features().dyn_compatible_for_dispatch; + let defer_to_coercion = tcx.features().dyn_compatible_for_dispatch(); if !defer_to_coercion { if let Some(principal) = data.principal_def_id() { @@ -1021,6 +1025,7 @@ pub(crate) fn required_region_bounds<'tcx>( } } ty::ClauseKind::Trait(_) + | ty::ClauseKind::HostEffect(..) | ty::ClauseKind::RegionOutlives(_) | ty::ClauseKind::Projection(_) | ty::ClauseKind::ConstArgHasType(_, _) diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index f01a12b0a00..3e2794f6489 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -55,6 +55,7 @@ fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool { | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) => false, ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)) | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index c1d0b704ab2..f7651e49cdd 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -85,7 +85,6 @@ mod rustc { use rustc_macros::TypeVisitable; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{Const, ParamEnv, Ty, TyCtxt, ValTree}; - use rustc_span::DUMMY_SP; use super::*; @@ -134,13 +133,8 @@ mod rustc { use rustc_middle::ty::ScalarInt; use rustc_span::symbol::sym; - let Ok((ty, cv)) = c.eval(tcx, param_env, DUMMY_SP) else { - return Some(Self { - alignment: true, - lifetimes: true, - safety: true, - validity: true, - }); + let Some((cv, ty)) = c.try_to_valtree() else { + return None; }; let adt_def = ty.ty_adt_def()?; diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 7354ea5fb6a..48149a08de8 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -1,8 +1,7 @@ use std::iter; -use rustc_abi::Float::*; -use rustc_abi::Primitive::{Float, Pointer}; -use rustc_abi::{Abi, AddressSpace, PointerKind, Scalar, Size}; +use rustc_abi::Primitive::Pointer; +use rustc_abi::{Abi, PointerKind, Scalar, Size}; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_middle::bug; @@ -14,8 +13,7 @@ use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt}; use rustc_session::config::OptLevel; use rustc_span::def_id::DefId; use rustc_target::abi::call::{ - ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, Conv, FnAbi, PassMode, Reg, RegKind, - RiscvInterruptKind, + ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, Conv, FnAbi, PassMode, RiscvInterruptKind, }; use rustc_target::spec::abi::Abi as SpecAbi; use tracing::debug; @@ -679,6 +677,8 @@ fn fn_abi_adjust_for_abi<'tcx>( let tcx = cx.tcx(); if abi == SpecAbi::Rust || abi == SpecAbi::RustCall || abi == SpecAbi::RustIntrinsic { + fn_abi.adjust_for_rust_abi(cx, abi); + // Look up the deduced parameter attributes for this function, if we have its def ID and // we're optimizing in non-incremental mode. We'll tag its parameters with those attributes // as appropriate. @@ -689,88 +689,9 @@ fn fn_abi_adjust_for_abi<'tcx>( &[] }; - let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>, arg_idx: Option<usize>| { + for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() { if arg.is_ignore() { - return; - } - - // Avoid returning floats in x87 registers on x86 as loading and storing from x87 - // registers will quiet signalling NaNs. - if tcx.sess.target.arch == "x86" - && arg_idx.is_none() - // Intrinsics themselves are not actual "real" functions, so theres no need to - // change their ABIs. - && abi != SpecAbi::RustIntrinsic - { - match arg.layout.abi { - // Handle similar to the way arguments with an `Abi::Aggregate` abi are handled - // below, by returning arguments up to the size of a pointer (32 bits on x86) - // cast to an appropriately sized integer. - Abi::Scalar(s) if s.primitive() == Float(F32) => { - // Same size as a pointer, return in a register. - arg.cast_to(Reg::i32()); - return; - } - Abi::Scalar(s) if s.primitive() == Float(F64) => { - // Larger than a pointer, return indirectly. - arg.make_indirect(); - return; - } - Abi::ScalarPair(s1, s2) - if matches!(s1.primitive(), Float(F32 | F64)) - || matches!(s2.primitive(), Float(F32 | F64)) => - { - // Larger than a pointer, return indirectly. - arg.make_indirect(); - return; - } - _ => {} - }; - } - - match arg.layout.abi { - Abi::Aggregate { .. } => {} - - // This is a fun case! The gist of what this is doing is - // that we want callers and callees to always agree on the - // ABI of how they pass SIMD arguments. If we were to *not* - // make these arguments indirect then they'd be immediates - // in LLVM, which means that they'd used whatever the - // appropriate ABI is for the callee and the caller. That - // means, for example, if the caller doesn't have AVX - // enabled but the callee does, then passing an AVX argument - // across this boundary would cause corrupt data to show up. - // - // This problem is fixed by unconditionally passing SIMD - // arguments through memory between callers and callees - // which should get them all to agree on ABI regardless of - // target feature sets. Some more information about this - // issue can be found in #44367. - // - // Note that the intrinsic ABI is exempt here as - // that's how we connect up to LLVM and it's unstable - // anyway, we control all calls to it in libstd. - Abi::Vector { .. } - if abi != SpecAbi::RustIntrinsic && tcx.sess.target.simd_types_indirect => - { - arg.make_indirect(); - return; - } - - _ => return, - } - // Compute `Aggregate` ABI. - - let is_indirect_not_on_stack = - matches!(arg.mode, PassMode::Indirect { on_stack: false, .. }); - assert!(is_indirect_not_on_stack, "{:?}", arg); - - let size = arg.layout.size; - if !arg.layout.is_unsized() && size <= Pointer(AddressSpace::DATA).size(cx) { - // We want to pass small aggregates as immediates, but using - // an LLVM aggregate type for this leads to bad optimizations, - // so we pick an appropriately sized integer type instead. - arg.cast_to(Reg { kind: RegKind::Integer, size }); + continue; } // If we deduced that this parameter was read-only, add that to the attribute list now. @@ -778,9 +699,7 @@ fn fn_abi_adjust_for_abi<'tcx>( // The `readonly` parameter only applies to pointers, so we can only do this if the // argument was passed indirectly. (If the argument is passed directly, it's an SSA // value, so it's implicitly immutable.) - if let (Some(arg_idx), &mut PassMode::Indirect { ref mut attrs, .. }) = - (arg_idx, &mut arg.mode) - { + if let &mut PassMode::Indirect { ref mut attrs, .. } = &mut arg.mode { // The `deduced_param_attrs` list could be empty if this is a type of function // we can't deduce any parameters for, so make sure the argument index is in // bounds. @@ -791,11 +710,6 @@ fn fn_abi_adjust_for_abi<'tcx>( } } } - }; - - fixup(&mut fn_abi.ret, None); - for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() { - fixup(arg, Some(arg_idx)); } } else { fn_abi diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index a057caa9329..16fd28201c2 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -4,9 +4,8 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, ImplTraitInTraitData, Ty, TyCtxt}; +use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_span::sym; use rustc_span::symbol::kw; pub(crate) fn provide(providers: &mut Providers) { @@ -15,7 +14,6 @@ pub(crate) fn provide(providers: &mut Providers) { associated_item_def_ids, associated_items, associated_types_for_impl_traits_in_associated_fn, - associated_type_for_effects, associated_type_for_impl_trait_in_trait, impl_item_implementor_ids, ..*providers @@ -46,8 +44,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] { ) }) .copied(), - ) - .chain(tcx.associated_type_for_effects(def_id)), + ), ) } hir::ItemKind::Impl(impl_) => { @@ -73,8 +70,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] { ) }) .copied() - })) - .chain(tcx.associated_type_for_effects(def_id)), + })), ) } _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"), @@ -171,134 +167,6 @@ fn associated_item_from_impl_item_ref(impl_item_ref: &hir::ImplItemRef) -> ty::A } } -/// Given an `def_id` of a trait or a trait impl: -/// -/// If `def_id` is a trait that has `#[const_trait]`, then it synthesizes -/// a new def id corresponding to a new associated type for the effects. -/// -/// If `def_id` is an impl, then synthesize the associated type according -/// to the constness of the impl. -fn associated_type_for_effects(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> { - // don't synthesize the associated type even if the user has written `const_trait` - // if the effects feature is disabled. - if !tcx.features().effects { - return None; - } - let (feed, parent_did) = match tcx.def_kind(def_id) { - DefKind::Trait => { - let trait_def_id = def_id; - let attr = tcx.get_attr(def_id, sym::const_trait)?; - - let span = attr.span; - let trait_assoc_ty = tcx.at(span).create_def(trait_def_id, kw::Empty, DefKind::AssocTy); - - let local_def_id = trait_assoc_ty.def_id(); - let def_id = local_def_id.to_def_id(); - - // Copy span of the attribute. - trait_assoc_ty.def_ident_span(Some(span)); - - trait_assoc_ty.associated_item(ty::AssocItem { - name: kw::Empty, - kind: ty::AssocKind::Type, - def_id, - trait_item_def_id: None, - container: ty::TraitContainer, - fn_has_self_parameter: false, - opt_rpitit_info: None, - is_effects_desugaring: true, - }); - - // No default type - trait_assoc_ty.defaultness(hir::Defaultness::Default { has_value: false }); - - trait_assoc_ty.is_type_alias_impl_trait(false); - - (trait_assoc_ty, trait_def_id) - } - DefKind::Impl { .. } => { - let impl_def_id = def_id; - let trait_id = tcx.trait_id_of_impl(def_id.to_def_id())?; - - // first get the DefId of the assoc type on the trait, if there is not, - // then we don't need to generate it on the impl. - let trait_assoc_id = tcx.associated_type_for_effects(trait_id)?; - - // FIXME(effects): span - let span = tcx.def_ident_span(def_id).unwrap(); - - let impl_assoc_ty = tcx.at(span).create_def(def_id, kw::Empty, DefKind::AssocTy); - - let local_def_id = impl_assoc_ty.def_id(); - let def_id = local_def_id.to_def_id(); - - impl_assoc_ty.def_ident_span(Some(span)); - - impl_assoc_ty.associated_item(ty::AssocItem { - name: kw::Empty, - kind: ty::AssocKind::Type, - def_id, - trait_item_def_id: Some(trait_assoc_id), - container: ty::ImplContainer, - fn_has_self_parameter: false, - opt_rpitit_info: None, - is_effects_desugaring: true, - }); - - // no default value. - impl_assoc_ty.defaultness(hir::Defaultness::Final); - - // set the type of the associated type! If this is a const impl, - // we set to Maybe, otherwise we set to `Runtime`. - let type_def_id = if tcx.is_const_trait_impl_raw(impl_def_id.to_def_id()) { - tcx.require_lang_item(hir::LangItem::EffectsMaybe, Some(span)) - } else { - tcx.require_lang_item(hir::LangItem::EffectsRuntime, Some(span)) - }; - // FIXME(effects): make impls use `Min` for their effect types - impl_assoc_ty.type_of(ty::EarlyBinder::bind(Ty::new_adt( - tcx, - tcx.adt_def(type_def_id), - ty::GenericArgs::empty(), - ))); - - (impl_assoc_ty, impl_def_id) - } - def_kind => bug!( - "associated_type_for_effects: {:?} should be Trait or Impl but is {:?}", - def_id, - def_kind - ), - }; - - feed.feed_hir(); - - // visibility is public. - feed.visibility(ty::Visibility::Public); - - // Copy generics_of of the trait/impl, making the trait/impl as parent. - feed.generics_of({ - let parent_generics = tcx.generics_of(parent_did); - let parent_count = parent_generics.parent_count + parent_generics.own_params.len(); - - ty::Generics { - parent: Some(parent_did.to_def_id()), - parent_count, - own_params: vec![], - param_def_id_to_index: parent_generics.param_def_id_to_index.clone(), - has_self: false, - has_late_bound_regions: None, - host_effect_index: parent_generics.host_effect_index, - } - }); - feed.explicit_item_super_predicates(ty::EarlyBinder::bind(&[])); - - // There are no inferred outlives for the synthesized associated type. - feed.inferred_outlives_of(&[]); - - Some(feed.def_id().to_def_id()) -} - /// Given an `fn_def_id` of a trait or a trait implementation: /// /// if `fn_def_id` is a function defined inside a trait, then it synthesizes @@ -494,7 +362,6 @@ fn associated_type_for_impl_trait_in_impl( param_def_id_to_index, has_self: false, has_late_bound_regions: trait_assoc_generics.has_late_bound_regions, - host_effect_index: parent_generics.host_effect_index, } }); diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 391985ce88a..4b770d9938c 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -406,7 +406,7 @@ fn thir_abstract_const<'tcx>( tcx: TyCtxt<'tcx>, def: LocalDefId, ) -> Result<Option<ty::EarlyBinder<'tcx, ty::Const<'tcx>>>, ErrorGuaranteed> { - if !tcx.features().generic_const_exprs { + if !tcx.features().generic_const_exprs() { return Ok(None); } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index afdfa2e80c1..e755e90aa65 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -30,7 +30,8 @@ use {rustc_abi as abi, rustc_hir as hir}; use crate::errors::{ MultipleArrayFieldsSimdType, NonPrimitiveSimdType, OversizedSimdType, ZeroLengthSimdType, }; -use crate::layout_sanity_check::sanity_check_layout; + +mod invariant; pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { layout_of, ..*providers }; @@ -79,7 +80,7 @@ fn layout_of<'tcx>( record_layout_for_printing(&cx, layout); } - sanity_check_layout(&cx, &layout); + invariant::partially_check_layout(&cx, &layout); Ok(layout) } @@ -115,6 +116,11 @@ fn map_error<'tcx>( cx.tcx().dcx().delayed_bug(format!("computed layout of empty union: {ty:?}")); LayoutError::Unknown(ty) } + LayoutCalculatorError::ReprConflict => { + // packed enums are the only known trigger of this, but others might arise + cx.tcx().dcx().delayed_bug(format!("computed impossible repr (packed enum?): {ty:?}")); + LayoutError::Unknown(ty) + } }; error(cx, err) } @@ -170,12 +176,12 @@ fn layout_of_uncached<'tcx>( if let Abi::Scalar(scalar) | Abi::ScalarPair(scalar, _) = &mut layout.abi { if let Some(start) = start { scalar.valid_range_mut().start = start - .try_eval_bits(tcx, param_env) + .try_to_bits(tcx, param_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; } if let Some(end) = end { let mut end = end - .try_eval_bits(tcx, param_env) + .try_to_bits(tcx, param_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; if !include_end { end = end.wrapping_sub(1); @@ -315,7 +321,7 @@ fn layout_of_uncached<'tcx>( } let count = count - .try_eval_target_usize(tcx, param_env) + .try_to_target_usize(tcx) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; let element = cx.layout_of(element)?; let size = element diff --git a/compiler/rustc_ty_utils/src/layout_sanity_check.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index be0a7c5ee89..6cf114b74c1 100644 --- a/compiler/rustc_ty_utils/src/layout_sanity_check.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -5,7 +5,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout}; use rustc_target::abi::*; /// Enforce some basic invariants on layouts. -pub(super) fn sanity_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { +pub(super) fn partially_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { let tcx = cx.tcx(); // Type-level uninhabitedness should always imply ABI uninhabitedness. diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index dc5303317a8..8be1611bb9a 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -29,7 +29,6 @@ mod errors; mod implied_bounds; mod instance; mod layout; -mod layout_sanity_check; mod needs_drop; mod opaque_types; mod representability; diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 28a81b1b062..aa499995bcb 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -150,6 +150,16 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { }); } + // We extend the param-env of our item with the const conditions of the item, + // since we're allowed to assume `~const` bounds hold within the item itself. + if tcx.is_conditionally_const(def_id) { + predicates.extend( + tcx.const_conditions(def_id).instantiate_identity(tcx).into_iter().map( + |(trait_ref, _)| trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe), + ), + ); + } + let local_did = def_id.as_local(); let unnormalized_env = diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index f20beb79750..c06a578d8ec 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -496,8 +496,8 @@ where /// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity), /// but on an iterator of values that deref to a `TypeFoldable`. - pub fn iter_identity_copied(self) -> impl Iterator<Item = <Iter::Item as Deref>::Target> { - self.value.into_iter().map(|v| *v) + pub fn iter_identity_copied(self) -> IterIdentityCopied<Iter> { + IterIdentityCopied { it: self.value.into_iter() } } } @@ -546,6 +546,44 @@ where { } +pub struct IterIdentityCopied<Iter: IntoIterator> { + it: Iter::IntoIter, +} + +impl<Iter: IntoIterator> Iterator for IterIdentityCopied<Iter> +where + Iter::Item: Deref, + <Iter::Item as Deref>::Target: Copy, +{ + type Item = <Iter::Item as Deref>::Target; + + fn next(&mut self) -> Option<Self::Item> { + self.it.next().map(|i| *i) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.it.size_hint() + } +} + +impl<Iter: IntoIterator> DoubleEndedIterator for IterIdentityCopied<Iter> +where + Iter::IntoIter: DoubleEndedIterator, + Iter::Item: Deref, + <Iter::Item as Deref>::Target: Copy, +{ + fn next_back(&mut self) -> Option<Self::Item> { + self.it.next_back().map(|i| *i) + } +} + +impl<Iter: IntoIterator> ExactSizeIterator for IterIdentityCopied<Iter> +where + Iter::IntoIter: ExactSizeIterator, + Iter::Item: Deref, + <Iter::Item as Deref>::Target: Copy, +{ +} pub struct EarlyBinderIter<I, T> { t: T, _tcx: PhantomData<I>, diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index 07cb8b037ec..3fb7d87bcc4 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -108,7 +108,6 @@ impl<I: Interner> CanonicalVarInfo<I> { CanonicalVarKind::PlaceholderRegion(..) => false, CanonicalVarKind::Const(_) => true, CanonicalVarKind::PlaceholderConst(_) => false, - CanonicalVarKind::Effect => true, } } @@ -118,17 +117,15 @@ impl<I: Interner> CanonicalVarInfo<I> { CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) | CanonicalVarKind::Const(_) - | CanonicalVarKind::PlaceholderConst(_) - | CanonicalVarKind::Effect => false, + | CanonicalVarKind::PlaceholderConst(_) => false, } } pub fn expect_placeholder_index(self) -> usize { match self.kind { - CanonicalVarKind::Ty(_) - | CanonicalVarKind::Region(_) - | CanonicalVarKind::Const(_) - | CanonicalVarKind::Effect => panic!("expected placeholder: {self:?}"), + CanonicalVarKind::Ty(_) | CanonicalVarKind::Region(_) | CanonicalVarKind::Const(_) => { + panic!("expected placeholder: {self:?}") + } CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.var().as_usize(), CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.var().as_usize(), @@ -161,9 +158,6 @@ pub enum CanonicalVarKind<I: Interner> { /// Some kind of const inference variable. Const(UniverseIndex), - /// Effect variable `'?E`. - Effect, - /// A "placeholder" that represents "any const". PlaceholderConst(I::PlaceholderConst), } @@ -180,7 +174,6 @@ impl<I: Interner> CanonicalVarKind<I> { CanonicalVarKind::Ty(CanonicalTyVarKind::Float | CanonicalTyVarKind::Int) => { UniverseIndex::ROOT } - CanonicalVarKind::Effect => UniverseIndex::ROOT, } } @@ -205,8 +198,7 @@ impl<I: Interner> CanonicalVarKind<I> { CanonicalVarKind::PlaceholderConst(placeholder) => { CanonicalVarKind::PlaceholderConst(placeholder.with_updated_universe(ui)) } - CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) - | CanonicalVarKind::Effect => { + CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { assert_eq!(ui, UniverseIndex::ROOT); self } @@ -311,10 +303,6 @@ impl<I: Interner> CanonicalVarValues<I> { Region::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i)) .into() } - CanonicalVarKind::Effect => { - Const::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i)) - .into() - } CanonicalVarKind::Const(_) | CanonicalVarKind::PlaceholderConst(_) => { Const::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i)) .into() diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 7a8c612057f..03dfe547ced 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -84,32 +84,12 @@ rustc_index::newtype_index! { pub struct ConstVid {} } -rustc_index::newtype_index! { - /// An **effect** **v**ariable **ID**. - /// - /// Handling effect infer variables happens separately from const infer variables - /// because we do not want to reuse any of the const infer machinery. If we try to - /// relate an effect variable with a normal one, we would ICE, which can catch bugs - /// where we are not correctly using the effect var for an effect param. Fallback - /// is also implemented on top of having separate effect and normal const variables. - #[encodable] - #[orderable] - #[debug_format = "?{}e"] - #[gate_rustc_only] - pub struct EffectVid {} -} - /// An inference variable for a const, for use in const generics. #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable))] pub enum InferConst { /// Infer the value of the const. Var(ConstVid), - /// Infer the value of the effect. - /// - /// For why this is separate from the `Var` variant above, see the - /// documentation on `EffectVid`. - EffectVar(EffectVid), /// A fresh const variable. See `infer::freshen` for more details. Fresh(u32), } @@ -118,7 +98,6 @@ impl fmt::Debug for InferConst { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { InferConst::Var(var) => write!(f, "{var:?}"), - InferConst::EffectVar(var) => write!(f, "{var:?}"), InferConst::Fresh(var) => write!(f, "Fresh({var:?})"), } } @@ -128,7 +107,7 @@ impl fmt::Debug for InferConst { impl<CTX> HashStable<CTX> for InferConst { fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { match self { - InferConst::Var(_) | InferConst::EffectVar(_) => { + InferConst::Var(_) => { panic!("const variables should not be hashed: {self:?}") } InferConst::Fresh(i) => i.hash_stable(hcx, hasher), diff --git a/compiler/rustc_type_ir/src/effects.rs b/compiler/rustc_type_ir/src/effects.rs deleted file mode 100644 index ab43533dd86..00000000000 --- a/compiler/rustc_type_ir/src/effects.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::Interner; -use crate::inherent::*; -use crate::lang_items::TraitSolverLangItem::{EffectsMaybe, EffectsNoRuntime, EffectsRuntime}; - -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum EffectKind { - Maybe, - Runtime, - NoRuntime, -} - -impl EffectKind { - pub fn try_from_def_id<I: Interner>(cx: I, def_id: I::DefId) -> Option<EffectKind> { - if cx.is_lang_item(def_id, EffectsMaybe) { - Some(EffectKind::Maybe) - } else if cx.is_lang_item(def_id, EffectsRuntime) { - Some(EffectKind::Runtime) - } else if cx.is_lang_item(def_id, EffectsNoRuntime) { - Some(EffectKind::NoRuntime) - } else { - None - } - } - - pub fn to_def_id<I: Interner>(self, cx: I) -> I::DefId { - let lang_item = match self { - EffectKind::Maybe => EffectsMaybe, - EffectKind::NoRuntime => EffectsNoRuntime, - EffectKind::Runtime => EffectsRuntime, - }; - - cx.require_lang_item(lang_item) - } - - pub fn try_from_ty<I: Interner>(cx: I, ty: I::Ty) -> Option<EffectKind> { - if let crate::Adt(def, _) = ty.kind() { - Self::try_from_def_id(cx, def.def_id()) - } else { - None - } - } - - pub fn to_ty<I: Interner>(self, cx: I) -> I::Ty { - I::Ty::new_adt(cx, cx.adt_def(self.to_def_id(cx)), Default::default()) - } - - /// Returns an intersection between two effect kinds. If one effect kind - /// is more permissive than the other (e.g. `Maybe` vs `Runtime`), this - /// returns the less permissive effect kind (`Runtime`). - pub fn intersection(a: Self, b: Self) -> Option<Self> { - use EffectKind::*; - match (a, b) { - (Maybe, x) | (x, Maybe) => Some(x), - (Runtime, Runtime) => Some(Runtime), - (NoRuntime, NoRuntime) => Some(NoRuntime), - (Runtime, NoRuntime) | (NoRuntime, Runtime) => None, - } - } -} diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index dac45ff2aba..72d392ecd7b 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -4,7 +4,6 @@ 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 _}; @@ -130,70 +129,6 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> { return; } - // HACK(effects): The following code is required to get implied bounds for effects associated - // types to work with super traits. - // - // Suppose `data` is a trait predicate with the form `<T as Tr>::Fx: EffectsCompat<somebool>` - // and we know that `trait Tr: ~const SuperTr`, we need to elaborate this predicate into - // `<T as SuperTr>::Fx: EffectsCompat<somebool>`. - // - // Since the semantics for elaborating bounds about effects is equivalent to elaborating - // bounds about super traits (elaborate `T: Tr` into `T: SuperTr`), we place effects elaboration - // next to super trait elaboration. - if cx.is_lang_item(data.def_id(), TraitSolverLangItem::EffectsCompat) - && matches!(self.mode, Filter::All) - { - // first, ensure that the predicate we've got looks like a `<T as Tr>::Fx: EffectsCompat<somebool>`. - if let ty::Alias(ty::AliasTyKind::Projection, alias_ty) = data.self_ty().kind() - { - // look for effects-level bounds that look like `<Self as Tr>::Fx: TyCompat<<Self as SuperTr>::Fx>` - // on the trait, which is proof to us that `Tr: ~const SuperTr`. We're looking for bounds on the - // associated trait, so we use `explicit_implied_predicates_of` since it gives us more than just - // `Self: SuperTr` bounds. - let bounds = cx.explicit_implied_predicates_of(cx.parent(alias_ty.def_id)); - - // instantiate the implied bounds, so we get `<T as Tr>::Fx` and not `<Self as Tr>::Fx`. - let elaborated = bounds.iter_instantiated(cx, alias_ty.args).filter_map( - |(clause, _)| { - let ty::ClauseKind::Trait(tycompat_bound) = - clause.kind().skip_binder() - else { - return None; - }; - if !cx.is_lang_item( - tycompat_bound.def_id(), - TraitSolverLangItem::EffectsTyCompat, - ) { - return None; - } - - // extract `<T as SuperTr>::Fx` from the `TyCompat` bound. - let supertrait_effects_ty = - tycompat_bound.trait_ref.args.type_at(1); - let ty::Alias(ty::AliasTyKind::Projection, supertrait_alias_ty) = - supertrait_effects_ty.kind() - else { - return None; - }; - - // The self types (`T`) must be equal for `<T as Tr>::Fx` and `<T as SuperTr>::Fx`. - if supertrait_alias_ty.self_ty() != alias_ty.self_ty() { - return None; - }; - - // replace the self type in the original bound `<T as Tr>::Fx: EffectsCompat<somebool>` - // to the effects type of the super trait. (`<T as SuperTr>::Fx`) - let elaborated_bound = data.with_self_ty(cx, supertrait_effects_ty); - Some( - elaboratable - .child(bound_clause.rebind(elaborated_bound).upcast(cx)), - ) - }, - ); - self.extend_deduped(elaborated); - } - } - let map_to_child_clause = |(index, (clause, span)): (usize, (I::Clause, I::Span))| { elaboratable.child_with_derived_cause( @@ -220,6 +155,16 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> { ), }; } + // `T: ~const Trait` implies `T: ~const Supertrait`. + ty::ClauseKind::HostEffect(data) => self.extend_deduped( + cx.implied_const_bounds(data.def_id()).iter_identity().map(|trait_ref| { + elaboratable.child( + trait_ref + .to_host_effect_clause(cx, data.host) + .instantiate_supertrait(cx, bound_clause.rebind(data.trait_ref)), + ) + }), + ), ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => { // We know that `T: 'a` for some type `T`. We can // often elaborate this. For example, if we know that diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index 8a6d37b7d23..cdff77f742d 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -27,10 +27,9 @@ impl<T> ExpectedFound<T> { #[cfg_attr(feature = "nightly", rustc_pass_by_value)] pub enum TypeError<I: Interner> { Mismatch, - ConstnessMismatch(ExpectedFound<ty::BoundConstness>), - PolarityMismatch(ExpectedFound<ty::PredicatePolarity>), - SafetyMismatch(ExpectedFound<I::Safety>), - AbiMismatch(ExpectedFound<I::Abi>), + PolarityMismatch(#[type_visitable(ignore)] ExpectedFound<ty::PredicatePolarity>), + SafetyMismatch(#[type_visitable(ignore)] ExpectedFound<I::Safety>), + AbiMismatch(#[type_visitable(ignore)] ExpectedFound<I::Abi>), Mutability, ArgumentMutability(usize), TupleSize(ExpectedFound<usize>), @@ -73,9 +72,9 @@ impl<I: Interner> TypeError<I> { pub fn must_include_note(self) -> bool { use self::TypeError::*; match self { - CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | ConstnessMismatch(_) - | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_) - | ArgumentSorts(..) | Sorts(_) | VariadicMismatch(_) | TargetFeatureCast(_) => false, + CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | PolarityMismatch(_) | Mismatch + | AbiMismatch(_) | FixedArraySize(_) | ArgumentSorts(..) | Sorts(_) + | VariadicMismatch(_) | TargetFeatureCast(_) => false, Mutability | ArgumentMutability(_) diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index b9f5cde653e..7c6a3c65ebf 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -38,10 +38,6 @@ pub trait InferCtxtLike: Sized { &self, vid: ty::ConstVid, ) -> <Self::Interner as Interner>::Const; - fn opportunistic_resolve_effect_var( - &self, - vid: ty::EffectVid, - ) -> <Self::Interner as Interner>::Const; fn opportunistic_resolve_lt_var( &self, vid: ty::RegionVid, @@ -71,7 +67,6 @@ pub trait InferCtxtLike: Sized { fn equate_int_vids_raw(&self, a: ty::IntVid, b: ty::IntVid); fn equate_float_vids_raw(&self, a: ty::FloatVid, b: ty::FloatVid); fn equate_const_vids_raw(&self, a: ty::ConstVid, b: ty::ConstVid); - fn equate_effect_vids_raw(&self, a: ty::EffectVid, b: ty::EffectVid); fn instantiate_ty_var_raw<R: PredicateEmittingRelation<Self>>( &self, @@ -83,11 +78,6 @@ pub trait InferCtxtLike: Sized { ) -> RelateResult<Self::Interner, ()>; fn instantiate_int_var_raw(&self, vid: ty::IntVid, value: ty::IntVarValue); fn instantiate_float_var_raw(&self, vid: ty::FloatVid, value: ty::FloatVarValue); - fn instantiate_effect_var_raw( - &self, - vid: ty::EffectVid, - value: <Self::Interner as Interner>::Const, - ); fn instantiate_const_var_raw<R: PredicateEmittingRelation<Self>>( &self, relation: &mut R, diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index f7875bb5152..5af1aa2f8fa 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -208,14 +208,14 @@ pub trait Tys<I: Interner<Tys = Self>>: fn output(self) -> I::Ty; } -pub trait Abi<I: Interner<Abi = Self>>: Copy + Debug + Hash + Eq + Relate<I> { +pub trait Abi<I: Interner<Abi = Self>>: Copy + Debug + Hash + Eq { fn rust() -> Self; /// Whether this ABI is `extern "Rust"`. fn is_rust(self) -> bool; } -pub trait Safety<I: Interner<Safety = Self>>: Copy + Debug + Hash + Eq + Relate<I> { +pub trait Safety<I: Interner<Safety = Self>>: Copy + Debug + Hash + Eq { fn safe() -> Self; fn is_safe(self) -> bool; @@ -468,6 +468,14 @@ pub trait Clause<I: Interner<Clause = Self>>: .transpose() } + fn as_host_effect_clause(self) -> Option<ty::Binder<I, ty::HostEffectPredicate<I>>> { + self.kind() + .map_bound( + |clause| if let ty::ClauseKind::HostEffect(t) = clause { Some(t) } else { None }, + ) + .transpose() + } + fn as_projection_clause(self) -> Option<ty::Binder<I, ty::ProjectionPredicate<I>>> { self.kind() .map_bound( diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index f06017d7e5c..6a8113b38b7 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -15,9 +15,7 @@ use crate::solve::{ CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult, SolverMode, }; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; -use crate::{ - search_graph, {self as ty}, -}; +use crate::{self as ty, search_graph}; pub trait Interner: Sized @@ -26,6 +24,7 @@ pub trait Interner: + IrPrint<ty::AliasTerm<Self>> + IrPrint<ty::TraitRef<Self>> + IrPrint<ty::TraitPredicate<Self>> + + IrPrint<ty::HostEffectPredicate<Self>> + IrPrint<ty::ExistentialTraitRef<Self>> + IrPrint<ty::ExistentialProjection<Self>> + IrPrint<ty::ProjectionPredicate<Self>> @@ -173,6 +172,10 @@ pub trait Interner: fn debug_assert_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs); + /// Assert that the args from an `ExistentialTraitRef` or `ExistentialProjection` + /// are compatible with the `DefId`. + fn debug_assert_existential_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs); + fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output where I: Iterator<Item = T>, @@ -226,6 +229,16 @@ pub trait Interner: def_id: Self::DefId, ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>; + fn is_const_impl(self, def_id: Self::DefId) -> bool; + fn const_conditions( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = ty::Binder<Self, ty::TraitRef<Self>>>>; + fn implied_const_bounds( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = ty::Binder<Self, ty::TraitRef<Self>>>>; + fn has_target_features(self, def_id: Self::DefId) -> bool; fn require_lang_item(self, lang_item: TraitSolverLangItem) -> Self::DefId; diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs index d57d0816680..0c71f3a3df2 100644 --- a/compiler/rustc_type_ir/src/ir_print.rs +++ b/compiler/rustc_type_ir/src/ir_print.rs @@ -2,8 +2,8 @@ use std::fmt; use crate::{ AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, - Interner, NormalizesTo, OutlivesPredicate, ProjectionPredicate, SubtypePredicate, - TraitPredicate, TraitRef, + HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, ProjectionPredicate, + SubtypePredicate, TraitPredicate, TraitRef, }; pub trait IrPrint<T> { @@ -53,6 +53,7 @@ define_display_via_print!( NormalizesTo, SubtypePredicate, CoercePredicate, + HostEffectPredicate, AliasTy, AliasTerm, FnSig, diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index c680c844746..d6ca22a90a4 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -20,13 +20,6 @@ pub enum TraitSolverLangItem { Destruct, DiscriminantKind, DynMetadata, - EffectsCompat, - EffectsIntersection, - EffectsIntersectionOutput, - EffectsMaybe, - EffectsNoRuntime, - EffectsRuntime, - EffectsTyCompat, Fn, FnMut, FnOnce, diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 9e6d1f424ba..e7ca24178cb 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -43,7 +43,6 @@ mod macros; mod binder; mod canonical; mod const_kind; -mod effects; mod flags; mod generic_arg; mod infer_ctxt; @@ -67,7 +66,6 @@ pub use canonical::*; #[cfg(feature = "nightly")] pub use codec::*; pub use const_kind::*; -pub use effects::*; pub use flags::*; pub use generic_arg::*; pub use infer_ctxt::*; diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 8146181df6c..c3164550348 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -111,6 +111,13 @@ impl<I: Interner> ty::Binder<I, TraitRef<I>> { pub fn def_id(&self) -> I::DefId { self.skip_binder().def_id } + + pub fn to_host_effect_clause(self, cx: I, host: HostPolarity) -> I::Clause { + self.map_bound(|trait_ref| { + ty::ClauseKind::HostEffect(HostEffectPredicate { trait_ref, host }) + }) + .upcast(cx) + } } #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] @@ -289,9 +296,26 @@ impl<I: Interner> ty::Binder<I, ExistentialPredicate<I>> { pub struct ExistentialTraitRef<I: Interner> { pub def_id: I::DefId, pub args: I::GenericArgs, + /// This field exists to prevent the creation of `ExistentialTraitRef` without + /// calling [`ExistentialTraitRef::new_from_args`]. + _use_existential_trait_ref_new_instead: (), } impl<I: Interner> ExistentialTraitRef<I> { + pub fn new_from_args(interner: I, trait_def_id: I::DefId, args: I::GenericArgs) -> Self { + interner.debug_assert_existential_args_compatible(trait_def_id, args); + Self { def_id: trait_def_id, args, _use_existential_trait_ref_new_instead: () } + } + + pub fn new( + interner: I, + trait_def_id: I::DefId, + args: impl IntoIterator<Item: Into<I::GenericArg>>, + ) -> Self { + let args = interner.mk_args_from_iter(args.into_iter().map(Into::into)); + Self::new_from_args(interner, trait_def_id, args) + } + pub fn erase_self_ty(interner: I, trait_ref: TraitRef<I>) -> ExistentialTraitRef<I> { // Assert there is a Self. trait_ref.args.type_at(0); @@ -299,6 +323,7 @@ impl<I: Interner> ExistentialTraitRef<I> { ExistentialTraitRef { def_id: trait_ref.def_id, args: interner.mk_args(&trait_ref.args.as_slice()[1..]), + _use_existential_trait_ref_new_instead: (), } } @@ -336,9 +361,33 @@ pub struct ExistentialProjection<I: Interner> { pub def_id: I::DefId, pub args: I::GenericArgs, pub term: I::Term, + + /// This field exists to prevent the creation of `ExistentialProjection` + /// without using [`ExistentialProjection::new_from_args`]. + use_existential_projection_new_instead: (), } impl<I: Interner> ExistentialProjection<I> { + pub fn new_from_args( + interner: I, + def_id: I::DefId, + args: I::GenericArgs, + term: I::Term, + ) -> ExistentialProjection<I> { + interner.debug_assert_existential_args_compatible(def_id, args); + Self { def_id, args, term, use_existential_projection_new_instead: () } + } + + pub fn new( + interner: I, + def_id: I::DefId, + args: impl IntoIterator<Item: Into<I::GenericArg>>, + term: I::Term, + ) -> ExistentialProjection<I> { + let args = interner.mk_args_from_iter(args.into_iter().map(Into::into)); + Self::new_from_args(interner, def_id, args, term) + } + /// Extracts the underlying existential trait reference from this projection. /// For example, if this is a projection of `exists T. <T as Iterator>::Item == X`, /// then this function would return an `exists T. T: Iterator` existential trait @@ -347,7 +396,7 @@ impl<I: Interner> ExistentialProjection<I> { let def_id = interner.parent(self.def_id); let args_count = interner.generics_of(def_id).count() - 1; let args = interner.mk_args(&self.args.as_slice()[..args_count]); - ExistentialTraitRef { def_id, args } + ExistentialTraitRef { def_id, args, _use_existential_trait_ref_new_instead: () } } pub fn with_self_ty(&self, interner: I, self_ty: I::Ty) -> ProjectionPredicate<I> { @@ -372,6 +421,7 @@ impl<I: Interner> ExistentialProjection<I> { def_id: projection_predicate.projection_term.def_id, args: interner.mk_args(&projection_predicate.projection_term.args.as_slice()[1..]), term: projection_predicate.term, + use_existential_projection_new_instead: (), } } } @@ -702,6 +752,64 @@ impl<I: Interner> fmt::Debug for NormalizesTo<I> { } } +#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +pub struct HostEffectPredicate<I: Interner> { + pub trait_ref: ty::TraitRef<I>, + pub host: HostPolarity, +} + +impl<I: Interner> HostEffectPredicate<I> { + pub fn self_ty(self) -> I::Ty { + self.trait_ref.self_ty() + } + + pub fn with_self_ty(self, interner: I, self_ty: I::Ty) -> Self { + Self { trait_ref: self.trait_ref.with_self_ty(interner, self_ty), ..self } + } + + pub fn def_id(self) -> I::DefId { + self.trait_ref.def_id + } +} + +impl<I: Interner> ty::Binder<I, HostEffectPredicate<I>> { + pub fn def_id(self) -> I::DefId { + // Ok to skip binder since trait `DefId` does not care about regions. + self.skip_binder().def_id() + } + + pub fn self_ty(self) -> ty::Binder<I, I::Ty> { + self.map_bound(|trait_ref| trait_ref.self_ty()) + } + + #[inline] + pub fn host(self) -> HostPolarity { + self.skip_binder().host + } +} + +#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +pub enum HostPolarity { + /// May be called in const environments if the callee is const. + Maybe, + /// Always allowed to be called in const environments. + Const, +} + +impl HostPolarity { + pub fn satisfies(self, goal: HostPolarity) -> bool { + match (self, goal) { + (HostPolarity::Const, HostPolarity::Const | HostPolarity::Maybe) => true, + (HostPolarity::Maybe, HostPolarity::Maybe) => true, + (HostPolarity::Maybe, HostPolarity::Const) => false, + } + } +} + /// Encodes that `a` must be a subtype of `b`. The `a_is_expected` flag indicates /// whether the `a` type is the type that we should label as "expected" when /// presenting user diagnostics. @@ -726,9 +834,9 @@ pub struct CoercePredicate<I: Interner> { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))] pub enum BoundConstness { - /// `Type: Trait` - NotConst, /// `Type: const Trait` + /// + /// A bound is required to be unconditionally const, even in a runtime function. Const, /// `Type: ~const Trait` /// @@ -739,7 +847,6 @@ pub enum BoundConstness { impl BoundConstness { pub fn as_str(self) -> &'static str { match self { - Self::NotConst => "", Self::Const => "const", Self::ConstIfConst => "~const", } @@ -749,7 +856,6 @@ impl BoundConstness { impl fmt::Display for BoundConstness { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::NotConst => f.write_str("normal"), Self::Const => f.write_str("const"), Self::ConstIfConst => f.write_str("~const"), } diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index 46202dbb0f2..21f4456abd1 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -37,6 +37,12 @@ pub enum ClauseKind<I: Interner> { /// Constant initializer must evaluate successfully. ConstEvaluatable(I::Const), + + /// Enforces the constness of the predicate we're calling. Like a projection + /// goal from a where clause, it's always going to be paired with a + /// corresponding trait clause; this just enforces the *constness* of that + /// implementation. + HostEffect(ty::HostEffectPredicate<I>), } #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] @@ -110,6 +116,7 @@ impl<I: Interner> fmt::Debug for ClauseKind<I> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ClauseKind::ConstArgHasType(ct, ty) => write!(f, "ConstArgHasType({ct:?}, {ty:?})"), + ClauseKind::HostEffect(data) => data.fmt(f), ClauseKind::Trait(a) => a.fmt(f), ClauseKind::RegionOutlives(pair) => pair.fmt(f), ClauseKind::TypeOutlives(pair) => pair.fmt(f), diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index e1f3e493e36..ad17911830b 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -174,12 +174,17 @@ impl<I: Interner> Relate<I> for ty::FnSig<I> { ExpectedFound::new(true, a, b) })); } - let safety = relation.relate(a.safety, b.safety)?; - let abi = relation.relate(a.abi, b.abi)?; + + if a.safety != b.safety { + return Err(TypeError::SafetyMismatch(ExpectedFound::new(true, a.safety, b.safety))); + } + + if a.abi != b.abi { + return Err(TypeError::AbiMismatch(ExpectedFound::new(true, a.abi, b.abi))); + }; let a_inputs = a.inputs(); let b_inputs = b.inputs(); - if a_inputs.len() != b_inputs.len() { return Err(TypeError::ArgCount); } @@ -212,26 +217,12 @@ impl<I: Interner> Relate<I> for ty::FnSig<I> { Ok(ty::FnSig { inputs_and_output: cx.mk_type_list_from_iter(inputs_and_output)?, c_variadic: a.c_variadic, - safety, - abi, + safety: a.safety, + abi: a.abi, }) } } -impl<I: Interner> Relate<I> for ty::BoundConstness { - fn relate<R: TypeRelation<I>>( - _relation: &mut R, - a: ty::BoundConstness, - b: ty::BoundConstness, - ) -> RelateResult<I, ty::BoundConstness> { - if a != b { - Err(TypeError::ConstnessMismatch(ExpectedFound::new(true, a, b))) - } else { - Ok(a) - } - } -} - impl<I: Interner> Relate<I> for ty::AliasTy<I> { fn relate<R: TypeRelation<I>>( relation: &mut R, @@ -333,7 +324,7 @@ impl<I: Interner> Relate<I> for ty::ExistentialProjection<I> { a.args, b.args, )?; - Ok(ty::ExistentialProjection { def_id: a.def_id, args, term }) + Ok(ty::ExistentialProjection::new_from_args(relation.cx(), a.def_id, args, term)) } } } @@ -373,7 +364,7 @@ impl<I: Interner> Relate<I> for ty::ExistentialTraitRef<I> { })) } else { let args = relate_args_invariantly(relation, a.args, b.args)?; - Ok(ty::ExistentialTraitRef { def_id: a.def_id, args }) + Ok(ty::ExistentialTraitRef::new_from_args(relation.cx(), a.def_id, args)) } } } @@ -659,29 +650,18 @@ impl<I: Interner, T: Relate<I>> Relate<I> for ty::Binder<I, T> { } } -impl<I: Interner> Relate<I> for ty::PredicatePolarity { - fn relate<R: TypeRelation<I>>( - _relation: &mut R, - a: ty::PredicatePolarity, - b: ty::PredicatePolarity, - ) -> RelateResult<I, ty::PredicatePolarity> { - if a != b { - Err(TypeError::PolarityMismatch(ExpectedFound::new(true, a, b))) - } else { - Ok(a) - } - } -} - impl<I: Interner> Relate<I> for ty::TraitPredicate<I> { fn relate<R: TypeRelation<I>>( relation: &mut R, a: ty::TraitPredicate<I>, b: ty::TraitPredicate<I>, ) -> RelateResult<I, ty::TraitPredicate<I>> { - Ok(ty::TraitPredicate { - trait_ref: relation.relate(a.trait_ref, b.trait_ref)?, - polarity: relation.relate(a.polarity, b.polarity)?, - }) + let trait_ref = relation.relate(a.trait_ref, b.trait_ref)?; + if a.polarity != b.polarity { + return Err(TypeError::PolarityMismatch(ExpectedFound::new( + true, a.polarity, b.polarity, + ))); + } + Ok(ty::TraitPredicate { trait_ref, polarity: a.polarity }) } } diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 60a953801a4..17a3912730f 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -179,23 +179,9 @@ where Ok(a) } - ( - ty::ConstKind::Infer(ty::InferConst::EffectVar(a_vid)), - ty::ConstKind::Infer(ty::InferConst::EffectVar(b_vid)), - ) => { - infcx.equate_effect_vids_raw(a_vid, b_vid); - Ok(a) - } - // All other cases of inference with other variables are errors. - ( - ty::ConstKind::Infer(ty::InferConst::Var(_) | ty::InferConst::EffectVar(_)), - ty::ConstKind::Infer(_), - ) - | ( - ty::ConstKind::Infer(_), - ty::ConstKind::Infer(ty::InferConst::Var(_) | ty::InferConst::EffectVar(_)), - ) => { + (ty::ConstKind::Infer(ty::InferConst::Var(_)), ty::ConstKind::Infer(_)) + | (ty::ConstKind::Infer(_), ty::ConstKind::Infer(ty::InferConst::Var(_))) => { panic!( "tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var): {a:?} and {b:?}" ) @@ -211,16 +197,6 @@ where Ok(a) } - (ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)), _) => { - infcx.instantiate_effect_var_raw(vid, b); - Ok(b) - } - - (_, ty::ConstKind::Infer(ty::InferConst::EffectVar(vid))) => { - infcx.instantiate_effect_var_raw(vid, a); - Ok(a) - } - (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..)) if infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver() => { diff --git a/compiler/rustc_type_ir/src/solve/inspect.rs b/compiler/rustc_type_ir/src/solve/inspect.rs index 138ba8bac88..d0e618dc6f9 100644 --- a/compiler/rustc_type_ir/src/solve/inspect.rs +++ b/compiler/rustc_type_ir/src/solve/inspect.rs @@ -23,9 +23,7 @@ use std::hash::Hash; use derive_where::derive_where; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; -use crate::solve::{ - CandidateSource, CanonicalInput, Certainty, Goal, GoalSource, QueryInput, QueryResult, -}; +use crate::solve::{CandidateSource, CanonicalInput, Certainty, Goal, GoalSource, QueryResult}; use crate::{Canonical, CanonicalVarValues, Interner}; /// Some `data` together with information about how they relate to the input @@ -69,15 +67,10 @@ pub struct CanonicalGoalEvaluation<I: Interner> { #[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)] pub enum CanonicalGoalEvaluationKind<I: Interner> { Overflow, - Evaluation { final_revision: CanonicalGoalEvaluationStep<I> }, -} - -#[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)] -pub struct CanonicalGoalEvaluationStep<I: Interner> { - pub instantiated_goal: QueryInput<I, I::Predicate>, - - /// The actual evaluation of the goal, always `ProbeKind::Root`. - pub evaluation: Probe<I>, + Evaluation { + /// This is always `ProbeKind::Root`. + final_revision: Probe<I>, + }, } /// A self-contained computation during trait solving. This either diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index b7f6ef4ffbb..499e6d3dd37 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -861,7 +861,11 @@ pub struct TypeAndMut<I: Interner> { pub struct FnSig<I: Interner> { pub inputs_and_output: I::Tys, pub c_variadic: bool, + #[type_visitable(ignore)] + #[type_foldable(identity)] pub safety: I::Safety, + #[type_visitable(ignore)] + #[type_foldable(identity)] pub abi: I::Abi, } diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index 09a43b17955..10b164eae02 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -372,8 +372,12 @@ pub struct CoroutineClosureSignature<I: Interner> { /// Always false pub c_variadic: bool, /// Always `Normal` (safe) + #[type_visitable(ignore)] + #[type_foldable(identity)] pub safety: I::Safety, /// Always `RustCall` + #[type_visitable(ignore)] + #[type_foldable(identity)] pub abi: I::Abi, } diff --git a/compiler/rustc_type_ir_macros/src/lib.rs b/compiler/rustc_type_ir_macros/src/lib.rs index 1a0a2479f6f..aaf69e2648d 100644 --- a/compiler/rustc_type_ir_macros/src/lib.rs +++ b/compiler/rustc_type_ir_macros/src/lib.rs @@ -1,18 +1,73 @@ -use quote::quote; -use syn::parse_quote; +use quote::{ToTokens, quote}; use syn::visit_mut::VisitMut; +use syn::{Attribute, parse_quote}; use synstructure::decl_derive; decl_derive!( - [TypeFoldable_Generic] => type_foldable_derive + [TypeVisitable_Generic, attributes(type_visitable)] => type_visitable_derive ); decl_derive!( - [TypeVisitable_Generic] => type_visitable_derive + [TypeFoldable_Generic, attributes(type_foldable)] => type_foldable_derive ); decl_derive!( [Lift_Generic] => lift_derive ); +fn has_ignore_attr(attrs: &[Attribute], name: &'static str, meta: &'static str) -> bool { + let mut ignored = false; + attrs.iter().for_each(|attr| { + if !attr.path().is_ident(name) { + return; + } + let _ = attr.parse_nested_meta(|nested| { + if nested.path.is_ident(meta) { + ignored = true; + } + Ok(()) + }); + }); + + ignored +} + +fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + if let syn::Data::Union(_) = s.ast().data { + panic!("cannot derive on union") + } + + if !s.ast().generics.type_params().any(|ty| ty.ident == "I") { + s.add_impl_generic(parse_quote! { I }); + } + + s.filter(|bi| !has_ignore_attr(&bi.ast().attrs, "type_visitable", "ignore")); + + s.add_where_predicate(parse_quote! { I: Interner }); + s.add_bounds(synstructure::AddBounds::Fields); + let body_visit = s.each(|bind| { + quote! { + match ::rustc_ast_ir::visit::VisitorResult::branch( + ::rustc_type_ir::visit::TypeVisitable::visit_with(#bind, __visitor) + ) { + ::core::ops::ControlFlow::Continue(()) => {}, + ::core::ops::ControlFlow::Break(r) => { + return ::rustc_ast_ir::visit::VisitorResult::from_residual(r); + }, + } + } + }); + s.bind_with(|_| synstructure::BindStyle::Move); + + s.bound_impl(quote!(::rustc_type_ir::visit::TypeVisitable<I>), quote! { + fn visit_with<__V: ::rustc_type_ir::visit::TypeVisitor<I>>( + &self, + __visitor: &mut __V + ) -> __V::Result { + match *self { #body_visit } + <__V::Result as ::rustc_ast_ir::visit::VisitorResult>::output() + } + }) +} + fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { if let syn::Data::Union(_) = s.ast().data { panic!("cannot derive on union") @@ -29,12 +84,23 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke let bindings = vi.bindings(); vi.construct(|_, index| { let bind = &bindings[index]; - quote! { - ::rustc_type_ir::fold::TypeFoldable::try_fold_with(#bind, __folder)? + + // retain value of fields with #[type_foldable(identity)] + if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") { + bind.to_token_stream() + } else { + quote! { + ::rustc_type_ir::fold::TypeFoldable::try_fold_with(#bind, __folder)? + } } }) }); + // We filter fields which get ignored and don't require them to implement + // `TypeFoldable`. We do so after generating `body_fold` as we still need + // to generate code for them. + s.filter(|bi| !has_ignore_attr(&bi.ast().attrs, "type_foldable", "identity")); + s.add_bounds(synstructure::AddBounds::Fields); s.bound_impl(quote!(::rustc_type_ir::fold::TypeFoldable<I>), quote! { fn try_fold_with<__F: ::rustc_type_ir::fold::FallibleTypeFolder<I>>( self, @@ -113,39 +179,3 @@ fn lift(mut ty: syn::Type) -> syn::Type { ty } - -fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { - if let syn::Data::Union(_) = s.ast().data { - panic!("cannot derive on union") - } - - if !s.ast().generics.type_params().any(|ty| ty.ident == "I") { - s.add_impl_generic(parse_quote! { I }); - } - - s.add_where_predicate(parse_quote! { I: Interner }); - s.add_bounds(synstructure::AddBounds::Fields); - let body_visit = s.each(|bind| { - quote! { - match ::rustc_ast_ir::visit::VisitorResult::branch( - ::rustc_type_ir::visit::TypeVisitable::visit_with(#bind, __visitor) - ) { - ::core::ops::ControlFlow::Continue(()) => {}, - ::core::ops::ControlFlow::Break(r) => { - return ::rustc_ast_ir::visit::VisitorResult::from_residual(r); - }, - } - } - }); - s.bind_with(|_| synstructure::BindStyle::Move); - - s.bound_impl(quote!(::rustc_type_ir::visit::TypeVisitable<I>), quote! { - fn visit_with<__V: ::rustc_type_ir::visit::TypeVisitor<I>>( - &self, - __visitor: &mut __V - ) -> __V::Result { - match *self { #body_visit } - <__V::Result as ::rustc_ast_ir::visit::VisitorResult>::output() - } - }) -} diff --git a/compiler/stable_mir/README.md b/compiler/stable_mir/README.md index 31dee955f49..ab2546e377a 100644 --- a/compiler/stable_mir/README.md +++ b/compiler/stable_mir/README.md @@ -1,93 +1,33 @@ -This crate is regularly synced with its mirror in the rustc repo at `compiler/rustc_smir`. +This crate is currently developed in-tree together with the compiler. -We use `git subtree` for this to preserve commits and allow the rustc repo to -edit these crates without having to touch this repo. This keeps the crates compiling -while allowing us to independently work on them here. The effort of keeping them in -sync is pushed entirely onto us, without affecting rustc workflows negatively. -This may change in the future, but changes to policy should only be done via a -compiler team MCP. +Our goal is to start publishing `stable_mir` into crates.io. +Until then, users will use this as any other rustc crate, by installing +the rustup component `rustc-dev`, and declaring `stable-mir` as an external crate. -## Instructions for working on this crate locally - -Since the crate is the same in the rustc repo and here, the dependencies on rustc_* crates -will only either work here or there, but never in both places at the same time. Thus we use -optional dependencies on the rustc_* crates, requiring local development to use - -``` -cargo build --no-default-features -Zavoid-dev-deps -``` - -in order to compile successfully. - -## Instructions for syncing - -### Updating this repository - -In the rustc repo, execute - -``` -git subtree push --prefix=compiler/rustc_smir url_to_your_fork_of_project_stable_mir some_feature_branch -``` - -and then open a PR of your `some_feature_branch` against https://github.com/rust-lang/project-stable-mir - -### Updating the rustc library - -First we need to bump our stack limit, as the rustc repo otherwise quickly hits that: - -``` -ulimit -s 60000 -``` - -#### Maximum function recursion depth (1000) reached - -Then we need to disable `dash` as the default shell for sh scripts, as otherwise we run into a -hard limit of a recursion depth of 1000: - -``` -sudo dpkg-reconfigure dash -``` - -and then select `No` to disable dash. - - -#### Patching your `git worktree` - -The regular git worktree does not scale to repos of the size of the rustc repo. -So download the `git-subtree.sh` from https://github.com/gitgitgadget/git/pull/493/files and run - -``` -sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree -sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree -sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree -``` - -#### Actually doing a sync - -In the rustc repo, execute - -``` -git subtree pull --prefix=compiler/rustc_smir https://github.com/rust-lang/project-stable-mir smir -``` - -Note: only ever sync to rustc from the project-stable-mir's `smir` branch. Do not sync with your own forks. - -Then open a PR against rustc just like a regular PR. +See the StableMIR ["Getting Started"](https://rust-lang.github.io/project-stable-mir/getting-started.html) +guide for more information. ## Stable MIR Design -The stable-mir will follow a similar approach to proc-macro2. It’s -implementation will eventually be broken down into two main crates: +The stable-mir will follow a similar approach to proc-macro2. Its +implementation is split between two main crates: - `stable_mir`: Public crate, to be published on crates.io, which will contain -the stable data structure as well as proxy APIs to make calls to the -compiler. -- `rustc_smir`: The compiler crate that will translate from internal MIR to -SMIR. This crate will also implement APIs that will be invoked by -stable-mir to query the compiler for more information. +the stable data structure as well as calls to `rustc_smir` APIs. The +translation between stable and internal constructs will also be done in this crate, +however, this is currently implemented in the `rustc_smir` crate.[^translation]. +- `rustc_smir`: This crate implements the public APIs to the compiler. +It is responsible for gathering all the information requested, and providing +the data in its unstable form. + +[^translation]: This is currently implemented in the `rustc_smir` crate, +but we are working to change that. -This will help tools to communicate with the rust compiler via stable APIs. Tools will depend on -`stable_mir` crate, which will invoke the compiler using APIs defined in `rustc_smir`. I.e.: +I.e., +tools will depend on `stable_mir` crate, +which will invoke the compiler using APIs defined in `rustc_smir`. + +I.e.: ``` ┌──────────────────────────────────┐ ┌──────────────────────────────────┐ @@ -104,9 +44,3 @@ This will help tools to communicate with the rust compiler via stable APIs. Tool More details can be found here: https://hackmd.io/XhnYHKKuR6-LChhobvlT-g?view - -For now, the code for these two crates are in separate modules of this crate. -The modules have the same name for simplicity. We also have a third module, -`rustc_internal` which will expose APIs and definitions that allow users to -gather information from internal MIR constructs that haven't been exposed in -the `stable_mir` module. diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 9e6fbc8ea0c..8db1258b65f 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -1393,7 +1393,6 @@ pub struct Generics { pub param_def_id_to_index: Vec<(GenericDef, u32)>, pub has_self: bool, pub has_late_bound_regions: Option<Span>, - pub host_effect_index: Option<usize>, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] |
