diff options
Diffstat (limited to 'compiler')
101 files changed, 1783 insertions, 978 deletions
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 2e51753ede6..15a27c0b6ee 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1215,6 +1215,15 @@ impl Scalar { Scalar::Union { .. } => true, } } + + /// Returns `true` if this is a signed integer scalar + #[inline] + pub fn is_signed(&self) -> bool { + match self.primitive() { + Primitive::Int(_, signed) => signed, + _ => false, + } + } } // NOTE: This struct is generic over the FieldIdx for rust-analyzer usage. @@ -1401,10 +1410,7 @@ impl BackendRepr { #[inline] pub fn is_signed(&self) -> bool { match self { - BackendRepr::Scalar(scal) => match scal.primitive() { - Primitive::Int(_, signed) => signed, - _ => false, - }, + BackendRepr::Scalar(scal) => scal.is_signed(), _ => panic!("`is_signed` on non-scalar ABI {self:?}"), } } @@ -1499,7 +1505,11 @@ impl BackendRepr { #[cfg_attr(feature = "nightly", derive(HashStable_Generic))] pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> { /// Single enum variants, structs/tuples, unions, and all non-ADTs. - Single { index: VariantIdx }, + Single { + /// Always 0 for non-enums/generators. + /// For enums without a variant, this is an invalid index! + index: VariantIdx, + }, /// Enum-likes with more than one variant: each variant comes with /// a *discriminant* (usually the same as the variant index but the user can @@ -1528,14 +1538,22 @@ pub enum TagEncoding<VariantIdx: Idx> { /// The variant `untagged_variant` contains a niche at an arbitrary /// offset (field `tag_field` of the enum), which for a variant with /// discriminant `d` is set to - /// `(d - niche_variants.start).wrapping_add(niche_start)`. + /// `(d - niche_variants.start).wrapping_add(niche_start)` + /// (this is wrapping arithmetic using the type of the niche field). /// /// For example, `Option<(usize, &T)>` is represented such that /// `None` has a null pointer for the second tuple field, and /// `Some` is the identity function (with a non-null reference). + /// + /// Other variants that are not `untagged_variant` and that are outside the `niche_variants` + /// range cannot be represented; they must be uninhabited. Niche { untagged_variant: VariantIdx, + /// This range *may* contain `untagged_variant`; that is then just a "dead value" and + /// not used to encode anything. niche_variants: RangeInclusive<VariantIdx>, + /// This is inbounds of the type of the niche field + /// (not sign-extended, i.e., all bits beyond the niche field size are 0). niche_start: u128, }, } diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index ed2a3a507c8..2f55a9eaeda 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2566,6 +2566,18 @@ pub enum SelfKind { Explicit(P<Ty>, Mutability), } +impl SelfKind { + pub fn to_ref_suggestion(&self) -> String { + match self { + SelfKind::Region(None, mutbl) => mutbl.ref_prefix_str().to_string(), + SelfKind::Region(Some(lt), mutbl) => format!("&{lt} {}", mutbl.prefix_str()), + SelfKind::Value(_) | SelfKind::Explicit(_, _) => { + unreachable!("if we had an explicit self, we wouldn't be here") + } + } + } +} + pub type ExplicitSelf = Spanned<SelfKind>; impl Param { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 196fcc1af30..622c260868e 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1625,9 +1625,10 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token visit_thin_exprs(vis, call_args); vis.visit_span(span); } - ExprKind::Binary(_binop, lhs, rhs) => { + ExprKind::Binary(binop, lhs, rhs) => { vis.visit_expr(lhs); vis.visit_expr(rhs); + vis.visit_span(&mut binop.span); } ExprKind::Unary(_unop, ohs) => vis.visit_expr(ohs), ExprKind::Cast(expr, ty) => { @@ -1785,20 +1786,21 @@ pub fn noop_filter_map_expr<T: MutVisitor>(vis: &mut T, mut e: P<Expr>) -> Optio pub fn walk_flat_map_stmt<T: MutVisitor>( vis: &mut T, - Stmt { kind, mut span, mut id }: Stmt, + Stmt { kind, span, mut id }: Stmt, ) -> SmallVec<[Stmt; 1]> { vis.visit_id(&mut id); - let stmts: SmallVec<_> = walk_flat_map_stmt_kind(vis, kind) + let mut stmts: SmallVec<[Stmt; 1]> = walk_flat_map_stmt_kind(vis, kind) .into_iter() .map(|kind| Stmt { id, kind, span }) .collect(); - if stmts.len() > 1 { - panic!( + match stmts.len() { + 0 => {} + 1 => vis.visit_span(&mut stmts[0].span), + 2.. => panic!( "cloning statement `NodeId`s is prohibited by default, \ the visitor should implement custom statement visiting" - ); + ), } - vis.visit_span(&mut span); stmts } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 8ec1272e360..bac3f974cca 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2011,7 +2011,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ExprKind::Underscore => { if self.tcx.features().generic_arg_infer() { let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span)); - self.arena.alloc(hir::ConstArg { hir_id: self.next_id(), kind: ct_kind }) + self.arena + .alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind }) } else { feature_err( &self.tcx.sess, diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index d81fd53938b..5a0ec865f9d 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -207,8 +207,6 @@ ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc} -ast_passes_show_span = {$msg} - ast_passes_stability_outside_std = stability attributes may not be used outside of the standard library ast_passes_static_without_body = diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index f65056a494b..9b600e3ee92 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -780,14 +780,6 @@ pub(crate) struct IncompatibleFeatures { } #[derive(Diagnostic)] -#[diag(ast_passes_show_span)] -pub(crate) struct ShowSpan { - #[primary_span] - pub span: Span, - pub msg: &'static str, -} - -#[derive(Diagnostic)] #[diag(ast_passes_negative_bound_not_supported)] pub(crate) struct NegativeBoundUnsupported { #[primary_span] diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 8cdc7133cc0..89311516081 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -4,9 +4,9 @@ use rustc_ast::{NodeId, PatKind, attr, token}; use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features, GateIssue}; use rustc_session::Session; use rustc_session::parse::{feature_err, feature_err_issue, feature_warn}; +use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; +use rustc_span::symbol::{Symbol, sym}; use rustc_target::spec::abi; use thin_vec::ThinVec; @@ -516,6 +516,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { "async closures are unstable", "to use an async block, remove the `||`: `async {`" ); + gate_all!( + async_trait_bounds, + "`async` trait bounds are unstable", + "use the desugared name of the async trait, such as `AsyncFn`" + ); gate_all!(async_for_loop, "`for await` loops are experimental"); gate_all!( closure_lifetime_binder, @@ -690,6 +695,7 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) { .find(|feat| feat.gate_name == sym::generic_const_exprs) .map(|feat| feat.attr_sp) { + #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] sess.dcx().emit_err(errors::IncompatibleFeatures { spans: vec![gce_span], f1: Symbol::intern("-Znext-solver=globally"), diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 86752da79ae..b4ed70d83e5 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -1,8 +1,6 @@ //! The `rustc_ast_passes` crate contains passes which validate the AST in `syntax` //! parsed by `rustc_parse` and then lowered, after the passes in this crate, //! by `rustc_ast_lowering`. -//! -//! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`. // tidy-alphabetical-start #![allow(internal_features)] @@ -18,6 +16,5 @@ pub mod ast_validation; mod errors; pub mod feature_gate; -pub mod show_span; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_ast_passes/src/show_span.rs b/compiler/rustc_ast_passes/src/show_span.rs deleted file mode 100644 index e7ba2e7fc30..00000000000 --- a/compiler/rustc_ast_passes/src/show_span.rs +++ /dev/null @@ -1,68 +0,0 @@ -//! Span debugger -//! -//! This module shows spans for all expressions in the crate -//! to help with compiler debugging. - -use std::str::FromStr; - -use rustc_ast as ast; -use rustc_ast::visit; -use rustc_ast::visit::Visitor; -use rustc_errors::DiagCtxtHandle; - -use crate::errors; - -enum Mode { - Expression, - Pattern, - Type, -} - -impl FromStr for Mode { - type Err = (); - fn from_str(s: &str) -> Result<Mode, ()> { - let mode = match s { - "expr" => Mode::Expression, - "pat" => Mode::Pattern, - "ty" => Mode::Type, - _ => return Err(()), - }; - Ok(mode) - } -} - -struct ShowSpanVisitor<'a> { - dcx: DiagCtxtHandle<'a>, - mode: Mode, -} - -impl<'a> Visitor<'a> for ShowSpanVisitor<'a> { - fn visit_expr(&mut self, e: &'a ast::Expr) { - if let Mode::Expression = self.mode { - self.dcx.emit_warn(errors::ShowSpan { span: e.span, msg: "expression" }); - } - visit::walk_expr(self, e); - } - - fn visit_pat(&mut self, p: &'a ast::Pat) { - if let Mode::Pattern = self.mode { - self.dcx.emit_warn(errors::ShowSpan { span: p.span, msg: "pattern" }); - } - visit::walk_pat(self, p); - } - - fn visit_ty(&mut self, t: &'a ast::Ty) { - if let Mode::Type = self.mode { - self.dcx.emit_warn(errors::ShowSpan { span: t.span, msg: "type" }); - } - visit::walk_ty(self, t); - } -} - -pub fn run(dcx: DiagCtxtHandle<'_>, mode: &str, krate: &ast::Crate) { - let Ok(mode) = mode.parse() else { - return; - }; - let mut v = ShowSpanVisitor { dcx, mode }; - visit::walk_crate(&mut v, krate); -} diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 99af5500ac6..0eecf98a6ed 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -973,25 +973,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements: &mut Vec<ClosureOutlivesRequirement<'tcx>>, ) -> bool { let tcx = infcx.tcx; - - let TypeTest { generic_kind, lower_bound, span: _, verify_bound: _ } = type_test; + let TypeTest { generic_kind, lower_bound, span: blame_span, ref verify_bound } = *type_test; let generic_ty = generic_kind.to_ty(tcx); let Some(subject) = self.try_promote_type_test_subject(infcx, generic_ty) else { return false; }; - debug!("subject = {:?}", subject); - - let r_scc = self.constraint_sccs.scc(*lower_bound); - + let r_scc = self.constraint_sccs.scc(lower_bound); debug!( "lower_bound = {:?} r_scc={:?} universe={:?}", lower_bound, r_scc, self.constraint_sccs.annotation(r_scc).min_universe() ); - // If the type test requires that `T: 'a` where `'a` is a // placeholder from another universe, that effectively requires // `T: 'static`, so we have to propagate that requirement. @@ -1004,7 +999,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements.push(ClosureOutlivesRequirement { subject, outlived_free_region: static_r, - blame_span: type_test.span, + blame_span, category: ConstraintCategory::Boring, }); @@ -1031,12 +1026,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { // where `ur` is a local bound -- we are sometimes in a // position to prove things that our caller cannot. See // #53570 for an example. - if self.eval_verify_bound(infcx, generic_ty, ur, &type_test.verify_bound) { + if self.eval_verify_bound(infcx, generic_ty, ur, &verify_bound) { continue; } let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur); - debug!("try_promote_type_test: non_local_ub={:?}", non_local_ub); + debug!(?non_local_ub); // This is slightly too conservative. To show T: '1, given `'2: '1` // and `'3: '1` we only need to prove that T: '2 *or* T: '3, but to @@ -1049,10 +1044,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { let requirement = ClosureOutlivesRequirement { subject, outlived_free_region: upper_bound, - blame_span: type_test.span, + blame_span, category: ConstraintCategory::Boring, }; - debug!("try_promote_type_test: pushing {:#?}", requirement); + debug!(?requirement, "adding closure requirement"); propagated_outlives_requirements.push(requirement); } } @@ -1063,44 +1058,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// variables in the type `T` with an equal universal region from the /// closure signature. /// This is not always possible, so this is a fallible process. - #[instrument(level = "debug", skip(self, infcx))] + #[instrument(level = "debug", skip(self, infcx), ret)] fn try_promote_type_test_subject( &self, infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option<ClosureOutlivesSubject<'tcx>> { let tcx = infcx.tcx; - - // Opaque types' args may include useless lifetimes. - // We will replace them with ReStatic. - struct OpaqueFolder<'tcx> { - tcx: TyCtxt<'tcx>, - } - impl<'tcx> ty::TypeFolder<TyCtxt<'tcx>> for OpaqueFolder<'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.tcx - } - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - use ty::TypeSuperFoldable as _; - let tcx = self.tcx; - let &ty::Alias(ty::Opaque, ty::AliasTy { args, def_id, .. }) = t.kind() else { - return t.super_fold_with(self); - }; - let args = std::iter::zip(args, tcx.variances_of(def_id)).map(|(arg, v)| { - match (arg.unpack(), v) { - (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => { - tcx.lifetimes.re_static.into() - } - _ => arg.fold_with(self), - } - }); - Ty::new_opaque(tcx, def_id, tcx.mk_args_from_iter(args)) - } - } - - let ty = ty.fold_with(&mut OpaqueFolder { tcx }); let mut failed = false; - let ty = fold_regions(tcx, ty, |r, _depth| { let r_vid = self.to_region_vid(r); let r_scc = self.constraint_sccs.scc(r_vid); diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 86a0111db49..bb64d646ff3 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -33,8 +33,8 @@ use rustc_middle::ty::{ TyCtxt, TypeVisitableExt, }; use rustc_middle::{bug, span_bug}; +use rustc_span::ErrorGuaranteed; use rustc_span::symbol::{kw, sym}; -use rustc_span::{ErrorGuaranteed, Symbol}; use tracing::{debug, instrument}; use crate::BorrowckInferCtxt; @@ -524,7 +524,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let reg_vid = self .infcx - .next_nll_region_var(FR, || RegionCtxt::Free(Symbol::intern("c-variadic"))) + .next_nll_region_var(FR, || RegionCtxt::Free(sym::c_dash_variadic)) .as_var(); let region = ty::Region::new_var(self.infcx.tcx, reg_vid); @@ -540,10 +540,8 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { } } - let fr_fn_body = self - .infcx - .next_nll_region_var(FR, || RegionCtxt::Free(Symbol::intern("fn_body"))) - .as_var(); + let fr_fn_body = + self.infcx.next_nll_region_var(FR, || RegionCtxt::Free(sym::fn_body)).as_var(); let num_universals = self.infcx.num_region_vars(); diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 6ebc2fd870c..c05d44cb452 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -94,6 +94,21 @@ builtin_macros_cfg_accessible_indeterminate = cannot determine whether the path builtin_macros_cfg_accessible_literal_path = `cfg_accessible` path cannot be a literal builtin_macros_cfg_accessible_multiple_paths = multiple `cfg_accessible` paths are specified builtin_macros_cfg_accessible_unspecified_path = `cfg_accessible` path is not specified + +builtin_macros_coerce_pointee_requires_maybe_sized = `derive(CoercePointee)` requires `{$name}` to be marked `?Sized` + +builtin_macros_coerce_pointee_requires_one_field = `CoercePointee` can only be derived on `struct`s with at least one field + +builtin_macros_coerce_pointee_requires_one_generic = `CoercePointee` can only be derived on `struct`s that are generic over at least one type + +builtin_macros_coerce_pointee_requires_one_pointee = exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits + +builtin_macros_coerce_pointee_requires_transparent = `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` + +builtin_macros_coerce_pointee_too_many_pointees = only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits + .label = here another type parameter is marked as `#[pointee]` + + builtin_macros_concat_bytes_array = cannot concatenate doubly nested array .note = byte strings are treated as arrays of bytes .help = try flattening the array diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 8adb9a3f4b0..3bd8f899a4a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -9,6 +9,7 @@ use rustc_ast::{ use rustc_attr as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_macros::Diagnostic; use rustc_span::symbol::{Ident, sym}; use rustc_span::{Span, Symbol}; use thin_vec::{ThinVec, thin_vec}; @@ -38,12 +39,7 @@ pub(crate) fn expand_deriving_coerce_pointee( .any(|r| matches!(r, attr::ReprTransparent)) }); if !is_transparent { - cx.dcx() - .struct_span_err( - span, - "`CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`", - ) - .emit(); + cx.dcx().emit_err(RequireTransparent { span }); return; } if !matches!( @@ -51,22 +47,12 @@ pub(crate) fn expand_deriving_coerce_pointee( VariantData::Struct { fields, recovered: _ } | VariantData::Tuple(fields, _) if !fields.is_empty()) { - cx.dcx() - .struct_span_err( - span, - "`CoercePointee` can only be derived on `struct`s with at least one field", - ) - .emit(); + cx.dcx().emit_err(RequireOneField { span }); return; } (aitem.ident, g) } else { - cx.dcx() - .struct_span_err( - span, - "`CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`", - ) - .emit(); + cx.dcx().emit_err(RequireTransparent { span }); return; }; @@ -95,10 +81,7 @@ pub(crate) fn expand_deriving_coerce_pointee( let pointee_param_idx = if type_params.is_empty() { // `#[derive(CoercePointee)]` requires at least one generic type on the target `struct` - cx.dcx().struct_span_err( - span, - "`CoercePointee` can only be derived on `struct`s that are generic over at least one type", - ).emit(); + cx.dcx().emit_err(RequireOneGeneric { span }); return; } else if type_params.len() == 1 { // Regardless of the only type param being designed as `#[pointee]` or not, we can just use it as such @@ -111,19 +94,11 @@ pub(crate) fn expand_deriving_coerce_pointee( match (pointees.next(), pointees.next()) { (Some((idx, _span)), None) => idx, (None, _) => { - cx.dcx().struct_span_err( - span, - "exactly one generic type parameter must be marked as #[pointee] to derive CoercePointee traits", - ).emit(); + cx.dcx().emit_err(RequireOnePointee { span }); return; } (Some((_, one)), Some((_, another))) => { - cx.dcx() - .struct_span_err( - vec![one, another], - "only one type parameter can be marked as `#[pointee]` when deriving CoercePointee traits", - ) - .emit(); + cx.dcx().emit_err(TooManyPointees { one, another }); return; } } @@ -181,15 +156,10 @@ pub(crate) fn expand_deriving_coerce_pointee( pointee_ty_ident.name, ) { - cx.dcx() - .struct_span_err( - pointee_ty_ident.span, - format!( - "`derive(CoercePointee)` requires {} to be marked `?Sized`", - pointee_ty_ident.name - ), - ) - .emit(); + cx.dcx().emit_err(RequiresMaybeSized { + span: pointee_ty_ident.span, + name: pointee_ty_ident.name.to_ident_string(), + }); return; } let arg = GenericArg::Type(s_ty.clone()); @@ -459,3 +429,48 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b> } } } + +#[derive(Diagnostic)] +#[diag(builtin_macros_coerce_pointee_requires_transparent)] +struct RequireTransparent { + #[primary_span] + span: Span, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_coerce_pointee_requires_one_field)] +struct RequireOneField { + #[primary_span] + span: Span, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_coerce_pointee_requires_one_generic)] +struct RequireOneGeneric { + #[primary_span] + span: Span, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_coerce_pointee_requires_one_pointee)] +struct RequireOnePointee { + #[primary_span] + span: Span, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_coerce_pointee_too_many_pointees)] +struct TooManyPointees { + #[primary_span] + one: Span, + #[label] + another: Span, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_coerce_pointee_requires_maybe_sized)] +struct RequiresMaybeSized { + #[primary_span] + span: Span, + name: String, +} diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 0d62a13b472..ae9578eeffb 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -74,7 +74,7 @@ fn create_jit_module( jit_builder.symbol("__clif_jit_fn", clif_jit_fn as *const u8); let mut jit_module = UnwindModule::new(JITModule::new(jit_builder), false); - let cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, Symbol::intern("dummy_cgu_name")); + let cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, sym::dummy_cgu_name); crate::allocator::codegen(tcx, &mut jit_module); @@ -276,12 +276,7 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) -> jit_module.module.prepare_for_function_redefine(func_id).unwrap(); - let mut cx = crate::CodegenCx::new( - tcx, - jit_module.isa(), - false, - Symbol::intern("dummy_cgu_name"), - ); + let mut cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, sym::dummy_cgu_name); codegen_and_compile_fn(tcx, &mut cx, &mut Context::new(), jit_module, instance); assert!(cx.global_asm.is_empty()); diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index e6f6ae30581..cac9975f04c 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -189,18 +189,13 @@ impl CodegenBackend for CraneliftCodegenBackend { // FIXME return the actually used target features. this is necessary for #[cfg(target_feature)] if sess.target.arch == "x86_64" && sess.target.os != "none" { // x86_64 mandates SSE2 support - vec![Symbol::intern("fxsr"), sym::sse, Symbol::intern("sse2")] + vec![sym::fsxr, sym::sse, sym::sse2] } else if sess.target.arch == "aarch64" { match &*sess.target.os { "none" => vec![], // On macOS the aes, sha2 and sha3 features are enabled by default and ring // fails to compile on macOS when they are not present. - "macos" => vec![ - sym::neon, - Symbol::intern("aes"), - Symbol::intern("sha2"), - Symbol::intern("sha3"), - ], + "macos" => vec![sym::neon, sym::aes, sym::sha2, sym::sha3], // AArch64 mandates Neon support _ => vec![sym::neon], } diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index e4c3e748cb5..8baa69cefe1 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -10,6 +10,15 @@ use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; +fn round_up_to_alignment<'ll>( + bx: &mut Builder<'_, 'll, '_>, + mut value: &'ll Value, + align: Align, +) -> &'ll Value { + value = bx.add(value, bx.cx().const_i32(align.bytes() as i32 - 1)); + return bx.and(value, bx.cx().const_i32(-(align.bytes() as i32))); +} + fn round_pointer_up_to_alignment<'ll>( bx: &mut Builder<'_, 'll, '_>, addr: &'ll Value, @@ -17,8 +26,7 @@ fn round_pointer_up_to_alignment<'ll>( ptr_ty: &'ll Type, ) -> &'ll Value { let mut ptr_as_int = bx.ptrtoint(addr, bx.cx().type_isize()); - ptr_as_int = bx.add(ptr_as_int, bx.cx().const_i32(align.bytes() as i32 - 1)); - ptr_as_int = bx.and(ptr_as_int, bx.cx().const_i32(-(align.bytes() as i32))); + ptr_as_int = round_up_to_alignment(bx, ptr_as_int, align); bx.inttoptr(ptr_as_int, ptr_ty) } @@ -270,6 +278,106 @@ fn emit_s390x_va_arg<'ll, 'tcx>( bx.load(val_type, val_addr, layout.align.abi) } +fn emit_xtensa_va_arg<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + list: OperandRef<'tcx, &'ll Value>, + target_ty: Ty<'tcx>, +) -> &'ll Value { + // Implementation of va_arg for Xtensa. There doesn't seem to be an authoritative source for + // this, other than "what GCC does". + // + // The va_list type has three fields: + // struct __va_list_tag { + // int32_t *va_stk; // Arguments passed on the stack + // int32_t *va_reg; // Arguments passed in registers, saved to memory by the prologue. + // int32_t va_ndx; // Offset into the arguments, in bytes + // }; + // + // The first 24 bytes (equivalent to 6 registers) come from va_reg, the rest from va_stk. + // Thus if va_ndx is less than 24, the next va_arg *may* read from va_reg, + // otherwise it must come from va_stk. + // + // Primitive arguments are never split between registers and the stack. For example, if loading an 8 byte + // primitive value and va_ndx = 20, we instead bump the offset and read everything from va_stk. + let va_list_addr = list.immediate(); + // FIXME: handle multi-field structs that split across regsave/stack? + let layout = bx.cx.layout_of(target_ty); + let from_stack = bx.append_sibling_block("va_arg.from_stack"); + let from_regsave = bx.append_sibling_block("va_arg.from_regsave"); + let end = bx.append_sibling_block("va_arg.end"); + + // (*va).va_ndx + let va_reg_offset = 4; + let va_ndx_offset = va_reg_offset + 4; + let offset_ptr = + bx.inbounds_gep(bx.type_i8(), va_list_addr, &[bx.cx.const_usize(va_ndx_offset)]); + + let offset = bx.load(bx.type_i32(), offset_ptr, bx.tcx().data_layout.i32_align.abi); + let offset = round_up_to_alignment(bx, offset, layout.align.abi); + + let slot_size = layout.size.align_to(Align::from_bytes(4).unwrap()).bytes() as i32; + + // Update the offset in va_list, by adding the slot's size. + let offset_next = bx.add(offset, bx.const_i32(slot_size)); + + // Figure out where to look for our value. We do that by checking the end of our slot (offset_next). + // If that is within the regsave area, then load from there. Otherwise load from the stack area. + let regsave_size = bx.const_i32(24); + let use_regsave = bx.icmp(IntPredicate::IntULE, offset_next, regsave_size); + bx.cond_br(use_regsave, from_regsave, from_stack); + + bx.switch_to_block(from_regsave); + // update va_ndx + bx.store(offset_next, offset_ptr, bx.tcx().data_layout.pointer_align.abi); + + // (*va).va_reg + let regsave_area_ptr = + bx.inbounds_gep(bx.type_i8(), va_list_addr, &[bx.cx.const_usize(va_reg_offset)]); + let regsave_area = + bx.load(bx.type_ptr(), regsave_area_ptr, bx.tcx().data_layout.pointer_align.abi); + let regsave_value_ptr = bx.inbounds_gep(bx.type_i8(), regsave_area, &[offset]); + bx.br(end); + + bx.switch_to_block(from_stack); + + // The first time we switch from regsave to stack we needs to adjust our offsets a bit. + // va_stk is set up such that the first stack argument is always at va_stk + 32. + // The corrected offset is written back into the va_list struct. + + // let offset_corrected = cmp::max(offset, 32); + let stack_offset_start = bx.const_i32(32); + let needs_correction = bx.icmp(IntPredicate::IntULE, offset, stack_offset_start); + let offset_corrected = bx.select(needs_correction, stack_offset_start, offset); + + // let offset_next_corrected = offset_corrected + slot_size; + // va_ndx = offset_next_corrected; + let offset_next_corrected = bx.add(offset_next, bx.const_i32(slot_size)); + // update va_ndx + bx.store(offset_next_corrected, offset_ptr, bx.tcx().data_layout.pointer_align.abi); + + // let stack_value_ptr = unsafe { (*va).va_stk.byte_add(offset_corrected) }; + let stack_area_ptr = bx.inbounds_gep(bx.type_i8(), va_list_addr, &[bx.cx.const_usize(0)]); + let stack_area = bx.load(bx.type_ptr(), stack_area_ptr, bx.tcx().data_layout.pointer_align.abi); + let stack_value_ptr = bx.inbounds_gep(bx.type_i8(), stack_area, &[offset_corrected]); + bx.br(end); + + bx.switch_to_block(end); + + // On big-endian, for values smaller than the slot size we'd have to align the read to the end + // of the slot rather than the start. While the ISA and GCC support big-endian, all the Xtensa + // targets supported by rustc are litte-endian so don't worry about it. + + // if from_regsave { + // unsafe { *regsave_value_ptr } + // } else { + // unsafe { *stack_value_ptr } + // } + assert!(bx.tcx().sess.target.endian == Endian::Little); + let value_ptr = + bx.phi(bx.type_ptr(), &[regsave_value_ptr, stack_value_ptr], &[from_regsave, from_stack]); + return bx.load(layout.llvm_type(bx), value_ptr, layout.align.abi); +} + pub(super) fn emit_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, addr: OperandRef<'tcx, &'ll Value>, @@ -302,6 +410,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( let indirect: bool = target_ty_size > 8 || !target_ty_size.is_power_of_two(); emit_ptr_va_arg(bx, addr, target_ty, indirect, Align::from_bytes(8).unwrap(), false) } + "xtensa" => emit_xtensa_va_arg(bx, addr, target_ty), // For all other architecture/OS combinations fall back to using // the LLVM va_arg instruction. // https://llvm.org/docs/LangRef.html#va-arg-instruction diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 8cbdcd68e13..34f795bda75 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -2,6 +2,7 @@ use rustc_abi::VariantIdx; use rustc_middle::query::{Key, TyCtxtAt}; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use tracing::instrument; @@ -85,5 +86,6 @@ pub fn tag_for_variant_provider<'tcx>( crate::const_eval::DummyMachine, ); - ecx.tag_for_variant(ty, variant_index).unwrap().map(|(tag, _tag_field)| tag) + let layout = ecx.layout_of(ty).unwrap(); + ecx.tag_for_variant(layout, variant_index).unwrap().map(|(tag, _tag_field)| tag) } diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index f94d0cbb42b..6faac1582ab 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -1,7 +1,7 @@ //! Functions for reading and writing discriminants of multi-variant layouts (enums and coroutines). use rustc_abi::{self as abi, TagEncoding, VariantIdx, Variants}; -use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt}; +use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout}; use rustc_middle::ty::{self, CoroutineArgsExt, ScalarInt, Ty}; use rustc_middle::{mir, span_bug}; use tracing::{instrument, trace}; @@ -21,17 +21,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { variant_index: VariantIdx, dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { - // Layout computation excludes uninhabited variants from consideration - // therefore there's no way to represent those variants in the given layout. - // Essentially, uninhabited variants do not have a tag that corresponds to their - // discriminant, so we cannot do anything here. - // When evaluating we will always error before even getting here, but ConstProp 'executes' - // dead code, so we cannot ICE here. - if dest.layout().for_variant(self, variant_index).is_uninhabited() { - throw_ub!(UninhabitedEnumVariantWritten(variant_index)) - } - - match self.tag_for_variant(dest.layout().ty, variant_index)? { + match self.tag_for_variant(dest.layout(), variant_index)? { Some((tag, tag_field)) => { // No need to validate that the discriminant here because the // `TyAndLayout::for_variant()` call earlier already checks the @@ -80,7 +70,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if ty.is_enum() { // Hilariously, `Single` is used even for 0-variant enums. // (See https://github.com/rust-lang/rust/issues/89765). - if matches!(ty.kind(), ty::Adt(def, ..) if def.variants().is_empty()) { + if ty.ty_adt_def().unwrap().variants().is_empty() { throw_ub!(UninhabitedEnumVariantRead(index)) } // For consistency with `write_discriminant`, and to make sure that @@ -188,6 +178,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let variants = ty.ty_adt_def().expect("tagged layout for non adt").variants(); assert!(variant_index < variants.next_index()); + if variant_index == untagged_variant { + // The untagged variant can be in the niche range, but even then it + // is not a valid encoding. + throw_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))) + } variant_index } else { untagged_variant @@ -236,10 +231,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// given field index. pub(crate) fn tag_for_variant( &self, - ty: Ty<'tcx>, + layout: TyAndLayout<'tcx>, variant_index: VariantIdx, ) -> InterpResult<'tcx, Option<(ScalarInt, usize)>> { - match self.layout_of(ty)?.variants { + // Layout computation excludes uninhabited variants from consideration. + // Therefore, there's no way to represent those variants in the given layout. + // Essentially, uninhabited variants do not have a tag that corresponds to their + // discriminant, so we have to bail out here. + if layout.for_variant(self, variant_index).is_uninhabited() { + throw_ub!(UninhabitedEnumVariantWritten(variant_index)) + } + + match layout.variants { abi::Variants::Single { .. } => { // The tag of a `Single` enum is like the tag of the niched // variant: there's no tag as the discriminant is encoded @@ -260,7 +263,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // raw discriminants for enums are isize or bigger during // their computation, but the in-memory tag is the smallest possible // representation - let discr = self.discriminant_for_variant(ty, variant_index)?; + let discr = self.discriminant_for_variant(layout.ty, variant_index)?; let discr_size = discr.layout.size; let discr_val = discr.to_scalar().to_bits(discr_size)?; let tag_size = tag_layout.size(self); @@ -286,11 +289,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .. } => { assert!(variant_index != untagged_variant); + // We checked that this variant is inhabited, so it must be in the niche range. + assert!( + niche_variants.contains(&variant_index), + "invalid variant index for this enum" + ); let variants_start = niche_variants.start().as_u32(); - let variant_index_relative = variant_index - .as_u32() - .checked_sub(variants_start) - .expect("overflow computing relative variant idx"); + let variant_index_relative = variant_index.as_u32().strict_sub(variants_start); // We need to use machine arithmetic when taking into account `niche_start`: // tag_val = variant_index_relative + niche_start_val let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?; diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 2a7408f1c70..b5adf06b300 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -10,6 +10,7 @@ #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(slice_ptr_get)] +#![feature(strict_overflow_ops)] #![feature(trait_alias)] #![feature(try_blocks)] #![feature(unqualified_local_imports)] diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 5b472bb9b81..b7d64f75bf3 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -418,9 +418,7 @@ fn run_compiler( return early_exit(); } - if sess.opts.unstable_opts.parse_crate_root_only - || sess.opts.unstable_opts.show_span.is_some() - { + if sess.opts.unstable_opts.parse_crate_root_only { return early_exit(); } diff --git a/compiler/rustc_error_codes/src/error_codes/E0622.md b/compiler/rustc_error_codes/src/error_codes/E0622.md index 5d71ee9949d..4cb605b636d 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0622.md +++ b/compiler/rustc_error_codes/src/error_codes/E0622.md @@ -7,10 +7,11 @@ Erroneous code example: #![allow(internal_features)] extern "rust-intrinsic" { - pub static breakpoint: fn(); // error: intrinsic must be a function + pub static atomic_singlethreadfence_seqcst: fn(); + // error: intrinsic must be a function } -fn main() { unsafe { breakpoint(); } } +fn main() { unsafe { atomic_singlethreadfence_seqcst(); } } ``` An intrinsic is a function available for use in a given programming language @@ -22,8 +23,8 @@ error, just declare a function. Example: #![allow(internal_features)] extern "rust-intrinsic" { - pub fn breakpoint(); // ok! + pub fn atomic_singlethreadfence_seqcst(); // ok! } -fn main() { unsafe { breakpoint(); } } +fn main() { unsafe { atomic_singlethreadfence_seqcst(); } } ``` diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 69a14bd9f12..8b4f441dafe 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -119,6 +119,13 @@ declare_features! ( (removed, generator_clone, "1.65.0", Some(95360), Some("renamed to `coroutine_clone`")), /// Allows defining generators. (removed, generators, "1.21.0", Some(43122), Some("renamed to `coroutines`")), + /// An extension to the `generic_associated_types` feature, allowing incomplete features. + (removed, generic_associated_types_extended, "CURRENT_RUSTC_VERSION", Some(95451), + Some( + "feature needs overhaul and reimplementation pending \ + better implied higher-ranked implied bounds support" + ) + ), /// Allows `impl Trait` in bindings (`let`, `const`, `static`). (removed, impl_trait_in_bindings, "1.55.0", Some(63065), Some("the implementation was not maintainable, the feature may get reintroduced once the current refactorings are done")), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index ec908762da7..abc7200699c 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -394,6 +394,8 @@ declare_features! ( (unstable, async_fn_track_caller, "1.73.0", Some(110011)), /// Allows `for await` loops. (unstable, async_for_loop, "1.77.0", Some(118898)), + /// Allows `async` trait bound modifier. + (unstable, async_trait_bounds, "CURRENT_RUSTC_VERSION", Some(62290)), /// Allows using C-variadics. (unstable, c_variadic, "1.34.0", Some(44930)), /// Allows the use of `#[cfg(<true/false>)]`. @@ -497,8 +499,6 @@ declare_features! ( (unstable, gen_blocks, "1.75.0", Some(117078)), /// Infer generic args for both consts and types. (unstable, generic_arg_infer, "1.55.0", Some(85077)), - /// An extension to the `generic_associated_types` feature, allowing incomplete features. - (incomplete, generic_associated_types_extended, "1.61.0", Some(95451)), /// Allows non-trivial generic constants which have to have wfness manually propagated to callers (incomplete, generic_const_exprs, "1.56.0", Some(76560)), /// Allows generic parameters and where-clauses on free & associated const items. diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 7434bbf180b..2e6b511412b 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -87,6 +87,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid + | sym::breakpoint | sym::size_of | sym::min_align_of | sym::needs_drop diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 8d5824130d8..a4636da3f62 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -309,10 +309,10 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { self.tcx.ensure().type_of(param.def_id); if let Some(default) = default { // need to store default and type of default + self.tcx.ensure().const_param_default(param.def_id); if let hir::ConstArgKind::Anon(ac) = default.kind { self.tcx.ensure().type_of(ac.def_id); } - self.tcx.ensure().const_param_default(param.def_id); } } } @@ -1817,7 +1817,6 @@ fn const_param_default<'tcx>( ), }; let icx = ItemCtxt::new(tcx, def_id); - // FIXME(const_generics): investigate which places do and don't need const ty feeding - let ct = icx.lowerer().lower_const_arg(default_ct, FeedConstTy::No); + let ct = icx.lowerer().lower_const_arg(default_ct, FeedConstTy::Param(def_id.to_def_id())); ty::EarlyBinder::bind(ct) } diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 077696ac1f2..1d9114b0ef3 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -419,7 +419,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { if let Node::ConstBlock(_) = node { own_params.push(ty::GenericParamDef { index: next_index(), - name: Symbol::intern("<const_ty>"), + name: rustc_span::sym::const_ty_placeholder, def_id: def_id.to_def_id(), pure_wrt_drop: false, kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false }, diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 7cf99bebd36..72d5b3ac4f5 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -12,7 +12,6 @@ use rustc_middle::ty::{self, Article, IsSuggestable, Ty, TyCtxt, TypeVisitableEx use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span}; -use tracing::debug; use super::{ItemCtxt, bad_placeholder}; use crate::errors::TypeofReservedKeywordUsed; @@ -138,252 +137,26 @@ fn const_arg_anon_type_of<'tcx>(tcx: TyCtxt<'tcx>, arg_hir_id: HirId, span: Span use hir::*; use rustc_middle::ty::Ty; - let parent_node_id = tcx.parent_hir_id(arg_hir_id); - let parent_node = tcx.hir_node(parent_node_id); - - let (generics, arg_idx) = match parent_node { - // Easy case: arrays repeat expressions. + match tcx.parent_hir_node(arg_hir_id) { + // Array length const arguments do not have `type_of` fed as there is never a corresponding + // generic parameter definition. Node::Ty(&hir::Ty { kind: TyKind::Array(_, ref constant), .. }) | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) if constant.hir_id == arg_hir_id => { return tcx.types.usize; } - Node::GenericParam(&GenericParam { - def_id: param_def_id, - kind: GenericParamKind::Const { default: Some(ct), .. }, - .. - }) if ct.hir_id == arg_hir_id => { - return tcx - .type_of(param_def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"); - } - - // This match arm is for when the def_id appears in a GAT whose - // path can't be resolved without typechecking e.g. - // - // trait Foo { - // type Assoc<const N: usize>; - // fn foo() -> Self::Assoc<3>; - // } - // - // In the above code we would call this query with the def_id of 3 and - // the parent_node we match on would be the hir node for Self::Assoc<3> - // - // `Self::Assoc<3>` cant be resolved without typechecking here as we - // didnt write <Self as Foo>::Assoc<3>. If we did then another match - // arm would handle this. - // - // I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU - Node::Ty(hir_ty @ hir::Ty { kind: TyKind::Path(QPath::TypeRelative(ty, segment)), .. }) => { - // Find the Item containing the associated type so we can create an ItemCtxt. - // Using the ItemCtxt lower the HIR for the unresolved assoc type into a - // ty which is a fully resolved projection. - // For the code example above, this would mean lowering `Self::Assoc<3>` - // to a ty::Alias(ty::Projection, `<Self as Foo>::Assoc<3>`). - let item_def_id = tcx.hir().get_parent_item(ty.hir_id).def_id; - let ty = ItemCtxt::new(tcx, item_def_id).lower_ty(hir_ty); - - // Iterate through the generics of the projection to find the one that corresponds to - // the def_id that this query was called with. We filter to only type and const args here - // as a precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't - // but it can't hurt to be safe ^^ - if let ty::Alias(ty::Projection | ty::Inherent, projection) = ty.kind() { - let generics = tcx.generics_of(projection.def_id); - - let arg_index = segment - .args - .and_then(|args| { - args.args - .iter() - .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.hir_id() == arg_hir_id) - }) - .unwrap_or_else(|| { - bug!("no arg matching AnonConst in segment"); - }); - - (generics, arg_index) - } else { - // I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU - return Ty::new_error_with_message( - tcx, - span, - "unexpected non-GAT usage of an anon const", - ); - } - } - Node::Expr(&Expr { - kind: - ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)), - .. - }) => { - let body_owner = tcx.hir().enclosing_body_owner(arg_hir_id); - let tables = tcx.typeck(body_owner); - // This may fail in case the method/path does not actually exist. - // As there is no relevant param for `def_id`, we simply return - // `None` here. - let Some(type_dependent_def) = tables.type_dependent_def_id(parent_node_id) else { - return Ty::new_error_with_message( - tcx, - span, - format!("unable to find type-dependent def for {parent_node_id:?}"), - ); - }; - let idx = segment - .args - .and_then(|args| { - args.args - .iter() - .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.hir_id() == arg_hir_id) - }) - .unwrap_or_else(|| { - bug!("no arg matching ConstArg in segment"); - }); - - (tcx.generics_of(type_dependent_def), idx) - } - - Node::Ty(&hir::Ty { kind: TyKind::Path(_), .. }) - | Node::Expr(&Expr { kind: ExprKind::Path(_) | ExprKind::Struct(..), .. }) - | Node::TraitRef(..) - | Node::Pat(_) => { - let path = match parent_node { - Node::Ty(&hir::Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. }) - | Node::TraitRef(&TraitRef { path, .. }) => &*path, - Node::Expr(&Expr { - kind: - ExprKind::Path(QPath::Resolved(_, path)) - | ExprKind::Struct(&QPath::Resolved(_, path), ..), - .. - }) => { - let body_owner = tcx.hir().enclosing_body_owner(arg_hir_id); - let _tables = tcx.typeck(body_owner); - &*path - } - Node::Pat(pat) => { - if let Some(path) = get_path_containing_arg_in_pat(pat, arg_hir_id) { - path - } else { - return Ty::new_error_with_message( - tcx, - span, - format!("unable to find const parent for {arg_hir_id} in pat {pat:?}"), - ); - } - } - _ => { - return Ty::new_error_with_message( - tcx, - span, - format!("unexpected const parent path {parent_node:?}"), - ); - } - }; - - // We've encountered an `AnonConst` in some path, so we need to - // figure out which generic parameter it corresponds to and return - // the relevant type. - let Some((arg_index, segment)) = path.segments.iter().find_map(|seg| { - let args = seg.args?; - args.args - .iter() - .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.hir_id() == arg_hir_id) - .map(|index| (index, seg)) - .or_else(|| { - args.constraints - .iter() - .copied() - .filter_map(AssocItemConstraint::ct) - .position(|ct| ct.hir_id == arg_hir_id) - .map(|idx| (idx, seg)) - }) - }) else { - return Ty::new_error_with_message(tcx, span, "no arg matching AnonConst in path"); - }; - - let generics = match tcx.res_generics_def_id(segment.res) { - Some(def_id) => tcx.generics_of(def_id), - None => { - return Ty::new_error_with_message( - tcx, - span, - format!("unexpected anon const res {:?} in path: {:?}", segment.res, path), - ); - } - }; - - (generics, arg_index) - } - _ => { - return Ty::new_error_with_message( - tcx, - span, - format!("unexpected const arg parent in type_of(): {parent_node:?}"), - ); - } - }; - - debug!(?parent_node); - debug!(?generics, ?arg_idx); - if let Some(param_def_id) = generics - .own_params - .iter() - .filter(|param| param.kind.is_ty_or_const()) - .nth(match generics.has_self && generics.parent.is_none() { - true => arg_idx + 1, - false => arg_idx, - }) - .and_then(|param| match param.kind { - ty::GenericParamDefKind::Const { .. } => { - debug!(?param); - Some(param.def_id) - } - _ => None, - }) - { - tcx.type_of(param_def_id).no_bound_vars().expect("const parameter types cannot be generic") - } else { - return Ty::new_error_with_message( + // This is not a `bug!` as const arguments in path segments that did not resolve to anything + // will result in `type_of` never being fed. + _ => Ty::new_error_with_message( tcx, span, - format!("const generic parameter not found in {generics:?} at position {arg_idx:?}"), - ); + "`type_of` called on const argument's anon const before the const argument was lowered", + ), } } -fn get_path_containing_arg_in_pat<'hir>( - pat: &'hir hir::Pat<'hir>, - arg_id: HirId, -) -> Option<&'hir hir::Path<'hir>> { - use hir::*; - - let is_arg_in_path = |p: &hir::Path<'_>| { - p.segments - .iter() - .filter_map(|seg| seg.args) - .flat_map(|args| args.args) - .any(|arg| arg.hir_id() == arg_id) - }; - let mut arg_path = None; - pat.walk(|pat| match pat.kind { - PatKind::Struct(QPath::Resolved(_, path), _, _) - | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) - | PatKind::Path(QPath::Resolved(_, path)) - if is_arg_in_path(path) => - { - arg_path = Some(path); - false - } - _ => true, - }); - arg_path -} - pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, Ty<'_>> { use rustc_hir::*; use rustc_middle::ty::Ty; diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index fcea0052546..66255829dcf 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -226,13 +226,18 @@ impl TaitConstraintLocator<'_> { constrained = true; if !opaque_types_defined_by.contains(&self.def_id) { - self.tcx.dcx().emit_err(TaitForwardCompat { + let guar = self.tcx.dcx().emit_err(TaitForwardCompat { span: hidden_type.span, item_span: self .tcx .def_ident_span(item_def_id) .unwrap_or_else(|| self.tcx.def_span(item_def_id)), }); + // Avoid "opaque type not constrained" errors on the opaque itself. + self.found = Some(ty::OpaqueHiddenType { + span: DUMMY_SP, + ty: Ty::new_error(self.tcx, guar), + }); } let concrete_type = self.tcx.erase_regions(hidden_type.remap_generic_params_to_declaration_params( @@ -248,7 +253,7 @@ impl TaitConstraintLocator<'_> { if !constrained { debug!("no constraints in typeck results"); if opaque_types_defined_by.contains(&self.def_id) { - self.tcx.dcx().emit_err(TaitForwardCompat2 { + let guar = self.tcx.dcx().emit_err(TaitForwardCompat2 { span: self .tcx .def_ident_span(item_def_id) @@ -256,6 +261,11 @@ impl TaitConstraintLocator<'_> { opaque_type_span: self.tcx.def_span(self.def_id), opaque_type: self.tcx.def_path_str(self.def_id), }); + // Avoid "opaque type not constrained" errors on the opaque itself. + self.found = Some(ty::OpaqueHiddenType { + span: DUMMY_SP, + ty: Ty::new_error(self.tcx, guar), + }); } return; }; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 3940d138deb..aacdcf027b6 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -307,7 +307,11 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { ty::Alias(ty::Projection | ty::Inherent | ty::Weak, _) if !ty.has_escaping_bound_vars() => { - self.normalize(span, ty).ty_adt_def() + if self.next_trait_solver() { + self.try_structurally_resolve_type(span, ty).ty_adt_def() + } else { + self.normalize(span, ty).ty_adt_def() + } } _ => None, } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 61260dbd16c..ddcd90a2a9d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -3394,7 +3394,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else { return; }; - if self.can_eq(self.param_env, expected_ty, ty) { + if self.can_eq(self.param_env, expected_ty, ty) + // FIXME: this happens with macro calls. Need to figure out why the stmt + // `println!();` doesn't include the `;` in its `Span`. (#133845) + // We filter these out to avoid ICEs with debug assertions on caused by + // empty suggestions. + && stmt.span.hi() != tail_expr.span.hi() + { err.span_suggestion_short( stmt.span.with_lo(tail_expr.span.hi()), "remove this semicolon", diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index cff2aa68993..6b1a288510a 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -664,7 +664,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let is_write = sugg_span.ctxt().outer_expn_data().macro_def_id.is_some_and(|def_id| { tcx.is_diagnostic_item(sym::write_macro, def_id) || tcx.is_diagnostic_item(sym::writeln_macro, def_id) - }) && item_name.name == Symbol::intern("write_fmt"); + }) && item_name.name == sym::write_fmt; let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source { self.suggest_missing_writer(rcvr_ty, rcvr_expr) } else { diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 06e27ed8060..d42915f4110 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -54,10 +54,6 @@ pub(crate) fn parse<'a>(sess: &'a Session) -> Result<ast::Crate> { }) .map_err(|parse_error| parse_error.emit())?; - if let Some(ref s) = sess.opts.unstable_opts.show_span { - rustc_ast_passes::show_span::run(sess.dcx(), s, &krate); - } - if sess.opts.unstable_opts.input_stats { input_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS", "ast-stats-1"); } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 9384ed925c7..3c4d9c2e928 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -844,7 +844,6 @@ fn test_unstable_options_tracking_hash() { tracked!(sanitizer_recover, SanitizerSet::ADDRESS); tracked!(saturating_float_casts, Some(true)); tracked!(share_generics, Some(true)); - tracked!(show_span, Some(String::from("abc"))); tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc"))); tracked!(small_data_threshold, Some(16)); tracked!(split_lto_unit, Some(true)); diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 4aeaf616816..49e6b763590 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -775,6 +775,9 @@ lint_suspicious_double_ref_clone = lint_suspicious_double_ref_deref = using `.deref()` on a double reference, which returns `{$ty}` instead of dereferencing the inner type +lint_symbol_intern_string_literal = using `Symbol::intern` on a string literal + .help = consider adding the symbol to `compiler/rustc_span/src/symbol.rs` + lint_trailing_semi_macro = trailing semicolon in macro used in expression position .note1 = macro invocations at the end of a block are treated as expressions .note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}` @@ -882,6 +885,12 @@ lint_unnameable_test_items = cannot test inner items lint_unnecessary_qualification = unnecessary qualification .suggestion = remove the unnecessary path segments +lint_unpredictable_fn_pointer_comparisons = function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique + .note_duplicated_fn = the address of the same function can vary between different codegen units + .note_deduplicated_fn = furthermore, different functions could have the same address after being merged together + .note_visit_fn_addr_eq = for more information visit <https://doc.rust-lang.org/nightly/core/ptr/fn.fn_addr_eq.html> + .fn_addr_eq_suggestion = refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint + lint_unqualified_local_imports = `use` of a local item without leading `self::`, `super::`, or `crate::` lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 0e874669043..2db229ed133 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -103,8 +103,11 @@ fn expr_parent_is_else(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { } fn expr_parent_is_stmt(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { - let Some((_, hir::Node::Stmt(stmt))) = tcx.hir().parent_iter(hir_id).next() else { - return false; + let mut parents = tcx.hir().parent_iter(hir_id); + let stmt = match parents.next() { + Some((_, hir::Node::Stmt(stmt))) => stmt, + Some((_, hir::Node::Block(_) | hir::Node::Arm(_))) => return true, + _ => return false, }; let (hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr)) = stmt.kind else { return false }; expr.hir_id == hir_id diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 7c8a65059d1..482650e04e8 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -17,8 +17,9 @@ use tracing::debug; use crate::lints::{ BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistentDocKeyword, - NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag, TyQualified, - TykindDiag, TykindKind, TypeIrInherentUsage, UntranslatableDiag, + NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag, + SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrInherentUsage, + UntranslatableDiag, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; @@ -650,3 +651,33 @@ fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => false, } } + +declare_tool_lint! { + /// The `symbol_intern_string_literal` detects `Symbol::intern` being called on a string literal + pub rustc::SYMBOL_INTERN_STRING_LITERAL, + // rustc_driver crates out of the compiler can't/shouldn't add preinterned symbols; + // bootstrap will deny this manually + Allow, + "Forbid uses of string literals in `Symbol::intern`, suggesting preinterning instead", + report_in_external_macro: true +} + +declare_lint_pass!(SymbolInternStringLiteral => [SYMBOL_INTERN_STRING_LITERAL]); + +impl<'tcx> LateLintPass<'tcx> for SymbolInternStringLiteral { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + if let ExprKind::Call(path, [arg]) = expr.kind + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::SymbolIntern, def_id) + && let ExprKind::Lit(kind) = arg.kind + && let rustc_ast::LitKind::Str(_, _) = kind.node + { + cx.emit_span_lint( + SYMBOL_INTERN_STRING_LITERAL, + kind.span, + SymbolInternStringLiteralDiag, + ); + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 4cf5c7b4ff9..a99c94592b3 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -614,6 +614,8 @@ fn register_internals(store: &mut LintStore) { store.register_late_mod_pass(|_| Box::new(PassByValue)); store.register_lints(&SpanUseEqCtxt::lint_vec()); store.register_late_mod_pass(|_| Box::new(SpanUseEqCtxt)); + store.register_lints(&SymbolInternStringLiteral::lint_vec()); + store.register_late_mod_pass(|_| Box::new(SymbolInternStringLiteral)); // FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and // `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and // these lints will trigger all of the time - change this once migration to diagnostic structs diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 3a854796f67..20822f23bf1 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -908,6 +908,11 @@ pub(crate) struct QueryUntracked { pub(crate) struct SpanUseEqCtxtDiag; #[derive(LintDiagnostic)] +#[diag(lint_symbol_intern_string_literal)] +#[help] +pub(crate) struct SymbolInternStringLiteralDiag; + +#[derive(LintDiagnostic)] #[diag(lint_tykind_kind)] pub(crate) struct TykindKind { #[suggestion(code = "ty", applicability = "maybe-incorrect")] @@ -1810,6 +1815,42 @@ pub(crate) enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> { }, } +#[derive(LintDiagnostic)] +pub(crate) enum UnpredictableFunctionPointerComparisons<'a> { + #[diag(lint_unpredictable_fn_pointer_comparisons)] + #[note(lint_note_duplicated_fn)] + #[note(lint_note_deduplicated_fn)] + #[note(lint_note_visit_fn_addr_eq)] + Suggestion { + #[subdiagnostic] + sugg: UnpredictableFunctionPointerComparisonsSuggestion<'a>, + }, + #[diag(lint_unpredictable_fn_pointer_comparisons)] + #[note(lint_note_duplicated_fn)] + #[note(lint_note_deduplicated_fn)] + #[note(lint_note_visit_fn_addr_eq)] + Warn, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + lint_fn_addr_eq_suggestion, + style = "verbose", + applicability = "maybe-incorrect" +)] +pub(crate) struct UnpredictableFunctionPointerComparisonsSuggestion<'a> { + pub ne: &'a str, + pub cast_right: String, + pub deref_left: &'a str, + pub deref_right: &'a str, + #[suggestion_part(code = "{ne}std::ptr::fn_addr_eq({deref_left}")] + pub left: Span, + #[suggestion_part(code = ", {deref_right}")] + pub middle: Span, + #[suggestion_part(code = "{cast_right})")] + pub right: Span, +} + pub(crate) struct ImproperCTypes<'a> { pub ty: Ty<'a>, pub desc: &'a str, diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index b1d7d4ab689..33650be056d 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -23,7 +23,9 @@ use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons, - InvalidNanComparisonsSuggestion, UnusedComparisons, VariantSizeDifferencesDiag, + InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons, + UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, + VariantSizeDifferencesDiag, }; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; @@ -166,6 +168,35 @@ declare_lint! { "detects ambiguous wide pointer comparisons" } +declare_lint! { + /// The `unpredictable_function_pointer_comparisons` lint checks comparison + /// of function pointer as the operands. + /// + /// ### Example + /// + /// ```rust + /// fn a() {} + /// fn b() {} + /// + /// let f: fn() = a; + /// let g: fn() = b; + /// + /// let _ = f == g; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Function pointers comparisons do not produce meaningful result since + /// they are never guaranteed to be unique and could vary between different + /// code generation units. Furthermore, different functions could have the + /// same address after being merged together. + UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS, + Warn, + "detects unpredictable function pointer comparisons" +} + #[derive(Copy, Clone, Default)] pub(crate) struct TypeLimits { /// Id of the last visited negated expression @@ -178,7 +209,8 @@ impl_lint_pass!(TypeLimits => [ UNUSED_COMPARISONS, OVERFLOWING_LITERALS, INVALID_NAN_COMPARISONS, - AMBIGUOUS_WIDE_POINTER_COMPARISONS + AMBIGUOUS_WIDE_POINTER_COMPARISONS, + UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS ]); impl TypeLimits { @@ -255,7 +287,7 @@ fn lint_nan<'tcx>( cx.emit_span_lint(INVALID_NAN_COMPARISONS, e.span, lint); } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] enum ComparisonOp { BinOp(hir::BinOpKind), Other, @@ -383,6 +415,100 @@ fn lint_wide_pointer<'tcx>( ); } +fn lint_fn_pointer<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx hir::Expr<'tcx>, + cmpop: ComparisonOp, + l: &'tcx hir::Expr<'tcx>, + r: &'tcx hir::Expr<'tcx>, +) { + let peel_refs = |mut ty: Ty<'tcx>| -> (Ty<'tcx>, usize) { + let mut refs = 0; + + while let ty::Ref(_, inner_ty, _) = ty.kind() { + ty = *inner_ty; + refs += 1; + } + + (ty, refs) + }; + + // Left and right operands can have borrows, remove them + let l = l.peel_borrows(); + let r = r.peel_borrows(); + + let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else { return }; + let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else { return }; + + // Remove any references as `==` will deref through them (and count the + // number of references removed, for latter). + let (l_ty, l_ty_refs) = peel_refs(l_ty); + let (r_ty, r_ty_refs) = peel_refs(r_ty); + + if !l_ty.is_fn() || !r_ty.is_fn() { + return; + } + + // Let's try to suggest `ptr::fn_addr_eq` if/when possible. + + let is_eq_ne = matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne)); + + if !is_eq_ne { + // Neither `==` nor `!=`, we can't suggest `ptr::fn_addr_eq`, just show the warning. + return cx.emit_span_lint( + UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS, + e.span, + UnpredictableFunctionPointerComparisons::Warn, + ); + } + + let (Some(l_span), Some(r_span)) = + (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span)) + else { + // No appropriate spans for the left and right operands, just show the warning. + return cx.emit_span_lint( + UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS, + e.span, + UnpredictableFunctionPointerComparisons::Warn, + ); + }; + + let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" }; + + // `ptr::fn_addr_eq` only works with raw pointer, deref any references. + let deref_left = &*"*".repeat(l_ty_refs); + let deref_right = &*"*".repeat(r_ty_refs); + + let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo()); + let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo()); + let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi()); + + // We only check for a right cast as `FnDef` == `FnPtr` is not possible, + // only `FnPtr == FnDef` is possible. + let cast_right = if !r_ty.is_fn_ptr() { + let fn_sig = r_ty.fn_sig(cx.tcx); + format!(" as {fn_sig}") + } else { + String::new() + }; + + cx.emit_span_lint( + UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS, + e.span, + UnpredictableFunctionPointerComparisons::Suggestion { + sugg: UnpredictableFunctionPointerComparisonsSuggestion { + ne, + deref_left, + deref_right, + left, + middle, + right, + cast_right, + }, + }, + ); +} + impl<'tcx> LateLintPass<'tcx> for TypeLimits { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) { match e.kind { @@ -399,7 +525,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons); } else { lint_nan(cx, e, binop, l, r); - lint_wide_pointer(cx, e, ComparisonOp::BinOp(binop.node), l, r); + let cmpop = ComparisonOp::BinOp(binop.node); + lint_wide_pointer(cx, e, cmpop, l, r); + lint_fn_pointer(cx, e, cmpop, l, r); } } } @@ -411,6 +539,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { && let Some(cmpop) = diag_item_cmpop(diag_item) => { lint_wide_pointer(cx, e, cmpop, l, r); + lint_fn_pointer(cx, e, cmpop, l, r); } hir::ExprKind::MethodCall(_, l, [r], _) if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) @@ -418,6 +547,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { && let Some(cmpop) = diag_item_cmpop(diag_item) => { lint_wide_pointer(cx, e, cmpop, l, r); + lint_fn_pointer(cx, e, cmpop, l, r); } _ => {} }; diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index e1a0e1ec579..b775cd37409 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1563,7 +1563,7 @@ impl UnusedImportBraces { } rename.unwrap_or(orig_ident).name } - ast::UseTreeKind::Glob => Symbol::intern("*"), + ast::UseTreeKind::Glob => sym::asterisk, ast::UseTreeKind::Nested { .. } => return, }; diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index a18c6baec00..29dba2bca61 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -861,8 +861,10 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // First up we check for global allocators. Look at the crate graph here // and see what's a global allocator, including if we ourselves are a // global allocator. - let mut global_allocator = - self.cstore.has_global_allocator.then(|| Symbol::intern("this crate")); + #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + let this_crate = Symbol::intern("this crate"); + + let mut global_allocator = self.cstore.has_global_allocator.then_some(this_crate); for (_, data) in self.cstore.iter_crate_data() { if data.has_global_allocator() { match global_allocator { @@ -876,8 +878,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { } } } - let mut alloc_error_handler = - self.cstore.has_alloc_error_handler.then(|| Symbol::intern("this crate")); + let mut alloc_error_handler = self.cstore.has_alloc_error_handler.then_some(this_crate); for (_, data) in self.cstore.iter_crate_data() { if data.has_alloc_error_handler() { match alloc_error_handler { diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 56beff5aa64..f3f5af49412 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -872,6 +872,7 @@ impl MetadataBlob { let def_kind = root.tables.def_kind.get(blob, item).unwrap(); let def_key = root.tables.def_keys.get(blob, item).unwrap().decode(blob); + #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] let def_name = if item == CRATE_DEF_INDEX { rustc_span::symbol::kw::Crate } else { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 068a5d31a8e..a34ea18f716 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1389,10 +1389,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // `ConstArgKind::Path`. We never actually access this `DefId` // anywhere so we don't need to encode it for other crates. if def_kind == DefKind::AnonConst - && matches!( - tcx.hir_node_by_def_id(local_id), - hir::Node::ConstArg(hir::ConstArg { kind: hir::ConstArgKind::Path(..), .. }) - ) + && match tcx.hir_node_by_def_id(local_id) { + hir::Node::ConstArg(hir::ConstArg { kind, .. }) => match kind { + // Skip encoding defs for these as they should not have had a `DefId` created + hir::ConstArgKind::Path(..) | hir::ConstArgKind::Infer(..) => true, + hir::ConstArgKind::Anon(..) => false, + }, + _ => false, + } { continue; } diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 9438350ca09..28f406fbc96 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -1,6 +1,5 @@ use rustc_hir::def::CtorOf; use rustc_index::Idx; -use tracing::trace; use crate::rmeta::*; @@ -530,8 +529,6 @@ where { /// Given the metadata, extract out the value at a particular index (if any). pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(&self, metadata: M, i: I) -> T::Value<'tcx> { - trace!("LazyTable::lookup: index={:?} len={:?}", i, self.len); - // Access past the end of the table returns a Default if i.index() >= self.len { return Default::default(); diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index dfa62d875b3..8861f081485 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -916,6 +916,12 @@ rustc_queries! { cache_on_disk_if { true } } + /// Checks well-formedness of tail calls (`become f()`). + query check_tail_calls(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> { + desc { |tcx| "tail-call-checking `{}`", tcx.def_path_str(key) } + cache_on_disk_if { true } + } + /// Returns the types assumed to be well formed while "inside" of the given item. /// /// Note that we've liberated the late bound regions of function signatures, so @@ -1081,6 +1087,8 @@ rustc_queries! { } /// Computes the tag (if any) for a given type and variant. + /// `None` means that the variant doesn't need a tag (because it is niched). + /// Will panic for uninhabited variants. query tag_for_variant( key: (Ty<'tcx>, abi::VariantIdx) ) -> Option<ty::ScalarInt> { diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index f28cf84fa69..f647486f62a 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -84,12 +84,17 @@ mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable -mir_build_const_param_in_pattern = const parameters cannot be referenced in patterns +mir_build_const_defined_here = constant defined here -mir_build_const_pattern_depends_on_generic_parameter = - constant pattern depends on a generic parameter +mir_build_const_param_in_pattern = constant parameters cannot be referenced in patterns + .label = can't be used in patterns +mir_build_const_param_in_pattern_def = constant defined here + +mir_build_const_pattern_depends_on_generic_parameter = constant pattern cannot depend on generic parameters + .label = `const` depends on a generic parameter mir_build_could_not_eval_const_pattern = could not evaluate constant pattern + .label = could not evaluate constant mir_build_deref_raw_pointer_requires_unsafe = dereference of raw pointer is unsafe and requires unsafe block @@ -147,7 +152,8 @@ mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = mir_build_interpreted_as_const = introduce a variable instead -mir_build_invalid_pattern = `{$non_sm_ty}` cannot be used in patterns +mir_build_invalid_pattern = {$prefix} `{$non_sm_ty}` cannot be used in patterns + .label = {$prefix} can't be used in patterns mir_build_irrefutable_let_patterns_if_let = irrefutable `if let` {$count -> [one] pattern @@ -244,10 +250,12 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa .label = mutation of layout constrained field mir_build_nan_pattern = cannot use NaN in patterns + .label = evaluates to `NaN`, which is not allowed in patterns .note = NaNs compare inequal to everything, even themselves, so this pattern would never match .help = try using the `is_nan` method instead mir_build_non_const_path = runtime values cannot be referenced in patterns + .label = references a runtime value mir_build_non_empty_never_pattern = mismatched types @@ -265,13 +273,15 @@ mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type .suggestion = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown .help = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern -mir_build_non_partial_eq_match = - to use a constant of type `{$non_peq_ty}` in a pattern, the type must implement `PartialEq` +mir_build_non_partial_eq_match = constant of non-structural type `{$ty}` in a pattern + .label = constant of non-structural type mir_build_pattern_not_covered = refutable pattern in {$origin} .pattern_ty = the matched value is of type `{$pattern_ty}` -mir_build_pointer_pattern = function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. +mir_build_pointer_pattern = function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon + .label = can't be used in patterns + .note = see https://github.com/rust-lang/rust/issues/70861 for details mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future @@ -283,6 +293,8 @@ mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly .missing_box = `#[rustc_box]` requires the `owned_box` lang item mir_build_static_in_pattern = statics cannot be referenced in patterns + .label = can't be used in patterns +mir_build_static_in_pattern_def = `static` defined here mir_build_suggest_attempted_int_lit = alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits @@ -310,12 +322,12 @@ mir_build_trailing_irrefutable_let_patterns = trailing irrefutable {$count -> *[other] them } into the body -mir_build_type_not_structural = - to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq)]` - +mir_build_type_not_structural = constant of non-structural type `{$ty}` in a pattern + .label = constant of non-structural type +mir_build_type_not_structural_def = `{$ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns mir_build_type_not_structural_more_info = see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details - -mir_build_type_not_structural_tip = the traits must be derived, manual `impl`s are not sufficient +mir_build_type_not_structural_tip = + the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details mir_build_unconditional_recursion = function cannot return without recursing .label = cannot return without recursing @@ -334,6 +346,7 @@ mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = .label = access to union field mir_build_union_pattern = cannot use unions in constant patterns + .label = can't use a `union` here mir_build_unreachable_making_this_unreachable = collectively making this unreachable diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 3317f3b7f8a..f43c29d8f5d 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -50,6 +50,10 @@ pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx return construct_error(tcx, def, e); } + if let Err(err) = tcx.check_tail_calls(def) { + return construct_error(tcx, def, err); + } + let body = match tcx.thir_body(def) { Err(error_reported) => construct_error(tcx, def, error_reported), Ok((thir, expr)) => { diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs new file mode 100644 index 00000000000..b1f46d37d50 --- /dev/null +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -0,0 +1,386 @@ +use rustc_abi::ExternAbi; +use rustc_errors::Applicability; +use rustc_hir::LangItem; +use rustc_hir::def::DefKind; +use rustc_middle::span_bug; +use rustc_middle::thir::visit::{self, Visitor}; +use rustc_middle::thir::{BodyTy, Expr, ExprId, ExprKind, Thir}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; + +pub(crate) fn check_tail_calls(tcx: TyCtxt<'_>, def: LocalDefId) -> Result<(), ErrorGuaranteed> { + let (thir, expr) = tcx.thir_body(def)?; + let thir = &thir.borrow(); + + // If `thir` is empty, a type error occurred, skip this body. + if thir.exprs.is_empty() { + return Ok(()); + } + + let is_closure = matches!(tcx.def_kind(def), DefKind::Closure); + let caller_ty = tcx.type_of(def).skip_binder(); + + let mut visitor = TailCallCkVisitor { + tcx, + thir, + found_errors: Ok(()), + // FIXME(#132279): we're clearly in a body here. + typing_env: ty::TypingEnv::non_body_analysis(tcx, def), + is_closure, + caller_ty, + }; + + visitor.visit_expr(&thir[expr]); + + visitor.found_errors +} + +struct TailCallCkVisitor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + thir: &'a Thir<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + /// Whatever the currently checked body is one of a closure + is_closure: bool, + /// The result of the checks, `Err(_)` if there was a problem with some + /// tail call, `Ok(())` if all of them were fine. + found_errors: Result<(), ErrorGuaranteed>, + /// Type of the caller function. + caller_ty: Ty<'tcx>, +} + +impl<'tcx> TailCallCkVisitor<'_, 'tcx> { + fn check_tail_call(&mut self, call: &Expr<'_>, expr: &Expr<'_>) { + if self.is_closure { + self.report_in_closure(expr); + return; + } + + let BodyTy::Fn(caller_sig) = self.thir.body_type else { + span_bug!( + call.span, + "`become` outside of functions should have been disallowed by hit_typeck" + ) + }; + + let ExprKind::Scope { value, .. } = call.kind else { + span_bug!(call.span, "expected scope, found: {call:?}") + }; + let value = &self.thir[value]; + + if matches!( + value.kind, + ExprKind::Binary { .. } + | ExprKind::Unary { .. } + | ExprKind::AssignOp { .. } + | ExprKind::Index { .. } + ) { + self.report_builtin_op(call, expr); + return; + } + + let ExprKind::Call { ty, fun, ref args, from_hir_call, fn_span } = value.kind else { + self.report_non_call(value, expr); + return; + }; + + if !from_hir_call { + self.report_op(ty, args, fn_span, expr); + } + + // Closures in thir look something akin to + // `for<'a> extern "rust-call" fn(&'a [closure@...], ()) -> <[closure@...] as FnOnce<()>>::Output {<[closure@...] as Fn<()>>::call}` + // So we have to check for them in this weird way... + if let &ty::FnDef(did, args) = ty.kind() { + let parent = self.tcx.parent(did); + if self.tcx.fn_trait_kind_from_def_id(parent).is_some() + && args.first().and_then(|arg| arg.as_type()).is_some_and(Ty::is_closure) + { + self.report_calling_closure(&self.thir[fun], args[1].as_type().unwrap(), expr); + + // Tail calling is likely to cause unrelated errors (ABI, argument mismatches), + // skip them, producing an error about calling a closure is enough. + return; + }; + } + + // Erase regions since tail calls don't care about lifetimes + let callee_sig = + self.tcx.normalize_erasing_late_bound_regions(self.typing_env, ty.fn_sig(self.tcx)); + + if caller_sig.abi != callee_sig.abi { + self.report_abi_mismatch(expr.span, caller_sig.abi, callee_sig.abi); + } + + if caller_sig.inputs_and_output != callee_sig.inputs_and_output { + if caller_sig.inputs() != callee_sig.inputs() { + self.report_arguments_mismatch(expr.span, caller_sig, callee_sig); + } + + // FIXME(explicit_tail_calls): this currenly fails for cases where opaques are used. + // e.g. + // ``` + // fn a() -> impl Sized { become b() } // ICE + // fn b() -> u8 { 0 } + // ``` + // we should think what is the expected behavior here. + // (we should probably just accept this by revealing opaques?) + if caller_sig.output() != callee_sig.output() { + span_bug!(expr.span, "hir typeck should have checked the return type already"); + } + } + + { + let caller_needs_location = self.needs_location(self.caller_ty); + let callee_needs_location = self.needs_location(ty); + + if caller_needs_location != callee_needs_location { + self.report_track_caller_mismatch(expr.span, caller_needs_location); + } + } + + if caller_sig.c_variadic { + self.report_c_variadic_caller(expr.span); + } + + if callee_sig.c_variadic { + self.report_c_variadic_callee(expr.span); + } + } + + /// Returns true if function of type `ty` needs location argument + /// (i.e. if a function is marked as `#[track_caller]`) + fn needs_location(&self, ty: Ty<'tcx>) -> bool { + if let &ty::FnDef(did, substs) = ty.kind() { + let instance = + ty::Instance::expect_resolve(self.tcx, self.typing_env, did, substs, DUMMY_SP); + + instance.def.requires_caller_location(self.tcx) + } else { + false + } + } + + fn report_in_closure(&mut self, expr: &Expr<'_>) { + let err = self.tcx.dcx().span_err(expr.span, "`become` is not allowed in closures"); + self.found_errors = Err(err); + } + + fn report_builtin_op(&mut self, value: &Expr<'_>, expr: &Expr<'_>) { + let err = self + .tcx + .dcx() + .struct_span_err(value.span, "`become` does not support operators") + .with_note("using `become` on a builtin operator is not useful") + .with_span_suggestion( + value.span.until(expr.span), + "try using `return` instead", + "return ", + Applicability::MachineApplicable, + ) + .emit(); + self.found_errors = Err(err); + } + + fn report_op(&mut self, fun_ty: Ty<'_>, args: &[ExprId], fn_span: Span, expr: &Expr<'_>) { + let mut err = + self.tcx.dcx().struct_span_err(fn_span, "`become` does not support operators"); + + if let &ty::FnDef(did, _substs) = fun_ty.kind() + && let parent = self.tcx.parent(did) + && matches!(self.tcx.def_kind(parent), DefKind::Trait) + && let Some(method) = op_trait_as_method_name(self.tcx, parent) + { + match args { + &[arg] => { + let arg = &self.thir[arg]; + + err.multipart_suggestion( + "try using the method directly", + vec![ + (fn_span.shrink_to_lo().until(arg.span), "(".to_owned()), + (arg.span.shrink_to_hi(), format!(").{method}()")), + ], + Applicability::MaybeIncorrect, + ); + } + &[lhs, rhs] => { + let lhs = &self.thir[lhs]; + let rhs = &self.thir[rhs]; + + err.multipart_suggestion( + "try using the method directly", + vec![ + (lhs.span.shrink_to_lo(), format!("(")), + (lhs.span.between(rhs.span), format!(").{method}(")), + (rhs.span.between(expr.span.shrink_to_hi()), ")".to_owned()), + ], + Applicability::MaybeIncorrect, + ); + } + _ => span_bug!(expr.span, "operator with more than 2 args? {args:?}"), + } + } + + self.found_errors = Err(err.emit()); + } + + fn report_non_call(&mut self, value: &Expr<'_>, expr: &Expr<'_>) { + let err = self + .tcx + .dcx() + .struct_span_err(value.span, "`become` requires a function call") + .with_span_note(value.span, "not a function call") + .with_span_suggestion( + value.span.until(expr.span), + "try using `return` instead", + "return ", + Applicability::MaybeIncorrect, + ) + .emit(); + self.found_errors = Err(err); + } + + fn report_calling_closure(&mut self, fun: &Expr<'_>, tupled_args: Ty<'_>, expr: &Expr<'_>) { + let underscored_args = match tupled_args.kind() { + ty::Tuple(tys) if tys.is_empty() => "".to_owned(), + ty::Tuple(tys) => std::iter::repeat("_, ").take(tys.len() - 1).chain(["_"]).collect(), + _ => "_".to_owned(), + }; + + let err = self + .tcx + .dcx() + .struct_span_err(expr.span, "tail calling closures directly is not allowed") + .with_multipart_suggestion( + "try casting the closure to a function pointer type", + vec![ + (fun.span.shrink_to_lo(), "(".to_owned()), + (fun.span.shrink_to_hi(), format!(" as fn({underscored_args}) -> _)")), + ], + Applicability::MaybeIncorrect, + ) + .emit(); + self.found_errors = Err(err); + } + + fn report_abi_mismatch(&mut self, sp: Span, caller_abi: ExternAbi, callee_abi: ExternAbi) { + let err = self + .tcx + .dcx() + .struct_span_err(sp, "mismatched function ABIs") + .with_note("`become` requires caller and callee to have the same ABI") + .with_note(format!("caller ABI is `{caller_abi}`, while callee ABI is `{callee_abi}`")) + .emit(); + self.found_errors = Err(err); + } + + fn report_arguments_mismatch( + &mut self, + sp: Span, + caller_sig: ty::FnSig<'_>, + callee_sig: ty::FnSig<'_>, + ) { + let err = self + .tcx + .dcx() + .struct_span_err(sp, "mismatched signatures") + .with_note("`become` requires caller and callee to have matching signatures") + .with_note(format!("caller signature: `{caller_sig}`")) + .with_note(format!("callee signature: `{callee_sig}`")) + .emit(); + self.found_errors = Err(err); + } + + fn report_track_caller_mismatch(&mut self, sp: Span, caller_needs_location: bool) { + let err = match caller_needs_location { + true => self + .tcx + .dcx() + .struct_span_err( + sp, + "a function marked with `#[track_caller]` cannot tail-call one that is not", + ) + .emit(), + false => self + .tcx + .dcx() + .struct_span_err( + sp, + "a function mot marked with `#[track_caller]` cannot tail-call one that is", + ) + .emit(), + }; + + self.found_errors = Err(err); + } + + fn report_c_variadic_caller(&mut self, sp: Span) { + let err = self + .tcx + .dcx() + // FIXME(explicit_tail_calls): highlight the `...` + .struct_span_err(sp, "tail-calls are not allowed in c-variadic functions") + .emit(); + + self.found_errors = Err(err); + } + + fn report_c_variadic_callee(&mut self, sp: Span) { + let err = self + .tcx + .dcx() + // FIXME(explicit_tail_calls): highlight the function or something... + .struct_span_err(sp, "c-variadic functions can't be tail-called") + .emit(); + + self.found_errors = Err(err); + } +} + +impl<'a, 'tcx> Visitor<'a, 'tcx> for TailCallCkVisitor<'a, 'tcx> { + fn thir(&self) -> &'a Thir<'tcx> { + &self.thir + } + + fn visit_expr(&mut self, expr: &'a Expr<'tcx>) { + if let ExprKind::Become { value } = expr.kind { + let call = &self.thir[value]; + self.check_tail_call(call, expr); + } + + visit::walk_expr(self, expr); + } +} + +fn op_trait_as_method_name(tcx: TyCtxt<'_>, trait_did: DefId) -> Option<&'static str> { + let m = match tcx.as_lang_item(trait_did)? { + LangItem::Add => "add", + LangItem::Sub => "sub", + LangItem::Mul => "mul", + LangItem::Div => "div", + LangItem::Rem => "rem", + LangItem::Neg => "neg", + LangItem::Not => "not", + LangItem::BitXor => "bitxor", + LangItem::BitAnd => "bitand", + LangItem::BitOr => "bitor", + LangItem::Shl => "shl", + LangItem::Shr => "shr", + LangItem::AddAssign => "add_assign", + LangItem::SubAssign => "sub_assign", + LangItem::MulAssign => "mul_assign", + LangItem::DivAssign => "div_assign", + LangItem::RemAssign => "rem_assign", + LangItem::BitXorAssign => "bitxor_assign", + LangItem::BitAndAssign => "bitand_assign", + LangItem::BitOrAssign => "bitor_assign", + LangItem::ShlAssign => "shl_assign", + LangItem::ShrAssign => "shr_assign", + LangItem::Index => "index", + LangItem::IndexMut => "index_mut", + _ => return None, + }; + + Some(m) +} diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 58487a48184..3632da943e1 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -631,20 +631,27 @@ pub(crate) struct NonExhaustiveMatchAllArmsGuarded; #[diag(mir_build_static_in_pattern, code = E0158)] pub(crate) struct StaticInPattern { #[primary_span] + #[label] pub(crate) span: Span, + #[label(mir_build_static_in_pattern_def)] + pub(crate) static_span: Span, } #[derive(Diagnostic)] #[diag(mir_build_const_param_in_pattern, code = E0158)] pub(crate) struct ConstParamInPattern { #[primary_span] + #[label] pub(crate) span: Span, + #[label(mir_build_const_param_in_pattern_def)] + pub(crate) const_span: Span, } #[derive(Diagnostic)] #[diag(mir_build_non_const_path, code = E0080)] pub(crate) struct NonConstPath { #[primary_span] + #[label] pub(crate) span: Span, } @@ -695,6 +702,7 @@ pub(crate) struct WantedConstant { #[diag(mir_build_const_pattern_depends_on_generic_parameter, code = E0158)] pub(crate) struct ConstPatternDependsOnGenericParameter { #[primary_span] + #[label] pub(crate) span: Span, } @@ -702,6 +710,7 @@ pub(crate) struct ConstPatternDependsOnGenericParameter { #[diag(mir_build_could_not_eval_const_pattern)] pub(crate) struct CouldNotEvalConstPattern { #[primary_span] + #[label] pub(crate) span: Span, } @@ -867,33 +876,43 @@ pub(crate) enum Conflict { #[diag(mir_build_union_pattern)] pub(crate) struct UnionPattern { #[primary_span] + #[label] pub(crate) span: Span, } #[derive(Diagnostic)] #[diag(mir_build_type_not_structural)] -#[note(mir_build_type_not_structural_tip)] -#[note(mir_build_type_not_structural_more_info)] pub(crate) struct TypeNotStructural<'tcx> { #[primary_span] + #[label] pub(crate) span: Span, - pub(crate) non_sm_ty: Ty<'tcx>, + #[label(mir_build_type_not_structural_def)] + pub(crate) ty_def_span: Span, + pub(crate) ty: Ty<'tcx>, + #[note(mir_build_type_not_structural_tip)] + pub(crate) manual_partialeq_impl_span: Option<Span>, + #[note(mir_build_type_not_structural_more_info)] + pub(crate) manual_partialeq_impl_note: bool, } #[derive(Diagnostic)] #[diag(mir_build_non_partial_eq_match)] +#[note(mir_build_type_not_structural_more_info)] pub(crate) struct TypeNotPartialEq<'tcx> { #[primary_span] + #[label] pub(crate) span: Span, - pub(crate) non_peq_ty: Ty<'tcx>, + pub(crate) ty: Ty<'tcx>, } #[derive(Diagnostic)] #[diag(mir_build_invalid_pattern)] pub(crate) struct InvalidPattern<'tcx> { #[primary_span] + #[label] pub(crate) span: Span, pub(crate) non_sm_ty: Ty<'tcx>, + pub(crate) prefix: String, } #[derive(Diagnostic)] @@ -910,13 +929,16 @@ pub(crate) struct UnsizedPattern<'tcx> { #[help] pub(crate) struct NaNPattern { #[primary_span] + #[label] pub(crate) span: Span, } #[derive(Diagnostic)] #[diag(mir_build_pointer_pattern)] +#[note] pub(crate) struct PointerPattern { #[primary_span] + #[label] pub(crate) span: Span, } diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 3dbb552cdbb..833e5019865 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -12,6 +12,7 @@ // tidy-alphabetical-end mod build; +mod check_tail_calls; mod check_unsafety; mod errors; pub mod lints; @@ -28,6 +29,7 @@ pub fn provide(providers: &mut Providers) { providers.closure_saved_names_of_captured_variables = build::closure_saved_names_of_captured_variables; providers.check_unsafety = check_unsafety::check_unsafety; + providers.check_tail_calls = check_tail_calls::check_tail_calls; providers.thir_body = thir::cx::thir_body; providers.hooks.thir_tree = thir::print::thir_tree; providers.hooks.thir_flat = thir::print::thir_flat; diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 5db08f01fdb..aed00aecefc 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,14 +1,17 @@ use rustc_abi::{FieldIdx, VariantIdx}; use rustc_apfloat::Float; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Diag; use rustc_hir as hir; use rustc_index::Idx; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::thir::{FieldPat, Pat, PatKind}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, ValTree}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeVisitor, ValTree}; use rustc_middle::{mir, span_bug}; -use rustc_span::Span; +use rustc_span::def_id::DefId; +use rustc_span::{Span, sym}; use rustc_trait_selection::traits::ObligationCause; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use tracing::{debug, instrument, trace}; @@ -35,7 +38,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { id: hir::HirId, span: Span, ) -> Box<Pat<'tcx>> { - let mut convert = ConstToPat::new(self, id, span); + let mut convert = ConstToPat::new(self, id, span, c); match c.kind() { ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty), @@ -49,21 +52,26 @@ struct ConstToPat<'tcx> { tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, span: Span, + id: hir::HirId, treat_byte_string_as_slice: bool, + + c: ty::Const<'tcx>, } impl<'tcx> ConstToPat<'tcx> { - fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span) -> Self { + fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self { trace!(?pat_ctxt.typeck_results.hir_owner); ConstToPat { tcx: pat_ctxt.tcx, typing_env: pat_ctxt.typing_env, span, + id, treat_byte_string_as_slice: pat_ctxt .typeck_results .treat_byte_string_as_slice .contains(&id.local_id), + c, } } @@ -71,13 +79,32 @@ impl<'tcx> ConstToPat<'tcx> { ty.is_structural_eq_shallow(self.tcx) } + /// We errored. Signal that in the pattern, so that follow up errors can be silenced. + fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> { + if let ty::ConstKind::Unevaluated(uv) = self.c.kind() { + let def_kind = self.tcx.def_kind(uv.def); + if let hir::def::DefKind::AssocConst = def_kind + && let Some(def_id) = uv.def.as_local() + { + // Include the container item in the output. + err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), ""); + } + if let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = def_kind { + err.span_label( + self.tcx.def_span(uv.def), + crate::fluent_generated::mir_build_const_defined_here, + ); + } + } + Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()) }) + } + fn unevaluated_to_pat( &mut self, uv: ty::UnevaluatedConst<'tcx>, ty: Ty<'tcx>, ) -> Box<Pat<'tcx>> { trace!(self.treat_byte_string_as_slice); - let pat_from_kind = |kind| Box::new(Pat { span: self.span, ty, kind }); // It's not *technically* correct to be revealing opaque types here as borrowcheck has // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even @@ -96,32 +123,60 @@ impl<'tcx> ConstToPat<'tcx> { Ok(Ok(c)) => c, Err(ErrorHandled::Reported(_, _)) => { // Let's tell the use where this failing const occurs. - let e = self.tcx.dcx().emit_err(CouldNotEvalConstPattern { span: self.span }); - return pat_from_kind(PatKind::Error(e)); + let mut err = + self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span }); + // We've emitted an error on the original const, it would be redundant to complain + // on its use as well. + if let ty::ConstKind::Unevaluated(uv) = self.c.kind() + && let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = + self.tcx.def_kind(uv.def) + { + err.downgrade_to_delayed_bug(); + } + return self.mk_err(err, ty); } Err(ErrorHandled::TooGeneric(_)) => { - let e = self + let mut e = self .tcx .dcx() - .emit_err(ConstPatternDependsOnGenericParameter { span: self.span }); - return pat_from_kind(PatKind::Error(e)); + .create_err(ConstPatternDependsOnGenericParameter { span: self.span }); + for arg in uv.args { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Param(param_ty) = ty.kind() + { + let def_id = self.tcx.hir().enclosing_body_owner(self.id); + let generics = self.tcx.generics_of(def_id); + let param = generics.type_param(*param_ty, self.tcx); + let span = self.tcx.def_span(param.def_id); + e.span_label(span, "constant depends on this generic parameter"); + if let Some(ident) = self.tcx.def_ident_span(def_id) + && self.tcx.sess.source_map().is_multiline(ident.between(span)) + { + // Display the `fn` name as well in the diagnostic, as the generic isn't + // in the same line and it could be confusing otherwise. + e.span_label(ident, ""); + } + } + } + return self.mk_err(e, ty); } Ok(Err(bad_ty)) => { // The pattern cannot be turned into a valtree. let e = match bad_ty.kind() { ty::Adt(def, ..) => { assert!(def.is_union()); - self.tcx.dcx().emit_err(UnionPattern { span: self.span }) + self.tcx.dcx().create_err(UnionPattern { span: self.span }) } ty::FnPtr(..) | ty::RawPtr(..) => { - self.tcx.dcx().emit_err(PointerPattern { span: self.span }) + self.tcx.dcx().create_err(PointerPattern { span: self.span }) } - _ => self - .tcx - .dcx() - .emit_err(InvalidPattern { span: self.span, non_sm_ty: bad_ty }), + _ => self.tcx.dcx().create_err(InvalidPattern { + span: self.span, + non_sm_ty: bad_ty, + prefix: bad_ty.prefix_string(self.tcx).to_string(), + }), }; - return pat_from_kind(PatKind::Error(e)); + return self.mk_err(e, ty); } }; @@ -130,42 +185,16 @@ impl<'tcx> ConstToPat<'tcx> { if !inlined_const_as_pat.references_error() { // Always check for `PartialEq` if we had no other errors yet. - if !self.type_has_partial_eq_impl(ty) { - let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty }; - let e = self.tcx.dcx().emit_err(err); - return pat_from_kind(PatKind::Error(e)); + if !type_has_partial_eq_impl(self.tcx, typing_env, ty).0 { + let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty }); + extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err); + return self.mk_err(err, ty); } } inlined_const_as_pat } - #[instrument(level = "trace", skip(self), ret)] - fn type_has_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool { - let (infcx, param_env) = self.tcx.infer_ctxt().build_with_typing_env(self.typing_env); - // double-check there even *is* a semantic `PartialEq` to dispatch to. - // - // (If there isn't, then we can safely issue a hard - // error, because that's never worked, due to compiler - // using `PartialEq::eq` in this scenario in the past.) - let partial_eq_trait_id = - self.tcx.require_lang_item(hir::LangItem::PartialEq, Some(self.span)); - let partial_eq_obligation = Obligation::new( - self.tcx, - ObligationCause::dummy(), - param_env, - ty::TraitRef::new(self.tcx, partial_eq_trait_id, [ty, ty]), - ); - - // This *could* accept a type that isn't actually `PartialEq`, because region bounds get - // ignored. However that should be pretty much impossible since consts that do not depend on - // generics can only mention the `'static` lifetime, and how would one have a type that's - // `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem - // we'll need to leave some sort of trace of this requirement in the MIR so that borrowck - // can ensure that the type really implements `PartialEq`. - infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation) - } - fn field_pats( &self, vals: impl Iterator<Item = (ValTree<'tcx>, Ty<'tcx>)>, @@ -190,10 +219,25 @@ impl<'tcx> ConstToPat<'tcx> { // Extremely important check for all ADTs! Make sure they opted-in to be used in // patterns. debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty); - let err = TypeNotStructural { span, non_sm_ty: ty }; - let e = tcx.dcx().emit_err(err); - // We errored. Signal that in the pattern, so that follow up errors can be silenced. - PatKind::Error(e) + let (_impls_partial_eq, derived, structural, impl_def_id) = + type_has_partial_eq_impl(self.tcx, self.typing_env, ty); + let (manual_partialeq_impl_span, manual_partialeq_impl_note) = + match (structural, impl_def_id) { + (true, _) => (None, false), + (_, Some(def_id)) if def_id.is_local() && !derived => { + (Some(tcx.def_span(def_id)), false) + } + _ => (None, true), + }; + let ty_def_span = tcx.def_span(adt_def.did()); + let err = TypeNotStructural { + span, + ty, + ty_def_span, + manual_partialeq_impl_span, + manual_partialeq_impl_note, + }; + return self.mk_err(tcx.dcx().create_err(err), ty); } ty::Adt(adt_def, args) if adt_def.is_enum() => { let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap(); @@ -207,7 +251,7 @@ impl<'tcx> ConstToPat<'tcx> { adt_def.variants()[variant_index] .fields .iter() - .map(|field| field.ty(self.tcx, args)), + .map(|field| field.ty(tcx, args)), ), ), } @@ -216,7 +260,7 @@ impl<'tcx> ConstToPat<'tcx> { assert!(!def.is_union()); // Valtree construction would never succeed for unions. PatKind::Leaf { subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip( - def.non_enum_variant().fields.iter().map(|field| field.ty(self.tcx, args)), + def.non_enum_variant().fields.iter().map(|field| field.ty(tcx, args)), )), } } @@ -252,10 +296,10 @@ impl<'tcx> ConstToPat<'tcx> { // deref pattern. _ => { if !pointee_ty.is_sized(tcx, self.typing_env) && !pointee_ty.is_slice() { - let err = UnsizedPattern { span, non_sm_ty: *pointee_ty }; - let e = tcx.dcx().emit_err(err); - // We errored. Signal that in the pattern, so that follow up errors can be silenced. - PatKind::Error(e) + return self.mk_err( + tcx.dcx().create_err(UnsizedPattern { span, non_sm_ty: *pointee_ty }), + ty, + ); } else { // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when // matching against references, you can only use byte string literals. @@ -286,8 +330,7 @@ impl<'tcx> ConstToPat<'tcx> { if is_nan { // NaNs are not ever equal to anything so they make no sense as patterns. // Also see <https://github.com/rust-lang/rfcs/pull/3535>. - let e = tcx.dcx().emit_err(NaNPattern { span }); - PatKind::Error(e) + return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty); } else { PatKind::Constant { value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)), @@ -305,13 +348,153 @@ impl<'tcx> ConstToPat<'tcx> { ) } _ => { - let err = InvalidPattern { span, non_sm_ty: ty }; - let e = tcx.dcx().emit_err(err); - // We errored. Signal that in the pattern, so that follow up errors can be silenced. - PatKind::Error(e) + let err = InvalidPattern { + span, + non_sm_ty: ty, + prefix: ty.prefix_string(tcx).to_string(), + }; + return self.mk_err(tcx.dcx().create_err(err), ty); } }; Box::new(Pat { span, ty, kind }) } } + +/// Given a type with type parameters, visit every ADT looking for types that need to +/// `#[derive(PartialEq)]` for it to be a structural type. +fn extend_type_not_partial_eq<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, + err: &mut Diag<'_>, +) { + /// Collect all types that need to be `StructuralPartialEq`. + struct UsedParamsNeedInstantiationVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + /// The user has written `impl PartialEq for Ty` which means it's non-structual. + adts_with_manual_partialeq: FxHashSet<Span>, + /// The type has no `PartialEq` implementation, neither manual or derived. + adts_without_partialeq: FxHashSet<Span>, + /// The user has written `impl PartialEq for Ty` which means it's non-structual, + /// but we don't have a span to point at, so we'll just add them as a `note`. + manual: Vec<Ty<'tcx>>, + /// The type has no `PartialEq` implementation, neither manual or derived, but + /// we don't have a span to point at, so we'll just add them as a `note`. + without: Vec<Ty<'tcx>>, + } + + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if let ty::Adt(def, _args) = ty.kind() { + let ty_def_id = def.did(); + let ty_def_span = self.tcx.def_span(ty_def_id); + let (impls_partial_eq, derived, structural, impl_def_id) = + type_has_partial_eq_impl(self.tcx, self.typing_env, ty); + match (impls_partial_eq, derived, structural, impl_def_id) { + (_, _, true, _) => {} + (true, false, _, Some(def_id)) if def_id.is_local() => { + self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id)); + } + (true, false, _, _) if ty_def_id.is_local() => { + self.adts_with_manual_partialeq.insert(ty_def_span); + } + (false, _, _, _) if ty_def_id.is_local() => { + self.adts_without_partialeq.insert(ty_def_span); + } + (true, false, _, _) => { + self.manual.push(ty); + } + (false, _, _, _) => { + self.without.push(ty); + } + _ => {} + }; + } + use rustc_middle::ty::TypeSuperVisitable; + ty.super_visit_with(self) + } + } + let mut v = UsedParamsNeedInstantiationVisitor { + tcx, + typing_env, + adts_with_manual_partialeq: FxHashSet::default(), + adts_without_partialeq: FxHashSet::default(), + manual: vec![], + without: vec![], + }; + v.visit_ty(ty); + #[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering + for span in v.adts_with_manual_partialeq { + err.span_note(span, "the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details"); + } + #[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering + for span in v.adts_without_partialeq { + err.span_label( + span, + "must be annotated with `#[derive(PartialEq)]` to be usable in patterns", + ); + } + for ty in v.manual { + err.note(format!( + "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details" + )); + } + for ty in v.without { + err.note(format!( + "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns" + )); + } +} + +#[instrument(level = "trace", skip(tcx), ret)] +fn type_has_partial_eq_impl<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, +) -> ( + /* has impl */ bool, + /* is derived */ bool, + /* structural partial eq */ bool, + /* non-blanket impl */ Option<DefId>, +) { + let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); + // double-check there even *is* a semantic `PartialEq` to dispatch to. + // + // (If there isn't, then we can safely issue a hard + // error, because that's never worked, due to compiler + // using `PartialEq::eq` in this scenario in the past.) + let partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::PartialEq, None); + let structural_partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::StructuralPeq, None); + + let partial_eq_obligation = Obligation::new( + tcx, + ObligationCause::dummy(), + param_env, + ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]), + ); + + let mut automatically_derived = false; + let mut structural_peq = false; + let mut impl_def_id = None; + for def_id in tcx.non_blanket_impls_for_ty(partial_eq_trait_id, ty) { + automatically_derived = tcx.has_attr(def_id, sym::automatically_derived); + impl_def_id = Some(def_id); + } + for _ in tcx.non_blanket_impls_for_ty(structural_partial_eq_trait_id, ty) { + structural_peq = true; + } + // This *could* accept a type that isn't actually `PartialEq`, because region bounds get + // ignored. However that should be pretty much impossible since consts that do not depend on + // generics can only mention the `'static` lifetime, and how would one have a type that's + // `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem + // we'll need to leave some sort of trace of this requirement in the MIR so that borrowck + // can ensure that the type really implements `PartialEq`. + ( + infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation), + automatically_derived, + structural_peq, + impl_def_id, + ) +} diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 08c6b4abd3b..3ac53fa6272 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -528,11 +528,17 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { | Res::SelfCtor(..) => PatKind::Leaf { subpatterns }, _ => { let e = match res { - Res::Def(DefKind::ConstParam, _) => { - self.tcx.dcx().emit_err(ConstParamInPattern { span }) + Res::Def(DefKind::ConstParam, def_id) => { + self.tcx.dcx().emit_err(ConstParamInPattern { + span, + const_span: self.tcx().def_span(def_id), + }) } - Res::Def(DefKind::Static { .. }, _) => { - self.tcx.dcx().emit_err(StaticInPattern { span }) + Res::Def(DefKind::Static { .. }, def_id) => { + self.tcx.dcx().emit_err(StaticInPattern { + span, + static_span: self.tcx().def_span(def_id), + }) } _ => self.tcx.dcx().emit_err(NonConstPath { span }), }; diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index cf9f6981c5a..46efdd16ee8 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -1,3 +1,4 @@ +use std::cmp::Ordering; use std::fmt::{self, Debug}; use rustc_data_structures::captures::Captures; @@ -10,9 +11,12 @@ use tracing::{debug, debug_span, instrument}; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, TraverseCoverageGraphWithLoops}; +#[cfg(test)] +mod tests; + /// The coverage counter or counter expression associated with a particular /// BCB node or BCB edge. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] enum BcbCounter { Counter { id: CounterId }, Expression { id: ExpressionId }, @@ -43,8 +47,9 @@ struct BcbExpression { rhs: BcbCounter, } -#[derive(Debug)] -pub(super) enum CounterIncrementSite { +/// Enum representing either a node or an edge in the coverage graph. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(super) enum Site { Node { bcb: BasicCoverageBlock }, Edge { from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock }, } @@ -54,16 +59,10 @@ pub(super) enum CounterIncrementSite { pub(super) struct CoverageCounters { /// List of places where a counter-increment statement should be injected /// into MIR, each with its corresponding counter ID. - counter_increment_sites: IndexVec<CounterId, CounterIncrementSite>, + counter_increment_sites: IndexVec<CounterId, Site>, /// Coverage counters/expressions that are associated with individual BCBs. node_counters: IndexVec<BasicCoverageBlock, Option<BcbCounter>>, - /// Coverage counters/expressions that are associated with the control-flow - /// edge between two BCBs. - /// - /// We currently don't iterate over this map, but if we do in the future, - /// switch it back to `FxIndexMap` to avoid query stability hazards. - edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>, /// Table of expression data, associating each expression ID with its /// corresponding operator (+ or -) and its LHS/RHS operands. @@ -83,93 +82,30 @@ impl CoverageCounters { let mut builder = CountersBuilder::new(graph, bcb_needs_counter); builder.make_bcb_counters(); - builder.counters + builder.into_coverage_counters() } fn with_num_bcbs(num_bcbs: usize) -> Self { Self { counter_increment_sites: IndexVec::new(), node_counters: IndexVec::from_elem_n(None, num_bcbs), - edge_counters: FxHashMap::default(), expressions: IndexVec::new(), expressions_memo: FxHashMap::default(), } } - /// Shared helper used by [`Self::make_phys_node_counter`] and - /// [`Self::make_phys_edge_counter`]. Don't call this directly. - fn make_counter_inner(&mut self, site: CounterIncrementSite) -> BcbCounter { + /// Creates a new physical counter for a BCB node or edge. + fn make_phys_counter(&mut self, site: Site) -> BcbCounter { let id = self.counter_increment_sites.push(site); BcbCounter::Counter { id } } - /// Creates a new physical counter for a BCB node. - fn make_phys_node_counter(&mut self, bcb: BasicCoverageBlock) -> BcbCounter { - self.make_counter_inner(CounterIncrementSite::Node { bcb }) - } - - /// Creates a new physical counter for a BCB edge. - fn make_phys_edge_counter( - &mut self, - from_bcb: BasicCoverageBlock, - to_bcb: BasicCoverageBlock, - ) -> BcbCounter { - self.make_counter_inner(CounterIncrementSite::Edge { from_bcb, to_bcb }) - } - fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter { let new_expr = BcbExpression { lhs, op, rhs }; - *self - .expressions_memo - .entry(new_expr) - .or_insert_with(|| Self::make_expression_inner(&mut self.expressions, new_expr)) - } - - /// This is an associated function so that we can call it while borrowing - /// `&mut self.expressions_memo`. - fn make_expression_inner( - expressions: &mut IndexVec<ExpressionId, BcbExpression>, - new_expr: BcbExpression, - ) -> BcbCounter { - // Simplify expressions using basic algebra. - // - // Some of these cases might not actually occur in practice, depending - // on the details of how the instrumentor builds expressions. - let BcbExpression { lhs, op, rhs } = new_expr; - - if let BcbCounter::Expression { id } = lhs { - let lhs_expr = &expressions[id]; - - // Simplify `(a - b) + b` to `a`. - if lhs_expr.op == Op::Subtract && op == Op::Add && lhs_expr.rhs == rhs { - return lhs_expr.lhs; - } - // Simplify `(a + b) - b` to `a`. - if lhs_expr.op == Op::Add && op == Op::Subtract && lhs_expr.rhs == rhs { - return lhs_expr.lhs; - } - // Simplify `(a + b) - a` to `b`. - if lhs_expr.op == Op::Add && op == Op::Subtract && lhs_expr.lhs == rhs { - return lhs_expr.rhs; - } - } - - if let BcbCounter::Expression { id } = rhs { - let rhs_expr = &expressions[id]; - - // Simplify `a + (b - a)` to `b`. - if op == Op::Add && rhs_expr.op == Op::Subtract && lhs == rhs_expr.rhs { - return rhs_expr.lhs; - } - // Simplify `a - (a - b)` to `b`. - if op == Op::Subtract && rhs_expr.op == Op::Subtract && lhs == rhs_expr.lhs { - return rhs_expr.rhs; - } - } - - // Simplification failed, so actually create the new expression. - let id = expressions.push(new_expr); - BcbCounter::Expression { id } + *self.expressions_memo.entry(new_expr).or_insert_with(|| { + let id = self.expressions.push(new_expr); + BcbCounter::Expression { id } + }) } /// Creates a counter that is the sum of the given counters. @@ -182,6 +118,12 @@ impl CoverageCounters { .reduce(|accum, counter| self.make_expression(accum, Op::Add, counter)) } + /// Creates a counter whose value is `lhs - SUM(rhs)`. + fn make_subtracted_sum(&mut self, lhs: BcbCounter, rhs: &[BcbCounter]) -> BcbCounter { + let Some(rhs_sum) = self.make_sum(rhs) else { return lhs }; + self.make_expression(lhs, Op::Subtract, rhs_sum) + } + pub(super) fn num_counters(&self) -> usize { self.counter_increment_sites.len() } @@ -195,20 +137,6 @@ impl CoverageCounters { counter } - fn set_edge_counter( - &mut self, - from_bcb: BasicCoverageBlock, - to_bcb: BasicCoverageBlock, - counter: BcbCounter, - ) -> BcbCounter { - let existing = self.edge_counters.insert((from_bcb, to_bcb), counter); - assert!( - existing.is_none(), - "edge ({from_bcb:?} -> {to_bcb:?}) already has a counter: {existing:?} => {counter:?}" - ); - counter - } - pub(super) fn term_for_bcb(&self, bcb: BasicCoverageBlock) -> Option<CovTerm> { self.node_counters[bcb].map(|counter| counter.as_term()) } @@ -218,8 +146,8 @@ impl CoverageCounters { /// each site's corresponding counter ID. pub(super) fn counter_increment_sites( &self, - ) -> impl Iterator<Item = (CounterId, &CounterIncrementSite)> { - self.counter_increment_sites.iter_enumerated() + ) -> impl Iterator<Item = (CounterId, Site)> + Captures<'_> { + self.counter_increment_sites.iter_enumerated().map(|(id, &site)| (id, site)) } /// Returns an iterator over the subset of BCB nodes that have been associated @@ -254,22 +182,53 @@ impl CoverageCounters { } } +/// Symbolic representation of the coverage counter to be used for a particular +/// node or edge in the coverage graph. The same site counter can be used for +/// multiple sites, if they have been determined to have the same count. +#[derive(Clone, Copy, Debug)] +enum SiteCounter { + /// A physical counter at some node/edge. + Phys { site: Site }, + /// A counter expression for a node that takes the sum of all its in-edge + /// counters. + NodeSumExpr { bcb: BasicCoverageBlock }, + /// A counter expression for an edge that takes the counter of its source + /// node, and subtracts the counters of all its sibling out-edges. + EdgeDiffExpr { from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock }, +} + +/// Yields the graph successors of `from_bcb` that aren't `to_bcb`. This is +/// used when creating a counter expression for [`SiteCounter::EdgeDiffExpr`]. +/// +/// For example, in this diagram the sibling out-edge targets of edge `AC` are +/// the nodes `B` and `D`. +/// +/// ```text +/// A +/// / | \ +/// B C D +/// ``` +fn sibling_out_edge_targets( + graph: &CoverageGraph, + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, +) -> impl Iterator<Item = BasicCoverageBlock> + Captures<'_> { + graph.successors[from_bcb].iter().copied().filter(move |&t| t != to_bcb) +} + /// Helper struct that allows counter creation to inspect the BCB graph, and /// the set of nodes that need counters. struct CountersBuilder<'a> { - counters: CoverageCounters, graph: &'a CoverageGraph, bcb_needs_counter: &'a BitSet<BasicCoverageBlock>, + + site_counters: FxHashMap<Site, SiteCounter>, } impl<'a> CountersBuilder<'a> { fn new(graph: &'a CoverageGraph, bcb_needs_counter: &'a BitSet<BasicCoverageBlock>) -> Self { assert_eq!(graph.num_nodes(), bcb_needs_counter.domain_size()); - Self { - counters: CoverageCounters::with_num_bcbs(graph.num_nodes()), - graph, - bcb_needs_counter, - } + Self { graph, bcb_needs_counter, site_counters: FxHashMap::default() } } fn make_bcb_counters(&mut self) { @@ -302,9 +261,7 @@ impl<'a> CountersBuilder<'a> { fn make_node_counter_and_out_edge_counters(&mut self, from_bcb: BasicCoverageBlock) { // First, ensure that this node has a counter of some kind. // We might also use that counter to compute one of the out-edge counters. - let node_counter = self.get_or_make_node_counter(from_bcb); - - let successors = self.graph.successors[from_bcb].as_slice(); + self.get_or_make_node_counter(from_bcb); // If this node's out-edges won't sum to the node's counter, // then there's no reason to create edge counters here. @@ -315,11 +272,11 @@ impl<'a> CountersBuilder<'a> { // When choosing which out-edge should be given a counter expression, ignore edges that // already have counters, or could use the existing counter of their target node. let out_edge_has_counter = |to_bcb| { - if self.counters.edge_counters.contains_key(&(from_bcb, to_bcb)) { + if self.site_counters.contains_key(&Site::Edge { from_bcb, to_bcb }) { return true; } self.graph.sole_predecessor(to_bcb) == Some(from_bcb) - && self.counters.node_counters[to_bcb].is_some() + && self.site_counters.contains_key(&Site::Node { bcb: to_bcb }) }; // Determine the set of out-edges that could benefit from being given an expression. @@ -332,51 +289,41 @@ impl<'a> CountersBuilder<'a> { // If there are out-edges without counters, choose one to be given an expression // (computed from this node and the other out-edges) instead of a physical counter. - let Some(target_bcb) = self.choose_out_edge_for_expression(from_bcb, &candidate_successors) + let Some(to_bcb) = self.choose_out_edge_for_expression(from_bcb, &candidate_successors) else { return; }; // For each out-edge other than the one that was chosen to get an expression, - // ensure that it has a counter (existing counter/expression or a new counter), - // and accumulate the corresponding counters into a single sum expression. - let other_out_edge_counters = successors - .iter() - .copied() - // Skip the chosen edge, since we'll calculate its count from this sum. - .filter(|&edge_target_bcb| edge_target_bcb != target_bcb) - .map(|to_bcb| self.get_or_make_edge_counter(from_bcb, to_bcb)) - .collect::<Vec<_>>(); - let Some(sum_of_all_other_out_edges) = self.counters.make_sum(&other_out_edge_counters) - else { - return; - }; + // ensure that it has a counter (existing counter/expression or a new counter). + for target in sibling_out_edge_targets(self.graph, from_bcb, to_bcb) { + self.get_or_make_edge_counter(from_bcb, target); + } // Now create an expression for the chosen edge, by taking the counter // for its source node and subtracting the sum of its sibling out-edges. - let expression = - self.counters.make_expression(node_counter, Op::Subtract, sum_of_all_other_out_edges); - - debug!("{target_bcb:?} gets an expression: {expression:?}"); - self.counters.set_edge_counter(from_bcb, target_bcb, expression); + let counter = SiteCounter::EdgeDiffExpr { from_bcb, to_bcb }; + self.site_counters.insert(Site::Edge { from_bcb, to_bcb }, counter); } #[instrument(level = "debug", skip(self))] - fn get_or_make_node_counter(&mut self, bcb: BasicCoverageBlock) -> BcbCounter { + fn get_or_make_node_counter(&mut self, bcb: BasicCoverageBlock) -> SiteCounter { // If the BCB already has a counter, return it. - if let Some(counter) = self.counters.node_counters[bcb] { + if let Some(&counter) = self.site_counters.get(&Site::Node { bcb }) { debug!("{bcb:?} already has a counter: {counter:?}"); return counter; } let counter = self.make_node_counter_inner(bcb); - self.counters.set_node_counter(bcb, counter) + self.site_counters.insert(Site::Node { bcb }, counter); + counter } - fn make_node_counter_inner(&mut self, bcb: BasicCoverageBlock) -> BcbCounter { + fn make_node_counter_inner(&mut self, bcb: BasicCoverageBlock) -> SiteCounter { // If the node's sole in-edge already has a counter, use that. if let Some(sole_pred) = self.graph.sole_predecessor(bcb) - && let Some(&edge_counter) = self.counters.edge_counters.get(&(sole_pred, bcb)) + && let Some(&edge_counter) = + self.site_counters.get(&Site::Edge { from_bcb: sole_pred, to_bcb: bcb }) { return edge_counter; } @@ -390,20 +337,17 @@ impl<'a> CountersBuilder<'a> { // leading to infinite recursion. if predecessors.len() <= 1 || predecessors.contains(&bcb) { debug!(?bcb, ?predecessors, "node has <=1 predecessors or is its own predecessor"); - let counter = self.counters.make_phys_node_counter(bcb); + let counter = SiteCounter::Phys { site: Site::Node { bcb } }; debug!(?bcb, ?counter, "node gets a physical counter"); return counter; } // A BCB with multiple incoming edges can compute its count by ensuring that counters // exist for each of those edges, and then adding them up to get a total count. - let in_edge_counters = predecessors - .iter() - .copied() - .map(|from_bcb| self.get_or_make_edge_counter(from_bcb, bcb)) - .collect::<Vec<_>>(); - let sum_of_in_edges: BcbCounter = - self.counters.make_sum(&in_edge_counters).expect("there must be at least one in-edge"); + for &from_bcb in predecessors { + self.get_or_make_edge_counter(from_bcb, bcb); + } + let sum_of_in_edges = SiteCounter::NodeSumExpr { bcb }; debug!("{bcb:?} gets a new counter (sum of predecessor counters): {sum_of_in_edges:?}"); sum_of_in_edges @@ -414,22 +358,23 @@ impl<'a> CountersBuilder<'a> { &mut self, from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, - ) -> BcbCounter { + ) -> SiteCounter { // If the edge already has a counter, return it. - if let Some(&counter) = self.counters.edge_counters.get(&(from_bcb, to_bcb)) { + if let Some(&counter) = self.site_counters.get(&Site::Edge { from_bcb, to_bcb }) { debug!("Edge {from_bcb:?}->{to_bcb:?} already has a counter: {counter:?}"); return counter; } let counter = self.make_edge_counter_inner(from_bcb, to_bcb); - self.counters.set_edge_counter(from_bcb, to_bcb, counter) + self.site_counters.insert(Site::Edge { from_bcb, to_bcb }, counter); + counter } fn make_edge_counter_inner( &mut self, from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, - ) -> BcbCounter { + ) -> SiteCounter { // If the target node has exactly one in-edge (i.e. this one), then just // use the node's counter, since it will have the same value. if let Some(sole_pred) = self.graph.sole_predecessor(to_bcb) { @@ -447,7 +392,7 @@ impl<'a> CountersBuilder<'a> { } // Make a new counter to count this edge. - let counter = self.counters.make_phys_edge_counter(from_bcb, to_bcb); + let counter = SiteCounter::Phys { site: Site::Edge { from_bcb, to_bcb } }; debug!(?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter"); counter } @@ -510,4 +455,145 @@ impl<'a> CountersBuilder<'a> { None } + + fn into_coverage_counters(self) -> CoverageCounters { + Transcriber::new(&self).transcribe_counters() + } +} + +/// Helper struct for converting `CountersBuilder` into a final `CoverageCounters`. +struct Transcriber<'a> { + old: &'a CountersBuilder<'a>, + new: CoverageCounters, + phys_counter_for_site: FxHashMap<Site, BcbCounter>, +} + +impl<'a> Transcriber<'a> { + fn new(old: &'a CountersBuilder<'a>) -> Self { + Self { + old, + new: CoverageCounters::with_num_bcbs(old.graph.num_nodes()), + phys_counter_for_site: FxHashMap::default(), + } + } + + fn transcribe_counters(mut self) -> CoverageCounters { + for bcb in self.old.bcb_needs_counter.iter() { + let site = Site::Node { bcb }; + let site_counter = self.site_counter(site); + + // Resolve the site counter into flat lists of nodes/edges whose + // physical counts contribute to the counter for this node. + // Distinguish between counts that will be added vs subtracted. + let mut pos = vec![]; + let mut neg = vec![]; + self.push_resolved_sites(site_counter, &mut pos, &mut neg); + + // Simplify by cancelling out sites that appear on both sides. + let (mut pos, mut neg) = sort_and_cancel(pos, neg); + + if pos.is_empty() { + // If we somehow end up with no positive terms after cancellation, + // fall back to creating a physical counter. There's no known way + // for this to happen, but it's hard to confidently rule it out. + debug_assert!(false, "{site:?} has no positive counter terms"); + pos = vec![Some(site)]; + neg = vec![]; + } + + let mut new_counters_for_sites = |sites: Vec<Option<Site>>| { + sites + .into_iter() + .filter_map(|id| try { self.ensure_phys_counter(id?) }) + .collect::<Vec<_>>() + }; + let mut pos = new_counters_for_sites(pos); + let mut neg = new_counters_for_sites(neg); + + pos.sort(); + neg.sort(); + + let pos_counter = self.new.make_sum(&pos).expect("`pos` should not be empty"); + let new_counter = self.new.make_subtracted_sum(pos_counter, &neg); + self.new.set_node_counter(bcb, new_counter); + } + + self.new + } + + fn site_counter(&self, site: Site) -> SiteCounter { + self.old.site_counters.get(&site).copied().unwrap_or_else(|| { + // We should have already created all necessary site counters. + // But if we somehow didn't, avoid crashing in release builds, + // and just use an extra physical counter instead. + debug_assert!(false, "{site:?} should have a counter"); + SiteCounter::Phys { site } + }) + } + + fn ensure_phys_counter(&mut self, site: Site) -> BcbCounter { + *self.phys_counter_for_site.entry(site).or_insert_with(|| self.new.make_phys_counter(site)) + } + + /// Resolves the given counter into flat lists of nodes/edges, whose counters + /// will then be added and subtracted to form a counter expression. + fn push_resolved_sites(&self, counter: SiteCounter, pos: &mut Vec<Site>, neg: &mut Vec<Site>) { + match counter { + SiteCounter::Phys { site } => pos.push(site), + SiteCounter::NodeSumExpr { bcb } => { + for &from_bcb in &self.old.graph.predecessors[bcb] { + let edge_counter = self.site_counter(Site::Edge { from_bcb, to_bcb: bcb }); + self.push_resolved_sites(edge_counter, pos, neg); + } + } + SiteCounter::EdgeDiffExpr { from_bcb, to_bcb } => { + // First, add the count for `from_bcb`. + let node_counter = self.site_counter(Site::Node { bcb: from_bcb }); + self.push_resolved_sites(node_counter, pos, neg); + + // Then subtract the counts for the other out-edges. + for target in sibling_out_edge_targets(self.old.graph, from_bcb, to_bcb) { + let edge_counter = self.site_counter(Site::Edge { from_bcb, to_bcb: target }); + // Swap `neg` and `pos` so that the counter is subtracted. + self.push_resolved_sites(edge_counter, neg, pos); + } + } + } + } +} + +/// Given two lists: +/// - Sorts each list. +/// - Converts each list to `Vec<Option<T>>`. +/// - Scans for values that appear in both lists, and cancels them out by +/// replacing matching pairs of values with `None`. +fn sort_and_cancel<T: Ord>(mut pos: Vec<T>, mut neg: Vec<T>) -> (Vec<Option<T>>, Vec<Option<T>>) { + pos.sort(); + neg.sort(); + + // Convert to `Vec<Option<T>>`. If `T` has a niche, this should be zero-cost. + let mut pos = pos.into_iter().map(Some).collect::<Vec<_>>(); + let mut neg = neg.into_iter().map(Some).collect::<Vec<_>>(); + + // Scan through the lists using two cursors. When either cursor reaches the + // end of its list, there can be no more equal pairs, so stop. + let mut p = 0; + let mut n = 0; + while p < pos.len() && n < neg.len() { + // If the values are equal, remove them and advance both cursors. + // Otherwise, advance whichever cursor points to the lesser value. + // (Choosing which cursor to advance relies on both lists being sorted.) + match pos[p].cmp(&neg[n]) { + Ordering::Less => p += 1, + Ordering::Equal => { + pos[p] = None; + neg[n] = None; + p += 1; + n += 1; + } + Ordering::Greater => n += 1, + } + } + + (pos, neg) } diff --git a/compiler/rustc_mir_transform/src/coverage/counters/tests.rs b/compiler/rustc_mir_transform/src/coverage/counters/tests.rs new file mode 100644 index 00000000000..794d4358f82 --- /dev/null +++ b/compiler/rustc_mir_transform/src/coverage/counters/tests.rs @@ -0,0 +1,41 @@ +use std::fmt::Debug; + +use super::sort_and_cancel; + +fn flatten<T>(input: Vec<Option<T>>) -> Vec<T> { + input.into_iter().flatten().collect() +} + +fn sort_and_cancel_and_flatten<T: Clone + Ord>(pos: Vec<T>, neg: Vec<T>) -> (Vec<T>, Vec<T>) { + let (pos_actual, neg_actual) = sort_and_cancel(pos, neg); + (flatten(pos_actual), flatten(neg_actual)) +} + +#[track_caller] +fn check_test_case<T: Clone + Debug + Ord>( + pos: Vec<T>, + neg: Vec<T>, + pos_expected: Vec<T>, + neg_expected: Vec<T>, +) { + eprintln!("pos = {pos:?}; neg = {neg:?}"); + let output = sort_and_cancel_and_flatten(pos, neg); + assert_eq!(output, (pos_expected, neg_expected)); +} + +#[test] +fn cancellation() { + let cases: &[(Vec<u32>, Vec<u32>, Vec<u32>, Vec<u32>)] = &[ + (vec![], vec![], vec![], vec![]), + (vec![4, 2, 1, 5, 3], vec![], vec![1, 2, 3, 4, 5], vec![]), + (vec![5, 5, 5, 5, 5], vec![5], vec![5, 5, 5, 5], vec![]), + (vec![1, 1, 2, 2, 3, 3], vec![1, 2, 3], vec![1, 2, 3], vec![]), + (vec![1, 1, 2, 2, 3, 3], vec![2, 4, 2], vec![1, 1, 3, 3], vec![4]), + ]; + + for (pos, neg, pos_expected, neg_expected) in cases { + check_test_case(pos.to_vec(), neg.to_vec(), pos_expected.to_vec(), neg_expected.to_vec()); + // Same test case, but with its inputs flipped and its outputs flipped. + check_test_case(neg.to_vec(), pos.to_vec(), neg_expected.to_vec(), pos_expected.to_vec()); + } +} diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index e8b3d80be02..241c3c79114 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -25,7 +25,7 @@ use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, Pos, SourceFile, Span}; use tracing::{debug, debug_span, trace}; -use crate::coverage::counters::{CounterIncrementSite, CoverageCounters}; +use crate::coverage::counters::{CoverageCounters, Site}; use crate::coverage::graph::CoverageGraph; use crate::coverage::mappings::ExtractedMappings; @@ -265,13 +265,13 @@ fn inject_coverage_statements<'tcx>( coverage_counters: &CoverageCounters, ) { // Inject counter-increment statements into MIR. - for (id, counter_increment_site) in coverage_counters.counter_increment_sites() { + for (id, site) in coverage_counters.counter_increment_sites() { // Determine the block to inject a counter-increment statement into. // For BCB nodes this is just their first block, but for edges we need // to create a new block between the two BCBs, and inject into that. - let target_bb = match *counter_increment_site { - CounterIncrementSite::Node { bcb } => basic_coverage_blocks[bcb].leader_bb(), - CounterIncrementSite::Edge { from_bcb, to_bcb } => { + let target_bb = match site { + Site::Node { bcb } => basic_coverage_blocks[bcb].leader_bb(), + Site::Edge { from_bcb, to_bcb } => { // Create a new block between the last block of `from_bcb` and // the first block of `to_bcb`. let from_bb = basic_coverage_blocks[from_bcb].last_bb(); diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 809357ec110..b8383e734e2 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -747,8 +747,8 @@ fn build_call_shim<'tcx>( sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); } - // FIXME(eddyb) avoid having this snippet both here and in - // `Instance::fn_sig` (introduce `InstanceKind::fn_sig`?). + // FIXME: Avoid having to adjust the signature both here and in + // `fn_sig_for_fn_abi`. if let ty::InstanceKind::VTableShim(..) = instance { // Modify fn(self, ...) to fn(self: *mut Self, ...) let mut inputs_and_output = sig.inputs_and_output.to_vec(); diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index b9a325eddd8..e5cd4622dae 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -343,6 +343,9 @@ parse_incorrect_semicolon = .suggestion = remove this semicolon .help = {$name} declarations are not followed by a semicolon +parse_incorrect_type_on_self = type not allowed for shorthand `self` parameter + .suggestion = move the modifiers on `self` to the type + parse_incorrect_use_of_await = incorrect use of `await` .parentheses_suggestion = `await` is not a method call, remove the parentheses diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 2579e4c1f25..14f2dd32e92 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3409,3 +3409,22 @@ pub(crate) struct PolarityAndModifiers { pub polarity: &'static str, pub modifiers_concatenated: String, } + +#[derive(Diagnostic)] +#[diag(parse_incorrect_type_on_self)] +pub(crate) struct IncorrectTypeOnSelf { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub move_self_modifier: MoveSelfModifier, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +pub(crate) struct MoveSelfModifier { + #[suggestion_part(code = "")] + pub removal_span: Span, + #[suggestion_part(code = "{modifier}")] + pub insertion_span: Span, + pub modifier: String, +} diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 26e81b7676b..475cd09147f 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2941,6 +2941,32 @@ impl<'a> Parser<'a> { }; Ok((eself, eself_ident, eself_hi)) }; + let expect_self_ident_not_typed = + |this: &mut Self, modifier: &SelfKind, modifier_span: Span| { + let eself_ident = expect_self_ident(this); + + // Recover `: Type` after a qualified self + if this.may_recover() && this.eat_noexpect(&token::Colon) { + let snap = this.create_snapshot_for_diagnostic(); + match this.parse_ty() { + Ok(ty) => { + this.dcx().emit_err(errors::IncorrectTypeOnSelf { + span: ty.span, + move_self_modifier: errors::MoveSelfModifier { + removal_span: modifier_span, + insertion_span: ty.span.shrink_to_lo(), + modifier: modifier.to_ref_suggestion(), + }, + }); + } + Err(diag) => { + diag.cancel(); + this.restore_snapshot(snap); + } + } + } + eself_ident + }; // Recover for the grammar `*self`, `*const self`, and `*mut self`. let recover_self_ptr = |this: &mut Self| { this.dcx().emit_err(errors::SelfArgumentPointer { span: this.token.span }); @@ -2978,7 +3004,9 @@ impl<'a> Parser<'a> { // `¬_self` return Ok(None); }; - (eself, expect_self_ident(self), self.prev_token.span) + let hi = self.token.span; + let self_ident = expect_self_ident_not_typed(self, &eself, eself_lo.until(hi)); + (eself, self_ident, hi) } // `*self` token::BinOp(token::Star) if is_isolated_self(self, 1) => { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 505586e74f1..8cff23c2e32 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -9,7 +9,7 @@ use rustc_ast::{ }; use rustc_errors::{Applicability, PResult}; use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Span}; use thin_vec::{ThinVec, thin_vec}; use super::{Parser, PathStyle, SeqSep, TokenType, Trailing}; @@ -940,7 +940,7 @@ impl<'a> Parser<'a> { let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() && self.eat_keyword(kw::Async) { - self.psess.gated_spans.gate(sym::async_closure, self.prev_token.span); + self.psess.gated_spans.gate(sym::async_trait_bounds, self.prev_token.span); BoundAsyncness::Async(self.prev_token.span) } else if self.may_recover() && self.token.uninterpolated_span().is_rust_2015() @@ -951,7 +951,7 @@ impl<'a> Parser<'a> { span: self.prev_token.span, help: HelpUseLatestEdition::new(), }); - self.psess.gated_spans.gate(sym::async_closure, self.prev_token.span); + self.psess.gated_spans.gate(sym::async_trait_bounds, self.prev_token.span); BoundAsyncness::Async(self.prev_token.span) } else { BoundAsyncness::Normal @@ -1136,7 +1136,7 @@ impl<'a> Parser<'a> { Some(ast::Path { span: fn_token_span.to(self.prev_token.span), segments: thin_vec![ast::PathSegment { - ident: Ident::new(Symbol::intern("Fn"), fn_token_span), + ident: Ident::new(sym::Fn, fn_token_span), id: DUMMY_NODE_ID, args: Some(P(ast::GenericArgs::Parenthesized(ast::ParenthesizedArgs { span: args_lo.to(self.prev_token.span), diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 9b26a5bbcc6..4b47ce8389c 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -302,7 +302,11 @@ impl<D: Deps> DepGraph<D> { OP: FnOnce() -> R, { match self.data() { - Some(data) => data.with_anon_task(cx, dep_kind, op), + Some(data) => { + let (result, index) = data.with_anon_task_inner(cx, dep_kind, op); + self.read_index(index); + (result, index) + } None => (op(), self.next_virtual_depnode_index()), } } @@ -397,7 +401,16 @@ impl<D: Deps> DepGraphData<D> { /// Executes something within an "anonymous" task, that is, a task the /// `DepNode` of which is determined by the list of inputs it read from. - pub(crate) fn with_anon_task<Tcx: DepContext<Deps = D>, OP, R>( + /// + /// NOTE: this does not actually count as a read of the DepNode here. + /// Using the result of this task without reading the DepNode will result + /// in untracked dependencies which may lead to ICEs as nodes are + /// incorrectly marked green. + /// + /// FIXME: This could perhaps return a `WithDepNode` to ensure that the + /// user of this function actually performs the read; we'll have to see + /// how to make that work with `anon` in `execute_job_incr`, though. + pub(crate) fn with_anon_task_inner<Tcx: DepContext<Deps = D>, OP, R>( &self, cx: Tcx, dep_kind: DepKind, diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index aac8ab87c64..6fb5e37d2d0 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -520,9 +520,11 @@ where let (result, dep_node_index) = qcx.start_query(job_id, query.depth_limit(), Some(&diagnostics), || { if query.anon() { - return dep_graph_data.with_anon_task(*qcx.dep_context(), query.dep_kind(), || { - query.compute(qcx, key) - }); + return dep_graph_data.with_anon_task_inner( + *qcx.dep_context(), + query.dep_kind(), + || query.compute(qcx, key), + ); } // `to_dep_node` is expensive for some `DepKind`s. diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 663c3ac0045..09f3e848766 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3113,6 +3113,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } + #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] let existing_name = match &in_scope_lifetimes[..] { [] => Symbol::intern("'a"), [(existing, _)] => existing.name, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 094871ad268..ca4adce37ce 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2257,7 +2257,7 @@ fn module_to_string(module: Module<'_>) -> Option<String> { collect_mod(names, parent); } } else { - names.push(Symbol::intern("<opaque>")); + names.push(sym::opaque_module_name_placeholder); collect_mod(names, module.parent.unwrap()); } } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 9e95e278325..2c0302bbb2b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2038,8 +2038,6 @@ written to standard error output)"), "make the current crate share its generic instantiations"), shell_argfiles: bool = (false, parse_bool, [UNTRACKED], "allow argument files to be specified with POSIX \"shell-style\" argument quoting"), - show_span: Option<String> = (None, parse_opt_string, [TRACKED], - "show spans in the crate root file, for compiler debugging (expr|pat|ty)"), simulate_remapped_rust_src_base: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], "simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \ to rust's source base directory. only meant for testing purposes"), diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index 357d746c184..2b2ba50d3fb 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -87,7 +87,7 @@ pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute]) -> Symbol { } } - Symbol::intern("rust_out") + sym::rust_out } pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 5fa8ce835ed..47ceab750cf 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -298,6 +298,7 @@ symbols! { Return, Right, Rust, + RustaceansAreAwesome, RustcDecodable, RustcEncodable, RwLock, @@ -316,6 +317,7 @@ symbols! { StructuralPartialEq, SubdiagMessage, Subdiagnostic, + SymbolIntern, Sync, SyncUnsafeCell, T, @@ -377,6 +379,7 @@ symbols! { adt_const_params, advanced_slice_patterns, adx_target_feature, + aes, aggregate_raw_ptr, alias, align, @@ -440,6 +443,7 @@ symbols! { associated_types, assume, assume_init, + asterisk: "*", async_await, async_call, async_call_mut, @@ -468,6 +472,7 @@ symbols! { async_for_loop, async_iterator, async_iterator_poll_next, + async_trait_bounds, atomic, atomic_mod, atomics, @@ -520,6 +525,7 @@ symbols! { btreeset_iter, builtin_syntax, c, + c_dash_variadic, c_str, c_str_literals, c_unwind, @@ -650,6 +656,7 @@ symbols! { const_trait_bound_opt_out, const_trait_impl, const_try, + const_ty_placeholder: "<const_ty>", constant, constructor, convert_identity, @@ -779,6 +786,7 @@ symbols! { drop_types_in_const, dropck_eyepatch, dropck_parametricity, + dummy_cgu_name, dylib, dyn_compatible_for_dispatch, dyn_metadata, @@ -922,6 +930,7 @@ symbols! { fmuladdf32, fmuladdf64, fn_align, + fn_body, fn_delegation, fn_must_use, fn_mut, @@ -962,6 +971,7 @@ symbols! { fs_create_dir, fsub_algebraic, fsub_fast, + fsxr, full, fundamental, fused_iterator, @@ -1385,6 +1395,7 @@ symbols! { on, on_unimplemented, opaque, + opaque_module_name_placeholder: "<opaque>", open_options_new, ops, opt_out_copy, @@ -1654,6 +1665,7 @@ symbols! { rust_eh_catch_typeinfo, rust_eh_personality, rust_logo, + rust_out, rustc, rustc_abi, rustc_allocator, @@ -1776,6 +1788,8 @@ symbols! { self_in_typedefs, self_struct_ctor, semitransparent, + sha2, + sha3, sha512_sm_x86, shadow_call_stack, shallow, @@ -1890,6 +1904,7 @@ symbols! { sreg, sreg_low16, sse, + sse2, sse4a_target_feature, stable, staged_api, @@ -2177,6 +2192,7 @@ symbols! { wrapping_sub, wreg, write_bytes, + write_fmt, write_macro, write_str, write_via_move, @@ -2406,6 +2422,7 @@ impl Symbol { } /// Maps a string to its interned representation. + #[rustc_diagnostic_item = "SymbolIntern"] pub fn intern(string: &str) -> Self { with_session_globals(|session_globals| session_globals.symbol_interner.intern(string)) } diff --git a/compiler/rustc_target/src/spec/base/aix.rs b/compiler/rustc_target/src/spec/base/aix.rs index 1869369b9e3..fe37d313294 100644 --- a/compiler/rustc_target/src/spec/base/aix.rs +++ b/compiler/rustc_target/src/spec/base/aix.rs @@ -4,7 +4,7 @@ use crate::spec::{Cc, CodeModel, LinkOutputKind, LinkerFlavor, TargetOptions, cr pub(crate) fn opts() -> TargetOptions { TargetOptions { abi: "vec-extabi".into(), - code_model: Some(CodeModel::Small), + code_model: Some(CodeModel::Large), cpu: "pwr7".into(), os: "aix".into(), vendor: "ibm".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv4t_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/targets/armv4t_unknown_linux_gnueabi.rs index 3a2fb753ba1..7ca2babffe9 100644 --- a/compiler/rustc_target/src/spec/targets/armv4t_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv4t_unknown_linux_gnueabi.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { description: Some("Armv4T Linux".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs index e0bc1936e22..154103c36f7 100644 --- a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { description: Some("Armv5TE Linux with uClibc".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs index 1515d8fe00a..47c6d805483 100644 --- a/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs @@ -12,7 +12,7 @@ pub(crate) fn target() -> Target { description: Some("Motorola 680x0 Linux".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "E-m:e-p:32:16:32-i8:8:8-i16:16:16-i32:16:32-n8:16:32-a:0:16-S16".into(), diff --git a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs index b7415bf683d..cc288e042d9 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { description: Some("MIPS64 for OpenWrt Linux musl 1.2.3".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/mipsisa32r6_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/mipsisa32r6_unknown_linux_gnu.rs index 2ff06dc6669..3ef0988d235 100644 --- a/compiler/rustc_target/src/spec/targets/mipsisa32r6_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/mipsisa32r6_unknown_linux_gnu.rs @@ -8,7 +8,7 @@ pub(crate) fn target() -> Target { description: Some("32-bit MIPS Release 6 Big Endian".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/mipsisa32r6el_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/mipsisa32r6el_unknown_linux_gnu.rs index 05b24669848..c75023385c2 100644 --- a/compiler/rustc_target/src/spec/targets/mipsisa32r6el_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/mipsisa32r6el_unknown_linux_gnu.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { description: Some("32-bit MIPS Release 6 Little Endian".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs index c5948b745ff..d0d0ed1acc3 100644 --- a/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs @@ -8,7 +8,7 @@ pub(crate) fn target() -> Target { description: Some("64-bit MIPS Release 6 Big Endian".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs index 5da891cc13f..62c30aebc51 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs @@ -14,7 +14,7 @@ pub(crate) fn target() -> Target { description: Some("64-bit PowerPC Linux with musl 1.2.3".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: "E-m:e-Fn32-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs index e194f5f8a36..0c1218bd059 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs @@ -12,8 +12,8 @@ pub(crate) fn target() -> Target { metadata: crate::spec::TargetMetadata { description: Some("PPC64LE FreeBSD".into()), tier: Some(3), - host_tools: Some(false), - std: Some(false), + host_tools: Some(true), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-Fn32-i64:64-n32:64".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs index e2921aa17fe..50946ae4ce6 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { description: Some("64-bit PowerPC Linux with musl 1.2.3, Little Endian".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-Fn32-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_freebsd.rs index 393946980f8..9cfc2153ac1 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_freebsd.rs @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { description: Some("PowerPC FreeBSD".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 32, data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs index 9859e8ced4f..5372a83e29a 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { description: Some("PowerPC Linux with musl 1.2.3".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_openbsd.rs index a8254af9736..7aedc2d0665 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_openbsd.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { description: None, tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs index 8b0a140c9ad..e9c57b99b92 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { description: Some("RISC-V Linux (kernel 5.4, glibc 2.33)".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs index 212de791e49..08871d9d72b 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs @@ -11,7 +11,7 @@ pub(crate) fn target() -> Target { ), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs b/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs index 8b5d4751a57..f694a1cb60d 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { description: Some("RISC-V 64-bit Android".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs index d1b0d801fd6..905bed76db4 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { description: Some("RISC-V FreeBSD".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs index 6de51f9b07e..7a887b604c5 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { description: Some("RISC-V Fuchsia".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs index 2e6fce91d4c..397c202ec1a 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { description: Some("RISC-V Linux (kernel 4.20, musl 1.2.3)".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs index 4bde0fb729c..7a78004927b 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { description: Some("S390x Linux (kernel 3.2, musl 1.2.3)".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs index 5c43ca69bd8..2f539f4f600 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { description: Some("Thumb2-mode ARMv7-A Linux with NEON, musl 1.2.3".into()), tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs index 91eb7b53fea..900dbed205c 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs @@ -14,7 +14,7 @@ pub(crate) fn target() -> Target { description: None, tier: None, host_tools: None, - std: None, + std: Some(true), }, pointer_width: 64, data_layout: diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 9b969dd3e43..90b18253629 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -794,7 +794,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { closure_def_id, found_kind, expected_kind, - "async ", + "Async", ); self.note_obligation_cause(&mut err, &obligation); self.point_at_returns_when_relevant(&mut err, &obligation); 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 2bb503f17b4..94682f501a8 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3838,6 +3838,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && self.predicate_must_hold_modulo_regions(&Obligation::misc( tcx, expr.span, body_id, param_env, pred, )) + && expr.span.hi() != rcvr.span.hi() { err.span_suggestion_verbose( expr.span.with_lo(rcvr.span.hi()), @@ -4115,6 +4116,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // the expected is a projection that we need to resolve. // && let Some(tail_ty) = typeck_results.expr_ty_opt(expr) && expected_found.found.is_unit() + // FIXME: this happens with macro calls. Need to figure out why the stmt + // `println!();` doesn't include the `;` in its `Span`. (#133845) + // We filter these out to avoid ICEs with debug assertions on caused by + // empty suggestions. + && expr.span.hi() != stmt.span.hi() { err.span_suggestion_verbose( expr.span.shrink_to_hi().with_hi(stmt.span.hi()), diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index e0a9ddf1876..c9297027519 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -18,7 +18,6 @@ use rustc_middle::ty::{ TypeVisitableExt, TypeVisitor, TypingMode, Upcast, }; use rustc_span::Span; -use rustc_span::symbol::Symbol; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -329,10 +328,7 @@ pub fn dyn_compatibility_violations_for_assoc_item( .collect(), // Associated types can only be dyn-compatible if they have `Self: Sized` bounds. ty::AssocKind::Type => { - if !tcx.features().generic_associated_types_extended() - && !tcx.generics_of(item.def_id).is_own_empty() - && !item.is_impl_trait_in_trait() - { + if !tcx.generics_of(item.def_id).is_own_empty() && !item.is_impl_trait_in_trait() { vec![DynCompatibilityViolation::GAT(item.name, item.ident(tcx).span)] } else { // We will permit associated types if they are explicitly mentioned in the trait object. @@ -679,7 +675,7 @@ fn receiver_is_dispatchable<'tcx>( // FIXME(mikeyhew) this is a total hack. Once dyn_compatible_for_dispatch is stabilized, we can // replace this with `dyn Trait` let unsized_self_ty: Ty<'tcx> = - Ty::new_param(tcx, u32::MAX, Symbol::intern("RustaceansAreAwesome")); + Ty::new_param(tcx, u32::MAX, rustc_span::sym::RustaceansAreAwesome); // `Receiver[Self => U]` let unsized_receiver_ty = diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 01f6cccb375..49c34550f8e 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -14,7 +14,7 @@ use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::{BuiltinImplSource, ImplSource, ImplSourceUserDefinedData}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt}; +use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Term, Ty, TyCtxt, TypingMode, Upcast}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::sym; @@ -179,35 +179,11 @@ pub(super) fn poly_project_and_unify_term<'cx, 'tcx>( ) -> ProjectAndUnifyResult<'tcx> { let infcx = selcx.infcx; let r = infcx.commit_if_ok(|_snapshot| { - let old_universe = infcx.universe(); let placeholder_predicate = infcx.enter_forall_and_leak_universe(obligation.predicate); - let new_universe = infcx.universe(); let placeholder_obligation = obligation.with(infcx.tcx, placeholder_predicate); match project_and_unify_term(selcx, &placeholder_obligation) { ProjectAndUnifyResult::MismatchedProjectionTypes(e) => Err(e), - ProjectAndUnifyResult::Holds(obligations) - if old_universe != new_universe - && selcx.tcx().features().generic_associated_types_extended() => - { - // If the `generic_associated_types_extended` feature is active, then we ignore any - // obligations references lifetimes from any universe greater than or equal to the - // universe just created. Otherwise, we can end up with something like `for<'a> I: 'a`, - // which isn't quite what we want. Ideally, we want either an implied - // `for<'a where I: 'a> I: 'a` or we want to "lazily" check these hold when we - // instantiate concrete regions. There is design work to be done here; until then, - // however, this allows experimenting potential GAT features without running into - // well-formedness issues. - let new_obligations = obligations - .into_iter() - .filter(|obligation| { - let mut visitor = MaxUniverse::new(); - obligation.predicate.visit_with(&mut visitor); - visitor.max_universe() < new_universe - }) - .collect(); - Ok(ProjectAndUnifyResult::Holds(new_obligations)) - } other => Ok(other), } }); diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 712856e6a8f..2fbe2e1e323 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -16,9 +16,7 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk}; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; -use rustc_middle::ty::{ - self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, Ty, TyCtxt, Upcast, -}; +use rustc_middle::ty::{self, GenericArgsRef, ToPolyTraitRef, Ty, TyCtxt, Upcast}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; use tracing::{debug, instrument}; @@ -626,7 +624,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { for assoc_type in assoc_types { let defs: &ty::Generics = tcx.generics_of(assoc_type); - if !defs.own_params.is_empty() && !tcx.features().generic_associated_types_extended() { + if !defs.own_params.is_empty() { tcx.dcx().span_delayed_bug( obligation.cause.span, "GATs in trait object shouldn't have been considered", @@ -638,60 +636,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // higher-ranked things. // Prevent, e.g., `dyn Iterator<Item = str>`. for bound in self.tcx().item_bounds(assoc_type).transpose_iter() { - let arg_bound = if defs.is_empty() { - bound.instantiate(tcx, trait_predicate.trait_ref.args) - } else { - let mut args = smallvec::SmallVec::with_capacity(defs.count()); - args.extend(trait_predicate.trait_ref.args.iter()); - let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> = - smallvec::SmallVec::with_capacity( - bound.skip_binder().kind().bound_vars().len() + defs.count(), - ); - bound_vars.extend(bound.skip_binder().kind().bound_vars().into_iter()); - GenericArgs::fill_single(&mut args, defs, &mut |param, _| match param.kind { - GenericParamDefKind::Type { .. } => { - let kind = ty::BoundTyKind::Param(param.def_id, param.name); - let bound_var = ty::BoundVariableKind::Ty(kind); - bound_vars.push(bound_var); - Ty::new_bound(tcx, ty::INNERMOST, ty::BoundTy { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind, - }) - .into() - } - GenericParamDefKind::Lifetime => { - let kind = ty::BoundRegionKind::Named(param.def_id, param.name); - let bound_var = ty::BoundVariableKind::Region(kind); - bound_vars.push(bound_var); - ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind, - }) - .into() - } - GenericParamDefKind::Const { .. } => { - let bound_var = ty::BoundVariableKind::Const; - bound_vars.push(bound_var); - ty::Const::new_bound( - tcx, - ty::INNERMOST, - ty::BoundVar::from_usize(bound_vars.len() - 1), - ) - .into() - } - }); - let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars); - let assoc_ty_args = tcx.mk_args(&args); - let bound = - bound.map_bound(|b| b.kind().skip_binder()).instantiate(tcx, assoc_ty_args); - ty::Binder::bind_with_vars(bound, bound_vars).upcast(tcx) - }; let normalized_bound = normalize_with_depth_to( self, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, - arg_bound, + bound.instantiate(tcx, trait_predicate.trait_ref.args), &mut nested, ); nested.push(obligation.with(tcx, normalized_bound)); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 41a35c31fe4..d362866cbc3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1400,10 +1400,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { where OP: FnOnce(&mut Self) -> R, { - let (result, dep_node) = - self.tcx().dep_graph.with_anon_task(self.tcx(), dep_kinds::TraitSelect, || op(self)); - self.tcx().dep_graph.read_index(dep_node); - (result, dep_node) + self.tcx().dep_graph.with_anon_task(self.tcx(), dep_kinds::TraitSelect, || op(self)) } /// filter_impls filters candidates that have a positive impl for a negative diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index f19a567cd84..83463babc4f 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -319,38 +319,35 @@ pub(crate) mod rustc { ) -> Result<Self, Err> { assert!(def.is_enum()); - // Computes the variant of a given index. - let layout_of_variant = |index, encoding: Option<TagEncoding<VariantIdx>>| { - let tag = cx.tcx().tag_for_variant((cx.tcx().erase_regions(ty), index)); - let variant_def = Def::Variant(def.variant(index)); - let variant_layout = ty_variant(cx, (ty, layout), index); - Self::from_variant( - variant_def, - tag.map(|tag| (tag, index, encoding.unwrap())), - (ty, variant_layout), - layout.size, - cx, - ) - }; + // Computes the layout of a variant. + let layout_of_variant = + |index, encoding: Option<TagEncoding<VariantIdx>>| -> Result<Self, Err> { + let variant_layout = ty_variant(cx, (ty, layout), index); + if variant_layout.is_uninhabited() { + return Ok(Self::uninhabited()); + } + let tag = cx.tcx().tag_for_variant((cx.tcx().erase_regions(ty), index)); + let variant_def = Def::Variant(def.variant(index)); + Self::from_variant( + variant_def, + tag.map(|tag| (tag, index, encoding.unwrap())), + (ty, variant_layout), + layout.size, + cx, + ) + }; - // We consider three kinds of enums, each demanding a different - // treatment of their layout computation: - // 1. enums that are uninhabited ZSTs - // 2. enums that delegate their layout to a variant - // 3. enums with multiple variants match layout.variants() { - Variants::Single { .. } if layout.is_uninhabited() && layout.size == Size::ZERO => { - // The layout representation of uninhabited, ZST enums is - // defined to be like that of the `!` type, as opposed of a - // typical enum. Consequently, they cannot be descended into - // as if they typical enums. We therefore special-case this - // scenario and simply return an uninhabited `Tree`. - Ok(Self::uninhabited()) - } Variants::Single { index } => { - // `Variants::Single` on enums with variants denotes that - // the enum delegates its layout to the variant at `index`. - layout_of_variant(*index, None) + // Hilariously, `Single` is used even for 0-variant enums; + // `index` is just junk in that case. + if ty.ty_adt_def().unwrap().variants().is_empty() { + Ok(Self::uninhabited()) + } else { + // `Variants::Single` on enums with variants denotes that + // the enum delegates its layout to the variant at `index`. + layout_of_variant(*index, None) + } } Variants::Multiple { tag, tag_encoding, tag_field, .. } => { // `Variants::Multiple` denotes an enum with multiple @@ -369,7 +366,7 @@ pub(crate) mod rustc { }, )?; - return Ok(Self::def(Def::Adt(def)).then(variants)); + Ok(Self::def(Def::Adt(def)).then(variants)) } } } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index a5a9125c8b5..b746d6299ef 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -31,20 +31,20 @@ fn fn_sig_for_fn_abi<'tcx>( tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>, typing_env: ty::TypingEnv<'tcx>, -) -> ty::PolyFnSig<'tcx> { +) -> ty::FnSig<'tcx> { if let InstanceKind::ThreadLocalShim(..) = instance.def { - return ty::Binder::dummy(tcx.mk_fn_sig( + return tcx.mk_fn_sig( [], tcx.thread_local_ptr_ty(instance.def_id()), false, hir::Safety::Safe, rustc_abi::ExternAbi::Unadjusted, - )); + ); } let ty = instance.ty(tcx, typing_env); match *ty.kind() { - ty::FnDef(..) => { + ty::FnDef(def_id, args) => { // HACK(davidtwco,eddyb): This is a workaround for polymorphization considering // parameters unused if they show up in the signature, but not in the `mir::Body` // (i.e. due to being inside a projection that got normalized, see @@ -52,9 +52,8 @@ fn fn_sig_for_fn_abi<'tcx>( // track of a polymorphization `ParamEnv` to allow normalizing later. // // We normalize the `fn_sig` again after instantiating at a later point. - let mut sig = match *ty.kind() { - ty::FnDef(def_id, args) => tcx - .fn_sig(def_id) + let mut sig = tcx.instantiate_bound_regions_with_erased( + tcx.fn_sig(def_id) .map_bound(|fn_sig| { tcx.normalize_erasing_regions( ty::TypingEnv::non_body_analysis(tcx, def_id), @@ -62,62 +61,36 @@ fn fn_sig_for_fn_abi<'tcx>( ) }) .instantiate(tcx, args), - _ => unreachable!(), - }; + ); if let ty::InstanceKind::VTableShim(..) = instance.def { - // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. - sig = sig.map_bound(|mut sig| { - let mut inputs_and_output = sig.inputs_and_output.to_vec(); - inputs_and_output[0] = Ty::new_mut_ptr(tcx, inputs_and_output[0]); - sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); - sig - }); + let mut inputs_and_output = sig.inputs_and_output.to_vec(); + inputs_and_output[0] = Ty::new_mut_ptr(tcx, inputs_and_output[0]); + sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); } + sig } ty::Closure(def_id, args) => { - let sig = args.as_closure().sig(); - - let bound_vars = - tcx.mk_bound_variable_kinds_from_iter(sig.bound_vars().iter().chain(iter::once( - ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), - ))); - let br = ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind: ty::BoundRegionKind::ClosureEnv, - }; - let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); + let sig = tcx.instantiate_bound_regions_with_erased(args.as_closure().sig()); let env_ty = tcx.closure_env_ty( Ty::new_closure(tcx, def_id, args), args.as_closure().kind(), - env_region, + tcx.lifetimes.re_erased, ); - let sig = sig.skip_binder(); - ty::Binder::bind_with_vars( - tcx.mk_fn_sig( - iter::once(env_ty).chain(sig.inputs().iter().cloned()), - sig.output(), - sig.c_variadic, - sig.safety, - sig.abi, - ), - bound_vars, + tcx.mk_fn_sig( + iter::once(env_ty).chain(sig.inputs().iter().cloned()), + sig.output(), + sig.c_variadic, + sig.safety, + sig.abi, ) } ty::CoroutineClosure(def_id, args) => { let coroutine_ty = Ty::new_coroutine_closure(tcx, def_id, args); let sig = args.as_coroutine_closure().coroutine_closure_sig(); - let bound_vars = - tcx.mk_bound_variable_kinds_from_iter(sig.bound_vars().iter().chain(iter::once( - ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), - ))); - let br = ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind: ty::BoundRegionKind::ClosureEnv, - }; - let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); + // When this `CoroutineClosure` comes from a `ConstructCoroutineInClosureShim`, // make sure we respect the `target_kind` in that shim. // FIXME(async_closures): This shouldn't be needed, and we should be populating @@ -138,42 +111,32 @@ fn fn_sig_for_fn_abi<'tcx>( coroutine_ty } } else { - tcx.closure_env_ty(coroutine_ty, coroutine_kind, env_region) + tcx.closure_env_ty(coroutine_ty, coroutine_kind, tcx.lifetimes.re_erased) }; - let sig = sig.skip_binder(); - ty::Binder::bind_with_vars( - tcx.mk_fn_sig( - iter::once(env_ty).chain([sig.tupled_inputs_ty]), - sig.to_coroutine_given_kind_and_upvars( - tcx, - args.as_coroutine_closure().parent_args(), - tcx.coroutine_for_closure(def_id), - coroutine_kind, - env_region, - args.as_coroutine_closure().tupled_upvars_ty(), - args.as_coroutine_closure().coroutine_captures_by_ref_ty(), - ), - sig.c_variadic, - sig.safety, - sig.abi, + let sig = tcx.instantiate_bound_regions_with_erased(sig); + + tcx.mk_fn_sig( + iter::once(env_ty).chain([sig.tupled_inputs_ty]), + sig.to_coroutine_given_kind_and_upvars( + tcx, + args.as_coroutine_closure().parent_args(), + tcx.coroutine_for_closure(def_id), + coroutine_kind, + tcx.lifetimes.re_erased, + args.as_coroutine_closure().tupled_upvars_ty(), + args.as_coroutine_closure().coroutine_captures_by_ref_ty(), ), - bound_vars, + sig.c_variadic, + sig.safety, + sig.abi, ) } ty::Coroutine(did, args) => { let coroutine_kind = tcx.coroutine_kind(did).unwrap(); let sig = args.as_coroutine().sig(); - let bound_vars = tcx.mk_bound_variable_kinds_from_iter(iter::once( - ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), - )); - let br = ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind: ty::BoundRegionKind::ClosureEnv, - }; - - let env_ty = Ty::new_mut_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), ty); + let env_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, ty); let pin_did = tcx.require_lang_item(LangItem::Pin, None); let pin_adt_ref = tcx.adt_def(pin_did); @@ -268,7 +231,7 @@ fn fn_sig_for_fn_abi<'tcx>( } }; - let fn_sig = if let Some(resume_ty) = resume_ty { + if let Some(resume_ty) = resume_ty { tcx.mk_fn_sig( [env_ty, resume_ty], ret_ty, @@ -285,8 +248,7 @@ fn fn_sig_for_fn_abi<'tcx>( hir::Safety::Safe, rustc_abi::ExternAbi::Rust, ) - }; - ty::Binder::bind_with_vars(fn_sig, bound_vars) + } } _ => bug!("unexpected type {:?} in Instance::fn_sig", ty), } @@ -335,8 +297,16 @@ fn fn_abi_of_fn_ptr<'tcx>( query: ty::PseudoCanonicalInput<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List<Ty<'tcx>>)>, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let ty::PseudoCanonicalInput { typing_env, value: (sig, extra_args) } = query; + let cx = LayoutCx::new(tcx, typing_env); - fn_abi_new_uncached(&cx, sig, extra_args, None, None, false) + fn_abi_new_uncached( + &cx, + tcx.instantiate_bound_regions_with_erased(sig), + extra_args, + None, + None, + false, + ) } fn fn_abi_of_instance<'tcx>( @@ -567,7 +537,7 @@ fn fn_abi_sanity_check<'tcx>( #[tracing::instrument(level = "debug", skip(cx, caller_location, fn_def_id, force_thin_self_ptr))] fn fn_abi_new_uncached<'tcx>( cx: &LayoutCx<'tcx>, - sig: ty::PolyFnSig<'tcx>, + sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>], caller_location: Option<Ty<'tcx>>, fn_def_id: Option<DefId>, @@ -575,7 +545,7 @@ fn fn_abi_new_uncached<'tcx>( force_thin_self_ptr: bool, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let tcx = cx.tcx(); - let sig = tcx.normalize_erasing_late_bound_regions(cx.typing_env, sig); + let sig = tcx.normalize_erasing_regions(cx.typing_env, sig); let conv = conv_from_spec_abi(cx.tcx(), sig.abi, sig.c_variadic); diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 66134b81b2a..0d656f1b63b 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -81,7 +81,7 @@ fn layout_of<'tcx>( record_layout_for_printing(&cx, layout); } - invariant::partially_check_layout(&cx, &layout); + invariant::layout_sanity_check(&cx, &layout); Ok(layout) } diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index 26ea81daf78..f39b87622f4 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -1,11 +1,11 @@ use std::assert_matches::assert_matches; -use rustc_abi::{BackendRepr, FieldsShape, Scalar, Size, Variants}; +use rustc_abi::{BackendRepr, FieldsShape, Scalar, Size, TagEncoding, Variants}; use rustc_middle::bug; use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout}; /// Enforce some basic invariants on layouts. -pub(super) fn partially_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { +pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { let tcx = cx.tcx(); // Type-level uninhabitedness should always imply ABI uninhabitedness. @@ -241,7 +241,17 @@ pub(super) fn partially_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLa check_layout_abi(cx, layout); - if let Variants::Multiple { variants, .. } = &layout.variants { + if let Variants::Multiple { variants, tag, tag_encoding, .. } = &layout.variants { + if let TagEncoding::Niche { niche_start, untagged_variant, niche_variants } = tag_encoding { + let niche_size = tag.size(cx); + assert!(*niche_start <= niche_size.unsigned_int_max()); + for (idx, variant) in variants.iter_enumerated() { + // Ensure all inhabited variants are accounted for. + if !variant.is_uninhabited() { + assert!(idx == *untagged_variant || niche_variants.contains(&idx)); + } + } + } for variant in variants.iter() { // No nested "multiple". assert_matches!(variant.variants, Variants::Single { .. }); diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 5010fc09adc..3cc2dcbaca6 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -511,7 +511,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // This is for global caching, so we properly track query dependencies. // Everything that affects the `result` should be performed within this - // `with_anon_task` closure. If computing this goal depends on something + // `with_cached_task` closure. If computing this goal depends on something // not tracked by the cache key and from outside of this anon task, it // must not be added to the global cache. Notably, this is the case for // trait solver cycles participants. |
