diff options
Diffstat (limited to 'compiler')
281 files changed, 5135 insertions, 5242 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e5b61d7000a..2820d5e6e0c 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2667,13 +2667,16 @@ impl Item { #[derive(Clone, Copy, Encodable, Decodable, Debug)] pub enum Extern { None, - Implicit, - Explicit(StrLit), + Implicit(Span), + Explicit(StrLit, Span), } impl Extern { - pub fn from_abi(abi: Option<StrLit>) -> Extern { - abi.map_or(Extern::Implicit, Extern::Explicit) + pub fn from_abi(abi: Option<StrLit>, span: Span) -> Extern { + match abi { + Some(name) => Extern::Explicit(name, span), + None => Extern::Implicit(span), + } } } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 0ef21371694..112197c6e39 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1272,8 +1272,8 @@ impl<'hir> LoweringContext<'_, 'hir> { pub(super) fn lower_extern(&mut self, ext: Extern) -> abi::Abi { match ext { Extern::None => abi::Abi::Rust, - Extern::Implicit => abi::Abi::FALLBACK, - Extern::Explicit(abi) => self.lower_abi(abi), + Extern::Implicit(_) => abi::Abi::FALLBACK, + Extern::Explicit(abi, _) => self.lower_abi(abi), } } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 503bdbad258..3942062656f 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -13,7 +13,9 @@ use rustc_ast::walk_list; use rustc_ast::*; use rustc_ast_pretty::pprust::{self, State}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{error_code, pluralize, struct_span_err, Applicability}; +use rustc_errors::{ + error_code, pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, +}; use rustc_parse::validate_attr; use rustc_session::lint::builtin::{ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY, @@ -476,6 +478,17 @@ impl<'a> AstValidator<'a> { } fn error_item_without_body(&self, sp: Span, ctx: &str, msg: &str, sugg: &str) { + self.error_item_without_body_with_help(sp, ctx, msg, sugg, |_| ()); + } + + fn error_item_without_body_with_help( + &self, + sp: Span, + ctx: &str, + msg: &str, + sugg: &str, + help: impl FnOnce(&mut DiagnosticBuilder<'_, ErrorGuaranteed>), + ) { let source_map = self.session.source_map(); let end = source_map.end_point(sp); let replace_span = if source_map.span_to_snippet(end).map(|s| s == ";").unwrap_or(false) { @@ -483,15 +496,15 @@ impl<'a> AstValidator<'a> { } else { sp.shrink_to_hi() }; - self.err_handler() - .struct_span_err(sp, msg) - .span_suggestion( - replace_span, - &format!("provide a definition for the {}", ctx), - sugg, - Applicability::HasPlaceholders, - ) - .emit(); + let mut err = self.err_handler().struct_span_err(sp, msg); + err.span_suggestion( + replace_span, + &format!("provide a definition for the {}", ctx), + sugg, + Applicability::HasPlaceholders, + ); + help(&mut err); + err.emit(); } fn check_impl_item_provided<T>(&self, sp: Span, body: &Option<T>, ctx: &str, sugg: &str) { @@ -630,7 +643,8 @@ impl<'a> AstValidator<'a> { match (fk.ctxt(), fk.header()) { (Some(FnCtxt::Foreign), _) => return, (Some(FnCtxt::Free), Some(header)) => match header.ext { - Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }) | Extern::Implicit + Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }, _) + | Extern::Implicit(_) if matches!(header.unsafety, Unsafe::Yes(_)) => { return; @@ -842,7 +856,7 @@ impl<'a> AstValidator<'a> { .emit(); }); self.check_late_bound_lifetime_defs(&bfty.generic_params); - if let Extern::Implicit = bfty.ext { + if let Extern::Implicit(_) = bfty.ext { let sig_span = self.session.source_map().next_point(ty.span.shrink_to_lo()); self.maybe_lint_missing_abi(sig_span, ty.id); } @@ -1190,8 +1204,38 @@ impl<'a> Visitor<'a> for AstValidator<'a> { if body.is_none() { let msg = "free function without a body"; - self.error_item_without_body(item.span, "function", msg, " { <body> }"); + let ext = sig.header.ext; + + let f = |e: &mut DiagnosticBuilder<'_, _>| { + if let Extern::Implicit(start_span) | Extern::Explicit(_, start_span) = &ext + { + let start_suggestion = if let Extern::Explicit(abi, _) = ext { + format!("extern \"{}\" {{", abi.symbol_unescaped) + } else { + "extern {".to_owned() + }; + + let end_suggestion = " }".to_owned(); + let end_span = item.span.shrink_to_hi(); + + e + .multipart_suggestion( + "if you meant to declare an externally defined function, use an `extern` block", + vec![(*start_span, start_suggestion), (end_span, end_suggestion)], + Applicability::MaybeIncorrect, + ); + } + }; + + self.error_item_without_body_with_help( + item.span, + "function", + msg, + " { <body> }", + f, + ); } + self.visit_vis(&item.vis); self.visit_ident(item.ident); let kind = @@ -1556,7 +1600,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { if let FnKind::Fn( _, _, - FnSig { span: sig_span, header: FnHeader { ext: Extern::Implicit, .. }, .. }, + FnSig { span: sig_span, header: FnHeader { ext: Extern::Implicit(_), .. }, .. }, _, _, _, diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 37c4f665415..fd2dd6cf6c7 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -283,7 +283,7 @@ impl<'a> PostExpansionVisitor<'a> { } fn check_extern(&self, ext: ast::Extern, constness: ast::Const) { - if let ast::Extern::Explicit(abi) = ext { + if let ast::Extern::Explicit(abi, _) = ext { self.check_abi(abi, constness); } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index ad8dbfd506d..c9e3a7edfa6 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1734,10 +1734,10 @@ impl<'a> State<'a> { match header.ext { ast::Extern::None => {} - ast::Extern::Implicit => { + ast::Extern::Implicit(_) => { self.word_nbsp("extern"); } - ast::Extern::Explicit(abi) => { + ast::Extern::Explicit(abi, _) => { self.word_nbsp("extern"); self.print_literal(&abi.as_lit()); self.nbsp(); diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 708fe8719a1..a5d9a2bc597 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -478,7 +478,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { struct_span_err!(self, span, E0716, "temporary value dropped while borrowed",) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] fn struct_span_err_with_code<S: Into<MultiSpan>>( &self, sp: S, diff --git a/compiler/rustc_borrowck/src/constraint_generation.rs b/compiler/rustc_borrowck/src/constraint_generation.rs index e4ffae38c33..5e9cec5c350 100644 --- a/compiler/rustc_borrowck/src/constraint_generation.rs +++ b/compiler/rustc_borrowck/src/constraint_generation.rs @@ -5,8 +5,8 @@ use rustc_middle::mir::{ BasicBlock, BasicBlockData, Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UserTypeProjection, }; -use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, RegionVid, Ty}; use crate::{ @@ -149,7 +149,7 @@ impl<'cg, 'cx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'tcx> { fn visit_ascribe_user_ty( &mut self, _place: &Place<'tcx>, - _variance: &ty::Variance, + _variance: ty::Variance, _user_ty: &UserTypeProjection, _location: Location, ) { @@ -163,7 +163,7 @@ impl<'cx, 'cg, 'tcx> ConstraintGeneration<'cx, 'cg, 'tcx> { /// `location`. fn add_regular_live_constraint<T>(&mut self, live_ty: T, location: Location) where - T: TypeFoldable<'tcx>, + T: TypeVisitable<'tcx>, { debug!("add_regular_live_constraint(live_ty={:?}, location={:?})", live_ty, location); diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index fbc3a8cc088..6fea6941085 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -812,12 +812,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { return FnSelfUse { var_span: stmt.source_info.span, fn_call_span: *fn_span, - fn_span: self - .infcx - .tcx - .sess - .source_map() - .guess_head_span(self.infcx.tcx.def_span(method_did)), + fn_span: self.infcx.tcx.def_span(method_did), kind, }; } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index f5c9392948b..d0e0203bf8c 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -19,7 +19,9 @@ use rustc_middle::mir::{ }; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::ObligationCauseCode; -use rustc_middle::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{ + self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitable, +}; use rustc_span::Span; use crate::{ diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index d182c0cf4e8..d129e845426 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -2,7 +2,9 @@ use rustc_data_structures::vec_map::VecMap; use rustc_hir::def_id::DefId; use rustc_hir::OpaqueTyOrigin; use rustc_infer::infer::InferCtxt; -use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{ + self, OpaqueHiddenType, OpaqueTypeKey, TyCtxt, TypeFoldable, TypeVisitable, +}; use rustc_trait_selection::opaque_types::InferCtxtExt; use super::RegionInferenceContext; diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 5e33d9d25c2..3f9c0cecccc 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -6,7 +6,7 @@ use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; use rustc_infer::infer::{self, InferCtxt, SubregionOrigin}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::TypeVisitable; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{Span, DUMMY_SP}; diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index ac8670a5138..d5c401ae1c6 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -1,11 +1,11 @@ +use itertools::{Either, Itertools}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::{Body, Local}; use rustc_middle::ty::{RegionVid, TyCtxt}; -use std::rc::Rc; - use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::ResultsCursor; +use std::rc::Rc; use crate::{ constraints::OutlivesConstraintSet, @@ -46,7 +46,8 @@ pub(super) fn generate<'mir, 'tcx>( &typeck.borrowck_context.universal_regions, &typeck.borrowck_context.constraints.outlives_constraints, ); - let live_locals = compute_live_locals(typeck.tcx(), &free_regions, &body); + let (relevant_live_locals, boring_locals) = + compute_relevant_live_locals(typeck.tcx(), &free_regions, &body); let facts_enabled = use_polonius || AllFacts::enabled(typeck.tcx()); let polonius_drop_used = if facts_enabled { @@ -57,48 +58,44 @@ pub(super) fn generate<'mir, 'tcx>( None }; - if !live_locals.is_empty() || facts_enabled { - trace::trace( - typeck, - body, - elements, - flow_inits, - move_data, - live_locals, - polonius_drop_used, - ); - } + trace::trace( + typeck, + body, + elements, + flow_inits, + move_data, + relevant_live_locals, + boring_locals, + polonius_drop_used, + ); } -// The purpose of `compute_live_locals` is to define the subset of `Local` +// The purpose of `compute_relevant_live_locals` is to define the subset of `Local` // variables for which we need to do a liveness computation. We only need // to compute whether a variable `X` is live if that variable contains // some region `R` in its type where `R` is not known to outlive a free // region (i.e., where `R` may be valid for just a subset of the fn body). -fn compute_live_locals<'tcx>( +fn compute_relevant_live_locals<'tcx>( tcx: TyCtxt<'tcx>, free_regions: &FxHashSet<RegionVid>, body: &Body<'tcx>, -) -> Vec<Local> { - let live_locals: Vec<Local> = body - .local_decls - .iter_enumerated() - .filter_map(|(local, local_decl)| { +) -> (Vec<Local>, Vec<Local>) { + let (boring_locals, relevant_live_locals): (Vec<_>, Vec<_>) = + body.local_decls.iter_enumerated().partition_map(|(local, local_decl)| { if tcx.all_free_regions_meet(&local_decl.ty, |r| { free_regions.contains(&r.to_region_vid()) }) { - None + Either::Left(local) } else { - Some(local) + Either::Right(local) } - }) - .collect(); + }); debug!("{} total variables", body.local_decls.len()); - debug!("{} variables need liveness", live_locals.len()); + debug!("{} variables need liveness", relevant_live_locals.len()); debug!("{} regions outlive free regions", free_regions.len()); - live_locals + (relevant_live_locals, boring_locals) } /// Computes all regions that are (currently) known to outlive free diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 169de23facc..d4e61ec213b 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -3,7 +3,7 @@ use rustc_index::bit_set::HybridBitSet; use rustc_index::interval::IntervalSet; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location}; -use rustc_middle::ty::{Ty, TypeFoldable}; +use rustc_middle::ty::{Ty, TypeVisitable}; use rustc_trait_selection::traits::query::dropck_outlives::DropckOutlivesResult; use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; @@ -41,12 +41,13 @@ pub(super) fn trace<'mir, 'tcx>( elements: &Rc<RegionValueElements>, flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, move_data: &MoveData<'tcx>, - live_locals: Vec<Local>, + relevant_live_locals: Vec<Local>, + boring_locals: Vec<Local>, polonius_drop_used: Option<Vec<(Local, Location)>>, ) { debug!("trace()"); - let local_use_map = &LocalUseMap::build(&live_locals, elements, body); + let local_use_map = &LocalUseMap::build(&relevant_live_locals, elements, body); let cx = LivenessContext { typeck, @@ -61,10 +62,12 @@ pub(super) fn trace<'mir, 'tcx>( let mut results = LivenessResults::new(cx); if let Some(drop_used) = polonius_drop_used { - results.add_extra_drop_facts(drop_used, live_locals.iter().copied().collect()) + results.add_extra_drop_facts(drop_used, relevant_live_locals.iter().copied().collect()) } - results.compute_for_all_locals(live_locals); + results.compute_for_all_locals(relevant_live_locals); + + results.dropck_boring_locals(boring_locals); } /// Contextual state for the type-liveness generator. @@ -133,8 +136,8 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { } } - fn compute_for_all_locals(&mut self, live_locals: Vec<Local>) { - for local in live_locals { + fn compute_for_all_locals(&mut self, relevant_live_locals: Vec<Local>) { + for local in relevant_live_locals { self.reset_local_state(); self.add_defs_for(local); self.compute_use_live_points_for(local); @@ -157,6 +160,24 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { } } + // Runs dropck for locals whose liveness isn't relevant. This is + // necessary to eagerly detect unbound recursion during drop glue computation. + fn dropck_boring_locals(&mut self, boring_locals: Vec<Local>) { + for local in boring_locals { + let local_ty = self.cx.body.local_decls[local].ty; + let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({ + let typeck = &mut self.cx.typeck; + move || LivenessContext::compute_drop_data(typeck, local_ty) + }); + + drop_data.dropck_result.report_overflows( + self.cx.typeck.infcx.tcx, + self.cx.body.local_decls[local].source_info.span, + local_ty, + ); + } + } + /// Add extra drop facts needed for Polonius. /// /// Add facts for all locals with free regions, since regions may outlive @@ -164,12 +185,12 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { fn add_extra_drop_facts( &mut self, drop_used: Vec<(Local, Location)>, - live_locals: FxHashSet<Local>, + relevant_live_locals: FxHashSet<Local>, ) { let locations = IntervalSet::new(self.cx.elements.num_points()); for (local, location) in drop_used { - if !live_locals.contains(&local) { + if !relevant_live_locals.contains(&local) { let local_ty = self.cx.body.local_decls[local].ty; if local_ty.has_free_regions() { self.cx.add_drop_live_facts_for(local, local_ty, &[location], &locations); @@ -456,7 +477,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { /// points `live_at`. fn add_use_live_facts_for( &mut self, - value: impl TypeFoldable<'tcx>, + value: impl TypeVisitable<'tcx>, live_at: &IntervalSet<PointIndex>, ) { debug!("add_use_live_facts_for(value={:?})", value); @@ -521,7 +542,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { fn make_all_regions_live( elements: &RegionValueElements, typeck: &mut TypeChecker<'_, 'tcx>, - value: impl TypeFoldable<'tcx>, + value: impl TypeVisitable<'tcx>, live_at: &IntervalSet<PointIndex>, ) { debug!("make_all_regions_live(value={:?})", value); diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index a21a8dd48be..d71a4983a92 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -27,8 +27,8 @@ use rustc_middle::mir::AssertKind; use rustc_middle::mir::*; use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::cast::CastTy; -use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef, UserSubsts}; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, OpaqueHiddenType, OpaqueTypeKey, RegionVid, ToPredicate, Ty, TyCtxt, UserType, UserTypeAnnotationIndex, diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index ff431c8de5d..2a9e37081e0 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -66,7 +66,7 @@ //! //! # "`cs`" functions //! -//! The `cs_...` functions ("combine substructure) are designed to +//! The `cs_...` functions ("combine substructure") are designed to //! make life easier by providing some pre-made recipes for common //! threads; mostly calling the function being derived on all the //! arguments and then combining them back together in some way (or @@ -429,6 +429,7 @@ impl<'a> TraitDef<'a> { generics, from_scratch, use_temporaries, + is_packed, ), ast::ItemKind::Enum(ref enum_def, ref generics) => { // We ignore `use_temporaries` here, because @@ -448,6 +449,7 @@ impl<'a> TraitDef<'a> { generics, from_scratch, use_temporaries, + is_packed, ) } else { cx.span_err(mitem.span, "this trait cannot be derived for unions"); @@ -729,6 +731,7 @@ impl<'a> TraitDef<'a> { generics: &Generics, from_scratch: bool, use_temporaries: bool, + is_packed: bool, ) -> P<ast::Item> { let field_tys: Vec<P<ast::Ty>> = struct_def.fields().iter().map(|field| field.ty.clone()).collect(); @@ -757,6 +760,7 @@ impl<'a> TraitDef<'a> { &self_args, &nonself_args, use_temporaries, + is_packed, ) }; @@ -945,6 +949,7 @@ impl<'a> MethodDef<'a> { }) } + /// The normal case uses field access. /// ``` /// #[derive(PartialEq)] /// # struct Dummy; @@ -953,33 +958,21 @@ impl<'a> MethodDef<'a> { /// // equivalent to: /// impl PartialEq for A { /// fn eq(&self, other: &A) -> bool { - /// match *self { - /// A {x: ref __self_0_0, y: ref __self_0_1} => { - /// match *other { - /// A {x: ref __self_1_0, y: ref __self_1_1} => { - /// __self_0_0.eq(__self_1_0) && __self_0_1.eq(__self_1_1) - /// } - /// } - /// } - /// } + /// self.x == other.x && self.y == other.y /// } /// } /// ``` - /// or if A is repr(packed) - note fields are matched by-value - /// instead of by-reference. + /// But if the struct is `repr(packed)`, we can't use something like + /// `&self.x` on a packed type (as required for e.g. `Debug` and `Hash`) + /// because that might cause an unaligned ref. So we use let-destructuring + /// instead. /// ``` /// # struct A { x: i32, y: i32 } /// impl PartialEq for A { /// fn eq(&self, other: &A) -> bool { - /// match *self { - /// A {x: __self_0_0, y: __self_0_1} => { - /// match other { - /// A {x: __self_1_0, y: __self_1_1} => { - /// __self_0_0.eq(&__self_1_0) && __self_0_1.eq(&__self_1_1) - /// } - /// } - /// } - /// } + /// let Self { x: ref __self_0_0, y: ref __self_0_1 } = *self; + /// let Self { x: ref __self_1_0, y: ref __self_1_1 } = *other; + /// *__self_0_0 == *__self_1_0 && *__self_0_1 == *__self_1_1 /// } /// } /// ``` @@ -992,24 +985,33 @@ impl<'a> MethodDef<'a> { self_args: &[P<Expr>], nonself_args: &[P<Expr>], use_temporaries: bool, + is_packed: bool, ) -> P<Expr> { let mut raw_fields = Vec::new(); // Vec<[fields of self], [fields of next Self arg], [etc]> let span = trait_.span; let mut patterns = Vec::new(); - for i in 0..self_args.len() { - // We could use `type_ident` instead of `Self`, but in the case of a type parameter - // shadowing the struct name, that causes a second, unnecessary E0578 error. #97343 - let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]); - let (pat, ident_expr) = trait_.create_struct_pattern( - cx, - struct_path, - struct_def, - &format!("__self_{}", i), - ast::Mutability::Not, - use_temporaries, - ); - patterns.push(pat); - raw_fields.push(ident_expr); + + for (i, self_arg) in self_args.iter().enumerate() { + let ident_exprs = if !is_packed { + trait_.create_struct_field_accesses(cx, self_arg, struct_def) + } else { + // Get the pattern for the let-destructuring. + // + // We could use `type_ident` instead of `Self`, but in the case of a type parameter + // shadowing the struct name, that causes a second, unnecessary E0578 error. #97343 + let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]); + let (pat, ident_exprs) = trait_.create_struct_pattern( + cx, + struct_path, + struct_def, + &format!("__self_{}", i), + ast::Mutability::Not, + use_temporaries, + ); + patterns.push(pat); + ident_exprs + }; + raw_fields.push(ident_exprs); } // transpose raw_fields @@ -1036,7 +1038,6 @@ impl<'a> MethodDef<'a> { cx.span_bug(span, "no `self` parameter for method in generic `derive`") }; - // body of the inner most destructuring match let mut body = self.call_substructure_method( cx, trait_, @@ -1045,14 +1046,18 @@ impl<'a> MethodDef<'a> { &Struct(struct_def, fields), ); - // make a series of nested matches, to destructure the - // structs. This is actually right-to-left, but it shouldn't - // matter. - for (arg_expr, pat) in iter::zip(self_args, patterns) { - body = cx.expr_match(span, arg_expr.clone(), vec![cx.arm(span, pat.clone(), body)]) - } + if !is_packed { + body.span = span; + body + } else { + // Do the let-destructuring. + let mut stmts: Vec<_> = iter::zip(self_args, patterns) + .map(|(arg_expr, pat)| cx.stmt_let_pat(span, pat, arg_expr.clone())) + .collect(); + stmts.push(cx.stmt_expr(body)); - body + cx.expr_block(cx.block(span, stmts)) + } } fn expand_static_struct_method_body( @@ -1522,8 +1527,6 @@ impl<'a> TraitDef<'a> { paths.push(ident.with_span_pos(sp)); let val = cx.expr_path(cx.path_ident(sp, ident)); let val = if use_temporaries { val } else { cx.expr_deref(sp, val) }; - let val = cx.expr(sp, ast::ExprKind::Paren(val)); - ident_exprs.push((sp, struct_field.ident, val, &struct_field.attrs[..])); } @@ -1555,6 +1558,39 @@ impl<'a> TraitDef<'a> { (pattern, ident_exprs) } + fn create_struct_field_accesses( + &self, + cx: &mut ExtCtxt<'_>, + mut self_arg: &P<Expr>, + struct_def: &'a VariantData, + ) -> Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])> { + let mut ident_exprs = Vec::new(); + for (i, struct_field) in struct_def.fields().iter().enumerate() { + let sp = struct_field.span.with_ctxt(self.span.ctxt()); + + // We don't the need the deref, if there is one. + if let ast::ExprKind::Unary(ast::UnOp::Deref, inner) = &self_arg.kind { + self_arg = inner; + } + + // Note: we must use `struct_field.span` rather than `span` in the + // `unwrap_or_else` case otherwise the hygiene is wrong and we get + // "field `0` of struct `Point` is private" errors on tuple + // structs. + let val = cx.expr( + sp, + ast::ExprKind::Field( + self_arg.clone(), + struct_field.ident.unwrap_or_else(|| { + Ident::from_str_and_span(&i.to_string(), struct_field.span) + }), + ), + ); + ident_exprs.push((sp, struct_field.ident, val, &struct_field.attrs[..])); + } + ident_exprs + } + fn create_enum_variant_pattern( &self, cx: &mut ExtCtxt<'_>, @@ -1643,7 +1679,6 @@ where /// fields. /// When the `substructure` is an `EnumNonMatchingCollapsed`, the result of `enum_nonmatch_f` /// is returned. Statics may not be folded over. -/// See `cs_op` in `partial_ord.rs` for a model example. pub fn cs_fold1<F, B>( use_foldl: bool, f: F, diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index fa4ea426385..f71d749df62 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -635,29 +635,6 @@ fn codegen_stmt<'tcx>( let (ptr, _extra) = operand.load_scalar_pair(fx); lval.write_cvalue(fx, CValue::by_val(ptr, dest_layout)) } - } else if let ty::Adt(adt_def, _substs) = from_ty.kind() { - // enum -> discriminant value - assert!(adt_def.is_enum()); - match to_ty.kind() { - ty::Uint(_) | ty::Int(_) => {} - _ => unreachable!("cast adt {} -> {}", from_ty, to_ty), - } - let to_clif_ty = fx.clif_type(to_ty).unwrap(); - - let discriminant = crate::discriminant::codegen_get_discriminant( - fx, - operand, - fx.layout_of(operand.layout().ty.discriminant_ty(fx.tcx)), - ) - .load_scalar(fx); - - let res = crate::cast::clif_intcast( - fx, - discriminant, - to_clif_ty, - to_ty.is_signed(), - ); - lval.write_cvalue(fx, CValue::by_val(res, dest_layout)); } else { let to_clif_ty = fx.clif_type(to_ty).unwrap(); let from = operand.load_scalar(fx); diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 9d2e12f9898..be2d3108c5f 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -79,7 +79,7 @@ mod prelude { pub(crate) use rustc_middle::ty::layout::{self, LayoutOf, TyAndLayout}; pub(crate) use rustc_middle::ty::{ self, FloatTy, Instance, InstanceDef, IntTy, ParamEnv, Ty, TyCtxt, TypeAndMut, - TypeFoldable, UintTy, + TypeFoldable, TypeVisitable, UintTy, }; pub(crate) use rustc_target::abi::{Abi, Scalar, Size, VariantIdx}; diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs index 76419b103d0..c1041125ecc 100644 --- a/compiler/rustc_codegen_gcc/src/callee.rs +++ b/compiler/rustc_codegen_gcc/src/callee.rs @@ -1,6 +1,6 @@ use gccjit::{FunctionType, RValue}; use rustc_codegen_ssa::traits::BaseTypeMethods; -use rustc_middle::ty::{self, Instance, TypeFoldable}; +use rustc_middle::ty::{self, Instance, TypeVisitable}; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; use crate::abi::FnAbiGccExt; diff --git a/compiler/rustc_codegen_gcc/src/mono_item.rs b/compiler/rustc_codegen_gcc/src/mono_item.rs index e21d40b6c37..9468a1ef4bb 100644 --- a/compiler/rustc_codegen_gcc/src/mono_item.rs +++ b/compiler/rustc_codegen_gcc/src/mono_item.rs @@ -1,7 +1,7 @@ use rustc_codegen_ssa::traits::PreDefineMethods; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::mono::{Linkage, Visibility}; -use rustc_middle::ty::{self, Instance, TypeFoldable}; +use rustc_middle::ty::{self, Instance, TypeVisitable}; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf}; use rustc_span::def_id::DefId; diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 569ee2925b1..524d10fb5e2 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -3,7 +3,7 @@ use std::fmt::Write; use gccjit::{Struct, Type}; use crate::rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods}; use rustc_middle::bug; -use rustc_middle::ty::{self, Ty, TypeFoldable}; +use rustc_middle::ty::{self, Ty, TypeVisitable}; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_target::abi::{self, Abi, F32, F64, FieldsShape, Int, Integer, Pointer, PointeeInfo, Size, TyAbiInterface, Variants}; diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 8c1e865762c..4a4cccb490d 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -28,7 +28,7 @@ use std::ffi::CStr; use std::iter; use std::ops::Deref; use std::ptr; -use tracing::debug; +use tracing::{debug, instrument}; // All Builders must have an llfn associated with them #[must_use] @@ -464,15 +464,15 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } + #[instrument(level = "trace", skip(self))] fn load_operand(&mut self, place: PlaceRef<'tcx, &'ll Value>) -> OperandRef<'tcx, &'ll Value> { - debug!("PlaceRef::load: {:?}", place); - assert_eq!(place.llextra.is_some(), place.layout.is_unsized()); if place.layout.is_zst() { return OperandRef::new_zst(self, place.layout); } + #[instrument(level = "trace", skip(bx))] fn scalar_load_metadata<'a, 'll, 'tcx>( bx: &mut Builder<'a, 'll, 'tcx>, load: &'ll Value, diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index ac423a22703..72155d874a2 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -13,7 +13,7 @@ use rustc_codegen_ssa::traits::*; use tracing::debug; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; -use rustc_middle::ty::{self, Instance, TypeFoldable}; +use rustc_middle::ty::{self, Instance, TypeVisitable}; /// Codegens a reference to a fn/method item, monomorphizing and /// inlining as it goes. diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 71699b5cf38..64ecbc82c56 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -27,7 +27,7 @@ use rustc_index::vec::IndexVec; use rustc_middle::mir; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TypeFoldable}; +use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TypeVisitable}; use rustc_session::config::{self, DebugInfo}; use rustc_session::Session; use rustc_span::symbol::Symbol; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index d92d9d96fe2..5ebc2d6139f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1619,7 +1619,7 @@ extern "C" { B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, - IsSized: bool, + IsSigned: bool, ) -> &'a Value; // Comparisons diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index a3053742aad..6e94284852f 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -8,7 +8,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; pub use rustc_middle::mir::mono::MonoItem; use rustc_middle::mir::mono::{Linkage, Visibility}; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf}; -use rustc_middle::ty::{self, Instance, TypeFoldable}; +use rustc_middle::ty::{self, Instance, TypeVisitable}; use rustc_session::config::CrateType; use rustc_target::spec::RelocModel; use tracing::debug; diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 86280523631..9f0e6c80b19 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -6,7 +6,7 @@ use rustc_codegen_ssa::traits::*; use rustc_middle::bug; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; -use rustc_middle::ty::{self, Ty, TypeFoldable}; +use rustc_middle::ty::{self, Ty, TypeVisitable}; use rustc_target::abi::{Abi, AddressSpace, Align, FieldsShape}; use rustc_target::abi::{Int, Pointer, F32, F64}; use rustc_target::abi::{PointeeInfo, Scalar, Size, TyAbiInterface, Variants}; diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 0503d723240..f296a04dea1 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -17,7 +17,7 @@ use rustc_middle::mir::AssertKind; use rustc_middle::mir::{self, SwitchTargets}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; -use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; +use rustc_middle::ty::{self, Instance, Ty, TypeVisitable}; use rustc_span::source_map::Span; use rustc_span::{sym, Symbol}; use rustc_symbol_mangling::typeid_for_fnabi; diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 7f14b95317b..645afae30d8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -513,9 +513,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; let ty = substs.type_at(0); - if int_type_width_signed(ty, bx.tcx()).is_some() - || (ty.is_unsafe_ptr() && op == "xchg") - { + if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_unsafe_ptr() { let mut ptr = args[0].immediate(); let mut val = args[1].immediate(); if ty.is_unsafe_ptr() { diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 0c958de64fa..ec3f7a2156a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -2,7 +2,7 @@ use crate::traits::*; use rustc_middle::mir; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout}; -use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; +use rustc_middle::ty::{self, Instance, Ty, TypeFoldable, TypeVisitable}; use rustc_symbol_mangling::typeid_for_fnabi; use rustc_target::abi::call::{FnAbi, PassMode}; diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 5b88635982f..58cee0c8bb0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -204,6 +204,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { } /// Obtain the actual discriminant of a value. + #[instrument(level = "trace", skip(bx))] pub fn codegen_get_discr<Bx: BuilderMethods<'a, 'tcx, Value = V>>( self, bx: &mut Bx, @@ -420,12 +421,12 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { } impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + #[instrument(level = "trace", skip(self, bx))] pub fn codegen_place( &mut self, bx: &mut Bx, place_ref: mir::PlaceRef<'tcx>, ) -> PlaceRef<'tcx, Bx::Value> { - debug!("codegen_place(place_ref={:?})", place_ref); let cx = self.cx; let tcx = self.cx.tcx(); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index a5806d64d43..7ff12823bf7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -12,17 +12,15 @@ use rustc_middle::ty::cast::{CastTy, IntTy}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt}; use rustc_span::source_map::{Span, DUMMY_SP}; -use rustc_target::abi::{Abi, Int, Variants}; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + #[instrument(level = "trace", skip(self, bx))] pub fn codegen_rvalue( &mut self, mut bx: Bx, dest: PlaceRef<'tcx, Bx::Value>, rvalue: &mir::Rvalue<'tcx>, ) -> Bx { - debug!("codegen_rvalue(dest.llval={:?}, rvalue={:?})", dest.llval, rvalue); - match *rvalue { mir::Rvalue::Use(ref operand) => { let cg_operand = self.codegen_operand(&mut bx, operand); @@ -285,74 +283,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { CastTy::from_ty(operand.layout.ty).expect("bad input type for cast"); let r_t_out = CastTy::from_ty(cast.ty).expect("bad output type for cast"); let ll_t_in = bx.cx().immediate_backend_type(operand.layout); - match operand.layout.variants { - Variants::Single { index } => { - if let Some(discr) = - operand.layout.ty.discriminant_for_variant(bx.tcx(), index) - { - let discr_layout = bx.cx().layout_of(discr.ty); - let discr_t = bx.cx().immediate_backend_type(discr_layout); - let discr_val = bx.cx().const_uint_big(discr_t, discr.val); - let discr_val = - bx.intcast(discr_val, ll_t_out, discr.ty.is_signed()); - - return ( - bx, - OperandRef { - val: OperandValue::Immediate(discr_val), - layout: cast, - }, - ); - } - } - Variants::Multiple { .. } => {} - } let llval = operand.immediate(); - let mut signed = false; - if let Abi::Scalar(scalar) = operand.layout.abi { - if let Int(_, s) = scalar.primitive() { - // We use `i1` for bytes that are always `0` or `1`, - // e.g., `#[repr(i8)] enum E { A, B }`, but we can't - // let LLVM interpret the `i1` as signed, because - // then `i1 1` (i.e., E::B) is effectively `i8 -1`. - signed = !scalar.is_bool() && s; - - if !scalar.is_always_valid(bx.cx()) - && scalar.valid_range(bx.cx()).end - >= scalar.valid_range(bx.cx()).start - { - // We want `table[e as usize ± k]` to not - // have bound checks, and this is the most - // convenient place to put the `assume`s. - if scalar.valid_range(bx.cx()).start > 0 { - let enum_value_lower_bound = bx.cx().const_uint_big( - ll_t_in, - scalar.valid_range(bx.cx()).start, - ); - let cmp_start = bx.icmp( - IntPredicate::IntUGE, - llval, - enum_value_lower_bound, - ); - bx.assume(cmp_start); - } - - let enum_value_upper_bound = bx - .cx() - .const_uint_big(ll_t_in, scalar.valid_range(bx.cx()).end); - let cmp_end = bx.icmp( - IntPredicate::IntULE, - llval, - enum_value_upper_bound, - ); - bx.assume(cmp_end); - } - } - } - let newval = match (r_t_in, r_t_out) { - (CastTy::Int(_), CastTy::Int(_)) => bx.intcast(llval, ll_t_out, signed), + (CastTy::Int(i), CastTy::Int(_)) => { + bx.intcast(llval, ll_t_out, i.is_signed()) + } (CastTy::Float, CastTy::Float) => { let srcsz = bx.cx().float_width(ll_t_in); let dstsz = bx.cx().float_width(ll_t_out); @@ -364,8 +300,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { llval } } - (CastTy::Int(_), CastTy::Float) => { - if signed { + (CastTy::Int(i), CastTy::Float) => { + if i.is_signed() { bx.sitofp(llval, ll_t_out) } else { bx.uitofp(llval, ll_t_out) @@ -374,8 +310,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Ptr(_)) => { bx.pointercast(llval, ll_t_out) } - (CastTy::Int(_), CastTy::Ptr(_)) => { - let usize_llval = bx.intcast(llval, bx.cx().type_isize(), signed); + (CastTy::Int(i), CastTy::Ptr(_)) => { + let usize_llval = + bx.intcast(llval, bx.cx().type_isize(), i.is_signed()); bx.inttoptr(usize_llval, ll_t_out) } (CastTy::Float, CastTy::Int(IntTy::I)) => { diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index d9ebfc3e871..f452f29883f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -6,9 +6,8 @@ use crate::traits::BuilderMethods; use crate::traits::*; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + #[instrument(level = "debug", skip(self, bx))] pub fn codegen_statement(&mut self, mut bx: Bx, statement: &mir::Statement<'tcx>) -> Bx { - debug!("codegen_statement(statement={:?})", statement); - self.set_debug_loc(&mut bx, statement.source_info); match statement.kind { mir::StatementKind::Assign(box (ref place, ref rvalue)) => { diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 0dac4f8978e..f84dd9521ee 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -189,6 +189,7 @@ pub(super) fn op_to_const<'tcx>( let len: usize = len.try_into().unwrap(); ConstValue::Slice { data, start, end: start + len } } + Immediate::Uninit => to_const_value(&op.assert_mem_place()), }, } } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index d09ab5fa3e8..5d598b65c72 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -8,7 +8,7 @@ use rustc_middle::mir::CastKind; use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut}; -use rustc_target::abi::{Integer, Variants}; +use rustc_target::abi::Integer; use rustc_type_ir::sty::TyKind::*; use super::{ @@ -128,12 +128,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Float(FloatTy::F64) => { return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, cast_ty).into()); } - // The rest is integer/pointer-"like", including fn ptr casts and casts from enums that - // are represented as integers. + // The rest is integer/pointer-"like", including fn ptr casts _ => assert!( src.layout.ty.is_bool() || src.layout.ty.is_char() - || src.layout.ty.is_enum() || src.layout.ty.is_integral() || src.layout.ty.is_any_ptr(), "Unexpected cast from type {:?}", @@ -143,25 +141,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // # First handle non-scalar source values. - // Handle cast from a ZST enum (0 or 1 variants). - match src.layout.variants { - Variants::Single { index } => { - if src.layout.abi.is_uninhabited() { - // This is dead code, because an uninhabited enum is UB to - // instantiate. - throw_ub!(Unreachable); - } - if let Some(discr) = src.layout.ty.discriminant_for_variant(*self.tcx, index) { - assert!(src.layout.is_zst()); - let discr_layout = self.layout_of(discr.ty)?; - - let scalar = Scalar::from_uint(discr.val, discr_layout.layout.size()); - return Ok(self.cast_from_int_like(scalar, discr_layout, cast_ty)?.into()); - } - } - Variants::Multiple { .. } => {} - } - // Handle casting any ptr to raw ptr (might be a fat ptr). if src.layout.ty.is_any_ptr() && cast_ty.is_unsafe_ptr() { let dest_layout = self.layout_of(cast_ty)?; @@ -174,7 +153,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert_eq!(dest_layout.size, self.pointer_size()); assert!(src.layout.ty.is_unsafe_ptr()); return match **src { - Immediate::ScalarPair(data, _) => Ok(data.into()), + Immediate::ScalarPair(data, _) => Ok(data.check_init()?.into()), Immediate::Scalar(..) => span_bug!( self.cur_span(), "{:?} input to a fat-to-thin cast ({:?} -> {:?})", @@ -182,6 +161,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { src.layout.ty, cast_ty ), + Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)), }; } } @@ -379,7 +359,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let src_field = self.operand_field(src, i)?; let dst_field = self.place_field(dest, i)?; if src_field.layout.ty == cast_ty_field.ty { - self.copy_op(&src_field, &dst_field)?; + self.copy_op(&src_field, &dst_field, /*allow_transmute*/ false)?; } else { self.unsize_into(&src_field, cast_ty_field, &dst_field)?; } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 66c73624501..3892d1920ce 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -112,6 +112,8 @@ pub struct Frame<'mir, 'tcx, Tag: Provenance = AllocId, Extra = ()> { /// The locals are stored as `Option<Value>`s. /// `None` represents a local that is currently dead, while a live local /// can either directly contain `Scalar` or refer to some part of an `Allocation`. + /// + /// Do *not* access this directly; always go through the machine hook! pub locals: IndexVec<mir::Local, LocalState<'tcx, Tag>>, /// The span of the `tracing` crate is stored here. @@ -179,10 +181,6 @@ pub struct LocalState<'tcx, Tag: Provenance = AllocId> { pub enum LocalValue<Tag: Provenance = AllocId> { /// This local is not currently alive, and cannot be used at all. Dead, - /// This local is alive but not yet allocated. It cannot be read from or have its address taken, - /// and will be allocated on the first write. This is to support unsized locals, where we cannot - /// know their size in advance. - Unallocated, /// A normal, live local. /// Mostly for convenience, we re-use the `Operand` type here. /// This is an optimization over just always having a pointer here; @@ -196,12 +194,10 @@ impl<'tcx, Tag: Provenance + 'static> LocalState<'tcx, Tag> { /// /// Note: This may only be invoked from the `Machine::access_local` hook and not from /// anywhere else. You may be invalidating machine invariants if you do! - pub fn access(&self) -> InterpResult<'tcx, Operand<Tag>> { - match self.value { - LocalValue::Dead => throw_ub!(DeadLocal), - LocalValue::Unallocated => { - bug!("The type checker should prevent reading from a never-written local") - } + #[inline] + pub fn access(&self) -> InterpResult<'tcx, &Operand<Tag>> { + match &self.value { + LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? LocalValue::Live(val) => Ok(val), } } @@ -211,15 +207,11 @@ impl<'tcx, Tag: Provenance + 'static> LocalState<'tcx, Tag> { /// /// Note: This may only be invoked from the `Machine::access_local_mut` hook and not from /// anywhere else. You may be invalidating machine invariants if you do! - pub fn access_mut( - &mut self, - ) -> InterpResult<'tcx, Result<&mut LocalValue<Tag>, MemPlace<Tag>>> { - match self.value { - LocalValue::Dead => throw_ub!(DeadLocal), - LocalValue::Live(Operand::Indirect(mplace)) => Ok(Err(mplace)), - ref mut local @ (LocalValue::Live(Operand::Immediate(_)) | LocalValue::Unallocated) => { - Ok(Ok(local)) - } + #[inline] + pub fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Tag>> { + match &mut self.value { + LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? + LocalValue::Live(val) => Ok(val), } } } @@ -710,16 +702,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { })?; } - // Locals are initially unallocated. - let dummy = LocalState { value: LocalValue::Unallocated, layout: Cell::new(None) }; + // Most locals are initially dead. + let dummy = LocalState { value: LocalValue::Dead, layout: Cell::new(None) }; let mut locals = IndexVec::from_elem(dummy, &body.local_decls); - // Now mark those locals as dead that we do not want to initialize - // Mark locals that use `Storage*` annotations as dead on function entry. + // Now mark those locals as live that have no `Storage*` annotations. let always_live = always_live_locals(self.body()); for local in locals.indices() { - if !always_live.contains(local) { - locals[local].value = LocalValue::Dead; + if always_live.contains(local) { + locals[local].value = LocalValue::Live(Operand::Immediate(Immediate::Uninit)); } } // done @@ -791,7 +782,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if unwinding { "during unwinding" } else { "returning from function" } ); - // Sanity check `unwinding`. + // Check `unwinding`. assert_eq!( unwinding, match self.frame().loc { @@ -799,51 +790,61 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Err(_) => true, } ); - if unwinding && self.frame_idx() == 0 { throw_ub_format!("unwinding past the topmost frame of the stack"); } - let frame = - self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); - - if !unwinding { - let op = self.access_local(&frame, mir::RETURN_PLACE, None)?; - self.copy_op_transmute(&op, &frame.return_place)?; - trace!("{:?}", self.dump_place(*frame.return_place)); - } - - let return_to_block = frame.return_to_block; - - // Now where do we jump next? + // Copy return value. Must of course happen *before* we deallocate the locals. + let copy_ret_result = if !unwinding { + let op = self + .local_to_op(self.frame(), mir::RETURN_PLACE, None) + .expect("return place should always be live"); + let dest = self.frame().return_place; + let err = self.copy_op(&op, &dest, /*allow_transmute*/ true); + trace!("return value: {:?}", self.dump_place(*dest)); + // We delay actually short-circuiting on this error until *after* the stack frame is + // popped, since we want this error to be attributed to the caller, whose type defines + // this transmute. + err + } else { + Ok(()) + }; + // Cleanup: deallocate locals. // Usually we want to clean up (deallocate locals), but in a few rare cases we don't. - // In that case, we return early. We also avoid validation in that case, - // because this is CTFE and the final value will be thoroughly validated anyway. + // We do this while the frame is still on the stack, so errors point to the callee. + let return_to_block = self.frame().return_to_block; let cleanup = match return_to_block { StackPopCleanup::Goto { .. } => true, StackPopCleanup::Root { cleanup, .. } => cleanup, }; + if cleanup { + // We need to take the locals out, since we need to mutate while iterating. + let locals = mem::take(&mut self.frame_mut().locals); + for local in &locals { + self.deallocate_local(local.value)?; + } + } + + // All right, now it is time to actually pop the frame. + // Note that its locals are gone already, but that's fine. + let frame = + self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); + // Report error from return value copy, if any. + copy_ret_result?; + // If we are not doing cleanup, also skip everything else. if !cleanup { assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked"); assert!(!unwinding, "tried to skip cleanup during unwinding"); - // Leak the locals, skip validation, skip machine hook. + // Skip machine hook. return Ok(()); } - - trace!("locals: {:#?}", frame.locals); - - // Cleanup: deallocate all locals that are backed by an allocation. - for local in &frame.locals { - self.deallocate_local(local.value)?; - } - if M::after_stack_pop(self, frame, unwinding)? == StackPopJump::NoJump { // The hook already did everything. - // We want to skip the `info!` below, hence early return. return Ok(()); } + // Normal return, figure out where to jump. if unwinding { // Follow the unwind edge. @@ -874,7 +875,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert!(local != mir::RETURN_PLACE, "Cannot make return place live"); trace!("{:?} is now live", local); - let local_val = LocalValue::Unallocated; + let local_val = LocalValue::Live(Operand::Immediate(Immediate::Uninit)); // StorageLive expects the local to be dead, and marks it live. let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val); if !matches!(old, LocalValue::Dead) { @@ -977,12 +978,13 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug match self.ecx.stack()[frame].locals[local].value { LocalValue::Dead => write!(fmt, " is dead")?, - LocalValue::Unallocated => write!(fmt, " is unallocated")?, + LocalValue::Live(Operand::Immediate(Immediate::Uninit)) => { + write!(fmt, " is uninitialized")? + } LocalValue::Live(Operand::Indirect(mplace)) => { write!( fmt, - " by align({}){} ref {:?}:", - mplace.align.bytes(), + " by {} ref {:?}:", match mplace.meta { MemPlaceMeta::Meta(meta) => format!(" meta({:?})", meta), MemPlaceMeta::Poison | MemPlaceMeta::None => String::new(), @@ -1011,13 +1013,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug write!(fmt, ": {:?}", self.ecx.dump_allocs(allocs.into_iter().flatten().collect())) } Place::Ptr(mplace) => match mplace.ptr.provenance.and_then(Provenance::get_alloc_id) { - Some(alloc_id) => write!( - fmt, - "by align({}) ref {:?}: {:?}", - mplace.align.bytes(), - mplace.ptr, - self.ecx.dump_alloc(alloc_id) - ), + Some(alloc_id) => { + write!(fmt, "by ref {:?}: {:?}", mplace.ptr, self.ecx.dump_alloc(alloc_id)) + } ptr => write!(fmt, " integral by ref: {:?}", ptr), }, } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index e51c51cf45e..93b64d9d37a 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -174,7 +174,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let val = self.tcx.const_eval_global_id(self.param_env, gid, Some(self.tcx.span))?; let val = self.const_val_to_op(val, ty, Some(dest.layout))?; - self.copy_op(&val, dest)?; + self.copy_op(&val, dest, /*allow_transmute*/ false)?; } sym::ctpop @@ -217,7 +217,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::mul_with_overflow => BinOp::Mul, _ => bug!(), }; - self.binop_with_overflow(bin_op, &lhs, &rhs, dest)?; + self.binop_with_overflow( + bin_op, /*force_overflow_checks*/ true, &lhs, &rhs, dest, + )?; } sym::saturating_add | sym::saturating_sub => { let l = self.read_immediate(&args[0])?; @@ -392,7 +394,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } sym::transmute => { - self.copy_op_transmute(&args[0], dest)?; + self.copy_op(&args[0], dest, /*allow_transmute*/ true)?; } sym::assert_inhabited | sym::assert_zero_valid | sym::assert_uninit_valid => { let ty = instance.substs.type_at(0); @@ -459,7 +461,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let place = self.mplace_index(&dest, i)?; let value = if i == index { *elem } else { self.mplace_index(&input, i)?.into() }; - self.copy_op(&value, &place.into())?; + self.copy_op(&value, &place.into(), /*allow_transmute*/ false)?; } } sym::simd_extract => { @@ -471,11 +473,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { index, input_len ); - self.copy_op(&self.mplace_index(&input, index)?.into(), dest)?; + self.copy_op( + &self.mplace_index(&input, index)?.into(), + dest, + /*allow_transmute*/ false, + )?; } sym::likely | sym::unlikely | sym::black_box => { // These just return their argument - self.copy_op(&args[0], dest)?; + self.copy_op(&args[0], dest, /*allow_transmute*/ false)?; } sym::assume => { let cond = self.read_scalar(&args[0])?.check_init()?.to_bool()?; diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index c18ac84171d..b3461b414b6 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -14,8 +14,7 @@ use rustc_target::spec::abi::Abi; use super::{ AllocId, AllocRange, Allocation, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult, - LocalValue, MemPlace, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar, - StackPopUnwind, + MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar, StackPopUnwind, }; /// Data returned by Machine::stack_pop, @@ -144,6 +143,9 @@ pub trait Machine<'mir, 'tcx>: Sized { true } + /// Whether CheckedBinOp MIR statements should actually check for overflow. + fn checked_binop_checks_overflow(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; + /// Entry point for obtaining the MIR of anything that should get evaluated. /// So not just functions and shims, but also const/static initializers, anonymous /// constants, ... @@ -223,11 +225,13 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Since reading a ZST is not actually accessing memory or locals, this is never invoked /// for ZST reads. #[inline] - fn access_local( - _ecx: &InterpCx<'mir, 'tcx, Self>, - frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, + fn access_local<'a>( + frame: &'a Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, local: mir::Local, - ) -> InterpResult<'tcx, Operand<Self::PointerTag>> { + ) -> InterpResult<'tcx, &'a Operand<Self::PointerTag>> + where + 'tcx: 'mir, + { frame.locals[local].access() } @@ -239,7 +243,7 @@ pub trait Machine<'mir, 'tcx>: Sized { ecx: &'a mut InterpCx<'mir, 'tcx, Self>, frame: usize, local: mir::Local, - ) -> InterpResult<'tcx, Result<&'a mut LocalValue<Self::PointerTag>, MemPlace<Self::PointerTag>>> + ) -> InterpResult<'tcx, &'a mut Operand<Self::PointerTag>> where 'tcx: 'mir, { @@ -415,12 +419,14 @@ pub trait Machine<'mir, 'tcx>: Sized { } /// Called immediately after a stack frame got popped, but before jumping back to the caller. + /// The `locals` have already been destroyed! fn after_stack_pop( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _frame: Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, - _unwinding: bool, + unwinding: bool, ) -> InterpResult<'tcx, StackPopJump> { // By default, we do not support unwinding from panics + assert!(!unwinding); Ok(StackPopJump::Normal) } } @@ -469,6 +475,11 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] + fn checked_binop_checks_overflow(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { + true + } + + #[inline(always)] fn call_extra_fn( _ecx: &mut InterpCx<$mir, $tcx, Self>, fn_val: !, @@ -513,7 +524,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { _ecx: &InterpCx<$mir, $tcx, Self>, addr: u64, ) -> Pointer<Option<AllocId>> { - Pointer::new(None, Size::from_bytes(addr)) + Pointer::from_addr(addr) } #[inline(always)] @@ -523,7 +534,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { ) -> InterpResult<$tcx, Pointer<Option<AllocId>>> { // Allow these casts, but make the pointer not dereferenceable. // (I.e., they behave like transmutation.) - Ok(Pointer::new(None, Size::from_bytes(addr))) + Ok(Pointer::from_addr(addr)) } #[inline(always)] diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index c2a5b71b8f9..509fe576893 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -276,7 +276,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { kind: MemoryKind<M::MemoryKind>, ) -> InterpResult<'tcx> { let (alloc_id, offset, tag) = self.ptr_get_alloc_id(ptr)?; - trace!("deallocating: {}", alloc_id); + trace!("deallocating: {alloc_id:?}"); if offset.bytes() != 0 { throw_ub_format!( @@ -289,10 +289,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Deallocating global memory -- always an error return Err(match self.tcx.get_global_alloc(alloc_id) { Some(GlobalAlloc::Function(..)) => { - err_ub_format!("deallocating {}, which is a function", alloc_id) + err_ub_format!("deallocating {alloc_id:?}, which is a function") } Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => { - err_ub_format!("deallocating {}, which is static memory", alloc_id) + err_ub_format!("deallocating {alloc_id:?}, which is static memory") } None => err_ub!(PointerUseAfterFree(alloc_id)), } @@ -302,21 +302,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { debug!(?alloc); if alloc.mutability == Mutability::Not { - throw_ub_format!("deallocating immutable allocation {}", alloc_id); + throw_ub_format!("deallocating immutable allocation {alloc_id:?}"); } if alloc_kind != kind { throw_ub_format!( - "deallocating {}, which is {} memory, using {} deallocation operation", - alloc_id, - alloc_kind, - kind + "deallocating {alloc_id:?}, which is {alloc_kind} memory, using {kind} deallocation operation" ); } if let Some((size, align)) = old_size_and_align { if size != alloc.size() || align != alloc.align { throw_ub_format!( - "incorrect layout on deallocation: {} has size {} and alignment {}, but gave size {} and alignment {}", - alloc_id, + "incorrect layout on deallocation: {alloc_id:?} has size {} and alignment {}, but gave size {} and alignment {}", alloc.size().bytes(), alloc.align.bytes(), size.bytes(), @@ -815,7 +811,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, continue; } - write!(fmt, "{}", id)?; + write!(fmt, "{id:?}")?; match self.ecx.memory.alloc_map.get(id) { Some(&(kind, ref alloc)) => { // normal alloc @@ -859,25 +855,21 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, /// Reading and writing. impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> { + /// `range` is relative to this allocation reference, not the base of the allocation. pub fn write_scalar( &mut self, range: AllocRange, val: ScalarMaybeUninit<Tag>, ) -> InterpResult<'tcx> { let range = self.range.subrange(range); - debug!( - "write_scalar in {} at {:#x}, size {}: {:?}", - self.alloc_id, - range.start.bytes(), - range.size.bytes(), - val - ); + debug!("write_scalar at {:?}{range:?}: {val:?}", self.alloc_id); Ok(self .alloc .write_scalar(&self.tcx, range, val) .map_err(|e| e.to_interp_error(self.alloc_id))?) } + /// `offset` is relative to this allocation reference, not the base of the allocation. pub fn write_ptr_sized( &mut self, offset: Size, @@ -896,6 +888,7 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> { } impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> { + /// `range` is relative to this allocation reference, not the base of the allocation. pub fn read_scalar( &self, range: AllocRange, @@ -906,24 +899,16 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> { .alloc .read_scalar(&self.tcx, range, read_provenance) .map_err(|e| e.to_interp_error(self.alloc_id))?; - debug!( - "read_scalar in {} at {:#x}, size {}: {:?}", - self.alloc_id, - range.start.bytes(), - range.size.bytes(), - res - ); + debug!("read_scalar at {:?}{range:?}: {res:?}", self.alloc_id); Ok(res) } - pub fn read_integer( - &self, - offset: Size, - size: Size, - ) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> { - self.read_scalar(alloc_range(offset, size), /*read_provenance*/ false) + /// `range` is relative to this allocation reference, not the base of the allocation. + pub fn read_integer(&self, range: AllocRange) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> { + self.read_scalar(range, /*read_provenance*/ false) } + /// `offset` is relative to this allocation reference, not the base of the allocation. pub fn read_pointer(&self, offset: Size) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> { self.read_scalar( alloc_range(offset, self.tcx.data_layout().pointer_size), @@ -931,6 +916,7 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> { ) } + /// `range` is relative to this allocation reference, not the base of the allocation. pub fn check_bytes( &self, range: AllocRange, diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index e7a08e05275..805dcb38895 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -10,11 +10,11 @@ use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Printer}; use rustc_middle::ty::{ConstInt, DelaySpanBugEmitted, Ty}; use rustc_middle::{mir, ty}; -use rustc_target::abi::{self, Abi, HasDataLayout, Size, TagEncoding}; +use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, TagEncoding}; use rustc_target::abi::{VariantIdx, Variants}; use super::{ - alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, GlobalId, + alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit, }; @@ -28,8 +28,15 @@ use super::{ /// defined on `Immediate`, and do not have to work with a `Place`. #[derive(Copy, Clone, PartialEq, Eq, HashStable, Hash, Debug)] pub enum Immediate<Tag: Provenance = AllocId> { + /// A single scalar value (must have *initialized* `Scalar` ABI). + /// FIXME: we also currently often use this for ZST. + /// `ScalarMaybeUninit` should reject ZST, and we should use `Uninit` for them instead. Scalar(ScalarMaybeUninit<Tag>), + /// A pair of two scalar value (must have `ScalarPair` ABI where both fields are + /// `Scalar::Initialized`). ScalarPair(ScalarMaybeUninit<Tag>, ScalarMaybeUninit<Tag>), + /// A value of fully uninitialized memory. Can have and size and layout. + Uninit, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] @@ -75,6 +82,7 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> { match self { Immediate::Scalar(val) => val, Immediate::ScalarPair(..) => bug!("Got a scalar pair where a scalar was expected"), + Immediate::Uninit => ScalarMaybeUninit::Uninit, } } @@ -88,6 +96,7 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> { match self { Immediate::ScalarPair(val1, val2) => (val1, val2), Immediate::Scalar(..) => bug!("Got a scalar where a scalar pair was expected"), + Immediate::Uninit => (ScalarMaybeUninit::Uninit, ScalarMaybeUninit::Uninit), } } @@ -149,7 +158,10 @@ impl<Tag: Provenance> std::fmt::Display for ImmTy<'_, Tag> { } Immediate::ScalarPair(a, b) => { // FIXME(oli-obk): at least print tuples and slices nicely - write!(f, "({:x}, {:x}): {}", a, b, self.layout.ty,) + write!(f, "({:x}, {:x}): {}", a, b, self.layout.ty) + } + Immediate::Uninit => { + write!(f, "uninit: {}", self.layout.ty) } } }) @@ -177,10 +189,18 @@ pub enum Operand<Tag: Provenance = AllocId> { pub struct OpTy<'tcx, Tag: Provenance = AllocId> { op: Operand<Tag>, // Keep this private; it helps enforce invariants. pub layout: TyAndLayout<'tcx>, + /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct: + /// it needs to have a different alignment than the field type would usually have. + /// So we represent this here with a separate field that "overwrites" `layout.align`. + /// This means `layout.align` should never be used for an `OpTy`! + /// `None` means "alignment does not matter since this is a by-value operand" + /// (`Operand::Immediate`); this field is only relevant for `Operand::Indirect`. + /// Also CTFE ignores alignment anyway, so this is for Miri only. + pub align: Option<Align>, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(OpTy<'_>, 80); +rustc_data_structures::static_assert_size!(OpTy<'_>, 88); impl<'tcx, Tag: Provenance> std::ops::Deref for OpTy<'tcx, Tag> { type Target = Operand<Tag>; @@ -193,28 +213,28 @@ impl<'tcx, Tag: Provenance> std::ops::Deref for OpTy<'tcx, Tag> { impl<'tcx, Tag: Provenance> From<MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> { #[inline(always)] fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self { - OpTy { op: Operand::Indirect(*mplace), layout: mplace.layout } + OpTy { op: Operand::Indirect(*mplace), layout: mplace.layout, align: Some(mplace.align) } } } impl<'tcx, Tag: Provenance> From<&'_ MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> { #[inline(always)] fn from(mplace: &MPlaceTy<'tcx, Tag>) -> Self { - OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout } + OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) } } } impl<'tcx, Tag: Provenance> From<&'_ mut MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> { #[inline(always)] fn from(mplace: &mut MPlaceTy<'tcx, Tag>) -> Self { - OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout } + OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) } } } impl<'tcx, Tag: Provenance> From<ImmTy<'tcx, Tag>> for OpTy<'tcx, Tag> { #[inline(always)] fn from(val: ImmTy<'tcx, Tag>) -> Self { - OpTy { op: Operand::Immediate(val.imm), layout: val.layout } + OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None } } } @@ -298,9 +318,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { s.is_ptr() || (number_may_have_provenance && size == self.pointer_size()) }; if let Some(s) = scalar_layout { - //FIXME(#96185): let size = s.size(self); - //FIXME(#96185): assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size"); - let size = mplace.layout.size; //FIXME(#96185): remove this line + let size = s.size(self); + assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size"); let scalar = alloc.read_scalar(alloc_range(Size::ZERO, size), read_provenance(s, size))?; return Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout })); @@ -390,7 +409,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.scalar_to_ptr(self.read_scalar(op)?.check_init()?) } - // Turn the wide MPlace into a string (must already be dereferenced!) + /// Turn the wide MPlace into a string (must already be dereferenced!) pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx, &str> { let len = mplace.len(self)?; let bytes = self.read_bytes_ptr(mplace.ptr, Size::from_bytes(len))?; @@ -450,7 +469,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ), }; - Ok(OpTy { op: Operand::Immediate(field_val), layout: field_layout }) + Ok(OpTy { op: Operand::Immediate(field_val), layout: field_layout, align: None }) } pub fn operand_index( @@ -521,10 +540,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Will not access memory, instead an indirect `Operand` is returned. /// /// This is public because it is used by [priroda](https://github.com/oli-obk/priroda) to get an - /// OpTy from a local - pub fn access_local( + /// OpTy from a local. + pub fn local_to_op( &self, - frame: &super::Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, + frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, local: mir::Local, layout: Option<TyAndLayout<'tcx>>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { @@ -533,9 +552,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Do not read from ZST, they might not be initialized Operand::Immediate(Scalar::ZST.into()) } else { - M::access_local(&self, frame, local)? + *M::access_local(frame, local)? }; - Ok(OpTy { op, layout }) + Ok(OpTy { op, layout, align: Some(layout.align.abi) }) } /// Every place can be read from, so we can turn them into an operand. @@ -549,10 +568,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let op = match **place { Place::Ptr(mplace) => Operand::Indirect(mplace), Place::Local { frame, local } => { - *self.access_local(&self.stack()[frame], local, None)? + *self.local_to_op(&self.stack()[frame], local, None)? } }; - Ok(OpTy { op, layout: place.layout }) + Ok(OpTy { op, layout: place.layout, align: Some(place.align) }) } /// Evaluate a place with the goal of reading from it. This lets us sometimes @@ -566,7 +585,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // here is not the entire place. let layout = if place.projection.is_empty() { layout } else { None }; - let base_op = self.access_local(self.frame(), place.local, layout)?; + let base_op = self.local_to_op(self.frame(), place.local, layout)?; let op = place .projection @@ -603,11 +622,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Constant(ref constant) => { let val = self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal)?; + // This can still fail: // * During ConstProp, with `TooGeneric` or since the `required_consts` were not all // checked yet. // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail. - self.mir_const_to_op(&val, layout)? } }; @@ -683,7 +702,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // We rely on mutability being set correctly in that allocation to prevent writes // where none should happen. let ptr = self.global_base_pointer(Pointer::new(id, offset))?; - Operand::Indirect(MemPlace::from_ptr(ptr.into(), layout.align.abi)) + Operand::Indirect(MemPlace::from_ptr(ptr.into())) } ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x)?.into()), ConstValue::Slice { data, start, end } => { @@ -700,7 +719,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { )) } }; - Ok(OpTy { op, layout }) + Ok(OpTy { op, layout, align: Some(layout.align.abi) }) } /// Read discriminant, return the runtime value as well as the variant index. diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 85ee88e9e47..f0c113376ea 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -5,15 +5,20 @@ use rustc_middle::mir; use rustc_middle::mir::interpret::{InterpResult, Scalar}; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, FloatTy, Ty}; +use rustc_target::abi::Abi; use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy}; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Applies the binary operation `op` to the two operands and writes a tuple of the result /// and a boolean signifying the potential overflow to the destination. + /// + /// `force_overflow_checks` indicates whether overflow checks should be done even when + /// `tcx.sess.overflow_checks()` is `false`. pub fn binop_with_overflow( &mut self, op: mir::BinOp, + force_overflow_checks: bool, left: &ImmTy<'tcx, M::PointerTag>, right: &ImmTy<'tcx, M::PointerTag>, dest: &PlaceTy<'tcx, M::PointerTag>, @@ -25,8 +30,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { "type mismatch for result of {:?}", op, ); - let val = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into()); - self.write_immediate(val, dest) + // As per https://github.com/rust-lang/rust/pull/98738, we always return `false` in the 2nd + // component when overflow checking is disabled. + let overflowed = + overflowed && (force_overflow_checks || M::checked_binop_checks_overflow(self)); + // Write the result to `dest`. + if let Abi::ScalarPair(..) = dest.layout.abi { + // We can use the optimized path and avoid `place_field` (which might do + // `force_allocation`). + let pair = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into()); + self.write_immediate(pair, dest)?; + } else { + assert!(self.tcx.sess.opts.debugging_opts.randomize_layout); + // With randomized layout, `(int, bool)` might cease to be a `ScalarPair`, so we have to + // do a component-wise write here. This code path is slower than the above because + // `place_field` will have to `force_allocate` locals here. + let val_field = self.place_field(&dest, 0)?; + self.write_scalar(val, &val_field)?; + let overflowed_field = self.place_field(&dest, 1)?; + self.write_scalar(Scalar::from_bool(overflowed), &overflowed_field)?; + } + Ok(()) } /// Applies the binary operation `op` to the arguments and writes the result to the diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 337fcd28c66..57ecad07b42 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -10,13 +10,14 @@ use rustc_macros::HashStable; use rustc_middle::mir; use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout}; use rustc_middle::ty::{self, Ty}; -use rustc_target::abi::{Abi, Align, FieldsShape, TagEncoding}; -use rustc_target::abi::{HasDataLayout, Size, VariantIdx, Variants}; +use rustc_target::abi::{ + Abi, Align, FieldsShape, HasDataLayout, Size, TagEncoding, VariantIdx, Variants, +}; use super::{ alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg, - ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, - Operand, Pointer, Provenance, Scalar, ScalarMaybeUninit, + ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand, + Pointer, Provenance, Scalar, ScalarMaybeUninit, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)] @@ -57,7 +58,6 @@ impl<Tag: Provenance> MemPlaceMeta<Tag> { pub struct MemPlace<Tag: Provenance = AllocId> { /// The pointer can be a pure integer, with the `None` tag. pub ptr: Pointer<Option<Tag>>, - pub align: Align, /// Metadata for unsized places. Interpretation is up to the type. /// Must not be present for sized types, but can be missing for unsized types /// (e.g., `extern type`). @@ -65,7 +65,7 @@ pub struct MemPlace<Tag: Provenance = AllocId> { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(MemPlace, 48); +rustc_data_structures::static_assert_size!(MemPlace, 40); #[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)] pub enum Place<Tag: Provenance = AllocId> { @@ -78,12 +78,17 @@ pub enum Place<Tag: Provenance = AllocId> { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Place, 56); +rustc_data_structures::static_assert_size!(Place, 48); #[derive(Copy, Clone, Debug)] pub struct PlaceTy<'tcx, Tag: Provenance = AllocId> { place: Place<Tag>, // Keep this private; it helps enforce invariants. pub layout: TyAndLayout<'tcx>, + /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct: + /// it needs to have a different alignment than the field type would usually have. + /// So we represent this here with a separate field that "overwrites" `layout.align`. + /// This means `layout.align` should never be used for a `PlaceTy`! + pub align: Align, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] @@ -102,6 +107,11 @@ impl<'tcx, Tag: Provenance> std::ops::Deref for PlaceTy<'tcx, Tag> { pub struct MPlaceTy<'tcx, Tag: Provenance = AllocId> { mplace: MemPlace<Tag>, pub layout: TyAndLayout<'tcx>, + /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct: + /// it needs to have a different alignment than the field type would usually have. + /// So we represent this here with a separate field that "overwrites" `layout.align`. + /// This means `layout.align` should never be used for a `MPlaceTy`! + pub align: Align, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] @@ -118,28 +128,28 @@ impl<'tcx, Tag: Provenance> std::ops::Deref for MPlaceTy<'tcx, Tag> { impl<'tcx, Tag: Provenance> From<MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> { #[inline(always)] fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self { - PlaceTy { place: Place::Ptr(*mplace), layout: mplace.layout } + PlaceTy { place: Place::Ptr(*mplace), layout: mplace.layout, align: mplace.align } } } impl<'tcx, Tag: Provenance> From<&'_ MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> { #[inline(always)] fn from(mplace: &MPlaceTy<'tcx, Tag>) -> Self { - PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout } + PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align } } } impl<'tcx, Tag: Provenance> From<&'_ mut MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> { #[inline(always)] fn from(mplace: &mut MPlaceTy<'tcx, Tag>) -> Self { - PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout } + PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align } } } impl<Tag: Provenance> MemPlace<Tag> { #[inline(always)] - pub fn from_ptr(ptr: Pointer<Option<Tag>>, align: Align) -> Self { - MemPlace { ptr, align, meta: MemPlaceMeta::None } + pub fn from_ptr(ptr: Pointer<Option<Tag>>) -> Self { + MemPlace { ptr, meta: MemPlaceMeta::None } } /// Adjust the provenance of the main pointer (metadata is unaffected). @@ -170,11 +180,19 @@ impl<Tag: Provenance> MemPlace<Tag> { meta: MemPlaceMeta<Tag>, cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { - Ok(MemPlace { - ptr: self.ptr.offset(offset, cx)?, - align: self.align.restrict_for_offset(offset), - meta, - }) + Ok(MemPlace { ptr: self.ptr.offset(offset, cx)?, meta }) + } +} + +impl<Tag: Provenance> Place<Tag> { + /// Asserts that this points to some local variable. + /// Returns the frame idx and the variable idx. + #[inline] + pub fn assert_local(&self) -> (usize, mir::Local) { + match self { + Place::Local { frame, local } => (*frame, *local), + _ => bug!("assert_local: expected Place::Local, got {:?}", self), + } } } @@ -183,9 +201,9 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { #[inline] pub fn dangling(layout: TyAndLayout<'tcx>) -> Self { let align = layout.align.abi; - let ptr = Pointer::new(None, Size::from_bytes(align.bytes())); // no provenance, absolute address + let ptr = Pointer::from_addr(align.bytes()); // no provenance, absolute address // `Poison` this to make sure that the pointer value `ptr` is never observable by the program. - MPlaceTy { mplace: MemPlace { ptr, align, meta: MemPlaceMeta::Poison }, layout } + MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::Poison }, layout, align } } #[inline] @@ -196,12 +214,16 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { - Ok(MPlaceTy { mplace: self.mplace.offset(offset, meta, cx)?, layout }) + Ok(MPlaceTy { + mplace: self.mplace.offset(offset, meta, cx)?, + align: self.align.restrict_for_offset(offset), + layout, + }) } #[inline] pub fn from_aligned_ptr(ptr: Pointer<Option<Tag>>, layout: TyAndLayout<'tcx>) -> Self { - MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align.abi), layout } + MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi } } #[inline] @@ -210,10 +232,10 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { layout: TyAndLayout<'tcx>, meta: MemPlaceMeta<Tag>, ) -> Self { - let mut mplace = MemPlace::from_ptr(ptr, layout.align.abi); + let mut mplace = MemPlace::from_ptr(ptr); mplace.meta = meta; - MPlaceTy { mplace, layout } + MPlaceTy { mplace, layout, align: layout.align.abi } } #[inline] @@ -250,7 +272,9 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> { /// read from the resulting mplace, not to get its address back. pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Tag>, ImmTy<'tcx, Tag>> { match **self { - Operand::Indirect(mplace) => Ok(MPlaceTy { mplace, layout: self.layout }), + Operand::Indirect(mplace) => { + Ok(MPlaceTy { mplace, layout: self.layout, align: self.align.unwrap() }) + } Operand::Immediate(_) if self.layout.is_zst() => Ok(MPlaceTy::dangling(self.layout)), Operand::Immediate(imm) => Err(ImmTy::from_immediate(imm, self.layout)), } @@ -264,20 +288,19 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> { } } -impl<Tag: Provenance> Place<Tag> { +impl<'tcx, Tag: Provenance> PlaceTy<'tcx, Tag> { + /// A place is either an mplace or some local. #[inline] - pub fn assert_mem_place(self) -> MemPlace<Tag> { - match self { - Place::Ptr(mplace) => mplace, - _ => bug!("assert_mem_place: expected Place::Ptr, got {:?}", self), + pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Tag>, (usize, mir::Local)> { + match **self { + Place::Ptr(mplace) => Ok(MPlaceTy { mplace, layout: self.layout, align: self.align }), + Place::Local { frame, local } => Err((frame, local)), } } -} -impl<'tcx, Tag: Provenance> PlaceTy<'tcx, Tag> { #[inline] - pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> { - MPlaceTy { mplace: self.place.assert_mem_place(), layout: self.layout } + pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Tag> { + self.try_as_mplace().unwrap() } } @@ -304,18 +327,13 @@ where let (ptr, meta) = match **val { Immediate::Scalar(ptr) => (ptr, MemPlaceMeta::None), Immediate::ScalarPair(ptr, meta) => (ptr, MemPlaceMeta::Meta(meta.check_init()?)), + Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)), }; - let mplace = MemPlace { - ptr: self.scalar_to_ptr(ptr.check_init()?)?, - // We could use the run-time alignment here. For now, we do not, because - // the point of tracking the alignment here is to make sure that the *static* - // alignment information emitted with the loads is correct. The run-time - // alignment can only be more restrictive. - align: layout.align.abi, - meta, - }; - Ok(MPlaceTy { mplace, layout }) + let mplace = MemPlace { ptr: self.scalar_to_ptr(ptr.check_init()?)?, meta }; + // When deref'ing a pointer, the *static* alignment given by the type is what matters. + let align = layout.align.abi; + Ok(MPlaceTy { mplace, layout, align }) } /// Take an operand, representing a pointer, and dereference it to a place -- that @@ -368,7 +386,7 @@ where let (size, align) = self .size_and_align_of_mplace(&mplace)? .unwrap_or((mplace.layout.size, mplace.layout.align.abi)); - assert!(mplace.mplace.align <= align, "dynamic alignment less strict than static one?"); + assert!(mplace.align <= align, "dynamic alignment less strict than static one?"); let align = M::enforce_alignment(self).then_some(align); self.check_ptr_access_align(mplace.ptr, size, align.unwrap_or(Align::ONE), msg)?; Ok(()) @@ -533,7 +551,7 @@ where Index(local) => { let layout = self.layout_of(self.tcx.types.usize)?; - let n = self.access_local(self.frame(), local, Some(layout))?; + let n = self.local_to_op(self.frame(), local, Some(layout))?; let n = self.read_scalar(&n)?; let n = n.to_machine_usize(self)?; self.mplace_index(base, n)? @@ -608,11 +626,9 @@ where variant: VariantIdx, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { // Downcast just changes the layout - Ok(match base.place { - Place::Ptr(mplace) => { - self.mplace_downcast(&MPlaceTy { mplace, layout: base.layout }, variant)?.into() - } - Place::Local { .. } => { + Ok(match base.try_as_mplace() { + Ok(mplace) => self.mplace_downcast(&mplace, variant)?.into(), + Err(..) => { let layout = base.layout.for_variant(self, variant); PlaceTy { layout, ..*base } } @@ -649,6 +665,16 @@ where self.mplace_to_simd(&mplace) } + pub fn local_to_place( + &self, + frame: usize, + local: mir::Local, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + let layout = self.layout_of_local(&self.stack()[frame], local, None)?; + let place = Place::Local { frame, local }; + Ok(PlaceTy { place, layout, align: layout.align.abi }) + } + /// Computes a place. You should only use this if you intend to write into this /// place; for reading, a more efficient alternative is `eval_place_to_op`. #[instrument(skip(self), level = "debug")] @@ -656,11 +682,7 @@ where &mut self, place: mir::Place<'tcx>, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - let mut place_ty = PlaceTy { - // This works even for dead/uninitialized locals; we check further when writing - place: Place::Local { frame: self.frame_idx(), local: place.local }, - layout: self.layout_of_local(self.frame(), place.local, None)?, - }; + let mut place_ty = self.local_to_place(self.frame_idx(), place.local)?; for elem in place.projection.iter() { place_ty = self.place_projection(&place_ty, &elem)? @@ -668,14 +690,19 @@ where trace!("{:?}", self.dump_place(place_ty.place)); // Sanity-check the type we ended up with. - debug_assert!(mir_assign_valid_types( - *self.tcx, - self.param_env, - self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( - place.ty(&self.frame().body.local_decls, *self.tcx).ty - )?)?, - place_ty.layout, - )); + debug_assert!( + mir_assign_valid_types( + *self.tcx, + self.param_env, + self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( + place.ty(&self.frame().body.local_decls, *self.tcx).ty + )?)?, + place_ty.layout, + ), + "eval_place of a MIR place with type {:?} produced an interpret place with type {:?}", + place.ty(&self.frame().body.local_decls, *self.tcx).ty, + place_ty.layout.ty, + ); Ok(place_ty) } @@ -733,32 +760,33 @@ where let mplace = match dest.place { Place::Local { frame, local } => { match M::access_local_mut(self, frame, local)? { - Ok(local) => { + Operand::Immediate(local) => { // Local can be updated in-place. - *local = LocalValue::Live(Operand::Immediate(src)); + *local = src; return Ok(()); } - Err(mplace) => { + Operand::Indirect(mplace) => { // The local is in memory, go on below. - mplace + *mplace } } } Place::Ptr(mplace) => mplace, // already referring to memory }; - let dest = MPlaceTy { mplace, layout: dest.layout }; // This is already in memory, write there. - self.write_immediate_to_mplace_no_validate(src, &dest) + self.write_immediate_to_mplace_no_validate(src, dest.layout, dest.align, mplace) } /// Write an immediate to memory. /// If you use this you are responsible for validating that things got copied at the - /// right type. + /// right layout. fn write_immediate_to_mplace_no_validate( &mut self, value: Immediate<M::PointerTag>, - dest: &MPlaceTy<'tcx, M::PointerTag>, + layout: TyAndLayout<'tcx>, + align: Align, + dest: MemPlace<M::PointerTag>, ) -> InterpResult<'tcx> { // Note that it is really important that the type here is the right one, and matches the // type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here @@ -766,31 +794,30 @@ where // wrong type. let tcx = *self.tcx; - let Some(mut alloc) = self.get_place_alloc_mut(dest)? else { + let Some(mut alloc) = self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout, align })? else { // zero-sized access return Ok(()); }; match value { Immediate::Scalar(scalar) => { - let Abi::Scalar(s) = dest.layout.abi else { span_bug!( + let Abi::Scalar(s) = layout.abi else { span_bug!( self.cur_span(), - "write_immediate_to_mplace: invalid Scalar layout: {:#?}", - dest.layout + "write_immediate_to_mplace: invalid Scalar layout: {layout:#?}", ) }; let size = s.size(&tcx); - //FIXME(#96185): assert_eq!(dest.layout.size, size, "abi::Scalar size does not match layout size"); + assert_eq!(size, layout.size, "abi::Scalar size does not match layout size"); alloc.write_scalar(alloc_range(Size::ZERO, size), scalar) } Immediate::ScalarPair(a_val, b_val) => { // We checked `ptr_align` above, so all fields will have the alignment they need. // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`, // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. - let Abi::ScalarPair(a, b) = dest.layout.abi else { span_bug!( + let Abi::ScalarPair(a, b) = layout.abi else { span_bug!( self.cur_span(), "write_immediate_to_mplace: invalid ScalarPair layout: {:#?}", - dest.layout + layout ) }; let (a_size, b_size) = (a.size(&tcx), b.size(&tcx)); @@ -804,33 +831,22 @@ where alloc.write_scalar(alloc_range(Size::ZERO, a_size), a_val)?; alloc.write_scalar(alloc_range(b_offset, b_size), b_val) } + Immediate::Uninit => alloc.write_uninit(), } } pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { - let mplace = match dest.place { - Place::Ptr(mplace) => MPlaceTy { mplace, layout: dest.layout }, - Place::Local { frame, local } => { + let mplace = match dest.try_as_mplace() { + Ok(mplace) => mplace, + Err((frame, local)) => { match M::access_local_mut(self, frame, local)? { - Ok(local) => match dest.layout.abi { - Abi::Scalar(_) => { - *local = LocalValue::Live(Operand::Immediate(Immediate::Scalar( - ScalarMaybeUninit::Uninit, - ))); - return Ok(()); - } - Abi::ScalarPair(..) => { - *local = LocalValue::Live(Operand::Immediate(Immediate::ScalarPair( - ScalarMaybeUninit::Uninit, - ScalarMaybeUninit::Uninit, - ))); - return Ok(()); - } - _ => self.force_allocation(dest)?, - }, - Err(mplace) => { + Operand::Immediate(local) => { + *local = Immediate::Uninit; + return Ok(()); + } + Operand::Indirect(mplace) => { // The local is in memory, go on below. - MPlaceTy { mplace, layout: dest.layout } + MPlaceTy { mplace: *mplace, layout: dest.layout, align: dest.align } } } } @@ -843,16 +859,17 @@ where Ok(()) } - /// Copies the data from an operand to a place. This does not support transmuting! - /// Use `copy_op_transmute` if the layouts could disagree. + /// Copies the data from an operand to a place. + /// `allow_transmute` indicates whether the layouts may disagree. #[inline(always)] #[instrument(skip(self), level = "debug")] pub fn copy_op( &mut self, src: &OpTy<'tcx, M::PointerTag>, dest: &PlaceTy<'tcx, M::PointerTag>, + allow_transmute: bool, ) -> InterpResult<'tcx> { - self.copy_op_no_validate(src, dest)?; + self.copy_op_no_validate(src, dest, allow_transmute)?; if M::enforce_validity(self) { // Data got changed, better make sure it matches the type! @@ -862,8 +879,8 @@ where Ok(()) } - /// Copies the data from an operand to a place. This does not support transmuting! - /// Use `copy_op_transmute` if the layouts could disagree. + /// Copies the data from an operand to a place. + /// `allow_transmute` indicates whether the layouts may disagree. /// Also, if you use this you are responsible for validating that things get copied at the /// right type. #[instrument(skip(self), level = "debug")] @@ -871,10 +888,13 @@ where &mut self, src: &OpTy<'tcx, M::PointerTag>, dest: &PlaceTy<'tcx, M::PointerTag>, + allow_transmute: bool, ) -> InterpResult<'tcx> { // We do NOT compare the types for equality, because well-typed code can // actually "transmute" `&mut T` to `&T` in an assignment without a cast. - if !mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout) { + let layout_compat = + mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout); + if !allow_transmute && !layout_compat { span_bug!( self.cur_span(), "type mismatch when copying!\nsrc: {:?},\ndest: {:?}", @@ -883,100 +903,68 @@ where ); } - // Let us see if the layout is simple so we take a shortcut, avoid force_allocation. + // Let us see if the layout is simple so we take a shortcut, + // avoid force_allocation. let src = match self.read_immediate_raw(src, /*force*/ false)? { Ok(src_val) => { assert!(!src.layout.is_unsized(), "cannot have unsized immediates"); + assert!( + !dest.layout.is_unsized(), + "the src is sized, so the dest must also be sized" + ); + assert_eq!(src.layout.size, dest.layout.size); // Yay, we got a value that we can write directly. - return self.write_immediate_no_validate(*src_val, dest); + return if layout_compat { + self.write_immediate_no_validate(*src_val, dest) + } else { + // This is tricky. The problematic case is `ScalarPair`: the `src_val` was + // loaded using the offsets defined by `src.layout`. When we put this back into + // the destination, we have to use the same offsets! So (a) we make sure we + // write back to memory, and (b) we use `dest` *with the source layout*. + let dest_mem = self.force_allocation(dest)?; + self.write_immediate_to_mplace_no_validate( + *src_val, + src.layout, + dest_mem.align, + *dest_mem, + ) + }; } Err(mplace) => mplace, }; // Slow path, this does not fit into an immediate. Just memcpy. trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); - // This interprets `src.meta` with the `dest` local's layout, if an unsized local - // is being initialized! - let (dest, size) = self.force_allocation_maybe_sized(dest, src.meta)?; - let size = size.unwrap_or_else(|| { - assert!( - !dest.layout.is_unsized(), - "Cannot copy into already initialized unsized place" - ); - dest.layout.size - }); - assert_eq!(src.meta, dest.meta, "Can only copy between equally-sized instances"); - - self.mem_copy(src.ptr, src.align, dest.ptr, dest.align, size, /*nonoverlapping*/ false) - } - - /// Copies the data from an operand to a place. The layouts may disagree, but they must - /// have the same size. - pub fn copy_op_transmute( - &mut self, - src: &OpTy<'tcx, M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - if mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout) { - // Fast path: Just use normal `copy_op` - return self.copy_op(src, dest); - } - // We still require the sizes to match. - if src.layout.size != dest.layout.size { - span_bug!( - self.cur_span(), - "size-changing transmute, should have been caught by transmute checking: {:#?}\ndest: {:#?}", - src, - dest - ); - } - // Unsized copies rely on interpreting `src.meta` with `dest.layout`, we want - // to avoid that here. - assert!( - !src.layout.is_unsized() && !dest.layout.is_unsized(), - "Cannot transmute unsized data" - ); - - // The hard case is `ScalarPair`. `src` is already read from memory in this case, - // using `src.layout` to figure out which bytes to use for the 1st and 2nd field. - // We have to write them to `dest` at the offsets they were *read at*, which is - // not necessarily the same as the offsets in `dest.layout`! - // Hence we do the copy with the source layout on both sides. We also make sure to write - // into memory, because if `dest` is a local we would not even have a way to write - // at the `src` offsets; the fact that we came from a different layout would - // just be lost. - let dest = self.force_allocation(dest)?; - self.copy_op_no_validate( - src, - &PlaceTy::from(MPlaceTy { mplace: *dest, layout: src.layout }), - )?; - - if M::enforce_validity(self) { - // Data got changed, better make sure it matches the type! - self.validate_operand(&dest.into())?; + let dest = self.force_allocation(&dest)?; + let Some((dest_size, _)) = self.size_and_align_of_mplace(&dest)? else { + span_bug!(self.cur_span(), "copy_op needs (dynamically) sized values") + }; + if cfg!(debug_assertions) { + let src_size = self.size_and_align_of_mplace(&src)?.unwrap().0; + assert_eq!(src_size, dest_size, "Cannot copy differently-sized data"); + } else { + // As a cheap approximation, we compare the fixed parts of the size. + assert_eq!(src.layout.size, dest.layout.size); } - Ok(()) + self.mem_copy( + src.ptr, src.align, dest.ptr, dest.align, dest_size, /*nonoverlapping*/ false, + ) } /// Ensures that a place is in memory, and returns where it is. /// If the place currently refers to a local that doesn't yet have a matching allocation, /// create such an allocation. /// This is essentially `force_to_memplace`. - /// - /// This supports unsized types and returns the computed size to avoid some - /// redundant computation when copying; use `force_allocation` for a simpler, sized-only - /// version. #[instrument(skip(self), level = "debug")] - pub fn force_allocation_maybe_sized( + pub fn force_allocation( &mut self, place: &PlaceTy<'tcx, M::PointerTag>, - meta: MemPlaceMeta<M::PointerTag>, - ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, Option<Size>)> { - let (mplace, size) = match place.place { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + let mplace = match place.place { Place::Local { frame, local } => { match M::access_local_mut(self, frame, local)? { - Ok(&mut local_val) => { + &mut Operand::Immediate(local_val) => { // We need to make an allocation. // We need the layout of the local. We can NOT use the layout we got, @@ -984,40 +972,34 @@ where // that has different alignment than the outer field. let local_layout = self.layout_of_local(&self.stack()[frame], local, None)?; - // We also need to support unsized types, and hence cannot use `allocate`. - let (size, align) = self - .size_and_align_of(&meta, &local_layout)? - .expect("Cannot allocate for non-dyn-sized type"); - let ptr = self.allocate_ptr(size, align, MemoryKind::Stack)?; - let mplace = MemPlace { ptr: ptr.into(), align, meta }; - if let LocalValue::Live(Operand::Immediate(value)) = local_val { - // Preserve old value. + if local_layout.is_unsized() { + throw_unsup_format!("unsized locals are not supported"); + } + let mplace = *self.allocate(local_layout, MemoryKind::Stack)?; + if !matches!(local_val, Immediate::Uninit) { + // Preserve old value. (As an optimization, we can skip this if it was uninit.) // We don't have to validate as we can assume the local // was already valid for its type. - let mplace = MPlaceTy { mplace, layout: local_layout }; - self.write_immediate_to_mplace_no_validate(value, &mplace)?; + self.write_immediate_to_mplace_no_validate( + local_val, + local_layout, + local_layout.align.abi, + mplace, + )?; } // Now we can call `access_mut` again, asserting it goes well, // and actually overwrite things. - *M::access_local_mut(self, frame, local).unwrap().unwrap() = - LocalValue::Live(Operand::Indirect(mplace)); - (mplace, Some(size)) + *M::access_local_mut(self, frame, local).unwrap() = + Operand::Indirect(mplace); + mplace } - Err(mplace) => (mplace, None), // this already was an indirect local + &mut Operand::Indirect(mplace) => mplace, // this already was an indirect local } } - Place::Ptr(mplace) => (mplace, None), + Place::Ptr(mplace) => mplace, }; // Return with the original layout, so that the caller can go on - Ok((MPlaceTy { mplace, layout: place.layout }, size)) - } - - #[inline(always)] - pub fn force_allocation( - &mut self, - place: &PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - Ok(self.force_allocation_maybe_sized(place, MemPlaceMeta::None)?.0) + Ok(MPlaceTy { mplace, layout: place.layout, align: place.align }) } pub fn allocate( @@ -1025,6 +1007,7 @@ where layout: TyAndLayout<'tcx>, kind: MemoryKind<M::MemoryKind>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + assert!(!layout.is_unsized()); let ptr = self.allocate_ptr(layout.size, layout.align.abi, kind)?; Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout)) } @@ -1038,15 +1021,14 @@ where ) -> MPlaceTy<'tcx, M::PointerTag> { let ptr = self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl); let meta = Scalar::from_machine_usize(u64::try_from(str.len()).unwrap(), self); - let mplace = - MemPlace { ptr: ptr.into(), align: Align::ONE, meta: MemPlaceMeta::Meta(meta) }; + let mplace = MemPlace { ptr: ptr.into(), meta: MemPlaceMeta::Meta(meta) }; let ty = self.tcx.mk_ref( self.tcx.lifetimes.re_static, ty::TypeAndMut { ty: self.tcx.types.str_, mutbl }, ); let layout = self.layout_of(ty).unwrap(); - MPlaceTy { mplace, layout } + MPlaceTy { mplace, layout, align: layout.align.abi } } /// Writes the discriminant of the given variant. @@ -1166,7 +1148,11 @@ where assert_eq!(align, layout.align.abi); } - let mplace = MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..**mplace }, layout }; + let mplace = MPlaceTy { + mplace: MemPlace { meta: MemPlaceMeta::None, ..**mplace }, + layout, + align: layout.align.abi, + }; Ok((instance, mplace)) } } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 98f69456e49..240910c08b2 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -169,7 +169,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Use(ref operand) => { // Avoid recomputing the layout let op = self.eval_operand(operand, Some(dest.layout))?; - self.copy_op(&op, &dest)?; + self.copy_op(&op, &dest, /*allow_transmute*/ false)?; } BinaryOp(bin_op, box (ref left, ref right)) => { @@ -185,7 +185,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let left = self.read_immediate(&self.eval_operand(left, None)?)?; let layout = binop_right_homogeneous(bin_op).then_some(left.layout); let right = self.read_immediate(&self.eval_operand(right, layout)?)?; - self.binop_with_overflow(bin_op, &left, &right, &dest)?; + self.binop_with_overflow( + bin_op, /*force_overflow_checks*/ false, &left, &right, &dest, + )?; } UnaryOp(un_op, ref operand) => { @@ -202,7 +204,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { for (field_index, operand) in operands.iter().enumerate() { let op = self.eval_operand(operand, None)?; let field_dest = self.place_field(&dest, field_index)?; - self.copy_op(&op, &field_dest)?; + self.copy_op(&op, &field_dest, /*allow_transmute*/ false)?; } } @@ -218,7 +220,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } else { // Write the src to the first element. let first = self.mplace_field(&dest, 0)?; - self.copy_op(&src, &first.into())?; + self.copy_op(&src, &first.into(), /*allow_transmute*/ false)?; // This is performance-sensitive code for big static/const arrays! So we // avoid writing each operand individually and instead just make many copies diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 57d06b48ca4..515cc222dc6 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -12,8 +12,8 @@ use rustc_target::abi::call::{ArgAbi, ArgAttribute, ArgAttributes, FnAbi, PassMo use rustc_target::spec::abi::Abi; use super::{ - FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Scalar, - StackPopCleanup, StackPopUnwind, + FnVal, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemoryKind, OpTy, Operand, + PlaceTy, Scalar, StackPopCleanup, StackPopUnwind, }; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { @@ -185,11 +185,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // No question return true; } + if caller_abi.layout.is_unsized() || callee_abi.layout.is_unsized() { + // No, no, no. We require the types to *exactly* match for unsized arguments. If + // these are somehow unsized "in a different way" (say, `dyn Trait` vs `[i32]`), + // then who knows what happens. + return false; + } if caller_abi.layout.size != callee_abi.layout.size || caller_abi.layout.align.abi != callee_abi.layout.align.abi { // This cannot go well... - // FIXME: What about unsized types? return false; } // The rest *should* be okay, but we are extra conservative. @@ -287,11 +292,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { caller_arg.layout.ty ) } + // Special handling for unsized parameters. + if caller_arg.layout.is_unsized() { + // `check_argument_compat` ensures that both have the same type, so we know they will use the metadata the same way. + assert_eq!(caller_arg.layout.ty, callee_arg.layout.ty); + // We have to properly pre-allocate the memory for the callee. + // So let's tear down some wrappers. + // This all has to be in memory, there are no immediate unsized values. + let src = caller_arg.assert_mem_place(); + // The destination cannot be one of these "spread args". + let (dest_frame, dest_local) = callee_arg.assert_local(); + // We are just initializing things, so there can't be anything here yet. + assert!(matches!( + *self.local_to_op(&self.stack()[dest_frame], dest_local, None)?, + Operand::Immediate(Immediate::Uninit) + )); + // Allocate enough memory to hold `src`. + let Some((size, align)) = self.size_and_align_of_mplace(&src)? else { + span_bug!(self.cur_span(), "unsized fn arg with `extern` type tail should not be allowed") + }; + let ptr = self.allocate_ptr(size, align, MemoryKind::Stack)?; + let dest_place = + MPlaceTy::from_aligned_ptr_with_meta(ptr.into(), callee_arg.layout, src.meta); + // Update the local to be that new place. + *M::access_local_mut(self, dest_frame, dest_local)? = Operand::Indirect(*dest_place); + } // We allow some transmutes here. // FIXME: Depending on the PassMode, this should reset some padding to uninitialized. (This // is true for all `copy_op`, but there are a lot of special cases for argument passing // specifically.) - self.copy_op_transmute(&caller_arg, callee_arg) + self.copy_op(&caller_arg, callee_arg, /*allow_transmute*/ true) } /// Call this function -- pushing the stack frame and initializing the arguments. diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 9c48f3e8337..22c23df7b1a 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -1,6 +1,6 @@ use std::convert::TryFrom; -use rustc_middle::mir::interpret::{InterpResult, Pointer, PointerArithmetic}; +use rustc_middle::mir::interpret::{alloc_range, InterpResult, Pointer, PointerArithmetic}; use rustc_middle::ty::{ self, Ty, TyCtxt, COMMON_VTABLE_ENTRIES_ALIGN, COMMON_VTABLE_ENTRIES_DROPINPLACE, COMMON_VTABLE_ENTRIES_SIZE, @@ -102,18 +102,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { )? .expect("cannot be a ZST"); let size = vtable - .read_integer( + .read_integer(alloc_range( pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_SIZE).unwrap(), pointer_size, - )? + ))? .check_init()?; let size = size.to_machine_usize(self)?; let size = Size::from_bytes(size); let align = vtable - .read_integer( + .read_integer(alloc_range( pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_ALIGN).unwrap(), pointer_size, - )? + ))? .check_init()?; let align = align.to_machine_usize(self)?; let align = Align::from_bytes(align).map_err(|e| err_ub!(InvalidVtableAlignment(e)))?; diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 9d7905ed9a8..2bc521d5bbe 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -1,5 +1,5 @@ use rustc_middle::mir::interpret::InterpResult; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use std::convert::TryInto; use std::ops::ControlFlow; @@ -10,7 +10,7 @@ use std::ops::ControlFlow; /// case these parameters are unused. pub(crate) fn ensure_monomorphic_enough<'tcx, T>(tcx: TyCtxt<'tcx>, ty: T) -> InterpResult<'tcx> where - T: TypeFoldable<'tcx>, + T: TypeVisitable<'tcx>, { debug!("ensure_monomorphic_enough: ty={:?}", ty); if !ty.needs_subst() { diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 847694cbd10..0bf78446e37 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -593,16 +593,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' self.check_safe_pointer(value, "reference")?; Ok(true) } - ty::Adt(def, ..) if def.is_box() => { - let unique = self.ecx.operand_field(value, 0)?; - let nonnull = self.ecx.operand_field(&unique, 0)?; - let ptr = self.ecx.operand_field(&nonnull, 0)?; - self.check_safe_pointer(&ptr, "box")?; - - // Check other fields of Box - self.walk_value(value)?; - Ok(true) - } ty::FnPtr(_sig) => { let value = try_validation!( self.ecx.read_scalar(value).and_then(|v| v.check_init()), @@ -814,6 +804,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } #[inline] + fn visit_box(&mut self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { + self.check_safe_pointer(op, "box")?; + Ok(()) + } + + #[inline] fn visit_value(&mut self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { trace!("visit_value: {:?}, {:?}", *op, op.layout); @@ -821,8 +817,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> if self.try_visit_primitive(op)? { return Ok(()); } - // Sanity check: `builtin_deref` does not know any pointers that are not primitive. - assert!(op.layout.ty.builtin_deref(true).is_none()); // Special check preventing `UnsafeCell` in the inner part of constants if let Some(def) = op.layout.ty.ty_adt_def() { diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 2b77ed89893..ded4c6a557a 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -151,6 +151,14 @@ macro_rules! make_value_visitor { { Ok(()) } + /// Visits the given value as the pointer of a `Box`. There is nothing to recurse into. + /// The type of `v` will be a raw pointer, but this is a field of `Box<T>` and the + /// pointee type is the actual `T`. + #[inline(always)] + fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx> + { + Ok(()) + } /// Visits this value as an aggregate, you are getting an iterator yielding /// all the fields (still in an `InterpResult`, you have to do error handling yourself). /// Recurses into the fields. @@ -221,6 +229,47 @@ macro_rules! make_value_visitor { // Slices do not need special handling here: they have `Array` field // placement with length 0, so we enter the `Array` case below which // indirectly uses the metadata to determine the actual length. + + // However, `Box`... let's talk about `Box`. + ty::Adt(def, ..) if def.is_box() => { + // `Box` is a hybrid primitive-library-defined type that one the one hand is + // a dereferenceable pointer, on the other hand has *basically arbitrary + // user-defined layout* since the user controls the 'allocator' field. So it + // cannot be treated like a normal pointer, since it does not fit into an + // `Immediate`. Yeah, it is quite terrible. But many visitors want to do + // something with "all boxed pointers", so we handle this mess for them. + // + // When we hit a `Box`, we do not do the usual `visit_aggregate`; instead, + // we (a) call `visit_box` on the pointer value, and (b) recurse on the + // allocator field. We also assert tons of things to ensure we do not miss + // any other fields. + + // `Box` has two fields: the pointer we care about, and the allocator. + assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields"); + let (unique_ptr, alloc) = + (v.project_field(self.ecx(), 0)?, v.project_field(self.ecx(), 1)?); + // Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`... + // (which means another 2 fields, the second of which is a `PhantomData`) + assert_eq!(unique_ptr.layout().fields.count(), 2); + let (nonnull_ptr, phantom) = ( + unique_ptr.project_field(self.ecx(), 0)?, + unique_ptr.project_field(self.ecx(), 1)?, + ); + assert!( + phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()), + "2nd field of `Unique` should be PhantomData but is {:?}", + phantom.layout().ty, + ); + // ... that contains a `NonNull`... (gladly, only a single field here) + assert_eq!(nonnull_ptr.layout().fields.count(), 1); + let raw_ptr = nonnull_ptr.project_field(self.ecx(), 0)?; // the actual raw ptr + // ... whose only field finally is a raw ptr we can dereference. + self.visit_box(&raw_ptr)?; + + // The second `Box` field is the allocator, which we recursively check for validity + // like in regular structs. + self.visit_field(v, 1, &alloc)?; + } _ => {}, }; diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 5bf91879066..2d42ae236ad 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -21,6 +21,7 @@ Rust MIR: a lowered representation of Rust. #![feature(trusted_step)] #![feature(try_blocks)] #![feature(yeet_expr)] +#![feature(is_some_with)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index dc46583d5af..f87de4f6a08 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -10,7 +10,7 @@ use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceC use rustc_middle::mir::*; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt}; -use rustc_middle::ty::{Binder, TraitPredicate, TraitRef, TypeFoldable}; +use rustc_middle::ty::{Binder, TraitPredicate, TraitRef, TypeVisitable}; use rustc_mir_dataflow::{self, Analysis}; use rustc_span::{sym, Span, Symbol}; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 0c587220cb7..9574661282b 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -155,8 +155,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { }); if let Ok(Some(ImplSource::UserDefined(data))) = implsrc { - let span = - tcx.sess.source_map().guess_head_span(tcx.def_span(data.impl_def_id)); + let span = tcx.def_span(data.impl_def_id); err.span_note(span, "impl defined here, but it is not `const`"); } } @@ -205,7 +204,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { match self_ty.kind() { FnDef(def_id, ..) => { - let span = tcx.sess.source_map().guess_head_span(tcx.def_span(*def_id)); + let span = tcx.def_span(*def_id); if ccx.tcx.is_const_fn_raw(*def_id) { span_bug!(span, "calling const FnDef errored when it shouldn't"); } diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 8eee13196fc..6298fa7f062 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -17,7 +17,7 @@ use rustc_middle::mir::traversal::ReversePostorderIter; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::{self, List, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, List, TyCtxt, TypeVisitable}; use rustc_span::Span; use rustc_index::vec::{Idx, IndexVec}; diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index c9cb01701cf..7b9c6329d32 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -7,12 +7,12 @@ use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::NonUseContext::VarDebugInfo; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::{ - traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, Local, Location, MirPass, - MirPhase, Operand, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope, Statement, - StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK, + traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, CastKind, Local, Location, + MirPass, MirPhase, Operand, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope, + Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK, }; use rustc_middle::ty::fold::BottomUpFolder; -use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable, TypeVisitable}; use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::storage::always_live_locals; use rustc_mir_dataflow::{Analysis, ResultsCursor}; @@ -361,6 +361,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } } + Rvalue::Ref(..) => {} Rvalue::Len(p) => { let pty = p.ty(&self.body.local_decls, self.tcx).ty; check_kinds!( @@ -503,7 +504,30 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let a = operand.ty(&self.body.local_decls, self.tcx); check_kinds!(a, "Cannot shallow init type {:?}", ty::RawPtr(..)); } - _ => {} + Rvalue::Cast(kind, operand, target_type) => { + match kind { + CastKind::Misc => { + let op_ty = operand.ty(self.body, self.tcx); + if op_ty.is_enum() { + self.fail( + location, + format!( + "enum -> int casts should go through `Rvalue::Discriminant`: {operand:?}:{op_ty} as {target_type}", + ), + ); + } + } + // Nothing to check here + CastKind::PointerFromExposedAddress + | CastKind::PointerExposeAddress + | CastKind::Pointer(_) => {} + } + } + Rvalue::Repeat(_, _) + | Rvalue::ThreadLocalRef(_) + | Rvalue::AddressOf(_, _) + | Rvalue::NullaryOp(_, _) + | Rvalue::Discriminant(_) => {} } self.super_rvalue(rvalue, location); } diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 3096af90d47..b71cdad718a 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -849,10 +849,10 @@ Available lint options: }; println!("Lint checks provided by rustc:\n"); - println!(" {} {:7.7} {}", padded("name"), "default", "meaning"); - println!(" {} {:7.7} {}", padded("----"), "-------", "-------"); let print_lints = |lints: Vec<&Lint>| { + println!(" {} {:7.7} {}", padded("name"), "default", "meaning"); + println!(" {} {:7.7} {}", padded("----"), "-------", "-------"); for lint in lints { let name = lint.name_lower().replace('_', "-"); println!( @@ -884,11 +884,15 @@ Available lint options: }; println!("Lint groups provided by rustc:\n"); - println!(" {} sub-lints", padded("name")); - println!(" {} ---------", padded("----")); - println!(" {} all lints that are set to issue warnings", padded("warnings")); - let print_lint_groups = |lints: Vec<(&'static str, Vec<LintId>)>| { + let print_lint_groups = |lints: Vec<(&'static str, Vec<LintId>)>, all_warnings| { + println!(" {} sub-lints", padded("name")); + println!(" {} ---------", padded("----")); + + if all_warnings { + println!(" {} all lints that are set to issue warnings", padded("warnings")); + } + for (name, to) in lints { let name = name.to_lowercase().replace('_', "-"); let desc = to @@ -901,7 +905,7 @@ Available lint options: println!("\n"); }; - print_lint_groups(builtin_groups); + print_lint_groups(builtin_groups, true); match (loaded_plugins, plugin.len(), plugin_groups.len()) { (false, 0, _) | (false, _, 0) => { @@ -916,7 +920,7 @@ Available lint options: } if g > 0 { println!("Lint groups provided by plugins loaded by this crate:\n"); - print_lint_groups(plugin_groups); + print_lint_groups(plugin_groups, false); } } } diff --git a/compiler/rustc_error_messages/locales/en-US/lint.ftl b/compiler/rustc_error_messages/locales/en-US/lint.ftl new file mode 100644 index 00000000000..e7e07093c03 --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/lint.ftl @@ -0,0 +1,400 @@ +lint-array-into-iter = + this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <{$target} as IntoIterator>::into_iter in Rust 2021 + .use-iter-suggestion = use `.iter()` instead of `.into_iter()` to avoid ambiguity + .remove-into-iter-suggestion = or remove `.into_iter()` to iterate by value + .use-explicit-into-iter-suggestion = + or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value + +lint-enum-intrinsics-mem-discriminant = + the return value of `mem::discriminant` is unspecified when called with a non-enum type + .note = the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum. + +lint-enum-intrinsics-mem-variant = + the return value of `mem::variant_count` is unspecified when called with a non-enum type + .note = the type parameter of `variant_count` should be an enum, but it was instantiated with the type `{$ty_param}`, which is not an enum. + +lint-expectation = this lint expectation is unfulfilled + .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message + +lint-hidden-unicode-codepoints = unicode codepoint changing visible direction of text present in {$label} + .label = this {$label} contains {$count -> + [one] an invisible + *[other] invisible + } unicode text flow control {$count -> + [one] codepoint + *[other] codepoints + } + .note = these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + .suggestion-remove = if their presence wasn't intentional, you can remove them + .suggestion-escape = if you want to keep them but make them visible in your source code, you can escape them + .no-suggestion-note-escape = if you want to keep them but make them visible in your source code, you can escape them: {$escaped} + +lint-default-hash-types = prefer `{$preferred}` over `{$used}`, it has better performance + .note = a `use rustc_data_structures::fx::{$preferred}` may be necessary + +lint-query-instability = using `{$query}` can result in unstable query results + .note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale + +lint-tykind-kind = usage of `ty::TyKind::<kind>` + .suggestion = try using `ty::<kind>` directly + +lint-tykind = usage of `ty::TyKind` + .help = try using `Ty` instead + +lint-ty-qualified = usage of qualified `ty::{$ty}` + .suggestion = try importing it and using it unqualified + +lint-lintpass-by-hand = implementing `LintPass` by hand + .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead + +lint-non-existant-doc-keyword = found non-existing keyword `{$keyword}` used in `#[doc(keyword = \"...\")]` + .help = only existing keywords are allowed in core/std + +lint-diag-out-of-impl = + diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls + +lint-untranslatable-diag = diagnostics should be created using translatable messages + +lint-cstring-ptr = getting the inner pointer of a temporary `CString` + .as-ptr-label = this pointer will be invalid + .unwrap-label = this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + .note = pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + .help = for more information, see https://doc.rust-lang.org/reference/destructors.html + +lint-identifier-non-ascii-char = identifier contains non-ASCII characters + +lint-identifier-uncommon-codepoints = identifier contains uncommon Unicode codepoints + +lint-confusable-identifier-pair = identifier pair considered confusable between `{$existing_sym}` and `{$sym}` + .label = this is where the previous identifier occurred + +lint-mixed-script-confusables = + the usage of Script Group `{$set}` in this crate consists solely of mixed script confusables + .includes-note = the usage includes {$includes} + .note = please recheck to make sure their usages are indeed what you want + +lint-non-fmt-panic = panic message is not a string literal + .note = this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021 + .more-info-note = for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html> + .supports-fmt-note = the `{$name}!()` macro supports formatting, so there's no need for the `format!()` macro here + .supports-fmt-suggestion = remove the `format!(..)` macro call + .display-suggestion = add a "{"{"}{"}"}" format string to `Display` the message + .debug-suggestion = + add a "{"{"}:?{"}"}" format string to use the `Debug` implementation of `{$ty}` + .panic-suggestion = {$already_suggested -> + [true] or use + *[false] use + } std::panic::panic_any instead + +lint-non-fmt-panic-unused = + panic message contains {$count -> + [one] an unused + *[other] unused + } formatting {$count -> + [one] placeholder + *[other] placeholders + } + .note = this message is not used as a format string when given without arguments, but will be in Rust 2021 + .add-args-suggestion = add the missing {$count -> + [one] argument + *[other] arguments + } + .add-fmt-suggestion = or add a "{"{"}{"}"}" format string to use the message literally + +lint-non-fmt-panic-braces = + panic message contains {$count -> + [one] a brace + *[other] braces + } + .note = this message is not used as a format string, but will be in Rust 2021 + .suggestion = add a "{"{"}{"}"}" format string to use the message literally + +lint-non-camel-case-type = {$sort} `{$name}` should have an upper camel case name + .suggestion = convert the identifier to upper camel case + .label = should have an UpperCamelCase name + +lint-non-snake-case = {$sort} `{$name}` should have a snake case name + .rename-or-convert-suggestion = rename the identifier or convert it to a snake case raw identifier + .cannot-convert-note = `{$sc}` cannot be used as a raw identifier + .rename-suggestion = rename the identifier + .convert-suggestion = convert the identifier to snake case + .help = convert the identifier to snake case: `{$sc}` + .label = should have a snake_case name + +lint-non-upper_case-global = {$sort} `{$name}` should have an upper case name + .suggestion = convert the identifier to upper case + .label = should have an UPPER_CASE name + +lint-noop-method-call = call to `.{$method}()` on a reference in this situation does nothing + .label = unnecessary method call + .note = the type `{$receiver_ty}` which `{$method}` is being called on is the same as the type returned from `{$method}`, so the method call does not do anything and can be removed + +lint-pass-by-value = passing `{$ty}` by reference + .suggestion = try passing by value + +lint-redundant-semicolons = + unnecessary trailing {$multiple -> + [true] semicolons + *[false] semicolon + } + .suggestion = remove {$multiple -> + [true] these semicolons + *[false] this semicolon + } + +lint-drop-trait-constraints = + bounds on `{$predicate}` are most likely incorrect, consider instead using `{$needs_drop}` to detect whether a type can be trivially dropped + +lint-drop-glue = + types that do not implement `Drop` can still have drop glue, consider instead using `{$needs_drop}` to detect whether a type is trivially dropped + +lint-range-endpoint-out-of-range = range endpoint is out of range for `{$ty}` + .suggestion = use an inclusive range instead + +lint-overflowing-bin-hex = literal out of range for `{$ty}` + .negative-note = the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` + .negative-becomes-note = and the value `-{$lit}` will become `{$actually}{$ty}` + .positive-note = the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` and will become `{$actually}{$ty}` + .suggestion = consider using the type `{$suggestion_ty}` instead + .help = consider using the type `{$suggestion_ty}` instead + +lint-overflowing-int = literal out of range for `{$ty}` + .note = the literal `{$lit}` does not fit into the type `{$ty}` whose range is `{$min}..={$max}` + .help = consider using the type `{$suggestion_ty}` instead + +lint-only-cast-u8-to-char = only `u8` can be cast into `char` + .suggestion = use a `char` literal instead + +lint-overflowing-uint = literal out of range for `{$ty}` + .note = the literal `{$lit}` does not fit into the type `{$ty}` whose range is `{$min}..={$max}` + +lint-overflowing-literal = literal out of range for `{$ty}` + .note = the literal `{$lit}` does not fit into the type `{$ty}` and will be converted to `{$ty}::INFINITY` + +lint-unused-comparisons = comparison is useless due to type limits + +lint-improper-ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe + .label = not FFI-safe + .note = the type is defined here + +lint-improper-ctypes-opaque = opaque types have no C equivalent + +lint-improper-ctypes-fnptr-reason = this function pointer has Rust-specific calling convention +lint-improper-ctypes-fnptr-help = consider using an `extern fn(...) -> ...` function pointer instead + +lint-improper-ctypes-tuple-reason = tuples have unspecified layout +lint-improper-ctypes-tuple-help = consider using a struct instead + +lint-improper-ctypes-str-reason = string slices have no C equivalent +lint-improper-ctypes-str-help = consider using `*const u8` and a length instead + +lint-improper-ctypes-dyn = trait objects have no C equivalent + +lint-improper-ctypes-slice-reason = slices have no C equivalent +lint-improper-ctypes-slice-help = consider using a raw pointer instead + +lint-improper-ctypes-128bit = 128-bit integers don't currently have a known stable ABI + +lint-improper-ctypes-char-reason = the `char` type has no C equivalent +lint-improper-ctypes-char-help = consider using `u32` or `libc::wchar_t` instead + +lint-improper-ctypes-non-exhaustive = this enum is non-exhaustive +lint-improper-ctypes-non-exhaustive-variant = this enum has non-exhaustive variants + +lint-improper-ctypes-enum-repr-reason = enum has no representation hint +lint-improper-ctypes-enum-repr-help = + consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum + +lint-improper-ctypes-struct-fieldless-reason = this struct has no fields +lint-improper-ctypes-struct-fieldless-help = consider adding a member to this struct + +lint-improper-ctypes-union-fieldless-reason = this union has no fields +lint-improper-ctypes-union-fieldless-help = consider adding a member to this union + +lint-improper-ctypes-struct-non-exhaustive = this struct is non-exhaustive +lint-improper-ctypes-union-non-exhaustive = this union is non-exhaustive + +lint-improper-ctypes-struct-layout-reason = this struct has unspecified layout +lint-improper-ctypes-struct-layout-help = consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + +lint-improper-ctypes-union-layout-reason = this union has unspecified layout +lint-improper-ctypes-union-layout-help = consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union + +lint-improper-ctypes-box = box cannot be represented as a single pointer + +lint-improper-ctypes-enum-phantomdata = this enum contains a PhantomData field + +lint-improper-ctypes-struct-zst = this struct contains only zero-sized fields + +lint-improper-ctypes-array-reason = passing raw arrays by value is not FFI-safe +lint-improper-ctypes-array-help = consider passing a pointer to the array + +lint-improper-ctypes-only-phantomdata = composed only of `PhantomData` + +lint-variant-size-differences = + enum variant is more than three times larger ({$largest} bytes) than the next largest + +lint-atomic-ordering-load = atomic loads cannot have `Release` or `AcqRel` ordering + .help = consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +lint-atomic-ordering-store = atomic stores cannot have `Acquire` or `AcqRel` ordering + .help = consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +lint-atomic-ordering-fence = memory fences cannot have `Relaxed` ordering + .help = consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst` + +lint-atomic-ordering-invalid = `{$method}`'s failure ordering may not be `Release` or `AcqRel`, since a failed `{$method}` does not result in a write + .label = invalid failure ordering + .help = consider using `Acquire` or `Relaxed` failure ordering instead + +lint-atomic-ordering-invalid-fail-success = `{$method}`'s success ordering must be at least as strong as its failure ordering + .fail-label = `{$fail_ordering}` failure ordering + .success-label = `{$success_ordering}` success ordering + .suggestion = consider using `{$success_suggestion}` success ordering instead + +lint-unused-op = unused {$op} that must be used + .label = the {$op} produces a value + .suggestion = use `let _ = ...` to ignore the resulting value + +lint-unused-result = unused result of type `{$ty}` + +lint-unused-closure = + unused {$pre}{$count -> + [one] closure + *[other] closures + }{$post} that must be used + .note = closures are lazy and do nothing unless called + +lint-unused-generator = + unused {$pre}{$count -> + [one] generator + *[other] generator + }{$post} that must be used + .note = generators are lazy and do nothing unless resumed + +lint-unused-def = unused {$pre}`{$def}`{$post} that must be used + +lint-path-statement-drop = path statement drops value + .suggestion = use `drop` to clarify the intent + +lint-path-statement-no-effect = path statement with no effect + +lint-unused-delim = unnecessary {$delim} around {$item} + .suggestion = remove these {$delim} + +lint-unused-import-braces = braces around {$node} is unnecessary + +lint-unused-allocation = unnecessary allocation, use `&` instead +lint-unused-allocation-mut = unnecessary allocation, use `&mut` instead + +lint-builtin-while-true = denote infinite loops with `loop {"{"} ... {"}"}` + .suggestion = use `loop` + +lint-builtin-box-pointers = type uses owned (Box type) pointers: {$ty} + +lint-builtin-non-shorthand-field-patterns = the `{$ident}:` in this pattern is redundant + .suggestion = use shorthand field pattern + +lint-builtin-overridden-symbol-name = + the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them + +lint-builtin-overridden-symbol-section = + the program's behavior with overridden link sections on items is unpredictable and Rust cannot provide guarantees when you manually override them + +lint-builtin-allow-internal-unsafe = + `allow_internal_unsafe` allows defining macros using unsafe without triggering the `unsafe_code` lint at their call site + +lint-builtin-unsafe-block = usage of an `unsafe` block + +lint-builtin-unsafe-trait = declaration of an `unsafe` trait + +lint-builtin-unsafe-impl = implementation of an `unsafe` trait + +lint-builtin-no-mangle-fn = declaration of a `no_mangle` function +lint-builtin-export-name-fn = declaration of a function with `export_name` +lint-builtin-link-section-fn = declaration of a function with `link_section` + +lint-builtin-no-mangle-static = declaration of a `no_mangle` static +lint-builtin-export-name-static = declaration of a static with `export_name` +lint-builtin-link-section-static = declaration of a static with `link_section` + +lint-builtin-no-mangle-method = declaration of a `no_mangle` method +lint-builtin-export-name-method = declaration of a method with `export_name` + +lint-builtin-decl-unsafe-fn = declaration of an `unsafe` function +lint-builtin-decl-unsafe-method = declaration of an `unsafe` method +lint-builtin-impl-unsafe-method = implementation of an `unsafe` method + +lint-builtin-missing-doc = missing documentation for {$article} {$desc} + +lint-builtin-missing-copy-impl = type could implement `Copy`; consider adding `impl Copy` + +lint-builtin-missing-debug-impl = + type does not implement `{$debug}`; consider adding `#[derive(Debug)]` or a manual implementation + +lint-builtin-anonymous-params = anonymous parameters are deprecated and will be removed in the next edition + .suggestion = try naming the parameter or explicitly ignoring it + +lint-builtin-deprecated-attr-link = use of deprecated attribute `{$name}`: {$reason}. See {$link} +lint-builtin-deprecated-attr-used = use of deprecated attribute `{$name}`: no longer used. +lint-builtin-deprecated-attr-default-suggestion = remove this attribute + +lint-builtin-unused-doc-comment = unused doc comment + .label = rustdoc does not generate documentation for {$kind} + .plain-help = use `//` for a plain comment + .block-help = use `/* */` for a plain comment + +lint-builtin-no-mangle-generic = functions generic over types or consts must be mangled + .suggestion = remove this attribute + +lint-builtin-const-no-mangle = const items should never be `#[no_mangle]` + .suggestion = try a static value + +lint-builtin-mutable-transmutes = + transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell + +lint-builtin-unstable-features = unstable feature + +lint-builtin-unreachable-pub = unreachable `pub` {$what} + .suggestion = consider restricting its visibility + .help = or consider exporting it for use by other crates + +lint-builtin-type-alias-bounds-help = use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases + +lint-builtin-type-alias-where-clause = where clauses are not enforced in type aliases + .suggestion = the clause will not be checked when the type alias is used, and should be removed + +lint-builtin-type-alias-generic-bounds = bounds on generic parameters are not enforced in type aliases + .suggestion = the bound will not be checked when the type alias is used, and should be removed + +lint-builtin-trivial-bounds = {$predicate_kind_name} bound {$predicate} does not depend on any type or lifetime parameters + +lint-builtin-ellipsis-inclusive-range-patterns = `...` range patterns are deprecated + .suggestion = use `..=` for an inclusive range + +lint-builtin-unnameable-test-items = cannot test inner items + +lint-builtin-keyword-idents = `{$kw}` is a keyword in the {$next} edition + .suggestion = you can use a raw identifier to stay compatible + +lint-builtin-explicit-outlives = outlives requirements can be inferred + .suggestion = remove {$count -> + [one] this bound + *[other] these bounds + } + +lint-builtin-incomplete-features = the feature `{$name}` is incomplete and may not be safe to use and/or cause compiler crashes + .note = see issue #{$n} <https://github.com/rust-lang/rust/issues/{$n}> for more information + .help = consider using `min_{$name}` instead, which is more stable and complete + +lint-builtin-clashing-extern-same-name = `{$this_fi}` redeclared with a different signature + .previous-decl-label = `{$orig}` previously declared here + .mismatch-label = this signature doesn't match the previous declaration +lint-builtin-clashing-extern-diff-name = `{$this_fi}` redeclares `{$orig}` with a different signature + .previous-decl-label = `{$orig}` previously declared here + .mismatch-label = this signature doesn't match the previous declaration + +lint-builtin-deref-nullptr = dereferencing a null pointer + .label = this code causes undefined behavior when executed + +lint-builtin-asm-labels = avoid using named labels in inline assembly diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index d52b94b78df..563d0534d8f 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -31,11 +31,12 @@ pub use unic_langid::{langid, LanguageIdentifier}; // Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module. fluent_messages! { + borrowck => "../locales/en-US/borrowck.ftl", + builtin_macros => "../locales/en-US/builtin_macros.ftl", + lint => "../locales/en-US/lint.ftl", parser => "../locales/en-US/parser.ftl", privacy => "../locales/en-US/privacy.ftl", typeck => "../locales/en-US/typeck.ftl", - builtin_macros => "../locales/en-US/builtin_macros.ftl", - borrowck => "../locales/en-US/borrowck.ftl", } pub use fluent_generated::{self as fluent, DEFAULT_LOCALE_RESOURCES}; diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index b8545139cec..0d1d017d874 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1,14 +1,14 @@ use crate::snippet::Style; use crate::{ - CodeSuggestion, DiagnosticMessage, Level, MultiSpan, SubdiagnosticMessage, Substitution, - SubstitutionPart, SuggestionStyle, + CodeSuggestion, DiagnosticMessage, EmissionGuarantee, Level, LintDiagnosticBuilder, MultiSpan, + SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, }; use rustc_data_structures::stable_map::FxHashMap; use rustc_error_messages::FluentValue; use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{edition::Edition, Span, DUMMY_SP}; use std::borrow::Cow; use std::fmt; use std::hash::{Hash, Hasher}; @@ -39,12 +39,94 @@ pub trait IntoDiagnosticArg { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>; } +impl IntoDiagnosticArg for bool { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + if self { + DiagnosticArgValue::Str(Cow::Borrowed("true")) + } else { + DiagnosticArgValue::Str(Cow::Borrowed("false")) + } + } +} + +impl IntoDiagnosticArg for i8 { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + } +} + +impl IntoDiagnosticArg for u8 { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + } +} + +impl IntoDiagnosticArg for i16 { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + } +} + +impl IntoDiagnosticArg for u16 { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + } +} + +impl IntoDiagnosticArg for i32 { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + } +} + +impl IntoDiagnosticArg for u32 { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + } +} + +impl IntoDiagnosticArg for i64 { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + } +} + +impl IntoDiagnosticArg for u64 { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + } +} + +impl IntoDiagnosticArg for i128 { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + } +} + +impl IntoDiagnosticArg for u128 { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + } +} + impl IntoDiagnosticArg for String { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { DiagnosticArgValue::Str(Cow::Owned(self)) } } +impl IntoDiagnosticArg for std::num::NonZeroU32 { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + } +} + +impl IntoDiagnosticArg for Edition { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + } +} + impl IntoDiagnosticArg for Symbol { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { self.to_ident_string().into_diagnostic_arg() @@ -86,6 +168,14 @@ pub trait AddSubdiagnostic { fn add_to_diagnostic(self, diag: &mut Diagnostic); } +/// Trait implemented by lint types. This should not be implemented manually. Instead, use +/// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. +#[rustc_diagnostic_item = "DecorateLint"] +pub trait DecorateLint<'a, G: EmissionGuarantee> { + /// Decorate and emit a lint. + fn decorate_lint(self, diag: LintDiagnosticBuilder<'a, G>); +} + #[must_use] #[derive(Clone, Debug, Encodable, Decodable)] pub struct Diagnostic { @@ -286,7 +376,7 @@ impl Diagnostic { /// /// This span is *not* considered a ["primary span"][`MultiSpan`]; only /// the `Span` supplied when creating the diagnostic is primary. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self { self.span.push_span_label(span, self.subdiagnostic_message_to_diagnostic_message(label)); self @@ -405,7 +495,7 @@ impl Diagnostic { } /// Add a note attached to this diagnostic. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { self.sub(Level::Note, msg, MultiSpan::new(), None); self @@ -428,7 +518,7 @@ impl Diagnostic { /// Prints the span with a note above it. /// This is like [`Diagnostic::note()`], but it gets its own span. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_note<S: Into<MultiSpan>>( &mut self, sp: S, @@ -450,7 +540,7 @@ impl Diagnostic { } /// Add a warning attached to this diagnostic. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { self.sub(Level::Warning(None), msg, MultiSpan::new(), None); self @@ -458,7 +548,7 @@ impl Diagnostic { /// Prints the span with a warning above it. /// This is like [`Diagnostic::warn()`], but it gets its own span. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_warn<S: Into<MultiSpan>>( &mut self, sp: S, @@ -469,7 +559,7 @@ impl Diagnostic { } /// Add a help message attached to this diagnostic. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { self.sub(Level::Help, msg, MultiSpan::new(), None); self @@ -483,7 +573,7 @@ impl Diagnostic { /// Prints the span with some help above it. /// This is like [`Diagnostic::help()`], but it gets its own span. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_help<S: Into<MultiSpan>>( &mut self, sp: S, diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 9e0a99849a3..c3341fd68f4 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -529,7 +529,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { applicability: Applicability, ) -> &mut Self); - forward!(pub fn set_primary_message(&mut self, msg: impl Into<String>) -> &mut Self); + forward!(pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self); forward!(pub fn set_span(&mut self, sp: impl Into<MultiSpan>) -> &mut Self); forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); forward!(pub fn set_arg( @@ -589,3 +589,26 @@ macro_rules! struct_span_err { macro_rules! error_code { ($code:ident) => {{ $crate::DiagnosticId::Error(stringify!($code).to_owned()) }}; } + +/// Wrapper around a `DiagnosticBuilder` for creating lints. +pub struct LintDiagnosticBuilder<'a, G: EmissionGuarantee>(DiagnosticBuilder<'a, G>); + +impl<'a, G: EmissionGuarantee> LintDiagnosticBuilder<'a, G> { + /// Return the inner `DiagnosticBuilder`, first setting the primary message to `msg`. + pub fn build(mut self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'a, G> { + self.0.set_primary_message(msg); + self.0.set_is_lint(); + self.0 + } + + /// Create a `LintDiagnosticBuilder` from some existing `DiagnosticBuilder`. + pub fn new(err: DiagnosticBuilder<'a, G>) -> LintDiagnosticBuilder<'a, G> { + LintDiagnosticBuilder(err) + } +} + +impl<'a> LintDiagnosticBuilder<'a, ErrorGuaranteed> { + pub fn forget_guarantee(self) -> LintDiagnosticBuilder<'a, ()> { + LintDiagnosticBuilder(self.0.forget_guarantee()) + } +} diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 9c42f6f26fc..ffe4ecebb2e 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -370,10 +370,10 @@ impl fmt::Display for ExplicitBug { impl error::Error for ExplicitBug {} pub use diagnostic::{ - AddSubdiagnostic, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId, + AddSubdiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId, DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic, }; -pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee}; +pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, LintDiagnosticBuilder}; use std::backtrace::Backtrace; /// A handler deals with errors and other compiler output. @@ -649,7 +649,7 @@ impl Handler { /// Attempting to `.emit()` the builder will only emit if either: /// * `can_emit_warnings` is `true` /// * `is_force_warn` was set in `DiagnosticId::Lint` - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_warn( &self, span: impl Into<MultiSpan>, @@ -678,7 +678,7 @@ impl Handler { } /// Construct a builder at the `Allow` level at the given `span` and with the `msg`. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_allow( &self, span: impl Into<MultiSpan>, @@ -691,7 +691,7 @@ impl Handler { /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. /// Also include a code. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_warn_with_code( &self, span: impl Into<MultiSpan>, @@ -708,7 +708,7 @@ impl Handler { /// Attempting to `.emit()` the builder will only emit if either: /// * `can_emit_warnings` is `true` /// * `is_force_warn` was set in `DiagnosticId::Lint` - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Warning(None), msg) } @@ -728,13 +728,13 @@ impl Handler { } /// Construct a builder at the `Allow` level with the `msg`. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Allow, msg) } /// Construct a builder at the `Expect` level with the `msg`. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_expect( &self, msg: impl Into<DiagnosticMessage>, @@ -744,7 +744,7 @@ impl Handler { } /// Construct a builder at the `Error` level at the given `span` and with the `msg`. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_err( &self, span: impl Into<MultiSpan>, @@ -756,7 +756,7 @@ impl Handler { } /// Construct a builder at the `Error` level at the given `span`, with the `msg`, and `code`. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_err_with_code( &self, span: impl Into<MultiSpan>, @@ -770,7 +770,7 @@ impl Handler { /// Construct a builder at the `Error` level with the `msg`. // FIXME: This method should be removed (every error should have an associated error code). - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_err( &self, msg: impl Into<DiagnosticMessage>, @@ -785,7 +785,7 @@ impl Handler { } /// Construct a builder at the `Error` level with the `msg` and the `code`. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_err_with_code( &self, msg: impl Into<DiagnosticMessage>, @@ -797,7 +797,7 @@ impl Handler { } /// Construct a builder at the `Warn` level with the `msg` and the `code`. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_warn_with_code( &self, msg: impl Into<DiagnosticMessage>, @@ -809,7 +809,7 @@ impl Handler { } /// Construct a builder at the `Fatal` level at the given `span` and with the `msg`. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_fatal( &self, span: impl Into<MultiSpan>, @@ -821,7 +821,7 @@ impl Handler { } /// Construct a builder at the `Fatal` level at the given `span`, with the `msg`, and `code`. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_fatal_with_code( &self, span: impl Into<MultiSpan>, @@ -834,19 +834,19 @@ impl Handler { } /// Construct a builder at the `Error` level with the `msg`. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> { DiagnosticBuilder::new_fatal(self, msg) } /// Construct a builder at the `Help` level with the `msg`. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_help(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Help, msg) } /// Construct a builder at the `Note` level with the `msg`. - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_note_without_error( &self, msg: impl Into<DiagnosticMessage>, @@ -854,13 +854,13 @@ impl Handler { DiagnosticBuilder::new(self, Level::Note, msg) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span); FatalError.raise() } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_fatal_with_code( &self, span: impl Into<MultiSpan>, @@ -871,7 +871,7 @@ impl Handler { FatalError.raise() } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_err( &self, span: impl Into<MultiSpan>, @@ -880,7 +880,7 @@ impl Handler { self.emit_diag_at_span(Diagnostic::new(Error { lint: false }, msg), span).unwrap() } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_err_with_code( &self, span: impl Into<MultiSpan>, @@ -893,12 +893,12 @@ impl Handler { ); } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) { self.emit_diag_at_span(Diagnostic::new(Warning(None), msg), span); } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_warn_with_code( &self, span: impl Into<MultiSpan>, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 1e57d66dd9f..bfe2d773788 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1077,7 +1077,7 @@ impl<'a> ExtCtxt<'a> { self.current_expansion.id.expansion_cause() } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_err<S: Into<MultiSpan>>( &self, sp: S, @@ -1102,11 +1102,11 @@ impl<'a> ExtCtxt<'a> { /// /// Compilation will be stopped in the near future (at the end of /// the macro expansion phase). - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { self.sess.parse_sess.span_diagnostic.span_err(sp, msg); } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { self.sess.parse_sess.span_diagnostic.span_warn(sp, msg); } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 1694a8865dd..74e9bbeeeaf 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -152,6 +152,19 @@ impl<'a> ExtCtxt<'a> { ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) } } + pub fn stmt_let_pat(&self, sp: Span, pat: P<ast::Pat>, ex: P<ast::Expr>) -> ast::Stmt { + let local = P(ast::Local { + pat, + ty: None, + id: ast::DUMMY_NODE_ID, + kind: LocalKind::Init(ex), + span: sp, + attrs: AttrVec::new(), + tokens: None, + }); + self.stmt_local(local, sp) + } + pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P<ast::Expr>) -> ast::Stmt { self.stmt_let_ty(sp, mutbl, ident, None, ex) } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index fd843b0c403..50acb0270b0 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -59,7 +59,7 @@ impl PpAnn for &dyn rustc_hir::intravisit::Map<'_> { Nested::ImplItem(id) => state.print_impl_item(self.impl_item(id)), Nested::ForeignItem(id) => state.print_foreign_item(self.foreign_item(id)), Nested::Body(id) => state.print_expr(&self.body(id).value), - Nested::BodyParamPat(id, i) => state.print_pat(&self.body(id).params[i].pat), + Nested::BodyParamPat(id, i) => state.print_pat(self.body(id).params[i].pat), } } } @@ -74,37 +74,37 @@ pub struct State<'a> { impl<'a> State<'a> { pub fn print_node(&mut self, node: Node<'_>) { match node { - Node::Param(a) => self.print_param(&a), - Node::Item(a) => self.print_item(&a), - Node::ForeignItem(a) => self.print_foreign_item(&a), + Node::Param(a) => self.print_param(a), + Node::Item(a) => self.print_item(a), + Node::ForeignItem(a) => self.print_foreign_item(a), Node::TraitItem(a) => self.print_trait_item(a), Node::ImplItem(a) => self.print_impl_item(a), - Node::Variant(a) => self.print_variant(&a), - Node::AnonConst(a) => self.print_anon_const(&a), - Node::Expr(a) => self.print_expr(&a), - Node::Stmt(a) => self.print_stmt(&a), - Node::PathSegment(a) => self.print_path_segment(&a), - Node::Ty(a) => self.print_type(&a), - Node::TypeBinding(a) => self.print_type_binding(&a), - Node::TraitRef(a) => self.print_trait_ref(&a), - Node::Pat(a) => self.print_pat(&a), - Node::Arm(a) => self.print_arm(&a), + Node::Variant(a) => self.print_variant(a), + Node::AnonConst(a) => self.print_anon_const(a), + Node::Expr(a) => self.print_expr(a), + Node::Stmt(a) => self.print_stmt(a), + Node::PathSegment(a) => self.print_path_segment(a), + Node::Ty(a) => self.print_type(a), + Node::TypeBinding(a) => self.print_type_binding(a), + Node::TraitRef(a) => self.print_trait_ref(a), + Node::Pat(a) => self.print_pat(a), + Node::Arm(a) => self.print_arm(a), Node::Infer(_) => self.word("_"), Node::Block(a) => { // Containing cbox, will be closed by print-block at `}`. self.cbox(INDENT_UNIT); // Head-ibox, will be closed by print-block after `{`. self.ibox(0); - self.print_block(&a) + self.print_block(a); } - Node::Lifetime(a) => self.print_lifetime(&a), + Node::Lifetime(a) => self.print_lifetime(a), Node::GenericParam(_) => panic!("cannot print Node::GenericParam"), Node::Field(_) => panic!("cannot print Node::Field"), // These cases do not carry enough information in the // `hir_map` to reconstruct their full structure for pretty // printing. Node::Ctor(..) => panic!("cannot print isolated Ctor"), - Node::Local(a) => self.print_local_decl(&a), + Node::Local(a) => self.print_local_decl(a), Node::Crate(..) => panic!("cannot print Crate"), } } @@ -266,7 +266,7 @@ impl<'a> State<'a> { } pub fn commasep_exprs(&mut self, b: Breaks, exprs: &[hir::Expr<'_>]) { - self.commasep_cmnt(b, exprs, |s, e| s.print_expr(&e), |e| e.span) + self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span); } pub fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[ast::Attribute]) { @@ -287,9 +287,9 @@ impl<'a> State<'a> { self.maybe_print_comment(ty.span.lo()); self.ibox(0); match ty.kind { - hir::TyKind::Slice(ref ty) => { + hir::TyKind::Slice(ty) => { self.word("["); - self.print_type(&ty); + self.print_type(ty); self.word("]"); } hir::TyKind::Ptr(ref mt) => { @@ -304,23 +304,16 @@ impl<'a> State<'a> { hir::TyKind::Never => { self.word("!"); } - hir::TyKind::Tup(ref elts) => { + hir::TyKind::Tup(elts) => { self.popen(); - self.commasep(Inconsistent, &elts, |s, ty| s.print_type(&ty)); + self.commasep(Inconsistent, elts, |s, ty| s.print_type(ty)); if elts.len() == 1 { self.word(","); } self.pclose(); } - hir::TyKind::BareFn(ref f) => { - self.print_ty_fn( - f.abi, - f.unsafety, - &f.decl, - None, - &f.generic_params, - f.param_names, - ); + hir::TyKind::BareFn(f) => { + self.print_ty_fn(f.abi, f.unsafety, f.decl, None, f.generic_params, f.param_names); } hir::TyKind::OpaqueDef(..) => self.word("/*impl Trait*/"), hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false), @@ -344,9 +337,9 @@ impl<'a> State<'a> { self.print_lifetime(lifetime); } } - hir::TyKind::Array(ref ty, ref length) => { + hir::TyKind::Array(ty, ref length) => { self.word("["); - self.print_type(&ty); + self.print_type(ty); self.word("; "); self.print_array_length(length); self.word("]"); @@ -373,7 +366,7 @@ impl<'a> State<'a> { self.maybe_print_comment(item.span.lo()); self.print_outer_attributes(self.attrs(item.hir_id())); match item.kind { - hir::ForeignItemKind::Fn(ref decl, ref arg_names, ref generics) => { + hir::ForeignItemKind::Fn(decl, arg_names, generics) => { self.head(""); self.print_fn( decl, @@ -392,14 +385,14 @@ impl<'a> State<'a> { self.word(";"); self.end() // end the outer fn box } - hir::ForeignItemKind::Static(ref t, m) => { + hir::ForeignItemKind::Static(t, m) => { self.head("static"); if m == hir::Mutability::Mut { self.word_space("mut"); } self.print_ident(item.ident); self.word_space(":"); - self.print_type(&t); + self.print_type(t); self.word(";"); self.end(); // end the head-ibox self.end() // end the outer cbox @@ -442,7 +435,7 @@ impl<'a> State<'a> { ) { self.word_space("type"); self.print_ident(ident); - self.print_generic_params(&generics.params); + self.print_generic_params(generics.params); if let Some(bounds) = bounds { self.print_bounds(":", bounds); } @@ -463,7 +456,7 @@ impl<'a> State<'a> { ) { self.head("type"); self.print_ident(item.ident); - self.print_generic_params(&generics.params); + self.print_generic_params(generics.params); self.end(); // end the inner ibox self.print_where_clause(generics); @@ -494,7 +487,7 @@ impl<'a> State<'a> { self.end(); // end inner head-block self.end(); // end outer head-block } - hir::ItemKind::Use(ref path, kind) => { + hir::ItemKind::Use(path, kind) => { self.head("use"); self.print_path(path, false); @@ -513,14 +506,14 @@ impl<'a> State<'a> { self.end(); // end inner head-block self.end(); // end outer head-block } - hir::ItemKind::Static(ref ty, m, expr) => { + hir::ItemKind::Static(ty, m, expr) => { self.head("static"); if m == hir::Mutability::Mut { self.word_space("mut"); } self.print_ident(item.ident); self.word_space(":"); - self.print_type(&ty); + self.print_type(ty); self.space(); self.end(); // end the head-ibox @@ -529,11 +522,11 @@ impl<'a> State<'a> { self.word(";"); self.end(); // end the outer cbox } - hir::ItemKind::Const(ref ty, expr) => { + hir::ItemKind::Const(ty, expr) => { self.head("const"); self.print_ident(item.ident); self.word_space(":"); - self.print_type(&ty); + self.print_type(ty); self.space(); self.end(); // end the head-ibox @@ -542,10 +535,10 @@ impl<'a> State<'a> { self.word(";"); self.end(); // end the outer cbox } - hir::ItemKind::Fn(ref sig, ref param_names, body) => { + hir::ItemKind::Fn(ref sig, param_names, body) => { self.head(""); self.print_fn( - &sig.decl, + sig.decl, sig.header, Some(item.ident.name), param_names, @@ -578,22 +571,22 @@ impl<'a> State<'a> { } self.bclose(item.span); } - hir::ItemKind::GlobalAsm(ref asm) => { + hir::ItemKind::GlobalAsm(asm) => { self.head("global_asm!"); self.print_inline_asm(asm); self.end() } - hir::ItemKind::TyAlias(ref ty, ref generics) => { - self.print_item_type(item, &generics, |state| { + hir::ItemKind::TyAlias(ty, generics) => { + self.print_item_type(item, generics, |state| { state.word_space("="); - state.print_type(&ty); + state.print_type(ty); }); } hir::ItemKind::OpaqueTy(ref opaque_ty) => { - self.print_item_type(item, &opaque_ty.generics, |state| { + self.print_item_type(item, opaque_ty.generics, |state| { let mut real_bounds = Vec::with_capacity(opaque_ty.bounds.len()); - for b in opaque_ty.bounds.iter() { - if let GenericBound::Trait(ref ptr, hir::TraitBoundModifier::Maybe) = *b { + for b in opaque_ty.bounds { + if let GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = b { state.space(); state.word_space("for ?"); state.print_trait_ref(&ptr.trait_ref); @@ -604,39 +597,39 @@ impl<'a> State<'a> { state.print_bounds("= impl", real_bounds); }); } - hir::ItemKind::Enum(ref enum_definition, ref params) => { + hir::ItemKind::Enum(ref enum_definition, params) => { self.print_enum_def(enum_definition, params, item.ident.name, item.span); } - hir::ItemKind::Struct(ref struct_def, ref generics) => { + hir::ItemKind::Struct(ref struct_def, generics) => { self.head("struct"); self.print_struct(struct_def, generics, item.ident.name, item.span, true); } - hir::ItemKind::Union(ref struct_def, ref generics) => { + hir::ItemKind::Union(ref struct_def, generics) => { self.head("union"); self.print_struct(struct_def, generics, item.ident.name, item.span, true); } - hir::ItemKind::Impl(hir::Impl { + hir::ItemKind::Impl(&hir::Impl { unsafety, polarity, defaultness, constness, defaultness_span: _, - ref generics, + generics, ref of_trait, - ref self_ty, + self_ty, items, }) => { self.head(""); - self.print_defaultness(*defaultness); - self.print_unsafety(*unsafety); + self.print_defaultness(defaultness); + self.print_unsafety(unsafety); self.word_nbsp("impl"); if !generics.params.is_empty() { - self.print_generic_params(&generics.params); + self.print_generic_params(generics.params); self.space(); } - if *constness == hir::Constness::Const { + if constness == hir::Constness::Const { self.word_nbsp("const"); } @@ -644,33 +637,33 @@ impl<'a> State<'a> { self.word("!"); } - if let Some(ref t) = of_trait { + if let Some(t) = of_trait { self.print_trait_ref(t); self.space(); self.word_space("for"); } - self.print_type(&self_ty); + self.print_type(self_ty); self.print_where_clause(generics); self.space(); self.bopen(); self.print_inner_attributes(attrs); - for impl_item in *items { + for impl_item in items { self.ann.nested(self, Nested::ImplItem(impl_item.id)); } self.bclose(item.span); } - hir::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, trait_items) => { + hir::ItemKind::Trait(is_auto, unsafety, generics, bounds, trait_items) => { self.head(""); self.print_is_auto(is_auto); self.print_unsafety(unsafety); self.word_nbsp("trait"); self.print_ident(item.ident); - self.print_generic_params(&generics.params); + self.print_generic_params(generics.params); let mut real_bounds = Vec::with_capacity(bounds.len()); - for b in bounds.iter() { - if let GenericBound::Trait(ref ptr, hir::TraitBoundModifier::Maybe) = *b { + for b in bounds { + if let GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = b { self.space(); self.word_space("for ?"); self.print_trait_ref(&ptr.trait_ref); @@ -687,14 +680,14 @@ impl<'a> State<'a> { } self.bclose(item.span); } - hir::ItemKind::TraitAlias(ref generics, ref bounds) => { + hir::ItemKind::TraitAlias(generics, bounds) => { self.head("trait"); self.print_ident(item.ident); - self.print_generic_params(&generics.params); + self.print_generic_params(generics.params); let mut real_bounds = Vec::with_capacity(bounds.len()); // FIXME(durka) this seems to be some quite outdated syntax - for b in bounds.iter() { - if let GenericBound::Trait(ref ptr, hir::TraitBoundModifier::Maybe) = *b { + for b in bounds { + if let GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = b { self.space(); self.word_space("for ?"); self.print_trait_ref(&ptr.trait_ref); @@ -714,7 +707,7 @@ impl<'a> State<'a> { } pub fn print_trait_ref(&mut self, t: &hir::TraitRef<'_>) { - self.print_path(&t.path, false) + self.print_path(t.path, false); } fn print_formal_generic_params(&mut self, generic_params: &[hir::GenericParam<'_>]) { @@ -726,8 +719,8 @@ impl<'a> State<'a> { } fn print_poly_trait_ref(&mut self, t: &hir::PolyTraitRef<'_>) { - self.print_formal_generic_params(&t.bound_generic_params); - self.print_trait_ref(&t.trait_ref) + self.print_formal_generic_params(t.bound_generic_params); + self.print_trait_ref(&t.trait_ref); } pub fn print_enum_def( @@ -739,10 +732,10 @@ impl<'a> State<'a> { ) { self.head("enum"); self.print_name(name); - self.print_generic_params(&generics.params); + self.print_generic_params(generics.params); self.print_where_clause(generics); self.space(); - self.print_variants(&enum_definition.variants, span) + self.print_variants(enum_definition.variants, span); } pub fn print_variants(&mut self, variants: &[hir::Variant<'_>], span: rustc_span::Span) { @@ -776,7 +769,7 @@ impl<'a> State<'a> { print_finalizer: bool, ) { self.print_name(name); - self.print_generic_params(&generics.params); + self.print_generic_params(generics.params); match struct_def { hir::VariantData::Tuple(..) | hir::VariantData::Unit(..) => { if let hir::VariantData::Tuple(..) = struct_def { @@ -784,7 +777,7 @@ impl<'a> State<'a> { self.commasep(Inconsistent, struct_def.fields(), |s, field| { s.maybe_print_comment(field.span.lo()); s.print_outer_attributes(s.attrs(field.hir_id)); - s.print_type(&field.ty) + s.print_type(field.ty); }); self.pclose(); } @@ -807,7 +800,7 @@ impl<'a> State<'a> { self.print_outer_attributes(self.attrs(field.hir_id)); self.print_ident(field.ident); self.word_nbsp(":"); - self.print_type(&field.ty); + self.print_type(field.ty); self.word(","); } @@ -819,7 +812,7 @@ impl<'a> State<'a> { pub fn print_variant(&mut self, v: &hir::Variant<'_>) { self.head(""); let generics = hir::Generics::empty(); - self.print_struct(&v.data, &generics, v.ident.name, v.span, false); + self.print_struct(&v.data, generics, v.ident.name, v.span, false); if let Some(ref d) = v.disr_expr { self.space(); self.word_space("="); @@ -834,7 +827,7 @@ impl<'a> State<'a> { arg_names: &[Ident], body_id: Option<hir::BodyId>, ) { - self.print_fn(&m.decl, m.header, Some(ident.name), generics, arg_names, body_id) + self.print_fn(m.decl, m.header, Some(ident.name), generics, arg_names, body_id); } pub fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) { @@ -843,28 +836,23 @@ impl<'a> State<'a> { self.maybe_print_comment(ti.span.lo()); self.print_outer_attributes(self.attrs(ti.hir_id())); match ti.kind { - hir::TraitItemKind::Const(ref ty, default) => { - self.print_associated_const(ti.ident, &ty, default); + hir::TraitItemKind::Const(ty, default) => { + self.print_associated_const(ti.ident, ty, default); } - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(ref arg_names)) => { - self.print_method_sig(ti.ident, sig, &ti.generics, arg_names, None); + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(arg_names)) => { + self.print_method_sig(ti.ident, sig, ti.generics, arg_names, None); self.word(";"); } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { self.head(""); - self.print_method_sig(ti.ident, sig, &ti.generics, &[], Some(body)); + self.print_method_sig(ti.ident, sig, ti.generics, &[], Some(body)); self.nbsp(); self.end(); // need to close a box self.end(); // need to close a box self.ann.nested(self, Nested::Body(body)); } - hir::TraitItemKind::Type(ref bounds, ref default) => { - self.print_associated_type( - ti.ident, - &ti.generics, - Some(bounds), - default.as_ref().map(|ty| &**ty), - ); + hir::TraitItemKind::Type(bounds, default) => { + self.print_associated_type(ti.ident, ti.generics, Some(bounds), default); } } self.ann.post(self, AnnNode::SubItem(ti.hir_id())) @@ -877,19 +865,19 @@ impl<'a> State<'a> { self.print_outer_attributes(self.attrs(ii.hir_id())); match ii.kind { - hir::ImplItemKind::Const(ref ty, expr) => { - self.print_associated_const(ii.ident, &ty, Some(expr)); + hir::ImplItemKind::Const(ty, expr) => { + self.print_associated_const(ii.ident, ty, Some(expr)); } hir::ImplItemKind::Fn(ref sig, body) => { self.head(""); - self.print_method_sig(ii.ident, sig, &ii.generics, &[], Some(body)); + self.print_method_sig(ii.ident, sig, ii.generics, &[], Some(body)); self.nbsp(); self.end(); // need to close a box self.end(); // need to close a box self.ann.nested(self, Nested::Body(body)); } - hir::ImplItemKind::TyAlias(ref ty) => { - self.print_associated_type(ii.ident, &ii.generics, None, Some(ty)); + hir::ImplItemKind::TyAlias(ty) => { + self.print_associated_type(ii.ident, ii.generics, None, Some(ty)); } } self.ann.post(self, AnnNode::SubItem(ii.hir_id())) @@ -904,10 +892,10 @@ impl<'a> State<'a> { decl(self); self.end(); - if let Some(ref init) = init { + if let Some(init) = init { self.nbsp(); self.word_space("="); - self.print_expr(&init); + self.print_expr(init); } self.end() } @@ -915,17 +903,17 @@ impl<'a> State<'a> { pub fn print_stmt(&mut self, st: &hir::Stmt<'_>) { self.maybe_print_comment(st.span.lo()); match st.kind { - hir::StmtKind::Local(ref loc) => { - self.print_local(loc.init, |this| this.print_local_decl(&loc)); + hir::StmtKind::Local(loc) => { + self.print_local(loc.init, |this| this.print_local_decl(loc)); } hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)), - hir::StmtKind::Expr(ref expr) => { + hir::StmtKind::Expr(expr) => { self.space_if_not_bol(); - self.print_expr(&expr); + self.print_expr(expr); } - hir::StmtKind::Semi(ref expr) => { + hir::StmtKind::Semi(expr) => { self.space_if_not_bol(); - self.print_expr(&expr); + self.print_expr(expr); self.word(";"); } } @@ -966,9 +954,9 @@ impl<'a> State<'a> { for st in blk.stmts { self.print_stmt(st); } - if let Some(ref expr) = blk.expr { + if let Some(expr) = blk.expr { self.space_if_not_bol(); - self.print_expr(&expr); + self.print_expr(expr); self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi())); } self.bclose_maybe_open(blk.span, close_box); @@ -979,21 +967,21 @@ impl<'a> State<'a> { if let Some(els_inner) = els { match els_inner.kind { // Another `else if` block. - hir::ExprKind::If(ref i, ref then, ref e) => { + hir::ExprKind::If(i, then, e) => { self.cbox(INDENT_UNIT - 1); self.ibox(0); self.word(" else if "); - self.print_expr_as_cond(&i); + self.print_expr_as_cond(i); self.space(); - self.print_expr(&then); - self.print_else(e.as_ref().map(|e| &**e)) + self.print_expr(then); + self.print_else(e); } // Final `else` block. - hir::ExprKind::Block(ref b, _) => { + hir::ExprKind::Block(b, _) => { self.cbox(INDENT_UNIT - 1); self.ibox(0); self.word(" else "); - self.print_block(&b) + self.print_block(b); } // Constraints would be great here! _ => { @@ -1048,7 +1036,7 @@ impl<'a> State<'a> { if needs_par { self.popen(); } - if let hir::ExprKind::DropTemps(ref actual_expr) = expr.kind { + if let hir::ExprKind::DropTemps(actual_expr) = expr.kind { self.print_expr(actual_expr); } else { self.print_expr(expr); @@ -1114,7 +1102,7 @@ impl<'a> State<'a> { &mut self, qpath: &hir::QPath<'_>, fields: &[hir::ExprField<'_>], - wth: &Option<&hir::Expr<'_>>, + wth: Option<&hir::Expr<'_>>, ) { self.print_qpath(qpath, true); self.word("{"); @@ -1127,28 +1115,24 @@ impl<'a> State<'a> { s.print_ident(field.ident); s.word_space(":"); } - s.print_expr(&field.expr); + s.print_expr(field.expr); s.end() }, |f| f.span, ); - match *wth { - Some(ref expr) => { - self.ibox(INDENT_UNIT); - if !fields.is_empty() { - self.word(","); - self.space(); - } - self.word(".."); - self.print_expr(&expr); - self.end(); - } - _ => { - if !fields.is_empty() { - self.word(",") - } + if let Some(expr) = wth { + self.ibox(INDENT_UNIT); + if !fields.is_empty() { + self.word(","); + self.space(); } + self.word(".."); + self.print_expr(expr); + self.end(); + } else if !fields.is_empty() { + self.word(","); } + self.word("}"); } @@ -1249,18 +1233,17 @@ impl<'a> State<'a> { Options(ast::InlineAsmOptions), } - let mut args = - vec![AsmArg::Template(ast::InlineAsmTemplatePiece::to_string(&asm.template))]; + let mut args = vec![AsmArg::Template(ast::InlineAsmTemplatePiece::to_string(asm.template))]; args.extend(asm.operands.iter().map(|(o, _)| AsmArg::Operand(o))); if !asm.options.is_empty() { args.push(AsmArg::Options(asm.options)); } self.popen(); - self.commasep(Consistent, &args, |s, arg| match arg { - AsmArg::Template(template) => s.print_string(&template, ast::StrStyle::Cooked), - AsmArg::Operand(op) => match op { - hir::InlineAsmOperand::In { reg, expr } => { + self.commasep(Consistent, &args, |s, arg| match *arg { + AsmArg::Template(ref template) => s.print_string(template, ast::StrStyle::Cooked), + AsmArg::Operand(op) => match *op { + hir::InlineAsmOperand::In { reg, ref expr } => { s.word("in"); s.popen(); s.word(format!("{}", reg)); @@ -1268,8 +1251,8 @@ impl<'a> State<'a> { s.space(); s.print_expr(expr); } - hir::InlineAsmOperand::Out { reg, late, expr } => { - s.word(if *late { "lateout" } else { "out" }); + hir::InlineAsmOperand::Out { reg, late, ref expr } => { + s.word(if late { "lateout" } else { "out" }); s.popen(); s.word(format!("{}", reg)); s.pclose(); @@ -1279,16 +1262,16 @@ impl<'a> State<'a> { None => s.word("_"), } } - hir::InlineAsmOperand::InOut { reg, late, expr } => { - s.word(if *late { "inlateout" } else { "inout" }); + hir::InlineAsmOperand::InOut { reg, late, ref expr } => { + s.word(if late { "inlateout" } else { "inout" }); s.popen(); s.word(format!("{}", reg)); s.pclose(); s.space(); s.print_expr(expr); } - hir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => { - s.word(if *late { "inlateout" } else { "inout" }); + hir::InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => { + s.word(if late { "inlateout" } else { "inout" }); s.popen(); s.word(format!("{}", reg)); s.pclose(); @@ -1301,17 +1284,17 @@ impl<'a> State<'a> { None => s.word("_"), } } - hir::InlineAsmOperand::Const { anon_const } => { + hir::InlineAsmOperand::Const { ref anon_const } => { s.word("const"); s.space(); s.print_anon_const(anon_const); } - hir::InlineAsmOperand::SymFn { anon_const } => { + hir::InlineAsmOperand::SymFn { ref anon_const } => { s.word("sym_fn"); s.space(); s.print_anon_const(anon_const); } - hir::InlineAsmOperand::SymStatic { path, def_id: _ } => { + hir::InlineAsmOperand::SymStatic { ref path, def_id: _ } => { s.word("sym_static"); s.space(); s.print_qpath(path, true); @@ -1363,57 +1346,57 @@ impl<'a> State<'a> { self.ibox(INDENT_UNIT); self.ann.pre(self, AnnNode::Expr(expr)); match expr.kind { - hir::ExprKind::Box(ref expr) => { + hir::ExprKind::Box(expr) => { self.word_space("box"); self.print_expr_maybe_paren(expr, parser::PREC_PREFIX); } - hir::ExprKind::Array(ref exprs) => { + hir::ExprKind::Array(exprs) => { self.print_expr_vec(exprs); } hir::ExprKind::ConstBlock(ref anon_const) => { self.print_expr_anon_const(anon_const); } - hir::ExprKind::Repeat(ref element, ref count) => { - self.print_expr_repeat(&element, count); + hir::ExprKind::Repeat(element, ref count) => { + self.print_expr_repeat(element, count); } - hir::ExprKind::Struct(ref qpath, fields, ref wth) => { + hir::ExprKind::Struct(qpath, fields, wth) => { self.print_expr_struct(qpath, fields, wth); } - hir::ExprKind::Tup(ref exprs) => { + hir::ExprKind::Tup(exprs) => { self.print_expr_tup(exprs); } - hir::ExprKind::Call(ref func, ref args) => { - self.print_expr_call(&func, args); + hir::ExprKind::Call(func, args) => { + self.print_expr_call(func, args); } - hir::ExprKind::MethodCall(ref segment, ref args, _) => { + hir::ExprKind::MethodCall(segment, args, _) => { self.print_expr_method_call(segment, args); } - hir::ExprKind::Binary(op, ref lhs, ref rhs) => { - self.print_expr_binary(op, &lhs, &rhs); + hir::ExprKind::Binary(op, lhs, rhs) => { + self.print_expr_binary(op, lhs, rhs); } - hir::ExprKind::Unary(op, ref expr) => { - self.print_expr_unary(op, &expr); + hir::ExprKind::Unary(op, expr) => { + self.print_expr_unary(op, expr); } - hir::ExprKind::AddrOf(k, m, ref expr) => { - self.print_expr_addr_of(k, m, &expr); + hir::ExprKind::AddrOf(k, m, expr) => { + self.print_expr_addr_of(k, m, expr); } hir::ExprKind::Lit(ref lit) => { - self.print_literal(&lit); + self.print_literal(lit); } - hir::ExprKind::Cast(ref expr, ref ty) => { + hir::ExprKind::Cast(expr, ty) => { let prec = AssocOp::As.precedence() as i8; - self.print_expr_maybe_paren(&expr, prec); + self.print_expr_maybe_paren(expr, prec); self.space(); self.word_space("as"); - self.print_type(&ty); + self.print_type(ty); } - hir::ExprKind::Type(ref expr, ref ty) => { + hir::ExprKind::Type(expr, ty) => { let prec = AssocOp::Colon.precedence() as i8; - self.print_expr_maybe_paren(&expr, prec); + self.print_expr_maybe_paren(expr, prec); self.word_space(":"); - self.print_type(&ty); + self.print_type(ty); } - hir::ExprKind::DropTemps(ref init) => { + hir::ExprKind::DropTemps(init) => { // Print `{`: self.cbox(INDENT_UNIT); self.ibox(0); @@ -1431,25 +1414,25 @@ impl<'a> State<'a> { // Print `}`: self.bclose_maybe_open(expr.span, true); } - hir::ExprKind::Let(hir::Let { pat, ty, init, .. }) => { - self.print_let(pat, *ty, init); + hir::ExprKind::Let(&hir::Let { pat, ty, init, .. }) => { + self.print_let(pat, ty, init); } - hir::ExprKind::If(ref test, ref blk, ref elseopt) => { - self.print_if(&test, &blk, elseopt.as_ref().map(|e| &**e)); + hir::ExprKind::If(test, blk, elseopt) => { + self.print_if(test, blk, elseopt); } - hir::ExprKind::Loop(ref blk, opt_label, _, _) => { + hir::ExprKind::Loop(blk, opt_label, _, _) => { if let Some(label) = opt_label { self.print_ident(label.ident); self.word_space(":"); } self.head("loop"); - self.print_block(&blk); + self.print_block(blk); } - hir::ExprKind::Match(ref expr, arms, _) => { + hir::ExprKind::Match(expr, arms, _) => { self.cbox(INDENT_UNIT); self.ibox(INDENT_UNIT); self.word_nbsp("match"); - self.print_expr_as_cond(&expr); + self.print_expr_as_cond(expr); self.space(); self.bopen(); for arm in arms { @@ -1460,7 +1443,7 @@ impl<'a> State<'a> { hir::ExprKind::Closure { capture_clause, bound_generic_params, - ref fn_decl, + fn_decl, body, fn_decl_span: _, movability: _, @@ -1468,7 +1451,7 @@ impl<'a> State<'a> { self.print_formal_generic_params(bound_generic_params); self.print_capture_clause(capture_clause); - self.print_closure_params(&fn_decl, body); + self.print_closure_params(fn_decl, body); self.space(); // This is a bare expression. @@ -1480,7 +1463,7 @@ impl<'a> State<'a> { // empty box to satisfy the close. self.ibox(0); } - hir::ExprKind::Block(ref blk, opt_label) => { + hir::ExprKind::Block(blk, opt_label) => { if let Some(label) = opt_label { self.print_ident(label.ident); self.word_space(":"); @@ -1489,42 +1472,42 @@ impl<'a> State<'a> { self.cbox(INDENT_UNIT); // head-box, will be closed by print-block after `{` self.ibox(0); - self.print_block(&blk); + self.print_block(blk); } - hir::ExprKind::Assign(ref lhs, ref rhs, _) => { + hir::ExprKind::Assign(lhs, rhs, _) => { let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren(&lhs, prec + 1); + self.print_expr_maybe_paren(lhs, prec + 1); self.space(); self.word_space("="); - self.print_expr_maybe_paren(&rhs, prec); + self.print_expr_maybe_paren(rhs, prec); } - hir::ExprKind::AssignOp(op, ref lhs, ref rhs) => { + hir::ExprKind::AssignOp(op, lhs, rhs) => { let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren(&lhs, prec + 1); + self.print_expr_maybe_paren(lhs, prec + 1); self.space(); self.word(op.node.as_str()); self.word_space("="); - self.print_expr_maybe_paren(&rhs, prec); + self.print_expr_maybe_paren(rhs, prec); } - hir::ExprKind::Field(ref expr, ident) => { + hir::ExprKind::Field(expr, ident) => { self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); self.word("."); self.print_ident(ident); } - hir::ExprKind::Index(ref expr, ref index) => { - self.print_expr_maybe_paren(&expr, parser::PREC_POSTFIX); + hir::ExprKind::Index(expr, index) => { + self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); self.word("["); - self.print_expr(&index); + self.print_expr(index); self.word("]"); } hir::ExprKind::Path(ref qpath) => self.print_qpath(qpath, true), - hir::ExprKind::Break(destination, ref opt_expr) => { + hir::ExprKind::Break(destination, opt_expr) => { self.word("break"); if let Some(label) = destination.label { self.space(); self.print_ident(label.ident); } - if let Some(ref expr) = *opt_expr { + if let Some(expr) = opt_expr { self.space(); self.print_expr_maybe_paren(expr, parser::PREC_JUMP); } @@ -1536,20 +1519,20 @@ impl<'a> State<'a> { self.print_ident(label.ident); } } - hir::ExprKind::Ret(ref result) => { + hir::ExprKind::Ret(result) => { self.word("return"); - if let Some(ref expr) = *result { + if let Some(expr) = result { self.word(" "); - self.print_expr_maybe_paren(&expr, parser::PREC_JUMP); + self.print_expr_maybe_paren(expr, parser::PREC_JUMP); } } - hir::ExprKind::InlineAsm(ref asm) => { + hir::ExprKind::InlineAsm(asm) => { self.word("asm!"); self.print_inline_asm(asm); } - hir::ExprKind::Yield(ref expr, _) => { + hir::ExprKind::Yield(expr, _) => { self.word_space("yield"); - self.print_expr_maybe_paren(&expr, parser::PREC_JUMP); + self.print_expr_maybe_paren(expr, parser::PREC_JUMP); } hir::ExprKind::Err => { self.popen(); @@ -1562,10 +1545,10 @@ impl<'a> State<'a> { } pub fn print_local_decl(&mut self, loc: &hir::Local<'_>) { - self.print_pat(&loc.pat); - if let Some(ref ty) = loc.ty { + self.print_pat(loc.pat); + if let Some(ty) = loc.ty { self.word_space(":"); - self.print_type(&ty); + self.print_type(ty); } } @@ -1596,8 +1579,8 @@ impl<'a> State<'a> { pub fn print_qpath(&mut self, qpath: &hir::QPath<'_>, colons_before_params: bool) { match *qpath { - hir::QPath::Resolved(None, ref path) => self.print_path(path, colons_before_params), - hir::QPath::Resolved(Some(ref qself), ref path) => { + hir::QPath::Resolved(None, path) => self.print_path(path, colons_before_params), + hir::QPath::Resolved(Some(qself), path) => { self.word("<"); self.print_type(qself); self.space(); @@ -1627,11 +1610,11 @@ impl<'a> State<'a> { colons_before_params, ) } - hir::QPath::TypeRelative(ref qself, ref item_segment) => { + hir::QPath::TypeRelative(qself, item_segment) => { // If we've got a compound-qualified-path, let's push an additional pair of angle // brackets, so that we pretty-print `<<A::B>::C>` as `<A::B>::C`, instead of just // `A::B::C` (since the latter could be ambiguous to the user) - if let hir::TyKind::Path(hir::QPath::Resolved(None, _)) = &qself.kind { + if let hir::TyKind::Path(hir::QPath::Resolved(None, _)) = qself.kind { self.print_type(qself); } else { self.word("<"); @@ -1663,7 +1646,7 @@ impl<'a> State<'a> { ) { if generic_args.parenthesized { self.word("("); - self.commasep(Inconsistent, generic_args.inputs(), |s, ty| s.print_type(&ty)); + self.commasep(Inconsistent, generic_args.inputs(), |s, ty| s.print_type(ty)); self.word(")"); self.space_if_not_bol(); @@ -1694,7 +1677,7 @@ impl<'a> State<'a> { start_or_comma(self); self.commasep( Inconsistent, - &generic_args.args, + generic_args.args, |s, generic_arg| match generic_arg { GenericArg::Lifetime(lt) if !elide_lifetimes => s.print_lifetime(lt), GenericArg::Lifetime(_) => {} @@ -1712,7 +1695,7 @@ impl<'a> State<'a> { self.word(".."); } - for binding in generic_args.bindings.iter() { + for binding in generic_args.bindings { start_or_comma(self); self.print_type_binding(binding); } @@ -1731,7 +1714,7 @@ impl<'a> State<'a> { hir::TypeBindingKind::Equality { ref term } => { self.word_space("="); match term { - Term::Ty(ref ty) => self.print_type(ty), + Term::Ty(ty) => self.print_type(ty), Term::Const(ref c) => self.print_anon_const(c), } } @@ -1748,7 +1731,7 @@ impl<'a> State<'a> { // is that it doesn't matter match pat.kind { PatKind::Wild => self.word("_"), - PatKind::Binding(binding_mode, _, ident, ref sub) => { + PatKind::Binding(binding_mode, _, ident, sub) => { match binding_mode { hir::BindingAnnotation::Ref => { self.word_nbsp("ref"); @@ -1764,33 +1747,33 @@ impl<'a> State<'a> { } } self.print_ident(ident); - if let Some(ref p) = *sub { + if let Some(p) = sub { self.word("@"); - self.print_pat(&p); + self.print_pat(p); } } - PatKind::TupleStruct(ref qpath, ref elts, ddpos) => { + PatKind::TupleStruct(ref qpath, elts, ddpos) => { self.print_qpath(qpath, true); self.popen(); if let Some(ddpos) = ddpos { - self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p)); + self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(p)); if ddpos != 0 { self.word_space(","); } self.word(".."); if ddpos != elts.len() { self.word(","); - self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p)); + self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(p)); } } else { - self.commasep(Inconsistent, &elts, |s, p| s.print_pat(&p)); + self.commasep(Inconsistent, elts, |s, p| s.print_pat(p)); } self.pclose(); } PatKind::Path(ref qpath) => { self.print_qpath(qpath, true); } - PatKind::Struct(ref qpath, ref fields, etc) => { + PatKind::Struct(ref qpath, fields, etc) => { self.print_qpath(qpath, true); self.nbsp(); self.word("{"); @@ -1800,14 +1783,14 @@ impl<'a> State<'a> { } self.commasep_cmnt( Consistent, - &fields, + fields, |s, f| { s.cbox(INDENT_UNIT); if !f.is_shorthand { s.print_ident(f.ident); s.word_nbsp(":"); } - s.print_pat(&f.pat); + s.print_pat(f.pat); s.end() }, |f| f.pat.span, @@ -1823,58 +1806,58 @@ impl<'a> State<'a> { } self.word("}"); } - PatKind::Or(ref pats) => { - self.strsep("|", true, Inconsistent, &pats, |s, p| s.print_pat(&p)); + PatKind::Or(pats) => { + self.strsep("|", true, Inconsistent, pats, |s, p| s.print_pat(p)); } - PatKind::Tuple(ref elts, ddpos) => { + PatKind::Tuple(elts, ddpos) => { self.popen(); if let Some(ddpos) = ddpos { - self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p)); + self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(p)); if ddpos != 0 { self.word_space(","); } self.word(".."); if ddpos != elts.len() { self.word(","); - self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p)); + self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(p)); } } else { - self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)); + self.commasep(Inconsistent, elts, |s, p| s.print_pat(p)); if elts.len() == 1 { self.word(","); } } self.pclose(); } - PatKind::Box(ref inner) => { + PatKind::Box(inner) => { let is_range_inner = matches!(inner.kind, PatKind::Range(..)); self.word("box "); if is_range_inner { self.popen(); } - self.print_pat(&inner); + self.print_pat(inner); if is_range_inner { self.pclose(); } } - PatKind::Ref(ref inner, mutbl) => { + PatKind::Ref(inner, mutbl) => { let is_range_inner = matches!(inner.kind, PatKind::Range(..)); self.word("&"); self.word(mutbl.prefix_str()); if is_range_inner { self.popen(); } - self.print_pat(&inner); + self.print_pat(inner); if is_range_inner { self.pclose(); } } - PatKind::Lit(ref e) => self.print_expr(&e), - PatKind::Range(ref begin, ref end, ref end_kind) => { + PatKind::Lit(e) => self.print_expr(e), + PatKind::Range(begin, end, end_kind) => { if let Some(expr) = begin { self.print_expr(expr); } - match *end_kind { + match end_kind { RangeEnd::Included => self.word("..."), RangeEnd::Excluded => self.word(".."), } @@ -1882,24 +1865,24 @@ impl<'a> State<'a> { self.print_expr(expr); } } - PatKind::Slice(ref before, ref slice, ref after) => { + PatKind::Slice(before, slice, after) => { self.word("["); - self.commasep(Inconsistent, &before, |s, p| s.print_pat(&p)); - if let Some(ref p) = *slice { + self.commasep(Inconsistent, before, |s, p| s.print_pat(p)); + if let Some(p) = slice { if !before.is_empty() { self.word_space(","); } if let PatKind::Wild = p.kind { // Print nothing. } else { - self.print_pat(&p); + self.print_pat(p); } self.word(".."); if !after.is_empty() { self.word_space(","); } } - self.commasep(Inconsistent, &after, |s, p| s.print_pat(&p)); + self.commasep(Inconsistent, after, |s, p| s.print_pat(p)); self.word("]"); } } @@ -1908,7 +1891,7 @@ impl<'a> State<'a> { pub fn print_param(&mut self, arg: &hir::Param<'_>) { self.print_outer_attributes(self.attrs(arg.hir_id)); - self.print_pat(&arg.pat); + self.print_pat(arg.pat); } pub fn print_arm(&mut self, arm: &hir::Arm<'_>) { @@ -1920,32 +1903,32 @@ impl<'a> State<'a> { self.cbox(INDENT_UNIT); self.ann.pre(self, AnnNode::Arm(arm)); self.ibox(0); - self.print_outer_attributes(&self.attrs(arm.hir_id)); - self.print_pat(&arm.pat); + self.print_outer_attributes(self.attrs(arm.hir_id)); + self.print_pat(arm.pat); self.space(); if let Some(ref g) = arm.guard { - match g { + match *g { hir::Guard::If(e) => { self.word_space("if"); - self.print_expr(&e); + self.print_expr(e); self.space(); } - hir::Guard::IfLet(hir::Let { pat, ty, init, .. }) => { + hir::Guard::IfLet(&hir::Let { pat, ty, init, .. }) => { self.word_nbsp("if"); - self.print_let(pat, *ty, init); + self.print_let(pat, ty, init); } } } self.word_space("=>"); match arm.body.kind { - hir::ExprKind::Block(ref blk, opt_label) => { + hir::ExprKind::Block(blk, opt_label) => { if let Some(label) = opt_label { self.print_ident(label.ident); self.word_space(":"); } // the block will close the pattern's ibox - self.print_block_unclosed(&blk); + self.print_block_unclosed(blk); // If it is a user-provided unsafe block, print a comma after it if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = blk.rules @@ -1955,7 +1938,7 @@ impl<'a> State<'a> { } _ => { self.end(); // close the ibox for the pattern - self.print_expr(&arm.body); + self.print_expr(arm.body); self.word(","); } } @@ -1978,13 +1961,13 @@ impl<'a> State<'a> { self.nbsp(); self.print_name(name); } - self.print_generic_params(&generics.params); + self.print_generic_params(generics.params); self.popen(); let mut i = 0; // Make sure we aren't supplied *both* `arg_names` and `body_id`. assert!(arg_names.is_empty() || body_id.is_none()); - self.commasep(Inconsistent, &decl.inputs, |s, ty| { + self.commasep(Inconsistent, decl.inputs, |s, ty| { s.ibox(INDENT_UNIT); if let Some(arg_name) = arg_names.get(i) { s.word(arg_name.to_string()); @@ -2011,7 +1994,7 @@ impl<'a> State<'a> { fn print_closure_params(&mut self, decl: &hir::FnDecl<'_>, body_id: hir::BodyId) { self.word("|"); let mut i = 0; - self.commasep(Inconsistent, &decl.inputs, |s, ty| { + self.commasep(Inconsistent, decl.inputs, |s, ty| { s.ibox(INDENT_UNIT); s.ann.nested(s, Nested::BodyParamPat(body_id, i)); @@ -2035,8 +2018,8 @@ impl<'a> State<'a> { self.space_if_not_bol(); self.word_space("->"); match decl.output { - hir::FnRetTy::Return(ref ty) => { - self.print_type(&ty); + hir::FnRetTy::Return(ty) => { + self.print_type(ty); self.maybe_print_comment(ty.span.lo()); } hir::FnRetTy::DefaultReturn(..) => unreachable!(), @@ -2107,20 +2090,20 @@ impl<'a> State<'a> { match param.kind { GenericParamKind::Lifetime { .. } => {} - GenericParamKind::Type { ref default, .. } => { + GenericParamKind::Type { default, .. } => { if let Some(default) = default { self.space(); self.word_space("="); - self.print_type(&default) + self.print_type(default); } } - GenericParamKind::Const { ref ty, ref default } => { + GenericParamKind::Const { ty, ref default } => { self.word_space(":"); self.print_type(ty); - if let Some(ref default) = default { + if let Some(default) = default { self.space(); self.word_space("="); - self.print_anon_const(&default) + self.print_anon_const(default); } } } @@ -2143,7 +2126,7 @@ impl<'a> State<'a> { self.word_space(","); } - match predicate { + match *predicate { hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { bound_generic_params, bounded_ty, @@ -2151,11 +2134,11 @@ impl<'a> State<'a> { .. }) => { self.print_formal_generic_params(bound_generic_params); - self.print_type(&bounded_ty); - self.print_bounds(":", *bounds); + self.print_type(bounded_ty); + self.print_bounds(":", bounds); } hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { - lifetime, + ref lifetime, bounds, .. }) => { @@ -2200,7 +2183,7 @@ impl<'a> State<'a> { pub fn print_mt(&mut self, mt: &hir::MutTy<'_>, print_const: bool) { self.print_mutability(mt.mutbl, print_const); - self.print_type(&mt.ty) + self.print_type(mt.ty); } pub fn print_fn_output(&mut self, decl: &hir::FnDecl<'_>) { @@ -2213,11 +2196,11 @@ impl<'a> State<'a> { self.word_space("->"); match decl.output { hir::FnRetTy::DefaultReturn(..) => unreachable!(), - hir::FnRetTy::Return(ref ty) => self.print_type(&ty), + hir::FnRetTy::Return(ty) => self.print_type(ty), } self.end(); - if let hir::FnRetTy::Return(ref output) = decl.output { + if let hir::FnRetTy::Return(output) = decl.output { self.maybe_print_comment(output.span.lo()); } } @@ -2243,7 +2226,7 @@ impl<'a> State<'a> { asyncness: hir::IsAsync::NotAsync, }, name, - &generics, + generics, arg_names, None, ); @@ -2312,7 +2295,7 @@ fn stmt_ends_with_semi(stmt: &hir::StmtKind<'_>) -> bool { match *stmt { hir::StmtKind::Local(_) => true, hir::StmtKind::Item(_) => false, - hir::StmtKind::Expr(ref e) => expr_requires_semi_to_be_stmt(&e), + hir::StmtKind::Expr(e) => expr_requires_semi_to_be_stmt(e), hir::StmtKind::Semi(..) => false, } } @@ -2351,22 +2334,22 @@ fn contains_exterior_struct_lit(value: &hir::Expr<'_>) -> bool { match value.kind { hir::ExprKind::Struct(..) => true, - hir::ExprKind::Assign(ref lhs, ref rhs, _) - | hir::ExprKind::AssignOp(_, ref lhs, ref rhs) - | hir::ExprKind::Binary(_, ref lhs, ref rhs) => { + hir::ExprKind::Assign(lhs, rhs, _) + | hir::ExprKind::AssignOp(_, lhs, rhs) + | hir::ExprKind::Binary(_, lhs, rhs) => { // `X { y: 1 } + X { y: 2 }` - contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs) + contains_exterior_struct_lit(lhs) || contains_exterior_struct_lit(rhs) } - hir::ExprKind::Unary(_, ref x) - | hir::ExprKind::Cast(ref x, _) - | hir::ExprKind::Type(ref x, _) - | hir::ExprKind::Field(ref x, _) - | hir::ExprKind::Index(ref x, _) => { + hir::ExprKind::Unary(_, x) + | hir::ExprKind::Cast(x, _) + | hir::ExprKind::Type(x, _) + | hir::ExprKind::Field(x, _) + | hir::ExprKind::Index(x, _) => { // `&X { y: 1 }, X { y: 1 }.y` - contains_exterior_struct_lit(&x) + contains_exterior_struct_lit(x) } - hir::ExprKind::MethodCall(.., ref exprs, _) => { + hir::ExprKind::MethodCall(.., exprs, _) => { // `X { y: 1 }.bar(...)` contains_exterior_struct_lit(&exprs[0]) } diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 8938ed78a94..7120d5ad934 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -128,7 +128,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { let region_constraints = self.with_region_constraints(|region_constraints| { make_query_region_constraints( tcx, - region_obligations.iter().map(|(_, r_o)| (r_o.sup_type, r_o.sub_region)), + region_obligations.iter().map(|r_o| (r_o.sup_type, r_o.sub_region)), region_constraints, ) }); diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 67dcb6e708b..8bf1de34a9b 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -37,7 +37,7 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeVisitable}; use rustc_middle::ty::{IntType, UintType}; use rustc_span::{Span, DUMMY_SP}; diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index e319f17b0e6..05556f7d0f9 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -68,7 +68,7 @@ use rustc_middle::dep_graph::DepContext; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ self, error::TypeError, Binder, List, Region, Subst, Ty, TyCtxt, TypeFoldable, - TypeSuperFoldable, + TypeSuperVisitable, TypeVisitable, }; use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; @@ -148,12 +148,10 @@ fn msg_span_from_early_bound_and_free_regions<'tcx>( tcx: TyCtxt<'tcx>, region: ty::Region<'tcx>, ) -> (String, Span) { - let sm = tcx.sess.source_map(); - let scope = region.free_region_binding_scope(tcx).expect_local(); match *region { ty::ReEarlyBound(ref br) => { - let mut sp = sm.guess_head_span(tcx.def_span(scope)); + let mut sp = tcx.def_span(scope); if let Some(param) = tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) { @@ -174,7 +172,7 @@ fn msg_span_from_early_bound_and_free_regions<'tcx>( } else { match fr.bound_region { ty::BoundRegionKind::BrNamed(_, name) => { - let mut sp = sm.guess_head_span(tcx.def_span(scope)); + let mut sp = tcx.def_span(scope); if let Some(param) = tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name)) { @@ -193,7 +191,7 @@ fn msg_span_from_early_bound_and_free_regions<'tcx>( ), _ => ( format!("the lifetime `{}` as defined here", region), - sm.guess_head_span(tcx.def_span(scope)), + tcx.def_span(scope), ), } } @@ -1540,7 +1538,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - impl<'tcx> ty::fold::TypeVisitor<'tcx> for OpaqueTypesVisitor<'tcx> { + impl<'tcx> ty::visit::TypeVisitor<'tcx> for OpaqueTypesVisitor<'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { if let Some((kind, def_id)) = TyCategory::from_ty(self.tcx, t) { let span = self.tcx.def_span(def_id); diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 07dcf3876c8..4d29fc46946 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -315,8 +315,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { body_id: Option<hir::BodyId>, failure_span: Span, arg: GenericArg<'tcx>, - // FIXME(#94483): Either use this or remove it. - _impl_candidates: Vec<ty::TraitRef<'tcx>>, error_code: TypeAnnotationNeeded, should_label_span: bool, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { @@ -534,6 +532,23 @@ enum InferSourceKind<'tcx> { }, } +impl<'tcx> InferSource<'tcx> { + fn from_expansion(&self) -> bool { + let source_from_expansion = match self.kind { + InferSourceKind::LetBinding { insert_span, .. } + | InferSourceKind::ClosureArg { insert_span, .. } + | InferSourceKind::GenericArg { insert_span, .. } => insert_span.from_expansion(), + InferSourceKind::FullyQualifiedMethodCall { receiver, .. } => { + receiver.span.from_expansion() + } + InferSourceKind::ClosureReturn { data, should_wrap_expr, .. } => { + data.span().from_expansion() || should_wrap_expr.map_or(false, Span::from_expansion) + } + }; + source_from_expansion || self.span.from_expansion() + } +} + impl<'tcx> InferSourceKind<'tcx> { fn ty_msg(&self, infcx: &InferCtxt<'_, 'tcx>) -> String { match *self { @@ -604,43 +619,65 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { /// Sources with a small cost are prefer and should result /// in a clearer and idiomatic suggestion. fn source_cost(&self, source: &InferSource<'tcx>) -> usize { - let tcx = self.infcx.tcx; - - fn arg_cost<'tcx>(arg: GenericArg<'tcx>) -> usize { - match arg.unpack() { - GenericArgKind::Lifetime(_) => 0, // erased - GenericArgKind::Type(ty) => ty_cost(ty), - GenericArgKind::Const(_) => 3, // some non-zero value - } + #[derive(Clone, Copy)] + struct CostCtxt<'tcx> { + tcx: TyCtxt<'tcx>, } - fn ty_cost<'tcx>(ty: Ty<'tcx>) -> usize { - match ty.kind() { - ty::Closure(..) => 100, - ty::FnDef(..) => 20, - ty::FnPtr(..) => 10, - ty::Infer(..) => 0, - _ => 1, + impl<'tcx> CostCtxt<'tcx> { + fn arg_cost(self, arg: GenericArg<'tcx>) -> usize { + match arg.unpack() { + GenericArgKind::Lifetime(_) => 0, // erased + GenericArgKind::Type(ty) => self.ty_cost(ty), + GenericArgKind::Const(_) => 3, // some non-zero value + } + } + fn ty_cost(self, ty: Ty<'tcx>) -> usize { + match *ty.kind() { + ty::Closure(..) => 1000, + ty::FnDef(..) => 150, + ty::FnPtr(..) => 30, + ty::Adt(def, substs) => { + 5 + self + .tcx + .generics_of(def.did()) + .own_substs_no_defaults(self.tcx, substs) + .iter() + .map(|&arg| self.arg_cost(arg)) + .sum::<usize>() + } + ty::Tuple(args) => 5 + args.iter().map(|arg| self.ty_cost(arg)).sum::<usize>(), + ty::Ref(_, ty, _) => 2 + self.ty_cost(ty), + ty::Infer(..) => 0, + _ => 1, + } } } // The sources are listed in order of preference here. - match source.kind { - InferSourceKind::LetBinding { ty, .. } => ty_cost(ty), - InferSourceKind::ClosureArg { ty, .. } => 5 + ty_cost(ty), + let tcx = self.infcx.tcx; + let ctx = CostCtxt { tcx }; + let base_cost = match source.kind { + InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty), + InferSourceKind::ClosureArg { ty, .. } => ctx.ty_cost(ty), InferSourceKind::GenericArg { def_id, generic_args, .. } => { let variant_cost = match tcx.def_kind(def_id) { - DefKind::Variant | DefKind::Ctor(CtorOf::Variant, _) => 15, // `None::<u32>` and friends are ugly. - _ => 12, + // `None::<u32>` and friends are ugly. + DefKind::Variant | DefKind::Ctor(CtorOf::Variant, _) => 15, + _ => 10, }; - variant_cost + generic_args.iter().map(|&arg| arg_cost(arg)).sum::<usize>() + variant_cost + generic_args.iter().map(|&arg| ctx.arg_cost(arg)).sum::<usize>() } InferSourceKind::FullyQualifiedMethodCall { substs, .. } => { - 20 + substs.iter().map(|arg| arg_cost(arg)).sum::<usize>() + 20 + substs.iter().map(|arg| ctx.arg_cost(arg)).sum::<usize>() } InferSourceKind::ClosureReturn { ty, should_wrap_expr, .. } => { - 30 + ty_cost(ty) + if should_wrap_expr.is_some() { 10 } else { 0 } + 30 + ctx.ty_cost(ty) + if should_wrap_expr.is_some() { 10 } else { 0 } } - } + }; + + let suggestion_may_apply = if source.from_expansion() { 10000 } else { 0 }; + + base_cost + suggestion_may_apply } /// Uses `fn source_cost` to determine whether this inference source is preferable to @@ -648,6 +685,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn update_infer_source(&mut self, new_source: InferSource<'tcx>) { let cost = self.source_cost(&new_source) + self.attempt; + debug!(?cost); self.attempt += 1; if cost < self.infer_source_cost { self.infer_source_cost = cost; @@ -655,6 +693,11 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { } } + fn node_substs_opt(&self, hir_id: HirId) -> Option<SubstsRef<'tcx>> { + let substs = self.typeck_results.node_substs_opt(hir_id); + self.infcx.resolve_vars_if_possible(substs) + } + fn opt_node_type(&self, hir_id: HirId) -> Option<Ty<'tcx>> { let ty = self.typeck_results.node_type_opt(hir_id); self.infcx.resolve_vars_if_possible(ty) @@ -737,7 +780,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { let tcx = self.infcx.tcx; match expr.kind { hir::ExprKind::Path(ref path) => { - if let Some(substs) = self.typeck_results.node_substs_opt(expr.hir_id) { + if let Some(substs) = self.node_substs_opt(expr.hir_id) { return self.path_inferred_subst_iter(expr.hir_id, substs, path); } } @@ -765,7 +808,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { if generics.has_impl_trait() { None? } - let substs = self.typeck_results.node_substs_opt(expr.hir_id)?; + let substs = self.node_substs_opt(expr.hir_id)?; let span = tcx.hir().span(segment.hir_id?); let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); InsertableGenericArgs { @@ -980,8 +1023,10 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { debug!(?args); let InsertableGenericArgs { insert_span, substs, generics_def_id, def_id } = args; let generics = tcx.generics_of(generics_def_id); - if let Some(argument_index) = - generics.own_substs(substs).iter().position(|&arg| self.generic_arg_is_target(arg)) + if let Some(argument_index) = generics + .own_substs(substs) + .iter() + .position(|&arg| self.generic_arg_contains_target(arg)) { let substs = self.infcx.resolve_vars_if_possible(substs); let generic_args = &generics.own_substs_no_defaults(tcx, substs) @@ -1037,7 +1082,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { .any(|generics| generics.has_impl_trait()) }; if let ExprKind::MethodCall(path, args, span) = expr.kind - && let Some(substs) = self.typeck_results.node_substs_opt(expr.hir_id) + && let Some(substs) = self.node_substs_opt(expr.hir_id) && substs.iter().any(|arg| self.generic_arg_contains_target(arg)) && let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) && self.infcx.tcx.trait_of_item(def_id).is_some() diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 09430a135a3..02928c4aa57 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -10,7 +10,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind}; use rustc_middle::ty::{ - self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperFoldable, TypeVisitor, + self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, }; use rustc_span::symbol::Ident; use rustc_span::Span; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs index f6b49e41d4c..91bf9695dfc 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -11,7 +11,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; use rustc_middle::ty::print::RegionHighlightMode; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperFoldable, TypeVisitor}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_span::{Span, Symbol}; use std::ops::ControlFlow; @@ -88,7 +88,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } } - impl<'tcx> ty::fold::TypeVisitor<'tcx> for HighlightBuilder<'tcx> { + impl<'tcx> ty::visit::TypeVisitor<'tcx> for HighlightBuilder<'tcx> { fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { if !r.has_name() && self.counter <= 3 { self.highlight.highlighting_region(r, self.counter); diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index 42d52446ab6..b356da0be55 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -5,7 +5,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::TyCtxt; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_middle::ty::{self, Binder, DefIdTree, Region, Ty, TypeFoldable}; +use rustc_middle::ty::{self, Binder, DefIdTree, Region, Ty, TypeVisitable}; use rustc_span::Span; /// Information about the anonymous region we are searching for. @@ -142,7 +142,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { fn includes_region( &self, - ty: Binder<'tcx, impl TypeFoldable<'tcx>>, + ty: Binder<'tcx, impl TypeVisitable<'tcx>>, region: ty::BoundRegionKind, ) -> bool { let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(&ty); diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs index 082eb1bf111..fad949a3bc6 100644 --- a/compiler/rustc_infer/src/infer/free_regions.rs +++ b/compiler/rustc_infer/src/infer/free_regions.rs @@ -4,7 +4,6 @@ //! and use that to decide when one free region outlives another, and so forth. use rustc_data_structures::transitive_relation::TransitiveRelation; -use rustc_hir::def_id::DefId; use rustc_middle::ty::{self, Lift, Region, TyCtxt}; /// Combines a `FreeRegionMap` and a `TyCtxt`. @@ -14,16 +13,13 @@ use rustc_middle::ty::{self, Lift, Region, TyCtxt}; pub(crate) struct RegionRelations<'a, 'tcx> { pub tcx: TyCtxt<'tcx>, - /// The context used for debug messages - pub context: DefId, - /// Free-region relationships. pub free_regions: &'a FreeRegionMap<'tcx>, } impl<'a, 'tcx> RegionRelations<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, context: DefId, free_regions: &'a FreeRegionMap<'tcx>) -> Self { - Self { tcx, context, free_regions } + pub fn new(tcx: TyCtxt<'tcx>, free_regions: &'a FreeRegionMap<'tcx>) -> Self { + Self { tcx, free_regions } } pub fn lub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> Region<'tcx> { diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index 024f7409947..84004d2b21f 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -34,7 +34,7 @@ use super::InferCtxt; use rustc_data_structures::fx::FxHashMap; use rustc_middle::infer::unify_key::ToType; use rustc_middle::ty::fold::TypeFolder; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitable}; use std::collections::hash_map::Entry; pub struct TypeFreshener<'a, 'tcx> { diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 68c709a2e24..87fa22b3835 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -120,13 +120,9 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { ) -> LexicalRegionResolutions<'tcx> { let mut var_data = self.construct_var_data(self.tcx()); - // Dorky hack to cause `dump_constraints` to only get called - // if debug mode is enabled: - debug!( - "----() End constraint listing (context={:?}) {:?}---", - self.region_rels.context, - self.dump_constraints(self.region_rels) - ); + if cfg!(debug_assertions) { + self.dump_constraints(); + } let graph = self.construct_graph(); self.expand_givens(&graph); @@ -156,8 +152,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } - fn dump_constraints(&self, free_regions: &RegionRelations<'_, 'tcx>) { - debug!("----() Start constraint listing (context={:?}) ()----", free_regions.context); + #[instrument(level = "debug", skip(self))] + fn dump_constraints(&self) { for (idx, (constraint, _)) in self.data.constraints.iter().enumerate() { debug!("Constraint {} => {:?}", idx, constraint); } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 6f88b83a473..991fd23ab43 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -15,7 +15,6 @@ use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::Rollback; use rustc_data_structures::unify as ut; use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; -use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; @@ -26,6 +25,7 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef}; +use rustc_middle::ty::visit::TypeVisitable; pub use rustc_middle::ty::IntVarValue; use rustc_middle::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt}; use rustc_middle::ty::{ConstVid, FloatVid, IntVid, TyVid}; @@ -147,7 +147,7 @@ pub struct InferCtxtInner<'tcx> { /// for each body-id in this map, which will process the /// obligations within. This is expected to be done 'late enough' /// that all type inference variables have been bound and so forth. - region_obligations: Vec<(hir::HirId, RegionObligation<'tcx>)>, + region_obligations: Vec<RegionObligation<'tcx>>, undo_log: InferCtxtUndoLogs<'tcx>, @@ -171,7 +171,7 @@ impl<'tcx> InferCtxtInner<'tcx> { } #[inline] - pub fn region_obligations(&self) -> &[(hir::HirId, RegionObligation<'tcx>)] { + pub fn region_obligations(&self) -> &[RegionObligation<'tcx>] { &self.region_obligations } @@ -319,7 +319,7 @@ pub struct InferCtxt<'a, 'tcx> { } /// See the `error_reporting` module for more details. -#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable)] pub enum ValuePairs<'tcx> { Regions(ExpectedFound<ty::Region<'tcx>>), Terms(ExpectedFound<ty::Term<'tcx>>), @@ -1267,7 +1267,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// `resolve_vars_if_possible` as well as `fully_resolve`. pub fn resolve_regions( &self, - region_context: DefId, outlives_env: &OutlivesEnvironment<'tcx>, ) -> Vec<RegionResolutionError<'tcx>> { let (var_infos, data) = { @@ -1286,8 +1285,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .into_infos_and_data() }; - let region_rels = - &RegionRelations::new(self.tcx, region_context, outlives_env.free_region_map()); + let region_rels = &RegionRelations::new(self.tcx, outlives_env.free_region_map()); let (lexical_region_resolutions, errors) = lexical_region_resolve::resolve(outlives_env.param_env, region_rels, var_infos, data); @@ -1302,12 +1300,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// result. After this, no more unification operations should be /// done -- or the compiler will panic -- but it is legal to use /// `resolve_vars_if_possible` as well as `fully_resolve`. - pub fn resolve_regions_and_report_errors( - &self, - region_context: DefId, - outlives_env: &OutlivesEnvironment<'tcx>, - ) { - let errors = self.resolve_regions(region_context, outlives_env); + pub fn resolve_regions_and_report_errors(&self, outlives_env: &OutlivesEnvironment<'tcx>) { + let errors = self.resolve_regions(outlives_env); if !self.is_tainted_by_errors() { // As a heuristic, just skip reporting region errors @@ -1445,7 +1439,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// `resolve_vars_if_possible()`. pub fn unresolved_type_vars<T>(&self, value: &T) -> Option<(Ty<'tcx>, Option<Span>)> where - T: TypeFoldable<'tcx>, + T: TypeVisitable<'tcx>, { value.visit_with(&mut resolve::UnresolvedTypeFinder::new(self)).break_value() } diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index 846e7f7b921..bab4f3e9e36 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -27,8 +27,8 @@ use crate::infer::{ConstVarValue, ConstVariableValue}; use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::fold::{TypeFoldable, TypeSuperFoldable, TypeVisitor}; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; use rustc_span::Span; use std::fmt::Debug; @@ -810,7 +810,7 @@ struct ScopeInstantiator<'me, 'tcx> { } impl<'me, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx> { - fn visit_binder<T: TypeFoldable<'tcx>>( + fn visit_binder<T: TypeVisitable<'tcx>>( &mut self, t: &ty::Binder<'tcx, T>, ) -> ControlFlow<Self::BreakTy> { diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index cc36d6a0a4f..26d689f29ee 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -9,7 +9,8 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::subst::{GenericArgKind, Subst}; use rustc_middle::ty::{ - self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor, + self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitor, }; use rustc_span::Span; @@ -461,7 +462,7 @@ impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<OP> where OP: FnMut(ty::Region<'tcx>), { - fn visit_binder<T: TypeFoldable<'tcx>>( + fn visit_binder<T: TypeVisitable<'tcx>>( &mut self, t: &ty::Binder<'tcx, T>, ) -> ControlFlow<Self::BreakTy> { diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs index 5ea096f5cc2..7234660dbcd 100644 --- a/compiler/rustc_infer/src/infer/outlives/components.rs +++ b/compiler/rustc_infer/src/infer/outlives/components.rs @@ -4,7 +4,7 @@ use rustc_data_structures::sso::SsoHashSet; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; use smallvec::{smallvec, SmallVec}; #[derive(Debug)] diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index 9ddda7b92eb..b897de7315a 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -1,8 +1,6 @@ use crate::infer::free_regions::FreeRegionMap; use crate::infer::{GenericKind, InferCtxt}; use crate::traits::query::OutlivesBound; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir as hir; use rustc_middle::ty::{self, ReEarlyBound, ReFree, ReVar, Region}; use super::explicit_outlives_bounds; @@ -31,9 +29,7 @@ pub struct OutlivesEnvironment<'tcx> { pub param_env: ty::ParamEnv<'tcx>, free_region_map: FreeRegionMap<'tcx>, - // Contains, for each body B that we are checking (that is, the fn - // item, but also any nested closures), the set of implied region - // bounds that are in scope in that particular body. + // Contains the implied region bounds in scope for our current body. // // Example: // @@ -43,24 +39,15 @@ pub struct OutlivesEnvironment<'tcx> { // } // body B0 // ``` // - // Here, for body B0, the list would be `[T: 'a]`, because we + // Here, when checking the body B0, the list would be `[T: 'a]`, because we // infer that `T` must outlive `'a` from the implied bounds on the // fn declaration. // - // For the body B1, the list would be `[T: 'a, T: 'b]`, because we + // For the body B1 however, the list would be `[T: 'a, T: 'b]`, because we // also can see that -- within the closure body! -- `T` must // outlive `'b`. This is not necessarily true outside the closure // body, since the closure may never be called. - // - // We collect this map as we descend the tree. We then use the - // results when proving outlives obligations like `T: 'x` later - // (e.g., if `T: 'x` must be proven within the body B1, then we - // know it is true if either `'a: 'x` or `'b: 'x`). - region_bound_pairs_map: FxHashMap<hir::HirId, RegionBoundPairs<'tcx>>, - - // Used to compute `region_bound_pairs_map`: contains the set of - // in-scope region-bound pairs thus far. - region_bound_pairs_accum: RegionBoundPairs<'tcx>, + region_bound_pairs: RegionBoundPairs<'tcx>, } /// "Region-bound pairs" tracks outlives relations that are known to @@ -73,8 +60,7 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { let mut env = OutlivesEnvironment { param_env, free_region_map: Default::default(), - region_bound_pairs_map: Default::default(), - region_bound_pairs_accum: vec![], + region_bound_pairs: Default::default(), }; env.add_outlives_bounds(None, explicit_outlives_bounds(param_env)); @@ -87,62 +73,9 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { &self.free_region_map } - /// Borrows current value of the `region_bound_pairs`. - pub fn region_bound_pairs_map(&self) -> &FxHashMap<hir::HirId, RegionBoundPairs<'tcx>> { - &self.region_bound_pairs_map - } - - /// This is a hack to support the old-school regionck, which - /// processes region constraints from the main function and the - /// closure together. In that context, when we enter a closure, we - /// want to be able to "save" the state of the surrounding a - /// function. We can then add implied bounds and the like from the - /// closure arguments into the environment -- these should only - /// apply in the closure body, so once we exit, we invoke - /// `pop_snapshot_post_typeck_child` to remove them. - /// - /// Example: - /// - /// ```ignore (pseudo-rust) - /// fn foo<T>() { - /// callback(for<'a> |x: &'a T| { - /// // ^^^^^^^ not legal syntax, but probably should be - /// // within this closure body, `T: 'a` holds - /// }) - /// } - /// ``` - /// - /// This "containment" of closure's effects only works so well. In - /// particular, we (intentionally) leak relationships between free - /// regions that are created by the closure's bounds. The case - /// where this is useful is when you have (e.g.) a closure with a - /// signature like `for<'a, 'b> fn(x: &'a &'b u32)` -- in this - /// case, we want to keep the relationship `'b: 'a` in the - /// free-region-map, so that later if we have to take `LUB('b, - /// 'a)` we can get the result `'b`. - /// - /// I have opted to keep **all modifications** to the - /// free-region-map, however, and not just those that concern free - /// variables bound in the closure. The latter seems more correct, - /// but it is not the existing behavior, and I could not find a - /// case where the existing behavior went wrong. In any case, it - /// seems like it'd be readily fixed if we wanted. There are - /// similar leaks around givens that seem equally suspicious, to - /// be honest. --nmatsakis - pub fn push_snapshot_pre_typeck_child(&self) -> usize { - self.region_bound_pairs_accum.len() - } - - /// See `push_snapshot_pre_typeck_child`. - pub fn pop_snapshot_post_typeck_child(&mut self, len: usize) { - self.region_bound_pairs_accum.truncate(len); - } - - /// Save the current set of region-bound pairs under the given `body_id`. - pub fn save_implied_bounds(&mut self, body_id: hir::HirId) { - let old = - self.region_bound_pairs_map.insert(body_id, self.region_bound_pairs_accum.clone()); - assert!(old.is_none()); + /// Borrows current `region_bound_pairs`. + pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> { + &self.region_bound_pairs } /// Processes outlives bounds that are known to hold, whether from implied or other sources. @@ -164,11 +97,10 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound); match outlives_bound { OutlivesBound::RegionSubParam(r_a, param_b) => { - self.region_bound_pairs_accum.push((r_a, GenericKind::Param(param_b))); + self.region_bound_pairs.push((r_a, GenericKind::Param(param_b))); } OutlivesBound::RegionSubProjection(r_a, projection_b) => { - self.region_bound_pairs_accum - .push((r_a, GenericKind::Projection(projection_b))); + self.region_bound_pairs.push((r_a, GenericKind::Projection(projection_b))); } OutlivesBound::RegionSubRegion(r_a, r_b) => { if let (ReEarlyBound(_) | ReFree(_), ReVar(vid_b)) = (r_a.kind(), r_b.kind()) { diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 1c1906f3375..59cf39abe64 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -60,18 +60,16 @@ //! imply that `'b: 'a`. use crate::infer::outlives::components::{push_outlives_components, Component}; +use crate::infer::outlives::env::OutlivesEnvironment; use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::outlives::verify::VerifyBoundCx; use crate::infer::{ self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, UndoLog, VerifyBound, }; use crate::traits::{ObligationCause, ObligationCauseCode}; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeFoldable}; - -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::undo_log::UndoLogs; -use rustc_hir as hir; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeVisitable}; use smallvec::smallvec; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { @@ -80,16 +78,11 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// and later processed by regionck, when full type information is /// available (see `region_obligations` field for more /// information). - pub fn register_region_obligation( - &self, - body_id: hir::HirId, - obligation: RegionObligation<'tcx>, - ) { - debug!("register_region_obligation(body_id={:?}, obligation={:?})", body_id, obligation); - + #[instrument(level = "debug", skip(self))] + pub fn register_region_obligation(&self, obligation: RegionObligation<'tcx>) { let mut inner = self.inner.borrow_mut(); inner.undo_log.push(UndoLog::PushRegionObligation); - inner.region_obligations.push((body_id, obligation)); + inner.region_obligations.push(obligation); } pub fn register_region_obligation_with_cause( @@ -109,14 +102,11 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { ) }); - self.register_region_obligation( - cause.body_id, - RegionObligation { sup_type, sub_region, origin }, - ); + self.register_region_obligation(RegionObligation { sup_type, sub_region, origin }); } /// Trait queries just want to pass back type obligations "as is" - pub fn take_registered_region_obligations(&self) -> Vec<(hir::HirId, RegionObligation<'tcx>)> { + pub fn take_registered_region_obligations(&self) -> Vec<RegionObligation<'tcx>> { std::mem::take(&mut self.inner.borrow_mut().region_obligations) } @@ -144,10 +134,10 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// - `param_env` is the parameter environment for the enclosing function. /// - `body_id` is the body-id whose region obligations are being /// processed. - #[instrument(level = "debug", skip(self, region_bound_pairs_map))] + #[instrument(level = "debug", skip(self, region_bound_pairs))] pub fn process_registered_region_obligations( &self, - region_bound_pairs_map: &FxHashMap<hir::HirId, RegionBoundPairs<'tcx>>, + region_bound_pairs: &RegionBoundPairs<'tcx>, param_env: ty::ParamEnv<'tcx>, ) { assert!( @@ -157,7 +147,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { let my_region_obligations = self.take_registered_region_obligations(); - for (body_id, RegionObligation { sup_type, sub_region, origin }) in my_region_obligations { + for RegionObligation { sup_type, sub_region, origin } in my_region_obligations { debug!( "process_registered_region_obligations: sup_type={:?} sub_region={:?} origin={:?}", sup_type, sub_region, origin @@ -165,18 +155,23 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { let sup_type = self.resolve_vars_if_possible(sup_type); - if let Some(region_bound_pairs) = region_bound_pairs_map.get(&body_id) { - let outlives = - &mut TypeOutlives::new(self, self.tcx, ®ion_bound_pairs, None, param_env); - outlives.type_must_outlive(origin, sup_type, sub_region); - } else { - self.tcx.sess.delay_span_bug( - origin.span(), - &format!("no region-bound-pairs for {:?}", body_id), - ); - } + let outlives = + &mut TypeOutlives::new(self, self.tcx, ®ion_bound_pairs, None, param_env); + outlives.type_must_outlive(origin, sup_type, sub_region); } } + + pub fn check_region_obligations_and_report_errors( + &self, + outlives_env: &OutlivesEnvironment<'tcx>, + ) { + self.process_registered_region_obligations( + outlives_env.region_bound_pairs(), + outlives_env.param_env, + ); + + self.resolve_regions_and_report_errors(outlives_env) + } } /// The `TypeOutlives` struct has the job of "lowering" a `T: 'a` diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index 9f71ebae99e..772e297b7b4 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -1,7 +1,7 @@ use std::collections::hash_map::Entry; use rustc_data_structures::fx::FxHashMap; -use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::TypeVisitable; use rustc_middle::ty::{ self, error::TypeError, diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 19f83e3377a..c5747ecf702 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -165,7 +165,7 @@ pub struct Verify<'tcx> { pub bound: VerifyBound<'tcx>, } -#[derive(Copy, Clone, PartialEq, Eq, Hash, TypeFoldable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)] pub enum GenericKind<'tcx> { Param(ty::ParamTy), Projection(ty::ProjectionTy<'tcx>), @@ -272,7 +272,7 @@ pub enum VerifyBound<'tcx> { /// } /// } /// ``` -#[derive(Debug, Copy, Clone, TypeFoldable)] +#[derive(Debug, Copy, Clone, TypeFoldable, TypeVisitable)] pub struct VerifyIfEq<'tcx> { /// Type which must match the generic `G` pub ty: Ty<'tcx>, diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 1f3cb401314..a6145687429 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -1,8 +1,9 @@ use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use super::{FixupError, FixupResult, InferCtxt, Span}; use rustc_middle::mir; -use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable, TypeVisitor}; -use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitor}; +use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable, TypeVisitable}; use std::ops::ControlFlow; diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index d0c6d8d16eb..b27571275b7 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -4,8 +4,8 @@ use super::SubregionOrigin; use crate::infer::combine::ConstEquateRelation; use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::traits::Obligation; -use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::TyVar; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use std::mem; diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs index 4eafa3329c3..7e42458fda3 100644 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs @@ -23,10 +23,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let mut err = struct_span_err!(self.tcx.sess, sp, E0276, "{}", msg); - if let Some(trait_item_span) = self.tcx.hir().span_if_local(trait_item_def_id) { - let span = self.tcx.sess.source_map().guess_head_span(trait_item_span); + if trait_item_def_id.is_local() { let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); - err.span_label(span, format!("definition of `{}` from trait", item_name)); + err.span_label( + self.tcx.def_span(trait_item_def_id), + format!("definition of `{}` from trait", item_name), + ); } err.span_label(sp, format!("impl has extra requirement {}", requirement)); diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs index b84ed3dc689..932e597509f 100644 --- a/compiler/rustc_infer/src/traits/project.rs +++ b/compiler/rustc_infer/src/traits/project.rs @@ -20,7 +20,7 @@ pub struct MismatchedProjectionTypes<'tcx> { pub err: ty::error::TypeError<'tcx>, } -#[derive(Clone, TypeFoldable)] +#[derive(Clone, TypeFoldable, TypeVisitable)] pub struct Normalized<'tcx, T> { pub value: T, pub obligations: Vec<PredicateObligation<'tcx>>, diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs index 82ee4bb29e8..573d2d1e330 100644 --- a/compiler/rustc_infer/src/traits/structural_impls.rs +++ b/compiler/rustc_infer/src/traits/structural_impls.rs @@ -1,7 +1,8 @@ use crate::traits; use crate::traits::project::Normalized; use rustc_middle::ty; -use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeVisitor}; +use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable}; +use rustc_middle::ty::visit::{TypeVisitable, TypeVisitor}; use std::fmt; use std::ops::ControlFlow; @@ -68,7 +69,9 @@ impl<'tcx, O: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Obligation<'tcx param_env: self.param_env.try_fold_with(folder)?, }) } +} +impl<'tcx, O: TypeVisitable<'tcx>> TypeVisitable<'tcx> for traits::Obligation<'tcx, O> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.predicate.visit_with(visitor)?; self.param_env.visit_with(visitor) diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index fab60b6f609..7c0f2c440d5 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -22,3 +22,4 @@ rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_parse_format = { path = "../rustc_parse_format" } rustc_infer = { path = "../rustc_infer" } rustc_type_ir = { path = "../rustc_type_ir" } +rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs index b33ab40eb39..121fefdc620 100644 --- a/compiler/rustc_lint/src/array_into_iter.rs +++ b/compiler/rustc_lint/src/array_into_iter.rs @@ -1,5 +1,5 @@ use crate::{LateContext, LateLintPass, LintContext}; -use rustc_errors::Applicability; +use rustc_errors::{fluent, Applicability}; use rustc_hir as hir; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; @@ -120,31 +120,30 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { _ => bug!("array type coerced to something other than array or slice"), }; cx.struct_span_lint(ARRAY_INTO_ITER, call.ident.span, |lint| { - let mut diag = lint.build(&format!( - "this method call resolves to `<&{} as IntoIterator>::into_iter` \ - (due to backwards compatibility), \ - but will resolve to <{} as IntoIterator>::into_iter in Rust 2021", - target, target, - )); + let mut diag = lint.build(fluent::lint::array_into_iter); + diag.set_arg("target", target); diag.span_suggestion( call.ident.span, - "use `.iter()` instead of `.into_iter()` to avoid ambiguity", + fluent::lint::use_iter_suggestion, "iter", Applicability::MachineApplicable, ); if self.for_expr_span == expr.span { diag.span_suggestion( receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), - "or remove `.into_iter()` to iterate by value", + fluent::lint::remove_into_iter_suggestion, "", Applicability::MaybeIncorrect, ); } else if receiver_ty.is_array() { diag.multipart_suggestion( - "or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value", + fluent::lint::use_explicit_into_iter_suggestion, vec![ (expr.span.shrink_to_lo(), "IntoIterator::into_iter(".into()), - (receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), ")".into()), + ( + receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), + ")".into(), + ), ], Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index c0cf8c6b76b..a0472f98d72 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -31,14 +31,17 @@ use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust::{self, expr_to_string}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::{Applicability, Diagnostic, DiagnosticStyledString, MultiSpan}; +use rustc_errors::{ + fluent, Applicability, Diagnostic, DiagnosticMessage, DiagnosticStyledString, + LintDiagnosticBuilder, MultiSpan, +}; use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID}; use rustc_hir::{ForeignItemKind, GenericParamKind, HirId, PatKind, PredicateOrigin}; use rustc_index::vec::Idx; -use rustc_middle::lint::{in_external_macro, LintDiagnosticBuilder}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::{LayoutError, LayoutOf}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::GenericArgKind; @@ -99,13 +102,12 @@ impl EarlyLintPass for WhileTrue { if let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind { if let ast::LitKind::Bool(true) = lit.kind { if !lit.span.from_expansion() { - let msg = "denote infinite loops with `loop { ... }`"; let condition_span = e.span.with_hi(cond.span.hi()); cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| { - lint.build(msg) + lint.build(fluent::lint::builtin_while_true) .span_suggestion_short( condition_span, - "use `loop`", + fluent::lint::suggestion, format!( "{}loop", label.map_or_else(String::new, |label| format!( @@ -156,7 +158,7 @@ impl BoxPointers { if let GenericArgKind::Type(leaf_ty) = leaf.unpack() { if leaf_ty.is_box() { cx.struct_span_lint(BOX_POINTERS, span, |lint| { - lint.build(&format!("type uses owned (Box type) pointers: {}", ty)).emit(); + lint.build(fluent::lint::builtin_box_pointers).set_arg("ty", ty).emit(); }); } } @@ -257,26 +259,26 @@ impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns { == Some(cx.tcx.field_index(fieldpat.hir_id, cx.typeck_results())) { cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span, |lint| { - let mut err = lint - .build(&format!("the `{}:` in this pattern is redundant", ident)); let binding = match binding_annot { hir::BindingAnnotation::Unannotated => None, hir::BindingAnnotation::Mutable => Some("mut"), hir::BindingAnnotation::Ref => Some("ref"), hir::BindingAnnotation::RefMut => Some("ref mut"), }; - let ident = if let Some(binding) = binding { + let suggested_ident = if let Some(binding) = binding { format!("{} {}", binding, ident) } else { ident.to_string() }; - err.span_suggestion( - fieldpat.span, - "use shorthand field pattern", - ident, - Applicability::MachineApplicable, - ); - err.emit(); + lint.build(fluent::lint::builtin_non_shorthand_field_patterns) + .set_arg("ident", ident.clone()) + .span_suggestion( + fieldpat.span, + fluent::lint::suggestion, + suggested_ident, + Applicability::MachineApplicable, + ) + .emit(); }); } } @@ -327,26 +329,25 @@ impl UnsafeCode { cx.struct_span_lint(UNSAFE_CODE, span, decorate); } - fn report_overridden_symbol_name(&self, cx: &EarlyContext<'_>, span: Span, msg: &str) { + fn report_overridden_symbol_name( + &self, + cx: &EarlyContext<'_>, + span: Span, + msg: DiagnosticMessage, + ) { self.report_unsafe(cx, span, |lint| { - lint.build(msg) - .note( - "the linker's behavior with multiple libraries exporting duplicate symbol \ - names is undefined and Rust cannot provide guarantees when you manually \ - override them", - ) - .emit(); + lint.build(msg).note(fluent::lint::builtin_overridden_symbol_name).emit(); }) } - fn report_overridden_symbol_section(&self, cx: &EarlyContext<'_>, span: Span, msg: &str) { + fn report_overridden_symbol_section( + &self, + cx: &EarlyContext<'_>, + span: Span, + msg: DiagnosticMessage, + ) { self.report_unsafe(cx, span, |lint| { - lint.build(msg) - .note( - "the program's behavior with overridden link sections on items is unpredictable \ - and Rust cannot provide guarantees when you manually override them", - ) - .emit(); + lint.build(msg).note(fluent::lint::builtin_overridden_symbol_section).emit(); }) } } @@ -355,12 +356,7 @@ impl EarlyLintPass for UnsafeCode { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { if attr.has_name(sym::allow_internal_unsafe) { self.report_unsafe(cx, attr.span, |lint| { - lint.build( - "`allow_internal_unsafe` allows defining \ - macros using unsafe without triggering \ - the `unsafe_code` lint at their call site", - ) - .emit(); + lint.build(fluent::lint::builtin_allow_internal_unsafe).emit(); }); } } @@ -370,7 +366,7 @@ impl EarlyLintPass for UnsafeCode { // Don't warn about generated blocks; that'll just pollute the output. if blk.rules == ast::BlockCheckMode::Unsafe(ast::UserProvided) { self.report_unsafe(cx, blk.span, |lint| { - lint.build("usage of an `unsafe` block").emit(); + lint.build(fluent::lint::builtin_unsafe_block).emit(); }); } } @@ -380,12 +376,12 @@ impl EarlyLintPass for UnsafeCode { match it.kind { ast::ItemKind::Trait(box ast::Trait { unsafety: ast::Unsafe::Yes(_), .. }) => self .report_unsafe(cx, it.span, |lint| { - lint.build("declaration of an `unsafe` trait").emit(); + lint.build(fluent::lint::builtin_unsafe_trait).emit(); }), ast::ItemKind::Impl(box ast::Impl { unsafety: ast::Unsafe::Yes(_), .. }) => self .report_unsafe(cx, it.span, |lint| { - lint.build("implementation of an `unsafe` trait").emit(); + lint.build(fluent::lint::builtin_unsafe_impl).emit(); }), ast::ItemKind::Fn(..) => { @@ -393,7 +389,7 @@ impl EarlyLintPass for UnsafeCode { self.report_overridden_symbol_name( cx, attr.span, - "declaration of a `no_mangle` function", + fluent::lint::builtin_no_mangle_fn, ); } @@ -401,7 +397,7 @@ impl EarlyLintPass for UnsafeCode { self.report_overridden_symbol_name( cx, attr.span, - "declaration of a function with `export_name`", + fluent::lint::builtin_export_name_fn, ); } @@ -409,7 +405,7 @@ impl EarlyLintPass for UnsafeCode { self.report_overridden_symbol_section( cx, attr.span, - "declaration of a function with `link_section`", + fluent::lint::builtin_link_section_fn, ); } } @@ -419,7 +415,7 @@ impl EarlyLintPass for UnsafeCode { self.report_overridden_symbol_name( cx, attr.span, - "declaration of a `no_mangle` static", + fluent::lint::builtin_no_mangle_static, ); } @@ -427,7 +423,7 @@ impl EarlyLintPass for UnsafeCode { self.report_overridden_symbol_name( cx, attr.span, - "declaration of a static with `export_name`", + fluent::lint::builtin_export_name_static, ); } @@ -435,7 +431,7 @@ impl EarlyLintPass for UnsafeCode { self.report_overridden_symbol_section( cx, attr.span, - "declaration of a static with `link_section`", + fluent::lint::builtin_link_section_static, ); } } @@ -450,14 +446,14 @@ impl EarlyLintPass for UnsafeCode { self.report_overridden_symbol_name( cx, attr.span, - "declaration of a `no_mangle` method", + fluent::lint::builtin_no_mangle_method, ); } if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) { self.report_overridden_symbol_name( cx, attr.span, - "declaration of a method with `export_name`", + fluent::lint::builtin_export_name_method, ); } } @@ -475,9 +471,9 @@ impl EarlyLintPass for UnsafeCode { { let msg = match ctxt { FnCtxt::Foreign => return, - FnCtxt::Free => "declaration of an `unsafe` function", - FnCtxt::Assoc(_) if body.is_none() => "declaration of an `unsafe` method", - FnCtxt::Assoc(_) => "implementation of an `unsafe` method", + FnCtxt::Free => fluent::lint::builtin_decl_unsafe_fn, + FnCtxt::Assoc(_) if body.is_none() => fluent::lint::builtin_decl_unsafe_method, + FnCtxt::Assoc(_) => fluent::lint::builtin_impl_unsafe_method, }; self.report_unsafe(cx, span, |lint| { lint.build(msg).emit(); @@ -556,7 +552,6 @@ impl MissingDoc { &self, cx: &LateContext<'_>, def_id: LocalDefId, - sp: Span, article: &'static str, desc: &'static str, ) { @@ -583,13 +578,12 @@ impl MissingDoc { let attrs = cx.tcx.hir().attrs(cx.tcx.hir().local_def_id_to_hir_id(def_id)); let has_doc = attrs.iter().any(has_doc); if !has_doc { - cx.struct_span_lint( - MISSING_DOCS, - cx.tcx.sess.source_map().guess_head_span(sp), - |lint| { - lint.build(&format!("missing documentation for {} {}", article, desc)).emit(); - }, - ); + cx.struct_span_lint(MISSING_DOCS, cx.tcx.def_span(def_id), |lint| { + lint.build(fluent::lint::builtin_missing_doc) + .set_arg("article", article) + .set_arg("desc", desc) + .emit(); + }); } } } @@ -612,13 +606,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_crate(&mut self, cx: &LateContext<'_>) { - self.check_missing_docs_attrs( - cx, - CRATE_DEF_ID, - cx.tcx.def_span(CRATE_DEF_ID), - "the", - "crate", - ); + self.check_missing_docs_attrs(cx, CRATE_DEF_ID, "the", "crate"); } fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { @@ -648,13 +636,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { let (article, desc) = cx.tcx.article_and_description(it.def_id.to_def_id()); - self.check_missing_docs_attrs(cx, it.def_id, it.span, article, desc); + self.check_missing_docs_attrs(cx, it.def_id, article, desc); } fn check_trait_item(&mut self, cx: &LateContext<'_>, trait_item: &hir::TraitItem<'_>) { let (article, desc) = cx.tcx.article_and_description(trait_item.def_id.to_def_id()); - self.check_missing_docs_attrs(cx, trait_item.def_id, trait_item.span, article, desc); + self.check_missing_docs_attrs(cx, trait_item.def_id, article, desc); } fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { @@ -682,23 +670,23 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } let (article, desc) = cx.tcx.article_and_description(impl_item.def_id.to_def_id()); - self.check_missing_docs_attrs(cx, impl_item.def_id, impl_item.span, article, desc); + self.check_missing_docs_attrs(cx, impl_item.def_id, article, desc); } fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'_>) { let (article, desc) = cx.tcx.article_and_description(foreign_item.def_id.to_def_id()); - self.check_missing_docs_attrs(cx, foreign_item.def_id, foreign_item.span, article, desc); + self.check_missing_docs_attrs(cx, foreign_item.def_id, article, desc); } fn check_field_def(&mut self, cx: &LateContext<'_>, sf: &hir::FieldDef<'_>) { if !sf.is_positional() { let def_id = cx.tcx.hir().local_def_id(sf.hir_id); - self.check_missing_docs_attrs(cx, def_id, sf.span, "a", "struct field") + self.check_missing_docs_attrs(cx, def_id, "a", "struct field") } } fn check_variant(&mut self, cx: &LateContext<'_>, v: &hir::Variant<'_>) { - self.check_missing_docs_attrs(cx, cx.tcx.hir().local_def_id(v.id), v.span, "a", "variant"); + self.check_missing_docs_attrs(cx, cx.tcx.hir().local_def_id(v.id), "a", "variant"); } } @@ -783,11 +771,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { .is_ok() { cx.struct_span_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, |lint| { - lint.build( - "type could implement `Copy`; consider adding `impl \ - Copy`", - ) - .emit(); + lint.build(fluent::lint::builtin_missing_copy_impl).emit(); }) } } @@ -863,12 +847,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { if !self.impling_types.as_ref().unwrap().contains(&item.def_id) { cx.struct_span_lint(MISSING_DEBUG_IMPLEMENTATIONS, item.span, |lint| { - lint.build(&format!( - "type does not implement `{}`; consider adding `#[derive(Debug)]` \ - or a manual implementation", - cx.tcx.def_path_str(debug) - )) - .emit(); + lint.build(fluent::lint::builtin_missing_debug_impl) + .set_arg("debug", cx.tcx.def_path_str(debug)) + .emit(); }); } } @@ -946,18 +927,14 @@ impl EarlyLintPass for AnonymousParameters { ("<type>", Applicability::HasPlaceholders) }; - lint.build( - "anonymous parameters are deprecated and will be \ - removed in the next edition", - ) - .span_suggestion( - arg.pat.span, - "try naming the parameter or explicitly \ - ignoring it", - format!("_: {}", ty_snip), - appl, - ) - .emit(); + lint.build(fluent::lint::builtin_anonymous_params) + .span_suggestion( + arg.pat.span, + fluent::lint::suggestion, + format!("_: {}", ty_snip), + appl, + ) + .emit(); }) } } @@ -982,24 +959,6 @@ impl DeprecatedAttr { } } -fn lint_deprecated_attr( - cx: &EarlyContext<'_>, - attr: &ast::Attribute, - msg: &str, - suggestion: Option<&str>, -) { - cx.struct_span_lint(DEPRECATED, attr.span, |lint| { - lint.build(msg) - .span_suggestion_short( - attr.span, - suggestion.unwrap_or("remove this attribute"), - "", - Applicability::MachineApplicable, - ) - .emit(); - }) -} - impl EarlyLintPass for DeprecatedAttr { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { for BuiltinAttribute { name, gate, .. } in &self.depr_attrs { @@ -1011,17 +970,38 @@ impl EarlyLintPass for DeprecatedAttr { _, ) = gate { - let msg = - format!("use of deprecated attribute `{}`: {}. See {}", name, reason, link); - lint_deprecated_attr(cx, attr, &msg, suggestion); + cx.struct_span_lint(DEPRECATED, attr.span, |lint| { + // FIXME(davidtwco) translatable deprecated attr + lint.build(fluent::lint::builtin_deprecated_attr_link) + .set_arg("name", name) + .set_arg("reason", reason) + .set_arg("link", link) + .span_suggestion_short( + attr.span, + suggestion.map(|s| s.into()).unwrap_or( + fluent::lint::builtin_deprecated_attr_default_suggestion, + ), + "", + Applicability::MachineApplicable, + ) + .emit(); + }); } return; } } if attr.has_name(sym::no_start) || attr.has_name(sym::crate_id) { - let path_str = pprust::path_to_string(&attr.get_normal_item().path); - let msg = format!("use of deprecated attribute `{}`: no longer used.", path_str); - lint_deprecated_attr(cx, attr, &msg, None); + cx.struct_span_lint(DEPRECATED, attr.span, |lint| { + lint.build(fluent::lint::builtin_deprecated_attr_used) + .set_arg("name", pprust::path_to_string(&attr.get_normal_item().path)) + .span_suggestion_short( + attr.span, + fluent::lint::builtin_deprecated_attr_default_suggestion, + "", + Applicability::MachineApplicable, + ) + .emit(); + }); } } } @@ -1049,17 +1029,15 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: & if is_doc_comment || attr.has_name(sym::doc) { cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, |lint| { - let mut err = lint.build("unused doc comment"); - err.span_label( - node_span, - format!("rustdoc does not generate documentation for {}", node_kind), - ); + let mut err = lint.build(fluent::lint::builtin_unused_doc_comment); + err.set_arg("kind", node_kind); + err.span_label(node_span, fluent::lint::label); match attr.kind { AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => { - err.help("use `//` for a plain comment"); + err.help(fluent::lint::plain_help); } AttrKind::DocComment(CommentKind::Block, _) => { - err.help("use `/* */` for a plain comment"); + err.help(fluent::lint::block_help); } } err.emit(); @@ -1178,10 +1156,10 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { GenericParamKind::Lifetime { .. } => {} GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { cx.struct_span_lint(NO_MANGLE_GENERIC_ITEMS, span, |lint| { - lint.build("functions generic over types or consts must be mangled") + lint.build(fluent::lint::builtin_no_mangle_generic) .span_suggestion_short( no_mangle_attr.span, - "remove this attribute", + fluent::lint::suggestion, "", // Use of `#[no_mangle]` suggests FFI intent; correct // fix may be to monomorphize source by hand @@ -1205,8 +1183,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { // Const items do not refer to a particular location in memory, and therefore // don't have anything to attach a symbol to cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, |lint| { - let msg = "const items should never be `#[no_mangle]`"; - let mut err = lint.build(msg); + let mut err = lint.build(fluent::lint::builtin_const_no_mangle); // account for "pub const" (#45562) let start = cx @@ -1220,7 +1197,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5)); err.span_suggestion( const_span, - "try a static value", + fluent::lint::suggestion, "pub static", Applicability::MachineApplicable, ); @@ -1285,10 +1262,8 @@ impl<'tcx> LateLintPass<'tcx> for MutableTransmutes { get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind())) { if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not { - let msg = "transmuting &T to &mut T is undefined behavior, \ - even if the reference is unused, consider instead using an UnsafeCell"; cx.struct_span_lint(MUTABLE_TRANSMUTES, expr.span, |lint| { - lint.build(msg).emit(); + lint.build(fluent::lint::builtin_mutable_transmutes).emit(); }); } } @@ -1338,7 +1313,7 @@ impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { if let Some(items) = attr.meta_item_list() { for item in items { cx.struct_span_lint(UNSTABLE_FEATURES, item.span(), |lint| { - lint.build("unstable feature").emit(); + lint.build(fluent::lint::builtin_unstable_features).emit(); }); } } @@ -1400,16 +1375,17 @@ impl UnreachablePub { } let def_span = cx.tcx.sess.source_map().guess_head_span(span); cx.struct_span_lint(UNREACHABLE_PUB, def_span, |lint| { - let mut err = lint.build(&format!("unreachable `pub` {}", what)); + let mut err = lint.build(fluent::lint::builtin_unreachable_pub); + err.set_arg("what", what); err.span_suggestion( vis_span, - "consider restricting its visibility", + fluent::lint::suggestion, "pub(crate)", applicability, ); if exportable { - err.help("or consider exporting it for use by other crates"); + err.help(fluent::lint::help); } err.emit(); }); @@ -1513,11 +1489,7 @@ impl TypeAliasBounds { impl Visitor<'_> for WalkAssocTypes<'_> { fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) { if TypeAliasBounds::is_type_variable_assoc(qpath) { - self.err.span_help( - span, - "use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to \ - associated types in type aliases", - ); + self.err.span_help(span, fluent::lint::builtin_type_alias_bounds_help); } intravisit::walk_qpath(self, qpath, id, span) } @@ -1561,11 +1533,11 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { let mut suggested_changing_assoc_types = false; if !where_spans.is_empty() { cx.lint(TYPE_ALIAS_BOUNDS, |lint| { - let mut err = lint.build("where clauses are not enforced in type aliases"); + let mut err = lint.build(fluent::lint::builtin_type_alias_where_clause); err.set_span(where_spans); err.span_suggestion( type_alias_generics.where_clause_span, - "the clause will not be checked when the type alias is used, and should be removed", + fluent::lint::suggestion, "", Applicability::MachineApplicable, ); @@ -1579,11 +1551,10 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { if !inline_spans.is_empty() { cx.lint(TYPE_ALIAS_BOUNDS, |lint| { - let mut err = - lint.build("bounds on generic parameters are not enforced in type aliases"); + let mut err = lint.build(fluent::lint::builtin_type_alias_generic_bounds); err.set_span(inline_spans); err.multipart_suggestion( - "the bound will not be checked when the type alias is used, and should be removed", + fluent::lint::suggestion, inline_sugg, Applicability::MachineApplicable, ); @@ -1664,7 +1635,7 @@ declare_lint_pass!( impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - use rustc_middle::ty::fold::TypeFoldable; + use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::PredicateKind::*; if cx.tcx.features().trivial_bounds { @@ -1690,12 +1661,10 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { }; if predicate.is_global() { cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| { - lint.build(&format!( - "{} bound {} does not depend on any type \ - or lifetime parameters", - predicate_kind_name, predicate - )) - .emit(); + lint.build(fluent::lint::builtin_trivial_bounds) + .set_arg("predicate_kind_name", predicate_kind_name) + .set_arg("predicate", predicate) + .emit(); }); } } @@ -1796,8 +1765,8 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { }; if let Some((start, end, join)) = endpoints { - let msg = "`...` range patterns are deprecated"; - let suggestion = "use `..=` for an inclusive range"; + let msg = fluent::lint::builtin_ellipsis_inclusive_range_patterns; + let suggestion = fluent::lint::suggestion; if parenthesise { self.node_id = Some(pat.id); let end = expr_to_string(&end); @@ -1806,8 +1775,11 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { None => format!("&(..={})", end), }; if join.edition() >= Edition::Edition2021 { - let mut err = - rustc_errors::struct_span_err!(cx.sess(), pat.span, E0783, "{}", msg,); + let mut err = cx.sess().struct_span_err_with_code( + pat.span, + msg, + rustc_errors::error_code!(E0783), + ); err.span_suggestion( pat.span, suggestion, @@ -1830,8 +1802,11 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { } else { let replace = "..="; if join.edition() >= Edition::Edition2021 { - let mut err = - rustc_errors::struct_span_err!(cx.sess(), pat.span, E0783, "{}", msg,); + let mut err = cx.sess().struct_span_err_with_code( + pat.span, + msg, + rustc_errors::error_code!(E0783), + ); err.span_suggestion_short( join, suggestion, @@ -1930,7 +1905,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems { let attrs = cx.tcx.hir().attrs(it.hir_id()); if let Some(attr) = cx.sess().find_by_name(attrs, sym::rustc_test_marker) { cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, |lint| { - lint.build("cannot test inner items").emit(); + lint.build(fluent::lint::builtin_unnameable_test_items).emit(); }); } } @@ -2048,10 +2023,12 @@ impl KeywordIdents { } cx.struct_span_lint(KEYWORD_IDENTS, ident.span, |lint| { - lint.build(&format!("`{}` is a keyword in the {} edition", ident, next_edition)) + lint.build(fluent::lint::builtin_keyword_idents) + .set_arg("kw", ident.clone()) + .set_arg("next", next_edition) .span_suggestion( ident.span, - "you can use a raw identifier to stay compatible", + fluent::lint::suggestion, format!("r#{}", ident), Applicability::MachineApplicable, ) @@ -2301,13 +2278,10 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { if !lint_spans.is_empty() { cx.struct_span_lint(EXPLICIT_OUTLIVES_REQUIREMENTS, lint_spans.clone(), |lint| { - lint.build("outlives requirements can be inferred") + lint.build(fluent::lint::builtin_explicit_outlives) + .set_arg("count", bound_count) .multipart_suggestion( - if bound_count == 1 { - "remove this bound" - } else { - "remove these bounds" - }, + fluent::lint::suggestion, lint_spans .into_iter() .map(|span| (span, String::new())) @@ -2363,23 +2337,14 @@ impl EarlyLintPass for IncompleteFeatures { .filter(|(&name, _)| features.incomplete(name)) .for_each(|(&name, &span)| { cx.struct_span_lint(INCOMPLETE_FEATURES, span, |lint| { - let mut builder = lint.build(&format!( - "the feature `{}` is incomplete and may not be safe to use \ - and/or cause compiler crashes", - name, - )); + let mut builder = lint.build(fluent::lint::builtin_incomplete_features); + builder.set_arg("name", name); if let Some(n) = rustc_feature::find_feature_issue(name, GateIssue::Language) { - builder.note(&format!( - "see issue #{} <https://github.com/rust-lang/rust/issues/{}> \ - for more information", - n, n, - )); + builder.set_arg("n", n); + builder.note(fluent::lint::note); } if HAS_MIN_FEATURES.contains(&name) { - builder.help(&format!( - "consider using `min_{}` instead, which is more stable and complete", - name, - )); + builder.help(fluent::lint::help); } builder.emit(); }) @@ -2620,6 +2585,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { if let Some((msg, span)) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) { + // FIXME(davidtwco): make translatable cx.struct_span_lint(INVALID_VALUE, expr.span, |lint| { let mut err = lint.build(&format!( "the type `{}` does not permit {}", @@ -2996,23 +2962,19 @@ impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations { let mut found_str = DiagnosticStyledString::new(); found_str.push(this_decl_ty.fn_sig(tcx).to_string(), true); - lint.build(&format!( - "`{}` redeclare{} with a different signature", - this_fi.ident.name, - if orig.get_name() == this_fi.ident.name { - "d".to_string() - } else { - format!("s `{}`", orig.get_name()) - } - )) + lint.build(if orig.get_name() == this_fi.ident.name { + fluent::lint::builtin_clashing_extern_same_name + } else { + fluent::lint::builtin_clashing_extern_diff_name + }) + .set_arg("this_fi", this_fi.ident.name) + .set_arg("orig", orig.get_name()) .span_label( get_relevant_span(orig_fi), - &format!("`{}` previously declared here", orig.get_name()), - ) - .span_label( - get_relevant_span(this_fi), - "this signature doesn't match the previous declaration", + fluent::lint::previous_decl_label, ) + .span_label(get_relevant_span(this_fi), fluent::lint::mismatch_label) + // FIXME(davidtwco): translatable expected/found .note_expected_found(&"", expected_str, &"", found_str) .emit(); }, @@ -3096,8 +3058,8 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { if let rustc_hir::ExprKind::Unary(rustc_hir::UnOp::Deref, expr_deref) = expr.kind { if is_null_ptr(cx, expr_deref) { cx.struct_span_lint(DEREF_NULLPTR, expr.span, |lint| { - let mut err = lint.build("dereferencing a null pointer"); - err.span_label(expr.span, "this code causes undefined behavior when executed"); + let mut err = lint.build(fluent::lint::builtin_deref_nullptr); + err.span_label(expr.span, fluent::lint::label); err.emit(); }); } @@ -3210,9 +3172,7 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { NAMED_ASM_LABELS, Some(target_spans), |diag| { - let mut err = - diag.build("avoid using named labels in inline assembly"); - err.emit(); + diag.build(fluent::lint::builtin_asm_labels).emit(); }, BuiltinLintDiagnostics::NamedAsmLabel( "only local labels of the form `<number>:` should be used in inline asm" diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index eeb66f2d738..83328093e9f 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -22,12 +22,13 @@ use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync; use rustc_errors::{add_elided_lifetime_in_path_suggestion, struct_span_err}; -use rustc_errors::{Applicability, MultiSpan, SuggestionStyle}; +use rustc_errors::{ + Applicability, DecorateLint, LintDiagnosticBuilder, MultiSpan, SuggestionStyle, +}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; -use rustc_middle::lint::LintDiagnosticBuilder; use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::middle::stability; use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; @@ -871,6 +872,17 @@ pub trait LintContext: Sized { decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>), ); + /// Emit a lint at `span` from a lint struct (some type that implements `DecorateLint`, + /// typically generated by `#[derive(LintDiagnostic)]`). + fn emit_spanned_lint<S: Into<MultiSpan>>( + &self, + lint: &'static Lint, + span: S, + decorator: impl for<'a> DecorateLint<'a, ()>, + ) { + self.lookup(lint, Some(span), |diag| decorator.decorate_lint(diag)); + } + fn struct_span_lint<S: Into<MultiSpan>>( &self, lint: &'static Lint, @@ -879,6 +891,13 @@ pub trait LintContext: Sized { ) { self.lookup(lint, Some(span), decorate); } + + /// Emit a lint from a lint struct (some type that implements `DecorateLint`, typically + /// generated by `#[derive(LintDiagnostic)]`). + fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> DecorateLint<'a, ()>) { + self.lookup(lint, None as Option<Span>, |diag| decorator.decorate_lint(diag)); + } + /// Emit a lint at the appropriate level, with no associated span. fn lint( &self, diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs index c5e15a88fdf..f41ee640499 100644 --- a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs +++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs @@ -1,6 +1,7 @@ use crate::{context::LintContext, LateContext, LateLintPass}; +use rustc_errors::fluent; use rustc_hir as hir; -use rustc_middle::ty::{fold::TypeFoldable, Ty}; +use rustc_middle::ty::{visit::TypeVisitable, Ty}; use rustc_span::{symbol::sym, Span}; declare_lint! { @@ -51,19 +52,9 @@ fn enforce_mem_discriminant( if is_non_enum(ty_param) { cx.struct_span_lint(ENUM_INTRINSICS_NON_ENUMS, expr_span, |builder| { builder - .build( - "the return value of `mem::discriminant` is \ - unspecified when called with a non-enum type", - ) - .span_note( - args_span, - &format!( - "the argument to `discriminant` should be a \ - reference to an enum, but it was passed \ - a reference to a `{}`, which is not an enum.", - ty_param, - ), - ) + .build(fluent::lint::enum_intrinsics_mem_discriminant) + .set_arg("ty_param", ty_param) + .span_note(args_span, fluent::lint::note) .emit(); }); } @@ -74,16 +65,9 @@ fn enforce_mem_variant_count(cx: &LateContext<'_>, func_expr: &hir::Expr<'_>, sp if is_non_enum(ty_param) { cx.struct_span_lint(ENUM_INTRINSICS_NON_ENUMS, span, |builder| { builder - .build( - "the return value of `mem::variant_count` is \ - unspecified when called with a non-enum type", - ) - .note(&format!( - "the type parameter of `variant_count` should \ - be an enum, but it was instantiated with \ - the type `{}`, which is not an enum.", - ty_param, - )) + .build(fluent::lint::enum_intrinsics_mem_variant) + .set_arg("ty_param", ty_param) + .note(fluent::lint::note) .emit(); }); } diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 95e3125045d..699e8154318 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -1,4 +1,5 @@ use crate::builtin; +use rustc_errors::fluent; use rustc_hir::HirId; use rustc_middle::ty::query::Providers; use rustc_middle::{lint::LintExpectation, ty::TyCtxt}; @@ -43,13 +44,13 @@ fn emit_unfulfilled_expectation_lint( hir_id, expectation.emission_span, |diag| { - let mut diag = diag.build("this lint expectation is unfulfilled"); + let mut diag = diag.build(fluent::lint::expectation); if let Some(rationale) = expectation.reason { diag.note(rationale.as_str()); } if expectation.is_unfulfilled_lint_expectations { - diag.note("the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message"); + diag.note(fluent::lint::note); } diag.emit(); diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs index fc99d759a03..fe2712525ee 100644 --- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs +++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs @@ -1,7 +1,7 @@ use crate::{EarlyContext, EarlyLintPass, LintContext}; use ast::util::unicode::{contains_text_flow_control_chars, TEXT_FLOW_CONTROL_CHARS}; use rustc_ast as ast; -use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_errors::{fluent, Applicability, SuggestionStyle}; use rustc_span::{BytePos, Span, Symbol}; declare_lint! { @@ -61,41 +61,25 @@ impl HiddenUnicodeCodepoints { .collect(); cx.struct_span_lint(TEXT_DIRECTION_CODEPOINT_IN_LITERAL, span, |lint| { - let mut err = lint.build(&format!( - "unicode codepoint changing visible direction of text present in {}", - label - )); - let (an, s) = match spans.len() { - 1 => ("an ", ""), - _ => ("", "s"), - }; - err.span_label( - span, - &format!( - "this {} contains {}invisible unicode text flow control codepoint{}", - label, an, s, - ), - ); + let mut err = lint.build(fluent::lint::hidden_unicode_codepoints); + err.set_arg("label", label); + err.set_arg("count", spans.len()); + err.span_label(span, fluent::lint::label); + err.note(fluent::lint::note); if point_at_inner_spans { for (c, span) in &spans { err.span_label(*span, format!("{:?}", c)); } } - err.note( - "these kind of unicode codepoints change the way text flows on applications that \ - support them, but can cause confusion because they change the order of \ - characters on the screen", - ); if point_at_inner_spans && !spans.is_empty() { err.multipart_suggestion_with_style( - "if their presence wasn't intentional, you can remove them", + fluent::lint::suggestion_remove, spans.iter().map(|(_, span)| (*span, "".to_string())).collect(), Applicability::MachineApplicable, SuggestionStyle::HideCodeAlways, ); err.multipart_suggestion( - "if you want to keep them but make them visible in your source code, you can \ - escape them", + fluent::lint::suggestion_escape, spans .into_iter() .map(|(c, span)| { @@ -109,16 +93,16 @@ impl HiddenUnicodeCodepoints { // FIXME: in other suggestions we've reversed the inner spans of doc comments. We // should do the same here to provide the same good suggestions as we do for // literals above. - err.note("if their presence wasn't intentional, you can remove them"); - err.note(&format!( - "if you want to keep them but make them visible in your source code, you can \ - escape them: {}", + err.set_arg( + "escaped", spans .into_iter() - .map(|(c, _)| { format!("{:?}", c) }) + .map(|(c, _)| format!("{:?}", c)) .collect::<Vec<String>>() .join(", "), - )); + ); + err.note(fluent::lint::suggestion_remove); + err.note(fluent::lint::no_suggestion_note_escape); } err.emit(); }); diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 56c8635a189..738f475983e 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -3,7 +3,7 @@ use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_ast as ast; -use rustc_errors::Applicability; +use rustc_errors::{fluent, Applicability}; use rustc_hir::def::Res; use rustc_hir::{def_id::DefId, Expr, ExprKind, GenericArg, PatKind, Path, PathSegment, QPath}; use rustc_hir::{HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind}; @@ -36,13 +36,10 @@ impl LateLintPass<'_> for DefaultHashTypes { _ => return, }; cx.struct_span_lint(DEFAULT_HASH_TYPES, path.span, |lint| { - let msg = format!( - "prefer `{}` over `{}`, it has better performance", - replace, - cx.tcx.item_name(def_id) - ); - lint.build(&msg) - .note(&format!("a `use rustc_data_structures::fx::{}` may be necessary", replace)) + lint.build(fluent::lint::default_hash_types) + .set_arg("preferred", replace) + .set_arg("used", cx.tcx.item_name(def_id)) + .note(fluent::lint::note) .emit(); }); } @@ -99,12 +96,9 @@ impl LateLintPass<'_> for QueryStability { let def_id = instance.def_id(); if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) { cx.struct_span_lint(POTENTIAL_QUERY_INSTABILITY, span, |lint| { - let msg = format!( - "using `{}` can result in unstable query results", - cx.tcx.item_name(def_id) - ); - lint.build(&msg) - .note("if you believe this case to be fine, allow this lint and add a comment explaining your rationale") + lint.build(fluent::lint::query_instability) + .set_arg("query", cx.tcx.item_name(def_id)) + .note(fluent::lint::note) .emit(); }) } @@ -146,10 +140,10 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { segment.args.map_or(segment.ident.span, |a| a.span_ext).hi() ); cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, |lint| { - lint.build("usage of `ty::TyKind::<kind>`") + lint.build(fluent::lint::tykind_kind) .span_suggestion( span, - "try using `ty::<kind>` directly", + fluent::lint::suggestion, "ty", Applicability::MaybeIncorrect, // ty maybe needs an import ) @@ -175,10 +169,10 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { if let QPath::TypeRelative(qpath_ty, ..) = qpath && qpath_ty.hir_id == ty.hir_id { - lint.build("usage of `ty::TyKind::<kind>`") + lint.build(fluent::lint::tykind_kind) .span_suggestion( path.span, - "try using `ty::<kind>` directly", + fluent::lint::suggestion, "ty", Applicability::MaybeIncorrect, // ty maybe needs an import ) @@ -193,10 +187,10 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { if let QPath::TypeRelative(qpath_ty, ..) = qpath && qpath_ty.hir_id == ty.hir_id { - lint.build("usage of `ty::TyKind::<kind>`") + lint.build(fluent::lint::tykind_kind) .span_suggestion( path.span, - "try using `ty::<kind>` directly", + fluent::lint::suggestion, "ty", Applicability::MaybeIncorrect, // ty maybe needs an import ) @@ -213,10 +207,10 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { if let QPath::TypeRelative(qpath_ty, ..) = qpath && qpath_ty.hir_id == ty.hir_id { - lint.build("usage of `ty::TyKind::<kind>`") + lint.build(fluent::lint::tykind_kind) .span_suggestion( path.span, - "try using `ty::<kind>` directly", + fluent::lint::suggestion, "ty", Applicability::MaybeIncorrect, // ty maybe needs an import ) @@ -226,15 +220,16 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { } _ => {} } - lint.build("usage of `ty::TyKind`").help("try using `Ty` instead").emit(); + lint.build(fluent::lint::tykind).help(fluent::lint::help).emit(); }) } else if !ty.span.from_expansion() && let Some(t) = is_ty_or_ty_ctxt(cx, &path) { if path.segments.len() > 1 { cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, |lint| { - lint.build(&format!("usage of qualified `ty::{}`", t)) + lint.build(fluent::lint::ty_qualified) + .set_arg("ty", t.clone()) .span_suggestion( path.span, - "try importing it and using it unqualified", + fluent::lint::suggestion, t, // The import probably needs to be changed Applicability::MaybeIncorrect, @@ -330,8 +325,8 @@ impl EarlyLintPass for LintPassImpl { LINT_PASS_IMPL_WITHOUT_MACRO, lint_pass.path.span, |lint| { - lint.build("implementing `LintPass` by hand") - .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead") + lint.build(fluent::lint::lintpass_by_hand) + .help(fluent::lint::help) .emit(); }, ) @@ -371,13 +366,10 @@ impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword { return; } cx.struct_span_lint(EXISTING_DOC_KEYWORD, attr.span, |lint| { - lint.build(&format!( - "Found non-existing keyword `{}` used in \ - `#[doc(keyword = \"...\")]`", - v, - )) - .help("only existing keywords are allowed in core/std") - .emit(); + lint.build(fluent::lint::non_existant_doc_keyword) + .set_arg("keyword", v) + .help(fluent::lint::help) + .emit(); }); } } @@ -422,7 +414,7 @@ impl LateLintPass<'_> for Diagnostics { let Impl { of_trait: Some(of_trait), .. } = impl_ && let Some(def_id) = of_trait.trait_def_id() && let Some(name) = cx.tcx.get_diagnostic_name(def_id) && - matches!(name, sym::SessionDiagnostic | sym::AddSubdiagnostic) + matches!(name, sym::SessionDiagnostic | sym::AddSubdiagnostic | sym::DecorateLint) { found_impl = true; break; @@ -431,8 +423,7 @@ impl LateLintPass<'_> for Diagnostics { debug!(?found_impl); if !found_impl { cx.struct_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, |lint| { - lint.build("diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls") - .emit(); + lint.build(fluent::lint::diag_out_of_impl).emit(); }) } @@ -450,7 +441,7 @@ impl LateLintPass<'_> for Diagnostics { debug!(?found_diagnostic_message); if !found_diagnostic_message { cx.struct_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, |lint| { - lint.build("diagnostics should be created using translatable messages").emit(); + lint.build(fluent::lint::untranslatable_diag).emit(); }) } } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 4773feded12..00e96f20d1a 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -3,13 +3,13 @@ use crate::late::unerased_lint_store; use rustc_ast as ast; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan}; +use rustc_errors::{struct_span_err, Applicability, Diagnostic, LintDiagnosticBuilder, MultiSpan}; use rustc_hir as hir; use rustc_hir::{intravisit, HirId}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::{ - struct_lint_level, LevelAndSource, LintDiagnosticBuilder, LintExpectation, LintLevelMap, - LintLevelSets, LintLevelSource, LintSet, LintStackIndex, COMMAND_LINE, + struct_lint_level, LevelAndSource, LintExpectation, LintLevelMap, LintLevelSets, + LintLevelSource, LintSet, LintStackIndex, COMMAND_LINE, }; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{RegisteredTools, TyCtxt}; @@ -521,7 +521,7 @@ impl<'s> LintLevelsBuilder<'s> { src, Some(sp.into()), |lint| { - let mut err = lint.build(&msg); + let mut err = lint.build(msg); if let Some(new_name) = &renamed { err.span_suggestion( sp, @@ -548,7 +548,7 @@ impl<'s> LintLevelsBuilder<'s> { } else { name.to_string() }; - let mut db = lint.build(&format!("unknown lint: `{}`", name)); + let mut db = lint.build(format!("unknown lint: `{}`", name)); if let Some(suggestion) = suggestion { db.span_suggestion( sp, diff --git a/compiler/rustc_lint/src/methods.rs b/compiler/rustc_lint/src/methods.rs index b6a45676a30..ff5a01749c1 100644 --- a/compiler/rustc_lint/src/methods.rs +++ b/compiler/rustc_lint/src/methods.rs @@ -1,6 +1,7 @@ use crate::LateContext; use crate::LateLintPass; use crate::LintContext; +use rustc_errors::fluent; use rustc_hir::{Expr, ExprKind, PathSegment}; use rustc_middle::ty; use rustc_span::{symbol::sym, ExpnKind, Span}; @@ -88,16 +89,12 @@ fn lint_cstring_as_ptr( if let ty::Adt(adt, _) = substs.type_at(0).kind() { if cx.tcx.is_diagnostic_item(sym::cstring_type, adt.did()) { cx.struct_span_lint(TEMPORARY_CSTRING_AS_PTR, as_ptr_span, |diag| { - let mut diag = diag - .build("getting the inner pointer of a temporary `CString`"); - diag.span_label(as_ptr_span, "this pointer will be invalid"); - diag.span_label( - unwrap.span, - "this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime", - ); - diag.note("pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned"); - diag.help("for more information, see https://doc.rust-lang.org/reference/destructors.html"); - diag.emit(); + diag.build(fluent::lint::cstring_ptr) + .span_label(as_ptr_span, fluent::lint::as_ptr_label) + .span_label(unwrap.span, fluent::lint::unwrap_label) + .note(fluent::lint::note) + .help(fluent::lint::help) + .emit(); }); } } diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index 6182d2b10ed..764003e61a6 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -1,6 +1,7 @@ use crate::{EarlyContext, EarlyLintPass, LintContext}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; +use rustc_errors::fluent; use rustc_span::symbol::Symbol; declare_lint! { @@ -180,13 +181,13 @@ impl EarlyLintPass for NonAsciiIdents { } has_non_ascii_idents = true; cx.struct_span_lint(NON_ASCII_IDENTS, sp, |lint| { - lint.build("identifier contains non-ASCII characters").emit(); + lint.build(fluent::lint::identifier_non_ascii_char).emit(); }); if check_uncommon_codepoints && !symbol_str.chars().all(GeneralSecurityProfile::identifier_allowed) { cx.struct_span_lint(UNCOMMON_CODEPOINTS, sp, |lint| { - lint.build("identifier contains uncommon Unicode codepoints").emit(); + lint.build(fluent::lint::identifier_uncommon_codepoints).emit(); }) } } @@ -216,15 +217,11 @@ impl EarlyLintPass for NonAsciiIdents { .and_modify(|(existing_symbol, existing_span, existing_is_ascii)| { if !*existing_is_ascii || !is_ascii { cx.struct_span_lint(CONFUSABLE_IDENTS, sp, |lint| { - lint.build(&format!( - "identifier pair considered confusable between `{}` and `{}`", - existing_symbol, symbol - )) - .span_label( - *existing_span, - "this is where the previous identifier occurred", - ) - .emit(); + lint.build(fluent::lint::confusable_identifier_pair) + .set_arg("existing_sym", *existing_symbol) + .set_arg("sym", symbol) + .span_label(*existing_span, fluent::lint::label) + .emit(); }); } if *existing_is_ascii && !is_ascii { @@ -326,18 +323,20 @@ impl EarlyLintPass for NonAsciiIdents { for ((sp, ch_list), script_set) in lint_reports { cx.struct_span_lint(MIXED_SCRIPT_CONFUSABLES, sp, |lint| { - let message = format!( - "the usage of Script Group `{}` in this crate consists solely of mixed script confusables", - script_set); - let mut note = "the usage includes ".to_string(); + let mut includes = String::new(); for (idx, ch) in ch_list.into_iter().enumerate() { if idx != 0 { - note += ", "; + includes += ", "; } let char_info = format!("'{}' (U+{:04X})", ch, ch as u32); - note += &char_info; + includes += &char_info; } - lint.build(&message).note(¬e).note("please recheck to make sure their usages are indeed what you want").emit(); + lint.build(fluent::lint::mixed_script_confusables) + .set_arg("set", script_set.to_string()) + .set_arg("includes", includes) + .note(fluent::lint::includes_note) + .note(fluent::lint::note) + .emit(); }); } } diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index 4e7aeca9ce1..cdad2d2e8f9 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -1,6 +1,6 @@ use crate::{LateContext, LateLintPass, LintContext}; use rustc_ast as ast; -use rustc_errors::{pluralize, Applicability}; +use rustc_errors::{fluent, Applicability}; use rustc_hir as hir; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::lint::in_external_macro; @@ -120,9 +120,10 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc } cx.struct_span_lint(NON_FMT_PANICS, arg_span, |lint| { - let mut l = lint.build("panic message is not a string literal"); - l.note(&format!("this usage of {}!() is deprecated; it will be a hard error in Rust 2021", symbol)); - l.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>"); + let mut l = lint.build(fluent::lint::non_fmt_panic); + l.set_arg("name", symbol); + l.note(fluent::lint::note); + l.note(fluent::lint::more_info_note); if !is_arg_inside_call(arg_span, span) { // No clue where this argument is coming from. l.emit(); @@ -130,10 +131,10 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc } if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) { // A case of `panic!(format!(..))`. - l.note(format!("the {}!() macro supports formatting, so there's no need for the format!() macro here", symbol).as_str()); + l.note(fluent::lint::supports_fmt_note); if let Some((open, close, _)) = find_delimiters(cx, arg_span) { l.multipart_suggestion( - "remove the `format!(..)` macro call", + fluent::lint::supports_fmt_suggestion, vec![ (arg_span.until(open.shrink_to_hi()), "".into()), (close.until(arg_span.shrink_to_hi()), "".into()), @@ -153,12 +154,18 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc ); let (suggest_display, suggest_debug) = cx.tcx.infer_ctxt().enter(|infcx| { - let display = is_str || cx.tcx.get_diagnostic_item(sym::Display).map(|t| { - infcx.type_implements_trait(t, ty, InternalSubsts::empty(), cx.param_env).may_apply() - }) == Some(true); - let debug = !display && cx.tcx.get_diagnostic_item(sym::Debug).map(|t| { - infcx.type_implements_trait(t, ty, InternalSubsts::empty(), cx.param_env).may_apply() - }) == Some(true); + let display = is_str + || cx.tcx.get_diagnostic_item(sym::Display).map(|t| { + infcx + .type_implements_trait(t, ty, InternalSubsts::empty(), cx.param_env) + .may_apply() + }) == Some(true); + let debug = !display + && cx.tcx.get_diagnostic_item(sym::Debug).map(|t| { + infcx + .type_implements_trait(t, ty, InternalSubsts::empty(), cx.param_env) + .may_apply() + }) == Some(true); (display, debug) }); @@ -175,17 +182,15 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc if suggest_display { l.span_suggestion_verbose( arg_span.shrink_to_lo(), - "add a \"{}\" format string to Display the message", + fluent::lint::display_suggestion, "\"{}\", ", fmt_applicability, ); } else if suggest_debug { + l.set_arg("ty", ty); l.span_suggestion_verbose( arg_span.shrink_to_lo(), - &format!( - "add a \"{{:?}}\" format string to use the Debug implementation of `{}`", - ty, - ), + fluent::lint::debug_suggestion, "\"{:?}\", ", fmt_applicability, ); @@ -193,15 +198,9 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc if suggest_panic_any { if let Some((open, close, del)) = find_delimiters(cx, span) { + l.set_arg("already_suggested", suggest_display || suggest_debug); l.multipart_suggestion( - &format!( - "{}use std::panic::panic_any instead", - if suggest_display || suggest_debug { - "or " - } else { - "" - }, - ), + fluent::lint::panic_suggestion, if del == '(' { vec![(span.until(open), "std::panic::panic_any".into())] } else { @@ -260,21 +259,19 @@ fn check_panic_str<'tcx>( .collect(), }; cx.struct_span_lint(NON_FMT_PANICS, arg_spans, |lint| { - let mut l = lint.build(match n_arguments { - 1 => "panic message contains an unused formatting placeholder", - _ => "panic message contains unused formatting placeholders", - }); - l.note("this message is not used as a format string when given without arguments, but will be in Rust 2021"); + let mut l = lint.build(fluent::lint::non_fmt_panic_unused); + l.set_arg("count", n_arguments); + l.note(fluent::lint::note); if is_arg_inside_call(arg.span, span) { l.span_suggestion( arg.span.shrink_to_hi(), - &format!("add the missing argument{}", pluralize!(n_arguments)), + fluent::lint::add_args_suggestion, ", ...", Applicability::HasPlaceholders, ); l.span_suggestion( arg.span.shrink_to_lo(), - "or add a \"{}\" format string to use the message literally", + fluent::lint::add_fmt_suggestion, "\"{}\", ", Applicability::MachineApplicable, ); @@ -289,17 +286,15 @@ fn check_panic_str<'tcx>( .map(|(i, _)| fmt_span.from_inner(InnerSpan { start: i, end: i + 1 })) .collect() }); - let msg = match &brace_spans { - Some(v) if v.len() == 1 => "panic message contains a brace", - _ => "panic message contains braces", - }; + let count = brace_spans.as_ref().map(|v| v.len()).unwrap_or(/* any number >1 */ 2); cx.struct_span_lint(NON_FMT_PANICS, brace_spans.unwrap_or_else(|| vec![span]), |lint| { - let mut l = lint.build(msg); - l.note("this message is not used as a format string, but will be in Rust 2021"); + let mut l = lint.build(fluent::lint::non_fmt_panic_braces); + l.set_arg("count", count); + l.note(fluent::lint::note); if is_arg_inside_call(arg.span, span) { l.span_suggestion( arg.span.shrink_to_lo(), - "add a \"{}\" format string to use the message literally", + fluent::lint::suggestion, "\"{}\", ", Applicability::MachineApplicable, ); diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index e1507d0fbb4..33ac2ed02aa 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,7 +1,7 @@ use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_ast as ast; use rustc_attr as attr; -use rustc_errors::Applicability; +use rustc_errors::{fluent, Applicability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::FnKind; @@ -137,22 +137,23 @@ impl NonCamelCaseTypes { if !is_camel_case(name) { cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, |lint| { - let msg = format!("{} `{}` should have an upper camel case name", sort, name); - let mut err = lint.build(&msg); + let mut err = lint.build(fluent::lint::non_camel_case_type); let cc = to_camel_case(name); // We cannot provide meaningful suggestions // if the characters are in the category of "Lowercase Letter". if *name != cc { err.span_suggestion( ident.span, - "convert the identifier to upper camel case", + fluent::lint::suggestion, to_camel_case(name), Applicability::MaybeIncorrect, ); } else { - err.span_label(ident.span, "should have an UpperCamelCase name"); + err.span_label(ident.span, fluent::lint::label); } + err.set_arg("sort", sort); + err.set_arg("name", name); err.emit(); }) } @@ -281,11 +282,10 @@ impl NonSnakeCase { if !is_snake_case(name) { cx.struct_span_lint(NON_SNAKE_CASE, ident.span, |lint| { let sc = NonSnakeCase::to_snake_case(name); - let msg = format!("{} `{}` should have a snake case name", sort, name); - let mut err = lint.build(&msg); + let mut err = lint.build(fluent::lint::non_snake_case); // We cannot provide meaningful suggestions // if the characters are in the category of "Uppercase Letter". - if *name != sc { + if name != sc { // We have a valid span in almost all cases, but we don't have one when linting a crate // name provided via the command line. if !ident.span.is_dummy() { @@ -295,13 +295,13 @@ impl NonSnakeCase { // Instead, recommend renaming the identifier entirely or, if permitted, // escaping it to create a raw identifier. if sc_ident.name.can_be_raw() { - ("rename the identifier or convert it to a snake case raw identifier", sc_ident.to_string()) + (fluent::lint::rename_or_convert_suggestion, sc_ident.to_string()) } else { - err.note(&format!("`{}` cannot be used as a raw identifier", sc)); - ("rename the identifier", String::new()) + err.note(fluent::lint::cannot_convert_note); + (fluent::lint::rename_suggestion, String::new()) } } else { - ("convert the identifier to snake case", sc) + (fluent::lint::convert_suggestion, sc.clone()) }; err.span_suggestion( @@ -311,12 +311,15 @@ impl NonSnakeCase { Applicability::MaybeIncorrect, ); } else { - err.help(&format!("convert the identifier to snake case: `{}`", sc)); + err.help(fluent::lint::help); } } else { - err.span_label(ident.span, "should have a snake_case name"); + err.span_label(ident.span, fluent::lint::label); } + err.set_arg("sort", sort); + err.set_arg("name", name); + err.set_arg("sc", sc); err.emit(); }); } @@ -488,21 +491,22 @@ impl NonUpperCaseGlobals { if name.chars().any(|c| c.is_lowercase()) { cx.struct_span_lint(NON_UPPER_CASE_GLOBALS, ident.span, |lint| { let uc = NonSnakeCase::to_snake_case(&name).to_uppercase(); - let mut err = - lint.build(&format!("{} `{}` should have an upper case name", sort, name)); + let mut err = lint.build(fluent::lint::non_upper_case_global); // We cannot provide meaningful suggestions // if the characters are in the category of "Lowercase Letter". if *name != uc { err.span_suggestion( ident.span, - "convert the identifier to upper case", + fluent::lint::suggestion, uc, Applicability::MaybeIncorrect, ); } else { - err.span_label(ident.span, "should have an UPPER_CASE name"); + err.span_label(ident.span, fluent::lint::label); } + err.set_arg("sort", sort); + err.set_arg("name", name); err.emit(); }) } diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 675bee738a6..11a752ff097 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -1,7 +1,8 @@ use crate::context::LintContext; -use crate::rustc_middle::ty::TypeFoldable; +use crate::rustc_middle::ty::TypeVisitable; use crate::LateContext; use crate::LateLintPass; +use rustc_errors::fluent; use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind}; use rustc_middle::ty; @@ -80,7 +81,6 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { ) { return; } - let method = &call.ident.name; let receiver = &elements[0]; let receiver_ty = cx.typeck_results().expr_ty(receiver); let expr_ty = cx.typeck_results().expr_ty_adjusted(expr); @@ -90,19 +90,14 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { return; } let expr_span = expr.span; - let note = format!( - "the type `{:?}` which `{}` is being called on is the same as \ - the type returned from `{}`, so the method call does not do \ - anything and can be removed", - receiver_ty, method, method, - ); - let span = expr_span.with_lo(receiver.span.hi()); cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| { - let method = &call.ident.name; - let message = - format!("call to `.{}()` on a reference in this situation does nothing", &method,); - lint.build(&message).span_label(span, "unnecessary method call").note(¬e).emit(); + lint.build(fluent::lint::noop_method_call) + .set_arg("method", call.ident.name) + .set_arg("receiver_ty", receiver_ty) + .span_label(span, fluent::lint::label) + .note(fluent::lint::note) + .emit(); }); } } diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index 2c8b41d7214..af5e5faf1f5 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -1,5 +1,5 @@ use crate::{LateContext, LateLintPass, LintContext}; -use rustc_errors::Applicability; +use rustc_errors::{fluent, Applicability}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::{GenericArg, PathSegment, QPath, TyKind}; @@ -30,10 +30,11 @@ impl<'tcx> LateLintPass<'tcx> for PassByValue { } if let Some(t) = path_for_pass_by_value(cx, &inner_ty) { cx.struct_span_lint(PASS_BY_VALUE, ty.span, |lint| { - lint.build(&format!("passing `{}` by reference", t)) + lint.build(fluent::lint::pass_by_value) + .set_arg("ty", t.clone()) .span_suggestion( ty.span, - "try passing by value", + fluent::lint::suggestion, t, // Changing type of function argument Applicability::MaybeIncorrect, diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs index f06a8b8f4b0..26f41345383 100644 --- a/compiler/rustc_lint/src/redundant_semicolon.rs +++ b/compiler/rustc_lint/src/redundant_semicolon.rs @@ -1,6 +1,6 @@ use crate::{EarlyContext, EarlyLintPass, LintContext}; use rustc_ast::{Block, StmtKind}; -use rustc_errors::Applicability; +use rustc_errors::{fluent, Applicability}; use rustc_span::Span; declare_lint! { @@ -49,12 +49,10 @@ fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, boo } cx.struct_span_lint(REDUNDANT_SEMICOLONS, span, |lint| { - let (msg, rem) = if multiple { - ("unnecessary trailing semicolons", "remove these semicolons") - } else { - ("unnecessary trailing semicolon", "remove this semicolon") - }; - lint.build(msg).span_suggestion(span, rem, "", Applicability::MaybeIncorrect).emit(); + lint.build(fluent::lint::redundant_semicolons) + .set_arg("multiple", multiple) + .span_suggestion(span, fluent::lint::suggestion, "", Applicability::MaybeIncorrect) + .emit(); }); } } diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index 81d308ee347..df1587c5948 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -1,6 +1,7 @@ use crate::LateContext; use crate::LateLintPass; use crate::LintContext; +use rustc_errors::fluent; use rustc_hir as hir; use rustc_span::symbol::sym; @@ -103,13 +104,10 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return }; - let msg = format!( - "bounds on `{}` are most likely incorrect, consider instead \ - using `{}` to detect whether a type can be trivially dropped", - predicate, - cx.tcx.def_path_str(needs_drop) - ); - lint.build(&msg).emit(); + lint.build(fluent::lint::drop_trait_constraints) + .set_arg("predicate", predicate) + .set_arg("needs_drop", cx.tcx.def_path_str(needs_drop)) + .emit(); }); } } @@ -126,12 +124,9 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return }; - let msg = format!( - "types that do not implement `Drop` can still have drop glue, consider \ - instead using `{}` to detect whether a type is trivially dropped", - cx.tcx.def_path_str(needs_drop) - ); - lint.build(&msg).emit(); + lint.build(fluent::lint::drop_glue) + .set_arg("needs_drop", cx.tcx.def_path_str(needs_drop)) + .emit(); }); } } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 5579e4d19cf..be4843c7ff1 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -2,12 +2,13 @@ use crate::{LateContext, LateLintPass, LintContext}; use rustc_ast as ast; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::Applicability; +use rustc_errors::{fluent, Applicability, DiagnosticMessage}; use rustc_hir as hir; use rustc_hir::{is_range_literal, Expr, ExprKind, Node}; +use rustc_macros::LintDiagnostic; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton}; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable}; +use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; use rustc_span::source_map; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol, DUMMY_SP}; @@ -139,7 +140,8 @@ fn lint_overflowing_range_endpoint<'tcx>( // overflowing and only by 1. if eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max { cx.struct_span_lint(OVERFLOWING_LITERALS, parent_expr.span, |lint| { - let mut err = lint.build(&format!("range endpoint is out of range for `{}`", ty)); + let mut err = lint.build(fluent::lint::range_endpoint_out_of_range); + err.set_arg("ty", ty); if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) { use ast::{LitIntType, LitKind}; // We need to preserve the literal's suffix, @@ -153,7 +155,7 @@ fn lint_overflowing_range_endpoint<'tcx>( let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); err.span_suggestion( parent_expr.span, - "use an inclusive range instead", + fluent::lint::suggestion, suggestion, Applicability::MachineApplicable, ); @@ -229,38 +231,35 @@ fn report_bin_hex_error( (t.name_str(), actually.to_string()) } }; - let mut err = lint.build(&format!("literal out of range for `{}`", t)); + let mut err = lint.build(fluent::lint::overflowing_bin_hex); if negative { // If the value is negative, // emits a note about the value itself, apart from the literal. - err.note(&format!( - "the literal `{}` (decimal `{}`) does not fit into \ - the type `{}`", - repr_str, val, t - )); - err.note(&format!("and the value `-{}` will become `{}{}`", repr_str, actually, t)); + err.note(fluent::lint::negative_note); + err.note(fluent::lint::negative_becomes_note); } else { - err.note(&format!( - "the literal `{}` (decimal `{}`) does not fit into \ - the type `{}` and will become `{}{}`", - repr_str, val, t, actually, t - )); + err.note(fluent::lint::positive_note); } if let Some(sugg_ty) = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative) { + err.set_arg("suggestion_ty", sugg_ty); if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { let (sans_suffix, _) = repr_str.split_at(pos); err.span_suggestion( expr.span, - &format!("consider using the type `{}` instead", sugg_ty), + fluent::lint::suggestion, format!("{}{}", sans_suffix, sugg_ty), Applicability::MachineApplicable, ); } else { - err.help(&format!("consider using the type `{}` instead", sugg_ty)); + err.help(fluent::lint::help); } } + err.set_arg("ty", t); + err.set_arg("lit", repr_str); + err.set_arg("dec", val); + err.set_arg("actually", actually); err.emit(); }); } @@ -353,21 +352,23 @@ fn lint_int_literal<'tcx>( } cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| { - let mut err = lint.build(&format!("literal out of range for `{}`", t.name_str())); - err.note(&format!( - "the literal `{}` does not fit into the type `{}` whose range is `{}..={}`", + let mut err = lint.build(fluent::lint::overflowing_int); + err.set_arg("ty", t.name_str()); + err.set_arg( + "lit", cx.sess() .source_map() .span_to_snippet(lit.span) .expect("must get snippet from literal"), - t.name_str(), - min, - max, - )); + ); + err.set_arg("min", min); + err.set_arg("max", max); + err.note(fluent::lint::note); if let Some(sugg_ty) = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative) { - err.help(&format!("consider using the type `{}` instead", sugg_ty)); + err.set_arg("suggestion_ty", sugg_ty); + err.help(fluent::lint::help); } err.emit(); }); @@ -395,10 +396,10 @@ fn lint_uint_literal<'tcx>( hir::ExprKind::Cast(..) => { if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { cx.struct_span_lint(OVERFLOWING_LITERALS, par_e.span, |lint| { - lint.build("only `u8` can be cast into `char`") + lint.build(fluent::lint::only_cast_u8_to_char) .span_suggestion( par_e.span, - "use a `char` literal instead", + fluent::lint::suggestion, format!("'\\u{{{:X}}}'", lit_val), Applicability::MachineApplicable, ) @@ -429,17 +430,18 @@ fn lint_uint_literal<'tcx>( return; } cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| { - lint.build(&format!("literal out of range for `{}`", t.name_str())) - .note(&format!( - "the literal `{}` does not fit into the type `{}` whose range is `{}..={}`", + lint.build(fluent::lint::overflowing_uint) + .set_arg("ty", t.name_str()) + .set_arg( + "lit", cx.sess() .source_map() .span_to_snippet(lit.span) .expect("must get snippet from literal"), - t.name_str(), - min, - max, - )) + ) + .set_arg("min", min) + .set_arg("max", max) + .note(fluent::lint::note) .emit(); }); } @@ -471,16 +473,16 @@ fn lint_literal<'tcx>( }; if is_infinite == Ok(true) { cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| { - lint.build(&format!("literal out of range for `{}`", t.name_str())) - .note(&format!( - "the literal `{}` does not fit into the type `{}` and will be converted to `{}::INFINITY`", + lint.build(fluent::lint::overflowing_literal) + .set_arg("ty", t.name_str()) + .set_arg( + "lit", cx.sess() .source_map() .span_to_snippet(lit.span) .expect("must get snippet from literal"), - t.name_str(), - t.name_str(), - )) + ) + .note(fluent::lint::note) .emit(); }); } @@ -501,7 +503,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { hir::ExprKind::Binary(binop, ref l, ref r) => { if is_comparison(binop) && !check_limits(cx, binop, &l, &r) { cx.struct_span_lint(UNUSED_COMPARISONS, e.span, |lint| { - lint.build("comparison is useless due to type limits").emit(); + lint.build(fluent::lint::unused_comparisons).emit(); }); } } @@ -663,7 +665,7 @@ struct ImproperCTypesVisitor<'a, 'tcx> { enum FfiResult<'tcx> { FfiSafe, FfiPhantom(Ty<'tcx>), - FfiUnsafe { ty: Ty<'tcx>, reason: String, help: Option<String> }, + FfiUnsafe { ty: Ty<'tcx>, reason: DiagnosticMessage, help: Option<DiagnosticMessage> }, } pub(crate) fn nonnull_optimization_guaranteed<'tcx>( @@ -823,8 +825,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { self.emit_ffi_unsafe_type_lint( ty, sp, - "passing raw arrays by value is not FFI-safe", - Some("consider passing a pointer to the array"), + fluent::lint::improper_ctypes_array_reason, + Some(fluent::lint::improper_ctypes_array_help), ); true } else { @@ -867,11 +869,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } else { // All fields are ZSTs; this means that the type should behave // like (), which is FFI-unsafe - FfiUnsafe { - ty, - reason: "this struct contains only zero-sized fields".into(), - help: None, - } + FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_struct_zst, help: None } } } else { // We can't completely trust repr(C) markings; make sure the fields are @@ -885,7 +883,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiPhantom(..) if def.is_enum() => { return FfiUnsafe { ty, - reason: "this enum contains a PhantomData field".into(), + reason: fluent::lint::improper_ctypes_enum_phantomdata, help: None, }; } @@ -921,7 +919,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } else { return FfiUnsafe { ty, - reason: "box cannot be represented as a single pointer".to_string(), + reason: fluent::lint::improper_ctypes_box, help: None, }; } @@ -931,17 +929,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } match def.adt_kind() { AdtKind::Struct | AdtKind::Union => { - let kind = if def.is_struct() { "struct" } else { "union" }; - if !def.repr().c() && !def.repr().transparent() { return FfiUnsafe { ty, - reason: format!("this {} has unspecified layout", kind), - help: Some(format!( - "consider adding a `#[repr(C)]` or \ - `#[repr(transparent)]` attribute to this {}", - kind - )), + reason: if def.is_struct() { + fluent::lint::improper_ctypes_struct_layout_reason + } else { + fluent::lint::improper_ctypes_union_layout_reason + }, + help: if def.is_struct() { + Some(fluent::lint::improper_ctypes_struct_layout_help) + } else { + Some(fluent::lint::improper_ctypes_union_layout_help) + }, }; } @@ -950,7 +950,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if is_non_exhaustive && !def.did().is_local() { return FfiUnsafe { ty, - reason: format!("this {} is non-exhaustive", kind), + reason: if def.is_struct() { + fluent::lint::improper_ctypes_struct_non_exhaustive + } else { + fluent::lint::improper_ctypes_union_non_exhaustive + }, help: None, }; } @@ -958,8 +962,16 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if def.non_enum_variant().fields.is_empty() { return FfiUnsafe { ty, - reason: format!("this {} has no fields", kind), - help: Some(format!("consider adding a member to this {}", kind)), + reason: if def.is_struct() { + fluent::lint::improper_ctypes_struct_fieldless_reason + } else { + fluent::lint::improper_ctypes_union_fieldless_reason + }, + help: if def.is_struct() { + Some(fluent::lint::improper_ctypes_struct_fieldless_help) + } else { + Some(fluent::lint::improper_ctypes_union_fieldless_help) + }, }; } @@ -979,13 +991,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if repr_nullable_ptr(self.cx, ty, self.mode).is_none() { return FfiUnsafe { ty, - reason: "enum has no representation hint".into(), - help: Some( - "consider adding a `#[repr(C)]`, \ - `#[repr(transparent)]`, or integer `#[repr(...)]` \ - attribute to this enum" - .into(), - ), + reason: fluent::lint::improper_ctypes_enum_repr_reason, + help: Some(fluent::lint::improper_ctypes_enum_repr_help), }; } } @@ -993,7 +1000,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if def.is_variant_list_non_exhaustive() && !def.did().is_local() { return FfiUnsafe { ty, - reason: "this enum is non-exhaustive".into(), + reason: fluent::lint::improper_ctypes_non_exhaustive, help: None, }; } @@ -1004,7 +1011,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if is_non_exhaustive && !variant.def_id.is_local() { return FfiUnsafe { ty, - reason: "this enum has non-exhaustive variants".into(), + reason: fluent::lint::improper_ctypes_non_exhaustive_variant, help: None, }; } @@ -1022,39 +1029,37 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Char => FfiUnsafe { ty, - reason: "the `char` type has no C equivalent".into(), - help: Some("consider using `u32` or `libc::wchar_t` instead".into()), + reason: fluent::lint::improper_ctypes_char_reason, + help: Some(fluent::lint::improper_ctypes_char_help), }, - ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => FfiUnsafe { - ty, - reason: "128-bit integers don't currently have a known stable ABI".into(), - help: None, - }, + ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => { + FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_128bit, help: None } + } // Primitive types with a stable representation. ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe, ty::Slice(_) => FfiUnsafe { ty, - reason: "slices have no C equivalent".into(), - help: Some("consider using a raw pointer instead".into()), + reason: fluent::lint::improper_ctypes_slice_reason, + help: Some(fluent::lint::improper_ctypes_slice_help), }, ty::Dynamic(..) => { - FfiUnsafe { ty, reason: "trait objects have no C equivalent".into(), help: None } + FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_dyn, help: None } } ty::Str => FfiUnsafe { ty, - reason: "string slices have no C equivalent".into(), - help: Some("consider using `*const u8` and a length instead".into()), + reason: fluent::lint::improper_ctypes_str_reason, + help: Some(fluent::lint::improper_ctypes_str_help), }, ty::Tuple(..) => FfiUnsafe { ty, - reason: "tuples have unspecified layout".into(), - help: Some("consider using a struct instead".into()), + reason: fluent::lint::improper_ctypes_tuple_reason, + help: Some(fluent::lint::improper_ctypes_tuple_help), }, ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) @@ -1085,12 +1090,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if self.is_internal_abi(sig.abi()) { return FfiUnsafe { ty, - reason: "this function pointer has Rust-specific calling convention".into(), - help: Some( - "consider using an `extern fn(...) -> ...` \ - function pointer instead" - .into(), - ), + reason: fluent::lint::improper_ctypes_fnptr_reason, + help: Some(fluent::lint::improper_ctypes_fnptr_help), }; } @@ -1121,7 +1122,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // While opaque types are checked for earlier, if a projection in a struct field // normalizes to an opaque type, then it will reach this branch. ty::Opaque(..) => { - FfiUnsafe { ty, reason: "opaque types have no C equivalent".into(), help: None } + FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_opaque, help: None } } // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe, @@ -1147,8 +1148,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { &mut self, ty: Ty<'tcx>, sp: Span, - note: &str, - help: Option<&str>, + note: DiagnosticMessage, + help: Option<DiagnosticMessage>, ) { let lint = match self.mode { CItemKind::Declaration => IMPROPER_CTYPES, @@ -1160,18 +1161,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { CItemKind::Declaration => "block", CItemKind::Definition => "fn", }; - let mut diag = lint.build(&format!( - "`extern` {} uses type `{}`, which is not FFI-safe", - item_description, ty - )); - diag.span_label(sp, "not FFI-safe"); + let mut diag = lint.build(fluent::lint::improper_ctypes); + diag.set_arg("ty", ty); + diag.set_arg("desc", item_description); + diag.span_label(sp, fluent::lint::label); if let Some(help) = help { diag.help(help); } diag.note(note); if let ty::Adt(def, _) = ty.kind() { if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) { - diag.span_note(sp, "the type is defined here"); + diag.span_note(sp, fluent::lint::note); } } diag.emit(); @@ -1183,7 +1183,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, } - impl<'a, 'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'a, 'tcx> { + impl<'a, 'tcx> ty::visit::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'a, 'tcx> { type BreakTy = Ty<'tcx>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { @@ -1208,7 +1208,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } if let Some(ty) = ty.visit_with(&mut ProhibitOpaqueTypes { cx: self.cx }).break_value() { - self.emit_ffi_unsafe_type_lint(ty, sp, "opaque types have no C equivalent", None); + self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint::improper_ctypes_opaque, None); true } else { false @@ -1250,13 +1250,18 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { match self.check_type_for_ffi(&mut FxHashSet::default(), ty) { FfiResult::FfiSafe => {} FfiResult::FfiPhantom(ty) => { - self.emit_ffi_unsafe_type_lint(ty, sp, "composed only of `PhantomData`", None); + self.emit_ffi_unsafe_type_lint( + ty, + sp, + fluent::lint::improper_ctypes_only_phantomdata, + None, + ); } // If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic // argument, which after substitution, is `()`, then this branch can be hit. FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => {} FfiResult::FfiUnsafe { ty, reason, help } => { - self.emit_ffi_unsafe_type_lint(ty, sp, &reason, help.as_deref()); + self.emit_ffi_unsafe_type_lint(ty, sp, reason, help); } } } @@ -1383,12 +1388,9 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences { VARIANT_SIZE_DIFFERENCES, enum_definition.variants[largest_index].span, |lint| { - lint.build(&format!( - "enum variant is more than three times \ - larger ({} bytes) than the next largest", - largest - )) - .emit(); + lint.build(fluent::lint::variant_size_differences) + .set_arg("largest", largest) + .emit(); }, ); } @@ -1511,13 +1513,13 @@ impl InvalidAtomicOrdering { { cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, |diag| { if method == sym::load { - diag.build("atomic loads cannot have `Release` or `AcqRel` ordering") - .help("consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`") + diag.build(fluent::lint::atomic_ordering_load) + .help(fluent::lint::help) .emit() } else { debug_assert_eq!(method, sym::store); - diag.build("atomic stores cannot have `Acquire` or `AcqRel` ordering") - .help("consider using ordering modes `Release`, `SeqCst` or `Relaxed`") + diag.build(fluent::lint::atomic_ordering_store) + .help(fluent::lint::help) .emit(); } }); @@ -1532,8 +1534,8 @@ impl InvalidAtomicOrdering { && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed) { cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, |diag| { - diag.build("memory fences cannot have `Relaxed` ordering") - .help("consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`") + diag.build(fluent::lint::atomic_ordering_fence) + .help(fluent::lint::help) .emit(); }); } @@ -1552,15 +1554,20 @@ impl InvalidAtomicOrdering { let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return }; if matches!(fail_ordering, sym::Release | sym::AcqRel) { - cx.struct_span_lint(INVALID_ATOMIC_ORDERING, fail_order_arg.span, |diag| { - diag.build(&format!( - "`{method}`'s failure ordering may not be `Release` or `AcqRel`, \ - since a failed `{method}` does not result in a write", - )) - .span_label(fail_order_arg.span, "invalid failure ordering") - .help("consider using `Acquire` or `Relaxed` failure ordering instead") - .emit(); - }); + #[derive(LintDiagnostic)] + #[lint(lint::atomic_ordering_invalid)] + #[help] + struct InvalidAtomicOrderingDiag { + method: Symbol, + #[label] + fail_order_arg_span: Span, + } + + cx.emit_spanned_lint( + INVALID_ATOMIC_ORDERING, + fail_order_arg.span, + InvalidAtomicOrderingDiag { method, fail_order_arg_span: fail_order_arg.span }, + ); } let Some(success_ordering) = Self::match_ordering(cx, success_order_arg) else { return }; @@ -1577,18 +1584,20 @@ impl InvalidAtomicOrdering { fail_ordering }; cx.struct_span_lint(INVALID_ATOMIC_ORDERING, success_order_arg.span, |diag| { - diag.build(&format!( - "`{method}`'s success ordering must be at least as strong as its failure ordering" - )) - .span_label(fail_order_arg.span, format!("`{fail_ordering}` failure ordering")) - .span_label(success_order_arg.span, format!("`{success_ordering}` success ordering")) - .span_suggestion_short( - success_order_arg.span, - format!("consider using `{success_suggestion}` success ordering instead"), - format!("std::sync::atomic::Ordering::{success_suggestion}"), - Applicability::MaybeIncorrect, - ) - .emit(); + diag.build(fluent::lint::atomic_ordering_invalid_fail_success) + .set_arg("method", method) + .set_arg("fail_ordering", fail_ordering) + .set_arg("success_ordering", success_ordering) + .set_arg("success_suggestion", success_suggestion) + .span_label(fail_order_arg.span, fluent::lint::fail_label) + .span_label(success_order_arg.span, fluent::lint::success_label) + .span_suggestion_short( + success_order_arg.span, + fluent::lint::suggestion, + format!("std::sync::atomic::Ordering::{success_suggestion}"), + Applicability::MaybeIncorrect, + ) + .emit(); }); } } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 73f353e62c1..53269d18527 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -3,7 +3,7 @@ use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext} use rustc_ast as ast; use rustc_ast::util::{classify, parser}; use rustc_ast::{ExprKind, StmtKind}; -use rustc_errors::{pluralize, Applicability, MultiSpan}; +use rustc_errors::{fluent, pluralize, Applicability, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -155,22 +155,23 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { if let Some(must_use_op) = must_use_op { cx.struct_span_lint(UNUSED_MUST_USE, expr.span, |lint| { - let mut lint = lint.build(&format!("unused {} that must be used", must_use_op)); - lint.span_label(expr.span, &format!("the {} produces a value", must_use_op)); - lint.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "use `let _ = ...` to ignore the resulting value", - "let _ = ", - Applicability::MachineApplicable, - ); - lint.emit(); + lint.build(fluent::lint::unused_op) + .set_arg("op", must_use_op) + .span_label(expr.span, fluent::lint::label) + .span_suggestion_verbose( + expr.span.shrink_to_lo(), + fluent::lint::suggestion, + "let _ = ", + Applicability::MachineApplicable, + ) + .emit(); }); op_warned = true; } if !(type_permits_lack_of_use || fn_warned || op_warned) { cx.struct_span_lint(UNUSED_RESULTS, s.span, |lint| { - lint.build(&format!("unused result of type `{}`", ty)).emit(); + lint.build(fluent::lint::unused_result).set_arg("ty", ty).emit(); }); } @@ -267,23 +268,27 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { }, ty::Closure(..) => { cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { - let mut err = lint.build(&format!( - "unused {}closure{}{} that must be used", - descr_pre, plural_suffix, descr_post, - )); - err.note("closures are lazy and do nothing unless called"); - err.emit(); + // FIXME(davidtwco): this isn't properly translatable becauses of the + // pre/post strings + lint.build(fluent::lint::unused_closure) + .set_arg("count", plural_len) + .set_arg("pre", descr_pre) + .set_arg("post", descr_post) + .note(fluent::lint::note) + .emit(); }); true } ty::Generator(..) => { cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { - let mut err = lint.build(&format!( - "unused {}generator{}{} that must be used", - descr_pre, plural_suffix, descr_post, - )); - err.note("generators are lazy and do nothing unless resumed"); - err.emit(); + // FIXME(davidtwco): this isn't properly translatable becauses of the + // pre/post strings + lint.build(fluent::lint::unused_generator) + .set_arg("count", plural_len) + .set_arg("pre", descr_pre) + .set_arg("post", descr_post) + .note(fluent::lint::note) + .emit(); }); true } @@ -305,13 +310,12 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ) -> bool { if let Some(attr) = cx.tcx.get_attr(def_id, sym::must_use) { cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { - let msg = format!( - "unused {}`{}`{} that must be used", - descr_pre_path, - cx.tcx.def_path_str(def_id), - descr_post_path - ); - let mut err = lint.build(&msg); + // FIXME(davidtwco): this isn't properly translatable becauses of the pre/post + // strings + let mut err = lint.build(fluent::lint::unused_def); + err.set_arg("pre", descr_pre_path); + err.set_arg("post", descr_post_path); + err.set_arg("def", cx.tcx.def_path_str(def_id)); // check for #[must_use = "..."] if let Some(note) = attr.value_str() { err.note(note.as_str()); @@ -356,20 +360,20 @@ impl<'tcx> LateLintPass<'tcx> for PathStatements { cx.struct_span_lint(PATH_STATEMENTS, s.span, |lint| { let ty = cx.typeck_results().expr_ty(expr); if ty.needs_drop(cx.tcx, cx.param_env) { - let mut lint = lint.build("path statement drops value"); + let mut lint = lint.build(fluent::lint::path_statement_drop); if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) { lint.span_suggestion( s.span, - "use `drop` to clarify the intent", + fluent::lint::suggestion, format!("drop({});", snippet), Applicability::MachineApplicable, ); } else { - lint.span_help(s.span, "use `drop` to clarify the intent"); + lint.span_help(s.span, fluent::lint::suggestion); } lint.emit(); } else { - lint.build("path statement with no effect").emit(); + lint.build(fluent::lint::path_statement_no_effect).emit(); } }); } @@ -540,15 +544,19 @@ trait UnusedDelimLint { } cx.struct_span_lint(self.lint(), MultiSpan::from(vec![spans.0, spans.1]), |lint| { - let span_msg = format!("unnecessary {} around {}", Self::DELIM_STR, msg); - let mut err = lint.build(&span_msg); let replacement = vec![ (spans.0, if keep_space.0 { " ".into() } else { "".into() }), (spans.1, if keep_space.1 { " ".into() } else { "".into() }), ]; - let suggestion = format!("remove these {}", Self::DELIM_STR); - err.multipart_suggestion(&suggestion, replacement, Applicability::MachineApplicable); - err.emit(); + lint.build(fluent::lint::unused_delim) + .set_arg("delim", Self::DELIM_STR) + .set_arg("item", msg) + .multipart_suggestion( + fluent::lint::suggestion, + replacement, + Applicability::MachineApplicable, + ) + .emit(); }); } @@ -1110,7 +1118,7 @@ impl UnusedImportBraces { }; cx.struct_span_lint(UNUSED_IMPORT_BRACES, item.span, |lint| { - lint.build(&format!("braces around {} is unnecessary", node_name)).emit(); + lint.build(fluent::lint::unused_import_braces).set_arg("node", node_name).emit(); }); } } @@ -1161,15 +1169,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAllocation { for adj in cx.typeck_results().expr_adjustments(e) { if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind { cx.struct_span_lint(UNUSED_ALLOCATION, e.span, |lint| { - let msg = match m { - adjustment::AutoBorrowMutability::Not => { - "unnecessary allocation, use `&` instead" - } + lint.build(match m { + adjustment::AutoBorrowMutability::Not => fluent::lint::unused_allocation, adjustment::AutoBorrowMutability::Mut { .. } => { - "unnecessary allocation, use `&mut` instead" + fluent::lint::unused_allocation_mut } - }; - lint.build(msg).emit(); + }) + .emit(); }); } } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index d0c86527189..027f377b0ac 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -1,125 +1,54 @@ #![deny(unused_must_use)] -use crate::diagnostics::error::{ - invalid_nested_attr, span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, - SessionDiagnosticDeriveError, -}; -use crate::diagnostics::utils::{ - report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path, - Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce, -}; -use proc_macro2::{Ident, TokenStream}; -use quote::{format_ident, quote}; -use std::collections::HashMap; -use std::str::FromStr; -use syn::{ - parse_quote, spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type, -}; -use synstructure::{BindingInfo, Structure}; +use crate::diagnostics::diagnostic_builder::{DiagnosticDeriveBuilder, DiagnosticDeriveKind}; +use crate::diagnostics::error::{span_err, DiagnosticDeriveError}; +use crate::diagnostics::utils::{build_field_mapping, SetOnce}; +use proc_macro2::TokenStream; +use quote::quote; +use syn::spanned::Spanned; +use synstructure::Structure; /// The central struct for constructing the `into_diagnostic` method from an annotated struct. pub(crate) struct SessionDiagnosticDerive<'a> { structure: Structure<'a>, - builder: SessionDiagnosticDeriveBuilder, + sess: syn::Ident, + builder: DiagnosticDeriveBuilder, } impl<'a> SessionDiagnosticDerive<'a> { pub(crate) fn new(diag: syn::Ident, sess: syn::Ident, structure: Structure<'a>) -> Self { - // Build the mapping of field names to fields. This allows attributes to peek values from - // other fields. - let mut fields_map = HashMap::new(); - - // Convenience bindings. - let ast = structure.ast(); - - if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data { - for field in fields.iter() { - if let Some(ident) = &field.ident { - fields_map.insert(ident.to_string(), quote! { &self.#ident }); - } - } - } - Self { - builder: SessionDiagnosticDeriveBuilder { + builder: DiagnosticDeriveBuilder { diag, - sess, - fields: fields_map, + fields: build_field_mapping(&structure), kind: None, code: None, slug: None, }, + sess, structure, } } pub(crate) fn into_tokens(self) -> TokenStream { - let SessionDiagnosticDerive { mut structure, mut builder } = self; + let SessionDiagnosticDerive { mut structure, sess, mut builder } = self; let ast = structure.ast(); - let attrs = &ast.attrs; - let (implementation, param_ty) = { if let syn::Data::Struct(..) = ast.data { - let preamble = { - let preamble = attrs.iter().map(|attr| { - builder - .generate_structure_code(attr) - .unwrap_or_else(|v| v.to_compile_error()) - }); - - quote! { - #(#preamble)*; - } - }; - - // Keep track of which fields are subdiagnostics or have no attributes. - let mut subdiagnostics_or_empty = std::collections::HashSet::new(); - - // Generates calls to `span_label` and similar functions based on the attributes - // on fields. Code for suggestions uses formatting machinery and the value of - // other fields - because any given field can be referenced multiple times, it - // should be accessed through a borrow. When passing fields to `add_subdiagnostic` - // or `set_arg` (which happens below) for Fluent, we want to move the data, so that - // has to happen in a separate pass over the fields. - let attrs = structure - .clone() - .filter(|field_binding| { - let attrs = &field_binding.ast().attrs; - - (!attrs.is_empty() - && attrs.iter().all(|attr| { - "subdiagnostic" - != attr.path.segments.last().unwrap().ident.to_string() - })) - || { - subdiagnostics_or_empty.insert(field_binding.binding.clone()); - false - } - }) - .each(|field_binding| builder.generate_field_attrs_code(field_binding)); - - structure.bind_with(|_| synstructure::BindStyle::Move); - // When a field has attributes like `#[label]` or `#[note]` then it doesn't - // need to be passed as an argument to the diagnostic. But when a field has no - // attributes or a `#[subdiagnostic]` attribute then it must be passed as an - // argument to the diagnostic so that it can be referred to by Fluent messages. - let args = structure - .filter(|field_binding| { - subdiagnostics_or_empty.contains(&field_binding.binding) - }) - .each(|field_binding| builder.generate_field_attrs_code(field_binding)); + let preamble = builder.preamble(&structure); + let (attrs, args) = builder.body(&mut structure); let span = ast.span().unwrap(); - let (diag, sess) = (&builder.diag, &builder.sess); - let init = match (builder.kind, builder.slug) { + let diag = &builder.diag; + let init = match (builder.kind.value(), builder.slug.value()) { (None, _) => { span_err(span, "diagnostic kind not specified") .help("use the `#[error(...)]` attribute to create an error") .emit(); - return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error(); + return DiagnosticDeriveError::ErrorHandled.to_compile_error(); } - (Some((kind, _)), None) => { + (Some(kind), None) => { span_err(span, "diagnostic slug not specified") .help(&format!( "specify the slug as the first argument to the attribute, such as \ @@ -127,14 +56,20 @@ impl<'a> SessionDiagnosticDerive<'a> { kind.descr() )) .emit(); - return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error(); + return DiagnosticDeriveError::ErrorHandled.to_compile_error(); } - (Some((SessionDiagnosticKind::Error, _)), Some((slug, _))) => { + (Some(DiagnosticDeriveKind::Lint), _) => { + span_err(span, "only `#[error(..)]` and `#[warn(..)]` are supported") + .help("use the `#[error(...)]` attribute to create a error") + .emit(); + return DiagnosticDeriveError::ErrorHandled.to_compile_error(); + } + (Some(DiagnosticDeriveKind::Error), Some(slug)) => { quote! { let mut #diag = #sess.struct_err(rustc_errors::fluent::#slug); } } - (Some((SessionDiagnosticKind::Warn, _)), Some((slug, _))) => { + (Some(DiagnosticDeriveKind::Warn), Some(slug)) => { quote! { let mut #diag = #sess.struct_warn(rustc_errors::fluent::#slug); } @@ -153,10 +88,12 @@ impl<'a> SessionDiagnosticDerive<'a> { #diag }; let param_ty = match builder.kind { - Some((SessionDiagnosticKind::Error, _)) => { + Some((DiagnosticDeriveKind::Error, _)) => { quote! { rustc_errors::ErrorGuaranteed } } - Some((SessionDiagnosticKind::Warn, _)) => quote! { () }, + Some((DiagnosticDeriveKind::Lint | DiagnosticDeriveKind::Warn, _)) => { + quote! { () } + } _ => unreachable!(), }; @@ -168,13 +105,12 @@ impl<'a> SessionDiagnosticDerive<'a> { ) .emit(); - let implementation = SessionDiagnosticDeriveError::ErrorHandled.to_compile_error(); + let implementation = DiagnosticDeriveError::ErrorHandled.to_compile_error(); let param_ty = quote! { rustc_errors::ErrorGuaranteed }; (implementation, param_ty) } }; - let sess = &builder.sess; structure.gen_impl(quote! { gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess, #param_ty> for @Self @@ -191,525 +127,99 @@ impl<'a> SessionDiagnosticDerive<'a> { } } -/// What kind of session diagnostic is being derived - an error or a warning? -#[derive(Copy, Clone)] -enum SessionDiagnosticKind { - /// `#[error(..)]` - Error, - /// `#[warn(..)]` - Warn, +/// The central struct for constructing the `decorate_lint` method from an annotated struct. +pub(crate) struct LintDiagnosticDerive<'a> { + structure: Structure<'a>, + builder: DiagnosticDeriveBuilder, } -impl SessionDiagnosticKind { - /// Returns human-readable string corresponding to the kind. - fn descr(&self) -> &'static str { - match self { - SessionDiagnosticKind::Error => "error", - SessionDiagnosticKind::Warn => "warning", +impl<'a> LintDiagnosticDerive<'a> { + pub(crate) fn new(diag: syn::Ident, structure: Structure<'a>) -> Self { + Self { + builder: DiagnosticDeriveBuilder { + diag, + fields: build_field_mapping(&structure), + kind: None, + code: None, + slug: None, + }, + structure, } } -} - -/// Tracks persistent information required for building up the individual calls to diagnostic -/// methods for the final generated method. This is a separate struct to `SessionDiagnosticDerive` -/// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a -/// double mut borrow later on. -struct SessionDiagnosticDeriveBuilder { - /// Name of the session parameter that's passed in to the `as_error` method. - sess: syn::Ident, - /// The identifier to use for the generated `DiagnosticBuilder` instance. - diag: syn::Ident, - - /// Store a map of field name to its corresponding field. This is built on construction of the - /// derive builder. - fields: HashMap<String, TokenStream>, - - /// Kind of diagnostic requested via the struct attribute. - kind: Option<(SessionDiagnosticKind, proc_macro::Span)>, - /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that - /// has the actual diagnostic message. - slug: Option<(Path, proc_macro::Span)>, - /// Error codes are a optional part of the struct attribute - this is only set to detect - /// multiple specifications. - code: Option<(String, proc_macro::Span)>, -} - -impl HasFieldMap for SessionDiagnosticDeriveBuilder { - fn get_field_binding(&self, field: &String) -> Option<&TokenStream> { - self.fields.get(field) - } -} - -impl SessionDiagnosticDeriveBuilder { - /// Establishes state in the `SessionDiagnosticDeriveBuilder` resulting from the struct - /// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates - /// diagnostic builder calls for setting error code and creating note/help messages. - fn generate_structure_code( - &mut self, - attr: &Attribute, - ) -> Result<TokenStream, SessionDiagnosticDeriveError> { - let diag = &self.diag; - let span = attr.span().unwrap(); - - let name = attr.path.segments.last().unwrap().ident.to_string(); - let name = name.as_str(); - let meta = attr.parse_meta()?; - - let is_help_or_note = matches!(name, "help" | "note"); - - let nested = match meta { - // Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or - // `#[help(..)]`/`#[note(..)]` when the user is specifying a alternative slug. - Meta::List(MetaList { ref nested, .. }) => nested, - // Subdiagnostics without spans can be applied to the type too, and these are just - // paths: `#[help]` and `#[note]` - Meta::Path(_) if is_help_or_note => { - let fn_name = proc_macro2::Ident::new(name, attr.span()); - return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); }); - } - _ => throw_invalid_attr!(attr, &meta), - }; - - // Check the kind before doing any further processing so that there aren't misleading - // "no kind specified" errors if there are failures later. - match name { - "error" => self.kind.set_once((SessionDiagnosticKind::Error, span)), - "warning" => self.kind.set_once((SessionDiagnosticKind::Warn, span)), - "help" | "note" => (), - _ => throw_invalid_attr!(attr, &meta, |diag| { - diag.help("only `error`, `warning`, `help` and `note` are valid attributes") - }), - } - // First nested element should always be the path, e.g. `#[error(typeck::invalid)]` or - // `#[help(typeck::another_help)]`. - let mut nested_iter = nested.into_iter(); - if let Some(nested_attr) = nested_iter.next() { - // Report an error if there are any other list items after the path. - if is_help_or_note && nested_iter.next().is_some() { - throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("`help` and `note` struct attributes can only have one argument") - }); - } - - match nested_attr { - NestedMeta::Meta(Meta::Path(path)) if is_help_or_note => { - let fn_name = proc_macro2::Ident::new(name, attr.span()); - return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); }); - } - NestedMeta::Meta(Meta::Path(path)) => { - self.slug.set_once((path.clone(), span)); - } - NestedMeta::Meta(meta @ Meta::NameValue(_)) - if !is_help_or_note - && meta.path().segments.last().unwrap().ident.to_string() == "code" => - { - // don't error for valid follow-up attributes - } - nested_attr => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("first argument of the attribute should be the diagnostic slug") - }), - }; - } + pub(crate) fn into_tokens(self) -> TokenStream { + let LintDiagnosticDerive { mut structure, mut builder } = self; - // Remaining attributes are optional, only `code = ".."` at the moment. - let mut tokens = Vec::new(); - for nested_attr in nested_iter { - let meta = match nested_attr { - syn::NestedMeta::Meta(meta) => meta, - _ => throw_invalid_nested_attr!(attr, &nested_attr), - }; + let ast = structure.ast(); + let implementation = { + if let syn::Data::Struct(..) = ast.data { + let preamble = builder.preamble(&structure); + let (attrs, args) = builder.body(&mut structure); - let path = meta.path(); - let nested_name = path.segments.last().unwrap().ident.to_string(); - // Struct attributes are only allowed to be applied once, and the diagnostic - // changes will be set in the initialisation code. - if let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) = &meta { - let span = s.span().unwrap(); - match nested_name.as_str() { - "code" => { - self.code.set_once((s.value(), span)); - let code = &self.code.as_ref().map(|(v, _)| v); - tokens.push(quote! { - #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); - }); + let diag = &builder.diag; + let span = ast.span().unwrap(); + let init = match (builder.kind.value(), builder.slug.value()) { + (None, _) => { + span_err(span, "diagnostic kind not specified") + .help("use the `#[error(...)]` attribute to create an error") + .emit(); + return DiagnosticDeriveError::ErrorHandled.to_compile_error(); } - _ => invalid_nested_attr(attr, &nested_attr) - .help("only `code` is a valid nested attributes following the slug") - .emit(), - } - } else { - invalid_nested_attr(attr, &nested_attr).emit() - } - } - - Ok(tokens.drain(..).collect()) - } - - fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { - let field = binding_info.ast(); - let field_binding = &binding_info.binding; - - let inner_ty = FieldInnerTy::from_type(&field.ty); - - // When generating `set_arg` or `add_subdiagnostic` calls, move data rather than - // borrow it to avoid requiring clones - this must therefore be the last use of - // each field (for example, any formatting machinery that might refer to a field - // should be generated already). - if field.attrs.is_empty() { - let diag = &self.diag; - let ident = field.ident.as_ref().unwrap(); - quote! { - #diag.set_arg( - stringify!(#ident), - #field_binding - ); - } - } else { - field - .attrs - .iter() - .map(move |attr| { - let name = attr.path.segments.last().unwrap().ident.to_string(); - let (binding, needs_destructure) = match (name.as_str(), &inner_ty) { - // `primary_span` can accept a `Vec<Span>` so don't destructure that. - ("primary_span", FieldInnerTy::Vec(_)) => { - (quote! { #field_binding.clone() }, false) + (Some(kind), None) => { + span_err(span, "diagnostic slug not specified") + .help(&format!( + "specify the slug as the first argument to the attribute, such as \ + `#[{}(typeck::example_error)]`", + kind.descr() + )) + .emit(); + return DiagnosticDeriveError::ErrorHandled.to_compile_error(); + } + (Some(DiagnosticDeriveKind::Error | DiagnosticDeriveKind::Warn), _) => { + span_err(span, "only `#[lint(..)]` is supported") + .help("use the `#[lint(...)]` attribute to create a lint") + .emit(); + return DiagnosticDeriveError::ErrorHandled.to_compile_error(); + } + (Some(DiagnosticDeriveKind::Lint), Some(slug)) => { + quote! { + let mut #diag = #diag.build(rustc_errors::fluent::#slug); } - // `subdiagnostics` are not derefed because they are bound by value. - ("subdiagnostic", _) => (quote! { #field_binding }, true), - _ => (quote! { *#field_binding }, true), - }; - - let generated_code = self - .generate_inner_field_code( - attr, - FieldInfo { - binding: binding_info, - ty: inner_ty.inner_type().unwrap_or(&field.ty), - span: &field.span(), - }, - binding, - ) - .unwrap_or_else(|v| v.to_compile_error()); - - if needs_destructure { - inner_ty.with(field_binding, generated_code) - } else { - generated_code } - }) - .collect() - } - } - - fn generate_inner_field_code( - &mut self, - attr: &Attribute, - info: FieldInfo<'_>, - binding: TokenStream, - ) -> Result<TokenStream, SessionDiagnosticDeriveError> { - let meta = attr.parse_meta()?; - match meta { - Meta::Path(_) => self.generate_inner_field_code_path(attr, info, binding), - Meta::List(MetaList { .. }) => self.generate_inner_field_code_list(attr, info, binding), - _ => throw_invalid_attr!(attr, &meta), - } - } - - fn generate_inner_field_code_path( - &mut self, - attr: &Attribute, - info: FieldInfo<'_>, - binding: TokenStream, - ) -> Result<TokenStream, SessionDiagnosticDeriveError> { - assert!(matches!(attr.parse_meta()?, Meta::Path(_))); - let diag = &self.diag; - - let meta = attr.parse_meta()?; - - let ident = &attr.path.segments.last().unwrap().ident; - let name = ident.to_string(); - let name = name.as_str(); - match name { - "skip_arg" => { - // Don't need to do anything - by virtue of the attribute existing, the - // `set_arg` call will not be generated. - Ok(quote! {}) - } - "primary_span" => { - report_error_if_not_applied_to_span(attr, &info)?; - Ok(quote! { - #diag.set_span(#binding); - }) - } - "label" => { - report_error_if_not_applied_to_span(attr, &info)?; - Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label })) - } - "note" | "help" => { - let path = match name { - "note" => parse_quote! { _subdiag::note }, - "help" => parse_quote! { _subdiag::help }, - _ => unreachable!(), }; - if type_matches_path(&info.ty, &["rustc_span", "Span"]) { - Ok(self.add_spanned_subdiagnostic(binding, ident, path)) - } else if type_is_unit(&info.ty) { - Ok(self.add_subdiagnostic(ident, path)) - } else { - report_type_error(attr, "`Span` or `()`")?; - } - } - "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }), - _ => throw_invalid_attr!(attr, &meta, |diag| { - diag.help( - "only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` \ - are valid field attributes", - ) - }), - } - } - - fn generate_inner_field_code_list( - &mut self, - attr: &Attribute, - info: FieldInfo<'_>, - binding: TokenStream, - ) -> Result<TokenStream, SessionDiagnosticDeriveError> { - let meta = attr.parse_meta()?; - let Meta::List(MetaList { ref path, ref nested, .. }) = meta else { unreachable!() }; - - let ident = &attr.path.segments.last().unwrap().ident; - let name = path.segments.last().unwrap().ident.to_string(); - let name = name.as_ref(); - match name { - "suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => { - return self.generate_inner_field_code_suggestion(attr, info); - } - "label" | "help" | "note" => (), - _ => throw_invalid_attr!(attr, &meta, |diag| { - diag.help( - "only `label`, `note`, `help` or `suggestion{,_short,_hidden,_verbose}` are \ - valid field attributes", - ) - }), - } - // For `#[label(..)]`, `#[note(..)]` and `#[help(..)]`, the first nested element must be a - // path, e.g. `#[label(typeck::label)]`. - let mut nested_iter = nested.into_iter(); - let msg = match nested_iter.next() { - Some(NestedMeta::Meta(Meta::Path(path))) => path.clone(), - Some(nested_attr) => throw_invalid_nested_attr!(attr, &nested_attr), - None => throw_invalid_attr!(attr, &meta), - }; - - // None of these attributes should have anything following the slug. - if nested_iter.next().is_some() { - throw_invalid_attr!(attr, &meta); - } - - match name { - "label" => { - report_error_if_not_applied_to_span(attr, &info)?; - Ok(self.add_spanned_subdiagnostic(binding, ident, msg)) - } - "note" | "help" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => { - Ok(self.add_spanned_subdiagnostic(binding, ident, msg)) - } - "note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)), - "note" | "help" => { - report_type_error(attr, "`Span` or `()`")?; - } - _ => unreachable!(), - } - } - - fn generate_inner_field_code_suggestion( - &mut self, - attr: &Attribute, - info: FieldInfo<'_>, - ) -> Result<TokenStream, SessionDiagnosticDeriveError> { - let diag = &self.diag; - - let mut meta = attr.parse_meta()?; - let Meta::List(MetaList { ref path, ref mut nested, .. }) = meta else { unreachable!() }; - - let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?; - - let mut msg = None; - let mut code = None; - - let mut nested_iter = nested.into_iter().peekable(); - if let Some(nested_attr) = nested_iter.peek() { - if let NestedMeta::Meta(Meta::Path(path)) = nested_attr { - msg = Some(path.clone()); - } - }; - // Move the iterator forward if a path was found (don't otherwise so that - // code/applicability can be found or an error emitted). - if msg.is_some() { - let _ = nested_iter.next(); - } - - for nested_attr in nested_iter { - let meta = match nested_attr { - syn::NestedMeta::Meta(ref meta) => meta, - syn::NestedMeta::Lit(_) => throw_invalid_nested_attr!(attr, &nested_attr), - }; - - let nested_name = meta.path().segments.last().unwrap().ident.to_string(); - let nested_name = nested_name.as_str(); - match meta { - Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => { - let span = meta.span().unwrap(); - match nested_name { - "code" => { - let formatted_str = self.build_format(&s.value(), s.span()); - code = Some(formatted_str); - } - "applicability" => { - applicability = match applicability { - Some(v) => { - span_err( - span, - "applicability cannot be set in both the field and \ - attribute", - ) - .emit(); - Some(v) - } - None => match Applicability::from_str(&s.value()) { - Ok(v) => Some(quote! { #v }), - Err(()) => { - span_err(span, "invalid applicability").emit(); - None - } - }, - } - } - _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help( - "only `message`, `code` and `applicability` are valid field \ - attributes", - ) - }), + let implementation = quote! { + #init + #preamble + match self { + #attrs } - } - _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - if matches!(meta, Meta::Path(_)) { - diag.help("a diagnostic slug must be the first argument to the attribute") - } else { - diag + match self { + #args } - }), - } - } - - let applicability = - applicability.unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified)); - - let name = path.segments.last().unwrap().ident.to_string(); - let method = format_ident!("span_{}", name); - - let msg = msg.unwrap_or_else(|| parse_quote! { _subdiag::suggestion }); - let msg = quote! { rustc_errors::fluent::#msg }; - let code = code.unwrap_or_else(|| quote! { String::new() }); - - Ok(quote! { #diag.#method(#span_field, #msg, #code, #applicability); }) - } - - /// Adds a spanned subdiagnostic by generating a `diag.span_$kind` call with the current slug - /// and `fluent_attr_identifier`. - fn add_spanned_subdiagnostic( - &self, - field_binding: TokenStream, - kind: &Ident, - fluent_attr_identifier: Path, - ) -> TokenStream { - let diag = &self.diag; - let fn_name = format_ident!("span_{}", kind); - quote! { - #diag.#fn_name( - #field_binding, - rustc_errors::fluent::#fluent_attr_identifier - ); - } - } + #diag.emit(); + }; - /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug - /// and `fluent_attr_identifier`. - fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: Path) -> TokenStream { - let diag = &self.diag; - quote! { - #diag.#kind(rustc_errors::fluent::#fluent_attr_identifier); - } - } + implementation + } else { + span_err( + ast.span().unwrap(), + "`#[derive(LintDiagnostic)]` can only be used on structs", + ) + .emit(); - fn span_and_applicability_of_ty( - &self, - info: FieldInfo<'_>, - ) -> Result<(TokenStream, Option<TokenStream>), SessionDiagnosticDeriveError> { - match &info.ty { - // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`. - ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => { - let binding = &info.binding.binding; - Ok((quote!(*#binding), None)) + DiagnosticDeriveError::ErrorHandled.to_compile_error() } - // If `ty` is `(Span, Applicability)` then return tokens accessing those. - Type::Tuple(tup) => { - let mut span_idx = None; - let mut applicability_idx = None; - - for (idx, elem) in tup.elems.iter().enumerate() { - if type_matches_path(elem, &["rustc_span", "Span"]) { - if span_idx.is_none() { - span_idx = Some(syn::Index::from(idx)); - } else { - throw_span_err!( - info.span.unwrap(), - "type of field annotated with `#[suggestion(...)]` contains more \ - than one `Span`" - ); - } - } else if type_matches_path(elem, &["rustc_errors", "Applicability"]) { - if applicability_idx.is_none() { - applicability_idx = Some(syn::Index::from(idx)); - } else { - throw_span_err!( - info.span.unwrap(), - "type of field annotated with `#[suggestion(...)]` contains more \ - than one Applicability" - ); - } - } - } - - if let Some(span_idx) = span_idx { - let binding = &info.binding.binding; - let span = quote!(#binding.#span_idx); - let applicability = applicability_idx - .map(|applicability_idx| quote!(#binding.#applicability_idx)) - .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified)); + }; - return Ok((span, Some(applicability))); + let diag = &builder.diag; + structure.gen_impl(quote! { + gen impl<'__a> rustc_errors::DecorateLint<'__a, ()> for @Self { + fn decorate_lint(self, #diag: rustc_errors::LintDiagnosticBuilder<'__a, ()>) { + use rustc_errors::IntoDiagnosticArg; + #implementation } - - throw_span_err!(info.span.unwrap(), "wrong types for suggestion", |diag| { - diag.help( - "`#[suggestion(...)]` on a tuple field must be applied to fields of type \ - `(Span, Applicability)`", - ) - }); } - // If `ty` isn't a `Span` or `(Span, Applicability)` then emit an error. - _ => throw_span_err!(info.span.unwrap(), "wrong field type for suggestion", |diag| { - diag.help( - "`#[suggestion(...)]` should be applied to fields of type `Span` or \ - `(Span, Applicability)`", - ) - }), - } + }) } } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs new file mode 100644 index 00000000000..74ce1ab08c2 --- /dev/null +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -0,0 +1,590 @@ +#![deny(unused_must_use)] + +use crate::diagnostics::error::{ + invalid_nested_attr, span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, + DiagnosticDeriveError, +}; +use crate::diagnostics::utils::{ + report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path, + Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce, +}; +use proc_macro2::{Ident, TokenStream}; +use quote::{format_ident, quote}; +use std::collections::HashMap; +use std::str::FromStr; +use syn::{ + parse_quote, spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type, +}; +use synstructure::{BindingInfo, Structure}; + +/// What kind of diagnostic is being derived - an error, a warning or a lint? +#[derive(Copy, Clone)] +pub(crate) enum DiagnosticDeriveKind { + /// `#[error(..)]` + Error, + /// `#[warn(..)]` + Warn, + /// `#[lint(..)]` + Lint, +} + +impl DiagnosticDeriveKind { + /// Returns human-readable string corresponding to the kind. + pub fn descr(&self) -> &'static str { + match self { + DiagnosticDeriveKind::Error => "error", + DiagnosticDeriveKind::Warn => "warning", + DiagnosticDeriveKind::Lint => "lint", + } + } +} + +/// Tracks persistent information required for building up individual calls to diagnostic methods +/// for generated diagnostic derives - both `SessionDiagnostic` for errors/warnings and +/// `LintDiagnostic` for lints. +pub(crate) struct DiagnosticDeriveBuilder { + /// The identifier to use for the generated `DiagnosticBuilder` instance. + pub diag: syn::Ident, + + /// Store a map of field name to its corresponding field. This is built on construction of the + /// derive builder. + pub fields: HashMap<String, TokenStream>, + + /// Kind of diagnostic requested via the struct attribute. + pub kind: Option<(DiagnosticDeriveKind, proc_macro::Span)>, + /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that + /// has the actual diagnostic message. + pub slug: Option<(Path, proc_macro::Span)>, + /// Error codes are a optional part of the struct attribute - this is only set to detect + /// multiple specifications. + pub code: Option<(String, proc_macro::Span)>, +} + +impl HasFieldMap for DiagnosticDeriveBuilder { + fn get_field_binding(&self, field: &String) -> Option<&TokenStream> { + self.fields.get(field) + } +} + +impl DiagnosticDeriveBuilder { + pub fn preamble<'s>(&mut self, structure: &Structure<'s>) -> TokenStream { + let ast = structure.ast(); + let attrs = &ast.attrs; + let preamble = attrs.iter().map(|attr| { + self.generate_structure_code_for_attr(attr).unwrap_or_else(|v| v.to_compile_error()) + }); + + quote! { + #(#preamble)*; + } + } + + pub fn body<'s>(&mut self, structure: &mut Structure<'s>) -> (TokenStream, TokenStream) { + // Keep track of which fields are subdiagnostics or have no attributes. + let mut subdiagnostics_or_empty = std::collections::HashSet::new(); + + // Generates calls to `span_label` and similar functions based on the attributes + // on fields. Code for suggestions uses formatting machinery and the value of + // other fields - because any given field can be referenced multiple times, it + // should be accessed through a borrow. When passing fields to `add_subdiagnostic` + // or `set_arg` (which happens below) for Fluent, we want to move the data, so that + // has to happen in a separate pass over the fields. + let attrs = structure + .clone() + .filter(|field_binding| { + let attrs = &field_binding.ast().attrs; + + (!attrs.is_empty() + && attrs.iter().all(|attr| { + "subdiagnostic" != attr.path.segments.last().unwrap().ident.to_string() + })) + || { + subdiagnostics_or_empty.insert(field_binding.binding.clone()); + false + } + }) + .each(|field_binding| self.generate_field_attrs_code(field_binding)); + + structure.bind_with(|_| synstructure::BindStyle::Move); + // When a field has attributes like `#[label]` or `#[note]` then it doesn't + // need to be passed as an argument to the diagnostic. But when a field has no + // attributes or a `#[subdiagnostic]` attribute then it must be passed as an + // argument to the diagnostic so that it can be referred to by Fluent messages. + let args = structure + .filter(|field_binding| subdiagnostics_or_empty.contains(&field_binding.binding)) + .each(|field_binding| self.generate_field_attrs_code(field_binding)); + + (attrs, args) + } + + /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct + /// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates + /// diagnostic builder calls for setting error code and creating note/help messages. + fn generate_structure_code_for_attr( + &mut self, + attr: &Attribute, + ) -> Result<TokenStream, DiagnosticDeriveError> { + let diag = &self.diag; + let span = attr.span().unwrap(); + + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); + let meta = attr.parse_meta()?; + + let is_help_or_note = matches!(name, "help" | "note"); + + let nested = match meta { + // Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or + // `#[help(..)]`/`#[note(..)]` when the user is specifying a alternative slug. + Meta::List(MetaList { ref nested, .. }) => nested, + // Subdiagnostics without spans can be applied to the type too, and these are just + // paths: `#[help]` and `#[note]` + Meta::Path(_) if is_help_or_note => { + let fn_name = proc_macro2::Ident::new(name, attr.span()); + return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); }); + } + _ => throw_invalid_attr!(attr, &meta), + }; + + // Check the kind before doing any further processing so that there aren't misleading + // "no kind specified" errors if there are failures later. + match name { + "error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)), + "warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)), + "lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)), + "help" | "note" => (), + _ => throw_invalid_attr!(attr, &meta, |diag| { + diag.help("only `error`, `warning`, `help` and `note` are valid attributes") + }), + } + + // First nested element should always be the path, e.g. `#[error(typeck::invalid)]` or + // `#[help(typeck::another_help)]`. + let mut nested_iter = nested.into_iter(); + if let Some(nested_attr) = nested_iter.next() { + // Report an error if there are any other list items after the path. + if is_help_or_note && nested_iter.next().is_some() { + throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help("`help` and `note` struct attributes can only have one argument") + }); + } + + match nested_attr { + NestedMeta::Meta(Meta::Path(path)) if is_help_or_note => { + let fn_name = proc_macro2::Ident::new(name, attr.span()); + return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); }); + } + NestedMeta::Meta(Meta::Path(path)) => { + self.slug.set_once((path.clone(), span)); + } + NestedMeta::Meta(meta @ Meta::NameValue(_)) + if !is_help_or_note + && meta.path().segments.last().unwrap().ident.to_string() == "code" => + { + // don't error for valid follow-up attributes + } + nested_attr => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help("first argument of the attribute should be the diagnostic slug") + }), + }; + } + + // Remaining attributes are optional, only `code = ".."` at the moment. + let mut tokens = Vec::new(); + for nested_attr in nested_iter { + let meta = match nested_attr { + syn::NestedMeta::Meta(meta) => meta, + _ => throw_invalid_nested_attr!(attr, &nested_attr), + }; + + let path = meta.path(); + let nested_name = path.segments.last().unwrap().ident.to_string(); + // Struct attributes are only allowed to be applied once, and the diagnostic + // changes will be set in the initialisation code. + if let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) = &meta { + let span = s.span().unwrap(); + match nested_name.as_str() { + "code" => { + self.code.set_once((s.value(), span)); + let code = &self.code.as_ref().map(|(v, _)| v); + tokens.push(quote! { + #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); + }); + } + _ => invalid_nested_attr(attr, &nested_attr) + .help("only `code` is a valid nested attributes following the slug") + .emit(), + } + } else { + invalid_nested_attr(attr, &nested_attr).emit() + } + } + + Ok(tokens.drain(..).collect()) + } + + fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { + let field = binding_info.ast(); + let field_binding = &binding_info.binding; + + let inner_ty = FieldInnerTy::from_type(&field.ty); + + // When generating `set_arg` or `add_subdiagnostic` calls, move data rather than + // borrow it to avoid requiring clones - this must therefore be the last use of + // each field (for example, any formatting machinery that might refer to a field + // should be generated already). + if field.attrs.is_empty() { + let diag = &self.diag; + let ident = field.ident.as_ref().unwrap(); + quote! { + #diag.set_arg( + stringify!(#ident), + #field_binding + ); + } + } else { + field + .attrs + .iter() + .map(move |attr| { + let name = attr.path.segments.last().unwrap().ident.to_string(); + let (binding, needs_destructure) = match (name.as_str(), &inner_ty) { + // `primary_span` can accept a `Vec<Span>` so don't destructure that. + ("primary_span", FieldInnerTy::Vec(_)) => { + (quote! { #field_binding.clone() }, false) + } + // `subdiagnostics` are not derefed because they are bound by value. + ("subdiagnostic", _) => (quote! { #field_binding }, true), + _ => (quote! { *#field_binding }, true), + }; + + let generated_code = self + .generate_inner_field_code( + attr, + FieldInfo { + binding: binding_info, + ty: inner_ty.inner_type().unwrap_or(&field.ty), + span: &field.span(), + }, + binding, + ) + .unwrap_or_else(|v| v.to_compile_error()); + + if needs_destructure { + inner_ty.with(field_binding, generated_code) + } else { + generated_code + } + }) + .collect() + } + } + + fn generate_inner_field_code( + &mut self, + attr: &Attribute, + info: FieldInfo<'_>, + binding: TokenStream, + ) -> Result<TokenStream, DiagnosticDeriveError> { + let meta = attr.parse_meta()?; + match meta { + Meta::Path(_) => self.generate_inner_field_code_path(attr, info, binding), + Meta::List(MetaList { .. }) => self.generate_inner_field_code_list(attr, info, binding), + _ => throw_invalid_attr!(attr, &meta), + } + } + + fn generate_inner_field_code_path( + &mut self, + attr: &Attribute, + info: FieldInfo<'_>, + binding: TokenStream, + ) -> Result<TokenStream, DiagnosticDeriveError> { + assert!(matches!(attr.parse_meta()?, Meta::Path(_))); + let diag = &self.diag; + + let meta = attr.parse_meta()?; + + let ident = &attr.path.segments.last().unwrap().ident; + let name = ident.to_string(); + let name = name.as_str(); + match name { + "skip_arg" => { + // Don't need to do anything - by virtue of the attribute existing, the + // `set_arg` call will not be generated. + Ok(quote! {}) + } + "primary_span" => { + report_error_if_not_applied_to_span(attr, &info)?; + Ok(quote! { + #diag.set_span(#binding); + }) + } + "label" => { + report_error_if_not_applied_to_span(attr, &info)?; + Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label })) + } + "note" | "help" => { + let path = match name { + "note" => parse_quote! { _subdiag::note }, + "help" => parse_quote! { _subdiag::help }, + _ => unreachable!(), + }; + if type_matches_path(&info.ty, &["rustc_span", "Span"]) { + Ok(self.add_spanned_subdiagnostic(binding, ident, path)) + } else if type_is_unit(&info.ty) { + Ok(self.add_subdiagnostic(ident, path)) + } else { + report_type_error(attr, "`Span` or `()`")? + } + } + "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }), + _ => throw_invalid_attr!(attr, &meta, |diag| { + diag.help( + "only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` \ + are valid field attributes", + ) + }), + } + } + + fn generate_inner_field_code_list( + &mut self, + attr: &Attribute, + info: FieldInfo<'_>, + binding: TokenStream, + ) -> Result<TokenStream, DiagnosticDeriveError> { + let meta = attr.parse_meta()?; + let Meta::List(MetaList { ref path, ref nested, .. }) = meta else { unreachable!() }; + + let ident = &attr.path.segments.last().unwrap().ident; + let name = path.segments.last().unwrap().ident.to_string(); + let name = name.as_ref(); + match name { + "suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => { + return self.generate_inner_field_code_suggestion(attr, info); + } + "label" | "help" | "note" => (), + _ => throw_invalid_attr!(attr, &meta, |diag| { + diag.help( + "only `label`, `note`, `help` or `suggestion{,_short,_hidden,_verbose}` are \ + valid field attributes", + ) + }), + } + + // For `#[label(..)]`, `#[note(..)]` and `#[help(..)]`, the first nested element must be a + // path, e.g. `#[label(typeck::label)]`. + let mut nested_iter = nested.into_iter(); + let msg = match nested_iter.next() { + Some(NestedMeta::Meta(Meta::Path(path))) => path.clone(), + Some(nested_attr) => throw_invalid_nested_attr!(attr, &nested_attr), + None => throw_invalid_attr!(attr, &meta), + }; + + // None of these attributes should have anything following the slug. + if nested_iter.next().is_some() { + throw_invalid_attr!(attr, &meta); + } + + match name { + "label" => { + report_error_if_not_applied_to_span(attr, &info)?; + Ok(self.add_spanned_subdiagnostic(binding, ident, msg)) + } + "note" | "help" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => { + Ok(self.add_spanned_subdiagnostic(binding, ident, msg)) + } + "note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)), + "note" | "help" => report_type_error(attr, "`Span` or `()`")?, + _ => unreachable!(), + } + } + + fn generate_inner_field_code_suggestion( + &mut self, + attr: &Attribute, + info: FieldInfo<'_>, + ) -> Result<TokenStream, DiagnosticDeriveError> { + let diag = &self.diag; + + let mut meta = attr.parse_meta()?; + let Meta::List(MetaList { ref path, ref mut nested, .. }) = meta else { unreachable!() }; + + let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?; + + let mut msg = None; + let mut code = None; + + let mut nested_iter = nested.into_iter().peekable(); + if let Some(nested_attr) = nested_iter.peek() { + if let NestedMeta::Meta(Meta::Path(path)) = nested_attr { + msg = Some(path.clone()); + } + }; + // Move the iterator forward if a path was found (don't otherwise so that + // code/applicability can be found or an error emitted). + if msg.is_some() { + let _ = nested_iter.next(); + } + + for nested_attr in nested_iter { + let meta = match nested_attr { + syn::NestedMeta::Meta(ref meta) => meta, + syn::NestedMeta::Lit(_) => throw_invalid_nested_attr!(attr, &nested_attr), + }; + + let nested_name = meta.path().segments.last().unwrap().ident.to_string(); + let nested_name = nested_name.as_str(); + match meta { + Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + let span = meta.span().unwrap(); + match nested_name { + "code" => { + let formatted_str = self.build_format(&s.value(), s.span()); + code = Some(formatted_str); + } + "applicability" => { + applicability = match applicability { + Some(v) => { + span_err( + span, + "applicability cannot be set in both the field and \ + attribute", + ) + .emit(); + Some(v) + } + None => match Applicability::from_str(&s.value()) { + Ok(v) => Some(quote! { #v }), + Err(()) => { + span_err(span, "invalid applicability").emit(); + None + } + }, + } + } + _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help( + "only `message`, `code` and `applicability` are valid field \ + attributes", + ) + }), + } + } + _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + if matches!(meta, Meta::Path(_)) { + diag.help("a diagnostic slug must be the first argument to the attribute") + } else { + diag + } + }), + } + } + + let applicability = + applicability.unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified)); + + let name = path.segments.last().unwrap().ident.to_string(); + let method = format_ident!("span_{}", name); + + let msg = msg.unwrap_or_else(|| parse_quote! { _subdiag::suggestion }); + let msg = quote! { rustc_errors::fluent::#msg }; + let code = code.unwrap_or_else(|| quote! { String::new() }); + + Ok(quote! { #diag.#method(#span_field, #msg, #code, #applicability); }) + } + + /// Adds a spanned subdiagnostic by generating a `diag.span_$kind` call with the current slug + /// and `fluent_attr_identifier`. + fn add_spanned_subdiagnostic( + &self, + field_binding: TokenStream, + kind: &Ident, + fluent_attr_identifier: Path, + ) -> TokenStream { + let diag = &self.diag; + let fn_name = format_ident!("span_{}", kind); + quote! { + #diag.#fn_name( + #field_binding, + rustc_errors::fluent::#fluent_attr_identifier + ); + } + } + + /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug + /// and `fluent_attr_identifier`. + fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: Path) -> TokenStream { + let diag = &self.diag; + quote! { + #diag.#kind(rustc_errors::fluent::#fluent_attr_identifier); + } + } + + fn span_and_applicability_of_ty( + &self, + info: FieldInfo<'_>, + ) -> Result<(TokenStream, Option<TokenStream>), DiagnosticDeriveError> { + match &info.ty { + // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`. + ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => { + let binding = &info.binding.binding; + Ok((quote!(*#binding), None)) + } + // If `ty` is `(Span, Applicability)` then return tokens accessing those. + Type::Tuple(tup) => { + let mut span_idx = None; + let mut applicability_idx = None; + + for (idx, elem) in tup.elems.iter().enumerate() { + if type_matches_path(elem, &["rustc_span", "Span"]) { + if span_idx.is_none() { + span_idx = Some(syn::Index::from(idx)); + } else { + throw_span_err!( + info.span.unwrap(), + "type of field annotated with `#[suggestion(...)]` contains more \ + than one `Span`" + ); + } + } else if type_matches_path(elem, &["rustc_errors", "Applicability"]) { + if applicability_idx.is_none() { + applicability_idx = Some(syn::Index::from(idx)); + } else { + throw_span_err!( + info.span.unwrap(), + "type of field annotated with `#[suggestion(...)]` contains more \ + than one Applicability" + ); + } + } + } + + if let Some(span_idx) = span_idx { + let binding = &info.binding.binding; + let span = quote!(#binding.#span_idx); + let applicability = applicability_idx + .map(|applicability_idx| quote!(#binding.#applicability_idx)) + .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified)); + + return Ok((span, Some(applicability))); + } + + throw_span_err!(info.span.unwrap(), "wrong types for suggestion", |diag| { + diag.help( + "`#[suggestion(...)]` on a tuple field must be applied to fields of type \ + `(Span, Applicability)`", + ) + }); + } + // If `ty` isn't a `Span` or `(Span, Applicability)` then emit an error. + _ => throw_span_err!(info.span.unwrap(), "wrong field type for suggestion", |diag| { + diag.help( + "`#[suggestion(...)]` should be applied to fields of type `Span` or \ + `(Span, Applicability)`", + ) + }), + } + } +} diff --git a/compiler/rustc_macros/src/diagnostics/error.rs b/compiler/rustc_macros/src/diagnostics/error.rs index d088402abc6..0b1ededa775 100644 --- a/compiler/rustc_macros/src/diagnostics/error.rs +++ b/compiler/rustc_macros/src/diagnostics/error.rs @@ -4,16 +4,16 @@ use quote::quote; use syn::{spanned::Spanned, Attribute, Error as SynError, Meta, NestedMeta}; #[derive(Debug)] -pub(crate) enum SessionDiagnosticDeriveError { +pub(crate) enum DiagnosticDeriveError { SynError(SynError), ErrorHandled, } -impl SessionDiagnosticDeriveError { +impl DiagnosticDeriveError { pub(crate) fn to_compile_error(self) -> TokenStream { match self { - SessionDiagnosticDeriveError::SynError(e) => e.to_compile_error(), - SessionDiagnosticDeriveError::ErrorHandled => { + DiagnosticDeriveError::SynError(e) => e.to_compile_error(), + DiagnosticDeriveError::ErrorHandled => { // Return ! to avoid having to create a blank DiagnosticBuilder to return when an // error has already been emitted to the compiler. quote! { @@ -24,9 +24,9 @@ impl SessionDiagnosticDeriveError { } } -impl From<SynError> for SessionDiagnosticDeriveError { +impl From<SynError> for DiagnosticDeriveError { fn from(e: SynError) -> Self { - SessionDiagnosticDeriveError::SynError(e) + DiagnosticDeriveError::SynError(e) } } @@ -34,9 +34,9 @@ impl From<SynError> for SessionDiagnosticDeriveError { pub(crate) fn _throw_err( diag: Diagnostic, f: impl FnOnce(Diagnostic) -> Diagnostic, -) -> SessionDiagnosticDeriveError { +) -> DiagnosticDeriveError { f(diag).emit(); - SessionDiagnosticDeriveError::ErrorHandled + DiagnosticDeriveError::ErrorHandled } /// Helper function for printing `syn::Path` - doesn't handle arguments in paths and these are @@ -60,7 +60,7 @@ pub(crate) fn span_err(span: impl MultiSpan, msg: &str) -> Diagnostic { /// Emit a diagnostic on span `$span` with msg `$msg` (optionally performing additional decoration /// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`. /// -/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`: +/// For methods that return a `Result<_, DiagnosticDeriveError>`: macro_rules! throw_span_err { ($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }}; ($span:expr, $msg:expr, $f:expr) => {{ @@ -87,7 +87,7 @@ pub(crate) fn invalid_attr(attr: &Attribute, meta: &Meta) -> Diagnostic { /// Emit a error diagnostic for an invalid attribute (optionally performing additional decoration /// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`. /// -/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`: +/// For methods that return a `Result<_, DiagnosticDeriveError>`: macro_rules! throw_invalid_attr { ($attr:expr, $meta:expr) => {{ throw_invalid_attr!($attr, $meta, |diag| diag) }}; ($attr:expr, $meta:expr, $f:expr) => {{ @@ -129,7 +129,7 @@ pub(crate) fn invalid_nested_attr(attr: &Attribute, nested: &NestedMeta) -> Diag /// Emit a error diagnostic for an invalid nested attribute (optionally performing additional /// decoration using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`. /// -/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`: +/// For methods that return a `Result<_, DiagnosticDeriveError>`: macro_rules! throw_invalid_nested_attr { ($attr:expr, $nested_attr:expr) => {{ throw_invalid_nested_attr!($attr, $nested_attr, |diag| diag) }}; ($attr:expr, $nested_attr:expr, $f:expr) => {{ diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index 2eee4bfb5dd..39979002666 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -1,10 +1,11 @@ mod diagnostic; +mod diagnostic_builder; mod error; mod fluent; mod subdiagnostic; mod utils; -use diagnostic::SessionDiagnosticDerive; +use diagnostic::{LintDiagnosticDerive, SessionDiagnosticDerive}; pub(crate) use fluent::fluent_messages; use proc_macro2::TokenStream; use quote::format_ident; @@ -56,13 +57,55 @@ use synstructure::Structure; /// ``` /// /// See rustc dev guide for more examples on using the `#[derive(SessionDiagnostic)]`: -/// <https://rustc-dev-guide.rust-lang.org/diagnostics/sessiondiagnostic.html> +/// <https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-structs.html> pub fn session_diagnostic_derive(s: Structure<'_>) -> TokenStream { - // Names for the diagnostic we build and the session we build it from. - let diag = format_ident!("diag"); - let sess = format_ident!("sess"); + SessionDiagnosticDerive::new(format_ident!("diag"), format_ident!("sess"), s).into_tokens() +} - SessionDiagnosticDerive::new(diag, sess, s).into_tokens() +/// Implements `#[derive(LintDiagnostic)]`, which allows for lints to be specified as a struct, +/// independent from the actual lint emitting code. +/// +/// ```ignore (rust) +/// #[derive(LintDiagnostic)] +/// #[lint(lint::atomic_ordering_invalid_fail_success)] +/// pub struct AtomicOrderingInvalidLint { +/// method: Symbol, +/// success_ordering: Symbol, +/// fail_ordering: Symbol, +/// #[label(lint::fail_label)] +/// fail_order_arg_span: Span, +/// #[label(lint::success_label)] +/// #[suggestion( +/// code = "std::sync::atomic::Ordering::{success_suggestion}", +/// applicability = "maybe-incorrect" +/// )] +/// success_order_arg_span: Span, +/// } +/// ``` +/// +/// ```fluent +/// lint-atomic-ordering-invalid-fail-success = `{$method}`'s success ordering must be at least as strong as its failure ordering +/// .fail-label = `{$fail_ordering}` failure ordering +/// .success-label = `{$success_ordering}` success ordering +/// .suggestion = consider using `{$success_suggestion}` success ordering instead +/// ``` +/// +/// Then, later, to emit the error: +/// +/// ```ignore (rust) +/// cx.struct_span_lint(INVALID_ATOMIC_ORDERING, fail_order_arg_span, AtomicOrderingInvalidLint { +/// method, +/// success_ordering, +/// fail_ordering, +/// fail_order_arg_span, +/// success_order_arg_span, +/// }); +/// ``` +/// +/// See rustc dev guide for more examples on using the `#[derive(LintDiagnostic)]`: +/// <https://rustc-dev-guide.rust-lang.org/diagnostics/sessiondiagnostic.html> +pub fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream { + LintDiagnosticDerive::new(format_ident!("diag"), s).into_tokens() } /// Implements `#[derive(SessionSubdiagnostic)]`, which allows for labels, notes, helps and diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index eab954a9c1b..2a5b6beba94 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -1,8 +1,7 @@ #![deny(unused_must_use)] use crate::diagnostics::error::{ - span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, - SessionDiagnosticDeriveError, + span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, DiagnosticDeriveError, }; use crate::diagnostics::utils::{ report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, @@ -214,7 +213,7 @@ impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> { } impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { - fn identify_kind(&mut self) -> Result<(), SessionDiagnosticDeriveError> { + fn identify_kind(&mut self) -> Result<(), DiagnosticDeriveError> { for attr in self.variant.ast().attrs { let span = attr.span().unwrap(); @@ -351,7 +350,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { &mut self, binding: &BindingInfo<'_>, is_suggestion: bool, - ) -> Result<TokenStream, SessionDiagnosticDeriveError> { + ) -> Result<TokenStream, DiagnosticDeriveError> { let ast = binding.ast(); let inner_ty = FieldInnerTy::from_type(&ast.ty); @@ -411,7 +410,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { Ok(inner_ty.with(binding, generated)) } - fn into_tokens(&mut self) -> Result<TokenStream, SessionDiagnosticDeriveError> { + fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> { self.identify_kind()?; let Some(kind) = self.kind.map(|(kind, _)| kind) else { throw_span_err!( diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 636bcf1f7b1..8977db4606c 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -1,11 +1,11 @@ -use crate::diagnostics::error::{span_err, throw_span_err, SessionDiagnosticDeriveError}; +use crate::diagnostics::error::{span_err, throw_span_err, DiagnosticDeriveError}; use proc_macro::Span; use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; -use std::collections::BTreeSet; +use std::collections::{BTreeSet, HashMap}; use std::str::FromStr; use syn::{spanned::Spanned, Attribute, Meta, Type, TypeTuple}; -use synstructure::BindingInfo; +use synstructure::{BindingInfo, Structure}; /// Checks whether the type name of `ty` matches `name`. /// @@ -34,7 +34,7 @@ pub(crate) fn type_is_unit(ty: &Type) -> bool { pub(crate) fn report_type_error( attr: &Attribute, ty_name: &str, -) -> Result<!, SessionDiagnosticDeriveError> { +) -> Result<!, DiagnosticDeriveError> { let name = attr.path.segments.last().unwrap().ident.to_string(); let meta = attr.parse_meta()?; @@ -59,7 +59,7 @@ fn report_error_if_not_applied_to_ty( info: &FieldInfo<'_>, path: &[&str], ty_name: &str, -) -> Result<(), SessionDiagnosticDeriveError> { +) -> Result<(), DiagnosticDeriveError> { if !type_matches_path(&info.ty, path) { report_type_error(attr, ty_name)?; } @@ -71,7 +71,7 @@ fn report_error_if_not_applied_to_ty( pub(crate) fn report_error_if_not_applied_to_applicability( attr: &Attribute, info: &FieldInfo<'_>, -) -> Result<(), SessionDiagnosticDeriveError> { +) -> Result<(), DiagnosticDeriveError> { report_error_if_not_applied_to_ty( attr, info, @@ -84,7 +84,7 @@ pub(crate) fn report_error_if_not_applied_to_applicability( pub(crate) fn report_error_if_not_applied_to_span( attr: &Attribute, info: &FieldInfo<'_>, -) -> Result<(), SessionDiagnosticDeriveError> { +) -> Result<(), DiagnosticDeriveError> { report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "`Span`") } @@ -166,10 +166,12 @@ pub(crate) struct FieldInfo<'a> { /// Small helper trait for abstracting over `Option` fields that contain a value and a `Span` /// for error reporting if they are set more than once. pub(crate) trait SetOnce<T> { - fn set_once(&mut self, value: T); + fn set_once(&mut self, _: (T, Span)); + + fn value(self) -> Option<T>; } -impl<T> SetOnce<(T, Span)> for Option<(T, Span)> { +impl<T> SetOnce<T> for Option<(T, Span)> { fn set_once(&mut self, (value, span): (T, Span)) { match self { None => { @@ -182,6 +184,10 @@ impl<T> SetOnce<(T, Span)> for Option<(T, Span)> { } } } + + fn value(self) -> Option<T> { + self.map(|(v, _)| v) + } } pub(crate) trait HasFieldMap { @@ -325,3 +331,20 @@ impl quote::ToTokens for Applicability { }); } } + +/// Build the mapping of field names to fields. This allows attributes to peek values from +/// other fields. +pub(crate) fn build_field_mapping<'a>(structure: &Structure<'a>) -> HashMap<String, TokenStream> { + let mut fields_map = HashMap::new(); + + let ast = structure.ast(); + if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data { + for field in fields.iter() { + if let Some(ident) = &field.ident { + fields_map.insert(ident.to_string(), quote! { &self.#ident }); + } + } + } + + fields_map +} diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 7c8e3c6d140..168530c54b9 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -18,6 +18,7 @@ mod query; mod serialize; mod symbols; mod type_foldable; +mod type_visitable; #[proc_macro] pub fn rustc_queries(input: TokenStream) -> TokenStream { @@ -121,12 +122,14 @@ decl_derive!([TyEncodable] => serialize::type_encodable_derive); decl_derive!([MetadataDecodable] => serialize::meta_decodable_derive); decl_derive!([MetadataEncodable] => serialize::meta_encodable_derive); decl_derive!([TypeFoldable, attributes(type_foldable)] => type_foldable::type_foldable_derive); +decl_derive!([TypeVisitable, attributes(type_visitable)] => type_visitable::type_visitable_derive); decl_derive!([Lift, attributes(lift)] => lift::lift_derive); decl_derive!( [SessionDiagnostic, attributes( // struct attributes warning, error, + lint, note, help, // field attributes @@ -140,6 +143,24 @@ decl_derive!( suggestion_verbose)] => diagnostics::session_diagnostic_derive ); decl_derive!( + [LintDiagnostic, attributes( + // struct attributes + warning, + error, + lint, + note, + help, + // field attributes + skip_arg, + primary_span, + label, + subdiagnostic, + suggestion, + suggestion_short, + suggestion_hidden, + suggestion_verbose)] => diagnostics::lint_diagnostic_derive +); +decl_derive!( [SessionSubdiagnostic, attributes( // struct/variant attributes label, diff --git a/compiler/rustc_macros/src/type_foldable.rs b/compiler/rustc_macros/src/type_foldable.rs index 9e834d3ba1c..23e619221aa 100644 --- a/compiler/rustc_macros/src/type_foldable.rs +++ b/compiler/rustc_macros/src/type_foldable.rs @@ -11,11 +11,6 @@ pub fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2:: } s.add_bounds(synstructure::AddBounds::Generics); - let body_visit = s.each(|bind| { - quote! { - ::rustc_middle::ty::fold::TypeFoldable::visit_with(#bind, __folder)?; - } - }); s.bind_with(|_| synstructure::BindStyle::Move); let body_fold = s.each_variant(|vi| { let bindings = vi.bindings(); @@ -36,14 +31,6 @@ pub fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2:: ) -> Result<Self, __F::Error> { Ok(match self { #body_fold }) } - - fn visit_with<__F: ::rustc_middle::ty::fold::TypeVisitor<'tcx>>( - &self, - __folder: &mut __F - ) -> ::std::ops::ControlFlow<__F::BreakTy> { - match *self { #body_visit } - ::std::ops::ControlFlow::CONTINUE - } }, ) } diff --git a/compiler/rustc_macros/src/type_visitable.rs b/compiler/rustc_macros/src/type_visitable.rs new file mode 100644 index 00000000000..14e6aa6e0c1 --- /dev/null +++ b/compiler/rustc_macros/src/type_visitable.rs @@ -0,0 +1,33 @@ +use quote::quote; +use syn::parse_quote; + +pub fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + if let syn::Data::Union(_) = s.ast().data { + panic!("cannot derive on union") + } + + if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { + s.add_impl_generic(parse_quote! { 'tcx }); + } + + s.add_bounds(synstructure::AddBounds::Generics); + let body_visit = s.each(|bind| { + quote! { + ::rustc_middle::ty::visit::TypeVisitable::visit_with(#bind, __visitor)?; + } + }); + s.bind_with(|_| synstructure::BindStyle::Move); + + s.bound_impl( + quote!(::rustc_middle::ty::visit::TypeVisitable<'tcx>), + quote! { + fn visit_with<__V: ::rustc_middle::ty::visit::TypeVisitor<'tcx>>( + &self, + __visitor: &mut __V + ) -> ::std::ops::ControlFlow<__V::BreakTy> { + match *self { #body_visit } + ::std::ops::ControlFlow::CONTINUE + } + }, + ) +} diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 984c95b314b..661a9b1944c 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -96,6 +96,7 @@ macro_rules! arena_types { // (during lowering) and the `librustc_middle` arena (for decoding MIR) [decode] asm_template: rustc_ast::InlineAsmTemplatePiece, [decode] used_trait_imports: rustc_data_structures::fx::FxHashSet<rustc_hir::def_id::LocalDefId>, + [decode] is_late_bound_map: rustc_data_structures::fx::FxIndexSet<rustc_hir::def_id::LocalDefId>, [decode] impl_source: rustc_middle::traits::ImplSource<'tcx, ()>, [] dep_kind: rustc_middle::dep_graph::DepKindStruct, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 26b43488408..cda0a60fa4e 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -225,7 +225,8 @@ impl<'hir> Map<'hir> { self.tcx.definitions_untracked().iter_local_def_id() } - pub fn opt_def_kind(self, local_def_id: LocalDefId) -> Option<DefKind> { + /// Do not call this function directly. The query should be called. + pub(super) fn opt_def_kind(self, local_def_id: LocalDefId) -> Option<DefKind> { let hir_id = self.local_def_id_to_hir_id(local_def_id); let def_kind = match self.find(hir_id)? { Node::Item(item) => match item.kind { @@ -1012,12 +1013,13 @@ impl<'hir> Map<'hir> { ItemKind::Use(path, _) => path.span, _ => named_span(item.span, item.ident, item.kind.generics()), }, + Node::Variant(variant) => named_span(variant.span, variant.ident, None), Node::ImplItem(item) => named_span(item.span, item.ident, Some(item.generics)), Node::ForeignItem(item) => match item.kind { ForeignItemKind::Fn(decl, _, _) => until_within(item.span, decl.output.span()), _ => named_span(item.span, item.ident, None), }, - Node::Ctor(..) => return self.opt_span(self.get_parent_node(hir_id)), + Node::Ctor(_) => return self.opt_span(self.get_parent_node(hir_id)), _ => self.span_with_body(hir_id), }; Some(span) diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 8622a620721..12209d6725c 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -63,6 +63,15 @@ impl ModuleItems { self.foreign_items.iter().copied() } + pub fn definitions(&self) -> impl Iterator<Item = LocalDefId> + '_ { + self.items + .iter() + .map(|id| id.def_id) + .chain(self.trait_items.iter().map(|id| id.def_id)) + .chain(self.impl_items.iter().map(|id| id.def_id)) + .chain(self.foreign_items.iter().map(|id| id.def_id)) + } + pub fn par_items(&self, f: impl Fn(ItemId) + Send + Sync) { par_for_each_in(&self.items[..], |&id| f(id)) } diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs index 00db19019c4..83d3b0100b8 100644 --- a/compiler/rustc_middle/src/hir/place.rs +++ b/compiler/rustc_middle/src/hir/place.rs @@ -4,18 +4,8 @@ use crate::ty::Ty; use rustc_hir::HirId; use rustc_target::abi::VariantIdx; -#[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - TyEncodable, - TyDecodable, - TypeFoldable, - HashStable -)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub enum PlaceBase { /// A temporary variable. Rvalue, @@ -27,18 +17,8 @@ pub enum PlaceBase { Upvar(ty::UpvarId), } -#[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - TyEncodable, - TyDecodable, - TypeFoldable, - HashStable -)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub enum ProjectionKind { /// A dereference of a pointer, reference or `Box<T>` of the given type. Deref, @@ -58,18 +38,8 @@ pub enum ProjectionKind { Subslice, } -#[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - TyEncodable, - TyDecodable, - TypeFoldable, - HashStable -)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub struct Projection<'tcx> { /// Type after the projection is applied. pub ty: Ty<'tcx>, @@ -81,7 +51,8 @@ pub struct Projection<'tcx> { /// A `Place` represents how a value is located in memory. /// /// This is an HIR version of [`rustc_middle::mir::Place`]. -#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub struct Place<'tcx> { /// The type of the `PlaceBase` pub base_ty: Ty<'tcx>, @@ -94,7 +65,8 @@ pub struct Place<'tcx> { /// A `PlaceWithHirId` represents how a value is located in memory. /// /// This is an HIR version of [`rustc_middle::mir::Place`]. -#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub struct PlaceWithHirId<'tcx> { /// `HirId` of the expression or pattern producing this value. pub hir_id: HirId, diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 79f94802d20..c6fe3e72103 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -34,7 +34,7 @@ use std::ops::Index; /// variables have been rewritten to "canonical vars". These are /// numbered starting from 0 in order of first appearance. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable)] -#[derive(HashStable, TypeFoldable, Lift)] +#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct Canonical<'tcx, V> { pub max_universe: ty::UniverseIndex, pub variables: CanonicalVarInfos<'tcx>, @@ -53,7 +53,7 @@ pub type CanonicalVarInfos<'tcx> = &'tcx List<CanonicalVarInfo<'tcx>>; /// variables. You will need to supply it later to instantiate the /// canonicalized query response. #[derive(Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable)] -#[derive(HashStable, TypeFoldable, Lift)] +#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct CanonicalVarValues<'tcx> { pub var_values: IndexVec<BoundVar, GenericArg<'tcx>>, } @@ -173,7 +173,7 @@ pub enum CanonicalTyVarKind { /// After we execute a query with a canonicalized key, we get back a /// `Canonical<QueryResponse<..>>`. You can use /// `instantiate_query_result` to access the data in this result. -#[derive(Clone, Debug, HashStable, TypeFoldable, Lift)] +#[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct QueryResponse<'tcx, R> { pub var_values: CanonicalVarValues<'tcx>, pub region_constraints: QueryRegionConstraints<'tcx>, @@ -187,7 +187,7 @@ pub struct QueryResponse<'tcx, R> { pub value: R, } -#[derive(Clone, Debug, Default, HashStable, TypeFoldable, Lift)] +#[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct QueryRegionConstraints<'tcx> { pub outlives: Vec<QueryOutlivesConstraint<'tcx>>, pub member_constraints: Vec<MemberConstraint<'tcx>>, @@ -293,7 +293,7 @@ impl<'tcx, V> Canonical<'tcx, V> { pub type QueryOutlivesConstraint<'tcx> = ty::Binder<'tcx, ty::OutlivesPredicate<GenericArg<'tcx>, Region<'tcx>>>; -TrivialTypeFoldableAndLiftImpls! { +TrivialTypeTraversalAndLiftImpls! { for <'tcx> { crate::infer::canonical::Certainty, crate::infer::canonical::CanonicalVarInfo<'tcx>, @@ -301,7 +301,7 @@ TrivialTypeFoldableAndLiftImpls! { } } -TrivialTypeFoldableImpls! { +TrivialTypeTraversalImpls! { for <'tcx> { crate::infer::canonical::CanonicalVarInfos<'tcx>, } diff --git a/compiler/rustc_middle/src/infer/mod.rs b/compiler/rustc_middle/src/infer/mod.rs index 2350a6ab155..55e00c4c0d8 100644 --- a/compiler/rustc_middle/src/infer/mod.rs +++ b/compiler/rustc_middle/src/infer/mod.rs @@ -13,7 +13,7 @@ use rustc_span::Span; /// ```text /// R0 member of [O1..On] /// ``` -#[derive(Debug, Clone, HashStable, TypeFoldable, Lift)] +#[derive(Debug, Clone, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct MemberConstraint<'tcx> { /// The `DefId` of the opaque type causing this constraint: used for error reporting. pub opaque_type_def_id: DefId, diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 215d8decf2a..4b156de410d 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -2,9 +2,7 @@ use std::cmp; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_errors::{ - Diagnostic, DiagnosticBuilder, DiagnosticId, EmissionGuarantee, ErrorGuaranteed, MultiSpan, -}; +use rustc_errors::{Diagnostic, DiagnosticId, LintDiagnosticBuilder, MultiSpan}; use rustc_hir::HirId; use rustc_index::vec::IndexVec; use rustc_query_system::ich::StableHashingContext; @@ -227,28 +225,6 @@ impl LintExpectation { } } -pub struct LintDiagnosticBuilder<'a, G: EmissionGuarantee>(DiagnosticBuilder<'a, G>); - -impl<'a, G: EmissionGuarantee> LintDiagnosticBuilder<'a, G> { - /// Return the inner `DiagnosticBuilder`, first setting the primary message to `msg`. - pub fn build(mut self, msg: &str) -> DiagnosticBuilder<'a, G> { - self.0.set_primary_message(msg); - self.0.set_is_lint(); - self.0 - } - - /// Create a `LintDiagnosticBuilder` from some existing `DiagnosticBuilder`. - pub fn new(err: DiagnosticBuilder<'a, G>) -> LintDiagnosticBuilder<'a, G> { - LintDiagnosticBuilder(err) - } -} - -impl<'a> LintDiagnosticBuilder<'a, ErrorGuaranteed> { - pub fn forget_guarantee(self) -> LintDiagnosticBuilder<'a, ()> { - LintDiagnosticBuilder(self.0.forget_guarantee()) - } -} - pub fn explain_lint_level_source( lint: &'static Lint, level: Level, diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index 33b4dff977e..0e85c60a363 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -18,7 +18,7 @@ macro_rules! span_bug { } /////////////////////////////////////////////////////////////////////////// -// Lift and TypeFoldable macros +// Lift and TypeFoldable/TypeVisitable macros // // When possible, use one of these (relatively) convenient macros to write // the impls for you. @@ -48,7 +48,7 @@ macro_rules! CloneLiftImpls { /// Used for types that are `Copy` and which **do not care arena /// allocated data** (i.e., don't need to be folded). #[macro_export] -macro_rules! TrivialTypeFoldableImpls { +macro_rules! TrivialTypeTraversalImpls { (for <$tcx:lifetime> { $($ty:ty,)+ }) => { $( impl<$tcx> $crate::ty::fold::TypeFoldable<$tcx> for $ty { @@ -58,8 +58,10 @@ macro_rules! TrivialTypeFoldableImpls { ) -> ::std::result::Result<$ty, F::Error> { Ok(self) } + } - fn visit_with<F: $crate::ty::fold::TypeVisitor<$tcx>>( + impl<$tcx> $crate::ty::visit::TypeVisitable<$tcx> for $ty { + fn visit_with<F: $crate::ty::visit::TypeVisitor<$tcx>>( &self, _: &mut F) -> ::std::ops::ControlFlow<F::BreakTy> @@ -71,7 +73,7 @@ macro_rules! TrivialTypeFoldableImpls { }; ($($ty:ty,)+) => { - TrivialTypeFoldableImpls! { + TrivialTypeTraversalImpls! { for <'tcx> { $($ty,)+ } @@ -80,15 +82,15 @@ macro_rules! TrivialTypeFoldableImpls { } #[macro_export] -macro_rules! TrivialTypeFoldableAndLiftImpls { +macro_rules! TrivialTypeTraversalAndLiftImpls { ($($t:tt)*) => { - TrivialTypeFoldableImpls! { $($t)* } + TrivialTypeTraversalImpls! { $($t)* } CloneLiftImpls! { $($t)* } } } #[macro_export] -macro_rules! EnumTypeFoldableImpl { +macro_rules! EnumTypeTraversalImpl { (impl<$($p:tt),*> TypeFoldable<$tcx:tt> for $s:path { $($variants:tt)* } $(where $($wc:tt)*)*) => { @@ -99,14 +101,22 @@ macro_rules! EnumTypeFoldableImpl { self, folder: &mut V, ) -> ::std::result::Result<Self, V::Error> { - EnumTypeFoldableImpl!(@FoldVariants(self, folder) input($($variants)*) output()) + EnumTypeTraversalImpl!(@FoldVariants(self, folder) input($($variants)*) output()) } + } + }; - fn visit_with<V: $crate::ty::fold::TypeVisitor<$tcx>>( + (impl<$($p:tt),*> TypeVisitable<$tcx:tt> for $s:path { + $($variants:tt)* + } $(where $($wc:tt)*)*) => { + impl<$($p),*> $crate::ty::visit::TypeVisitable<$tcx> for $s + $(where $($wc)*)* + { + fn visit_with<V: $crate::ty::visit::TypeVisitor<$tcx>>( &self, visitor: &mut V, ) -> ::std::ops::ControlFlow<V::BreakTy> { - EnumTypeFoldableImpl!(@VisitVariants(self, visitor) input($($variants)*) output()) + EnumTypeTraversalImpl!(@VisitVariants(self, visitor) input($($variants)*) output()) } } }; @@ -120,7 +130,7 @@ macro_rules! EnumTypeFoldableImpl { (@FoldVariants($this:expr, $folder:expr) input( ($variant:path) ( $($variant_arg:ident),* ) , $($input:tt)*) output( $($output:tt)*) ) => { - EnumTypeFoldableImpl!( + EnumTypeTraversalImpl!( @FoldVariants($this, $folder) input($($input)*) output( @@ -137,7 +147,7 @@ macro_rules! EnumTypeFoldableImpl { (@FoldVariants($this:expr, $folder:expr) input( ($variant:path) { $($variant_arg:ident),* $(,)? } , $($input:tt)*) output( $($output:tt)*) ) => { - EnumTypeFoldableImpl!( + EnumTypeTraversalImpl!( @FoldVariants($this, $folder) input($($input)*) output( @@ -155,7 +165,7 @@ macro_rules! EnumTypeFoldableImpl { (@FoldVariants($this:expr, $folder:expr) input( ($variant:path), $($input:tt)*) output( $($output:tt)*) ) => { - EnumTypeFoldableImpl!( + EnumTypeTraversalImpl!( @FoldVariants($this, $folder) input($($input)*) output( @@ -174,12 +184,12 @@ macro_rules! EnumTypeFoldableImpl { (@VisitVariants($this:expr, $visitor:expr) input( ($variant:path) ( $($variant_arg:ident),* ) , $($input:tt)*) output( $($output:tt)*) ) => { - EnumTypeFoldableImpl!( + EnumTypeTraversalImpl!( @VisitVariants($this, $visitor) input($($input)*) output( $variant ( $($variant_arg),* ) => { - $($crate::ty::fold::TypeFoldable::visit_with( + $($crate::ty::visit::TypeVisitable::visit_with( $variant_arg, $visitor )?;)* ::std::ops::ControlFlow::CONTINUE @@ -192,12 +202,12 @@ macro_rules! EnumTypeFoldableImpl { (@VisitVariants($this:expr, $visitor:expr) input( ($variant:path) { $($variant_arg:ident),* $(,)? } , $($input:tt)*) output( $($output:tt)*) ) => { - EnumTypeFoldableImpl!( + EnumTypeTraversalImpl!( @VisitVariants($this, $visitor) input($($input)*) output( $variant { $($variant_arg),* } => { - $($crate::ty::fold::TypeFoldable::visit_with( + $($crate::ty::visit::TypeVisitable::visit_with( $variant_arg, $visitor )?;)* ::std::ops::ControlFlow::CONTINUE @@ -210,7 +220,7 @@ macro_rules! EnumTypeFoldableImpl { (@VisitVariants($this:expr, $visitor:expr) input( ($variant:path), $($input:tt)*) output( $($output:tt)*) ) => { - EnumTypeFoldableImpl!( + EnumTypeTraversalImpl!( @VisitVariants($this, $visitor) input($($input)*) output( diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 2336d460407..efa9464529e 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -100,7 +100,7 @@ impl From<InjectedExpressionId> for ExpressionOperandId { } } -#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] +#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] pub enum CoverageKind { Counter { function_source_hash: u64, @@ -148,18 +148,8 @@ impl Debug for CoverageKind { } } -#[derive( - Clone, - TyEncodable, - TyDecodable, - Hash, - HashStable, - TypeFoldable, - PartialEq, - Eq, - PartialOrd, - Ord -)] +#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, Eq, PartialOrd, Ord)] +#[derive(TypeFoldable, TypeVisitable)] pub struct CodeRegion { pub file_name: Symbol, pub start_line: u32, @@ -178,7 +168,8 @@ impl Debug for CodeRegion { } } -#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] +#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub enum Op { Subtract, Add, diff --git a/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs b/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs index 1279f5aee36..f97bf2883b3 100644 --- a/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs +++ b/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs @@ -58,6 +58,6 @@ impl<CTX> HashStable<CTX> for GraphIsCyclicCache { } } -TrivialTypeFoldableAndLiftImpls! { +TrivialTypeTraversalAndLiftImpls! { GraphIsCyclicCache, } diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 1e2b53040d2..1bbd71c3f1f 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -160,12 +160,18 @@ impl AllocError { } /// The information that makes up a memory access: offset and size. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone)] pub struct AllocRange { pub start: Size, pub size: Size, } +impl fmt::Debug for AllocRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[{:#x}..{:#x}]", self.start.bytes(), self.end().bytes()) + } +} + /// Free-starting constructor for less syntactic overhead. #[inline(always)] pub fn alloc_range(start: Size, size: Size) -> AllocRange { diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index a33a2921f57..2a1fd6f736e 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -29,7 +29,7 @@ impl From<ErrorGuaranteed> for ErrorHandled { } } -TrivialTypeFoldableAndLiftImpls! { +TrivialTypeTraversalAndLiftImpls! { ErrorHandled, } @@ -186,7 +186,7 @@ pub enum CheckInAllocMsg { impl fmt::Display for CheckInAllocMsg { /// When this is printed as an error the context looks like this: - /// "{msg}0x01 is not a valid pointer". + /// "{msg}{pointer} is a dangling pointer". fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, @@ -194,9 +194,9 @@ impl fmt::Display for CheckInAllocMsg { match *self { CheckInAllocMsg::DerefTest => "dereferencing pointer failed: ", CheckInAllocMsg::MemoryAccessTest => "memory access failed: ", - CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic failed: ", + CheckInAllocMsg::PointerArithmeticTest => "out-of-bounds pointer arithmetic: ", CheckInAllocMsg::OffsetFromTest => "out-of-bounds offset_from: ", - CheckInAllocMsg::InboundsTest => "", + CheckInAllocMsg::InboundsTest => "out-of-bounds pointer use: ", } ) } @@ -334,36 +334,28 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { p, ), PointerUseAfterFree(a) => { - write!(f, "pointer to {} was dereferenced after this allocation got freed", a) + write!(f, "pointer to {a:?} was dereferenced after this allocation got freed") } PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size: Size::ZERO, msg } => { write!( f, - "{}{alloc_id} has size {alloc_size}, so pointer at offset {ptr_offset} is out-of-bounds", - msg, - alloc_id = alloc_id, + "{msg}{alloc_id:?} has size {alloc_size}, so pointer at offset {ptr_offset} is out-of-bounds", alloc_size = alloc_size.bytes(), - ptr_offset = ptr_offset, ) } PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => write!( f, - "{}{alloc_id} has size {alloc_size}, so pointer to {ptr_size} byte{ptr_size_p} starting at offset {ptr_offset} is out-of-bounds", - msg, - alloc_id = alloc_id, + "{msg}{alloc_id:?} has size {alloc_size}, so pointer to {ptr_size} byte{ptr_size_p} starting at offset {ptr_offset} is out-of-bounds", alloc_size = alloc_size.bytes(), ptr_size = ptr_size.bytes(), ptr_size_p = pluralize!(ptr_size.bytes()), - ptr_offset = ptr_offset, ), - DanglingIntPointer(0, CheckInAllocMsg::InboundsTest) => { - write!(f, "null pointer is not a valid pointer for this operation") - } - DanglingIntPointer(0, msg) => { - write!(f, "{}null pointer is not a valid pointer", msg) - } DanglingIntPointer(i, msg) => { - write!(f, "{}0x{:x} is not a valid pointer", msg, i) + write!( + f, + "{msg}{pointer} is a dangling pointer (it has no provenance)", + pointer = Pointer::<Option<AllocId>>::from_addr(*i), + ) } AlignmentCheckFailed { required, has } => write!( f, @@ -371,8 +363,8 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { has.bytes(), required.bytes() ), - WriteToReadOnly(a) => write!(f, "writing to {} which is read-only", a), - DerefFunctionPointer(a) => write!(f, "accessing {} which contains a function", a), + WriteToReadOnly(a) => write!(f, "writing to {a:?} which is read-only"), + DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"), ValidationFailure { path: None, msg } => { write!(f, "constructing invalid value: {}", msg) } diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 8733a85ef3f..698024b2330 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -190,11 +190,7 @@ impl fmt::Debug for AllocId { } } -impl fmt::Display for AllocId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self, f) - } -} +// No "Display" since AllocIds are not usually user-visible. #[derive(TyDecodable, TyEncodable)] enum AllocDiscriminant { @@ -470,7 +466,7 @@ impl<'tcx> TyCtxt<'tcx> { return alloc_id; } let id = alloc_map.reserve(); - debug!("creating alloc {:?} with id {}", alloc, id); + debug!("creating alloc {alloc:?} with id {id:?}"); alloc_map.alloc_map.insert(id, alloc.clone()); alloc_map.dedup.insert(alloc, id); id @@ -538,7 +534,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn global_alloc(self, id: AllocId) -> GlobalAlloc<'tcx> { match self.get_global_alloc(id) { Some(alloc) => alloc, - None => bug!("could not find allocation for {}", id), + None => bug!("could not find allocation for {id:?}"), } } @@ -546,7 +542,7 @@ impl<'tcx> TyCtxt<'tcx> { /// call this function twice, even with the same `Allocation` will ICE the compiler. pub fn set_alloc_id_memory(self, id: AllocId, mem: ConstAllocation<'tcx>) { if let Some(old) = self.alloc_map.lock().alloc_map.insert(id, GlobalAlloc::Memory(mem)) { - bug!("tried to set allocation ID {}, but it was already existing as {:#?}", id, old); + bug!("tried to set allocation ID {id:?}, but it was already existing as {old:#?}"); } } diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index 26da93b9dce..d4cdf45d186 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -144,7 +144,7 @@ impl Provenance for AllocId { } // Print offset only if it is non-zero. if ptr.offset.bytes() > 0 { - write!(f, "+0x{:x}", ptr.offset.bytes())?; + write!(f, "+{:#x}", ptr.offset.bytes())?; } Ok(()) } @@ -181,7 +181,17 @@ impl<Tag: Provenance> fmt::Debug for Pointer<Option<Tag>> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.provenance { Some(tag) => Provenance::fmt(&Pointer::new(tag, self.offset), f), - None => write!(f, "0x{:x}", self.offset.bytes()), + None => write!(f, "{:#x}[noalloc]", self.offset.bytes()), + } + } +} + +impl<Tag: Provenance> fmt::Display for Pointer<Option<Tag>> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.provenance.is_none() && self.offset.bytes() == 0 { + write!(f, "null pointer") + } else { + fmt::Debug::fmt(self, f) } } } @@ -227,8 +237,13 @@ impl<Tag> Pointer<Option<Tag>> { impl<Tag> Pointer<Option<Tag>> { #[inline(always)] + pub fn from_addr(addr: u64) -> Self { + Pointer { provenance: None, offset: Size::from_bytes(addr) } + } + + #[inline(always)] pub fn null() -> Self { - Pointer { provenance: None, offset: Size::ZERO } + Pointer::from_addr(0) } } diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 575147feebc..786927e2dad 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -1,8 +1,8 @@ use super::{ErrorHandled, EvalToConstValueResult, EvalToValTreeResult, GlobalId}; use crate::mir; -use crate::ty::fold::TypeFoldable; use crate::ty::subst::InternalSubsts; +use crate::ty::visit::TypeVisitable; use crate::ty::{self, query::TyCtxtAt, query::TyCtxtEnsure, TyCtxt}; use rustc_hir::def_id::DefId; use rustc_span::{Span, DUMMY_SP}; diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index e80918d5e5d..8ecbb5ab0b3 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -167,7 +167,7 @@ impl<Tag: Provenance> fmt::LowerHex for Scalar<Tag> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Scalar::Ptr(ptr, _size) => write!(f, "pointer to {:?}", ptr), - Scalar::Int(int) => write!(f, "0x{:x}", int), + Scalar::Int(int) => write!(f, "{:#x}", int), } } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index ad33a54e0a2..e7d7317456c 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -8,9 +8,10 @@ use crate::mir::interpret::{ use crate::mir::traversal::PostorderCache; use crate::mir::visit::MirVisitable; use crate::ty::codec::{TyDecoder, TyEncoder}; -use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable, TypeVisitor}; +use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; use crate::ty::print::{FmtPrinter, Printer}; use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; +use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; use crate::ty::{self, List, Ty, TyCtxt}; use crate::ty::{AdtDef, InstanceDef, ScalarInt, UserTypeAnnotationIndex}; @@ -68,6 +69,7 @@ pub use terminator::*; pub mod traversal; mod type_foldable; +mod type_visitable; pub mod visit; pub use self::generic_graph::graphviz_safe_def_name; @@ -136,7 +138,7 @@ impl MirPhase { /// Where a specific `mir::Body` comes from. #[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable)] +#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)] pub struct MirSource<'tcx> { pub instance: InstanceDef<'tcx>, @@ -166,7 +168,7 @@ impl<'tcx> MirSource<'tcx> { } } -#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable)] +#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct GeneratorInfo<'tcx> { /// The yield type of the function, if it is a generator. pub yield_ty: Option<Ty<'tcx>>, @@ -183,7 +185,7 @@ pub struct GeneratorInfo<'tcx> { } /// The lowered representation of a single function. -#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable)] +#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct Body<'tcx> { /// A list of basic blocks. References to basic block use a newtyped index type [`BasicBlock`] /// that indexes into this vector. @@ -357,10 +359,7 @@ impl<'tcx> Body<'tcx> { // // FIXME: Use a finer-grained API for this, so only transformations that alter terminators // invalidate the caches. - self.predecessor_cache.invalidate(); - self.switch_source_cache.invalidate(); - self.is_cyclic.invalidate(); - self.postorder_cache.invalidate(); + self.invalidate_cfg_cache(); &mut self.basic_blocks } @@ -368,10 +367,7 @@ impl<'tcx> Body<'tcx> { pub fn basic_blocks_and_local_decls_mut( &mut self, ) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) { - self.predecessor_cache.invalidate(); - self.switch_source_cache.invalidate(); - self.is_cyclic.invalidate(); - self.postorder_cache.invalidate(); + self.invalidate_cfg_cache(); (&mut self.basic_blocks, &mut self.local_decls) } @@ -383,11 +379,43 @@ impl<'tcx> Body<'tcx> { &mut LocalDecls<'tcx>, &mut Vec<VarDebugInfo<'tcx>>, ) { + self.invalidate_cfg_cache(); + (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info) + } + + /// Get mutable access to parts of the Body without invalidating the CFG cache. + /// + /// By calling this method instead of eg [`Body::basic_blocks_mut`], you promise not to change + /// the CFG. This means that + /// + /// 1) The number of basic blocks remains unchanged + /// 2) The set of successors of each terminator remains unchanged. + /// 3) For each `TerminatorKind::SwitchInt`, the `targets` remains the same and the terminator + /// kind is not changed. + /// + /// If any of these conditions cannot be upheld, you should call [`Body::invalidate_cfg_cache`]. + #[inline] + pub fn basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate( + &mut self, + ) -> ( + &mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, + &mut LocalDecls<'tcx>, + &mut Vec<VarDebugInfo<'tcx>>, + ) { + (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info) + } + + /// Invalidates cached information about the CFG. + /// + /// You will only ever need this if you have also called + /// [`Body::basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate`]. All other methods + /// that allow you to mutate the body also call this method themselves, thereby avoiding any + /// risk of accidentaly cache invalidation. + pub fn invalidate_cfg_cache(&mut self) { self.predecessor_cache.invalidate(); self.switch_source_cache.invalidate(); self.is_cyclic.invalidate(); self.postorder_cache.invalidate(); - (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info) } /// Returns `true` if a cycle exists in the control-flow graph that is reachable from the @@ -575,7 +603,7 @@ impl<'tcx> IndexMut<BasicBlock> for Body<'tcx> { } } -#[derive(Copy, Clone, Debug, HashStable, TypeFoldable)] +#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] pub enum ClearCrossCrate<T> { Clear, Set(T), @@ -736,7 +764,7 @@ pub enum ImplicitSelfKind { None, } -TrivialTypeFoldableAndLiftImpls! { BindingForm<'tcx>, } +TrivialTypeTraversalAndLiftImpls! { BindingForm<'tcx>, } mod binding_form_impl { use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -781,7 +809,7 @@ pub struct BlockTailInfo { /// /// This can be a binding declared by the user, a temporary inserted by the compiler, a function /// argument, or the return place. -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct LocalDecl<'tcx> { /// Whether this is a mutable binding (i.e., `let x` or `let mut x`). /// @@ -916,7 +944,7 @@ static_assert_size!(LocalDecl<'_>, 56); /// /// Not used for non-StaticRef temporaries, the return place, or anonymous /// function parameters. -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub enum LocalInfo<'tcx> { /// A user-defined local variable or function parameter /// @@ -1055,7 +1083,7 @@ impl<'tcx> LocalDecl<'tcx> { } } -#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub enum VarDebugInfoContents<'tcx> { /// NOTE(eddyb) There's an unenforced invariant that this `Place` is /// based on a `Local`, not a `Static`, and contains no indexing. @@ -1073,7 +1101,7 @@ impl<'tcx> Debug for VarDebugInfoContents<'tcx> { } /// Debug information pertaining to a user variable. -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct VarDebugInfo<'tcx> { pub name: Symbol, @@ -1129,7 +1157,7 @@ impl BasicBlock { // BasicBlockData /// See [`BasicBlock`] for documentation on what basic blocks are at a high level. -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct BasicBlockData<'tcx> { /// List of statements in this block. pub statements: Vec<Statement<'tcx>>, @@ -1366,7 +1394,7 @@ impl<O: fmt::Debug> fmt::Debug for AssertKind<O> { /////////////////////////////////////////////////////////////////////////// // Statements -#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct Statement<'tcx> { pub source_info: SourceInfo, pub kind: StatementKind<'tcx>, @@ -1732,7 +1760,7 @@ impl SourceScope { } } -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct SourceScopeData<'tcx> { pub span: Span, pub parent_scope: Option<SourceScope>, @@ -2498,7 +2526,7 @@ impl<'tcx> ConstantKind<'tcx> { /// The first will lead to the constraint `w: &'1 str` (for some /// inferred region `'1`). The second will lead to the constraint `w: /// &'static str`. -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct UserTypeProjections { pub contents: Vec<(UserTypeProjection, Span)>, } @@ -2615,7 +2643,7 @@ impl UserTypeProjection { } } -TrivialTypeFoldableAndLiftImpls! { ProjectionKind, } +TrivialTypeTraversalAndLiftImpls! { ProjectionKind, } impl<'tcx> TypeFoldable<'tcx> for UserTypeProjection { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { @@ -2624,7 +2652,9 @@ impl<'tcx> TypeFoldable<'tcx> for UserTypeProjection { projs: self.projs.try_fold_with(folder)?, }) } +} +impl<'tcx> TypeVisitable<'tcx> for UserTypeProjection { fn visit_with<Vs: TypeVisitor<'tcx>>(&self, visitor: &mut Vs) -> ControlFlow<Vs::BreakTy> { self.base.visit_with(visitor) // Note: there's nothing in `self.proj` to visit. diff --git a/compiler/rustc_middle/src/mir/predecessors.rs b/compiler/rustc_middle/src/mir/predecessors.rs index 620cf7e336b..5f1fadaf3bc 100644 --- a/compiler/rustc_middle/src/mir/predecessors.rs +++ b/compiler/rustc_middle/src/mir/predecessors.rs @@ -73,6 +73,6 @@ impl<CTX> HashStable<CTX> for PredecessorCache { } } -TrivialTypeFoldableAndLiftImpls! { +TrivialTypeTraversalAndLiftImpls! { PredecessorCache, } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 462c0ada3cf..24c6cd91d0a 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -716,12 +716,12 @@ pub fn write_allocations<'tcx>( } write!(w, "{}", display_allocation(tcx, alloc.inner())) }; - write!(w, "\n{}", id)?; + write!(w, "\n{id:?}")?; match tcx.get_global_alloc(id) { // This can't really happen unless there are bugs, but it doesn't cost us anything to // gracefully handle it and allow buggy rustc to be debugged via allocation printing. None => write!(w, " (deallocated)")?, - Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {})", inst)?, + Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {inst})")?, Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { match tcx.eval_static_initializer(did) { Ok(alloc) => { diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index da4793fa039..620f0380d53 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -161,7 +161,7 @@ rustc_index::newtype_index! { } /// The layout of generator state. -#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct GeneratorLayout<'tcx> { /// The type of every local stored inside the generator. pub field_tys: IndexVec<GeneratorSavedLocal, Ty<'tcx>>, diff --git a/compiler/rustc_middle/src/mir/switch_sources.rs b/compiler/rustc_middle/src/mir/switch_sources.rs index 99d13fcfef4..d1f3e6b6fe6 100644 --- a/compiler/rustc_middle/src/mir/switch_sources.rs +++ b/compiler/rustc_middle/src/mir/switch_sources.rs @@ -73,6 +73,6 @@ impl<CTX> HashStable<CTX> for SwitchSourceCache { } } -TrivialTypeFoldableAndLiftImpls! { +TrivialTypeTraversalAndLiftImpls! { SwitchSourceCache, } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index f8ee59f306f..dcc37c565c9 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -179,7 +179,8 @@ pub enum BorrowKind { /// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which /// ones you do not have to worry about. The MIR validator will generally enforce such restrictions, /// causing an ICE if they are violated. -#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] +#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub enum StatementKind<'tcx> { /// Assign statements roughly correspond to an assignment in Rust proper (`x = ...`) except /// without the possibility of dropping the previous value (that must be done separately, if at @@ -311,6 +312,7 @@ pub enum StatementKind<'tcx> { /// Describes what kind of retag is to be performed. #[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, Hash, HashStable)] +#[rustc_pass_by_value] pub enum RetagKind { /// The initial retag when entering a function. FnEntry, @@ -375,13 +377,15 @@ pub enum FakeReadCause { ForIndex, } -#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] +#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub struct Coverage { pub kind: CoverageKind, pub code_region: Option<CodeRegion>, } -#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] +#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub struct CopyNonOverlapping<'tcx> { pub src: Operand<'tcx>, pub dst: Operand<'tcx>, @@ -392,6 +396,8 @@ pub struct CopyNonOverlapping<'tcx> { /////////////////////////////////////////////////////////////////////////// // Terminators +/// The various kinds of terminators, representing ways of exiting from a basic block. +/// /// A note on unwinding: Panics may occur during the execution of some terminators. Depending on the /// `-C panic` flag, this may either cause the program to abort or the call stack to unwind. Such /// terminators have a `cleanup: Option<BasicBlock>` field on them. If stack unwinding occurs, then @@ -671,7 +677,8 @@ pub enum AssertKind<O> { ResumedAfterPanic(GeneratorKind), } -#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] +#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub enum InlineAsmOperand<'tcx> { In { reg: InlineAsmRegOrRegClass, @@ -906,7 +913,7 @@ pub enum Operand<'tcx> { static_assert_size!(Operand<'_>, 24); /////////////////////////////////////////////////////////////////////////// -/// Rvalues +// Rvalues /// The various kinds of rvalues that can appear in MIR. /// @@ -990,11 +997,20 @@ pub enum Rvalue<'tcx> { /// matching types and return a value of that type. BinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>), - /// Same as `BinaryOp`, but yields `(T, bool)` instead of `T`. In addition to performing the - /// same computation as the matching `BinaryOp`, checks if the infinite precison result would be - /// unequal to the actual result and sets the `bool` if this is the case. + /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition. + /// + /// When overflow checking is disabled and we are generating run-time code, the error condition + /// is false. Otherwise, and always during CTFE, the error condition is determined as described + /// below. + /// + /// For addition, subtraction, and multiplication on integers the error condition is set when + /// the infinite precision result would be unequal to the actual result. + /// + /// For shift operations on integers the error condition is set when the value of right-hand + /// side is greater than or equal to the number of bits in the type of the left-hand side, or + /// when the value of right-hand side is negative. /// - /// This only supports addition, subtraction, multiplication, and shift operations on integers. + /// Other combinations of types and operators are unsupported. CheckedBinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>), /// Computes a value as described by the operation. diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index c93b7a95502..c99faf80187 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -9,7 +9,7 @@ use crate::ty::{self, Ty, TyCtxt}; use rustc_hir as hir; use rustc_target::abi::VariantIdx; -#[derive(Copy, Clone, Debug, TypeFoldable)] +#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)] pub struct PlaceTy<'tcx> { pub ty: Ty<'tcx>, /// Downcast to a particular variant of an enum or a generator, if included. diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs index 7228e3f33b1..30648679dae 100644 --- a/compiler/rustc_middle/src/mir/traversal.rs +++ b/compiler/rustc_middle/src/mir/traversal.rs @@ -384,6 +384,6 @@ impl<CTX> HashStable<CTX> for PostorderCache { } } -TrivialTypeFoldableAndLiftImpls! { +TrivialTypeTraversalAndLiftImpls! { PostorderCache, } diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index 4201b2d11ce..3c9850a9eb3 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -4,7 +4,7 @@ use super::*; use crate::ty; use rustc_data_structures::functor::IdFunctor; -TrivialTypeFoldableAndLiftImpls! { +TrivialTypeTraversalAndLiftImpls! { BlockTailInfo, MirPhase, SourceInfo, @@ -89,65 +89,12 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { }; Ok(Terminator { source_info: self.source_info, kind }) } - - fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { - use crate::mir::TerminatorKind::*; - - match self.kind { - SwitchInt { ref discr, switch_ty, .. } => { - discr.visit_with(visitor)?; - switch_ty.visit_with(visitor) - } - Drop { ref place, .. } => place.visit_with(visitor), - DropAndReplace { ref place, ref value, .. } => { - place.visit_with(visitor)?; - value.visit_with(visitor) - } - Yield { ref value, .. } => value.visit_with(visitor), - Call { ref func, ref args, ref destination, .. } => { - destination.visit_with(visitor)?; - func.visit_with(visitor)?; - args.visit_with(visitor) - } - Assert { ref cond, ref msg, .. } => { - cond.visit_with(visitor)?; - use AssertKind::*; - match msg { - BoundsCheck { ref len, ref index } => { - len.visit_with(visitor)?; - index.visit_with(visitor) - } - Overflow(_, l, r) => { - l.visit_with(visitor)?; - r.visit_with(visitor) - } - OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => { - op.visit_with(visitor) - } - ResumedAfterReturn(_) | ResumedAfterPanic(_) => ControlFlow::CONTINUE, - } - } - InlineAsm { ref operands, .. } => operands.visit_with(visitor), - Goto { .. } - | Resume - | Abort - | Return - | GeneratorDrop - | Unreachable - | FalseEdge { .. } - | FalseUnwind { .. } => ControlFlow::CONTINUE, - } - } } impl<'tcx> TypeFoldable<'tcx> for GeneratorKind { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, _: &mut F) -> Result<Self, F::Error> { Ok(self) } - - fn visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<V::BreakTy> { - ControlFlow::CONTINUE - } } impl<'tcx> TypeFoldable<'tcx> for Place<'tcx> { @@ -157,21 +104,12 @@ impl<'tcx> TypeFoldable<'tcx> for Place<'tcx> { projection: self.projection.try_fold_with(folder)?, }) } - - fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { - self.local.visit_with(visitor)?; - self.projection.visit_with(visitor) - } } impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<PlaceElem<'tcx>> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { ty::util::fold_list(self, folder, |tcx, v| tcx.intern_place_elems(v)) } - - fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { - self.iter().try_for_each(|t| t.visit_with(visitor)) - } } impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> { @@ -224,55 +162,6 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> { } }) } - - fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { - use crate::mir::Rvalue::*; - match *self { - Use(ref op) => op.visit_with(visitor), - Repeat(ref op, _) => op.visit_with(visitor), - ThreadLocalRef(did) => did.visit_with(visitor), - Ref(region, _, ref place) => { - region.visit_with(visitor)?; - place.visit_with(visitor) - } - AddressOf(_, ref place) => place.visit_with(visitor), - Len(ref place) => place.visit_with(visitor), - Cast(_, ref op, ty) => { - op.visit_with(visitor)?; - ty.visit_with(visitor) - } - BinaryOp(_, box (ref rhs, ref lhs)) | CheckedBinaryOp(_, box (ref rhs, ref lhs)) => { - rhs.visit_with(visitor)?; - lhs.visit_with(visitor) - } - UnaryOp(_, ref val) => val.visit_with(visitor), - Discriminant(ref place) => place.visit_with(visitor), - NullaryOp(_, ty) => ty.visit_with(visitor), - Aggregate(ref kind, ref fields) => { - match **kind { - AggregateKind::Array(ty) => { - ty.visit_with(visitor)?; - } - AggregateKind::Tuple => {} - AggregateKind::Adt(_, _, substs, user_ty, _) => { - substs.visit_with(visitor)?; - user_ty.visit_with(visitor)?; - } - AggregateKind::Closure(_, substs) => { - substs.visit_with(visitor)?; - } - AggregateKind::Generator(_, substs, _) => { - substs.visit_with(visitor)?; - } - } - fields.visit_with(visitor) - } - ShallowInitBox(ref op, ty) => { - op.visit_with(visitor)?; - ty.visit_with(visitor) - } - } - } } impl<'tcx> TypeFoldable<'tcx> for Operand<'tcx> { @@ -283,13 +172,6 @@ impl<'tcx> TypeFoldable<'tcx> for Operand<'tcx> { Operand::Constant(c) => Operand::Constant(c.try_fold_with(folder)?), }) } - - fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { - match *self { - Operand::Copy(ref place) | Operand::Move(ref place) => place.visit_with(visitor), - Operand::Constant(ref c) => c.visit_with(visitor), - } - } } impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> { @@ -307,43 +189,24 @@ impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> { Subslice { from, to, from_end } => Subslice { from, to, from_end }, }) } - - fn visit_with<Vs: TypeVisitor<'tcx>>(&self, visitor: &mut Vs) -> ControlFlow<Vs::BreakTy> { - use crate::mir::ProjectionElem::*; - - match self { - Field(_, ty) => ty.visit_with(visitor), - Index(v) => v.visit_with(visitor), - _ => ControlFlow::CONTINUE, - } - } } impl<'tcx> TypeFoldable<'tcx> for Field { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, _: &mut F) -> Result<Self, F::Error> { Ok(self) } - fn visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<V::BreakTy> { - ControlFlow::CONTINUE - } } impl<'tcx> TypeFoldable<'tcx> for GeneratorSavedLocal { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, _: &mut F) -> Result<Self, F::Error> { Ok(self) } - fn visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<V::BreakTy> { - ControlFlow::CONTINUE - } } impl<'tcx, R: Idx, C: Idx> TypeFoldable<'tcx> for BitMatrix<R, C> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, _: &mut F) -> Result<Self, F::Error> { Ok(self) } - fn visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<V::BreakTy> { - ControlFlow::CONTINUE - } } impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> { @@ -354,10 +217,6 @@ impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> { literal: self.literal.try_fold_with(folder)?, }) } - fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { - self.literal.visit_with(visitor)?; - self.user_ty.visit_with(visitor) - } } impl<'tcx> TypeFoldable<'tcx> for ConstantKind<'tcx> { @@ -365,10 +224,6 @@ impl<'tcx> TypeFoldable<'tcx> for ConstantKind<'tcx> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { folder.try_fold_mir_const(self) } - - fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { - visitor.visit_mir_const(*self) - } } impl<'tcx> TypeSuperFoldable<'tcx> for ConstantKind<'tcx> { @@ -381,11 +236,4 @@ impl<'tcx> TypeSuperFoldable<'tcx> for ConstantKind<'tcx> { ConstantKind::Val(v, t) => Ok(ConstantKind::Val(v, t.try_fold_with(folder)?)), } } - - fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { - match *self { - ConstantKind::Ty(c) => c.visit_with(visitor), - ConstantKind::Val(_, t) => t.visit_with(visitor), - } - } } diff --git a/compiler/rustc_middle/src/mir/type_visitable.rs b/compiler/rustc_middle/src/mir/type_visitable.rs new file mode 100644 index 00000000000..d52ae5fac67 --- /dev/null +++ b/compiler/rustc_middle/src/mir/type_visitable.rs @@ -0,0 +1,186 @@ +//! `TypeVisitable` implementations for MIR types + +use super::*; +use crate::ty; + +impl<'tcx> TypeVisitable<'tcx> for Terminator<'tcx> { + fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + use crate::mir::TerminatorKind::*; + + match self.kind { + SwitchInt { ref discr, switch_ty, .. } => { + discr.visit_with(visitor)?; + switch_ty.visit_with(visitor) + } + Drop { ref place, .. } => place.visit_with(visitor), + DropAndReplace { ref place, ref value, .. } => { + place.visit_with(visitor)?; + value.visit_with(visitor) + } + Yield { ref value, .. } => value.visit_with(visitor), + Call { ref func, ref args, ref destination, .. } => { + destination.visit_with(visitor)?; + func.visit_with(visitor)?; + args.visit_with(visitor) + } + Assert { ref cond, ref msg, .. } => { + cond.visit_with(visitor)?; + use AssertKind::*; + match msg { + BoundsCheck { ref len, ref index } => { + len.visit_with(visitor)?; + index.visit_with(visitor) + } + Overflow(_, l, r) => { + l.visit_with(visitor)?; + r.visit_with(visitor) + } + OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => { + op.visit_with(visitor) + } + ResumedAfterReturn(_) | ResumedAfterPanic(_) => ControlFlow::CONTINUE, + } + } + InlineAsm { ref operands, .. } => operands.visit_with(visitor), + Goto { .. } + | Resume + | Abort + | Return + | GeneratorDrop + | Unreachable + | FalseEdge { .. } + | FalseUnwind { .. } => ControlFlow::CONTINUE, + } + } +} + +impl<'tcx> TypeVisitable<'tcx> for GeneratorKind { + fn visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<V::BreakTy> { + ControlFlow::CONTINUE + } +} + +impl<'tcx> TypeVisitable<'tcx> for Place<'tcx> { + fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + self.local.visit_with(visitor)?; + self.projection.visit_with(visitor) + } +} + +impl<'tcx> TypeVisitable<'tcx> for &'tcx ty::List<PlaceElem<'tcx>> { + fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + self.iter().try_for_each(|t| t.visit_with(visitor)) + } +} + +impl<'tcx> TypeVisitable<'tcx> for Rvalue<'tcx> { + fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + use crate::mir::Rvalue::*; + match *self { + Use(ref op) => op.visit_with(visitor), + Repeat(ref op, _) => op.visit_with(visitor), + ThreadLocalRef(did) => did.visit_with(visitor), + Ref(region, _, ref place) => { + region.visit_with(visitor)?; + place.visit_with(visitor) + } + AddressOf(_, ref place) => place.visit_with(visitor), + Len(ref place) => place.visit_with(visitor), + Cast(_, ref op, ty) => { + op.visit_with(visitor)?; + ty.visit_with(visitor) + } + BinaryOp(_, box (ref rhs, ref lhs)) | CheckedBinaryOp(_, box (ref rhs, ref lhs)) => { + rhs.visit_with(visitor)?; + lhs.visit_with(visitor) + } + UnaryOp(_, ref val) => val.visit_with(visitor), + Discriminant(ref place) => place.visit_with(visitor), + NullaryOp(_, ty) => ty.visit_with(visitor), + Aggregate(ref kind, ref fields) => { + match **kind { + AggregateKind::Array(ty) => { + ty.visit_with(visitor)?; + } + AggregateKind::Tuple => {} + AggregateKind::Adt(_, _, substs, user_ty, _) => { + substs.visit_with(visitor)?; + user_ty.visit_with(visitor)?; + } + AggregateKind::Closure(_, substs) => { + substs.visit_with(visitor)?; + } + AggregateKind::Generator(_, substs, _) => { + substs.visit_with(visitor)?; + } + } + fields.visit_with(visitor) + } + ShallowInitBox(ref op, ty) => { + op.visit_with(visitor)?; + ty.visit_with(visitor) + } + } + } +} + +impl<'tcx> TypeVisitable<'tcx> for Operand<'tcx> { + fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + match *self { + Operand::Copy(ref place) | Operand::Move(ref place) => place.visit_with(visitor), + Operand::Constant(ref c) => c.visit_with(visitor), + } + } +} + +impl<'tcx> TypeVisitable<'tcx> for PlaceElem<'tcx> { + fn visit_with<Vs: TypeVisitor<'tcx>>(&self, visitor: &mut Vs) -> ControlFlow<Vs::BreakTy> { + use crate::mir::ProjectionElem::*; + + match self { + Field(_, ty) => ty.visit_with(visitor), + Index(v) => v.visit_with(visitor), + _ => ControlFlow::CONTINUE, + } + } +} + +impl<'tcx> TypeVisitable<'tcx> for Field { + fn visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<V::BreakTy> { + ControlFlow::CONTINUE + } +} + +impl<'tcx> TypeVisitable<'tcx> for GeneratorSavedLocal { + fn visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<V::BreakTy> { + ControlFlow::CONTINUE + } +} + +impl<'tcx, R: Idx, C: Idx> TypeVisitable<'tcx> for BitMatrix<R, C> { + fn visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<V::BreakTy> { + ControlFlow::CONTINUE + } +} + +impl<'tcx> TypeVisitable<'tcx> for Constant<'tcx> { + fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + self.literal.visit_with(visitor)?; + self.user_ty.visit_with(visitor) + } +} + +impl<'tcx> TypeVisitable<'tcx> for ConstantKind<'tcx> { + fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + visitor.visit_mir_const(*self) + } +} + +impl<'tcx> TypeSuperVisitable<'tcx> for ConstantKind<'tcx> { + fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + match *self { + ConstantKind::Ty(c) => c.visit_with(visitor), + ConstantKind::Val(_, t) => t.visit_with(visitor), + } + } +} diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 9285246eb79..d1477f9e2ae 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -147,7 +147,7 @@ macro_rules! make_mir_visitor { fn visit_ascribe_user_ty( &mut self, place: & $($mutability)? Place<'tcx>, - variance: & $($mutability)? ty::Variance, + variance: $(& $mutability)? ty::Variance, user_ty: & $($mutability)? UserTypeProjection, location: Location, ) { @@ -164,7 +164,7 @@ macro_rules! make_mir_visitor { fn visit_retag( &mut self, - kind: & $($mutability)? RetagKind, + kind: $(& $mutability)? RetagKind, place: & $($mutability)? Place<'tcx>, location: Location, ) { @@ -425,7 +425,7 @@ macro_rules! make_mir_visitor { self.visit_source_info(source_info); match kind { StatementKind::Assign( - box(ref $($mutability)? place, ref $($mutability)? rvalue) + box (place, rvalue) ) => { self.visit_assign(place, rvalue, location); } @@ -465,13 +465,13 @@ macro_rules! make_mir_visitor { ); } StatementKind::Retag(kind, place) => { - self.visit_retag(kind, place, location); + self.visit_retag($(& $mutability)? *kind, place, location); } StatementKind::AscribeUserType( - box(ref $($mutability)? place, ref $($mutability)? user_ty), + box (place, user_ty), variance ) => { - self.visit_ascribe_user_ty(place, variance, user_ty, location); + self.visit_ascribe_user_ty(place, $(& $mutability)? *variance, user_ty, location); } StatementKind::Coverage(coverage) => { self.visit_coverage( @@ -480,9 +480,9 @@ macro_rules! make_mir_visitor { ) } StatementKind::CopyNonOverlapping(box crate::mir::CopyNonOverlapping{ - ref $($mutability)? src, - ref $($mutability)? dst, - ref $($mutability)? count, + src, + dst, + count, }) => { self.visit_operand(src, location); self.visit_operand(dst, location); @@ -517,8 +517,7 @@ macro_rules! make_mir_visitor { TerminatorKind::GeneratorDrop | TerminatorKind::Unreachable | TerminatorKind::FalseEdge { .. } | - TerminatorKind::FalseUnwind { .. } => { - } + TerminatorKind::FalseUnwind { .. } => {} TerminatorKind::Return => { // `return` logically moves from the return place `_0`. Note that the place @@ -830,7 +829,7 @@ macro_rules! make_mir_visitor { fn super_ascribe_user_ty(&mut self, place: & $($mutability)? Place<'tcx>, - _variance: & $($mutability)? ty::Variance, + _variance: $(& $mutability)? ty::Variance, user_ty: & $($mutability)? UserTypeProjection, location: Location) { self.visit_place( @@ -847,7 +846,7 @@ macro_rules! make_mir_visitor { } fn super_retag(&mut self, - _kind: & $($mutability)? RetagKind, + _kind: $(& $mutability)? RetagKind, place: & $($mutability)? Place<'tcx>, location: Location) { self.visit_place( diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 294f56d16b1..57c4f3f3ba3 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1573,7 +1573,7 @@ rustc_queries! { Option<&'tcx FxHashMap<ItemLocalId, Region>> { desc { "looking up a named region" } } - query is_late_bound_map(_: LocalDefId) -> Option<&'tcx FxHashSet<LocalDefId>> { + query is_late_bound_map(_: LocalDefId) -> Option<&'tcx FxIndexSet<LocalDefId>> { desc { "testing if a region is late bound" } } /// For a given item (like a struct), gets the default lifetimes to be used @@ -1825,7 +1825,8 @@ rustc_queries! { remap_env_constness } - /// Do not call this query directly: invoke `infcx.at().dropck_outlives()` instead. + /// Do not call this query directly: + /// invoke `DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx)` instead. query dropck_outlives( goal: CanonicalTyGoal<'tcx> ) -> Result< diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 120d09ee353..03c11c2863f 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -191,18 +191,8 @@ pub enum StmtKind<'tcx> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Expr<'_>, 104); -#[derive( - Clone, - Debug, - Copy, - PartialEq, - Eq, - Hash, - HashStable, - TyEncodable, - TyDecodable, - TypeFoldable -)] +#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] +#[derive(TypeFoldable, TypeVisitable)] pub struct LocalVarId(pub hir::HirId); /// A THIR expression. diff --git a/compiler/rustc_middle/src/thir/abstract_const.rs b/compiler/rustc_middle/src/thir/abstract_const.rs index e02ed414574..527dbd1cd09 100644 --- a/compiler/rustc_middle/src/thir/abstract_const.rs +++ b/compiler/rustc_middle/src/thir/abstract_const.rs @@ -42,7 +42,7 @@ impl From<ErrorGuaranteed> for NotConstEvaluatable { } } -TrivialTypeFoldableAndLiftImpls! { +TrivialTypeTraversalAndLiftImpls! { NotConstEvaluatable, } diff --git a/compiler/rustc_middle/src/traits/chalk.rs b/compiler/rustc_middle/src/traits/chalk.rs index 70abdb9ab4c..6d4af8bea62 100644 --- a/compiler/rustc_middle/src/traits/chalk.rs +++ b/compiler/rustc_middle/src/traits/chalk.rs @@ -390,7 +390,7 @@ impl<'tcx> chalk_ir::interner::HasInterner for RustInterner<'tcx> { } /// A chalk environment and goal. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct ChalkEnvironmentAndGoal<'tcx> { pub environment: &'tcx ty::List<ty::Predicate<'tcx>>, pub goal: ty::Predicate<'tcx>, diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 7f913faf860..eee44df8645 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -523,7 +523,7 @@ pub struct DerivedObligationCause<'tcx> { pub parent_code: InternedObligationCauseCode<'tcx>, } -#[derive(Clone, Debug, TypeFoldable, Lift)] +#[derive(Clone, Debug, TypeFoldable, TypeVisitable, Lift)] pub enum SelectionError<'tcx> { /// The trait is not implemented. Unimplemented, @@ -592,7 +592,8 @@ pub type SelectionResult<'tcx, T> = Result<Option<T>, SelectionError<'tcx>>; /// ### The type parameter `N` /// /// See explanation on `ImplSourceUserDefinedData`. -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] +#[derive(TypeFoldable, TypeVisitable)] pub enum ImplSource<'tcx, N> { /// ImplSource identifying a particular impl. UserDefined(ImplSourceUserDefinedData<'tcx, N>), @@ -753,14 +754,16 @@ impl<'tcx, N> ImplSource<'tcx, N> { /// is `Obligation`, as one might expect. During codegen, however, this /// is `()`, because codegen only requires a shallow resolution of an /// impl, and nested obligations are satisfied later. -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] +#[derive(TypeFoldable, TypeVisitable)] pub struct ImplSourceUserDefinedData<'tcx, N> { pub impl_def_id: DefId, pub substs: SubstsRef<'tcx>, pub nested: Vec<N>, } -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] +#[derive(TypeFoldable, TypeVisitable)] pub struct ImplSourceGeneratorData<'tcx, N> { pub generator_def_id: DefId, pub substs: SubstsRef<'tcx>, @@ -769,7 +772,8 @@ pub struct ImplSourceGeneratorData<'tcx, N> { pub nested: Vec<N>, } -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] +#[derive(TypeFoldable, TypeVisitable)] pub struct ImplSourceClosureData<'tcx, N> { pub closure_def_id: DefId, pub substs: SubstsRef<'tcx>, @@ -778,13 +782,15 @@ pub struct ImplSourceClosureData<'tcx, N> { pub nested: Vec<N>, } -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] +#[derive(TypeFoldable, TypeVisitable)] pub struct ImplSourceAutoImplData<N> { pub trait_def_id: DefId, pub nested: Vec<N>, } -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] +#[derive(TypeFoldable, TypeVisitable)] pub struct ImplSourceTraitUpcastingData<'tcx, N> { /// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`. pub upcast_trait_ref: ty::PolyTraitRef<'tcx>, @@ -798,12 +804,14 @@ pub struct ImplSourceTraitUpcastingData<'tcx, N> { pub nested: Vec<N>, } -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] +#[derive(TypeFoldable, TypeVisitable)] pub struct ImplSourceBuiltinData<N> { pub nested: Vec<N>, } -#[derive(PartialEq, Eq, Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +#[derive(PartialEq, Eq, Clone, TyEncodable, TyDecodable, HashStable, Lift)] +#[derive(TypeFoldable, TypeVisitable)] pub struct ImplSourceObjectData<'tcx, N> { /// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`. pub upcast_trait_ref: ty::PolyTraitRef<'tcx>, @@ -817,7 +825,8 @@ pub struct ImplSourceObjectData<'tcx, N> { pub nested: Vec<N>, } -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] +#[derive(TypeFoldable, TypeVisitable)] pub struct ImplSourceFnPointerData<'tcx, N> { pub fn_ty: Ty<'tcx>, pub nested: Vec<N>, @@ -830,12 +839,14 @@ pub struct ImplSourceDiscriminantKindData; #[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] pub struct ImplSourcePointeeData; -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] +#[derive(TypeFoldable, TypeVisitable)] pub struct ImplSourceConstDestructData<N> { pub nested: Vec<N>, } -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] +#[derive(TypeFoldable, TypeVisitable)] pub struct ImplSourceTraitAliasData<'tcx, N> { pub alias_def_id: DefId, pub substs: SubstsRef<'tcx>, diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index d43492c903c..937b166d484 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -24,7 +24,8 @@ pub mod type_op { use rustc_hir::def_id::DefId; use std::fmt; - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)] + #[derive(TypeFoldable, TypeVisitable)] pub struct AscribeUserType<'tcx> { pub mir_ty: Ty<'tcx>, pub def_id: DefId, @@ -37,19 +38,22 @@ pub mod type_op { } } - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)] + #[derive(TypeFoldable, TypeVisitable)] pub struct Eq<'tcx> { pub a: Ty<'tcx>, pub b: Ty<'tcx>, } - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)] + #[derive(TypeFoldable, TypeVisitable)] pub struct Subtype<'tcx> { pub sub: Ty<'tcx>, pub sup: Ty<'tcx>, } - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)] + #[derive(TypeFoldable, TypeVisitable)] pub struct ProvePredicate<'tcx> { pub predicate: Predicate<'tcx>, } @@ -60,7 +64,8 @@ pub mod type_op { } } - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)] + #[derive(TypeFoldable, TypeVisitable)] pub struct Normalize<T> { pub value: T, } @@ -107,7 +112,7 @@ impl<'tcx> From<TypeError<'tcx>> for NoSolution { } } -#[derive(Clone, Debug, Default, HashStable, TypeFoldable, Lift)] +#[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct DropckOutlivesResult<'tcx> { pub kinds: Vec<GenericArg<'tcx>>, pub overflows: Vec<Ty<'tcx>>, @@ -208,7 +213,7 @@ pub struct MethodAutoderefBadTy<'tcx> { } /// Result from the `normalize_projection_ty` query. -#[derive(Clone, Debug, HashStable, TypeFoldable, Lift)] +#[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct NormalizationResult<'tcx> { /// Result of normalization. pub normalized_ty: Ty<'tcx>, @@ -221,7 +226,7 @@ pub struct NormalizationResult<'tcx> { /// case they are called implied bounds). They are fed to the /// `OutlivesEnv` which in turn is supplied to the region checker and /// other parts of the inference system. -#[derive(Clone, Debug, TypeFoldable, Lift)] +#[derive(Clone, Debug, TypeFoldable, TypeVisitable, Lift)] pub enum OutlivesBound<'tcx> { RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), RegionSubParam(ty::Region<'tcx>, ty::ParamTy), diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index ffa70cddbd5..34703b62820 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -103,7 +103,7 @@ pub type EvaluationCache<'tcx> = Cache< /// required for associated types to work in default impls, as the bounds /// are visible both as projection bounds and as where-clauses from the /// parameter environment. -#[derive(PartialEq, Eq, Debug, Clone, TypeFoldable)] +#[derive(PartialEq, Eq, Debug, Clone, TypeFoldable, TypeVisitable)] pub enum SelectionCandidate<'tcx> { BuiltinCandidate { /// `false` if there are no *further* obligations. @@ -283,7 +283,7 @@ impl From<ErrorGuaranteed> for OverflowError { } } -TrivialTypeFoldableAndLiftImpls! { +TrivialTypeTraversalAndLiftImpls! { OverflowError, } diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index ce13825b016..3c1d0061ae1 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -1,5 +1,5 @@ use crate::ty::fast_reject::SimplifiedType; -use crate::ty::fold::TypeFoldable; +use crate::ty::visit::TypeVisitable; use crate::ty::{self, TyCtxt}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index ea706053231..8f1a1564fc8 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -129,7 +129,7 @@ impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceConstDestructData<N> { /////////////////////////////////////////////////////////////////////////// // Lift implementations -TrivialTypeFoldableAndLiftImpls! { +TrivialTypeTraversalAndLiftImpls! { super::IfExpressionCause, super::ImplSourceDiscriminantKindData, super::ImplSourcePointeeData, diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index d9332f6896a..d36cf2fe3f8 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -77,7 +77,7 @@ pub enum PointerCast { /// At some point, of course, `Box` should move out of the compiler, in which /// case this is analogous to transforming a struct. E.g., `Box<[i32; 4]>` -> /// `Box<[i32]>` is an `Adjust::Unsize` with the target `Box<[i32]>`. -#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct Adjustment<'tcx> { pub kind: Adjust<'tcx>, pub target: Ty<'tcx>, @@ -89,7 +89,7 @@ impl<'tcx> Adjustment<'tcx> { } } -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub enum Adjust<'tcx> { /// Go from ! to any type. NeverToAny, @@ -107,7 +107,8 @@ pub enum Adjust<'tcx> { /// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`. /// The target type is `U` in both cases, with the region and mutability /// being those shared by both the receiver and the returned reference. -#[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub struct OverloadedDeref<'tcx> { pub region: ty::Region<'tcx>, pub mutbl: hir::Mutability, @@ -165,7 +166,8 @@ impl From<AutoBorrowMutability> for hir::Mutability { } } -#[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub enum AutoBorrow<'tcx> { /// Converts from T to &T. Ref(ty::Region<'tcx>, AutoBorrowMutability), diff --git a/compiler/rustc_middle/src/ty/binding.rs b/compiler/rustc_middle/src/ty/binding.rs index 7ab192daf4b..3d65429f2e5 100644 --- a/compiler/rustc_middle/src/ty/binding.rs +++ b/compiler/rustc_middle/src/ty/binding.rs @@ -8,7 +8,7 @@ pub enum BindingMode { BindByValue(Mutability), } -TrivialTypeFoldableAndLiftImpls! { BindingMode, } +TrivialTypeTraversalAndLiftImpls! { BindingMode, } impl BindingMode { pub fn convert(ba: BindingAnnotation) -> BindingMode { diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index 20a6af5f6c1..c4b743dd467 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -15,6 +15,12 @@ pub enum IntTy { Char, } +impl IntTy { + pub fn is_signed(self) -> bool { + matches!(self, Self::I) + } +} + // Valid types for the result of a non-coercion cast #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum CastTy<'tcx> { diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index c88cac30a19..f5ce43f3afb 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -18,18 +18,8 @@ use self::BorrowKind::*; // This represents accessing self in the closure structure pub const CAPTURE_STRUCT_LOCAL: mir::Local = mir::Local::from_u32(1); -#[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - TyEncodable, - TyDecodable, - TypeFoldable, - HashStable -)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub struct UpvarPath { pub hir_id: hir::HirId, } @@ -37,7 +27,8 @@ pub struct UpvarPath { /// Upvars do not get their own `NodeId`. Instead, we use the pair of /// the original var ID (that is, the root variable that is referenced /// by the upvar) and the ID of the closure expression. -#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub struct UpvarId { pub var_path: UpvarPath, pub closure_expr_id: LocalDefId, @@ -51,7 +42,8 @@ impl UpvarId { /// Information describing the capture of an upvar. This is computed /// during `typeck`, specifically by `regionck`. -#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] +#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub enum UpvarCapture { /// Upvar is captured by value. This is always true when the /// closure is labeled `move`, but can also be true in other cases @@ -139,7 +131,8 @@ impl<'tcx> ClosureKind { } /// A composite describing a `Place` that is captured by a closure. -#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, TypeFoldable, HashStable)] +#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub struct CapturedPlace<'tcx> { /// The `Place` that is captured. pub place: HirPlace<'tcx>, @@ -284,7 +277,8 @@ pub fn is_ancestor_or_same_capture( /// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move) /// for a particular capture as well as identifying the part of the source code /// that triggered this capture to occur. -#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] +#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub struct CaptureInfo { /// Expr Id pointing to use that resulted in selecting the current capture kind /// @@ -362,7 +356,8 @@ pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tc curr_string } -#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, TypeFoldable, Copy, HashStable)] +#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub enum BorrowKind { /// Data must be immutable and is aliasable. ImmBorrow, diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index bc52259b151..a4e7a12bba3 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -2,7 +2,7 @@ use crate::mir::interpret::LitToConstInput; use crate::mir::ConstantKind; use crate::ty::{ self, InlineConstSubsts, InlineConstSubstsParts, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, - TyCtxt, TypeFoldable, + TyCtxt, TypeVisitable, }; use rustc_data_structures::intern::Interned; use rustc_errors::ErrorGuaranteed; diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index 51e51a63fd0..c7c2692281e 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -452,6 +452,10 @@ impl fmt::Debug for ScalarInt { impl fmt::LowerHex for ScalarInt { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.check_data(); + if f.alternate() { + // Like regular ints, alternate flag adds leading `0x`. + write!(f, "0x")?; + } // Format as hex number wide enough to fit any value of the given `size`. // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014". // Using a block `{self.data}` here to force a copy instead of using `self.data` diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 10d03065c79..cb0137d2e5e 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -4,7 +4,7 @@ use crate::mir::interpret::{AllocId, ConstValue, Scalar}; use crate::mir::Promoted; use crate::ty::subst::{InternalSubsts, SubstsRef}; use crate::ty::ParamEnv; -use crate::ty::{self, TyCtxt, TypeFoldable}; +use crate::ty::{self, TyCtxt, TypeVisitable}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; use rustc_macros::HashStable; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index c3df9a66fe7..af071b4e939 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -4,7 +4,7 @@ use crate::arena::Arena; use crate::dep_graph::{DepGraph, DepKind, DepKindStruct}; use crate::hir::place::Place as HirPlace; use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; -use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource}; +use crate::lint::{struct_lint_level, LintLevelSource}; use crate::middle::codegen_fn_attrs::CodegenFnAttrs; use crate::middle::resolve_lifetime; use crate::middle::stability; @@ -34,7 +34,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal}; use rustc_data_structures::vec_map::VecMap; -use rustc_errors::{ErrorGuaranteed, MultiSpan}; +use rustc_errors::{DecorateLint, ErrorGuaranteed, LintDiagnosticBuilder, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; @@ -390,7 +390,7 @@ impl<'a, V> LocalTableInContextMut<'a, V> { /// Here, we would store the type `T`, the span of the value `x`, the "scope-span" for /// the scope that contains `x`, the expr `T` evaluated from, and the span of `foo.await`. #[derive(TyEncodable, TyDecodable, Clone, Debug, Eq, Hash, PartialEq, HashStable)] -#[derive(TypeFoldable)] +#[derive(TypeFoldable, TypeVisitable)] pub struct GeneratorInteriorTypeCause<'tcx> { /// Type of the captured binding. pub ty: Ty<'tcx>, @@ -871,7 +871,7 @@ rustc_index::newtype_index! { pub type CanonicalUserTypeAnnotations<'tcx> = IndexVec<UserTypeAnnotationIndex, CanonicalUserTypeAnnotation<'tcx>>; -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct CanonicalUserTypeAnnotation<'tcx> { pub user_ty: CanonicalUserType<'tcx>, pub span: Span, @@ -931,7 +931,7 @@ impl<'tcx> CanonicalUserType<'tcx> { /// from constants that are named via paths, like `Foo::<A>::new` and /// so forth. #[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, Lift)] +#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub enum UserType<'tcx> { Ty(Ty<'tcx>), @@ -2787,6 +2787,18 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Emit a lint at `span` from a lint struct (some type that implements `DecorateLint`, + /// typically generated by `#[derive(LintDiagnostic)]`). + pub fn emit_spanned_lint( + self, + lint: &'static Lint, + hir_id: HirId, + span: impl Into<MultiSpan>, + decorator: impl for<'a> DecorateLint<'a, ()>, + ) { + self.struct_span_lint_hir(lint, hir_id, span, |diag| decorator.decorate_lint(diag)) + } + pub fn struct_span_lint_hir( self, lint: &'static Lint, @@ -2798,6 +2810,17 @@ impl<'tcx> TyCtxt<'tcx> { struct_lint_level(self.sess, lint, level, src, Some(span.into()), decorate); } + /// Emit a lint from a lint struct (some type that implements `DecorateLint`, typically + /// generated by `#[derive(LintDiagnostic)]`). + pub fn emit_lint( + self, + lint: &'static Lint, + id: HirId, + decorator: impl for<'a> DecorateLint<'a, ()>, + ) { + self.struct_lint_node(lint, id, |diag| decorator.decorate_lint(diag)) + } + pub fn struct_lint_node( self, lint: &'static Lint, diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index a84b3c9373b..25bc6dc6167 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -3,8 +3,8 @@ use std::ops::ControlFlow; use crate::ty::{ - fold::TypeFoldable, Const, ConstKind, DefIdTree, ExistentialPredicate, InferTy, - PolyTraitPredicate, Ty, TyCtxt, TypeSuperFoldable, TypeVisitor, + visit::TypeVisitable, Const, ConstKind, DefIdTree, ExistentialPredicate, InferTy, + PolyTraitPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, }; use rustc_data_structures::fx::FxHashMap; @@ -87,7 +87,7 @@ pub trait IsSuggestable<'tcx> { impl<'tcx, T> IsSuggestable<'tcx> for T where - T: TypeFoldable<'tcx>, + T: TypeVisitable<'tcx>, { fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool { self.visit_with(&mut IsSuggestableVisitor { tcx }).is_continue() diff --git a/compiler/rustc_middle/src/ty/erase_regions.rs b/compiler/rustc_middle/src/ty/erase_regions.rs index 6d7e60ecc31..f39fa363a16 100644 --- a/compiler/rustc_middle/src/ty/erase_regions.rs +++ b/compiler/rustc_middle/src/ty/erase_regions.rs @@ -1,5 +1,6 @@ use crate::mir; use crate::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use crate::ty::visit::TypeVisitable; use crate::ty::{self, Ty, TyCtxt, TypeFlags}; pub(super) fn provide(providers: &mut ty::query::Providers) { diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 812dd2adc2e..49a518b101d 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -13,7 +13,7 @@ use rustc_target::spec::abi; use std::borrow::Cow; use std::fmt; -#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable)] pub struct ExpectedFound<T> { pub expected: T, pub found: T, @@ -30,7 +30,7 @@ impl<T> ExpectedFound<T> { } // Data structures used in type unification -#[derive(Clone, Debug, TypeFoldable)] +#[derive(Clone, Debug, TypeFoldable, TypeVisitable)] pub enum TypeError<'tcx> { Mismatch, ConstnessMismatch(ExpectedFound<ty::BoundConstness>), @@ -795,7 +795,7 @@ fn foo(&self) -> Self::T { String::new() } if item_def_id == proj_ty_item_def_id => { Some(( - self.sess.source_map().guess_head_span(self.def_span(item.def_id)), + self.def_span(item.def_id), format!("consider calling `{}`", self.def_path_str(item.def_id)), )) } diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index a8145e6820c..8d019a3bad8 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -1,6 +1,6 @@ use crate::mir::Mutability; use crate::ty::subst::GenericArgKind; -use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; +use crate::ty::{self, Ty, TyCtxt, TypeVisitable}; use rustc_hir::def_id::DefId; use std::fmt::Debug; use std::hash::Hash; diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index a6310ae5e66..f8893ae29f5 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -1,76 +1,60 @@ -//! A generalized traversal mechanism for complex data structures that contain -//! type information. +//! A folding traversal mechanism for complex data structures that contain type +//! information. //! -//! There are two types of traversal. -//! - Folding. This is a modifying traversal. It consumes the data structure, -//! producing a (possibly) modified version of it. Both fallible and -//! infallible versions are available. The name is potentially -//! confusing, because this traversal is more like `Iterator::map` than -//! `Iterator::fold`. -//! - Visiting. This is a read-only traversal of the data structure. +//! This is a modifying traversal. It consumes the data structure, producing a +//! (possibly) modified version of it. Both fallible and infallible versions are +//! available. The name is potentially confusing, because this traversal is more +//! like `Iterator::map` than `Iterator::fold`. //! -//! These traversals have limited flexibility. Only a small number of "types of +//! This traversal has limited flexibility. Only a small number of "types of //! interest" within the complex data structures can receive custom -//! modification (when folding) or custom visitation (when visiting). These are -//! the ones containing the most important type-related information, such as -//! `Ty`, `Predicate`, `Region`, and `Const`. +//! modification. These are the ones containing the most important type-related +//! information, such as `Ty`, `Predicate`, `Region`, and `Const`. //! -//! There are three traits involved in each traversal type. -//! - `TypeFoldable`. This is implemented once for many types. This includes -//! both: +//! There are three groups of traits involved in each traversal. +//! - `TypeFoldable`. This is implemented once for many types, including: //! - Types of interest, for which the the methods delegate to the -//! folder/visitor. +//! folder. //! - All other types, including generic containers like `Vec` and `Option`. -//! It defines a "skeleton" of how they should be traversed, for both -//! folding and visiting. +//! It defines a "skeleton" of how they should be folded. //! - `TypeSuperFoldable`. This is implemented only for each type of interest, -//! and defines the traversal "skeleton" for these types. -//! - `TypeFolder`/`FallibleTypeFolder` (for infallible/fallible folding -//! traversals) or `TypeVisitor` (for visiting traversals). One of these is -//! implemented for each folder/visitor. This defines how types of interest -//! are folded/visited. +//! and defines the folding "skeleton" for these types. +//! - `TypeFolder`/`FallibleTypeFolder. One of these is implemented for each +//! folder. This defines how types of interest are folded. //! -//! This means each traversal is a mixture of (a) generic traversal operations, -//! and (b) custom fold/visit operations that are specific to the -//! folder/visitor. +//! This means each fold is a mixture of (a) generic folding operations, and (b) +//! custom fold operations that are specific to the folder. //! - The `TypeFoldable` impls handle most of the traversal, and call into -//! `TypeFolder`/`FallibleTypeFolder`/`TypeVisitor` when they encounter a -//! type of interest. -//! - A `TypeFolder`/`FallibleTypeFolder`/`TypeVisitor` may call into another -//! `TypeFoldable` impl, because some of the types of interest are recursive -//! and can contain other types of interest. -//! - A `TypeFolder`/`FallibleTypeFolder`/`TypeVisitor` may also call into -//! a `TypeSuperFoldable` impl, because each folder/visitor might provide -//! custom handling only for some types of interest, or only for some -//! variants of each type of interest, and then use default traversal for the -//! remaining cases. +//! `TypeFolder`/`FallibleTypeFolder` when they encounter a type of interest. +//! - A `TypeFolder`/`FallibleTypeFolder` may call into another `TypeFoldable` +//! impl, because some of the types of interest are recursive and can contain +//! other types of interest. +//! - A `TypeFolder`/`FallibleTypeFolder` may also call into a `TypeSuperFoldable` +//! impl, because each folder might provide custom handling only for some types +//! of interest, or only for some variants of each type of interest, and then +//! use default traversal for the remaining cases. //! //! For example, if you have `struct S(Ty, U)` where `S: TypeFoldable` and `U: -//! TypeFoldable`, and an instance `s = S(ty, u)`, it would be visited like so: +//! TypeFoldable`, and an instance `s = S(ty, u)`, it would be folded like so: //! ```text -//! s.visit_with(visitor) calls -//! - ty.visit_with(visitor) calls -//! - visitor.visit_ty(ty) may call -//! - ty.super_visit_with(visitor) -//! - u.visit_with(visitor) +//! s.fold_with(folder) calls +//! - ty.fold_with(folder) calls +//! - folder.fold_ty(ty) may call +//! - ty.super_fold_with(folder) +//! - u.fold_with(folder) //! ``` use crate::mir; -use crate::ty::{self, flags::FlagComputation, Binder, Ty, TyCtxt, TypeFlags}; -use rustc_errors::ErrorGuaranteed; +use crate::ty::{self, Binder, Ty, TyCtxt, TypeVisitable}; use rustc_hir::def_id::DefId; -use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sso::SsoHashSet; use std::collections::BTreeMap; -use std::fmt; -use std::ops::ControlFlow; -/// This trait is implemented for every type that can be folded/visited, +/// This trait is implemented for every type that can be folded, /// providing the skeleton of the traversal. /// /// To implement this conveniently, use the derive macro located in /// `rustc_macros`. -pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { +pub trait TypeFoldable<'tcx>: TypeVisitable<'tcx> { /// The entry point for folding. To fold a value `t` with a folder `f` /// call: `t.try_fold_with(f)`. /// @@ -89,115 +73,6 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { fn fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self { self.try_fold_with(folder).into_ok() } - - /// The entry point for visiting. To visit a value `t` with a visitor `v` - /// call: `t.visit_with(v)`. - /// - /// For most types, this just traverses the value, calling `visit_with` on - /// each field/element. - /// - /// For types of interest (such as `Ty`), the implementation of this method - /// that calls a visitor method specifically for that type (such as - /// `V::visit_ty`). This is where control transfers from `TypeFoldable` to - /// `TypeVisitor`. - fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy>; - - /// Returns `true` if `self` has any late-bound regions that are either - /// bound by `binder` or bound by some binder outside of `binder`. - /// If `binder` is `ty::INNERMOST`, this indicates whether - /// there are any late-bound regions that appear free. - fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { - self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break() - } - - /// Returns `true` if this `self` has any regions that escape `binder` (and - /// hence are not bound by it). - fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool { - self.has_vars_bound_at_or_above(binder.shifted_in(1)) - } - - fn has_escaping_bound_vars(&self) -> bool { - self.has_vars_bound_at_or_above(ty::INNERMOST) - } - - #[instrument(level = "trace")] - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags) - } - fn has_projections(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_PROJECTION) - } - fn has_opaque_types(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) - } - fn references_error(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_ERROR) - } - fn error_reported(&self) -> Option<ErrorGuaranteed> { - if self.references_error() { - Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()) - } else { - None - } - } - fn has_param_types_or_consts(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_CT_PARAM) - } - fn has_infer_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_INFER) - } - fn has_infer_types(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_INFER) - } - fn has_infer_types_or_consts(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_CT_INFER) - } - fn needs_infer(&self) -> bool { - self.has_type_flags(TypeFlags::NEEDS_INFER) - } - fn has_placeholders(&self) -> bool { - self.has_type_flags( - TypeFlags::HAS_RE_PLACEHOLDER - | TypeFlags::HAS_TY_PLACEHOLDER - | TypeFlags::HAS_CT_PLACEHOLDER, - ) - } - fn needs_subst(&self) -> bool { - self.has_type_flags(TypeFlags::NEEDS_SUBST) - } - /// "Free" regions in this context means that it has any region - /// that is not (a) erased or (b) late-bound. - fn has_free_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) - } - - fn has_erased_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_ERASED) - } - - /// True if there are any un-erased free regions. - fn has_erasable_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) - } - - /// Indicates whether this value references only 'global' - /// generic parameters that are the same regardless of what fn we are - /// in. This is used for caching. - fn is_global(&self) -> bool { - !self.has_type_flags(TypeFlags::HAS_FREE_LOCAL_NAMES) - } - - /// True if there are any late-bound regions - fn has_late_bound_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_LATE_BOUND) - } - - /// Indicates whether this value still has parameters/placeholders/inference variables - /// which could be replaced later, in a way that would change the results of `impl` - /// specialization. - fn still_further_specializable(&self) -> bool { - self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE) - } } // This trait is implemented for types of interest. @@ -219,14 +94,6 @@ pub trait TypeSuperFoldable<'tcx>: TypeFoldable<'tcx> { fn super_fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self { self.try_super_fold_with(folder).into_ok() } - - /// Provides a default visit for a type of interest. This should only be - /// called within `TypeVisitor` methods, when a non-custom traversal is - /// desired for the value of the type of interest passed to that method. - /// For example, in `MyVisitor::visit_ty(ty)`, it is valid to call - /// `ty.super_visit_with(self)`, but any other visiting should be done - /// with `xyz.visit_with(self)`. - fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy>; } /// This trait is implemented for every infallible folding traversal. There is @@ -376,44 +243,6 @@ where } } -/// This trait is implemented for every visiting traversal. There is a visit -/// method defined for every type of interest. Each such method has a default -/// that recurses into the type's fields in a non-custom fashion. -pub trait TypeVisitor<'tcx>: Sized { - type BreakTy = !; - - fn visit_binder<T: TypeFoldable<'tcx>>( - &mut self, - t: &Binder<'tcx, T>, - ) -> ControlFlow<Self::BreakTy> { - t.super_visit_with(self) - } - - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - t.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - r.super_visit_with(self) - } - - fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - c.super_visit_with(self) - } - - fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::BreakTy> { - uv.super_visit_with(self) - } - - fn visit_predicate(&mut self, p: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> { - p.super_visit_with(self) - } - - fn visit_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> ControlFlow<Self::BreakTy> { - c.super_visit_with(self) - } -} - /////////////////////////////////////////////////////////////////////////// // Some sample folders @@ -472,99 +301,6 @@ impl<'tcx> TyCtxt<'tcx> { { value.fold_with(&mut RegionFolder::new(self, &mut f)) } - - /// Invoke `callback` on every region appearing free in `value`. - pub fn for_each_free_region( - self, - value: &impl TypeFoldable<'tcx>, - mut callback: impl FnMut(ty::Region<'tcx>), - ) { - self.any_free_region_meets(value, |r| { - callback(r); - false - }); - } - - /// Returns `true` if `callback` returns true for every region appearing free in `value`. - pub fn all_free_regions_meet( - self, - value: &impl TypeFoldable<'tcx>, - mut callback: impl FnMut(ty::Region<'tcx>) -> bool, - ) -> bool { - !self.any_free_region_meets(value, |r| !callback(r)) - } - - /// Returns `true` if `callback` returns true for some region appearing free in `value`. - pub fn any_free_region_meets( - self, - value: &impl TypeFoldable<'tcx>, - callback: impl FnMut(ty::Region<'tcx>) -> bool, - ) -> bool { - struct RegionVisitor<F> { - /// The index of a binder *just outside* the things we have - /// traversed. If we encounter a bound region bound by this - /// binder or one outer to it, it appears free. Example: - /// - /// ```ignore (illustrative) - /// for<'a> fn(for<'b> fn(), T) - /// // ^ ^ ^ ^ - /// // | | | | here, would be shifted in 1 - /// // | | | here, would be shifted in 2 - /// // | | here, would be `INNERMOST` shifted in by 1 - /// // | here, initially, binder would be `INNERMOST` - /// ``` - /// - /// You see that, initially, *any* bound value is free, - /// because we've not traversed any binders. As we pass - /// through a binder, we shift the `outer_index` by 1 to - /// account for the new binder that encloses us. - outer_index: ty::DebruijnIndex, - callback: F, - } - - impl<'tcx, F> TypeVisitor<'tcx> for RegionVisitor<F> - where - F: FnMut(ty::Region<'tcx>) -> bool, - { - type BreakTy = (); - - fn visit_binder<T: TypeFoldable<'tcx>>( - &mut self, - t: &Binder<'tcx, T>, - ) -> ControlFlow<Self::BreakTy> { - self.outer_index.shift_in(1); - let result = t.super_visit_with(self); - self.outer_index.shift_out(1); - result - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - match *r { - ty::ReLateBound(debruijn, _) if debruijn < self.outer_index => { - ControlFlow::CONTINUE - } - _ => { - if (self.callback)(r) { - ControlFlow::BREAK - } else { - ControlFlow::CONTINUE - } - } - } - } - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - // We're only interested in types involving regions - if ty.flags().intersects(TypeFlags::HAS_FREE_REGIONS) { - ty.super_visit_with(self) - } else { - ControlFlow::CONTINUE - } - } - } - - value.visit_with(&mut RegionVisitor { outer_index: ty::INNERMOST, callback }).is_break() - } } /// Folds over the substructure of a type, visiting its component @@ -861,45 +597,6 @@ impl<'tcx> TyCtxt<'tcx> { ) } - /// Returns a set of all late-bound regions that are constrained - /// by `value`, meaning that if we instantiate those LBR with - /// variables and equate `value` with something else, those - /// variables will also be equated. - pub fn collect_constrained_late_bound_regions<T>( - self, - value: &Binder<'tcx, T>, - ) -> FxHashSet<ty::BoundRegionKind> - where - T: TypeFoldable<'tcx>, - { - self.collect_late_bound_regions(value, true) - } - - /// Returns a set of all late-bound regions that appear in `value` anywhere. - pub fn collect_referenced_late_bound_regions<T>( - self, - value: &Binder<'tcx, T>, - ) -> FxHashSet<ty::BoundRegionKind> - where - T: TypeFoldable<'tcx>, - { - self.collect_late_bound_regions(value, false) - } - - fn collect_late_bound_regions<T>( - self, - value: &Binder<'tcx, T>, - just_constraint: bool, - ) -> FxHashSet<ty::BoundRegionKind> - where - T: TypeFoldable<'tcx>, - { - let mut collector = LateBoundRegionsCollector::new(just_constraint); - let result = value.as_ref().skip_binder().visit_with(&mut collector); - assert!(result.is_continue()); // should never have stopped early - collector.regions - } - /// Replaces any late-bound regions bound in `value` with `'erased`. Useful in codegen but also /// method lookup and a few other places where precise region relationships are not required. pub fn erase_late_bound_regions<T>(self, value: Binder<'tcx, T>) -> T @@ -940,103 +637,6 @@ impl<'tcx> TyCtxt<'tcx> { } } -pub struct ValidateBoundVars<'tcx> { - bound_vars: &'tcx ty::List<ty::BoundVariableKind>, - binder_index: ty::DebruijnIndex, - // We may encounter the same variable at different levels of binding, so - // this can't just be `Ty` - visited: SsoHashSet<(ty::DebruijnIndex, Ty<'tcx>)>, -} - -impl<'tcx> ValidateBoundVars<'tcx> { - pub fn new(bound_vars: &'tcx ty::List<ty::BoundVariableKind>) -> Self { - ValidateBoundVars { - bound_vars, - binder_index: ty::INNERMOST, - visited: SsoHashSet::default(), - } - } -} - -impl<'tcx> TypeVisitor<'tcx> for ValidateBoundVars<'tcx> { - type BreakTy = (); - - fn visit_binder<T: TypeFoldable<'tcx>>( - &mut self, - t: &Binder<'tcx, T>, - ) -> ControlFlow<Self::BreakTy> { - self.binder_index.shift_in(1); - let result = t.super_visit_with(self); - self.binder_index.shift_out(1); - result - } - - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - if t.outer_exclusive_binder() < self.binder_index - || !self.visited.insert((self.binder_index, t)) - { - return ControlFlow::BREAK; - } - match *t.kind() { - ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { - if self.bound_vars.len() <= bound_ty.var.as_usize() { - bug!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars); - } - let list_var = self.bound_vars[bound_ty.var.as_usize()]; - match list_var { - ty::BoundVariableKind::Ty(kind) => { - if kind != bound_ty.kind { - bug!( - "Mismatched type kinds: {:?} doesn't var in list {:?}", - bound_ty.kind, - list_var - ); - } - } - _ => { - bug!("Mismatched bound variable kinds! Expected type, found {:?}", list_var) - } - } - } - - _ => (), - }; - - t.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - match *r { - ty::ReLateBound(index, br) if index == self.binder_index => { - if self.bound_vars.len() <= br.var.as_usize() { - bug!("Not enough bound vars: {:?} not found in {:?}", br, self.bound_vars); - } - let list_var = self.bound_vars[br.var.as_usize()]; - match list_var { - ty::BoundVariableKind::Region(kind) => { - if kind != br.kind { - bug!( - "Mismatched region kinds: {:?} doesn't match var ({:?}) in list ({:?})", - br.kind, - list_var, - self.bound_vars - ); - } - } - _ => bug!( - "Mismatched bound variable kinds! Expected region, found {:?}", - list_var - ), - } - } - - _ => (), - }; - - r.super_visit_with(self) - } -} - /////////////////////////////////////////////////////////////////////////// // Shifter // @@ -1141,301 +741,3 @@ where value.fold_with(&mut Shifter::new(tcx, amount)) } - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -struct FoundEscapingVars; - -/// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a -/// bound region or a bound type. -/// -/// So, for example, consider a type like the following, which has two binders: -/// -/// for<'a> fn(x: for<'b> fn(&'a isize, &'b isize)) -/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope -/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope -/// -/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the -/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner -/// fn type*, that type has an escaping region: `'a`. -/// -/// Note that what I'm calling an "escaping var" is often just called a "free var". However, -/// we already use the term "free var". It refers to the regions or types that we use to represent -/// bound regions or type params on a fn definition while we are type checking its body. -/// -/// To clarify, conceptually there is no particular difference between -/// an "escaping" var and a "free" var. However, there is a big -/// difference in practice. Basically, when "entering" a binding -/// level, one is generally required to do some sort of processing to -/// a bound var, such as replacing it with a fresh/placeholder -/// var, or making an entry in the environment to represent the -/// scope to which it is attached, etc. An escaping var represents -/// a bound var for which this processing has not yet been done. -struct HasEscapingVarsVisitor { - /// Anything bound by `outer_index` or "above" is escaping. - outer_index: ty::DebruijnIndex, -} - -impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor { - type BreakTy = FoundEscapingVars; - - fn visit_binder<T: TypeFoldable<'tcx>>( - &mut self, - t: &Binder<'tcx, T>, - ) -> ControlFlow<Self::BreakTy> { - self.outer_index.shift_in(1); - let result = t.super_visit_with(self); - self.outer_index.shift_out(1); - result - } - - #[inline] - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - // If the outer-exclusive-binder is *strictly greater* than - // `outer_index`, that means that `t` contains some content - // bound at `outer_index` or above (because - // `outer_exclusive_binder` is always 1 higher than the - // content in `t`). Therefore, `t` has some escaping vars. - if t.outer_exclusive_binder() > self.outer_index { - ControlFlow::Break(FoundEscapingVars) - } else { - ControlFlow::CONTINUE - } - } - - #[inline] - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - // If the region is bound by `outer_index` or anything outside - // of outer index, then it escapes the binders we have - // visited. - if r.bound_at_or_above_binder(self.outer_index) { - ControlFlow::Break(FoundEscapingVars) - } else { - ControlFlow::CONTINUE - } - } - - fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - // we don't have a `visit_infer_const` callback, so we have to - // hook in here to catch this case (annoying...), but - // otherwise we do want to remember to visit the rest of the - // const, as it has types/regions embedded in a lot of other - // places. - match ct.kind() { - ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => { - ControlFlow::Break(FoundEscapingVars) - } - _ => ct.super_visit_with(self), - } - } - - #[inline] - fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> { - if predicate.outer_exclusive_binder() > self.outer_index { - ControlFlow::Break(FoundEscapingVars) - } else { - ControlFlow::CONTINUE - } - } -} - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -struct FoundFlags; - -// FIXME: Optimize for checking for infer flags -struct HasTypeFlagsVisitor { - flags: ty::TypeFlags, -} - -impl std::fmt::Debug for HasTypeFlagsVisitor { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.flags.fmt(fmt) - } -} - -impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { - type BreakTy = FoundFlags; - - #[inline] - #[instrument(skip(self), level = "trace")] - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - let flags = t.flags(); - trace!(t.flags=?t.flags()); - if flags.intersects(self.flags) { - ControlFlow::Break(FoundFlags) - } else { - ControlFlow::CONTINUE - } - } - - #[inline] - #[instrument(skip(self), level = "trace")] - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - let flags = r.type_flags(); - trace!(r.flags=?flags); - if flags.intersects(self.flags) { - ControlFlow::Break(FoundFlags) - } else { - ControlFlow::CONTINUE - } - } - - #[inline] - #[instrument(level = "trace")] - fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - let flags = FlagComputation::for_const(c); - trace!(r.flags=?flags); - if flags.intersects(self.flags) { - ControlFlow::Break(FoundFlags) - } else { - ControlFlow::CONTINUE - } - } - - #[inline] - #[instrument(level = "trace")] - fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::BreakTy> { - let flags = FlagComputation::for_unevaluated_const(uv); - trace!(r.flags=?flags); - if flags.intersects(self.flags) { - ControlFlow::Break(FoundFlags) - } else { - ControlFlow::CONTINUE - } - } - - #[inline] - #[instrument(level = "trace")] - fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> { - debug!( - "HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}", - predicate, - predicate.flags(), - self.flags - ); - if predicate.flags().intersects(self.flags) { - ControlFlow::Break(FoundFlags) - } else { - ControlFlow::CONTINUE - } - } -} - -/// Collects all the late-bound regions at the innermost binding level -/// into a hash set. -struct LateBoundRegionsCollector { - current_index: ty::DebruijnIndex, - regions: FxHashSet<ty::BoundRegionKind>, - - /// `true` if we only want regions that are known to be - /// "constrained" when you equate this type with another type. In - /// particular, if you have e.g., `&'a u32` and `&'b u32`, equating - /// them constraints `'a == 'b`. But if you have `<&'a u32 as - /// Trait>::Foo` and `<&'b u32 as Trait>::Foo`, normalizing those - /// types may mean that `'a` and `'b` don't appear in the results, - /// so they are not considered *constrained*. - just_constrained: bool, -} - -impl LateBoundRegionsCollector { - fn new(just_constrained: bool) -> Self { - LateBoundRegionsCollector { - current_index: ty::INNERMOST, - regions: Default::default(), - just_constrained, - } - } -} - -impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { - fn visit_binder<T: TypeFoldable<'tcx>>( - &mut self, - t: &Binder<'tcx, T>, - ) -> ControlFlow<Self::BreakTy> { - self.current_index.shift_in(1); - let result = t.super_visit_with(self); - self.current_index.shift_out(1); - result - } - - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - // if we are only looking for "constrained" region, we have to - // ignore the inputs to a projection, as they may not appear - // in the normalized form - if self.just_constrained { - if let ty::Projection(..) = t.kind() { - return ControlFlow::CONTINUE; - } - } - - t.super_visit_with(self) - } - - fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - // if we are only looking for "constrained" region, we have to - // ignore the inputs of an unevaluated const, as they may not appear - // in the normalized form - if self.just_constrained { - if let ty::ConstKind::Unevaluated(..) = c.kind() { - return ControlFlow::CONTINUE; - } - } - - c.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::ReLateBound(debruijn, br) = *r { - if debruijn == self.current_index { - self.regions.insert(br.kind); - } - } - ControlFlow::CONTINUE - } -} - -/// Finds the max universe present -pub struct MaxUniverse { - max_universe: ty::UniverseIndex, -} - -impl MaxUniverse { - pub fn new() -> Self { - MaxUniverse { max_universe: ty::UniverseIndex::ROOT } - } - - pub fn max_universe(self) -> ty::UniverseIndex { - self.max_universe - } -} - -impl<'tcx> TypeVisitor<'tcx> for MaxUniverse { - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::Placeholder(placeholder) = t.kind() { - self.max_universe = ty::UniverseIndex::from_u32( - self.max_universe.as_u32().max(placeholder.universe.as_u32()), - ); - } - - t.super_visit_with(self) - } - - fn visit_const(&mut self, c: ty::consts::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::ConstKind::Placeholder(placeholder) = c.kind() { - self.max_universe = ty::UniverseIndex::from_u32( - self.max_universe.as_u32().max(placeholder.universe.as_u32()), - ); - } - - c.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::RePlaceholder(placeholder) = *r { - self.max_universe = ty::UniverseIndex::from_u32( - self.max_universe.as_u32().max(placeholder.universe.as_u32()), - ); - } - - ControlFlow::CONTINUE - } -} diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 0e10fe25c10..391abdbe84c 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -1,7 +1,9 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::ty::print::{FmtPrinter, Printer}; use crate::ty::subst::{InternalSubsts, Subst}; -use crate::ty::{self, EarlyBinder, SubstsRef, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable}; +use crate::ty::{ + self, EarlyBinder, SubstsRef, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitable, +}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::Namespace; use rustc_hir::def_id::{CrateNum, DefId}; @@ -25,7 +27,7 @@ pub struct Instance<'tcx> { } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub enum InstanceDef<'tcx> { /// A user-defined callable item. /// diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 3b05e42a53e..f87b6e4212d 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -2,7 +2,7 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::mir::{GeneratorLayout, GeneratorSavedLocal}; use crate::ty::normalize_erasing_regions::NormalizationError; use crate::ty::subst::Subst; -use crate::ty::{self, subst::SubstsRef, EarlyBinder, ReprOptions, Ty, TyCtxt, TypeFoldable}; +use crate::ty::{self, subst::SubstsRef, EarlyBinder, ReprOptions, Ty, TyCtxt, TypeVisitable}; use rustc_ast as ast; use rustc_attr as attr; use rustc_hir as hir; @@ -235,9 +235,8 @@ fn sanity_check_layout<'tcx>( if cfg!(debug_assertions) { fn check_layout_abi<'tcx>(tcx: TyCtxt<'tcx>, layout: Layout<'tcx>) { match layout.abi() { - Abi::Scalar(_scalar) => { + Abi::Scalar(scalar) => { // No padding in scalars. - /* FIXME(#96185): assert_eq!( layout.align().abi, scalar.align(&tcx).abi, @@ -247,7 +246,7 @@ fn sanity_check_layout<'tcx>( layout.size(), scalar.size(&tcx), "size mismatch between ABI and layout in {layout:#?}" - );*/ + ); } Abi::Vector { count, element } => { // No padding in vectors. Alignment can be strengthened, though. @@ -1418,9 +1417,9 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { if layout_variants.iter().all(|v| v.abi.is_uninhabited()) { abi = Abi::Uninhabited; - } else if tag.size(dl) == size || variants.iter().all(|layout| layout.is_empty()) { - // Without latter check aligned enums with custom discriminant values - // Would result in ICE see the issue #92464 for more info + } else if tag.size(dl) == size { + // Make sure we only use scalar layout when the enum is entirely its + // own tag (i.e. it has no padding nor any non-ZST variant fields). abi = Abi::Scalar(tag); } else { // Try to use a ScalarPair for all tagged enums. diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index f73eca5bf61..3a795af2121 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -9,9 +9,8 @@ //! //! ["The `ty` module: representing types"]: https://rustc-dev-guide.rust-lang.org/ty.html -pub use self::fold::{ - FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitor, -}; +pub use self::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable}; +pub use self::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; pub use self::AssocItemContainer::*; pub use self::BorrowKind::*; pub use self::IntVarValue::*; @@ -110,6 +109,7 @@ pub mod relate; pub mod subst; pub mod trait_def; pub mod util; +pub mod visit; pub mod vtable; pub mod walk; @@ -206,7 +206,7 @@ impl MainDefinition { /// The "header" of an impl is everything outside the body: a Self type, a trait /// ref (in the case of a trait impl), and a set of predicates (from the /// bounds / where-clauses). -#[derive(Clone, Debug, TypeFoldable)] +#[derive(Clone, Debug, TypeFoldable, TypeVisitable)] pub struct ImplHeader<'tcx> { pub impl_def_id: DefId, pub self_ty: Ty<'tcx>, @@ -214,24 +214,14 @@ pub struct ImplHeader<'tcx> { pub predicates: Vec<Predicate<'tcx>>, } -#[derive(Copy, Clone, Debug, TypeFoldable)] +#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)] pub enum ImplSubject<'tcx> { Trait(TraitRef<'tcx>), Inherent(Ty<'tcx>), } -#[derive( - Copy, - Clone, - PartialEq, - Eq, - Hash, - TyEncodable, - TyDecodable, - HashStable, - Debug, - TypeFoldable -)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)] +#[derive(TypeFoldable, TypeVisitable)] pub enum ImplPolarity { /// `impl Trait for Type` Positive, @@ -307,18 +297,8 @@ impl fmt::Display for BoundConstness { } } -#[derive( - Clone, - Debug, - PartialEq, - Eq, - Copy, - Hash, - TyEncodable, - TyDecodable, - HashStable, - TypeFoldable -)] +#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] pub struct ClosureSizeProfileData<'tcx> { /// Tuple containing the types of closure captures before the feature `capture_disjoint_fields` pub before_feature_tys: Ty<'tcx>, @@ -611,8 +591,14 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Predicate<'tcx> { } } +impl rustc_errors::IntoDiagnosticArg for Predicate<'_> { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + rustc_errors::DiagnosticArgValue::Str(std::borrow::Cow::Owned(self.to_string())) + } +} + #[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub enum PredicateKind<'tcx> { /// Corresponds to `where Foo: Bar<A, B, C>`. `Foo` here would be /// the `Self` type of the trait reference and `A`, `B`, and `C` @@ -784,7 +770,7 @@ impl<'tcx> Predicate<'tcx> { } #[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct TraitPredicate<'tcx> { pub trait_ref: TraitRef<'tcx>, @@ -863,7 +849,7 @@ impl<'tcx> PolyTraitPredicate<'tcx> { } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct OutlivesPredicate<A, B>(pub A, pub B); // `A: B` pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<ty::Region<'tcx>, ty::Region<'tcx>>; pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>; @@ -874,7 +860,7 @@ pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicat /// whether the `a` type is the type that we should label as "expected" when /// presenting user diagnostics. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct SubtypePredicate<'tcx> { pub a_is_expected: bool, pub a: Ty<'tcx>, @@ -884,7 +870,7 @@ pub type PolySubtypePredicate<'tcx> = ty::Binder<'tcx, SubtypePredicate<'tcx>>; /// Encodes that we have to coerce *from* the `a` type to the `b` type. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct CoercePredicate<'tcx> { pub a: Ty<'tcx>, pub b: Ty<'tcx>, @@ -892,7 +878,7 @@ pub struct CoercePredicate<'tcx> { pub type PolyCoercePredicate<'tcx> = ty::Binder<'tcx, CoercePredicate<'tcx>>; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub enum Term<'tcx> { Ty(Ty<'tcx>), Const(Const<'tcx>), @@ -940,7 +926,7 @@ impl<'tcx> Term<'tcx> { /// Form #2 eventually yields one of these `ProjectionPredicate` /// instances to normalize the LHS. #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct ProjectionPredicate<'tcx> { pub projection_ty: ProjectionTy<'tcx>, pub term: Term<'tcx>, @@ -1084,7 +1070,7 @@ impl<'tcx> Predicate<'tcx> { /// `[[], [U:Bar<T>]]`. Now if there were some particular reference /// like `Foo<isize,usize>`, then the `InstantiatedPredicates` would be `[[], /// [usize:Bar<isize>]]`. -#[derive(Clone, Debug, TypeFoldable)] +#[derive(Clone, Debug, TypeFoldable, TypeVisitable)] pub struct InstantiatedPredicates<'tcx> { pub predicates: Vec<Predicate<'tcx>>, pub spans: Vec<Span>, @@ -1100,24 +1086,14 @@ impl<'tcx> InstantiatedPredicates<'tcx> { } } -#[derive( - Copy, - Clone, - Debug, - PartialEq, - Eq, - HashStable, - TyEncodable, - TyDecodable, - TypeFoldable, - Lift -)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable, Lift)] +#[derive(TypeFoldable, TypeVisitable)] pub struct OpaqueTypeKey<'tcx> { pub def_id: DefId, pub substs: SubstsRef<'tcx>, } -#[derive(Copy, Clone, Debug, TypeFoldable, HashStable, TyEncodable, TyDecodable)] +#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)] pub struct OpaqueHiddenType<'tcx> { /// The span of this particular definition of the opaque type. So /// for example: @@ -1253,7 +1229,7 @@ pub type PlaceholderConst<'tcx> = Placeholder<BoundConst<'tcx>>; /// except that instead of a `Ty` we bundle the `DefId` of the const parameter. /// Meaning that we need to use `type_of(const_param_did)` if `const_param_did` is `Some` /// to get the type of `did`. -#[derive(Copy, Clone, Debug, TypeFoldable, Lift, TyEncodable, TyDecodable)] +#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, Lift, TyEncodable, TyDecodable)] #[derive(PartialEq, Eq, PartialOrd, Ord)] #[derive(Hash, HashStable)] pub struct WithOptConstParam<T> { @@ -1409,7 +1385,9 @@ impl<'tcx> TypeFoldable<'tcx> for ParamEnv<'tcx> { self.constness().try_fold_with(folder)?, )) } +} +impl<'tcx> TypeVisitable<'tcx> for ParamEnv<'tcx> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.caller_bounds().visit_with(visitor)?; self.reveal().visit_with(visitor)?; @@ -1536,7 +1514,7 @@ impl<'tcx> ParamEnv<'tcx> { /// `where Box<u32>: Copy`, which are clearly never /// satisfiable. We generally want to behave as if they were true, /// although the surrounding function is never reachable. - pub fn and<T: TypeFoldable<'tcx>>(self, value: T) -> ParamEnvAnd<'tcx, T> { + pub fn and<T: TypeVisitable<'tcx>>(self, value: T) -> ParamEnvAnd<'tcx, T> { match self.reveal() { Reveal::UserFacing => ParamEnvAnd { param_env: self, value }, @@ -1569,7 +1547,7 @@ impl<'tcx> PolyTraitRef<'tcx> { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)] pub struct ParamEnvAnd<'tcx, T> { pub param_env: ParamEnv<'tcx>, pub value: T, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 7f3b0fdccc6..887236c484b 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2,7 +2,7 @@ use crate::mir::interpret::{AllocRange, GlobalAlloc, Pointer, Provenance, Scalar use crate::ty::subst::{GenericArg, GenericArgKind, Subst}; use crate::ty::{ self, ConstInt, DefIdTree, ParamConst, ScalarInt, Term, Ty, TyCtxt, TypeFoldable, - TypeSuperFoldable, + TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, }; use rustc_apfloat::ieee::{Double, Single}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; @@ -2277,14 +2277,14 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { fn prepare_late_bound_region_info<T>(&mut self, value: &ty::Binder<'tcx, T>) where - T: TypeFoldable<'tcx>, + T: TypeVisitable<'tcx>, { struct LateBoundRegionNameCollector<'a, 'tcx> { used_region_names: &'a mut FxHashSet<Symbol>, type_collector: SsoHashSet<Ty<'tcx>>, } - impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_, 'tcx> { + impl<'tcx> ty::visit::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_, 'tcx> { type BreakTy = (); fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { @@ -2388,7 +2388,7 @@ macro_rules! define_print_and_forward_display { /// Wrapper type for `ty::TraitRef` which opts-in to pretty printing only /// the trait path. That is, it will print `Trait<U>` instead of /// `<T as Trait<U>>`. -#[derive(Copy, Clone, TypeFoldable, Lift)] +#[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift)] pub struct TraitRefPrintOnlyTraitPath<'tcx>(ty::TraitRef<'tcx>); impl<'tcx> fmt::Debug for TraitRefPrintOnlyTraitPath<'tcx> { @@ -2400,7 +2400,7 @@ impl<'tcx> fmt::Debug for TraitRefPrintOnlyTraitPath<'tcx> { /// Wrapper type for `ty::TraitRef` which opts-in to pretty printing only /// the trait name. That is, it will print `Trait` instead of /// `<T as Trait<U>>`. -#[derive(Copy, Clone, TypeFoldable, Lift)] +#[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift)] pub struct TraitRefPrintOnlyTraitName<'tcx>(ty::TraitRef<'tcx>); impl<'tcx> fmt::Debug for TraitRefPrintOnlyTraitName<'tcx> { @@ -2425,7 +2425,7 @@ impl<'tcx> ty::Binder<'tcx, ty::TraitRef<'tcx>> { } } -#[derive(Copy, Clone, TypeFoldable, Lift)] +#[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift)] pub struct TraitPredPrintModifiersAndPath<'tcx>(ty::TraitPredicate<'tcx>); impl<'tcx> fmt::Debug for TraitPredPrintModifiersAndPath<'tcx> { diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 51980acd38f..818affa7113 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -345,7 +345,7 @@ impl<'tcx> Relate<'tcx> for ty::ExistentialTraitRef<'tcx> { } } -#[derive(Copy, Debug, Clone, TypeFoldable)] +#[derive(Copy, Debug, Clone, TypeFoldable, TypeVisitable)] struct GeneratorWitness<'tcx>(&'tcx ty::List<Ty<'tcx>>); impl<'tcx> Relate<'tcx> for GeneratorWitness<'tcx> { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 8ba5b882fdd..1b4008019fb 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -4,8 +4,9 @@ use crate::mir::interpret; use crate::mir::ProjectionKind; -use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable, TypeVisitor}; +use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer}; +use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; use crate::ty::{self, InferConst, Lift, Term, Ty, TyCtxt}; use rustc_data_structures::functor::IdFunctor; use rustc_hir as hir; @@ -183,7 +184,7 @@ impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> { // For things that don't carry any arena-allocated data (and are // copy...), just add them to this list. -TrivialTypeFoldableAndLiftImpls! { +TrivialTypeTraversalAndLiftImpls! { (), bool, usize, @@ -452,7 +453,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::PredicateKind<'a> { impl<'a, 'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::Binder<'a, T> where - <T as Lift<'tcx>>::Lifted: TypeFoldable<'tcx>, + <T as Lift<'tcx>>::Lifted: TypeVisitable<'tcx>, { type Lifted = ty::Binder<'tcx, T::Lifted>; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> { @@ -651,7 +652,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::AdtDef<'tcx> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, _folder: &mut F) -> Result<Self, F::Error> { Ok(self) } +} +impl<'tcx> TypeVisitable<'tcx> for ty::AdtDef<'tcx> { fn visit_with<V: TypeVisitor<'tcx>>(&self, _visitor: &mut V) -> ControlFlow<V::BreakTy> { ControlFlow::CONTINUE } @@ -664,7 +667,9 @@ impl<'tcx, T: TypeFoldable<'tcx>, U: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ) -> Result<(T, U), F::Error> { Ok((self.0.try_fold_with(folder)?, self.1.try_fold_with(folder)?)) } +} +impl<'tcx, T: TypeVisitable<'tcx>, U: TypeVisitable<'tcx>> TypeVisitable<'tcx> for (T, U) { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.0.visit_with(visitor)?; self.1.visit_with(visitor) @@ -684,7 +689,11 @@ impl<'tcx, A: TypeFoldable<'tcx>, B: TypeFoldable<'tcx>, C: TypeFoldable<'tcx>> self.2.try_fold_with(folder)?, )) } +} +impl<'tcx, A: TypeVisitable<'tcx>, B: TypeVisitable<'tcx>, C: TypeVisitable<'tcx>> + TypeVisitable<'tcx> for (A, B, C) +{ fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.0.visit_with(visitor)?; self.1.visit_with(visitor)?; @@ -692,19 +701,31 @@ impl<'tcx, A: TypeFoldable<'tcx>, B: TypeFoldable<'tcx>, C: TypeFoldable<'tcx>> } } -EnumTypeFoldableImpl! { +EnumTypeTraversalImpl! { impl<'tcx, T> TypeFoldable<'tcx> for Option<T> { (Some)(a), (None), } where T: TypeFoldable<'tcx> } +EnumTypeTraversalImpl! { + impl<'tcx, T> TypeVisitable<'tcx> for Option<T> { + (Some)(a), + (None), + } where T: TypeVisitable<'tcx> +} -EnumTypeFoldableImpl! { +EnumTypeTraversalImpl! { impl<'tcx, T, E> TypeFoldable<'tcx> for Result<T, E> { (Ok)(a), (Err)(a), } where T: TypeFoldable<'tcx>, E: TypeFoldable<'tcx>, } +EnumTypeTraversalImpl! { + impl<'tcx, T, E> TypeVisitable<'tcx> for Result<T, E> { + (Ok)(a), + (Err)(a), + } where T: TypeVisitable<'tcx>, E: TypeVisitable<'tcx>, +} impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc<T> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>( @@ -744,7 +765,9 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc<T> { Ok(Rc::from_raw(Rc::into_raw(unique).cast())) } } +} +impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for Rc<T> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { (**self).visit_with(visitor) } @@ -788,7 +811,9 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Arc<T> { Ok(Arc::from_raw(Arc::into_raw(unique).cast())) } } +} +impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for Arc<T> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { (**self).visit_with(visitor) } @@ -798,7 +823,9 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<T> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { self.try_map_id(|value| value.try_fold_with(folder)) } +} +impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for Box<T> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { (**self).visit_with(visitor) } @@ -808,7 +835,9 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Vec<T> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { self.try_map_id(|t| t.try_fold_with(folder)) } +} +impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for Vec<T> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.iter().try_for_each(|t| t.visit_with(visitor)) } @@ -818,7 +847,9 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { self.try_map_id(|t| t.try_fold_with(folder)) } +} +impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for Box<[T]> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.iter().try_for_each(|t| t.visit_with(visitor)) } @@ -828,7 +859,9 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::EarlyBinder<T> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { self.try_map_bound(|ty| ty.try_fold_with(folder)) } +} +impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for ty::EarlyBinder<T> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.as_ref().0.visit_with(visitor) } @@ -838,7 +871,9 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder<'tcx, T> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { folder.try_fold_binder(self) } +} +impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for ty::Binder<'tcx, T> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { visitor.visit_binder(self) } @@ -851,7 +886,9 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeSuperFoldable<'tcx> for ty::Binder<'tcx, T ) -> Result<Self, F::Error> { self.try_map_bound(|ty| ty.try_fold_with(folder)) } +} +impl<'tcx, T: TypeVisitable<'tcx>> TypeSuperVisitable<'tcx> for ty::Binder<'tcx, T> { fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.as_ref().skip_binder().visit_with(visitor) } @@ -861,7 +898,11 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ty::Binder<'tcx, ty::Existentia fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { ty::util::fold_list(self, folder, |tcx, v| tcx.intern_poly_existential_predicates(v)) } +} +impl<'tcx> TypeVisitable<'tcx> + for &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>> +{ fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.iter().try_for_each(|p| p.visit_with(visitor)) } @@ -871,7 +912,9 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ProjectionKind> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { ty::util::fold_list(self, folder, |tcx, v| tcx.intern_projs(v)) } +} +impl<'tcx> TypeVisitable<'tcx> for &'tcx ty::List<ProjectionKind> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.iter().try_for_each(|t| t.visit_with(visitor)) } @@ -903,7 +946,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { }, }) } +} +impl<'tcx> TypeVisitable<'tcx> for ty::instance::Instance<'tcx> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { use crate::ty::InstanceDef::*; self.substs.visit_with(visitor)?; @@ -929,7 +974,9 @@ impl<'tcx> TypeFoldable<'tcx> for interpret::GlobalId<'tcx> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { Ok(Self { instance: self.instance.try_fold_with(folder)?, promoted: self.promoted }) } +} +impl<'tcx> TypeVisitable<'tcx> for interpret::GlobalId<'tcx> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.instance.visit_with(visitor) } @@ -939,7 +986,9 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { folder.try_fold_ty(self) } +} +impl<'tcx> TypeVisitable<'tcx> for Ty<'tcx> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { visitor.visit_ty(*self) } @@ -989,7 +1038,9 @@ impl<'tcx> TypeSuperFoldable<'tcx> for Ty<'tcx> { Ok(if *self.kind() == kind { self } else { folder.tcx().mk_ty(kind) }) } +} +impl<'tcx> TypeSuperVisitable<'tcx> for Ty<'tcx> { fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { match self.kind() { ty::RawPtr(ref tm) => tm.visit_with(visitor), @@ -1037,7 +1088,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Region<'tcx> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { folder.try_fold_region(self) } +} +impl<'tcx> TypeVisitable<'tcx> for ty::Region<'tcx> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { visitor.visit_region(*self) } @@ -1050,7 +1103,9 @@ impl<'tcx> TypeSuperFoldable<'tcx> for ty::Region<'tcx> { ) -> Result<Self, F::Error> { Ok(self) } +} +impl<'tcx> TypeSuperVisitable<'tcx> for ty::Region<'tcx> { fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _visitor: &mut V) -> ControlFlow<V::BreakTy> { ControlFlow::CONTINUE } @@ -1060,7 +1115,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { folder.try_fold_predicate(self) } +} +impl<'tcx> TypeVisitable<'tcx> for ty::Predicate<'tcx> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { visitor.visit_predicate(*self) } @@ -1082,7 +1139,9 @@ impl<'tcx> TypeSuperFoldable<'tcx> for ty::Predicate<'tcx> { let new = self.kind().try_fold_with(folder)?; Ok(folder.tcx().reuse_or_mk_predicate(self, new)) } +} +impl<'tcx> TypeSuperVisitable<'tcx> for ty::Predicate<'tcx> { fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.kind().visit_with(visitor) } @@ -1092,7 +1151,9 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ty::Predicate<'tcx>> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { ty::util::fold_list(self, folder, |tcx, v| tcx.intern_predicates(v)) } +} +impl<'tcx> TypeVisitable<'tcx> for &'tcx ty::List<ty::Predicate<'tcx>> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.iter().try_for_each(|p| p.visit_with(visitor)) } @@ -1102,7 +1163,9 @@ impl<'tcx, T: TypeFoldable<'tcx>, I: Idx> TypeFoldable<'tcx> for IndexVec<I, T> fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { self.try_map_id(|x| x.try_fold_with(folder)) } +} +impl<'tcx, T: TypeVisitable<'tcx>, I: Idx> TypeVisitable<'tcx> for IndexVec<I, T> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.iter().try_for_each(|t| t.visit_with(visitor)) } @@ -1112,7 +1175,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Const<'tcx> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { folder.try_fold_const(self) } +} +impl<'tcx> TypeVisitable<'tcx> for ty::Const<'tcx> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { visitor.visit_const(*self) } @@ -1131,7 +1196,9 @@ impl<'tcx> TypeSuperFoldable<'tcx> for ty::Const<'tcx> { Ok(self) } } +} +impl<'tcx> TypeSuperVisitable<'tcx> for ty::Const<'tcx> { fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.ty().visit_with(visitor)?; self.kind().visit_with(visitor) @@ -1150,7 +1217,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> { | ty::ConstKind::Error(_) => self, }) } +} +impl<'tcx> TypeVisitable<'tcx> for ty::ConstKind<'tcx> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { match *self { ty::ConstKind::Infer(ic) => ic.visit_with(visitor), @@ -1168,7 +1237,9 @@ impl<'tcx> TypeFoldable<'tcx> for InferConst<'tcx> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, _folder: &mut F) -> Result<Self, F::Error> { Ok(self) } +} +impl<'tcx> TypeVisitable<'tcx> for InferConst<'tcx> { fn visit_with<V: TypeVisitor<'tcx>>(&self, _visitor: &mut V) -> ControlFlow<V::BreakTy> { ControlFlow::CONTINUE } @@ -1178,7 +1249,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Unevaluated<'tcx> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { folder.try_fold_unevaluated(self) } +} +impl<'tcx> TypeVisitable<'tcx> for ty::Unevaluated<'tcx> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { visitor.visit_unevaluated(*self) } @@ -1195,7 +1268,9 @@ impl<'tcx> TypeSuperFoldable<'tcx> for ty::Unevaluated<'tcx> { promoted: self.promoted, }) } +} +impl<'tcx> TypeSuperVisitable<'tcx> for ty::Unevaluated<'tcx> { fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.substs.visit_with(visitor) } @@ -1205,7 +1280,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Unevaluated<'tcx, ()> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { Ok(self.expand().try_fold_with(folder)?.shrink()) } +} +impl<'tcx> TypeVisitable<'tcx> for ty::Unevaluated<'tcx, ()> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.expand().visit_with(visitor) } @@ -1215,7 +1292,9 @@ impl<'tcx> TypeFoldable<'tcx> for hir::Constness { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, _: &mut F) -> Result<Self, F::Error> { Ok(self) } +} +impl<'tcx> TypeVisitable<'tcx> for hir::Constness { fn visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<V::BreakTy> { ControlFlow::CONTINUE } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 296442e2436..815e39aab57 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -3,11 +3,11 @@ #![allow(rustc::usage_of_ty_tykind)] use crate::infer::canonical::Canonical; -use crate::ty::fold::ValidateBoundVars; use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; +use crate::ty::visit::ValidateBoundVars; use crate::ty::InferTy::*; use crate::ty::{ - self, AdtDef, DefIdTree, Discr, Term, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeSuperFoldable, + self, AdtDef, DefIdTree, Discr, Term, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use crate::ty::{List, ParamEnv}; @@ -38,7 +38,7 @@ pub type TyKind<'tcx> = IrTyKind<TyCtxt<'tcx>>; pub type RegionKind<'tcx> = IrRegionKind<TyCtxt<'tcx>>; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, Lift)] +#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct TypeAndMut<'tcx> { pub ty: Ty<'tcx>, pub mutbl: hir::Mutability, @@ -201,7 +201,7 @@ static_assert_size!(TyKind<'_>, 32); /// * `GR`: The "return type", which is the type of value returned upon /// completion of the generator. /// * `GW`: The "generator witness". -#[derive(Copy, Clone, Debug, TypeFoldable)] +#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)] pub struct ClosureSubsts<'tcx> { /// Lifetime and type parameters from the enclosing function, /// concatenated with a tuple containing the types of the upvars. @@ -328,7 +328,7 @@ impl<'tcx> ClosureSubsts<'tcx> { } /// Similar to `ClosureSubsts`; see the above documentation for more. -#[derive(Copy, Clone, Debug, TypeFoldable)] +#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)] pub struct GeneratorSubsts<'tcx> { pub substs: SubstsRef<'tcx>, } @@ -608,7 +608,7 @@ impl<'tcx> UpvarSubsts<'tcx> { /// type of the constant. The reason that `R` is represented as an extra type parameter /// is the same reason that [`ClosureSubsts`] have `CS` and `U` as type parameters: /// inline const can reference lifetimes that are internal to the creating function. -#[derive(Copy, Clone, Debug, TypeFoldable)] +#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)] pub struct InlineConstSubsts<'tcx> { /// Generic parameters from the enclosing item, /// concatenated with the inferred type of the constant. @@ -655,7 +655,7 @@ impl<'tcx> InlineConstSubsts<'tcx> { } #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub enum ExistentialPredicate<'tcx> { /// E.g., `Iterator`. Trait(ExistentialTraitRef<'tcx>), @@ -781,7 +781,7 @@ impl<'tcx> List<ty::Binder<'tcx, ExistentialPredicate<'tcx>>> { /// Trait references also appear in object types like `Foo<U>`, but in /// that case the `Self` parameter is absent from the substitutions. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct TraitRef<'tcx> { pub def_id: DefId, pub substs: SubstsRef<'tcx>, @@ -853,7 +853,7 @@ impl<'tcx> PolyTraitRef<'tcx> { /// The substitutions don't include the erased `Self`, only trait /// type and lifetime parameters (`[X, Y]` and `['a, 'b]` above). #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct ExistentialTraitRef<'tcx> { pub def_id: DefId, pub substs: SubstsRef<'tcx>, @@ -986,7 +986,7 @@ pub struct Binder<'tcx, T>(T, &'tcx List<BoundVariableKind>); impl<'tcx, T> Binder<'tcx, T> where - T: TypeFoldable<'tcx>, + T: TypeVisitable<'tcx>, { /// Wraps `value` in a binder, asserting that `value` does not /// contain any bound vars that would be bound by the @@ -1050,14 +1050,14 @@ impl<'tcx, T> Binder<'tcx, T> { Binder(value, self.1) } - pub fn map_bound_ref<F, U: TypeFoldable<'tcx>>(&self, f: F) -> Binder<'tcx, U> + pub fn map_bound_ref<F, U: TypeVisitable<'tcx>>(&self, f: F) -> Binder<'tcx, U> where F: FnOnce(&T) -> U, { self.as_ref().map_bound(f) } - pub fn map_bound<F, U: TypeFoldable<'tcx>>(self, f: F) -> Binder<'tcx, U> + pub fn map_bound<F, U: TypeVisitable<'tcx>>(self, f: F) -> Binder<'tcx, U> where F: FnOnce(T) -> U, { @@ -1069,7 +1069,7 @@ impl<'tcx, T> Binder<'tcx, T> { Binder(value, self.1) } - pub fn try_map_bound<F, U: TypeFoldable<'tcx>, E>(self, f: F) -> Result<Binder<'tcx, U>, E> + pub fn try_map_bound<F, U: TypeVisitable<'tcx>, E>(self, f: F) -> Result<Binder<'tcx, U>, E> where F: FnOnce(T) -> Result<U, E>, { @@ -1092,7 +1092,7 @@ impl<'tcx, T> Binder<'tcx, T> { /// in `bind`. This may be (debug) asserted in the future. pub fn rebind<U>(&self, value: U) -> Binder<'tcx, U> where - U: TypeFoldable<'tcx>, + U: TypeVisitable<'tcx>, { if cfg!(debug_assertions) { let mut validator = ValidateBoundVars::new(self.bound_vars()); @@ -1113,7 +1113,7 @@ impl<'tcx, T> Binder<'tcx, T> { /// would not be that useful.) pub fn no_bound_vars(self) -> Option<T> where - T: TypeFoldable<'tcx>, + T: TypeVisitable<'tcx>, { if self.0.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) } } @@ -1143,7 +1143,7 @@ impl<'tcx, T> Binder<'tcx, Option<T>> { /// Represents the projection of an associated type. In explicit UFCS /// form this would be written `<T as Trait<..>>::N`. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct ProjectionTy<'tcx> { /// The parameters of the associated item. pub substs: SubstsRef<'tcx>, @@ -1192,7 +1192,7 @@ impl<'tcx> ProjectionTy<'tcx> { } } -#[derive(Copy, Clone, Debug, TypeFoldable)] +#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)] pub struct GenSig<'tcx> { pub resume_ty: Ty<'tcx>, pub yield_ty: Ty<'tcx>, @@ -1208,7 +1208,7 @@ pub type PolyGenSig<'tcx> = Binder<'tcx, GenSig<'tcx>>; /// - `output`: is the return type. /// - `c_variadic`: indicates whether this is a C-variadic function. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct FnSig<'tcx> { pub inputs_and_output: &'tcx List<Ty<'tcx>>, pub c_variadic: bool, @@ -1385,7 +1385,7 @@ impl From<BoundVar> for BoundTy { /// A `ProjectionPredicate` for an `ExistentialTraitRef`. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct ExistentialProjection<'tcx> { pub item_def_id: DefId, pub substs: SubstsRef<'tcx>, diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index 1417c8a511c..3a524d7b0f3 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -2,10 +2,9 @@ use crate::mir; use crate::ty::codec::{TyDecoder, TyEncoder}; -use crate::ty::fold::{ - FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitor, -}; +use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable}; use crate::ty::sty::{ClosureSubsts, GeneratorSubsts, InlineConstSubsts}; +use crate::ty::visit::{TypeVisitable, TypeVisitor}; use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt}; use rustc_data_structures::intern::{Interned, WithStableHash}; @@ -205,7 +204,9 @@ impl<'tcx> TypeFoldable<'tcx> for GenericArg<'tcx> { GenericArgKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), } } +} +impl<'tcx> TypeVisitable<'tcx> for GenericArg<'tcx> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { match self.unpack() { GenericArgKind::Lifetime(lt) => lt.visit_with(visitor), @@ -449,7 +450,9 @@ impl<'tcx> TypeFoldable<'tcx> for SubstsRef<'tcx> { _ => ty::util::fold_list(self, folder, |tcx, v| tcx.intern_substs(v)), } } +} +impl<'tcx> TypeVisitable<'tcx> for SubstsRef<'tcx> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.iter().try_for_each(|t| t.visit_with(visitor)) } @@ -485,7 +488,9 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<Ty<'tcx>> { _ => ty::util::fold_list(self, folder, |tcx, v| tcx.intern_type_list(v)), } } +} +impl<'tcx> TypeVisitable<'tcx> for &'tcx ty::List<Ty<'tcx>> { fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.iter().try_for_each(|t| t.visit_with(visitor)) } @@ -722,7 +727,7 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { /// Stores the user-given substs to reach some fully qualified path /// (e.g., `<T>::Item` or `<T as Trait>::Item`). #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, Lift)] +#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct UserSubsts<'tcx> { /// The substitutions for the item as given by the user. pub substs: SubstsRef<'tcx>, @@ -749,7 +754,7 @@ pub struct UserSubsts<'tcx> { /// the self type, giving `Foo<?A>`. Finally, we unify that with /// the self type here, which contains `?A` to be `&'static u32` #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, Lift)] +#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct UserSelfTy<'tcx> { pub impl_def_id: DefId, pub self_ty: Ty<'tcx>, diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index cb34b64660d..826c16dda4a 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -1,6 +1,6 @@ use crate::traits::specialization_graph; use crate::ty::fast_reject::{self, SimplifiedType, TreatParams}; -use crate::ty::fold::TypeFoldable; +use crate::ty::visit::TypeVisitable; use crate::ty::{Ident, Ty, TyCtxt}; use hir::def_id::LOCAL_CRATE; use rustc_hir as hir; diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 38846031bad..3a876df84c2 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -6,6 +6,7 @@ use crate::ty::query::TyCtxtAt; use crate::ty::subst::{GenericArgKind, Subst, SubstsRef}; use crate::ty::{ self, DefIdTree, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitable, }; use rustc_apfloat::Float as _; use rustc_ast as ast; diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs new file mode 100644 index 00000000000..5365067209a --- /dev/null +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -0,0 +1,745 @@ +//! A visiting traversal mechanism for complex data structures that contain type +//! information. +//! +//! This is a read-only traversal of the data structure. +//! +//! This traversal has limited flexibility. Only a small number of "types of +//! interest" within the complex data structures can receive custom +//! visitation. These are the ones containing the most important type-related +//! information, such as `Ty`, `Predicate`, `Region`, and `Const`. +//! +//! There are three groups of traits involved in each traversal. +//! - `TypeVisitable`. This is implemented once for many types, including: +//! - Types of interest, for which the the methods delegate to the +//! visitor. +//! - All other types, including generic containers like `Vec` and `Option`. +//! It defines a "skeleton" of how they should be visited. +//! - `TypeSuperVisitable`. This is implemented only for each type of interest, +//! and defines the visiting "skeleton" for these types. +//! - `TypeVisitor`. This is implemented for each visitor. This defines how +//! types of interest are visited. +//! +//! This means each visit is a mixture of (a) generic visiting operations, and (b) +//! custom visit operations that are specific to the visitor. +//! - The `TypeVisitable` impls handle most of the traversal, and call into +//! `TypeVisitor` when they encounter a type of interest. +//! - A `TypeVisitor` may call into another `TypeVisitable` impl, because some of +//! the types of interest are recursive and can contain other types of interest. +//! - A `TypeVisitor` may also call into a `TypeSuperVisitable` impl, because each +//! visitor might provide custom handling only for some types of interest, or +//! only for some variants of each type of interest, and then use default +//! traversal for the remaining cases. +//! +//! For example, if you have `struct S(Ty, U)` where `S: TypeVisitable` and `U: +//! TypeVisitable`, and an instance `s = S(ty, u)`, it would be visited like so: +//! ```text +//! s.visit_with(visitor) calls +//! - ty.visit_with(visitor) calls +//! - visitor.visit_ty(ty) may call +//! - ty.super_visit_with(visitor) +//! - u.visit_with(visitor) +//! ``` +use crate::mir; +use crate::ty::{self, flags::FlagComputation, Binder, Ty, TyCtxt, TypeFlags}; +use rustc_errors::ErrorGuaranteed; + +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sso::SsoHashSet; +use std::fmt; +use std::ops::ControlFlow; + +/// This trait is implemented for every type that can be visited, +/// providing the skeleton of the traversal. +/// +/// To implement this conveniently, use the derive macro located in +/// `rustc_macros`. +pub trait TypeVisitable<'tcx>: fmt::Debug + Clone { + /// The entry point for visiting. To visit a value `t` with a visitor `v` + /// call: `t.visit_with(v)`. + /// + /// For most types, this just traverses the value, calling `visit_with` on + /// each field/element. + /// + /// For types of interest (such as `Ty`), the implementation of this method + /// that calls a visitor method specifically for that type (such as + /// `V::visit_ty`). This is where control transfers from `TypeFoldable` to + /// `TypeVisitor`. + fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy>; + + /// Returns `true` if `self` has any late-bound regions that are either + /// bound by `binder` or bound by some binder outside of `binder`. + /// If `binder` is `ty::INNERMOST`, this indicates whether + /// there are any late-bound regions that appear free. + fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { + self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break() + } + + /// Returns `true` if this `self` has any regions that escape `binder` (and + /// hence are not bound by it). + fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool { + self.has_vars_bound_at_or_above(binder.shifted_in(1)) + } + + fn has_escaping_bound_vars(&self) -> bool { + self.has_vars_bound_at_or_above(ty::INNERMOST) + } + + #[instrument(level = "trace")] + fn has_type_flags(&self, flags: TypeFlags) -> bool { + self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags) + } + fn has_projections(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_PROJECTION) + } + fn has_opaque_types(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) + } + fn references_error(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_ERROR) + } + fn error_reported(&self) -> Option<ErrorGuaranteed> { + if self.references_error() { + Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()) + } else { + None + } + } + fn has_param_types_or_consts(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_CT_PARAM) + } + fn has_infer_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RE_INFER) + } + fn has_infer_types(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_INFER) + } + fn has_infer_types_or_consts(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_CT_INFER) + } + fn needs_infer(&self) -> bool { + self.has_type_flags(TypeFlags::NEEDS_INFER) + } + fn has_placeholders(&self) -> bool { + self.has_type_flags( + TypeFlags::HAS_RE_PLACEHOLDER + | TypeFlags::HAS_TY_PLACEHOLDER + | TypeFlags::HAS_CT_PLACEHOLDER, + ) + } + fn needs_subst(&self) -> bool { + self.has_type_flags(TypeFlags::NEEDS_SUBST) + } + /// "Free" regions in this context means that it has any region + /// that is not (a) erased or (b) late-bound. + fn has_free_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) + } + + fn has_erased_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RE_ERASED) + } + + /// True if there are any un-erased free regions. + fn has_erasable_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) + } + + /// Indicates whether this value references only 'global' + /// generic parameters that are the same regardless of what fn we are + /// in. This is used for caching. + fn is_global(&self) -> bool { + !self.has_type_flags(TypeFlags::HAS_FREE_LOCAL_NAMES) + } + + /// True if there are any late-bound regions + fn has_late_bound_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RE_LATE_BOUND) + } + + /// Indicates whether this value still has parameters/placeholders/inference variables + /// which could be replaced later, in a way that would change the results of `impl` + /// specialization. + fn still_further_specializable(&self) -> bool { + self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE) + } +} + +pub trait TypeSuperVisitable<'tcx>: TypeVisitable<'tcx> { + /// Provides a default visit for a type of interest. This should only be + /// called within `TypeVisitor` methods, when a non-custom traversal is + /// desired for the value of the type of interest passed to that method. + /// For example, in `MyVisitor::visit_ty(ty)`, it is valid to call + /// `ty.super_visit_with(self)`, but any other visiting should be done + /// with `xyz.visit_with(self)`. + fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy>; +} + +/// This trait is implemented for every visiting traversal. There is a visit +/// method defined for every type of interest. Each such method has a default +/// that recurses into the type's fields in a non-custom fashion. +pub trait TypeVisitor<'tcx>: Sized { + type BreakTy = !; + + fn visit_binder<T: TypeVisitable<'tcx>>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow<Self::BreakTy> { + t.super_visit_with(self) + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + r.super_visit_with(self) + } + + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + c.super_visit_with(self) + } + + fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::BreakTy> { + uv.super_visit_with(self) + } + + fn visit_predicate(&mut self, p: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> { + p.super_visit_with(self) + } + + fn visit_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> ControlFlow<Self::BreakTy> { + c.super_visit_with(self) + } +} + +/////////////////////////////////////////////////////////////////////////// +// Region folder + +impl<'tcx> TyCtxt<'tcx> { + /// Invoke `callback` on every region appearing free in `value`. + pub fn for_each_free_region( + self, + value: &impl TypeVisitable<'tcx>, + mut callback: impl FnMut(ty::Region<'tcx>), + ) { + self.any_free_region_meets(value, |r| { + callback(r); + false + }); + } + + /// Returns `true` if `callback` returns true for every region appearing free in `value`. + pub fn all_free_regions_meet( + self, + value: &impl TypeVisitable<'tcx>, + mut callback: impl FnMut(ty::Region<'tcx>) -> bool, + ) -> bool { + !self.any_free_region_meets(value, |r| !callback(r)) + } + + /// Returns `true` if `callback` returns true for some region appearing free in `value`. + pub fn any_free_region_meets( + self, + value: &impl TypeVisitable<'tcx>, + callback: impl FnMut(ty::Region<'tcx>) -> bool, + ) -> bool { + struct RegionVisitor<F> { + /// The index of a binder *just outside* the things we have + /// traversed. If we encounter a bound region bound by this + /// binder or one outer to it, it appears free. Example: + /// + /// ```ignore (illustrative) + /// for<'a> fn(for<'b> fn(), T) + /// // ^ ^ ^ ^ + /// // | | | | here, would be shifted in 1 + /// // | | | here, would be shifted in 2 + /// // | | here, would be `INNERMOST` shifted in by 1 + /// // | here, initially, binder would be `INNERMOST` + /// ``` + /// + /// You see that, initially, *any* bound value is free, + /// because we've not traversed any binders. As we pass + /// through a binder, we shift the `outer_index` by 1 to + /// account for the new binder that encloses us. + outer_index: ty::DebruijnIndex, + callback: F, + } + + impl<'tcx, F> TypeVisitor<'tcx> for RegionVisitor<F> + where + F: FnMut(ty::Region<'tcx>) -> bool, + { + type BreakTy = (); + + fn visit_binder<T: TypeVisitable<'tcx>>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow<Self::BreakTy> { + self.outer_index.shift_in(1); + let result = t.super_visit_with(self); + self.outer_index.shift_out(1); + result + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + match *r { + ty::ReLateBound(debruijn, _) if debruijn < self.outer_index => { + ControlFlow::CONTINUE + } + _ => { + if (self.callback)(r) { + ControlFlow::BREAK + } else { + ControlFlow::CONTINUE + } + } + } + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + // We're only interested in types involving regions + if ty.flags().intersects(TypeFlags::HAS_FREE_REGIONS) { + ty.super_visit_with(self) + } else { + ControlFlow::CONTINUE + } + } + } + + value.visit_with(&mut RegionVisitor { outer_index: ty::INNERMOST, callback }).is_break() + } + + /// Returns a set of all late-bound regions that are constrained + /// by `value`, meaning that if we instantiate those LBR with + /// variables and equate `value` with something else, those + /// variables will also be equated. + pub fn collect_constrained_late_bound_regions<T>( + self, + value: &Binder<'tcx, T>, + ) -> FxHashSet<ty::BoundRegionKind> + where + T: TypeVisitable<'tcx>, + { + self.collect_late_bound_regions(value, true) + } + + /// Returns a set of all late-bound regions that appear in `value` anywhere. + pub fn collect_referenced_late_bound_regions<T>( + self, + value: &Binder<'tcx, T>, + ) -> FxHashSet<ty::BoundRegionKind> + where + T: TypeVisitable<'tcx>, + { + self.collect_late_bound_regions(value, false) + } + + fn collect_late_bound_regions<T>( + self, + value: &Binder<'tcx, T>, + just_constraint: bool, + ) -> FxHashSet<ty::BoundRegionKind> + where + T: TypeVisitable<'tcx>, + { + let mut collector = LateBoundRegionsCollector::new(just_constraint); + let result = value.as_ref().skip_binder().visit_with(&mut collector); + assert!(result.is_continue()); // should never have stopped early + collector.regions + } +} + +pub struct ValidateBoundVars<'tcx> { + bound_vars: &'tcx ty::List<ty::BoundVariableKind>, + binder_index: ty::DebruijnIndex, + // We may encounter the same variable at different levels of binding, so + // this can't just be `Ty` + visited: SsoHashSet<(ty::DebruijnIndex, Ty<'tcx>)>, +} + +impl<'tcx> ValidateBoundVars<'tcx> { + pub fn new(bound_vars: &'tcx ty::List<ty::BoundVariableKind>) -> Self { + ValidateBoundVars { + bound_vars, + binder_index: ty::INNERMOST, + visited: SsoHashSet::default(), + } + } +} + +impl<'tcx> TypeVisitor<'tcx> for ValidateBoundVars<'tcx> { + type BreakTy = (); + + fn visit_binder<T: TypeVisitable<'tcx>>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow<Self::BreakTy> { + self.binder_index.shift_in(1); + let result = t.super_visit_with(self); + self.binder_index.shift_out(1); + result + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + if t.outer_exclusive_binder() < self.binder_index + || !self.visited.insert((self.binder_index, t)) + { + return ControlFlow::BREAK; + } + match *t.kind() { + ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { + if self.bound_vars.len() <= bound_ty.var.as_usize() { + bug!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars); + } + let list_var = self.bound_vars[bound_ty.var.as_usize()]; + match list_var { + ty::BoundVariableKind::Ty(kind) => { + if kind != bound_ty.kind { + bug!( + "Mismatched type kinds: {:?} doesn't var in list {:?}", + bound_ty.kind, + list_var + ); + } + } + _ => { + bug!("Mismatched bound variable kinds! Expected type, found {:?}", list_var) + } + } + } + + _ => (), + }; + + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + match *r { + ty::ReLateBound(index, br) if index == self.binder_index => { + if self.bound_vars.len() <= br.var.as_usize() { + bug!("Not enough bound vars: {:?} not found in {:?}", br, self.bound_vars); + } + let list_var = self.bound_vars[br.var.as_usize()]; + match list_var { + ty::BoundVariableKind::Region(kind) => { + if kind != br.kind { + bug!( + "Mismatched region kinds: {:?} doesn't match var ({:?}) in list ({:?})", + br.kind, + list_var, + self.bound_vars + ); + } + } + _ => bug!( + "Mismatched bound variable kinds! Expected region, found {:?}", + list_var + ), + } + } + + _ => (), + }; + + r.super_visit_with(self) + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +struct FoundEscapingVars; + +/// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a +/// bound region or a bound type. +/// +/// So, for example, consider a type like the following, which has two binders: +/// +/// for<'a> fn(x: for<'b> fn(&'a isize, &'b isize)) +/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope +/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope +/// +/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the +/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner +/// fn type*, that type has an escaping region: `'a`. +/// +/// Note that what I'm calling an "escaping var" is often just called a "free var". However, +/// we already use the term "free var". It refers to the regions or types that we use to represent +/// bound regions or type params on a fn definition while we are type checking its body. +/// +/// To clarify, conceptually there is no particular difference between +/// an "escaping" var and a "free" var. However, there is a big +/// difference in practice. Basically, when "entering" a binding +/// level, one is generally required to do some sort of processing to +/// a bound var, such as replacing it with a fresh/placeholder +/// var, or making an entry in the environment to represent the +/// scope to which it is attached, etc. An escaping var represents +/// a bound var for which this processing has not yet been done. +struct HasEscapingVarsVisitor { + /// Anything bound by `outer_index` or "above" is escaping. + outer_index: ty::DebruijnIndex, +} + +impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor { + type BreakTy = FoundEscapingVars; + + fn visit_binder<T: TypeVisitable<'tcx>>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow<Self::BreakTy> { + self.outer_index.shift_in(1); + let result = t.super_visit_with(self); + self.outer_index.shift_out(1); + result + } + + #[inline] + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + // If the outer-exclusive-binder is *strictly greater* than + // `outer_index`, that means that `t` contains some content + // bound at `outer_index` or above (because + // `outer_exclusive_binder` is always 1 higher than the + // content in `t`). Therefore, `t` has some escaping vars. + if t.outer_exclusive_binder() > self.outer_index { + ControlFlow::Break(FoundEscapingVars) + } else { + ControlFlow::CONTINUE + } + } + + #[inline] + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + // If the region is bound by `outer_index` or anything outside + // of outer index, then it escapes the binders we have + // visited. + if r.bound_at_or_above_binder(self.outer_index) { + ControlFlow::Break(FoundEscapingVars) + } else { + ControlFlow::CONTINUE + } + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + // we don't have a `visit_infer_const` callback, so we have to + // hook in here to catch this case (annoying...), but + // otherwise we do want to remember to visit the rest of the + // const, as it has types/regions embedded in a lot of other + // places. + match ct.kind() { + ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => { + ControlFlow::Break(FoundEscapingVars) + } + _ => ct.super_visit_with(self), + } + } + + #[inline] + fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> { + if predicate.outer_exclusive_binder() > self.outer_index { + ControlFlow::Break(FoundEscapingVars) + } else { + ControlFlow::CONTINUE + } + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +struct FoundFlags; + +// FIXME: Optimize for checking for infer flags +struct HasTypeFlagsVisitor { + flags: ty::TypeFlags, +} + +impl std::fmt::Debug for HasTypeFlagsVisitor { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.flags.fmt(fmt) + } +} + +impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { + type BreakTy = FoundFlags; + + #[inline] + #[instrument(skip(self), level = "trace")] + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + let flags = t.flags(); + trace!(t.flags=?t.flags()); + if flags.intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::CONTINUE + } + } + + #[inline] + #[instrument(skip(self), level = "trace")] + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + let flags = r.type_flags(); + trace!(r.flags=?flags); + if flags.intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::CONTINUE + } + } + + #[inline] + #[instrument(level = "trace")] + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + let flags = FlagComputation::for_const(c); + trace!(r.flags=?flags); + if flags.intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::CONTINUE + } + } + + #[inline] + #[instrument(level = "trace")] + fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::BreakTy> { + let flags = FlagComputation::for_unevaluated_const(uv); + trace!(r.flags=?flags); + if flags.intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::CONTINUE + } + } + + #[inline] + #[instrument(level = "trace")] + fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> { + debug!( + "HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}", + predicate, + predicate.flags(), + self.flags + ); + if predicate.flags().intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::CONTINUE + } + } +} + +/// Collects all the late-bound regions at the innermost binding level +/// into a hash set. +struct LateBoundRegionsCollector { + current_index: ty::DebruijnIndex, + regions: FxHashSet<ty::BoundRegionKind>, + + /// `true` if we only want regions that are known to be + /// "constrained" when you equate this type with another type. In + /// particular, if you have e.g., `&'a u32` and `&'b u32`, equating + /// them constraints `'a == 'b`. But if you have `<&'a u32 as + /// Trait>::Foo` and `<&'b u32 as Trait>::Foo`, normalizing those + /// types may mean that `'a` and `'b` don't appear in the results, + /// so they are not considered *constrained*. + just_constrained: bool, +} + +impl LateBoundRegionsCollector { + fn new(just_constrained: bool) -> Self { + LateBoundRegionsCollector { + current_index: ty::INNERMOST, + regions: Default::default(), + just_constrained, + } + } +} + +impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { + fn visit_binder<T: TypeVisitable<'tcx>>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow<Self::BreakTy> { + self.current_index.shift_in(1); + let result = t.super_visit_with(self); + self.current_index.shift_out(1); + result + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + // if we are only looking for "constrained" region, we have to + // ignore the inputs to a projection, as they may not appear + // in the normalized form + if self.just_constrained { + if let ty::Projection(..) = t.kind() { + return ControlFlow::CONTINUE; + } + } + + t.super_visit_with(self) + } + + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + // if we are only looking for "constrained" region, we have to + // ignore the inputs of an unevaluated const, as they may not appear + // in the normalized form + if self.just_constrained { + if let ty::ConstKind::Unevaluated(..) = c.kind() { + return ControlFlow::CONTINUE; + } + } + + c.super_visit_with(self) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::ReLateBound(debruijn, br) = *r { + if debruijn == self.current_index { + self.regions.insert(br.kind); + } + } + ControlFlow::CONTINUE + } +} + +/// Finds the max universe present +pub struct MaxUniverse { + max_universe: ty::UniverseIndex, +} + +impl MaxUniverse { + pub fn new() -> Self { + MaxUniverse { max_universe: ty::UniverseIndex::ROOT } + } + + pub fn max_universe(self) -> ty::UniverseIndex { + self.max_universe + } +} + +impl<'tcx> TypeVisitor<'tcx> for MaxUniverse { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::Placeholder(placeholder) = t.kind() { + self.max_universe = ty::UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + + t.super_visit_with(self) + } + + fn visit_const(&mut self, c: ty::consts::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::ConstKind::Placeholder(placeholder) = c.kind() { + self.max_universe = ty::UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + + c.super_visit_with(self) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::RePlaceholder(placeholder) = *r { + self.max_universe = ty::UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + + ControlFlow::CONTINUE + } +} diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index 8e87ecd27d2..e3a383f86a7 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -1,6 +1,7 @@ //! See docs in `build/expr/mod.rs`. use rustc_index::vec::Idx; +use rustc_middle::ty::util::IntTypeExt; use crate::build::expr::as_place::PlaceBase; use crate::build::expr::category::{Category, RvalueFunc}; @@ -190,7 +191,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } ExprKind::Cast { source } => { let source = &this.thir[source]; - let from_ty = CastTy::from_ty(source.ty); + + // Casting an enum to an integer is equivalent to computing the discriminant and casting the + // discriminant. Previously every backend had to repeat the logic for this operation. Now we + // create all the steps directly in MIR with operations all backends need to support anyway. + let (source, ty) = if let ty::Adt(adt_def, ..) = source.ty.kind() && adt_def.is_enum() { + let discr_ty = adt_def.repr().discr_type().to_ty(this.tcx); + let place = unpack!(block = this.as_place(block, source)); + let discr = this.temp(discr_ty, source.span); + this.cfg.push_assign( + block, + source_info, + discr, + Rvalue::Discriminant(place), + ); + + (Operand::Move(discr), discr_ty) + } else { + let ty = source.ty; + let source = unpack!( + block = this.as_operand(block, scope, source, None, NeedsTemporary::No) + ); + (source, ty) + }; + let from_ty = CastTy::from_ty(ty); let cast_ty = CastTy::from_ty(expr.ty); let cast_kind = match (from_ty, cast_ty) { (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => { @@ -201,9 +225,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } (_, _) => CastKind::Misc, }; - let source = unpack!( - block = this.as_operand(block, scope, source, None, NeedsTemporary::No) - ); block.and(Rvalue::Cast(cast_kind, source, expr.ty)) } ExprKind::Pointer { cast, source } => { diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index e27761381f6..e6d42af9817 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -20,7 +20,7 @@ use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::*; use rustc_middle::thir::{BindingMode, Expr, ExprId, LintLevel, LocalVarId, PatKind, Thir}; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable, TypeckResults}; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_span::Symbol; diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 08b66d0abc7..4bc3d216a40 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -32,13 +32,14 @@ impl<'tcx> Cx<'tcx> { exprs.iter().map(|expr| self.mirror_expr_inner(expr)).collect() } + #[instrument(level = "trace", skip(self, hir_expr))] pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> ExprId { let temp_lifetime = self.rvalue_scopes.temporary_scope(self.region_scope_tree, hir_expr.hir_id.local_id); let expr_scope = region::Scope { id: hir_expr.hir_id.local_id, data: region::ScopeData::Node }; - debug!("Expr::make_mirror(): id={}, span={:?}", hir_expr.hir_id, hir_expr.span); + trace!(?hir_expr.hir_id, ?hir_expr.span); let mut expr = self.make_mirror_unadjusted(hir_expr); @@ -49,7 +50,7 @@ impl<'tcx> Cx<'tcx> { // Now apply adjustments, if any. for adjustment in self.typeck_results.expr_adjustments(hir_expr) { - debug!("make_mirror: expr={:?} applying adjustment={:?}", expr, adjustment); + trace!(?expr, ?adjustment); let span = expr.span; expr = self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span)); 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 845be2ab264..f22f3f61a01 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 @@ -550,7 +550,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { id, span, |lint| { - lint.build(&msg).emit(); + lint.build(msg).emit(); }, ); } diff --git a/compiler/rustc_mir_dataflow/src/framework/lattice.rs b/compiler/rustc_mir_dataflow/src/framework/lattice.rs index 9a462f6e1a4..d6b89eb8227 100644 --- a/compiler/rustc_mir_dataflow/src/framework/lattice.rs +++ b/compiler/rustc_mir_dataflow/src/framework/lattice.rs @@ -199,14 +199,16 @@ impl<T: JoinSemiLattice> MeetSemiLattice for Dual<T> { } /// Extends a type `T` with top and bottom elements to make it a partially ordered set in which no -/// value of `T` is comparable with any other. A flat set has the following [Hasse diagram]: +/// value of `T` is comparable with any other. +/// +/// A flat set has the following [Hasse diagram]: /// /// ```text -/// top -/// / / \ \ +/// top +/// / ... / / \ \ ... \ /// all possible values of `T` -/// \ \ / / -/// bottom +/// \ ... \ \ / / ... / +/// bottom /// ``` /// /// [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram diff --git a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs index 097a6186cd5..8838b14c53a 100644 --- a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs +++ b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs @@ -1,5 +1,4 @@ -use rustc_errors::DiagnosticBuilder; -use rustc_middle::lint::LintDiagnosticBuilder; +use rustc_errors::{DiagnosticBuilder, LintDiagnosticBuilder}; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 36844d5f6cf..01eda979f9e 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -19,7 +19,7 @@ use rustc_middle::mir::{ use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; use rustc_middle::ty::{ - self, ConstKind, EarlyBinder, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable, + self, ConstKind, EarlyBinder, Instance, ParamEnv, Ty, TyCtxt, TypeVisitable, }; use rustc_span::{def_id::DefId, Span}; use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; @@ -29,9 +29,8 @@ use rustc_trait_selection::traits; use crate::MirPass; use rustc_const_eval::interpret::{ self, compile_time_machine, AllocId, ConstAllocation, ConstValue, CtfeValidationMode, Frame, - ImmTy, Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemPlace, MemoryKind, OpTy, - Operand as InterpOperand, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, StackPopCleanup, - StackPopUnwind, + ImmTy, Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemoryKind, OpTy, PlaceTy, + Pointer, Scalar, ScalarMaybeUninit, StackPopCleanup, StackPopUnwind, }; /// The maximum number of bytes that we'll allocate space for a local or the return value. @@ -237,15 +236,19 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp") } - fn access_local( - _ecx: &InterpCx<'mir, 'tcx, Self>, - frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, + fn access_local<'a>( + frame: &'a Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, local: Local, - ) -> InterpResult<'tcx, InterpOperand<Self::PointerTag>> { + ) -> InterpResult<'tcx, &'a interpret::Operand<Self::PointerTag>> { let l = &frame.locals[local]; - if l.value == LocalValue::Unallocated { - throw_machine_stop_str!("tried to access an unallocated local") + if matches!( + l.value, + LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit)) + ) { + // For us "uninit" means "we don't know its value, might be initiailized or not". + // So stop here. + throw_machine_stop_str!("tried to access alocal with unknown value ") } l.access() @@ -255,8 +258,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> ecx: &'a mut InterpCx<'mir, 'tcx, Self>, frame: usize, local: Local, - ) -> InterpResult<'tcx, Result<&'a mut LocalValue<Self::PointerTag>, MemPlace<Self::PointerTag>>> - { + ) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::PointerTag>> { if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation { throw_machine_stop_str!("tried to write to a local that is marked as not propagatable") } @@ -391,7 +393,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { .layout_of(EarlyBinder(body.return_ty()).subst(tcx, substs)) .ok() // Don't bother allocating memory for large values. - .filter(|ret_layout| ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT)) + // I don't know how return types can seem to be unsized but this happens in the + // `type/type-unsatisfiable.rs` test. + .filter(|ret_layout| { + !ret_layout.is_unsized() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) + }) .unwrap_or_else(|| ecx.layout_of(tcx.types.unit).unwrap()); let ret = ecx @@ -436,8 +442,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { /// Remove `local` from the pool of `Locals`. Allows writing to them, /// but not reading from them anymore. fn remove_const(ecx: &mut InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, local: Local) { - ecx.frame_mut().locals[local] = - LocalState { value: LocalValue::Unallocated, layout: Cell::new(None) }; + ecx.frame_mut().locals[local] = LocalState { + value: LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit)), + layout: Cell::new(None), + }; } fn use_ecx<F, T>(&mut self, f: F) -> Option<T> @@ -1042,7 +1050,9 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { let frame = self.ecx.frame_mut(); frame.locals[local].value = if let StatementKind::StorageLive(_) = statement.kind { - LocalValue::Unallocated + LocalValue::Live(interpret::Operand::Immediate( + interpret::Immediate::Uninit, + )) } else { LocalValue::Dead }; diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index dc3cb282c73..280ed17f03c 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -18,7 +18,8 @@ use rustc_middle::mir::{ use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; use rustc_middle::ty::{ - self, ConstInt, ConstKind, EarlyBinder, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeFoldable, + self, ConstInt, ConstKind, EarlyBinder, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, + TypeVisitable, }; use rustc_session::lint; use rustc_span::{def_id::DefId, Span}; @@ -30,8 +31,8 @@ use crate::MirLint; use rustc_const_eval::const_eval::ConstEvalErr; use rustc_const_eval::interpret::{ self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult, - LocalState, LocalValue, MemPlace, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer, - Scalar, ScalarMaybeUninit, StackPopCleanup, StackPopUnwind, + LocalState, LocalValue, MemoryKind, OpTy, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, + StackPopCleanup, StackPopUnwind, }; /// The maximum number of bytes that we'll allocate space for a local or the return value. @@ -228,15 +229,19 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp") } - fn access_local( - _ecx: &InterpCx<'mir, 'tcx, Self>, - frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, + fn access_local<'a>( + frame: &'a Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, local: Local, - ) -> InterpResult<'tcx, InterpOperand<Self::PointerTag>> { + ) -> InterpResult<'tcx, &'a interpret::Operand<Self::PointerTag>> { let l = &frame.locals[local]; - if l.value == LocalValue::Unallocated { - throw_machine_stop_str!("tried to access an uninitialized local") + if matches!( + l.value, + LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit)) + ) { + // For us "uninit" means "we don't know its value, might be initiailized or not". + // So stop here. + throw_machine_stop_str!("tried to access a local with unknown value") } l.access() @@ -246,8 +251,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> ecx: &'a mut InterpCx<'mir, 'tcx, Self>, frame: usize, local: Local, - ) -> InterpResult<'tcx, Result<&'a mut LocalValue<Self::PointerTag>, MemPlace<Self::PointerTag>>> - { + ) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::PointerTag>> { if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation { throw_machine_stop_str!("tried to write to a local that is marked as not propagatable") } @@ -383,7 +387,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { .layout_of(EarlyBinder(body.return_ty()).subst(tcx, substs)) .ok() // Don't bother allocating memory for large values. - .filter(|ret_layout| ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT)) + // I don't know how return types can seem to be unsized but this happens in the + // `type/type-unsatisfiable.rs` test. + .filter(|ret_layout| { + !ret_layout.is_unsized() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) + }) .unwrap_or_else(|| ecx.layout_of(tcx.types.unit).unwrap()); let ret = ecx @@ -429,8 +437,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { /// Remove `local` from the pool of `Locals`. Allows writing to them, /// but not reading from them anymore. fn remove_const(ecx: &mut InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, local: Local) { - ecx.frame_mut().locals[local] = - LocalState { value: LocalValue::Unallocated, layout: Cell::new(None) }; + ecx.frame_mut().locals[local] = LocalState { + value: LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit)), + layout: Cell::new(None), + }; } fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> { @@ -914,7 +924,9 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { let frame = self.ecx.frame_mut(); frame.locals[local].value = if let StatementKind::StorageLive(_) = statement.kind { - LocalValue::Unallocated + LocalValue::Live(interpret::Operand::Immediate( + interpret::Immediate::Uninit, + )) } else { LocalValue::Dead }; diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 28f3790914b..c96497abf8f 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -66,7 +66,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS return; } - let bbs = body.basic_blocks_mut(); + let bbs = body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate().0; for Location { block, statement_index } in patch { bbs[block].statements[statement_index].make_nop(); } diff --git a/compiler/rustc_mir_transform/src/deaggregator.rs b/compiler/rustc_mir_transform/src/deaggregator.rs index 616ba819982..01f490e23bf 100644 --- a/compiler/rustc_mir_transform/src/deaggregator.rs +++ b/compiler/rustc_mir_transform/src/deaggregator.rs @@ -11,7 +11,8 @@ impl<'tcx> MirPass<'tcx> for Deaggregator { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); + let (basic_blocks, local_decls, _) = + body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate(); let local_decls = &*local_decls; for bb in basic_blocks { bb.expand_statements(|stmt| { diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index ce387cb4453..12f5764152e 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -180,16 +180,20 @@ impl<'tcx> Inliner<'tcx> { return Err("failed to normalize return type"); } if callsite.fn_sig.abi() == Abi::RustCall { - let mut args = args.into_iter(); - let _ = args.next(); // Skip `self` argument. - let arg_tuple_ty = args.next().unwrap().ty(&caller_body.local_decls, self.tcx); - assert!(args.next().is_none()); + let (arg_tuple, skipped_args) = match &args[..] { + [arg_tuple] => (arg_tuple, 0), + [_, arg_tuple] => (arg_tuple, 1), + _ => bug!("Expected `rust-call` to have 1 or 2 args"), + }; + let arg_tuple_ty = arg_tuple.ty(&caller_body.local_decls, self.tcx); let ty::Tuple(arg_tuple_tys) = arg_tuple_ty.kind() else { bug!("Closure arguments are not passed as a tuple"); }; - for (arg_ty, input) in arg_tuple_tys.iter().zip(callee_body.args_iter().skip(1)) { + for (arg_ty, input) in + arg_tuple_tys.iter().zip(callee_body.args_iter().skip(skipped_args)) + { let input_type = callee_body.local_decls[input].ty; if !equal_up_to_regions(self.tcx, self.param_env, arg_ty, input_type) { trace!(?arg_ty, ?input_type); diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index ee4a6bfba0e..a3a35f95071 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -2,7 +2,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::mir::TerminatorKind; -use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::TypeVisitable; use rustc_middle::ty::{self, subst::SubstsRef, InstanceDef, TyCtxt}; use rustc_session::Limit; diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 12fcb299ce3..9776cd0ab8b 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -28,7 +28,7 @@ use rustc_index::vec::IndexVec; use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPass, MirPhase, Promoted}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; use rustc_span::{Span, Symbol}; #[macro_use] diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index 07163cfe575..813ab4001a7 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -26,7 +26,9 @@ pub fn lower_slice_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { return; }; - let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); + // The one successor remains unchanged, so no need to invalidate + let (basic_blocks, local_decls, _) = + body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate(); for block in basic_blocks { // lower `<[_]>::len` calls diff --git a/compiler/rustc_mir_transform/src/normalize_array_len.rs b/compiler/rustc_mir_transform/src/normalize_array_len.rs index 0f45711baa3..3396a446df2 100644 --- a/compiler/rustc_mir_transform/src/normalize_array_len.rs +++ b/compiler/rustc_mir_transform/src/normalize_array_len.rs @@ -32,7 +32,9 @@ impl<'tcx> MirPass<'tcx> for NormalizeArrayLen { } pub fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); + // We don't ever touch terminators, so no need to invalidate the CFG cache + let (basic_blocks, local_decls, _) = + body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate(); // do a preliminary analysis to see if we ever have locals of type `[T;N]` or `&[T;N]` let mut interesting_locals = BitSet::new_empty(local_decls.len()); diff --git a/compiler/rustc_mir_transform/src/remove_storage_markers.rs b/compiler/rustc_mir_transform/src/remove_storage_markers.rs index c9b6e1459d3..5bb4f8bb9b3 100644 --- a/compiler/rustc_mir_transform/src/remove_storage_markers.rs +++ b/compiler/rustc_mir_transform/src/remove_storage_markers.rs @@ -17,7 +17,7 @@ impl<'tcx> MirPass<'tcx> for RemoveStorageMarkers { } trace!("Running RemoveStorageMarkers on {:?}", body.source); - for data in body.basic_blocks_mut() { + for data in body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate().0 { data.statements.retain(|statement| match statement.kind { StatementKind::StorageLive(..) | StatementKind::StorageDead(..) diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index aaee6f491cd..34941c1907d 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -18,7 +18,8 @@ impl<'tcx> MirPass<'tcx> for RemoveZsts { return; } let param_env = tcx.param_env(body.source.def_id()); - let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); + let (basic_blocks, local_decls, _) = + body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate(); for block in basic_blocks.iter_mut() { for statement in block.statements.iter_mut() { if let StatementKind::Assign(box (place, _)) | StatementKind::Deinit(box place) = diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index b9936e35b77..b3ac0f4cbea 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -194,7 +194,9 @@ use rustc_middle::mir::{self, Local, Location}; use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCast}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; -use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable, VtblEntry}; +use rustc_middle::ty::{ + self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitable, VtblEntry, +}; use rustc_middle::{middle::codegen_fn_attrs::CodegenFnAttrFlags, mir::visit::TyContext}; use rustc_session::config::EntryFnType; use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; diff --git a/compiler/rustc_monomorphize/src/partitioning/default.rs b/compiler/rustc_monomorphize/src/partitioning/default.rs index 320765e7af3..9190e5fe4eb 100644 --- a/compiler/rustc_monomorphize/src/partitioning/default.rs +++ b/compiler/rustc_monomorphize/src/partitioning/default.rs @@ -9,7 +9,7 @@ use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, Linkage, Visibility}; use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; use rustc_middle::ty::print::characteristic_def_id_of_type; -use rustc_middle::ty::{self, fold::TypeFoldable, DefIdTree, InstanceDef, TyCtxt}; +use rustc_middle::ty::{self, visit::TypeVisitable, DefIdTree, InstanceDef, TyCtxt}; use rustc_span::symbol::Symbol; use super::PartitioningCx; diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs index f29143b4480..aaa924d7fa0 100644 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ b/compiler/rustc_monomorphize/src/polymorphize.rs @@ -13,9 +13,9 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::{ self, - fold::{TypeFoldable, TypeSuperFoldable, TypeVisitor}, query::Providers, subst::SubstsRef, + visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, Const, Ty, TyCtxt, }; use rustc_span::symbol::sym; diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 0869ed65ad2..63055c56c5c 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -357,7 +357,7 @@ impl<'a> DerefMut for SnapshotParser<'a> { } impl<'a> Parser<'a> { - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub(super) fn span_err<S: Into<MultiSpan>>( &self, sp: S, @@ -366,7 +366,7 @@ impl<'a> Parser<'a> { err.span_err(sp, self.diagnostic()) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_err<S: Into<MultiSpan>>( &self, sp: S, diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 00002f6f59b..67e6402c0ae 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1353,7 +1353,16 @@ impl<'a> Parser<'a> { /// Parses `extern string_literal?`. fn parse_extern(&mut self) -> Extern { - if self.eat_keyword(kw::Extern) { Extern::from_abi(self.parse_abi()) } else { Extern::None } + if self.eat_keyword(kw::Extern) { + let mut extern_span = self.prev_token.span; + let abi = self.parse_abi(); + if let Some(abi) = abi { + extern_span = extern_span.to(abi.span); + } + Extern::from_abi(abi, extern_span) + } else { + Extern::None + } } /// Parses a string literal as an ABI spec. diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 40545b19b24..8c123c052e5 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1163,7 +1163,7 @@ impl CheckAttrVisitor<'_> { hir_id, meta.span(), |lint| { - lint.build(&"invalid `doc` attribute").emit(); + lint.build("invalid `doc` attribute").emit(); }, ); is_valid = false; diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index f9e67310452..1add91fc9c5 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -1,4 +1,4 @@ -use rustc_ast::entry::EntryPointType; +use rustc_ast::{entry::EntryPointType, Attribute}; use rustc_errors::struct_span_err; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; @@ -7,9 +7,8 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::{DefIdTree, TyCtxt}; use rustc_session::config::{CrateType, EntryFnType}; use rustc_session::parse::feature_err; -use rustc_session::Session; use rustc_span::symbol::sym; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{Span, Symbol, DUMMY_SP}; struct EntryContext<'tcx> { tcx: TyCtxt<'tcx>, @@ -72,9 +71,16 @@ fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> Entry } } -fn throw_attr_err(sess: &Session, span: Span, attr: &str) { - sess.struct_span_err(span, &format!("`{}` attribute can only be used on functions", attr)) - .emit(); +fn err_if_attr_found(ctxt: &EntryContext<'_>, attrs: &[Attribute], sym: Symbol) { + if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym) { + ctxt.tcx + .sess + .struct_span_err( + attr.span, + &format!("`{}` attribute can only be used on functions", sym.as_str()), + ) + .emit(); + } } fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { @@ -84,12 +90,8 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { EntryPointType::None => (), _ if !matches!(ctxt.tcx.def_kind(id.def_id), DefKind::Fn) => { let attrs = ctxt.tcx.hir().attrs(id.hir_id()); - if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym::start) { - throw_attr_err(&ctxt.tcx.sess, attr.span, "start"); - } - if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym::rustc_main) { - throw_attr_err(&ctxt.tcx.sess, attr.span, "rustc_main"); - } + err_if_attr_found(ctxt, attrs, sym::start); + err_if_attr_found(ctxt, attrs, sym::rustc_main); } EntryPointType::MainNamed => (), EntryPointType::OtherMain => { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index f5e323e2bc4..5560d44aa0d 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -27,7 +27,7 @@ use rustc_middle::thir::abstract_const::Node as ACNode; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{self, Const, DefIdTree, GenericParamDefKind}; -use rustc_middle::ty::{TraitRef, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor}; +use rustc_middle::ty::{TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_session::lint; use rustc_span::hygiene::Transparency; use rustc_span::symbol::{kw, Ident}; @@ -80,7 +80,7 @@ trait DefIdVisitor<'tcx> { dummy: Default::default(), } } - fn visit(&mut self, ty_fragment: impl TypeFoldable<'tcx>) -> ControlFlow<Self::BreakTy> { + fn visit(&mut self, ty_fragment: impl TypeVisitable<'tcx>) -> ControlFlow<Self::BreakTy> { ty_fragment.visit_with(&mut self.skeleton()) } fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> ControlFlow<Self::BreakTy> { @@ -467,7 +467,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { } let macro_module_def_id = self.tcx.local_parent(local_def_id); - if self.tcx.hir().opt_def_kind(macro_module_def_id) != Some(DefKind::Mod) { + if self.tcx.opt_def_kind(macro_module_def_id) != Some(DefKind::Mod) { // The macro's parent doesn't correspond to a `mod`, return early (#63164, #65252). return; } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 307ad4e844b..3ed6632ba66 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -282,11 +282,14 @@ macro_rules! define_queries { } else { Some(key.default_span(*tcx)) }; - // Use `tcx.hir().opt_def_kind()` to reduce the chance of - // accidentally triggering an infinite query loop. - let def_kind = key.key_as_def_id() - .and_then(|def_id| def_id.as_local()) - .and_then(|def_id| tcx.hir().opt_def_kind(def_id)); + let def_kind = if kind == dep_graph::DepKind::opt_def_kind { + // Try to avoid infinite recursion. + None + } else { + key.key_as_def_id() + .and_then(|def_id| def_id.as_local()) + .and_then(|def_id| tcx.opt_def_kind(def_id)) + }; let hash = || { let mut hcx = tcx.create_stable_hashing_context(); let mut hasher = StableHasher::new(); diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index e8b7cee5734..0f58206eee9 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1495,6 +1495,15 @@ impl<'a> Resolver<'a> { err.help("have you added the `#[macro_use]` on the module/import?"); return; } + if ident.name == kw::Default + && let ModuleKind::Def(DefKind::Enum, def_id, _) = parent_scope.module.kind + && let Some(span) = self.opt_span(def_id) + { + err.span_help( + self.session.source_map().guess_head_span(span), + "consider adding `#[derive(Default)]` to this enum", + ); + } for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] { if let Ok(binding) = self.early_resolve_ident_in_lexical_scope( ident, @@ -2580,8 +2589,10 @@ fn show_candidates( } else { "item".to_string() }; + let plural_descr = + if descr.ends_with("s") { format!("{}es", descr) } else { format!("{}s", descr) }; - let mut msg = format!("{}these {}s exist but are inaccessible", prefix, descr); + let mut msg = format!("{}these {} exist but are inaccessible", prefix, plural_descr); let mut has_colon = false; let mut spans = Vec::new(); diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index ed5d1165c7d..557dbecfabe 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -2539,12 +2539,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { /// "Constrained" basically means that it appears in any type but /// not amongst the inputs to a projection. In other words, `<&'a /// T as Trait<''b>>::Foo` does not constrain `'a` or `'b`. -fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxHashSet<LocalDefId>> { +fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet<LocalDefId>> { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let decl = tcx.hir().fn_decl_by_hir_id(hir_id)?; let generics = tcx.hir().get_generics(def_id)?; - let mut late_bound = FxHashSet::default(); + let mut late_bound = FxIndexSet::default(); let mut constrained_by_input = ConstrainedCollector::default(); for arg_ty in decl.inputs { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 57384877da5..28ef384f2c5 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -13,7 +13,6 @@ #![feature(let_chains)] #![feature(let_else)] #![feature(never_type)] -#![cfg_attr(bootstrap, feature(nll))] #![recursion_limit = "256"] #![allow(rustdoc::private_intra_doc_links)] #![allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 14ad1a42a7d..e7717f1367c 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1102,49 +1102,6 @@ impl CrateCheckConfig { .extend(atomic_values); // Target specific values - #[cfg(bootstrap)] - { - for target in TARGETS - .iter() - .map(|target| Target::expect_builtin(&TargetTriple::from_triple(target))) - { - self.values_valid - .entry(sym::target_os) - .or_default() - .insert(Symbol::intern(&target.options.os)); - self.values_valid - .entry(sym::target_family) - .or_default() - .extend(target.options.families.iter().map(|family| Symbol::intern(family))); - self.values_valid - .entry(sym::target_arch) - .or_default() - .insert(Symbol::intern(&target.arch)); - self.values_valid - .entry(sym::target_endian) - .or_default() - .insert(Symbol::intern(&target.options.endian.as_str())); - self.values_valid - .entry(sym::target_env) - .or_default() - .insert(Symbol::intern(&target.options.env)); - self.values_valid - .entry(sym::target_abi) - .or_default() - .insert(Symbol::intern(&target.options.abi)); - self.values_valid - .entry(sym::target_vendor) - .or_default() - .insert(Symbol::intern(&target.options.vendor)); - self.values_valid - .entry(sym::target_pointer_width) - .or_default() - .insert(sym::integer(target.pointer_width)); - } - } - - // Target specific values - #[cfg(not(bootstrap))] { const VALUES: [&Symbol; 8] = [ &sym::target_os, @@ -2767,8 +2724,8 @@ pub(crate) mod dep_tracking { use super::{ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType, InstrumentCoverage, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, OomStrategy, OptLevel, - OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, SwitchWithOptPath, - SymbolManglingVersion, TrimmedDefPaths, + OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, SplitDwarfKind, + SwitchWithOptPath, SymbolManglingVersion, TrimmedDefPaths, }; use crate::lint; use crate::options::WasiExecModel; @@ -2855,6 +2812,7 @@ pub(crate) mod dep_tracking { Edition, LinkerPluginLto, SplitDebuginfo, + SplitDwarfKind, StackProtector, SwitchWithOptPath, SymbolManglingVersion, diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 35b55981e37..7353c1ca0e2 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -6,7 +6,7 @@ #![feature(once_cell)] #![feature(option_get_or_insert_default)] #![feature(rustc_attrs)] -#![cfg_attr(not(bootstrap), feature(map_many_mut))] +#![feature(map_many_mut)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 441e1f9f6a2..be70ea5d5e4 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1496,7 +1496,7 @@ options! { "control if mem::uninitialized and mem::zeroed panic on more UB"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), - split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [UNTRACKED], + split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [TRACKED], "split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform) (default: `split`) @@ -1504,7 +1504,7 @@ options! { file which is ignored by the linker `single`: sections which do not require relocation are written into object file but ignored by the linker"), - split_dwarf_inlining: bool = (true, parse_bool, [UNTRACKED], + split_dwarf_inlining: bool = (true, parse_bool, [TRACKED], "provide minimal debug info in the object/executable to facilitate online \ symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"), symbol_mangling_version: Option<SymbolManglingVersion> = (None, diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index a5ccae047fc..f31d52147b4 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -311,7 +311,7 @@ impl ParseSess { self.create_warning(warning).emit() } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_err( &self, msg: impl Into<DiagnosticMessage>, @@ -319,7 +319,7 @@ impl ParseSess { self.span_diagnostic.struct_err(msg) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { self.span_diagnostic.struct_warn(msg) } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index b5058fd699a..2a5ddd4e9e4 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -280,7 +280,7 @@ impl Session { self.crate_types.set(crate_types).expect("`crate_types` was initialized twice") } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_warn<S: Into<MultiSpan>>( &self, sp: S, @@ -288,7 +288,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_warn(sp, msg) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_warn_with_expectation<S: Into<MultiSpan>>( &self, sp: S, @@ -297,7 +297,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_warn_with_expectation(sp, msg, id) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_warn_with_code<S: Into<MultiSpan>>( &self, sp: S, @@ -306,11 +306,11 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_warn_with_code(sp, msg, code) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_warn(msg) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_warn_with_expectation( &self, msg: impl Into<DiagnosticMessage>, @@ -318,7 +318,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_warn_with_expectation(msg, id) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_allow<S: Into<MultiSpan>>( &self, sp: S, @@ -326,11 +326,11 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_allow(sp, msg) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_allow(msg) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_expect( &self, msg: impl Into<DiagnosticMessage>, @@ -338,7 +338,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_expect(msg, id) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_err<S: Into<MultiSpan>>( &self, sp: S, @@ -346,7 +346,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.diagnostic().struct_span_err(sp, msg) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_err_with_code<S: Into<MultiSpan>>( &self, sp: S, @@ -356,14 +356,14 @@ impl Session { self.diagnostic().struct_span_err_with_code(sp, msg, code) } // FIXME: This method should be removed (every error should have an associated error code). - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_err( &self, msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.parse_sess.struct_err(msg) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_err_with_code( &self, msg: impl Into<DiagnosticMessage>, @@ -371,7 +371,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.diagnostic().struct_err_with_code(msg, code) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_warn_with_code( &self, msg: impl Into<DiagnosticMessage>, @@ -379,7 +379,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_warn_with_code(msg, code) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_fatal<S: Into<MultiSpan>>( &self, sp: S, @@ -387,7 +387,7 @@ impl Session { ) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_span_fatal(sp, msg) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>( &self, sp: S, @@ -396,16 +396,16 @@ impl Session { ) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_span_fatal_with_code(sp, msg, code) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_fatal(msg) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) -> ! { self.diagnostic().span_fatal(sp, msg) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_fatal_with_code<S: Into<MultiSpan>>( &self, sp: S, @@ -414,11 +414,11 @@ impl Session { ) -> ! { self.diagnostic().span_fatal_with_code(sp, msg, code) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> ! { self.diagnostic().fatal(msg).raise() } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_err_or_warn<S: Into<MultiSpan>>( &self, is_warning: bool, @@ -431,7 +431,7 @@ impl Session { self.span_err(sp, msg); } } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_err<S: Into<MultiSpan>>( &self, sp: S, @@ -439,7 +439,7 @@ impl Session { ) -> ErrorGuaranteed { self.diagnostic().span_err(sp, msg) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn span_err_with_code<S: Into<MultiSpan>>( &self, sp: S, @@ -448,7 +448,7 @@ impl Session { ) { self.diagnostic().span_err_with_code(sp, msg, code) } - #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] + #[rustc_lint_diagnostics] pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { self.diagnostic().err(msg) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4e28d2b6001..65a2a18e02f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -173,6 +173,7 @@ symbols! { DebugTuple, Decodable, Decoder, + DecorateLint, Default, Deref, DiagnosticMessage, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index f67b87a6a52..470438471cb 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -3,7 +3,7 @@ use rustc_hir::def_id::CrateNum; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; use rustc_middle::ty::print::{PrettyPrinter, Print, Printer}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeVisitable}; use rustc_middle::util::common::record_time; use tracing::debug; diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 1036c5d941b..13229a3995c 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::print::{Print, Printer}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{ - self, EarlyBinder, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeFoldable, UintTy, + self, EarlyBinder, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeVisitable, UintTy, }; use rustc_span::symbol::kw; use rustc_target::abi::call::FnAbi; @@ -240,7 +240,7 @@ impl<'tcx> SymbolMangler<'tcx> { print_value: impl FnOnce(&'a mut Self, &T) -> Result<&'a mut Self, !>, ) -> Result<&'a mut Self, !> where - T: TypeFoldable<'tcx>, + T: TypeVisitable<'tcx>, { let regions = if value.has_late_bound_regions() { self.tcx.collect_referenced_late_bound_regions(value) diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_trait_selection/src/autoderef.rs index 5b88cff03d7..8b7e8984a8a 100644 --- a/compiler/rustc_trait_selection/src/autoderef.rs +++ b/compiler/rustc_trait_selection/src/autoderef.rs @@ -4,7 +4,7 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_infer::infer::InferCtxt; use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt}; -use rustc_middle::ty::{ToPredicate, TypeFoldable}; +use rustc_middle::ty::{ToPredicate, TypeVisitable}; use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::Span; diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index f135f0c1b13..9d30374f8b8 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -9,7 +9,7 @@ use rustc_middle::infer::canonical::{Canonical, CanonicalizedQueryResponse, Quer use rustc_middle::traits::query::Fallible; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::ToPredicate; -use rustc_middle::ty::{self, Ty, TypeFoldable}; +use rustc_middle::ty::{self, Ty, TypeFoldable, TypeVisitable}; use rustc_span::{Span, DUMMY_SP}; use std::fmt::Debug; diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index 9dd8588cece..d290f7b074c 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -9,6 +9,7 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt as _}; use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts}; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, ToPredicate, Ty, TyCtxt}; use rustc_span::Span; diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 90ff07cba02..65ff9ceb67e 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -8,6 +8,7 @@ use crate::infer::InferCtxt; use crate::traits::project::ProjectAndUnifyResult; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{Region, RegionVid, Term}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -212,15 +213,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, errors); } - let body_id_map: FxHashMap<_, _> = infcx - .inner - .borrow() - .region_obligations() - .iter() - .map(|&(id, _)| (id, vec![])) - .collect(); - - infcx.process_registered_region_obligations(&body_id_map, full_env); + infcx.process_registered_region_obligations(&Default::default(), full_env); let region_data = infcx .inner diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs index 592b0ab477a..9ef7ac9a8e0 100644 --- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs @@ -8,7 +8,7 @@ use crate::traits::{ PredicateObligation, SelectionError, TraitEngine, }; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; -use rustc_middle::ty::{self, Ty, TypeFoldable}; +use rustc_middle::ty::{self, Ty, TypeVisitable}; pub struct FulfillmentContext<'tcx> { obligations: FxIndexSet<PredicateObligation<'tcx>>, diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 2b26b916d32..f933f1c3c94 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -16,13 +16,12 @@ use crate::traits::{ //use rustc_data_structures::fx::FxHashMap; use rustc_errors::Diagnostic; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::CRATE_HIR_ID; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{util, TraitEngine}; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; -use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; @@ -317,14 +316,13 @@ fn negative_impl<'cx, 'tcx>( let (subject2, obligations) = impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs); - !equate(&infcx, impl_env, impl1_def_id, subject1, subject2, obligations) + !equate(&infcx, impl_env, subject1, subject2, obligations) }) } fn equate<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, impl_env: ty::ParamEnv<'tcx>, - impl1_def_id: DefId, subject1: ImplSubject<'tcx>, subject2: ImplSubject<'tcx>, obligations: impl Iterator<Item = PredicateObligation<'tcx>>, @@ -341,7 +339,7 @@ fn equate<'cx, 'tcx>( let opt_failing_obligation = obligations .into_iter() .chain(more_obligations) - .find(|o| negative_impl_exists(selcx, impl_env, impl1_def_id, o)); + .find(|o| negative_impl_exists(selcx, impl_env, o)); if let Some(failing_obligation) = opt_failing_obligation { debug!("overlap: obligation unsatisfiable {:?}", failing_obligation); @@ -356,18 +354,17 @@ fn equate<'cx, 'tcx>( fn negative_impl_exists<'cx, 'tcx>( selcx: &SelectionContext<'cx, 'tcx>, param_env: ty::ParamEnv<'tcx>, - region_context: DefId, o: &PredicateObligation<'tcx>, ) -> bool { let infcx = &selcx.infcx().fork(); - if resolve_negative_obligation(infcx, param_env, region_context, o) { + if resolve_negative_obligation(infcx, param_env, o) { return true; } // Try to prove a negative obligation exists for super predicates for o in util::elaborate_predicates(infcx.tcx, iter::once(o.predicate)) { - if resolve_negative_obligation(infcx, param_env, region_context, &o) { + if resolve_negative_obligation(infcx, param_env, &o) { return true; } } @@ -379,7 +376,6 @@ fn negative_impl_exists<'cx, 'tcx>( fn resolve_negative_obligation<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, param_env: ty::ParamEnv<'tcx>, - region_context: DefId, o: &PredicateObligation<'tcx>, ) -> bool { let tcx = infcx.tcx; @@ -397,19 +393,11 @@ fn resolve_negative_obligation<'cx, 'tcx>( return false; } - let mut outlives_env = OutlivesEnvironment::new(param_env); - // FIXME -- add "assumed to be well formed" types into the `outlives_env` - - // "Save" the accumulated implied bounds into the outlives environment - // (due to the FIXME above, there aren't any, but this step is still needed). - // The "body id" is given as `CRATE_HIR_ID`, which is the same body-id used - // by the "dummy" causes elsewhere (body-id is only relevant when checking - // function bodies with closures). - outlives_env.save_implied_bounds(CRATE_HIR_ID); - - infcx.process_registered_region_obligations(outlives_env.region_bound_pairs_map(), param_env); + // FIXME -- also add "assumed to be well formed" types into the `outlives_env` + let outlives_env = OutlivesEnvironment::new(param_env); + infcx.process_registered_region_obligations(outlives_env.region_bound_pairs(), param_env); - let errors = infcx.resolve_regions(region_context, &outlives_env); + let errors = infcx.resolve_regions(&outlives_env); if !errors.is_empty() { return false; diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 424eff2f621..f9b4a1583cc 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -17,7 +17,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, LitToConstError, LitToConstInpu use rustc_middle::thir; use rustc_middle::thir::abstract_const::{self, Node, NodeId, NotConstEvaluatable}; use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, DelaySpanBugEmitted, EarlyBinder, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, DelaySpanBugEmitted, EarlyBinder, TyCtxt, TypeVisitable}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::Span; diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index fa56219b409..8efefd476ab 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -30,6 +30,7 @@ use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{ self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, + TypeVisitable, }; use rustc_span::symbol::{kw, sym}; use rustc_span::{ExpnKind, Span, DUMMY_SP}; @@ -1111,18 +1112,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }) .collect::<Option<Vec<ArgKind>>>()?, ), - Node::Item(&hir::Item { span, kind: hir::ItemKind::Fn(ref sig, ..), .. }) - | Node::ImplItem(&hir::ImplItem { - span, - kind: hir::ImplItemKind::Fn(ref sig, _), - .. - }) + Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) | Node::TraitItem(&hir::TraitItem { - span, - kind: hir::TraitItemKind::Fn(ref sig, _), - .. + kind: hir::TraitItemKind::Fn(ref sig, _), .. }) => ( - sm.guess_head_span(span), + sig.span, sig.decl .inputs .iter() @@ -1137,7 +1132,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ), Node::Ctor(ref variant_data) => { let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); - let span = sm.guess_head_span(span); (span, vec![ArgKind::empty(); variant_data.fields().len()]) } _ => panic!("non-FnLike node found: {:?}", node), @@ -1980,7 +1974,6 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { body_id, span, trait_ref.self_ty().skip_binder().into(), - vec![], ErrorCode::E0282, false, ) @@ -2005,19 +1998,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { let subst = data.trait_ref.substs.iter().find(|s| s.has_infer_types_or_consts()); let mut err = if let Some(subst) = subst { - let impl_candidates = self - .find_similar_impl_candidates(trait_ref) - .into_iter() - .map(|candidate| candidate.trait_ref) - .collect(); - self.emit_inference_failure_err( - body_id, - span, - subst, - impl_candidates, - ErrorCode::E0283, - true, - ) + self.emit_inference_failure_err(body_id, span, subst, ErrorCode::E0283, true) } else { struct_span_err!( self.tcx.sess, @@ -2117,7 +2098,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { return; } - self.emit_inference_failure_err(body_id, span, arg, vec![], ErrorCode::E0282, false) + self.emit_inference_failure_err(body_id, span, arg, ErrorCode::E0282, false) } ty::PredicateKind::Subtype(data) => { @@ -2131,14 +2112,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { let SubtypePredicate { a_is_expected: _, a, b } = data; // both must be type variables, or the other would've been instantiated assert!(a.is_ty_var() && b.is_ty_var()); - self.emit_inference_failure_err( - body_id, - span, - a.into(), - vec![], - ErrorCode::E0282, - true, - ) + self.emit_inference_failure_err(body_id, span, a.into(), ErrorCode::E0282, true) } ty::PredicateKind::Projection(data) => { if predicate.references_error() || self.is_tainted_by_errors() { @@ -2155,7 +2129,6 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { body_id, span, subst, - vec![], ErrorCode::E0284, true, ); @@ -2205,7 +2178,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { let mut post = vec![]; for def_id in impls { match self.tcx.span_of_impl(*def_id) { - Ok(span) => spans.push(self.tcx.sess.source_map().guess_head_span(span)), + Ok(span) => spans.push(span), Err(name) => { crates.push(name); if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) { @@ -2552,8 +2525,7 @@ pub fn recursive_type_with_infinite_size_error<'tcx>( spans: Vec<(Span, Option<hir::HirId>)>, ) { assert!(type_def_id.is_local()); - let span = tcx.hir().span_if_local(type_def_id).unwrap(); - let span = tcx.sess.source_map().guess_head_span(span); + let span = tcx.def_span(type_def_id); let path = tcx.def_path_str(type_def_id); let mut err = struct_span_err!(tcx.sess, span, E0072, "recursive type `{}` has infinite size", path); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index a8be6f74c99..31d54b5b403 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -24,7 +24,7 @@ use rustc_middle::hir::map; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree, GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, IsSuggestable, - ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, }; use rustc_middle::ty::{TypeAndMut, TypeckResults}; use rustc_session::Limit; diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index d61166437d7..78600652254 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -10,7 +10,7 @@ use rustc_middle::thir::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::ToPredicate; -use rustc_middle::ty::{self, Binder, Const, Ty, TypeFoldable}; +use rustc_middle::ty::{self, Binder, Const, Ty, TypeVisitable}; use std::marker::PhantomData; use super::const_evaluatable; @@ -131,8 +131,6 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { let span = debug_span!("select", obligation_forest_size = ?self.predicates.len()); let _enter = span.enter(); - let mut errors = Vec::new(); - // Process pending obligations. let outcome: Outcome<_, _> = self.predicates.process_obligations(&mut FulfillProcessor { selcx, @@ -142,7 +140,8 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { // FIXME: if we kept the original cache key, we could mark projection // obligations as complete for the projection cache here. - errors.extend(outcome.errors.into_iter().map(to_fulfillment_error)); + let errors: Vec<FulfillmentError<'tcx>> = + outcome.errors.into_iter().map(to_fulfillment_error).collect(); debug!( "select({} predicates remaining, {} errors) done", @@ -728,7 +727,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } return ProcessResult::Changed(vec![]); } else { - tracing::debug!("Does NOT hold: {:?}", obligation); + debug!("Does NOT hold: {:?}", obligation); } } diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index f04f527ccb7..dd2769c7186 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -5,7 +5,7 @@ use crate::traits::{self, ObligationCause}; use rustc_hir as hir; use rustc_infer::infer::TyCtxtInferExt; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; use crate::traits::error_reporting::InferCtxtExt; diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 34b0f431b8e..74d2eb17b6b 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -32,6 +32,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry}; use rustc_span::{sym, Span}; use smallvec::SmallVec; @@ -198,17 +199,13 @@ pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>( } } +#[instrument(level = "debug", skip(tcx, elaborated_env))] fn do_normalize_predicates<'tcx>( tcx: TyCtxt<'tcx>, - region_context: DefId, cause: ObligationCause<'tcx>, elaborated_env: ty::ParamEnv<'tcx>, predicates: Vec<ty::Predicate<'tcx>>, ) -> Result<Vec<ty::Predicate<'tcx>>, ErrorGuaranteed> { - debug!( - "do_normalize_predicates(predicates={:?}, region_context={:?}, cause={:?})", - predicates, region_context, cause, - ); let span = cause.span; tcx.infer_ctxt().enter(|infcx| { // FIXME. We should really... do something with these region @@ -240,7 +237,7 @@ fn do_normalize_predicates<'tcx>( // cares about declarations like `'a: 'b`. let outlives_env = OutlivesEnvironment::new(elaborated_env); - infcx.resolve_regions_and_report_errors(region_context, &outlives_env); + infcx.resolve_regions_and_report_errors(&outlives_env); let predicates = match infcx.fully_resolve(predicates) { Ok(predicates) => predicates, @@ -269,9 +266,9 @@ fn do_normalize_predicates<'tcx>( // FIXME: this is gonna need to be removed ... /// Normalizes the parameter environment, reporting errors if they occur. +#[instrument(level = "debug", skip(tcx))] pub fn normalize_param_env_or_error<'tcx>( tcx: TyCtxt<'tcx>, - region_context: DefId, unnormalized_env: ty::ParamEnv<'tcx>, cause: ObligationCause<'tcx>, ) -> ty::ParamEnv<'tcx> { @@ -289,12 +286,6 @@ pub fn normalize_param_env_or_error<'tcx>( // parameter environments once for every fn as it goes, // and errors will get reported then; so outside of type inference we // can be sure that no errors should occur. - - debug!( - "normalize_param_env_or_error(region_context={:?}, unnormalized_env={:?}, cause={:?})", - region_context, unnormalized_env, cause - ); - let mut predicates: Vec<_> = util::elaborate_predicates(tcx, unnormalized_env.caller_bounds().into_iter()) .map(|obligation| obligation.predicate) @@ -338,7 +329,6 @@ pub fn normalize_param_env_or_error<'tcx>( ); let Ok(non_outlives_predicates) = do_normalize_predicates( tcx, - region_context, cause.clone(), elaborated_env, predicates, @@ -362,7 +352,6 @@ pub fn normalize_param_env_or_error<'tcx>( ); let Ok(outlives_predicates) = do_normalize_predicates( tcx, - region_context, cause, outlives_env, outlives_predicates, diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 8d344591915..ac1811244ca 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -19,7 +19,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst}; use rustc_middle::ty::{ - self, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor, + self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use rustc_middle::ty::{Predicate, ToPredicate}; use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; @@ -731,7 +731,7 @@ fn receiver_is_dispatchable<'tcx>( }) } -fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>( +fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, value: T, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index aba4f144d4b..b3e7fbb3578 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -29,8 +29,9 @@ use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_middle::traits::select::OverflowError; -use rustc_middle::ty::fold::{MaxUniverse, TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable}; use rustc_middle::ty::{self, EarlyBinder, Term, ToPredicate, Ty, TyCtxt}; use rustc_span::symbol::sym; @@ -359,7 +360,7 @@ where result } -pub(crate) fn needs_normalization<'tcx, T: TypeFoldable<'tcx>>(value: &T, reveal: Reveal) -> bool { +pub(crate) fn needs_normalization<'tcx, T: TypeVisitable<'tcx>>(value: &T, reveal: Reveal) -> bool { match reveal { Reveal::UserFacing => value .has_type_flags(ty::TypeFlags::HAS_TY_PROJECTION | ty::TypeFlags::HAS_CT_PROJECTION), diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 25cc6e9f9f2..aad3c37f84e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -1,73 +1,7 @@ -use crate::infer::at::At; -use crate::infer::canonical::OriginalQueryValues; -use crate::infer::InferOk; - -use rustc_middle::ty::subst::GenericArg; use rustc_middle::ty::{self, Ty, TyCtxt}; pub use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult}; -pub trait AtExt<'tcx> { - fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec<GenericArg<'tcx>>>; -} - -impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { - /// Given a type `ty` of some value being dropped, computes a set - /// of "kinds" (types, regions) that must be outlive the execution - /// of the destructor. These basically correspond to data that the - /// destructor might access. This is used during regionck to - /// impose "outlives" constraints on any lifetimes referenced - /// within. - /// - /// The rules here are given by the "dropck" RFCs, notably [#1238] - /// and [#1327]. This is a fixed-point computation, where we - /// explore all the data that will be dropped (transitively) when - /// a value of type `ty` is dropped. For each type T that will be - /// dropped and which has a destructor, we must assume that all - /// the types/regions of T are live during the destructor, unless - /// they are marked with a special attribute (`#[may_dangle]`). - /// - /// [#1238]: https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md - /// [#1327]: https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md - fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec<GenericArg<'tcx>>> { - debug!("dropck_outlives(ty={:?}, param_env={:?})", ty, self.param_env,); - - // Quick check: there are a number of cases that we know do not require - // any destructor. - let tcx = self.infcx.tcx; - if trivial_dropck_outlives(tcx, ty) { - return InferOk { value: vec![], obligations: vec![] }; - } - - let mut orig_values = OriginalQueryValues::default(); - let c_ty = self.infcx.canonicalize_query(self.param_env.and(ty), &mut orig_values); - let span = self.cause.span; - debug!("c_ty = {:?}", c_ty); - if let Ok(result) = tcx.dropck_outlives(c_ty) - && result.is_proven() - && let Ok(InferOk { value, obligations }) = - self.infcx.instantiate_query_response_and_region_obligations( - self.cause, - self.param_env, - &orig_values, - result, - ) - { - let ty = self.infcx.resolve_vars_if_possible(ty); - let kinds = value.into_kinds_reporting_overflows(tcx, span, ty); - return InferOk { value: kinds, obligations }; - } - - // Errors and ambiguity in dropck occur in two cases: - // - unresolved inference variables at the end of typeck - // - non well-formed types where projections cannot be resolved - // Either of these should have created an error before. - tcx.sess.delay_span_bug(span, "dtorck encountered internal error"); - - InferOk { value: vec![], obligations: vec![] } - } -} - /// This returns true if the type `ty` is "trivial" for /// dropck-outlives -- that is, if it doesn't require any types to /// outlive. This is similar but not *quite* the same as the @@ -79,6 +13,8 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { /// /// Note also that `needs_drop` requires a "global" type (i.e., one /// with erased regions), but this function does not. +/// +// FIXME(@lcnr): remove this module and move this function somewhere else. pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind() { // None of these types have a destructor and hence they do not @@ -105,7 +41,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty), // (T1..Tn) and closures have same properties as T1..Tn -- - // check if *any* of those are trivial. + // check if *all* of them are trivial. ty::Tuple(tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t)), ty::Closure(_, ref substs) => { trivial_dropck_outlives(tcx, substs.as_closure().tupled_upvars_ty()) diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 7f15b683fda..eccfb3477b9 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -14,6 +14,7 @@ use rustc_infer::traits::Normalized; use rustc_middle::mir; use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor}; use std::ops::ControlFlow; @@ -108,7 +109,7 @@ struct MaxEscapingBoundVarVisitor { } impl<'tcx> TypeVisitor<'tcx> for MaxEscapingBoundVarVisitor { - fn visit_binder<T: TypeFoldable<'tcx>>( + fn visit_binder<T: TypeVisitable<'tcx>>( &mut self, t: &ty::Binder<'tcx, T>, ) -> ControlFlow<Self::BreakTy> { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 605c9ace5ed..c9d46b2810d 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -95,7 +95,7 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( infcx.tcx, region_obligations .iter() - .map(|(_, r_o)| (r_o.sup_type, r_o.sub_region)) + .map(|r_o| (r_o.sup_type, r_o.sub_region)) .map(|(ty, r)| (infcx.resolve_vars_if_possible(ty), r)), ®ion_constraint_data, ); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index 46d6e973d4c..2a3319f0f26 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -3,7 +3,7 @@ use crate::traits::query::Fallible; use rustc_infer::traits::query::OutlivesBound; use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt}; -#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, Lift)] +#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct ImpliedOutlivesBounds<'tcx> { pub ty: Ty<'tcx>, } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs index 82f147f8143..b63382429d0 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -3,7 +3,7 @@ use crate::traits::query::dropck_outlives::{trivial_dropck_outlives, DropckOutli use crate::traits::query::Fallible; use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; -#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, Lift)] +#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct DropckOutlives<'tcx> { dropped_ty: Ty<'tcx>, } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index cfd50c1afb9..0f7af41cfe3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -12,7 +12,7 @@ use rustc_infer::traits::TraitEngine; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, ToPredicate, Ty, TypeFoldable}; +use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable}; use rustc_target::spec::abi::Abi; use crate::traits; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4223cdb5dbb..596ee435622 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -39,7 +39,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::subst::{Subst, SubstsRef}; use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; -use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitable}; use rustc_span::symbol::sym; use std::cell::{Cell, RefCell}; diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index a1861529b59..a80354897d6 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -16,9 +16,8 @@ use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt}; use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine}; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{struct_span_err, EmissionGuarantee}; +use rustc_errors::{struct_span_err, EmissionGuarantee, LintDiagnosticBuilder}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::lint::LintDiagnosticBuilder; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::{self, ImplSubject, TyCtxt}; use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; @@ -341,10 +340,7 @@ fn report_negative_positive_conflict( positive_impl_def_id: DefId, sg: &mut specialization_graph::Graph, ) { - let impl_span = tcx - .sess - .source_map() - .guess_head_span(tcx.span_of_impl(local_impl_def_id.to_def_id()).unwrap()); + let impl_span = tcx.def_span(local_impl_def_id); let mut err = struct_span_err!( tcx.sess, @@ -357,10 +353,7 @@ fn report_negative_positive_conflict( match tcx.span_of_impl(negative_impl_def_id) { Ok(span) => { - err.span_label( - tcx.sess.source_map().guess_head_span(span), - "negative implementation here".to_string(), - ); + err.span_label(span, "negative implementation here"); } Err(cname) => { err.note(&format!("negative implementation in crate `{}`", cname)); @@ -369,10 +362,7 @@ fn report_negative_positive_conflict( match tcx.span_of_impl(positive_impl_def_id) { Ok(span) => { - err.span_label( - tcx.sess.source_map().guess_head_span(span), - "positive implementation here".to_string(), - ); + err.span_label(span, "positive implementation here"); } Err(cname) => { err.note(&format!("positive implementation in crate `{}`", cname)); @@ -389,8 +379,7 @@ fn report_conflicting_impls( used_to_be_allowed: Option<FutureCompatOverlapErrorKind>, sg: &mut specialization_graph::Graph, ) { - let impl_span = - tcx.sess.source_map().guess_head_span(tcx.span_of_impl(impl_def_id.to_def_id()).unwrap()); + let impl_span = tcx.def_span(impl_def_id); // Work to be done after we've built the DiagnosticBuilder. We have to define it // now because the struct_lint methods don't return back the DiagnosticBuilder @@ -417,10 +406,7 @@ fn report_conflicting_impls( let mut err = err.build(&msg); match tcx.span_of_impl(overlap.with_impl) { Ok(span) => { - err.span_label( - tcx.sess.source_map().guess_head_span(span), - "first implementation here".to_string(), - ); + err.span_label(span, "first implementation here".to_string()); err.span_label( impl_span, diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 930c80e0abb..fcb73b43fa8 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -4,7 +4,7 @@ use crate::traits; use rustc_hir::def_id::DefId; use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; pub use rustc_middle::traits::specialization_graph::*; diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index bc2ce31df6d..f3ae34ce30a 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor}; +use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::Span; use std::ops::ControlFlow; diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 3a00c41d90a..3170b29ee69 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -6,7 +6,7 @@ use smallvec::SmallVec; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef}; -use rustc_middle::ty::{self, EarlyBinder, ImplSubject, ToPredicate, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, EarlyBinder, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitable}; use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; pub use rustc_infer::traits::{self, util::*}; diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 8d666046ad9..d43b3c9091f 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -5,7 +5,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeVisitable}; use rustc_span::Span; use std::iter; diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index 2453d3a692a..c7c604e14e3 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -35,7 +35,8 @@ use rustc_ast::ast; use rustc_middle::traits::{ChalkEnvironmentAndGoal, ChalkRustInterner as RustInterner}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::{ - self, Binder, Region, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitor, + self, Binder, Region, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use rustc_span::def_id::DefId; @@ -896,7 +897,7 @@ impl<'tcx> BoundVarsCollector<'tcx> { } impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { - fn visit_binder<T: TypeFoldable<'tcx>>( + fn visit_binder<T: TypeVisitable<'tcx>>( &mut self, t: &Binder<'tcx, T>, ) -> ControlFlow<Self::BreakTy> { diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs index 59cf37fee9c..db7ea4253e3 100644 --- a/compiler/rustc_traits/src/chalk/mod.rs +++ b/compiler/rustc_traits/src/chalk/mod.rs @@ -14,7 +14,7 @@ use rustc_middle::infer::canonical::{CanonicalTyVarKind, CanonicalVarKind}; use rustc_middle::traits::ChalkRustInterner; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{self, BoundVar, ParamTy, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, BoundVar, ParamTy, TyCtxt, TypeFoldable, TypeVisitable}; use rustc_infer::infer::canonical::{ Canonical, CanonicalVarValues, Certainty, QueryRegionConstraints, QueryResponse, diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index ae48211d52d..c7cac8fca89 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -9,7 +9,7 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::query::OutlivesBound; use rustc_infer::traits::TraitEngineExt as _; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; use rustc_span::source_map::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::{CanonicalTyGoal, Fallible, NoSolution}; diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index d08fe6dada1..5e58f237982 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -4,7 +4,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::traits::CodegenObligationError; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{ - self, Binder, Instance, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor, + self, Binder, Instance, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use rustc_span::{sym, DUMMY_SP}; use rustc_trait_selection::traits; @@ -56,7 +56,7 @@ impl<'tcx> BoundVarsCollector<'tcx> { impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { type BreakTy = (); - fn visit_binder<T: TypeFoldable<'tcx>>( + fn visit_binder<T: TypeVisitable<'tcx>>( &mut self, t: &Binder<'tcx, T>, ) -> ControlFlow<Self::BreakTy> { diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 38ae6a25b18..1d345caf699 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -211,7 +211,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { tcx.hir().maybe_body_owned_by(id).map_or(id, |body| body.hir_id) }); let cause = traits::ObligationCause::misc(tcx.def_span(def_id), body_id); - traits::normalize_param_env_or_error(tcx, def_id, unnormalized_env, cause) + traits::normalize_param_env_or_error(tcx, unnormalized_env, cause) } /// Elaborate the environment. diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index f9708d6d919..6744536338c 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -599,6 +599,7 @@ impl UnifyKey for FloatVid { } #[derive(Copy, Clone, PartialEq, Decodable, Encodable, Hash)] +#[rustc_pass_by_value] pub enum Variance { Covariant, // T<A> <: T<B> iff A <: B -- e.g., function return type Invariant, // T<A> <: T<B> iff B == A -- e.g., type of mutable cell diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 37958cc0f40..0a2b54eec47 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -28,7 +28,7 @@ use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::{ - self, Const, DefIdTree, EarlyBinder, IsSuggestable, Ty, TyCtxt, TypeFoldable, + self, Const, DefIdTree, EarlyBinder, IsSuggestable, Ty, TyCtxt, TypeVisitable, }; use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS}; use rustc_span::edition::Edition; @@ -1958,9 +1958,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); } - if let Some(sp) = tcx.hir().span_if_local(adt_def.did()) { - let sp = tcx.sess.source_map().guess_head_span(sp); - err.span_label(sp, format!("variant `{}` not found here", assoc_ident)); + if adt_def.did().is_local() { + err.span_label( + tcx.def_span(adt_def.did()), + format!("variant `{assoc_ident}` not found for this enum"), + ); } err.emit() @@ -2450,7 +2452,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let msg = format!("`Self` is of type `{ty}`"); if let (Ok(i_sp), Some(t_sp)) = (span_of_impl, span_of_ty) { - let i_sp = tcx.sess.source_map().guess_head_span(i_sp); let mut span: MultiSpan = vec![t_sp].into(); span.push_span_label( i_sp, diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index 035571c881c..deaadf0e5c8 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -4,7 +4,7 @@ use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_hir::{self as hir, ExprKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::traits::Obligation; -use rustc_middle::ty::{self, ToPredicate, Ty, TypeFoldable}; +use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable}; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 83a8c5ea021..2c8be88ef6c 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_target::spec::abi; @@ -280,15 +280,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { callee_node: &hir::ExprKind<'_>, callee_span: Span, ) { - let hir_id = self.tcx.hir().get_parent_node(hir_id); - let parent_node = self.tcx.hir().get(hir_id); + let hir = self.tcx.hir(); + let parent_hir_id = hir.get_parent_node(hir_id); + let parent_node = hir.get(parent_hir_id); if let ( hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { fn_decl_span, .. }, .. + kind: hir::ExprKind::Closure { fn_decl_span, body, .. }, + .. }), hir::ExprKind::Block(..), ) = (parent_node, callee_node) { + let fn_decl_span = if hir.body(*body).generator_kind + == Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure)) + { + // Actually need to unwrap a few more layers of HIR to get to + // the _real_ closure... + let async_closure = hir.get_parent_node(hir.get_parent_node(parent_hir_id)); + if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure { fn_decl_span, .. }, + .. + }) = hir.get(async_closure) + { + *fn_decl_span + } else { + return; + } + } else { + *fn_decl_span + }; + let start = fn_decl_span.shrink_to_lo(); let end = callee_span.shrink_to_hi(); err.multipart_suggestion( diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 7d5d6849b3b..66dd5582490 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -40,7 +40,7 @@ use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable}; +use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable}; use rustc_session::lint; use rustc_session::Session; use rustc_span::symbol::sym; diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 6222dbb4074..e9709b64d93 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -13,6 +13,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{ItemKind, Node, PathSegment}; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::Obligation; @@ -20,7 +21,9 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::util::{Discr, IntTypeExt}; -use rustc_middle::ty::{self, ParamEnv, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable}; +use rustc_middle::ty::{ + self, ParamEnv, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, +}; use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS}; use rustc_span::symbol::sym; use rustc_span::{self, Span}; @@ -285,11 +288,9 @@ fn check_panic_info_fn( tcx.sess.span_err(decl.output.span(), "return type should be `!`"); } - let span = tcx.def_span(fn_id); let inputs = fn_sig.inputs(); if inputs.len() != 1 { - let span = tcx.sess.source_map().guess_head_span(span); - tcx.sess.span_err(span, "function should have one argument"); + tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument"); return; } @@ -342,9 +343,7 @@ fn check_alloc_error_fn( let inputs = fn_sig.inputs(); if inputs.len() != 1 { - let span = tcx.def_span(fn_id); - let span = tcx.sess.source_map().guess_head_span(span); - tcx.sess.span_err(span, "function should have one argument"); + tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument"); return; } @@ -521,7 +520,7 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>( struct FoundParentLifetime; struct FindParentLifetimeVisitor<'tcx>(&'tcx ty::Generics); - impl<'tcx> ty::fold::TypeVisitor<'tcx> for FindParentLifetimeVisitor<'tcx> { + impl<'tcx> ty::visit::TypeVisitor<'tcx> for FindParentLifetimeVisitor<'tcx> { type BreakTy = FoundParentLifetime; fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { @@ -555,7 +554,7 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>( selftys: Vec<(Span, Option<String>)>, } - impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> { + impl<'tcx> ty::visit::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> { type BreakTy = Ty<'tcx>; fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { @@ -736,10 +735,8 @@ fn check_opaque_meets_bounds<'tcx>( hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {} // Can have different predicates to their defining use hir::OpaqueTyOrigin::TyAlias => { - // Finally, resolve all regions. This catches wily misuses of - // lifetime parameters. - let fcx = FnCtxt::new(&inh, param_env, hir_id); - fcx.regionck_item(hir_id, span, FxHashSet::default()); + let outlives_environment = OutlivesEnvironment::new(param_env); + infcx.check_region_obligations_and_report_errors(&outlives_environment); } } @@ -1033,7 +1030,6 @@ fn check_impl_items_against_trait<'tcx>( compare_impl_method( tcx, &ty_impl_item, - impl_item.span, &ty_trait_item, impl_trait_ref, opt_trait_span, @@ -1093,17 +1089,20 @@ fn check_impl_items_against_trait<'tcx>( } if !missing_items.is_empty() { - let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); - missing_items_err(tcx, impl_span, &missing_items, full_impl_span); + missing_items_err(tcx, tcx.def_span(impl_id), &missing_items, full_impl_span); } if let Some(missing_items) = must_implement_one_of { - let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); let attr_span = tcx .get_attr(impl_trait_ref.def_id, sym::rustc_must_implement_one_of) .map(|attr| attr.span); - missing_items_must_implement_one_of_err(tcx, impl_span, missing_items, attr_span); + missing_items_must_implement_one_of_err( + tcx, + tcx.def_span(impl_id), + missing_items, + attr_span, + ); } } } @@ -1596,7 +1595,7 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) -> E .filter(|(_, ty)| !matches!(ty.kind(), ty::Never)) { struct OpaqueTypeCollector(Vec<DefId>); - impl<'tcx> ty::fold::TypeVisitor<'tcx> for OpaqueTypeCollector { + impl<'tcx> ty::visit::TypeVisitor<'tcx> for OpaqueTypeCollector { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { match *t.kind() { ty::Opaque(def, _) => { diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index 131e594ed94..1681e6af812 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -10,8 +10,8 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_infer::infer::{InferOk, InferResult}; -use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::InternalSubsts; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, Ty}; use rustc_span::source_map::Span; use rustc_target::spec::abi::Abi; diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 43fc49c6801..acd7e4a92dc 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -50,9 +50,9 @@ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, }; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, ToPredicate, Ty, TypeAndMut}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 95c82a7d2c3..2bfb9343877 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -1,3 +1,4 @@ +use crate::check::regionck::OutlivesEnvironmentExt; use crate::errors::LifetimesOrBoundsMismatchOnTrait; use rustc_data_structures::stable_set::FxHashSet; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed}; @@ -5,6 +6,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -31,14 +33,13 @@ use super::{potentially_plural_count, FnCtxt, Inherited}; pub(crate) fn compare_impl_method<'tcx>( tcx: TyCtxt<'tcx>, impl_m: &ty::AssocItem, - impl_m_span: Span, trait_m: &ty::AssocItem, impl_trait_ref: ty::TraitRef<'tcx>, trait_item_span: Option<Span>, ) { debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref); - let impl_m_span = tcx.sess.source_map().guess_head_span(impl_m_span); + let impl_m_span = tcx.def_span(impl_m.def_id); if let Err(_) = compare_self_type(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref) { return; @@ -78,10 +79,11 @@ fn compare_predicate_entailment<'tcx>( let trait_to_impl_substs = impl_trait_ref.substs; // This node-id should be used for the `body_id` field on each - // `ObligationCause` (and the `FnCtxt`). This is what - // `regionck_item` expects. + // `ObligationCause` (and the `FnCtxt`). + // + // FIXME(@lcnr): remove that after removing `cause.body_id` from + // obligations. let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); - // We sometimes modify the span further down. let mut cause = ObligationCause::new( impl_m_span, @@ -208,8 +210,7 @@ fn compare_predicate_entailment<'tcx>( Reveal::UserFacing, hir::Constness::NotConst, ); - let param_env = - traits::normalize_param_env_or_error(tcx, impl_m.def_id, param_env, normalize_cause); + let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); tcx.infer_ctxt().enter(|infcx| { let inh = Inherited::new(infcx, impl_m.def_id.expect_local()); @@ -399,8 +400,9 @@ fn compare_predicate_entailment<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let fcx = FnCtxt::new(&inh, param_env, impl_m_hir_id); - fcx.regionck_item(impl_m_hir_id, impl_m_span, wf_tys); + let mut outlives_environment = OutlivesEnvironment::new(param_env); + outlives_environment.add_implied_bounds(infcx, wf_tys, impl_m_hir_id); + infcx.check_region_obligations_and_report_errors(&outlives_environment); Ok(()) }) @@ -441,13 +443,9 @@ fn check_region_bounds_on_impl_item<'tcx>( .as_local() .and_then(|did| tcx.hir().get_generics(did)) .map_or(def_span, |g| g.span); - let generics_span = tcx.hir().span_if_local(trait_m.def_id).map(|sp| { - let def_sp = tcx.sess.source_map().guess_head_span(sp); - trait_m - .def_id - .as_local() - .and_then(|did| tcx.hir().get_generics(did)) - .map_or(def_sp, |g| g.span) + let generics_span = trait_m.def_id.as_local().map(|did| { + let def_sp = tcx.def_span(did); + tcx.hir().get_generics(did).map_or(def_sp, |g| g.span) }); let reported = tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait { @@ -1041,8 +1039,7 @@ fn compare_generic_param_kinds<'tcx>( err.span_label(trait_header_span, ""); err.span_label(param_trait_span, make_param_message("expected", param_trait)); - let impl_header_span = - tcx.sess.source_map().guess_head_span(tcx.def_span(tcx.parent(impl_item.def_id))); + let impl_header_span = tcx.def_span(tcx.parent(impl_item.def_id)); err.span_label(impl_header_span, ""); err.span_label(param_impl_span, make_param_message("found", param_impl)); @@ -1155,8 +1152,8 @@ pub(crate) fn compare_const_impl<'tcx>( return; } - let fcx = FnCtxt::new(&inh, param_env, impl_c_hir_id); - fcx.regionck_item(impl_c_hir_id, impl_c_span, FxHashSet::default()); + let outlives_environment = OutlivesEnvironment::new(param_env); + infcx.resolve_regions_and_report_errors(&outlives_environment); }); } @@ -1247,12 +1244,7 @@ fn compare_type_predicate_entailment<'tcx>( Reveal::UserFacing, hir::Constness::NotConst, ); - let param_env = traits::normalize_param_env_or_error( - tcx, - impl_ty.def_id, - param_env, - normalize_cause.clone(), - ); + let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause.clone()); tcx.infer_ctxt().enter(|infcx| { let inh = Inherited::new(infcx, impl_ty.def_id.expect_local()); let infcx = &inh.infcx; @@ -1279,8 +1271,8 @@ fn compare_type_predicate_entailment<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id); - fcx.regionck_item(impl_ty_hir_id, impl_ty_span, FxHashSet::default()); + let outlives_environment = OutlivesEnvironment::new(param_env); + infcx.check_region_obligations_and_report_errors(&outlives_environment); Ok(()) }) @@ -1504,12 +1496,16 @@ pub fn check_type_bounds<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. + // + // FIXME: Remove that `FnCtxt`. let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id); let implied_bounds = match impl_ty.container { ty::TraitContainer(_) => FxHashSet::default(), ty::ImplContainer(def_id) => fcx.impl_implied_bounds(def_id, impl_ty_span), }; - fcx.regionck_item(impl_ty_hir_id, impl_ty_span, implied_bounds); + let mut outlives_environment = OutlivesEnvironment::new(param_env); + outlives_environment.add_implied_bounds(infcx, implied_bounds, impl_ty_hir_id); + infcx.check_region_obligations_and_report_errors(&outlives_environment); Ok(()) }) diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index ed3b9f2db1f..72095c40807 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -1,5 +1,6 @@ -use crate::check::regionck::RegionCtxt; -use crate::hir; +// FIXME(@lcnr): Move this module out of `rustc_typeck`. +// +// We don't do any drop checking during hir typeck. use crate::hir::def_id::{DefId, LocalDefId}; use rustc_errors::{struct_span_err, ErrorGuaranteed}; use rustc_middle::ty::error::TypeError; @@ -7,9 +8,6 @@ use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::util::IgnoreRegions; use rustc_middle::ty::{self, Predicate, Ty, TyCtxt}; -use rustc_span::Span; -use rustc_trait_selection::traits::query::dropck_outlives::AtExt; -use rustc_trait_selection::traits::ObligationCause; /// This function confirms that the `Drop` implementation identified by /// `drop_impl_did` is not any more specialized than the type it is @@ -231,23 +229,6 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( result } -/// This function is not only checking that the dropck obligations are met for -/// the given type, but it's also currently preventing non-regular recursion in -/// types from causing stack overflows (dropck_no_diverge_on_nonregular_*.rs). -pub(crate) fn check_drop_obligations<'a, 'tcx>( - rcx: &mut RegionCtxt<'a, 'tcx>, - ty: Ty<'tcx>, - span: Span, - body_id: hir::HirId, -) { - debug!("check_drop_obligations typ: {:?}", ty); - - let cause = &ObligationCause::misc(span, body_id); - let infer_ok = rcx.infcx.at(cause, rcx.fcx.param_env).dropck_outlives(ty); - debug!("dropck_outlives = {:#?}", infer_ok); - rcx.fcx.register_infer_ok_obligations(infer_ok); -} - // This is an implementation of the TypeRelation trait with the // aim of simply comparing for equality (without side-effects). // It is not intended to be used anywhere else other than here. diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index b4476d5c59b..45ea04f2342 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -44,7 +44,7 @@ use rustc_middle::middle::stability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::error::TypeError::FieldMisMatch; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeFoldable}; +use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable}; use rustc_session::parse::feature_err; use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index bce2e85de84..f3341e72e73 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -23,6 +23,7 @@ use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{ self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts, }; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, ToPolyTraitRef, ToPredicate, Ty, UserType, @@ -557,7 +558,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // sufficiently enforced with erased regions. =) fn can_contain_user_lifetime_bounds<T>(t: T) -> bool where - T: TypeFoldable<'tcx>, + T: TypeVisitable<'tcx>, { t.has_free_regions() || t.has_projections() || t.has_infer_types() } @@ -1538,15 +1539,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } else { if !self.is_tainted_by_errors() { - self.emit_inference_failure_err( - (**self).body_id, - sp, - ty.into(), - vec![], - E0282, - true, - ) - .emit(); + self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) + .emit(); } let err = self.tcx.ty_error(); self.demand_suptype(sp, err, ty); diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index d2e16021827..1794446e92a 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -24,7 +24,7 @@ use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt}; use rustc_infer::infer::InferOk; use rustc_infer::infer::TypeTrace; use rustc_middle::ty::adjustment::AllowTwoPhase; -use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::symbol::Ident; diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs index 8b680a3d042..05bcc710e16 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs @@ -15,8 +15,8 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, Const, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::symbol::Ident; diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 7195da863db..edb0ec027a0 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -184,9 +184,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if let (ty::FnDef(def_id, ..), true) = (&found.kind(), self.suggest_fn_call(err, expr, expected, found)) { - if let Some(sp) = self.tcx.hir().span_if_local(*def_id) { - let sp = self.sess().source_map().guess_head_span(sp); - err.span_label(sp, &format!("{} defined here", found)); + if def_id.is_local() { + err.span_label(self.tcx.def_span(def_id), &format!("{} defined here", found)); } } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { let is_struct_pat_shorthand_field = diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_typeck/src/check/inherited.rs index 08a64d8e673..2ce258cf69c 100644 --- a/compiler/rustc_typeck/src/check/inherited.rs +++ b/compiler/rustc_typeck/src/check/inherited.rs @@ -8,6 +8,7 @@ use rustc_hir::HirIdMap; use rustc_infer::infer; use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::{self, Span}; use rustc_trait_selection::infer::InferCtxtExt as _; diff --git a/compiler/rustc_typeck/src/check/intrinsicck.rs b/compiler/rustc_typeck/src/check/intrinsicck.rs index 469e6118575..cc91f2431e0 100644 --- a/compiler/rustc_typeck/src/check/intrinsicck.rs +++ b/compiler/rustc_typeck/src/check/intrinsicck.rs @@ -4,7 +4,7 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; -use rustc_middle::ty::{self, Article, FloatTy, InferTy, IntTy, Ty, TyCtxt, TypeFoldable, UintTy}; +use rustc_middle::ty::{self, Article, FloatTy, InferTy, IntTy, Ty, TyCtxt, TypeVisitable, UintTy}; use rustc_session::lint; use rustc_span::{Span, Symbol, DUMMY_SP}; use rustc_target::abi::{Pointer, VariantIdx}; diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 5ca82218355..e29f0275bf4 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -21,7 +21,7 @@ use rustc_infer::infer::{self, InferOk}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; -use rustc_middle::ty::{self, ToPredicate, Ty, TypeFoldable}; +use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable}; use rustc_span::symbol::Ident; use rustc_span::Span; use rustc_trait_selection::traits; diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 87254b211d6..e9b91414a07 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -21,7 +21,9 @@ use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; -use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{ + self, EarlyBinder, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable, +}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::lev_distance::{ diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index fa5f0eff223..4e1a105fc71 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -17,7 +17,7 @@ use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::print::with_crate_prefix; use rustc_middle::ty::ToPolyTraitRef; -use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span}; use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _; @@ -121,11 +121,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) else { continue; }; - let note_span = self - .tcx - .hir() - .span_if_local(item.def_id) - .or_else(|| self.tcx.hir().span_if_local(impl_did)); + + let note_span = if item.def_id.is_local() { + Some(self.tcx.def_span(item.def_id)) + } else if impl_did.is_local() { + Some(self.tcx.def_span(impl_did)) + } else { + None + }; let impl_ty = self.tcx.at(span).type_of(impl_did); @@ -158,10 +161,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Some(note_span) = note_span { // We have a span pointing to the method. Show note with snippet. - err.span_note( - self.tcx.sess.source_map().guess_head_span(note_span), - ¬e_str, - ); + err.span_note(note_span, ¬e_str); } else { err.note(¬e_str); } @@ -197,11 +197,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } CandidateSource::Trait(trait_did) => { let Some(item) = self.associated_value(trait_did, item_name) else { continue }; - let item_span = self - .tcx - .sess - .source_map() - .guess_head_span(self.tcx.def_span(item.def_id)); + let item_span = self.tcx.def_span(item.def_id); let idx = if sources.len() > 1 { let msg = &format!( "candidate #{} is defined in the trait `{}`", @@ -471,9 +467,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.note(&format!("`count` is defined on `{iterator_trait}`, which `{actual}` does not implement")); } } else if !unsatisfied_predicates.is_empty() { - let def_span = |def_id| { - self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id)) - }; let mut type_params = FxHashMap::default(); // Pick out the list of unimplemented traits on the receiver. @@ -564,22 +557,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); match &self_ty.kind() { // Point at the type that couldn't satisfy the bound. - ty::Adt(def, _) => bound_spans.push((def_span(def.did()), msg)), + ty::Adt(def, _) => { + bound_spans.push((self.tcx.def_span(def.did()), msg)) + } // Point at the trait object that couldn't satisfy the bound. ty::Dynamic(preds, _) => { for pred in preds.iter() { match pred.skip_binder() { - ty::ExistentialPredicate::Trait(tr) => { - bound_spans.push((def_span(tr.def_id), msg.clone())) - } + ty::ExistentialPredicate::Trait(tr) => bound_spans + .push((self.tcx.def_span(tr.def_id), msg.clone())), ty::ExistentialPredicate::Projection(_) | ty::ExistentialPredicate::AutoTrait(_) => {} } } } // Point at the closure that couldn't satisfy the bound. - ty::Closure(def_id, _) => bound_spans - .push((def_span(*def_id), format!("doesn't satisfy `{}`", quiet))), + ty::Closure(def_id, _) => bound_spans.push(( + tcx.def_span(*def_id), + format!("doesn't satisfy `{}`", quiet), + )), _ => {} } }; @@ -1469,21 +1465,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }) .collect::<FxHashSet<_>>(); - let sm = self.tcx.sess.source_map(); let mut spans: MultiSpan = def_ids .iter() .filter_map(|def_id| { let span = self.tcx.def_span(*def_id); - if span.is_dummy() { None } else { Some(sm.guess_head_span(span)) } + if span.is_dummy() { None } else { Some(span) } }) .collect::<Vec<_>>() .into(); for pred in &preds { match pred.self_ty().kind() { - ty::Adt(def, _) => { + ty::Adt(def, _) if def.did().is_local() => { spans.push_span_label( - sm.guess_head_span(self.tcx.def_span(def.did())), + self.tcx.def_span(def.did()), format!("must implement `{}`", pred.trait_ref.print_only_trait_path()), ); } @@ -2090,9 +2085,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match &potential_candidates[..] { [] => {} [trait_info] if trait_info.def_id.is_local() => { - let span = self.tcx.hir().span_if_local(trait_info.def_id).unwrap(); err.span_note( - self.tcx.sess.source_map().guess_head_span(span), + self.tcx.def_span(trait_info.def_id), &format!( "`{}` defines an item `{}`, perhaps you need to {} it", self.tcx.def_path_str(trait_info.def_id), diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 0ede9ef7756..dee58791cec 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -368,7 +368,7 @@ fn typeck_with_fallback<'tcx>( let typeck_results = Inherited::build(tcx, def_id).enter(|inh| { let param_env = tcx.param_env(def_id); - let (fcx, wf_tys) = if let Some(hir::FnSig { header, decl, .. }) = fn_sig { + let fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig { let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() { let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); <dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None) @@ -378,13 +378,7 @@ fn typeck_with_fallback<'tcx>( check_abi(tcx, id, span, fn_sig.abi()); - // When normalizing the function signature, we assume all types are - // well-formed. So, we don't need to worry about the obligations - // from normalization. We could just discard these, but to align with - // compare_method and elsewhere, we just add implied bounds for - // these types. - let mut wf_tys = FxHashSet::default(); - // Compute the fty from point of view of inside the fn. + // Compute the function signature from point of view of inside the fn. let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); let fn_sig = inh.normalize_associated_types_in( body.value.span, @@ -392,10 +386,7 @@ fn typeck_with_fallback<'tcx>( param_env, fn_sig, ); - wf_tys.extend(fn_sig.inputs_and_output.iter()); - - let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0; - (fcx, wf_tys) + check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0 } else { let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); let expected_type = body_ty @@ -458,7 +449,7 @@ fn typeck_with_fallback<'tcx>( fcx.write_ty(id, expected_type); - (fcx, FxHashSet::default()) + fcx }; let fallback_has_occurred = fcx.type_inference_fallback(); @@ -490,11 +481,7 @@ fn typeck_with_fallback<'tcx>( fcx.check_asms(); - if fn_sig.is_some() { - fcx.regionck_fn(id, body, span, wf_tys); - } else { - fcx.regionck_expr(body); - } + fcx.infcx.skip_region_resolution(); fcx.resolve_type_vars_in_body(body) }); diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index c2f97a5051c..42893789957 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitor, + self, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident}; diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index e1ec9f13cd1..fbfbfba5c2a 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -13,7 +13,7 @@ use rustc_hir::{HirId, Pat, PatKind}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::middle::stability::EvalResult; -use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeFoldable}; +use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitable}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index 0ce63922098..1c3c5f999bc 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -1,106 +1,9 @@ -//! The region check is a final pass that runs over the AST after we have -//! inferred the type constraints but before we have actually finalized -//! the types. Its purpose is to embed a variety of region constraints. -//! Inserting these constraints as a separate pass is good because (1) it -//! localizes the code that has to do with region inference and (2) often -//! we cannot know what constraints are needed until the basic types have -//! been inferred. -//! -//! ### Interaction with the borrow checker -//! -//! In general, the job of the borrowck module (which runs later) is to -//! check that all soundness criteria are met, given a particular set of -//! regions. The job of *this* module is to anticipate the needs of the -//! borrow checker and infer regions that will satisfy its requirements. -//! It is generally true that the inference doesn't need to be sound, -//! meaning that if there is a bug and we inferred bad regions, the borrow -//! checker should catch it. This is not entirely true though; for -//! example, the borrow checker doesn't check subtyping, and it doesn't -//! check that region pointers are always live when they are used. It -//! might be worthwhile to fix this so that borrowck serves as a kind of -//! verification step -- that would add confidence in the overall -//! correctness of the compiler, at the cost of duplicating some type -//! checks and effort. -//! -//! ### Inferring the duration of borrows, automatic and otherwise -//! -//! Whenever we introduce a borrowed pointer, for example as the result of -//! a borrow expression `let x = &data`, the lifetime of the pointer `x` -//! is always specified as a region inference variable. `regionck` has the -//! job of adding constraints such that this inference variable is as -//! narrow as possible while still accommodating all uses (that is, every -//! dereference of the resulting pointer must be within the lifetime). -//! -//! #### Reborrows -//! -//! Generally speaking, `regionck` does NOT try to ensure that the data -//! `data` will outlive the pointer `x`. That is the job of borrowck. The -//! one exception is when "re-borrowing" the contents of another borrowed -//! pointer. For example, imagine you have a borrowed pointer `b` with -//! lifetime `L1` and you have an expression `&*b`. The result of this -//! expression will be another borrowed pointer with lifetime `L2` (which is -//! an inference variable). The borrow checker is going to enforce the -//! constraint that `L2 < L1`, because otherwise you are re-borrowing data -//! for a lifetime larger than the original loan. However, without the -//! routines in this module, the region inferencer would not know of this -//! dependency and thus it might infer the lifetime of `L2` to be greater -//! than `L1` (issue #3148). -//! -//! There are a number of troublesome scenarios in the tests -//! `region-dependent-*.rs`, but here is one example: -//! -//! struct Foo { i: i32 } -//! struct Bar { foo: Foo } -//! fn get_i<'a>(x: &'a Bar) -> &'a i32 { -//! let foo = &x.foo; // Lifetime L1 -//! &foo.i // Lifetime L2 -//! } -//! -//! Note that this comes up either with `&` expressions, `ref` -//! bindings, and `autorefs`, which are the three ways to introduce -//! a borrow. -//! -//! The key point here is that when you are borrowing a value that -//! is "guaranteed" by a borrowed pointer, you must link the -//! lifetime of that borrowed pointer (`L1`, here) to the lifetime of -//! the borrow itself (`L2`). What do I mean by "guaranteed" by a -//! borrowed pointer? I mean any data that is reached by first -//! dereferencing a borrowed pointer and then either traversing -//! interior offsets or boxes. We say that the guarantor -//! of such data is the region of the borrowed pointer that was -//! traversed. This is essentially the same as the ownership -//! relation, except that a borrowed pointer never owns its -//! contents. - -use crate::check::dropck; -use crate::check::FnCtxt; -use crate::mem_categorization as mc; use crate::outlives::outlives_bounds::InferCtxtExt as _; use rustc_data_structures::stable_set::FxHashSet; use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::PatKind; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{self, InferCtxt, RegionObligation}; -use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId}; -use rustc_middle::ty::adjustment; -use rustc_middle::ty::{self, Ty}; -use rustc_span::Span; -use std::ops::Deref; - -// a variation on try that just returns unit -macro_rules! ignore_err { - ($e:expr) => { - match $e { - Ok(e) => e, - Err(_) => { - debug!("ignoring mem-categorization error!"); - return (); - } - } - }; -} +use rustc_infer::infer::InferCtxt; +use rustc_middle::ty::Ty; pub(crate) trait OutlivesEnvironmentExt<'tcx> { fn add_implied_bounds( @@ -108,7 +11,6 @@ pub(crate) trait OutlivesEnvironmentExt<'tcx> { infcx: &InferCtxt<'_, 'tcx>, fn_sig_tys: FxHashSet<Ty<'tcx>>, body_id: hir::HirId, - span: Span, ); } @@ -135,750 +37,11 @@ impl<'tcx> OutlivesEnvironmentExt<'tcx> for OutlivesEnvironment<'tcx> { infcx: &InferCtxt<'a, 'tcx>, fn_sig_tys: FxHashSet<Ty<'tcx>>, body_id: hir::HirId, - span: Span, ) { for ty in fn_sig_tys { let ty = infcx.resolve_vars_if_possible(ty); - let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span); + let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty); self.add_outlives_bounds(Some(infcx), implied_bounds) } } } - -/////////////////////////////////////////////////////////////////////////// -// PUBLIC ENTRY POINTS - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn regionck_expr(&self, body: &'tcx hir::Body<'tcx>) { - let subject = self.tcx.hir().body_owner_def_id(body.id()); - let id = body.value.hir_id; - let mut rcx = RegionCtxt::new(self, id, Subject(subject), self.param_env); - - // There are no add'l implied bounds when checking a - // standalone expr (e.g., the `E` in a type like `[u32; E]`). - rcx.outlives_environment.save_implied_bounds(id); - - if !self.errors_reported_since_creation() { - // regionck assumes typeck succeeded - rcx.visit_body(body); - rcx.visit_region_obligations(id); - } - // Checked by NLL - rcx.fcx.skip_region_resolution(); - } - - /// Region checking during the WF phase for items. `wf_tys` are the - /// types from which we should derive implied bounds, if any. - #[instrument(level = "debug", skip(self))] - pub fn regionck_item(&self, item_id: hir::HirId, span: Span, wf_tys: FxHashSet<Ty<'tcx>>) { - let subject = self.tcx.hir().local_def_id(item_id); - let mut rcx = RegionCtxt::new(self, item_id, Subject(subject), self.param_env); - rcx.outlives_environment.add_implied_bounds(self, wf_tys, item_id, span); - rcx.outlives_environment.save_implied_bounds(item_id); - rcx.visit_region_obligations(item_id); - rcx.resolve_regions_and_report_errors(); - } - - /// Region check a function body. Not invoked on closures, but - /// only on the "root" fn item (in which closures may be - /// embedded). Walks the function body and adds various add'l - /// constraints that are needed for region inference. This is - /// separated both to isolate "pure" region constraints from the - /// rest of type check and because sometimes we need type - /// inference to have completed before we can determine which - /// constraints to add. - pub(crate) fn regionck_fn( - &self, - fn_id: hir::HirId, - body: &'tcx hir::Body<'tcx>, - span: Span, - wf_tys: FxHashSet<Ty<'tcx>>, - ) { - debug!("regionck_fn(id={})", fn_id); - let subject = self.tcx.hir().body_owner_def_id(body.id()); - let hir_id = body.value.hir_id; - let mut rcx = RegionCtxt::new(self, hir_id, Subject(subject), self.param_env); - // We need to add the implied bounds from the function signature - rcx.outlives_environment.add_implied_bounds(self, wf_tys, fn_id, span); - rcx.outlives_environment.save_implied_bounds(fn_id); - - if !self.errors_reported_since_creation() { - // regionck assumes typeck succeeded - rcx.visit_fn_body(fn_id, body, self.tcx.hir().span(fn_id)); - } - - // Checked by NLL - rcx.fcx.skip_region_resolution(); - } -} - -/////////////////////////////////////////////////////////////////////////// -// INTERNALS - -pub struct RegionCtxt<'a, 'tcx> { - pub fcx: &'a FnCtxt<'a, 'tcx>, - - outlives_environment: OutlivesEnvironment<'tcx>, - - // id of innermost fn body id - body_id: hir::HirId, - body_owner: LocalDefId, - - // id of AST node being analyzed (the subject of the analysis). - subject_def_id: LocalDefId, -} - -impl<'a, 'tcx> Deref for RegionCtxt<'a, 'tcx> { - type Target = FnCtxt<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - self.fcx - } -} - -pub struct Subject(LocalDefId); - -impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { - pub fn new( - fcx: &'a FnCtxt<'a, 'tcx>, - initial_body_id: hir::HirId, - Subject(subject): Subject, - param_env: ty::ParamEnv<'tcx>, - ) -> RegionCtxt<'a, 'tcx> { - let outlives_environment = OutlivesEnvironment::new(param_env); - RegionCtxt { - fcx, - body_id: initial_body_id, - body_owner: subject, - subject_def_id: subject, - outlives_environment, - } - } - - /// Try to resolve the type for the given node, returning `t_err` if an error results. Note that - /// we never care about the details of the error, the same error will be detected and reported - /// in the writeback phase. - /// - /// Note one important point: we do not attempt to resolve *region variables* here. This is - /// because regionck is essentially adding constraints to those region variables and so may yet - /// influence how they are resolved. - /// - /// Consider this silly example: - /// - /// ```ignore UNSOLVED (does replacing @i32 with Box<i32> preserve the desired semantics for the example?) - /// fn borrow(x: &i32) -> &i32 {x} - /// fn foo(x: @i32) -> i32 { // block: B - /// let b = borrow(x); // region: <R0> - /// *b - /// } - /// ``` - /// - /// Here, the region of `b` will be `<R0>`. `<R0>` is constrained to be some subregion of the - /// block B and some superregion of the call. If we forced it now, we'd choose the smaller - /// region (the call). But that would make the *b illegal. Since we don't resolve, the type - /// of b will be `&<R0>.i32` and then `*b` will require that `<R0>` be bigger than the let and - /// the `*b` expression, so we will effectively resolve `<R0>` to be the block B. - pub fn resolve_type(&self, unresolved_ty: Ty<'tcx>) -> Ty<'tcx> { - self.resolve_vars_if_possible(unresolved_ty) - } - - /// Try to resolve the type for the given node. - fn resolve_node_type(&self, id: hir::HirId) -> Ty<'tcx> { - let t = self.node_ty(id); - self.resolve_type(t) - } - - /// This is the "main" function when region-checking a function item or a - /// closure within a function item. It begins by updating various fields - /// (e.g., `outlives_environment`) to be appropriate to the function and - /// then adds constraints derived from the function body. - /// - /// Note that it does **not** restore the state of the fields that - /// it updates! This is intentional, since -- for the main - /// function -- we wish to be able to read the final - /// `outlives_environment` and other fields from the caller. For - /// closures, however, we save and restore any "scoped state" - /// before we invoke this function. (See `visit_fn` in the - /// `intravisit::Visitor` impl below.) - fn visit_fn_body( - &mut self, - id: hir::HirId, // the id of the fn itself - body: &'tcx hir::Body<'tcx>, - span: Span, - ) { - // When we enter a function, we can derive - debug!("visit_fn_body(id={:?})", id); - - let body_id = body.id(); - self.body_id = body_id.hir_id; - self.body_owner = self.tcx.hir().body_owner_def_id(body_id); - - let Some(fn_sig) = self.typeck_results.borrow().liberated_fn_sigs().get(id) else { - bug!("No fn-sig entry for id={:?}", id); - }; - - // Collect the types from which we create inferred bounds. - // For the return type, if diverging, substitute `bool` just - // because it will have no effect. - // - // FIXME(#27579) return types should not be implied bounds - let fn_sig_tys: FxHashSet<_> = - fn_sig.inputs().iter().cloned().chain(Some(fn_sig.output())).collect(); - - self.outlives_environment.add_implied_bounds(self.fcx, fn_sig_tys, body_id.hir_id, span); - self.outlives_environment.save_implied_bounds(body_id.hir_id); - self.link_fn_params(body.params); - self.visit_body(body); - self.visit_region_obligations(body_id.hir_id); - } - - fn visit_inline_const(&mut self, id: hir::HirId, body: &'tcx hir::Body<'tcx>) { - debug!("visit_inline_const(id={:?})", id); - - // Save state of current function. We will restore afterwards. - let old_body_id = self.body_id; - let old_body_owner = self.body_owner; - let env_snapshot = self.outlives_environment.push_snapshot_pre_typeck_child(); - - let body_id = body.id(); - self.body_id = body_id.hir_id; - self.body_owner = self.tcx.hir().body_owner_def_id(body_id); - - self.outlives_environment.save_implied_bounds(body_id.hir_id); - - self.visit_body(body); - self.visit_region_obligations(body_id.hir_id); - - // Restore state from previous function. - self.outlives_environment.pop_snapshot_post_typeck_child(env_snapshot); - self.body_id = old_body_id; - self.body_owner = old_body_owner; - } - - fn visit_region_obligations(&mut self, hir_id: hir::HirId) { - debug!("visit_region_obligations: hir_id={:?}", hir_id); - - // region checking can introduce new pending obligations - // which, when processed, might generate new region - // obligations. So make sure we process those. - self.select_all_obligations_or_error(); - } - - fn resolve_regions_and_report_errors(&self) { - self.infcx.process_registered_region_obligations( - self.outlives_environment.region_bound_pairs_map(), - self.param_env, - ); - - self.fcx.resolve_regions_and_report_errors( - self.subject_def_id.to_def_id(), - &self.outlives_environment, - ); - } - - fn constrain_bindings_in_pat(&mut self, pat: &hir::Pat<'_>) { - debug!("regionck::visit_pat(pat={:?})", pat); - pat.each_binding(|_, hir_id, span, _| { - let typ = self.resolve_node_type(hir_id); - let body_id = self.body_id; - dropck::check_drop_obligations(self, typ, span, body_id); - }) - } -} - -impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { - // (..) FIXME(#3238) should use visit_pat, not visit_arm/visit_local, - // However, right now we run into an issue whereby some free - // regions are not properly related if they appear within the - // types of arguments that must be inferred. This could be - // addressed by deferring the construction of the region - // hierarchy, and in particular the relationships between free - // regions, until regionck, as described in #3238. - - fn visit_fn( - &mut self, - fk: intravisit::FnKind<'tcx>, - _: &'tcx hir::FnDecl<'tcx>, - body_id: hir::BodyId, - span: Span, - hir_id: hir::HirId, - ) { - assert!( - matches!(fk, intravisit::FnKind::Closure), - "visit_fn invoked for something other than a closure" - ); - - // Save state of current function before invoking - // `visit_fn_body`. We will restore afterwards. - let old_body_id = self.body_id; - let old_body_owner = self.body_owner; - let env_snapshot = self.outlives_environment.push_snapshot_pre_typeck_child(); - - let body = self.tcx.hir().body(body_id); - self.visit_fn_body(hir_id, body, span); - - // Restore state from previous function. - self.outlives_environment.pop_snapshot_post_typeck_child(env_snapshot); - self.body_id = old_body_id; - self.body_owner = old_body_owner; - } - - //visit_pat: visit_pat, // (..) see above - - fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) { - // see above - self.constrain_bindings_in_pat(arm.pat); - intravisit::walk_arm(self, arm); - } - - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { - // see above - self.constrain_bindings_in_pat(l.pat); - self.link_local(l); - intravisit::walk_local(self, l); - } - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - // Check any autoderefs or autorefs that appear. - let cmt_result = self.constrain_adjustments(expr); - - // If necessary, constrain destructors in this expression. This will be - // the adjusted form if there is an adjustment. - match cmt_result { - Ok(head_cmt) => { - self.check_safety_of_rvalue_destructor_if_necessary(&head_cmt, expr.span); - } - Err(..) => { - self.tcx.sess.delay_span_bug(expr.span, "cat_expr Errd"); - } - } - - match expr.kind { - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, ref base) => { - self.link_addr_of(expr, m, base); - - intravisit::walk_expr(self, expr); - } - - hir::ExprKind::Match(ref discr, arms, _) => { - self.link_match(discr, arms); - - intravisit::walk_expr(self, expr); - } - - hir::ExprKind::ConstBlock(anon_const) => { - let body = self.tcx.hir().body(anon_const.body); - self.visit_inline_const(anon_const.hir_id, body); - } - - _ => intravisit::walk_expr(self, expr), - } - } -} - -impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { - /// Creates a temporary `MemCategorizationContext` and pass it to the closure. - fn with_mc<F, R>(&self, f: F) -> R - where - F: for<'b> FnOnce(mc::MemCategorizationContext<'b, 'tcx>) -> R, - { - f(mc::MemCategorizationContext::new( - &self.infcx, - self.outlives_environment.param_env, - self.body_owner, - &self.typeck_results.borrow(), - )) - } - - /// Invoked on any adjustments that occur. Checks that if this is a region pointer being - /// dereferenced, the lifetime of the pointer includes the deref expr. - fn constrain_adjustments( - &mut self, - expr: &hir::Expr<'_>, - ) -> mc::McResult<PlaceWithHirId<'tcx>> { - debug!("constrain_adjustments(expr={:?})", expr); - - let mut place = self.with_mc(|mc| mc.cat_expr_unadjusted(expr))?; - - let typeck_results = self.typeck_results.borrow(); - let adjustments = typeck_results.expr_adjustments(expr); - if adjustments.is_empty() { - return Ok(place); - } - - debug!("constrain_adjustments: adjustments={:?}", adjustments); - - // If necessary, constrain destructors in the unadjusted form of this - // expression. - self.check_safety_of_rvalue_destructor_if_necessary(&place, expr.span); - - for adjustment in adjustments { - debug!("constrain_adjustments: adjustment={:?}, place={:?}", adjustment, place); - - if let adjustment::Adjust::Deref(Some(deref)) = adjustment.kind { - self.link_region( - expr.span, - deref.region, - ty::BorrowKind::from_mutbl(deref.mutbl), - &place, - ); - } - - if let adjustment::Adjust::Borrow(ref autoref) = adjustment.kind { - self.link_autoref(expr, &place, autoref); - } - - place = self.with_mc(|mc| mc.cat_expr_adjusted(expr, place, adjustment))?; - } - - Ok(place) - } - - fn check_safety_of_rvalue_destructor_if_necessary( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - span: Span, - ) { - if let PlaceBase::Rvalue = place_with_id.place.base { - if place_with_id.place.projections.is_empty() { - let typ = self.resolve_type(place_with_id.place.ty()); - let body_id = self.body_id; - dropck::check_drop_obligations(self, typ, span, body_id); - } - } - } - /// Adds constraints to inference such that `T: 'a` holds (or - /// reports an error if it cannot). - /// - /// # Parameters - /// - /// - `origin`, the reason we need this constraint - /// - `ty`, the type `T` - /// - `region`, the region `'a` - pub fn type_must_outlive( - &self, - origin: infer::SubregionOrigin<'tcx>, - ty: Ty<'tcx>, - region: ty::Region<'tcx>, - ) { - self.infcx.register_region_obligation( - self.body_id, - RegionObligation { sub_region: region, sup_type: ty, origin }, - ); - } - - /// Computes the guarantor for an expression `&base` and then ensures that the lifetime of the - /// resulting pointer is linked to the lifetime of its guarantor (if any). - fn link_addr_of( - &mut self, - expr: &hir::Expr<'_>, - mutability: hir::Mutability, - base: &hir::Expr<'_>, - ) { - debug!("link_addr_of(expr={:?}, base={:?})", expr, base); - - let cmt = ignore_err!(self.with_mc(|mc| mc.cat_expr(base))); - - debug!("link_addr_of: cmt={:?}", cmt); - - self.link_region_from_node_type(expr.span, expr.hir_id, mutability, &cmt); - } - - /// Computes the guarantors for any ref bindings in a `let` and - /// then ensures that the lifetime of the resulting pointer is - /// linked to the lifetime of the initialization expression. - fn link_local(&self, local: &hir::Local<'_>) { - debug!("regionck::for_local()"); - let init_expr = match local.init { - None => { - return; - } - Some(expr) => &*expr, - }; - let discr_cmt = ignore_err!(self.with_mc(|mc| mc.cat_expr(init_expr))); - self.link_pattern(discr_cmt, local.pat); - } - - /// Computes the guarantors for any ref bindings in a match and - /// then ensures that the lifetime of the resulting pointer is - /// linked to the lifetime of its guarantor (if any). - fn link_match(&self, discr: &hir::Expr<'_>, arms: &[hir::Arm<'_>]) { - debug!("regionck::for_match()"); - let discr_cmt = ignore_err!(self.with_mc(|mc| mc.cat_expr(discr))); - debug!("discr_cmt={:?}", discr_cmt); - for arm in arms { - self.link_pattern(discr_cmt.clone(), arm.pat); - } - } - - /// Computes the guarantors for any ref bindings in a match and - /// then ensures that the lifetime of the resulting pointer is - /// linked to the lifetime of its guarantor (if any). - fn link_fn_params(&self, params: &[hir::Param<'_>]) { - for param in params { - let param_ty = self.node_ty(param.hir_id); - let param_cmt = - self.with_mc(|mc| mc.cat_rvalue(param.hir_id, param.pat.span, param_ty)); - debug!("param_ty={:?} param_cmt={:?} param={:?}", param_ty, param_cmt, param); - self.link_pattern(param_cmt, param.pat); - } - } - - /// Link lifetimes of any ref bindings in `root_pat` to the pointers found - /// in the discriminant, if needed. - fn link_pattern(&self, discr_cmt: PlaceWithHirId<'tcx>, root_pat: &hir::Pat<'_>) { - debug!("link_pattern(discr_cmt={:?}, root_pat={:?})", discr_cmt, root_pat); - ignore_err!(self.with_mc(|mc| { - mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, hir::Pat { kind, span, hir_id, .. }| { - // `ref x` pattern - if let PatKind::Binding(..) = kind - && let Some(ty::BindByReference(mutbl)) = mc.typeck_results.extract_binding_mode(self.tcx.sess, *hir_id, *span) { - self.link_region_from_node_type(*span, *hir_id, mutbl, sub_cmt); - } - }) - })); - } - - /// Link lifetime of borrowed pointer resulting from autoref to lifetimes in the value being - /// autoref'd. - fn link_autoref( - &self, - expr: &hir::Expr<'_>, - expr_cmt: &PlaceWithHirId<'tcx>, - autoref: &adjustment::AutoBorrow<'tcx>, - ) { - debug!("link_autoref(autoref={:?}, expr_cmt={:?})", autoref, expr_cmt); - - match *autoref { - adjustment::AutoBorrow::Ref(r, m) => { - self.link_region(expr.span, r, ty::BorrowKind::from_mutbl(m.into()), expr_cmt); - } - - adjustment::AutoBorrow::RawPtr(_) => {} - } - } - - /// Like `link_region()`, except that the region is extracted from the type of `id`, - /// which must be some reference (`&T`, `&str`, etc). - fn link_region_from_node_type( - &self, - span: Span, - id: hir::HirId, - mutbl: hir::Mutability, - cmt_borrowed: &PlaceWithHirId<'tcx>, - ) { - debug!( - "link_region_from_node_type(id={:?}, mutbl={:?}, cmt_borrowed={:?})", - id, mutbl, cmt_borrowed - ); - - let rptr_ty = self.resolve_node_type(id); - if let ty::Ref(r, _, _) = rptr_ty.kind() { - debug!("rptr_ty={}", rptr_ty); - self.link_region(span, *r, ty::BorrowKind::from_mutbl(mutbl), cmt_borrowed); - } - } - - /// Informs the inference engine that `borrow_cmt` is being borrowed with - /// kind `borrow_kind` and lifetime `borrow_region`. - /// In order to ensure borrowck is satisfied, this may create constraints - /// between regions, as explained in `link_reborrowed_region()`. - fn link_region( - &self, - span: Span, - borrow_region: ty::Region<'tcx>, - borrow_kind: ty::BorrowKind, - borrow_place: &PlaceWithHirId<'tcx>, - ) { - let origin = infer::DataBorrowed(borrow_place.place.ty(), span); - self.type_must_outlive(origin, borrow_place.place.ty(), borrow_region); - - for pointer_ty in borrow_place.place.deref_tys() { - debug!( - "link_region(borrow_region={:?}, borrow_kind={:?}, pointer_ty={:?})", - borrow_region, borrow_kind, borrow_place - ); - match *pointer_ty.kind() { - ty::RawPtr(_) => return, - ty::Ref(ref_region, _, ref_mutability) => { - if self.link_reborrowed_region(span, borrow_region, ref_region, ref_mutability) - { - return; - } - } - _ => assert!(pointer_ty.is_box(), "unexpected built-in deref type {}", pointer_ty), - } - } - if let PlaceBase::Upvar(upvar_id) = borrow_place.place.base { - self.link_upvar_region(span, borrow_region, upvar_id); - } - } - - /// This is the most complicated case: the path being borrowed is - /// itself the referent of a borrowed pointer. Let me give an - /// example fragment of code to make clear(er) the situation: - /// - /// ```ignore (incomplete Rust code) - /// let r: &'a mut T = ...; // the original reference "r" has lifetime 'a - /// ... - /// &'z *r // the reborrow has lifetime 'z - /// ``` - /// - /// Now, in this case, our primary job is to add the inference - /// constraint that `'z <= 'a`. Given this setup, let's clarify the - /// parameters in (roughly) terms of the example: - /// - /// ```plain,ignore (pseudo-Rust) - /// A borrow of: `& 'z bk * r` where `r` has type `& 'a bk T` - /// borrow_region ^~ ref_region ^~ - /// borrow_kind ^~ ref_kind ^~ - /// ref_cmt ^ - /// ``` - /// - /// Here `bk` stands for some borrow-kind (e.g., `mut`, `uniq`, etc). - /// - /// There is a complication beyond the simple scenario I just painted: there - /// may in fact be more levels of reborrowing. In the example, I said the - /// borrow was like `&'z *r`, but it might in fact be a borrow like - /// `&'z **q` where `q` has type `&'a &'b mut T`. In that case, we want to - /// ensure that `'z <= 'a` and `'z <= 'b`. - /// - /// The return value of this function indicates whether we *don't* need to - /// the recurse to the next reference up. - /// - /// This is explained more below. - fn link_reborrowed_region( - &self, - span: Span, - borrow_region: ty::Region<'tcx>, - ref_region: ty::Region<'tcx>, - ref_mutability: hir::Mutability, - ) -> bool { - debug!("link_reborrowed_region: {:?} <= {:?}", borrow_region, ref_region); - self.sub_regions(infer::Reborrow(span), borrow_region, ref_region); - - // Decide whether we need to recurse and link any regions within - // the `ref_cmt`. This is concerned for the case where the value - // being reborrowed is in fact a borrowed pointer found within - // another borrowed pointer. For example: - // - // let p: &'b &'a mut T = ...; - // ... - // &'z **p - // - // What makes this case particularly tricky is that, if the data - // being borrowed is a `&mut` or `&uniq` borrow, borrowck requires - // not only that `'z <= 'a`, (as before) but also `'z <= 'b` - // (otherwise the user might mutate through the `&mut T` reference - // after `'b` expires and invalidate the borrow we are looking at - // now). - // - // So let's re-examine our parameters in light of this more - // complicated (possible) scenario: - // - // A borrow of: `& 'z bk * * p` where `p` has type `&'b bk & 'a bk T` - // borrow_region ^~ ref_region ^~ - // borrow_kind ^~ ref_kind ^~ - // ref_cmt ^~~ - // - // (Note that since we have not examined `ref_cmt.cat`, we don't - // know whether this scenario has occurred; but I wanted to show - // how all the types get adjusted.) - match ref_mutability { - hir::Mutability::Not => { - // The reference being reborrowed is a shareable ref of - // type `&'a T`. In this case, it doesn't matter where we - // *found* the `&T` pointer, the memory it references will - // be valid and immutable for `'a`. So we can stop here. - true - } - - hir::Mutability::Mut => { - // The reference being reborrowed is either an `&mut T`. This is - // the case where recursion is needed. - false - } - } - } - - /// An upvar may be behind up to 2 references: - /// - /// * One can come from the reference to a "by-reference" upvar. - /// * Another one can come from the reference to the closure itself if it's - /// a `FnMut` or `Fn` closure. - /// - /// This function links the lifetimes of those references to the lifetime - /// of the borrow that's provided. See [RegionCtxt::link_reborrowed_region] for some - /// more explanation of this in the general case. - /// - /// We also supply a *cause*, and in this case we set the cause to - /// indicate that the reference being "reborrowed" is itself an upvar. This - /// provides a nicer error message should something go wrong. - fn link_upvar_region( - &self, - span: Span, - borrow_region: ty::Region<'tcx>, - upvar_id: ty::UpvarId, - ) { - debug!("link_upvar_region(borrorw_region={:?}, upvar_id={:?}", borrow_region, upvar_id); - // A by-reference upvar can't be borrowed for longer than the - // upvar is borrowed from the environment. - let closure_local_def_id = upvar_id.closure_expr_id; - let mut all_captures_are_imm_borrow = true; - for captured_place in self - .typeck_results - .borrow() - .closure_min_captures - .get(&closure_local_def_id.to_def_id()) - .and_then(|root_var_min_cap| root_var_min_cap.get(&upvar_id.var_path.hir_id)) - .into_iter() - .flatten() - { - match captured_place.info.capture_kind { - ty::UpvarCapture::ByRef(upvar_borrow) => { - self.sub_regions( - infer::ReborrowUpvar(span, upvar_id), - borrow_region, - captured_place.region.unwrap(), - ); - if let ty::ImmBorrow = upvar_borrow { - debug!("link_upvar_region: capture by shared ref"); - } else { - all_captures_are_imm_borrow = false; - } - } - ty::UpvarCapture::ByValue => { - all_captures_are_imm_borrow = false; - } - } - } - if all_captures_are_imm_borrow { - return; - } - let fn_hir_id = self.tcx.hir().local_def_id_to_hir_id(closure_local_def_id); - let ty = self.resolve_node_type(fn_hir_id); - debug!("link_upvar_region: ty={:?}", ty); - - // A closure capture can't be borrowed for longer than the - // reference to the closure. - if let ty::Closure(_, substs) = ty.kind() { - match self.infcx.closure_kind(substs) { - Some(ty::ClosureKind::Fn | ty::ClosureKind::FnMut) => { - // Region of environment pointer - let env_region = self.tcx.mk_region(ty::ReFree(ty::FreeRegion { - scope: upvar_id.closure_expr_id.to_def_id(), - bound_region: ty::BrEnv, - })); - self.sub_regions( - infer::ReborrowUpvar(span, upvar_id), - borrow_region, - env_region, - ); - } - Some(ty::ClosureKind::FnOnce) => {} - None => { - span_bug!(span, "Have not inferred closure kind before regionck"); - } - } - } - } -} diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 7fe36781cf4..84a8cc431f4 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst}; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ self, AdtKind, DefIdTree, EarlyBinder, GenericParamDefKind, ToPredicate, Ty, TyCtxt, - TypeFoldable, TypeSuperFoldable, TypeVisitor, + TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident, Symbol}; @@ -62,7 +62,10 @@ impl<'tcx> CheckWfFcxBuilder<'tcx> { } let wf_tys = f(&fcx); fcx.select_all_obligations_or_error(); - fcx.regionck_item(id, span, wf_tys); + + let mut outlives_environment = OutlivesEnvironment::new(param_env); + outlives_environment.add_implied_bounds(&fcx.infcx, wf_tys, id); + fcx.infcx.check_region_obligations_and_report_errors(&outlives_environment); }); } } @@ -655,13 +658,12 @@ fn resolve_regions_with_wf_tys<'tcx>( // call individually. tcx.infer_ctxt().enter(|infcx| { let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP); - outlives_environment.save_implied_bounds(id); - let region_bound_pairs = outlives_environment.region_bound_pairs_map().get(&id).unwrap(); + outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id); + let region_bound_pairs = outlives_environment.region_bound_pairs(); add_constraints(&infcx, region_bound_pairs); - let errors = infcx.resolve_regions(id.expect_owner().to_def_id(), &outlives_environment); + let errors = infcx.resolve_regions(&outlives_environment); debug!(?errors, "errors"); @@ -1367,7 +1369,7 @@ fn check_where_clauses<'tcx, 'fcx>( struct CountParams { params: FxHashSet<u32>, } - impl<'tcx> ty::fold::TypeVisitor<'tcx> for CountParams { + impl<'tcx> ty::visit::TypeVisitor<'tcx> for CountParams { type BreakTy = (); fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index 67160b98b9d..41d241f84ac 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -15,6 +15,7 @@ use rustc_middle::hir::place::Place as HirPlace; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable}; use rustc_middle::ty::{self, ClosureSizeProfileData, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -692,7 +693,6 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { Some(self.body.id()), self.span.to_span(self.tcx), t.into(), - vec![], E0282, false, ) @@ -707,7 +707,6 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { Some(self.body.id()), self.span.to_span(self.tcx), c.into(), - vec![], E0282, false, ) diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs index c647c2a4c1b..d532050d050 100644 --- a/compiler/rustc_typeck/src/coherence/builtin.rs +++ b/compiler/rustc_typeck/src/coherence/builtin.rs @@ -11,7 +11,7 @@ use rustc_infer::infer; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::adjustment::CoerceUnsizedInfo; -use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitable}; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError}; use rustc_trait_selection::traits::predicate_for_trait_def; @@ -349,7 +349,7 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: // Finally, resolve all regions. let outlives_env = OutlivesEnvironment::new(param_env); - infcx.resolve_regions_and_report_errors(impl_did.to_def_id(), &outlives_env); + infcx.resolve_regions_and_report_errors(&outlives_env); } } _ => { @@ -606,7 +606,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn // Finally, resolve all regions. let outlives_env = OutlivesEnvironment::new(param_env); - infcx.resolve_regions_and_report_errors(impl_did.to_def_id(), &outlives_env); + infcx.resolve_regions_and_report_errors(&outlives_env); CoerceUnsizedInfo { custom_kind: kind } }) diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_typeck/src/coherence/mod.rs index 447ec87f302..ae9ebe59091 100644 --- a/compiler/rustc_typeck/src/coherence/mod.rs +++ b/compiler/rustc_typeck/src/coherence/mod.rs @@ -8,8 +8,7 @@ use rustc_errors::struct_span_err; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; -use rustc_span::Span; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; use rustc_trait_selection::traits; mod builtin; @@ -18,11 +17,6 @@ mod inherent_impls_overlap; mod orphan; mod unsafety; -/// Obtains the span of just the impl header of `impl_def_id`. -fn impl_header_span(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) -> Span { - tcx.sess.source_map().guess_head_span(tcx.span_of_impl(impl_def_id.to_def_id()).unwrap()) -} - fn check_impl(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_ref: ty::TraitRef<'_>) { debug!( "(checking implementation) adding impl for trait '{:?}', item '{}'", @@ -47,56 +41,53 @@ fn enforce_trait_manually_implementable( ) { let did = Some(trait_def_id); let li = tcx.lang_items(); + let impl_header_span = tcx.def_span(impl_def_id); // Disallow *all* explicit impls of `Pointee`, `DiscriminantKind`, `Sized` and `Unsize` for now. if did == li.pointee_trait() { - let span = impl_header_span(tcx, impl_def_id); struct_span_err!( tcx.sess, - span, + impl_header_span, E0322, "explicit impls for the `Pointee` trait are not permitted" ) - .span_label(span, "impl of `Pointee` not allowed") + .span_label(impl_header_span, "impl of `Pointee` not allowed") .emit(); return; } if did == li.discriminant_kind_trait() { - let span = impl_header_span(tcx, impl_def_id); struct_span_err!( tcx.sess, - span, + impl_header_span, E0322, "explicit impls for the `DiscriminantKind` trait are not permitted" ) - .span_label(span, "impl of `DiscriminantKind` not allowed") + .span_label(impl_header_span, "impl of `DiscriminantKind` not allowed") .emit(); return; } if did == li.sized_trait() { - let span = impl_header_span(tcx, impl_def_id); struct_span_err!( tcx.sess, - span, + impl_header_span, E0322, "explicit impls for the `Sized` trait are not permitted" ) - .span_label(span, "impl of `Sized` not allowed") + .span_label(impl_header_span, "impl of `Sized` not allowed") .emit(); return; } if did == li.unsize_trait() { - let span = impl_header_span(tcx, impl_def_id); struct_span_err!( tcx.sess, - span, + impl_header_span, E0328, "explicit impls for the `Unsize` trait are not permitted" ) - .span_label(span, "impl of `Unsize` not allowed") + .span_label(impl_header_span, "impl of `Unsize` not allowed") .emit(); return; } @@ -110,10 +101,9 @@ fn enforce_trait_manually_implementable( tcx.trait_def(trait_def_id).specialization_kind { if !tcx.features().specialization && !tcx.features().min_specialization { - let span = impl_header_span(tcx, impl_def_id); tcx.sess .struct_span_err( - span, + impl_header_span, "implementing `rustc_specialization_trait` traits is unstable", ) .help("add `#![feature(min_specialization)]` to the crate attributes to enable") @@ -138,8 +128,13 @@ fn enforce_empty_impls_for_marker_traits( return; } - let span = impl_header_span(tcx, impl_def_id); - struct_span_err!(tcx.sess, span, E0715, "impls for marker traits cannot contain items").emit(); + struct_span_err!( + tcx.sess, + tcx.def_span(impl_def_id), + E0715, + "impls for marker traits cannot contain items" + ) + .emit(); } pub fn provide(providers: &mut Providers) { @@ -217,7 +212,7 @@ fn check_object_overlap<'tcx>( } else { let mut supertrait_def_ids = traits::supertrait_def_ids(tcx, component_def_id); if supertrait_def_ids.any(|d| d == trait_def_id) { - let span = impl_header_span(tcx, impl_def_id); + let span = tcx.def_span(impl_def_id); struct_span_err!( tcx.sess, span, diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs index f3a043a08a3..697ef7bc022 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_typeck/src/coherence/orphan.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::util::IgnoreRegions; use rustc_middle::ty::{ - self, ImplPolarity, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor, + self, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use rustc_session::lint; use rustc_span::def_id::{DefId, LocalDefId}; @@ -47,7 +47,7 @@ fn do_orphan_check_impl<'tcx>( let hir::ItemKind::Impl(ref impl_) = item.kind else { bug!("{:?} is not an impl: {:?}", def_id, item); }; - let sp = tcx.sess.source_map().guess_head_span(item.span); + let sp = tcx.def_span(def_id); let tr = impl_.of_trait.as_ref().unwrap(); // Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples, diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index 6ee2b544916..f942a4fb53a 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -8,7 +8,7 @@ use rustc_hir::{HirId, Node}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable}; use rustc_span::symbol::Ident; use rustc_span::{Span, DUMMY_SP}; diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_typeck/src/constrained_generic_params.rs index 858cf63390a..8428e466406 100644 --- a/compiler/rustc_typeck/src/constrained_generic_params.rs +++ b/compiler/rustc_typeck/src/constrained_generic_params.rs @@ -1,5 +1,5 @@ use rustc_data_structures::fx::FxHashSet; -use rustc_middle::ty::fold::{TypeFoldable, TypeSuperFoldable, TypeVisitor}; +use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::source_map::Span; use std::ops::ControlFlow; @@ -43,7 +43,7 @@ pub fn parameters_for_impl<'tcx>( /// of parameters whose values are needed in order to constrain `ty` - these /// differ, with the latter being a superset, in the presence of projections. pub fn parameters_for<'tcx>( - t: &impl TypeFoldable<'tcx>, + t: &impl TypeVisitable<'tcx>, include_nonconstraining: bool, ) -> Vec<Parameter> { let mut collector = ParameterCollector { parameters: vec![], include_nonconstraining }; diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs index 981c35e184b..e7ca70de4ba 100644 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ b/compiler/rustc_typeck/src/impl_wf_check.rs @@ -16,7 +16,7 @@ use rustc_errors::struct_span_err; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; use rustc_span::Span; use std::collections::hash_map::Entry::{Occupied, Vacant}; diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs index f07396ce74f..c46b825f457 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -75,7 +75,7 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::specialization_graph::Node; use rustc_middle::ty::subst::{GenericArg, InternalSubsts, SubstsRef}; use rustc_middle::ty::trait_def::TraitSpecializationKind; -use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; use rustc_span::Span; use rustc_trait_selection::traits::{self, translate_substs, wf}; @@ -150,7 +150,7 @@ fn get_impl_substs<'tcx>( // Conservatively use an empty `ParamEnv`. let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty()); - infcx.resolve_regions_and_report_errors(impl1_def_id.to_def_id(), &outlives_env); + infcx.resolve_regions_and_report_errors(&outlives_env); let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else { let span = tcx.def_span(impl1_def_id); tcx.sess.emit_err(SubstsOnOverriddenImpl { span }); @@ -279,11 +279,16 @@ fn check_predicates<'tcx>( span: Span, ) { let tcx = infcx.tcx; - let impl1_predicates: Vec<_> = traits::elaborate_predicates( + let instantiated = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs); + let impl1_predicates: Vec<_> = traits::elaborate_predicates_with_span( tcx, - tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs).predicates.into_iter(), + std::iter::zip( + instantiated.predicates, + // Don't drop predicates (unsound!) because `spans` is too short + instantiated.spans.into_iter().chain(std::iter::repeat(span)), + ), ) - .map(|obligation| obligation.predicate) + .map(|obligation| (obligation.predicate, obligation.cause.span)) .collect(); let mut impl2_predicates = if impl2_node.is_from_trait() { @@ -321,7 +326,7 @@ fn check_predicates<'tcx>( // which is sound because we forbid impls like the following // // impl<D: Debug> AlwaysApplicable for D { } - let always_applicable_traits = impl1_predicates.iter().copied().filter(|&predicate| { + let always_applicable_traits = impl1_predicates.iter().copied().filter(|&(predicate, _)| { matches!( trait_predicate_kind(tcx, predicate), Some(TraitSpecializationKind::AlwaysApplicable) @@ -345,11 +350,11 @@ fn check_predicates<'tcx>( } } impl2_predicates.extend( - traits::elaborate_predicates(tcx, always_applicable_traits) + traits::elaborate_predicates_with_span(tcx, always_applicable_traits) .map(|obligation| obligation.predicate), ); - for predicate in impl1_predicates { + for (predicate, span) in impl1_predicates { if !impl2_predicates.contains(&predicate) { check_specialization_on(tcx, predicate, span) } @@ -384,9 +389,17 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc .emit(); } } + ty::PredicateKind::Projection(ty::ProjectionPredicate { projection_ty, term }) => { + tcx.sess + .struct_span_err( + span, + &format!("cannot specialize on associated type `{projection_ty} == {term}`",), + ) + .emit(); + } _ => { tcx.sess - .struct_span_err(span, &format!("cannot specialize on `{:?}`", predicate)) + .struct_span_err(span, &format!("cannot specialize on predicate `{}`", predicate)) .emit(); } } diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index b6d4f5fcda6..08c194ec0b6 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -223,15 +223,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { if !def_id.is_local() { return None; } - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - match tcx.hir().find(hir_id) { - Some(Node::Item(hir::Item { span: item_span, .. })) => { - Some(tcx.sess.source_map().guess_head_span(*item_span)) - } - _ => { - span_bug!(tcx.def_span(def_id), "main has a non-function type"); - } - } + Some(tcx.def_span(def_id)) } fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> { @@ -416,7 +408,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { error = true; } if let hir::IsAsync::Async = sig.header.asyncness { - let span = tcx.sess.source_map().guess_head_span(it.span); + let span = tcx.def_span(it.def_id); struct_span_err!( tcx.sess, span, diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs index 9acae8d79e7..ced919f66db 100644 --- a/compiler/rustc_typeck/src/mem_categorization.rs +++ b/compiler/rustc_typeck/src/mem_categorization.rs @@ -51,6 +51,7 @@ use rustc_middle::hir::place::*; use rustc_middle::ty::adjustment; use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_data_structures::fx::FxIndexMap; diff --git a/compiler/rustc_typeck/src/outlives/outlives_bounds.rs b/compiler/rustc_typeck/src/outlives/outlives_bounds.rs index 3bf697e7682..70b8bcd0220 100644 --- a/compiler/rustc_typeck/src/outlives/outlives_bounds.rs +++ b/compiler/rustc_typeck/src/outlives/outlives_bounds.rs @@ -1,6 +1,5 @@ use rustc_hir as hir; use rustc_middle::ty::{self, Ty}; -use rustc_span::source_map::Span; use rustc_trait_selection::infer::InferCtxt; use rustc_trait_selection::traits::query::type_op::{self, TypeOp, TypeOpOutput}; use rustc_trait_selection::traits::query::NoSolution; @@ -14,7 +13,6 @@ pub trait InferCtxtExt<'tcx> { param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, ty: Ty<'tcx>, - span: Span, ) -> Vec<OutlivesBound<'tcx>>; } @@ -38,16 +36,14 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { /// Note that this may cause outlives obligations to be injected /// into the inference context with this body-id. /// - `ty`, the type that we are supposed to assume is WF. - /// - `span`, a span to use when normalizing, hopefully not important, - /// might be useful if a `bug!` occurs. - #[instrument(level = "debug", skip(self, param_env, body_id, span))] + #[instrument(level = "debug", skip(self, param_env, body_id))] fn implied_outlives_bounds( &self, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, ty: Ty<'tcx>, - span: Span, ) -> Vec<OutlivesBound<'tcx>> { + let span = self.tcx.hir().span(body_id); let result = param_env .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty }) .fully_perform(self); diff --git a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs b/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs index bafc5a0b918..324df313ef1 100644 --- a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs +++ b/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs @@ -1,6 +1,6 @@ use crate::structured_errors::StructuredDiagnostic; use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed}; -use rustc_middle::ty::{Ty, TypeFoldable}; +use rustc_middle::ty::{Ty, TypeVisitable}; use rustc_session::Session; use rustc_span::Span; diff --git a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs b/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs index afc5c1fe6cc..bb608805488 100644 --- a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs +++ b/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs @@ -1,6 +1,6 @@ use crate::structured_errors::StructuredDiagnostic; use rustc_errors::{DiagnosticBuilder, DiagnosticId, ErrorGuaranteed}; -use rustc_middle::ty::{Ty, TypeFoldable}; +use rustc_middle::ty::{Ty, TypeVisitable}; use rustc_session::Session; use rustc_span::Span; diff --git a/compiler/rustc_typeck/src/variance/constraints.rs b/compiler/rustc_typeck/src/variance/constraints.rs index a7dcbfff207..d79450e1ae7 100644 --- a/compiler/rustc_typeck/src/variance/constraints.rs +++ b/compiler/rustc_typeck/src/variance/constraints.rs @@ -64,74 +64,28 @@ pub fn add_constraints_from_crate<'a, 'tcx>( let crate_items = tcx.hir_crate_items(()); - for id in crate_items.items() { - constraint_cx.check_item(id); - } - - for id in crate_items.trait_items() { - if let DefKind::AssocFn = tcx.def_kind(id.def_id) { - constraint_cx.check_node_helper(id.hir_id()); - } - } - - for id in crate_items.impl_items() { - if let DefKind::AssocFn = tcx.def_kind(id.def_id) { - constraint_cx.check_node_helper(id.hir_id()); - } - } - - for id in crate_items.foreign_items() { - if let DefKind::Fn = tcx.def_kind(id.def_id) { - constraint_cx.check_node_helper(id.hir_id()); - } - } - - constraint_cx -} - -impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { - fn check_item(&mut self, id: hir::ItemId) { - let def_kind = self.tcx().def_kind(id.def_id); + for def_id in crate_items.definitions() { + let def_kind = tcx.def_kind(def_id); match def_kind { - DefKind::Struct | DefKind::Union => { - let item = self.tcx().hir().item(id); - - if let hir::ItemKind::Struct(ref struct_def, _) - | hir::ItemKind::Union(ref struct_def, _) = item.kind - { - self.check_node_helper(item.hir_id()); + DefKind::Struct | DefKind::Union | DefKind::Enum => { + constraint_cx.build_constraints_for_item(def_id); - if let hir::VariantData::Tuple(..) = *struct_def { - self.check_node_helper(struct_def.ctor_hir_id().unwrap()); + let adt = tcx.adt_def(def_id); + for variant in adt.variants() { + if let Some(ctor) = variant.ctor_def_id { + constraint_cx.build_constraints_for_item(ctor.expect_local()); } } } - DefKind::Enum => { - let item = self.tcx().hir().item(id); - - if let hir::ItemKind::Enum(ref enum_def, _) = item.kind { - self.check_node_helper(item.hir_id()); - - for variant in enum_def.variants { - if let hir::VariantData::Tuple(..) = variant.data { - self.check_node_helper(variant.data.ctor_hir_id().unwrap()); - } - } - } - } - DefKind::Fn => { - self.check_node_helper(id.hir_id()); - } + DefKind::Fn | DefKind::AssocFn => constraint_cx.build_constraints_for_item(def_id), _ => {} } } - fn check_node_helper(&mut self, id: hir::HirId) { - let tcx = self.terms_cx.tcx; - let def_id = tcx.hir().local_def_id(id); - self.build_constraints_for_item(def_id); - } + constraint_cx +} +impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.terms_cx.tcx } @@ -145,8 +99,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { return; } - let id = tcx.hir().local_def_id_to_hir_id(def_id); - let inferred_start = self.terms_cx.inferred_starts[&id]; + let inferred_start = self.terms_cx.inferred_starts[&def_id]; let current_item = &CurrentItem { inferred_start }; match tcx.type_of(def_id).kind() { ty::Adt(def, _) => { @@ -372,8 +325,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } let (local, remote) = if let Some(def_id) = def_id.as_local() { - let id = self.tcx().hir().local_def_id_to_hir_id(def_id); - (Some(self.terms_cx.inferred_starts[&id]), None) + (Some(self.terms_cx.inferred_starts[&def_id]), None) } else { (None, Some(self.tcx().variances_of(def_id))) }; diff --git a/compiler/rustc_typeck/src/variance/solve.rs b/compiler/rustc_typeck/src/variance/solve.rs index 1a4d88ced0e..97aca621aa2 100644 --- a/compiler/rustc_typeck/src/variance/solve.rs +++ b/compiler/rustc_typeck/src/variance/solve.rs @@ -96,8 +96,7 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> { self.terms_cx .inferred_starts .iter() - .map(|(&id, &InferredIndex(start))| { - let def_id = tcx.hir().local_def_id(id); + .map(|(&def_id, &InferredIndex(start))| { let generics = tcx.generics_of(def_id); let count = generics.count(); diff --git a/compiler/rustc_typeck/src/variance/terms.rs b/compiler/rustc_typeck/src/variance/terms.rs index ab64befe5dc..1f763011e06 100644 --- a/compiler/rustc_typeck/src/variance/terms.rs +++ b/compiler/rustc_typeck/src/variance/terms.rs @@ -10,9 +10,8 @@ // a variable. use rustc_arena::DroplessArena; -use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_hir::HirIdMap; +use rustc_hir::def_id::{LocalDefId, LocalDefIdMap}; use rustc_middle::ty::{self, TyCtxt}; use std::fmt; @@ -52,11 +51,11 @@ pub struct TermsContext<'a, 'tcx> { // For marker types, UnsafeCell, and other lang items where // variance is hardcoded, records the item-id and the hardcoded // variance. - pub lang_items: Vec<(hir::HirId, Vec<ty::Variance>)>, + pub lang_items: Vec<(LocalDefId, Vec<ty::Variance>)>, // Maps from the node id of an item to the first inferred index // used for its type & region parameters. - pub inferred_starts: HirIdMap<InferredIndex>, + pub inferred_starts: LocalDefIdMap<InferredIndex>, // Maps from an InferredIndex to the term for that variable. pub inferred_terms: Vec<VarianceTermPtr<'a>>, @@ -81,32 +80,31 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>( // - https://rustc-dev-guide.rust-lang.org/variance.html let crate_items = tcx.hir_crate_items(()); - for id in crate_items.items() { - terms_cx.check_item(id); - } + for def_id in crate_items.definitions() { + debug!("add_inferreds for item {:?}", def_id); - for id in crate_items.trait_items() { - if let DefKind::AssocFn = tcx.def_kind(id.def_id) { - terms_cx.add_inferreds_for_item(id.hir_id()); - } - } + let def_kind = tcx.def_kind(def_id); - for id in crate_items.impl_items() { - if let DefKind::AssocFn = tcx.def_kind(id.def_id) { - terms_cx.add_inferreds_for_item(id.hir_id()); - } - } + match def_kind { + DefKind::Struct | DefKind::Union | DefKind::Enum => { + terms_cx.add_inferreds_for_item(def_id); - for id in crate_items.foreign_items() { - if let DefKind::Fn = tcx.def_kind(id.def_id) { - terms_cx.add_inferreds_for_item(id.hir_id()); + let adt = tcx.adt_def(def_id); + for variant in adt.variants() { + if let Some(ctor) = variant.ctor_def_id { + terms_cx.add_inferreds_for_item(ctor.expect_local()); + } + } + } + DefKind::Fn | DefKind::AssocFn => terms_cx.add_inferreds_for_item(def_id), + _ => {} } } terms_cx } -fn lang_items(tcx: TyCtxt<'_>) -> Vec<(hir::HirId, Vec<ty::Variance>)> { +fn lang_items(tcx: TyCtxt<'_>) -> Vec<(LocalDefId, Vec<ty::Variance>)> { let lang_items = tcx.lang_items(); let all = [ (lang_items.phantom_data(), vec![ty::Covariant]), @@ -114,18 +112,16 @@ fn lang_items(tcx: TyCtxt<'_>) -> Vec<(hir::HirId, Vec<ty::Variance>)> { ]; all.into_iter() // iterating over (Option<DefId>, Variance) - .filter(|&(ref d, _)| d.is_some()) - .map(|(d, v)| (d.unwrap(), v)) // (DefId, Variance) .filter_map(|(d, v)| { - d.as_local().map(|d| tcx.hir().local_def_id_to_hir_id(d)).map(|n| (n, v)) - }) // (HirId, Variance) + let def_id = d?.as_local()?; // LocalDefId + Some((def_id, v)) + }) .collect() } impl<'a, 'tcx> TermsContext<'a, 'tcx> { - fn add_inferreds_for_item(&mut self, id: hir::HirId) { + fn add_inferreds_for_item(&mut self, def_id: LocalDefId) { let tcx = self.tcx; - let def_id = tcx.hir().local_def_id(id); let count = tcx.generics_of(def_id).count(); if count == 0 { @@ -134,7 +130,7 @@ impl<'a, 'tcx> TermsContext<'a, 'tcx> { // Record the start of this item's inferreds. let start = self.inferred_terms.len(); - let newly_added = self.inferred_starts.insert(id, InferredIndex(start)).is_none(); + let newly_added = self.inferred_starts.insert(def_id, InferredIndex(start)).is_none(); assert!(newly_added); // N.B., in the code below for writing the results back into the @@ -146,42 +142,4 @@ impl<'a, 'tcx> TermsContext<'a, 'tcx> { (start..(start + count)).map(|i| &*arena.alloc(InferredTerm(InferredIndex(i)))), ); } - - fn check_item(&mut self, id: hir::ItemId) { - debug!("add_inferreds for item {}", self.tcx.hir().node_to_string(id.hir_id())); - - let def_kind = self.tcx.def_kind(id.def_id); - match def_kind { - DefKind::Struct | DefKind::Union => { - let item = self.tcx.hir().item(id); - - if let hir::ItemKind::Struct(ref struct_def, _) - | hir::ItemKind::Union(ref struct_def, _) = item.kind - { - self.add_inferreds_for_item(item.hir_id()); - - if let hir::VariantData::Tuple(..) = *struct_def { - self.add_inferreds_for_item(struct_def.ctor_hir_id().unwrap()); - } - } - } - DefKind::Enum => { - let item = self.tcx.hir().item(id); - - if let hir::ItemKind::Enum(ref enum_def, _) = item.kind { - self.add_inferreds_for_item(item.hir_id()); - - for variant in enum_def.variants { - if let hir::VariantData::Tuple(..) = variant.data { - self.add_inferreds_for_item(variant.data.ctor_hir_id().unwrap()); - } - } - } - } - DefKind::Fn => { - self.add_inferreds_for_item(id.hir_id()); - } - _ => {} - } - } } |
