diff options
| author | bors <bors@rust-lang.org> | 2024-02-22 05:06:27 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-02-22 05:06:27 +0000 |
| commit | 3815fc06256f6281ee064fc607d24020dc96f05b (patch) | |
| tree | a9ec036cc3cb6e18d43f62e85574e81288b82cf0 /compiler | |
| parent | 596dd65cfa1d5f1a55a9a68d7f45bda436e3055d (diff) | |
| parent | 6f3bc7d938f5a8a772ba67cf41aad16a03b9fb1c (diff) | |
Auto merge of #3310 - rust-lang:rustup-2024-02-22, r=oli-obk
Automatic Rustup
Diffstat (limited to 'compiler')
234 files changed, 4579 insertions, 4971 deletions
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 5cb79d9eea5..bdbc59821de 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -95,7 +95,7 @@ impl<T> ArenaChunk<T> { unsafe { if mem::size_of::<T>() == 0 { // A pointer as large as possible for zero-sized elements. - ptr::invalid_mut(!0) + ptr::without_provenance_mut(!0) } else { self.start().add(self.storage.len()) } diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 274e6b7458c..834409da675 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -1,5 +1,6 @@ use rustc_errors::{ - codes::*, AddToDiagnostic, Diagnostic, DiagnosticArgFromDisplay, SubdiagnosticMessageOp, + codes::*, AddToDiagnostic, DiagnosticArgFromDisplay, DiagnosticBuilder, EmissionGuarantee, + SubdiagnosticMessageOp, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; @@ -41,7 +42,11 @@ pub struct InvalidAbi { pub struct InvalidAbiReason(pub &'static str); impl AddToDiagnostic for InvalidAbiReason { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _: F, + ) { #[allow(rustc::untranslatable_diagnostic)] diag.note(self.0); } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a5be91bb872..ef843da7307 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1636,9 +1636,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { if let Some(old_def_id) = self.orig_opt_local_def_id(param) { old_def_id } else { - self.dcx() - .span_delayed_bug(lifetime.ident.span, "no def-id for fresh lifetime"); - continue; + self.dcx().span_bug(lifetime.ident.span, "no def-id for fresh lifetime"); } } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index fa0f5326196..8c9ad836087 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -881,9 +881,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { &item.vis, errors::VisibilityNotPermittedNote::TraitImpl, ); - // njn: use Dummy here - if let TyKind::Err(_) = self_ty.kind { - this.dcx().emit_err(errors::ObsoleteAuto { span: item.span }); + if let TyKind::Dummy = self_ty.kind { + // Abort immediately otherwise the `TyKind::Dummy` will reach HIR lowering, + // which isn't allowed. Not a problem for this obscure, obsolete syntax. + this.dcx().emit_fatal(errors::ObsoleteAuto { span: item.span }); } if let (&Unsafe::Yes(span), &ImplPolarity::Negative(sp)) = (unsafety, polarity) { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 9662c73ca85..e5153c89790 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -1,7 +1,10 @@ //! Errors emitted by ast_passes. use rustc_ast::ParamKindOrd; -use rustc_errors::{codes::*, AddToDiagnostic, Applicability, Diagnostic, SubdiagnosticMessageOp}; +use rustc_errors::{ + codes::*, AddToDiagnostic, Applicability, DiagnosticBuilder, EmissionGuarantee, + SubdiagnosticMessageOp, +}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; @@ -372,7 +375,11 @@ pub struct EmptyLabelManySpans(pub Vec<Span>); // The derive for `Vec<Span>` does multiple calls to `span_label`, adding commas between each impl AddToDiagnostic for EmptyLabelManySpans { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _: F, + ) { diag.span_labels(self.0, ""); } } @@ -729,7 +736,11 @@ pub struct StableFeature { } impl AddToDiagnostic for StableFeature { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _: F, + ) { diag.arg("name", self.name); diag.arg("since", self.since); diag.help(fluent::ast_passes_stable_since); diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 409aef9185d..1b0dd9acc37 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -22,6 +22,9 @@ macro_rules! gate { }}; ($visitor:expr, $feature:ident, $span:expr, $explain:expr, $help:expr) => {{ if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) { + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] feature_err(&$visitor.sess, sym::$feature, $span, $explain).with_help($help).emit(); } }}; diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 2b0c0e939f5..e1509da913a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -6,9 +6,7 @@ use either::Either; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{ - codes::*, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan, -}; +use rustc_errors::{codes::*, struct_span_code_err, Applicability, DiagnosticBuilder, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; @@ -635,7 +633,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn suggest_assign_value( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, moved_place: PlaceRef<'tcx>, sugg_span: Span, ) { @@ -674,7 +672,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn suggest_borrow_fn_like( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ty: Ty<'tcx>, move_sites: &[MoveSite], value_name: &str, @@ -742,7 +740,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn suggest_cloning( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'_>, span: Span, @@ -778,7 +776,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) { + fn suggest_adding_copy_bounds( + &self, + err: &mut DiagnosticBuilder<'_>, + ty: Ty<'tcx>, + span: Span, + ) { let tcx = self.infcx.tcx; let generics = tcx.generics_of(self.mir_def_id()); @@ -1225,7 +1228,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { #[instrument(level = "debug", skip(self, err))] fn suggest_using_local_if_applicable( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, location: Location, issued_borrow: &BorrowData<'tcx>, explanation: BorrowExplanation<'tcx>, @@ -1321,7 +1324,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn suggest_slice_method_if_applicable( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, place: Place<'tcx>, borrowed_place: Place<'tcx>, ) { @@ -1430,7 +1433,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// ``` pub(crate) fn explain_iterator_advancement_in_for_loop_if_applicable( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, span: Span, issued_spans: &UseSpans<'tcx>, ) { @@ -1617,7 +1620,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// ``` fn suggest_using_closure_argument_instead_of_capture( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, borrowed_place: Place<'tcx>, issued_spans: &UseSpans<'tcx>, ) { @@ -1751,7 +1754,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn suggest_binding_for_closure_capture_self( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, issued_spans: &UseSpans<'tcx>, ) { let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return }; @@ -2997,7 +3000,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.buffer_error(err); } - fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) { + fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut DiagnosticBuilder<'_>) { let tcx = self.infcx.tcx; if let ( Some(Terminator { @@ -3532,7 +3535,11 @@ enum AnnotatedBorrowFnSignature<'tcx> { impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { /// Annotate the provided diagnostic with information about borrow from the fn signature that /// helps explain. - pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String { + pub(crate) fn emit( + &self, + cx: &mut MirBorrowckCtxt<'_, 'tcx>, + diag: &mut DiagnosticBuilder<'_>, + ) -> String { match self { &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => { diag.span_label( diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 483ecee850b..f7b59ec5fe0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -3,7 +3,7 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_index::IndexSlice; @@ -65,7 +65,7 @@ impl<'tcx> BorrowExplanation<'tcx> { tcx: TyCtxt<'tcx>, body: &Body<'tcx>, local_names: &IndexSlice<Local, Option<Symbol>>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, borrow_desc: &str, borrow_span: Option<Span>, multiple_borrow_span: Option<(Span, Span)>, @@ -306,7 +306,7 @@ impl<'tcx> BorrowExplanation<'tcx> { fn add_object_lifetime_default_note( &self, tcx: TyCtxt<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, unsize_ty: Ty<'tcx>, ) { if let ty::Adt(def, args) = unsize_ty.kind() { @@ -359,7 +359,7 @@ impl<'tcx> BorrowExplanation<'tcx> { fn add_lifetime_bound_suggestion_to_diagnostic( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, category: &ConstraintCategory<'tcx>, span: Span, region_name: &RegionName, diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 4ca854c857d..db0d69b6eaa 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -5,7 +5,7 @@ use crate::session_diagnostics::{ CaptureVarKind, CaptureVarPathUseCause, OnClosureNote, }; use itertools::Itertools; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::CoroutineKind; @@ -80,7 +80,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &self, location: Location, place: PlaceRef<'tcx>, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, ) -> bool { debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place); let mut target = place.local_or_deref_local(); @@ -588,7 +588,7 @@ impl UseSpans<'_> { pub(super) fn args_subdiag( self, dcx: &rustc_errors::DiagCtxt, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, f: impl FnOnce(Span) -> CaptureArgLabel, ) { if let UseSpans::ClosureUse { args_span, .. } = self { @@ -601,7 +601,7 @@ impl UseSpans<'_> { pub(super) fn var_path_only_subdiag( self, dcx: &rustc_errors::DiagCtxt, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, action: crate::InitializationRequiringAction, ) { use crate::InitializationRequiringAction::*; @@ -638,7 +638,7 @@ impl UseSpans<'_> { pub(super) fn var_subdiag( self, dcx: &rustc_errors::DiagCtxt, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, kind: Option<rustc_middle::mir::BorrowKind>, f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause, ) { @@ -1010,7 +1010,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn explain_captures( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, span: Span, move_span: Span, move_spans: UseSpans<'tcx>, diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index dad20690d02..3478a73254a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -1,7 +1,7 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty}; use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; @@ -437,7 +437,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { err } - fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diagnostic, span: Span) { + fn add_move_hints( + &self, + error: GroupedMoveError<'tcx>, + err: &mut DiagnosticBuilder<'_>, + span: Span, + ) { match error { GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => { self.add_borrow_suggestions(err, span); @@ -500,7 +505,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } - fn add_borrow_suggestions(&self, err: &mut Diagnostic, span: Span) { + fn add_borrow_suggestions(&self, err: &mut DiagnosticBuilder<'_>, span: Span) { match self.infcx.tcx.sess.source_map().span_to_snippet(span) { Ok(snippet) if snippet.starts_with('*') => { err.span_suggestion_verbose( @@ -521,7 +526,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } - fn add_move_error_suggestions(&self, err: &mut Diagnostic, binds_to: &[Local]) { + fn add_move_error_suggestions(&self, err: &mut DiagnosticBuilder<'_>, binds_to: &[Local]) { let mut suggestions: Vec<(Span, String, String)> = Vec::new(); for local in binds_to { let bind_to = &self.body.local_decls[*local]; @@ -573,7 +578,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } - fn add_move_error_details(&self, err: &mut Diagnostic, binds_to: &[Local]) { + fn add_move_error_details(&self, err: &mut DiagnosticBuilder<'_>, binds_to: &[Local]) { for (j, local) in binds_to.iter().enumerate() { let bind_to = &self.body.local_decls[*local]; let binding_span = bind_to.source_info.span; @@ -610,7 +615,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// expansion of a packed struct. /// Such errors happen because derive macro expansions shy away from taking /// references to the struct's fields since doing so would be undefined behaviour - fn add_note_for_packed_struct_derive(&self, err: &mut Diagnostic, local: Local) { + fn add_note_for_packed_struct_derive(&self, err: &mut DiagnosticBuilder<'_>, local: Local) { let local_place: PlaceRef<'tcx> = local.into(); let local_ty = local_place.ty(self.body.local_decls(), self.infcx.tcx).ty.peel_refs(); diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 514e9c39eb4..b8257ba0adc 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -2,7 +2,7 @@ #![allow(rustc::untranslatable_diagnostic)] use hir::ExprKind; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_hir::Node; @@ -540,7 +540,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } - fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diagnostic, span: Span) { + fn suggest_map_index_mut_alternatives( + &self, + ty: Ty<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + span: Span, + ) { let Some(adt) = ty.ty_adt_def() else { return }; let did = adt.did(); if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did) @@ -548,7 +553,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { { struct V<'a, 'tcx> { assign_span: Span, - err: &'a mut Diagnostic, + err: &'a mut DiagnosticBuilder<'tcx>, ty: Ty<'tcx>, suggested: bool, } @@ -790,7 +795,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { tcx: TyCtxt<'_>, closure_local_def_id: hir::def_id::LocalDefId, the_place_err: PlaceRef<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) { let tables = tcx.typeck(closure_local_def_id); if let Some((span, closure_kind_origin)) = tcx.closure_kind_origin(closure_local_def_id) { @@ -852,7 +857,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // Attempt to search similar mutable associated items for suggestion. // In the future, attempt in all path but initially for RHS of for_loop - fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diagnostic, span: Span) { + fn suggest_similar_mut_method_for_for_loop(&self, err: &mut DiagnosticBuilder<'_>, span: Span) { use hir::{ BorrowKind, Expr, ExprKind::{AddrOf, Block, Call, MethodCall}, @@ -936,7 +941,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected. - fn expected_fn_found_fn_mut_call(&self, err: &mut Diagnostic, sp: Span, act: &str) { + fn expected_fn_found_fn_mut_call(&self, err: &mut DiagnosticBuilder<'_>, sp: Span, act: &str) { err.span_label(sp, format!("cannot {act}")); let hir = self.infcx.tcx.hir(); diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index 93e28a5f3f3..b2c7a98142e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -5,7 +5,7 @@ #![allow(rustc::untranslatable_diagnostic)] use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::Diagnostic; +use rustc_errors::DiagnosticBuilder; use rustc_middle::ty::RegionVid; use smallvec::SmallVec; use std::collections::BTreeMap; @@ -158,13 +158,13 @@ impl OutlivesSuggestionBuilder { self.constraints_to_add.entry(fr).or_default().push(outlived_fr); } - /// Emit an intermediate note on the given `Diagnostic` if the involved regions are + /// Emit an intermediate note on the given `DiagnosticBuilder` if the involved regions are /// suggestable. pub(crate) fn intermediate_suggestion( &mut self, mbcx: &MirBorrowckCtxt<'_, '_>, errci: &ErrorConstraintInfo<'_>, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, ) { // Emit an intermediate note. let fr_name = self.region_vid_to_name(mbcx, errci.fr); diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index e8effd5c163..50d22881c3e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1,7 +1,7 @@ //! Error reporting machinery for lifetime errors. use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, MultiSpan}; +use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::Res::Def; use rustc_hir::def_id::DefId; @@ -251,6 +251,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { hrtb_bounds.iter().for_each(|bound| { let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }, _) = bound else { return; }; + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] diag.span_note( *trait_span, "due to current limitations in the borrow checker, this implies a `'static` lifetime" @@ -421,6 +424,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// ``` /// /// Here we would be invoked with `fr = 'a` and `outlived_fr = 'b`. + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] pub(crate) fn report_region_error( &mut self, fr: RegionVid, @@ -685,12 +691,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { borrowck_errors::borrowed_data_escapes_closure(self.infcx.tcx, *span, escapes_from); if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span { + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] diag.span_label( outlived_fr_span, format!("`{outlived_fr_name}` declared here, outside of the {escapes_from} body",), ); } + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] if let Some((Some(fr_name), fr_span)) = fr_name_and_span { diag.span_label( fr_span, @@ -714,6 +726,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let outlived_fr_region_name = self.give_region_a_name(errci.outlived_fr).unwrap(); outlived_fr_region_name.highlight_region_name(&mut diag); + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] diag.span_label( *span, format!( @@ -808,7 +823,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// ``` fn add_static_impl_trait_suggestion( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, fr: RegionVid, // We need to pass `fr_name` - computing it again will label it twice. fr_name: RegionName, @@ -897,7 +912,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fn maybe_suggest_constrain_dyn_trait_impl( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, f: Region<'tcx>, o: Region<'tcx>, category: &ConstraintCategory<'tcx>, @@ -959,7 +974,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { #[instrument(skip(self, err), level = "debug")] fn suggest_constrain_dyn_trait_in_impl( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, found_dids: &FxIndexSet<DefId>, ident: Ident, self_ty: &hir::Ty<'_>, @@ -994,7 +1009,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fn suggest_adding_lifetime_params( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, sub: RegionVid, sup: RegionVid, ) { @@ -1023,7 +1038,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag); } - fn suggest_move_on_borrowing_closure(&self, diag: &mut Diagnostic) { + fn suggest_move_on_borrowing_closure(&self, diag: &mut DiagnosticBuilder<'_>) { let map = self.infcx.tcx.hir(); let body_id = map.body_owned_by(self.mir_def_id()); let expr = &map.body(body_id).value.peel_blocks(); diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index e008d230656..e228bef1139 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Display}; use std::iter; use rustc_data_structures::fx::IndexEntry; -use rustc_errors::Diagnostic; +use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_middle::ty::print::RegionHighlightMode; @@ -106,7 +106,7 @@ impl RegionName { } } - pub(crate) fn highlight_region_name(&self, diag: &mut Diagnostic) { + pub(crate) fn highlight_region_name(&self, diag: &mut DiagnosticBuilder<'_>) { match &self.source { RegionNameSource::NamedLateParamRegion(span) | RegionNameSource::NamedEarlyParamRegion(span) => { @@ -626,9 +626,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { | GenericArgKind::Const(_), _, ) => { - // HIR lowering sometimes doesn't catch this in erroneous - // programs, so we need to use span_delayed_bug here. See #82126. - self.dcx().span_delayed_bug( + // This was previously a `span_delayed_bug` and could be + // reached by the test for #82126, but no longer. + self.dcx().span_bug( hir_arg.span(), format!("unmatched arg and hir arg: found {kind:?} vs {hir_arg:?}"), ); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 6c2a511538d..dbaa9e5bcfa 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2497,6 +2497,8 @@ mod diags { } for (_, (mut diag, count)) in std::mem::take(&mut self.diags.buffered_mut_errors) { if count > 10 { + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] diag.note(format!("...and {} other attempted mutable borrows", count - 10)); } self.diags.buffered_diags.push(BufferedDiag::Error(diag)); diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 34d60fc8f6e..ce2c0dbaff7 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -5,7 +5,7 @@ use rustc_data_structures::binary_search_util; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::graph::scc::Sccs; -use rustc_errors::Diagnostic; +use rustc_errors::DiagnosticBuilder; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::outlives::test_type_match; @@ -592,7 +592,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// Adds annotations for `#[rustc_regions]`; see `UniversalRegions::annotate`. - pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) { + pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut DiagnosticBuilder<'_, ()>) { self.universal_regions.annotate(tcx, err) } diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index fc600af1b76..e5ebf97cfc4 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -100,7 +100,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations: Locations, ) { for (predicate, span) in instantiated_predicates { - debug!(?predicate); + debug!(?span, ?predicate); let category = ConstraintCategory::Predicate(span); let predicate = self.normalize_with_category(predicate, locations, category); self.prove_predicate(predicate, locations, category); diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index c97e3170166..de75a9857f8 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -8,7 +8,7 @@ use rustc_middle::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Const use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::Span; use rustc_trait_selection::solve::deeply_normalize; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; @@ -183,7 +183,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { // we don't actually use this for anything, but // the `TypeOutlives` code needs an origin. - let origin = infer::RelateParamBound(DUMMY_SP, t1, None); + let origin = infer::RelateParamBound(self.span, t1, None); TypeOutlives::new( &mut *self, diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 2e0caf44819..897918fb0a4 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -10,7 +10,7 @@ use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt}; -use rustc_span::{ErrorGuaranteed, DUMMY_SP}; +use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::solve::deeply_normalize; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; @@ -269,7 +269,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { debug!("build: input_or_output={:?}", ty); // We add implied bounds from both the unnormalized and normalized ty. // See issue #87748 - let constraints_unnorm = self.add_implied_bounds(ty); + let constraints_unnorm = self.add_implied_bounds(ty, span); if let Some(c) = constraints_unnorm { constraints.push(c) } @@ -299,7 +299,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // ``` // Both &Self::Bar and &() are WF if ty != norm_ty { - let constraints_norm = self.add_implied_bounds(norm_ty); + let constraints_norm = self.add_implied_bounds(norm_ty, span); if let Some(c) = constraints_norm { constraints.push(c) } @@ -316,6 +316,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { .and(type_op::normalize::Normalize::new(ty)) .fully_perform(self.infcx, span) else { + // Note: this path is currently not reached in any test, so + // any example that triggers this would be worth minimizing + // and converting into a test. tcx.dcx().span_delayed_bug(span, format!("failed to normalize {ty:?}")); continue; }; @@ -323,7 +326,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // We currently add implied bounds from the normalized ty only. // This is more conservative and matches wfcheck behavior. - let c = self.add_implied_bounds(norm_ty); + let c = self.add_implied_bounds(norm_ty, span); constraints.extend(c); } } @@ -361,11 +364,15 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { /// the same time, compute and add any implied bounds that come /// from this local. #[instrument(level = "debug", skip(self))] - fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option<&'tcx QueryRegionConstraints<'tcx>> { + fn add_implied_bounds( + &mut self, + ty: Ty<'tcx>, + span: Span, + ) -> Option<&'tcx QueryRegionConstraints<'tcx>> { let TypeOpOutput { output: bounds, constraints, .. } = self .param_env .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty }) - .fully_perform(self.infcx, DUMMY_SP) + .fully_perform(self.infcx, span) .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty)) .ok()?; debug!(?bounds, ?constraints); diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index af5b635ae66..8af78b08f69 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -154,8 +154,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if argument_index + 1 >= body.local_decls.len() { self.tcx() .dcx() - .span_delayed_bug(body.span, "found more normalized_input_ty than local_decls"); - break; + .span_bug(body.span, "found more normalized_input_ty than local_decls"); } // In MIR, argument N is stored in local N+1. diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 64469727d0d..81193902f9b 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -37,7 +37,7 @@ use rustc_mir_dataflow::points::DenseLocationMap; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::Span; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; @@ -220,14 +220,13 @@ pub(crate) fn type_check<'mir, 'tcx>( "opaque_type_map", ), ); - let mut hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type); + let hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type); trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind()); if hidden_type.has_non_region_infer() { - let reported = infcx.dcx().span_delayed_bug( + infcx.dcx().span_bug( decl.hidden_type.span, format!("could not resolve {:#?}", hidden_type.ty.kind()), ); - hidden_type.ty = Ty::new_error(infcx.tcx, reported); } (opaque_type_key, hidden_type) @@ -1014,7 +1013,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ) -> Self { let mut checker = Self { infcx, - last_span: DUMMY_SP, + last_span: body.span, body, user_type_annotations: &body.user_type_annotations, param_env, @@ -1089,10 +1088,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); if result.is_err() { - self.infcx.dcx().span_delayed_bug( - self.body.span, - "failed re-defining predefined opaques in mir typeck", - ); + self.infcx + .dcx() + .span_bug(self.body.span, "failed re-defining predefined opaques in mir typeck"); } } @@ -2766,7 +2764,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.param_env, self.known_type_outlives_obligations, locations, - DUMMY_SP, // irrelevant; will be overridden. + self.body.span, // irrelevant; will be overridden. ConstraintCategory::Boring, // same as above. self.borrowck_context.constraints, ) diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index ee0bd13109b..dd355c3525c 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -1,11 +1,14 @@ +use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; -use rustc_infer::infer::nll_relate::{TypeRelating, TypeRelatingDelegate}; -use rustc_infer::infer::NllRegionVariableOrigin; -use rustc_infer::traits::PredicateObligations; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::{NllRegionVariableOrigin, ObligationEmittingRelation}; +use rustc_infer::traits::{Obligation, PredicateObligations}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::relate::TypeRelation; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::fold::FnMutDelegate; +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; @@ -32,12 +35,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations: Locations, category: ConstraintCategory<'tcx>, ) -> Result<(), NoSolution> { - TypeRelating::new( - self.infcx, - NllTypeRelatingDelegate::new(self, locations, category, UniverseInfo::relate(a, b)), - v, - ) - .relate(a, b)?; + NllTypeRelating::new(self, locations, category, UniverseInfo::relate(a, b), v) + .relate(a, b)?; Ok(()) } @@ -49,9 +48,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations: Locations, category: ConstraintCategory<'tcx>, ) -> Result<(), NoSolution> { - TypeRelating::new( - self.infcx, - NllTypeRelatingDelegate::new(self, locations, category, UniverseInfo::other()), + NllTypeRelating::new( + self, + locations, + category, + UniverseInfo::other(), ty::Variance::Invariant, ) .relate(a, b)?; @@ -59,7 +60,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } -struct NllTypeRelatingDelegate<'me, 'bccx, 'tcx> { +pub struct NllTypeRelating<'me, 'bccx, 'tcx> { type_checker: &'me mut TypeChecker<'bccx, 'tcx>, /// Where (and why) is this relation taking place? @@ -71,26 +72,177 @@ struct NllTypeRelatingDelegate<'me, 'bccx, 'tcx> { /// Information so that error reporting knows what types we are relating /// when reporting a bound region error. universe_info: UniverseInfo<'tcx>, + + /// How are we relating `a` and `b`? + /// + /// - Covariant means `a <: b`. + /// - Contravariant means `b <: a`. + /// - Invariant means `a == b`. + /// - Bivariant means that it doesn't matter. + ambient_variance: ty::Variance, + + ambient_variance_info: ty::VarianceDiagInfo<'tcx>, } -impl<'me, 'bccx, 'tcx> NllTypeRelatingDelegate<'me, 'bccx, 'tcx> { - fn new( +impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { + pub fn new( type_checker: &'me mut TypeChecker<'bccx, 'tcx>, locations: Locations, category: ConstraintCategory<'tcx>, universe_info: UniverseInfo<'tcx>, + ambient_variance: ty::Variance, ) -> Self { - Self { type_checker, locations, category, universe_info } + Self { + type_checker, + locations, + category, + universe_info, + ambient_variance, + ambient_variance_info: ty::VarianceDiagInfo::default(), + } } -} -impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> { - fn span(&self) -> Span { - self.locations.span(self.type_checker.body) + fn ambient_covariance(&self) -> bool { + match self.ambient_variance { + ty::Variance::Covariant | ty::Variance::Invariant => true, + ty::Variance::Contravariant | ty::Variance::Bivariant => false, + } } - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.type_checker.param_env + fn ambient_contravariance(&self) -> bool { + match self.ambient_variance { + ty::Variance::Contravariant | ty::Variance::Invariant => true, + ty::Variance::Covariant | ty::Variance::Bivariant => false, + } + } + + fn relate_opaques(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { + let infcx = self.type_checker.infcx; + debug_assert!(!infcx.next_trait_solver()); + let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; + // `handle_opaque_type` cannot handle subtyping, so to support subtyping + // we instead eagerly generalize here. This is a bit of a mess but will go + // away once we're using the new solver. + let mut enable_subtyping = |ty, ty_is_expected| { + let ty_vid = infcx.next_ty_var_id_in_universe( + TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: self.span(), + }, + ty::UniverseIndex::ROOT, + ); + + let variance = if ty_is_expected { + self.ambient_variance + } else { + self.ambient_variance.xform(ty::Contravariant) + }; + + self.type_checker.infcx.instantiate_ty_var( + self, + ty_is_expected, + ty_vid, + variance, + ty, + )?; + Ok(infcx.resolve_vars_if_possible(Ty::new_infer(infcx.tcx, ty::TyVar(ty_vid)))) + }; + + let (a, b) = match (a.kind(), b.kind()) { + (&ty::Alias(ty::Opaque, ..), _) => (a, enable_subtyping(b, false)?), + (_, &ty::Alias(ty::Opaque, ..)) => (enable_subtyping(a, true)?, b), + _ => unreachable!( + "expected at least one opaque type in `relate_opaques`, got {a} and {b}." + ), + }; + let cause = ObligationCause::dummy_with_span(self.span()); + let obligations = + infcx.handle_opaque_type(a, b, true, &cause, self.param_env())?.obligations; + self.register_obligations(obligations); + Ok(()) + } + + fn enter_forall<T, U>( + &mut self, + binder: ty::Binder<'tcx, T>, + f: impl FnOnce(&mut Self, T) -> U, + ) -> U + where + T: ty::TypeFoldable<TyCtxt<'tcx>> + Copy, + { + let value = if let Some(inner) = binder.no_bound_vars() { + inner + } else { + let infcx = self.type_checker.infcx; + let mut lazy_universe = None; + let delegate = FnMutDelegate { + regions: &mut |br: ty::BoundRegion| { + // The first time this closure is called, create a + // new universe for the placeholders we will make + // from here out. + let universe = lazy_universe.unwrap_or_else(|| { + let universe = self.create_next_universe(); + lazy_universe = Some(universe); + universe + }); + + let placeholder = ty::PlaceholderRegion { universe, bound: br }; + debug!(?placeholder); + let placeholder_reg = self.next_placeholder_region(placeholder); + debug!(?placeholder_reg); + + placeholder_reg + }, + types: &mut |_bound_ty: ty::BoundTy| { + unreachable!("we only replace regions in nll_relate, not types") + }, + consts: &mut |_bound_var: ty::BoundVar, _ty| { + unreachable!("we only replace regions in nll_relate, not consts") + }, + }; + + infcx.tcx.replace_bound_vars_uncached(binder, delegate) + }; + + debug!(?value); + f(self, value) + } + + #[instrument(skip(self), level = "debug")] + fn instantiate_binder_with_existentials<T>(&mut self, binder: ty::Binder<'tcx, T>) -> T + where + T: ty::TypeFoldable<TyCtxt<'tcx>> + Copy, + { + if let Some(inner) = binder.no_bound_vars() { + return inner; + } + + let infcx = self.type_checker.infcx; + let mut reg_map = FxHashMap::default(); + let delegate = FnMutDelegate { + regions: &mut |br: ty::BoundRegion| { + if let Some(ex_reg_var) = reg_map.get(&br) { + return *ex_reg_var; + } else { + let ex_reg_var = self.next_existential_region_var(true, br.kind.get_name()); + debug!(?ex_reg_var); + reg_map.insert(br, ex_reg_var); + + ex_reg_var + } + }, + types: &mut |_bound_ty: ty::BoundTy| { + unreachable!("we only replace regions in nll_relate, not types") + }, + consts: &mut |_bound_var: ty::BoundVar, _ty| { + unreachable!("we only replace regions in nll_relate, not consts") + }, + }; + + let replaced = infcx.tcx.replace_bound_vars_uncached(binder, delegate); + debug!(?replaced); + + replaced } fn create_next_universe(&mut self) -> ty::UniverseIndex { @@ -143,22 +295,6 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> reg } - #[instrument(skip(self), level = "debug")] - fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { - let reg = self.type_checker.infcx.next_nll_region_var_in_universe( - NllRegionVariableOrigin::Existential { from_forall: false }, - universe, - ); - - if cfg!(debug_assertions) { - let mut var_to_origin = self.type_checker.infcx.reg_var_to_origin.borrow_mut(); - let prev = var_to_origin.insert(reg.as_var(), RegionCtxt::Existential(None)); - assert_eq!(prev, None); - } - - reg - } - fn push_outlives( &mut self, sup: ty::Region<'tcx>, @@ -179,11 +315,250 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> }, ); } +} + +impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.type_checker.infcx.tcx + } + + fn tag(&self) -> &'static str { + "nll::subtype" + } - fn forbid_inference_vars() -> bool { + fn a_is_expected(&self) -> bool { true } + #[instrument(skip(self, info), level = "trace", ret)] + fn relate_with_variance<T: Relate<'tcx>>( + &mut self, + variance: ty::Variance, + info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + self.ambient_variance_info = self.ambient_variance_info.xform(info); + + debug!(?self.ambient_variance); + // In a bivariant context this always succeeds. + let r = + if self.ambient_variance == ty::Variance::Bivariant { a } else { self.relate(a, b)? }; + + self.ambient_variance = old_ambient_variance; + + Ok(r) + } + + #[instrument(skip(self), level = "debug")] + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + let infcx = self.type_checker.infcx; + + let a = self.type_checker.infcx.shallow_resolve(a); + assert!(!b.has_non_region_infer(), "unexpected inference var {:?}", b); + + if a == b { + return Ok(a); + } + + match (a.kind(), b.kind()) { + (_, &ty::Infer(ty::TyVar(_))) => { + span_bug!( + self.span(), + "should not be relating type variables on the right in MIR typeck" + ); + } + + (&ty::Infer(ty::TyVar(a_vid)), _) => { + infcx.instantiate_ty_var(self, true, a_vid, self.ambient_variance, b)? + } + + ( + &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), + &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), + ) if a_def_id == b_def_id || infcx.next_trait_solver() => { + infcx.super_combine_tys(self, a, b).map(|_| ()).or_else(|err| { + // This behavior is only there for the old solver, the new solver + // shouldn't ever fail. Instead, it unconditionally emits an + // alias-relate goal. + assert!(!self.type_checker.infcx.next_trait_solver()); + self.tcx().dcx().span_delayed_bug( + self.span(), + "failure to relate an opaque to itself should result in an error later on", + ); + if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) } + })?; + } + (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) + | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) + if def_id.is_local() && !self.type_checker.infcx.next_trait_solver() => + { + self.relate_opaques(a, b)?; + } + + _ => { + debug!(?a, ?b, ?self.ambient_variance); + + // Will also handle unification of `IntVar` and `FloatVar`. + self.type_checker.infcx.super_combine_tys(self, a, b)?; + } + } + + Ok(a) + } + + #[instrument(skip(self), level = "trace")] + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!(?self.ambient_variance); + + if self.ambient_covariance() { + // Covariant: &'a u8 <: &'b u8. Hence, `'a: 'b`. + self.push_outlives(a, b, self.ambient_variance_info); + } + + if self.ambient_contravariance() { + // Contravariant: &'b u8 <: &'a u8. Hence, `'b: 'a`. + self.push_outlives(b, a, self.ambient_variance_info); + } + + Ok(a) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + let a = self.type_checker.infcx.shallow_resolve(a); + assert!(!a.has_non_region_infer(), "unexpected inference var {:?}", a); + assert!(!b.has_non_region_infer(), "unexpected inference var {:?}", b); + + self.type_checker.infcx.super_combine_consts(self, a, b) + } + + #[instrument(skip(self), level = "trace")] + fn binders<T>( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + // We want that + // + // ``` + // for<'a> fn(&'a u32) -> &'a u32 <: + // fn(&'b u32) -> &'b u32 + // ``` + // + // but not + // + // ``` + // fn(&'a u32) -> &'a u32 <: + // for<'b> fn(&'b u32) -> &'b u32 + // ``` + // + // We therefore proceed as follows: + // + // - Instantiate binders on `b` universally, yielding a universe U1. + // - Instantiate binders on `a` existentially in U1. + + debug!(?self.ambient_variance); + + if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) { + // Fast path for the common case. + self.relate(a, b)?; + return Ok(ty::Binder::dummy(a)); + } + + if self.ambient_covariance() { + // Covariance, so we want `for<..> A <: for<..> B` -- + // therefore we compare any instantiation of A (i.e., A + // instantiated with existentials) against every + // instantiation of B (i.e., B instantiated with + // universals). + + // Reset the ambient variance to covariant. This is needed + // to correctly handle cases like + // + // for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32) + // + // Somewhat surprisingly, these two types are actually + // **equal**, even though the one on the right looks more + // polymorphic. The reason is due to subtyping. To see it, + // consider that each function can call the other: + // + // - The left function can call the right with `'b` and + // `'c` both equal to `'a` + // + // - The right function can call the left with `'a` set to + // `{P}`, where P is the point in the CFG where the call + // itself occurs. Note that `'b` and `'c` must both + // include P. At the point, the call works because of + // subtyping (i.e., `&'b u32 <: &{P} u32`). + let variance = std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant); + + // Note: the order here is important. Create the placeholders first, otherwise + // we assign the wrong universe to the existential! + self.enter_forall(b, |this, b| { + let a = this.instantiate_binder_with_existentials(a); + this.relate(a, b) + })?; + + self.ambient_variance = variance; + } + + if self.ambient_contravariance() { + // Contravariance, so we want `for<..> A :> for<..> B` + // -- therefore we compare every instantiation of A (i.e., + // A instantiated with universals) against any + // instantiation of B (i.e., B instantiated with + // existentials). Opposite of above. + + // Reset ambient variance to contravariance. See the + // covariant case above for an explanation. + let variance = + std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant); + + self.enter_forall(a, |this, a| { + let b = this.instantiate_binder_with_existentials(b); + this.relate(a, b) + })?; + + self.ambient_variance = variance; + } + + Ok(a) + } +} + +impl<'bccx, 'tcx> ObligationEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { + fn span(&self) -> Span { + self.locations.span(self.type_checker.body) + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.type_checker.param_env + } + + fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) { + self.register_obligations( + obligations + .into_iter() + .map(|to_pred| { + Obligation::new(self.tcx(), ObligationCause::dummy(), self.param_env(), to_pred) + }) + .collect(), + ); + } + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { let _: Result<_, ErrorGuaranteed> = self.type_checker.fully_perform_op( self.locations, @@ -196,4 +571,32 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> }, ); } + + fn alias_relate_direction(&self) -> ty::AliasRelationDirection { + unreachable!("manually overridden to handle ty::Variance::Contravariant ambient variance") + } + + fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { + self.register_predicates([ty::Binder::dummy(match self.ambient_variance { + ty::Variance::Covariant => ty::PredicateKind::AliasRelate( + a.into(), + b.into(), + ty::AliasRelationDirection::Subtype, + ), + // a :> b is b <: a + ty::Variance::Contravariant => ty::PredicateKind::AliasRelate( + b.into(), + a.into(), + ty::AliasRelationDirection::Subtype, + ), + ty::Variance::Invariant => ty::PredicateKind::AliasRelate( + a.into(), + b.into(), + ty::AliasRelationDirection::Equate, + ), + ty::Variance::Bivariant => { + unreachable!("cannot defer an alias-relate goal with Bivariant variance (yet?)") + } + })]); + } } diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index a69f5335f71..e7439481034 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -16,7 +16,7 @@ #![allow(rustc::untranslatable_diagnostic)] use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::Diagnostic; +use rustc_errors::DiagnosticBuilder; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::BodyOwnerKind; @@ -343,7 +343,7 @@ impl<'tcx> UniversalRegions<'tcx> { /// that this region imposes on others. The methods in this file /// handle the part about dumping the inference context internal /// state. - pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) { + pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut DiagnosticBuilder<'_, ()>) { match self.defining_ty { DefiningTy::Closure(def_id, args) => { let v = with_no_trimmed_paths!( diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 0b2e63b403b..c5a73c31995 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -773,7 +773,7 @@ pub(super) fn expand_global_asm<'cx>( kind: ast::VisibilityKind::Inherited, tokens: None, }, - span: ecx.with_def_site_ctxt(sp), + span: sp, tokens: None, })]) } else { diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 8d2e06bf30d..f304a37be85 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1,6 +1,6 @@ use rustc_errors::{ - codes::*, AddToDiagnostic, DiagCtxt, Diagnostic, DiagnosticBuilder, EmissionGuarantee, - IntoDiagnostic, Level, MultiSpan, SingleLabelManySpans, SubdiagnosticMessageOp, + codes::*, AddToDiagnostic, DiagCtxt, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic, + Level, MultiSpan, SingleLabelManySpans, SubdiagnosticMessageOp, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; @@ -611,7 +611,11 @@ pub(crate) struct FormatUnusedArg { // Allow the singular form to be a subdiagnostic of the multiple-unused // form of diagnostic. impl AddToDiagnostic for FormatUnusedArg { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { diag.arg("named", self.named); let msg = f(diag, crate::fluent_generated::builtin_macros_format_unused_arg.into()); diag.span_label(self.span, msg); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 476752c7230..199d5df29e7 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1152,17 +1152,26 @@ fn codegen_regular_intrinsic_call<'tcx>( ret.write_cvalue(fx, ret_val); } - sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => { + sym::fadd_fast + | sym::fsub_fast + | sym::fmul_fast + | sym::fdiv_fast + | sym::frem_fast + | sym::fadd_algebraic + | sym::fsub_algebraic + | sym::fmul_algebraic + | sym::fdiv_algebraic + | sym::frem_algebraic => { intrinsic_args!(fx, args => (x, y); intrinsic); let res = crate::num::codegen_float_binop( fx, match intrinsic { - sym::fadd_fast => BinOp::Add, - sym::fsub_fast => BinOp::Sub, - sym::fmul_fast => BinOp::Mul, - sym::fdiv_fast => BinOp::Div, - sym::frem_fast => BinOp::Rem, + sym::fadd_fast | sym::fadd_algebraic => BinOp::Add, + sym::fsub_fast | sym::fsub_algebraic => BinOp::Sub, + sym::fmul_fast | sym::fmul_algebraic => BinOp::Mul, + sym::fdiv_fast | sym::fdiv_algebraic => BinOp::Div, + sym::frem_fast | sym::frem_algebraic => BinOp::Rem, _ => unreachable!(), }, x, diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 42e61b3ccb5..5f1e4538376 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -705,6 +705,31 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { self.frem(lhs, rhs) } + fn fadd_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { + // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC. + lhs + rhs + } + + fn fsub_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { + // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC. + lhs - rhs + } + + fn fmul_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { + // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC. + lhs * rhs + } + + fn fdiv_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { + // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC. + lhs / rhs + } + + fn frem_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { + // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC. + self.frem(lhs, rhs) + } + fn checked_binop(&mut self, oop: OverflowOp, typ: Ty<'_>, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value) { self.gcc_checked_binop(oop, typ, lhs, rhs) } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 7ed27b33dce..cfa266720d2 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -340,6 +340,46 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } + fn fadd_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { + unsafe { + let instr = llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, UNNAMED); + llvm::LLVMRustSetAlgebraicMath(instr); + instr + } + } + + fn fsub_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { + unsafe { + let instr = llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, UNNAMED); + llvm::LLVMRustSetAlgebraicMath(instr); + instr + } + } + + fn fmul_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { + unsafe { + let instr = llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, UNNAMED); + llvm::LLVMRustSetAlgebraicMath(instr); + instr + } + } + + fn fdiv_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { + unsafe { + let instr = llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, UNNAMED); + llvm::LLVMRustSetAlgebraicMath(instr); + instr + } + } + + fn frem_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { + unsafe { + let instr = llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, UNNAMED); + llvm::LLVMRustSetAlgebraicMath(instr); + instr + } + } + fn checked_binop( &mut self, oop: OverflowOp, @@ -1327,17 +1367,17 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { pub fn vector_reduce_fmul(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value { unsafe { llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src) } } - pub fn vector_reduce_fadd_fast(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value { + pub fn vector_reduce_fadd_algebraic(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value { unsafe { let instr = llvm::LLVMRustBuildVectorReduceFAdd(self.llbuilder, acc, src); - llvm::LLVMRustSetFastMath(instr); + llvm::LLVMRustSetAlgebraicMath(instr); instr } } - pub fn vector_reduce_fmul_fast(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value { + pub fn vector_reduce_fmul_algebraic(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value { unsafe { let instr = llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src); - llvm::LLVMRustSetFastMath(instr); + llvm::LLVMRustSetAlgebraicMath(instr); instr } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 4415c51acf6..3b091fca28b 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1880,14 +1880,14 @@ fn generic_simd_intrinsic<'ll, 'tcx>( arith_red!(simd_reduce_mul_ordered: vector_reduce_mul, vector_reduce_fmul, true, mul, 1.0); arith_red!( simd_reduce_add_unordered: vector_reduce_add, - vector_reduce_fadd_fast, + vector_reduce_fadd_algebraic, false, add, 0.0 ); arith_red!( simd_reduce_mul_unordered: vector_reduce_mul, - vector_reduce_fmul_fast, + vector_reduce_fmul_algebraic, false, mul, 1.0 diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index d0044086c61..f9eb1da5dc7 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1618,6 +1618,7 @@ extern "C" { ) -> &'a Value; pub fn LLVMRustSetFastMath(Instr: &Value); + pub fn LLVMRustSetAlgebraicMath(Instr: &Value); // Miscellaneous instructions pub fn LLVMRustGetInstrProfIncrementIntrinsic(M: &Module) -> &Value; diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index e144b1dc1bd..781c54bdef8 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -48,7 +48,7 @@ libc = "0.2.50" [dependencies.object] version = "0.32.1" default-features = false -features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write"] +features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write", "wasm"] [target.'cfg(windows)'.dependencies.windows] version = "0.52.0" diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 435b517e602..1ad0dec0640 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -3,7 +3,7 @@ use rustc_ast::CRATE_NODE_ID; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; -use rustc_errors::{DiagCtxt, ErrorGuaranteed}; +use rustc_errors::{DiagCtxt, ErrorGuaranteed, FatalError}; use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::find_native_static_library; @@ -487,7 +487,9 @@ fn collate_raw_dylibs<'a, 'b>( } } } - sess.compile_status()?; + if let Some(guar) = sess.dcx().has_errors() { + return Err(guar); + } Ok(dylib_table .into_iter() .map(|(name, imports)| { @@ -720,10 +722,7 @@ fn link_dwarf_object<'a>( Ok(()) }) { Ok(()) => {} - Err(e) => { - sess.dcx().emit_err(errors::ThorinErrorWrapper(e)); - sess.dcx().abort_if_errors(); - } + Err(e) => sess.dcx().emit_fatal(errors::ThorinErrorWrapper(e)), } } @@ -999,7 +998,7 @@ fn link_natively<'a>( sess.dcx().emit_note(errors::CheckInstalledVisualStudio); sess.dcx().emit_note(errors::InsufficientVSCodeProduct); } - sess.dcx().abort_if_errors(); + FatalError.raise(); } } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 9f06f398288..1f3383815e2 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -626,7 +626,7 @@ impl<'a> Linker for GccLinker<'a> { // it does support --strip-all as a compatibility alias for -s. // The --strip-debug case is handled by running an external // `strip` utility as a separate step after linking. - if self.sess.target.os != "illumos" { + if !self.sess.target.is_like_solaris { self.linker_arg("--strip-debug"); } } diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index b683e1b45a8..8e76e47cfef 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -15,6 +15,7 @@ use rustc_data_structures::owned_slice::{try_slice_owned, OwnedSlice}; use rustc_metadata::creader::MetadataLoader; use rustc_metadata::fs::METADATA_FILENAME; use rustc_metadata::EncodedMetadata; +use rustc_serialize::leb128; use rustc_session::Session; use rustc_span::sym; use rustc_target::abi::Endian; @@ -420,10 +421,9 @@ pub enum MetadataPosition { /// it's not in an allowlist of otherwise well known dwarf section names to /// go into the final artifact. /// -/// * WebAssembly - we actually don't have any container format for this -/// target. WebAssembly doesn't support the `dylib` crate type anyway so -/// there's no need for us to support this at this time. Consequently the -/// metadata bytes are simply stored as-is into an rlib. +/// * WebAssembly - this uses wasm files themselves as the object file format +/// so an empty file with no linking metadata but a single custom section is +/// created holding our metadata. /// /// * COFF - Windows-like targets create an object with a section that has /// the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker @@ -438,22 +438,13 @@ pub fn create_wrapper_file( data: &[u8], ) -> (Vec<u8>, MetadataPosition) { let Some(mut file) = create_object_file(sess) else { - // This is used to handle all "other" targets. This includes targets - // in two categories: - // - // * Some targets don't have support in the `object` crate just yet - // to write an object file. These targets are likely to get filled - // out over time. - // - // * Targets like WebAssembly don't support dylibs, so the purpose - // of putting metadata in object files, to support linking rlibs - // into dylibs, is moot. - // - // In both of these cases it means that linking into dylibs will - // not be supported by rustc. This doesn't matter for targets like - // WebAssembly and for targets not supported by the `object` crate - // yet it means that work will need to be done in the `object` crate - // to add a case above. + if sess.target.is_like_wasm { + return (create_metadata_file_for_wasm(data, §ion_name), MetadataPosition::First); + } + + // Targets using this branch don't have support implemented here yet or + // they're not yet implemented in the `object` crate and will likely + // fill out this module over time. return (data.to_vec(), MetadataPosition::Last); }; let section = if file.format() == BinaryFormat::Xcoff { @@ -532,6 +523,9 @@ pub fn create_compressed_metadata_file( packed_metadata.extend(metadata.raw_data()); let Some(mut file) = create_object_file(sess) else { + if sess.target.is_like_wasm { + return create_metadata_file_for_wasm(&packed_metadata, b".rustc"); + } return packed_metadata.to_vec(); }; if file.format() == BinaryFormat::Xcoff { @@ -624,3 +618,57 @@ pub fn create_compressed_metadata_file_for_xcoff( file.append_section_data(section, data, 1); file.write().unwrap() } + +/// Creates a simple WebAssembly object file, which is itself a wasm module, +/// that contains a custom section of the name `section_name` with contents +/// `data`. +/// +/// NB: the `object` crate does not yet have support for writing the the wasm +/// object file format. The format is simple enough that for now an extra crate +/// from crates.io (such as `wasm-encoder`). The file format is: +/// +/// * 4-byte header "\0asm" +/// * 4-byte version number - 1u32 in little-endian format +/// * concatenated sections, which for this object is always "custom sections" +/// +/// Custom sections are then defined by: +/// * 1-byte section identifier - 0 for a custom section +/// * leb-encoded section length (size of the contents beneath this bullet) +/// * leb-encoded custom section name length +/// * custom section name +/// * section contents +/// +/// One custom section, `linking`, is added here in accordance with +/// <https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md> +/// which is required to inform LLD that this is an object file but it should +/// otherwise basically ignore it if it otherwise looks at it. The linking +/// section currently is defined by a single version byte (2) and then further +/// sections, but we have no more sections, so it's just the byte "2". +/// +/// The next custom section is the one we're interested in. +pub fn create_metadata_file_for_wasm(data: &[u8], section_name: &[u8]) -> Vec<u8> { + let mut bytes = b"\0asm\x01\0\0\0".to_vec(); + + let mut append_custom_section = |section_name: &[u8], data: &[u8]| { + let mut section_name_len = [0; leb128::max_leb128_len::<usize>()]; + let off = leb128::write_usize_leb128(&mut section_name_len, section_name.len()); + let section_name_len = §ion_name_len[..off]; + + let mut section_len = [0; leb128::max_leb128_len::<usize>()]; + let off = leb128::write_usize_leb128( + &mut section_len, + data.len() + section_name_len.len() + section_name.len(), + ); + let section_len = §ion_len[..off]; + + bytes.push(0u8); + bytes.extend_from_slice(section_len); + bytes.extend_from_slice(section_name_len); + bytes.extend_from_slice(section_name); + bytes.extend_from_slice(data); + }; + + append_custom_section(b"linking", &[2]); + append_custom_section(section_name, data); + bytes +} diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 24fdd013509..7a981217b52 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1856,9 +1856,7 @@ impl SharedEmitterMain { Ok(SharedEmitterMessage::Diagnostic(diag)) => { let dcx = sess.dcx(); let mut d = rustc_errors::Diagnostic::new_with_messages(diag.lvl, diag.msgs); - if let Some(code) = diag.code { - d.code(code); - } + d.code = diag.code; // may be `None`, that's ok d.replace_args(diag.args); dcx.emit_diagnostic(d); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 760b3f30ee5..f7afd22a48c 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -449,10 +449,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let Some(llfn) = cx.declare_c_main(llfty) else { // FIXME: We should be smart and show a better diagnostic here. let span = cx.tcx().def_span(rust_main_def_id); - let dcx = cx.tcx().dcx(); - dcx.emit_err(errors::MultipleMainFunctions { span }); - dcx.abort_if_errors(); - bug!(); + cx.tcx().dcx().emit_fatal(errors::MultipleMainFunctions { span }); }; // `main` should respect same config for frame pointer elimination as rest of code diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index e4633acd817..82488829b6e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -250,6 +250,38 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } } + sym::fadd_algebraic + | sym::fsub_algebraic + | sym::fmul_algebraic + | sym::fdiv_algebraic + | sym::frem_algebraic => match float_type_width(arg_tys[0]) { + Some(_width) => match name { + sym::fadd_algebraic => { + bx.fadd_algebraic(args[0].immediate(), args[1].immediate()) + } + sym::fsub_algebraic => { + bx.fsub_algebraic(args[0].immediate(), args[1].immediate()) + } + sym::fmul_algebraic => { + bx.fmul_algebraic(args[0].immediate(), args[1].immediate()) + } + sym::fdiv_algebraic => { + bx.fdiv_algebraic(args[0].immediate(), args[1].immediate()) + } + sym::frem_algebraic => { + bx.frem_algebraic(args[0].immediate(), args[1].immediate()) + } + _ => bug!(), + }, + None => { + bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType { + span, + name, + ty: arg_tys[0], + }); + return Ok(()); + } + }, sym::float_to_int_unchecked => { if float_type_width(arg_tys[0]).is_none() { diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 1c5c78e6ca2..86d3d1260c3 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -86,22 +86,27 @@ pub trait BuilderMethods<'a, 'tcx>: fn add(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn fadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn fadd_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn fadd_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn sub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn fsub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn fsub_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn fsub_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn mul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn fmul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn fmul_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn fmul_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn udiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn exactudiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn sdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn exactsdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn fdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn fdiv_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn fdiv_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn urem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn srem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn frem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn frem_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn frem_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn shl(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn lshr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn ashr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; 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 7099cdd5a75..9e4e7911c3a 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -3,10 +3,11 @@ use either::{Left, Right}; use rustc_hir::def::DefKind; use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo}; use rustc_middle::mir::{self, ConstAlloc, ConstValue}; +use rustc_middle::query::TyCtxtAt; use rustc_middle::traits::Reveal; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; use rustc_span::Span; use rustc_target::abi::{self, Abi}; @@ -87,13 +88,16 @@ fn eval_body_using_ecx<'mir, 'tcx>( } /// The `InterpCx` is only meant to be used to do field and index projections into constants for -/// `simd_shuffle` and const patterns in match arms. It never performs alignment checks. +/// `simd_shuffle` and const patterns in match arms. +/// +/// This should *not* be used to do any actual interpretation. In particular, alignment checks are +/// turned off! /// /// The function containing the `match` that is currently being analyzed may have generic bounds /// that inform us about the generic bounds of the constant. E.g., using an associated constant /// of a function's generic parameter will require knowledge about the bounds on the generic /// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument. -pub(crate) fn mk_eval_cx<'mir, 'tcx>( +pub(crate) fn mk_eval_cx_to_read_const_val<'mir, 'tcx>( tcx: TyCtxt<'tcx>, root_span: Span, param_env: ty::ParamEnv<'tcx>, @@ -108,6 +112,19 @@ pub(crate) fn mk_eval_cx<'mir, 'tcx>( ) } +/// Create an interpreter context to inspect the given `ConstValue`. +/// Returns both the context and an `OpTy` that represents the constant. +pub fn mk_eval_cx_for_const_val<'mir, 'tcx>( + tcx: TyCtxtAt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + val: mir::ConstValue<'tcx>, + ty: Ty<'tcx>, +) -> Option<(CompileTimeEvalContext<'mir, 'tcx>, OpTy<'tcx>)> { + let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No); + let op = ecx.const_val_to_op(val, ty, None).ok()?; + Some((ecx, op)) +} + /// This function converts an interpreter value into a MIR constant. /// /// The `for_diagnostics` flag turns the usual rules for returning `ConstValue::Scalar` into a @@ -203,7 +220,7 @@ pub(crate) fn turn_into_const_value<'tcx>( let def_id = cid.instance.def.def_id(); let is_static = tcx.is_static(def_id); // This is just accessing an already computed constant, so no need to check alignment here. - let ecx = mk_eval_cx( + let ecx = mk_eval_cx_to_read_const_val( tcx, tcx.def_span(key.value.instance.def_id()), key.param_env, diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 2c60ede7975..946ffc05cc1 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -393,11 +393,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, if ecx.tcx.is_ctfe_mir_available(def) { Ok(ecx.tcx.mir_for_ctfe(def)) } else if ecx.tcx.def_kind(def) == DefKind::AssocConst { - let guar = ecx - .tcx - .dcx() - .delayed_bug("This is likely a const item that is missing from its impl"); - throw_inval!(AlreadyReported(guar.into())); + ecx.tcx.dcx().bug("This is likely a const item that is missing from its impl"); } else { // `find_mir_or_eval_fn` checks that this is a const fn before even calling us, // so this should be unreachable. diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index cd50701040e..289dcb7d01d 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -47,8 +47,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( ty: Ty<'tcx>, ) -> Option<mir::DestructuredConstant<'tcx>> { let param_env = ty::ParamEnv::reveal_all(); - let ecx = mk_eval_cx(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No); - let op = ecx.const_val_to_op(val, ty, None).ok()?; + let (ecx, op) = mk_eval_cx_for_const_val(tcx, param_env, val, ty)?; // We go to `usize` as we cannot allocate anything bigger anyway. let (field_count, variant, down) = match ty.kind() { diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 514a6a7df76..d3428d27d52 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -5,7 +5,7 @@ use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use rustc_span::DUMMY_SP; use rustc_target::abi::{Abi, VariantIdx}; -use super::eval_queries::{mk_eval_cx, op_to_const}; +use super::eval_queries::{mk_eval_cx_to_read_const_val, op_to_const}; use super::machine::CompileTimeEvalContext; use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES}; use crate::const_eval::CanAccessMutGlobal; @@ -223,7 +223,7 @@ pub(crate) fn eval_to_valtree<'tcx>( let const_alloc = tcx.eval_to_allocation_raw(param_env.and(cid))?; // FIXME Need to provide a span to `eval_to_valtree` - let ecx = mk_eval_cx( + let ecx = mk_eval_cx_to_read_const_val( tcx, DUMMY_SP, param_env, @@ -287,7 +287,8 @@ pub fn valtree_to_const_value<'tcx>( } } ty::Ref(_, inner_ty, _) => { - let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No); + let mut ecx = + mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No); let imm = valtree_to_ref(&mut ecx, valtree, *inner_ty); let imm = ImmTy::from_immediate(imm, tcx.layout_of(param_env_ty).unwrap()); op_to_const(&ecx, &imm.into(), /* for diagnostics */ false) @@ -314,7 +315,8 @@ pub fn valtree_to_const_value<'tcx>( bug!("could not find non-ZST field during in {layout:#?}"); } - let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No); + let mut ecx = + mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No); // Need to create a place for this valtree. let place = create_valtree_place(&mut ecx, layout, valtree); 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 a3c4734f0a3..effaedd0820 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -249,7 +249,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { let secondary_errors = mem::take(&mut self.secondary_errors); if self.error_emitted.is_none() { for error in secondary_errors { - error.emit(); + self.error_emitted = Some(error.emit()); } } else { assert!(self.tcx.dcx().has_errors().is_some()); @@ -329,9 +329,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { fn check_static(&mut self, def_id: DefId, span: Span) { if self.tcx.is_thread_local_static(def_id) { - self.tcx - .dcx() - .span_delayed_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef`"); + self.tcx.dcx().span_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef`"); } self.check_op_spanned(ops::StaticAccess, span) } 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 25ddd5e85f9..5b4bbf8510b 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -93,6 +93,9 @@ pub struct FnCallNonConst<'tcx> { } impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> DiagnosticBuilder<'tcx> { let FnCallNonConst { caller, callee, args, span, call_source, feature } = *self; let ConstCx { tcx, param_env, .. } = *ccx; @@ -321,6 +324,8 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { .dcx() .create_err(errors::UnstableConstFn { span, def_path: ccx.tcx.def_path_str(def_id) }); + // FIXME: make this translatable + #[allow(rustc::untranslatable_diagnostic)] if ccx.is_const_stable_const_fn() { err.help("const-stable functions can only call other const-stable functions"); } else if ccx.tcx.sess.is_nightly_build() { @@ -591,6 +596,8 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess { span, format!("referencing statics in {}s is unstable", ccx.const_kind(),), ); + // FIXME: make this translatable + #[allow(rustc::untranslatable_diagnostic)] err .note("`static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.") .help("to fix this, the value can be extracted to a `const` and then used."); diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 1ff72516324..cc49e8ea247 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -517,7 +517,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { fn visit_source_scope(&mut self, scope: SourceScope) { if self.body.source_scopes.get(scope).is_none() { - self.tcx.dcx().span_delayed_bug( + self.tcx.dcx().span_bug( self.body.span, format!( "broken MIR in {:?} ({}):\ninvalid source scope {:?}", diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index d1c2d22b5a9..b8e15c485f5 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -6,7 +6,7 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_span::symbol::Symbol; use rustc_type_ir::Mutability; -use crate::const_eval::{mk_eval_cx, CanAccessMutGlobal, CompileTimeEvalContext}; +use crate::const_eval::{mk_eval_cx_to_read_const_val, CanAccessMutGlobal, CompileTimeEvalContext}; use crate::interpret::*; /// Allocate a `const core::panic::Location` with the provided filename and line/column numbers. @@ -57,7 +57,12 @@ pub(crate) fn const_caller_location_provider( col: u32, ) -> mir::ConstValue<'_> { trace!("const_caller_location: {}:{}:{}", file, line, col); - let mut ecx = mk_eval_cx(tcx.tcx, tcx.span, ty::ParamEnv::reveal_all(), CanAccessMutGlobal::No); + let mut ecx = mk_eval_cx_to_read_const_val( + tcx.tcx, + tcx.span, + ty::ParamEnv::reveal_all(), + CanAccessMutGlobal::No, + ); let loc_place = alloc_caller_location(&mut ecx, file, line, col); if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 60f11b1bdd4..692c059beb0 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -144,16 +144,6 @@ pub const EXIT_FAILURE: i32 = 1; pub const DEFAULT_BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\ ?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md"; -pub fn abort_on_err<T>(result: Result<T, ErrorGuaranteed>, sess: &Session) -> T { - match result { - Err(..) => { - sess.dcx().abort_if_errors(); - panic!("error reported but abort_if_errors didn't abort???"); - } - Ok(x) => x, - } -} - pub trait Callbacks { /// Called before creating the compiler instance fn config(&mut self, _config: &mut interface::Config) {} @@ -349,27 +339,33 @@ fn run_compiler( }, }; - callbacks.config(&mut config); - - default_early_dcx.abort_if_errors(); drop(default_early_dcx); + callbacks.config(&mut config); + interface::run_compiler(config, |compiler| { let sess = &compiler.sess; let codegen_backend = &*compiler.codegen_backend; + // This is used for early exits unrelated to errors. E.g. when just + // printing some information without compiling, or exiting immediately + // after parsing, etc. + let early_exit = || { + if let Some(guar) = sess.dcx().has_errors() { Err(guar) } else { Ok(()) } + }; + // This implements `-Whelp`. It should be handled very early, like // `--help`/`-Zhelp`/`-Chelp`. This is the earliest it can run, because // it must happen after lints are registered, during session creation. if sess.opts.describe_lints { describe_lints(sess); - return sess.compile_status(); + return early_exit(); } let early_dcx = EarlyDiagCtxt::new(sess.opts.error_format); if print_crate_info(&early_dcx, codegen_backend, sess, has_input) == Compilation::Stop { - return sess.compile_status(); + return early_exit(); } if !has_input { @@ -378,16 +374,16 @@ fn run_compiler( if !sess.opts.unstable_opts.ls.is_empty() { list_metadata(&early_dcx, sess, &*codegen_backend.metadata_loader()); - return sess.compile_status(); + return early_exit(); } if sess.opts.unstable_opts.link_only { process_rlink(sess, compiler); - return sess.compile_status(); + return early_exit(); } let linker = compiler.enter(|queries| { - let early_exit = || sess.compile_status().map(|_| None); + let early_exit = || early_exit().map(|_| None); queries.parse()?; if let Some(ppm) = &sess.opts.pretty { @@ -659,10 +655,11 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { }; } }; - let result = compiler.codegen_backend.link(sess, codegen_results, &outputs); - abort_on_err(result, sess); + if compiler.codegen_backend.link(sess, codegen_results, &outputs).is_err() { + FatalError.raise(); + } } else { - dcx.emit_fatal(RlinkNotAFile {}) + dcx.emit_fatal(RlinkNotAFile {}); } } diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index e5a7d550115..ff5ffd2454a 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -2,6 +2,7 @@ use rustc_ast as ast; use rustc_ast_pretty::pprust as pprust_ast; +use rustc_errors::FatalError; use rustc_hir as hir; use rustc_hir_pretty as pprust_hir; use rustc_middle::bug; @@ -18,7 +19,6 @@ use std::fmt::Write; pub use self::PpMode::*; pub use self::PpSourceMode::*; -use crate::abort_on_err; struct AstNoAnn; @@ -243,7 +243,9 @@ impl<'tcx> PrintExtra<'tcx> { pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { if ppm.needs_analysis() { - abort_on_err(ex.tcx().analysis(()), sess); + if ex.tcx().analysis(()).is_err() { + FatalError.raise(); + } } let (src, src_name) = get_source(sess); @@ -334,7 +336,9 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { ThirTree => { let tcx = ex.tcx(); let mut out = String::new(); - abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess); + if rustc_hir_analysis::check_crate(tcx).is_err() { + FatalError.raise(); + } debug!("pretty printing THIR tree"); for did in tcx.hir().body_owners() { let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_tree(did)); @@ -344,7 +348,9 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { ThirFlat => { let tcx = ex.tcx(); let mut out = String::new(); - abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess); + if rustc_hir_analysis::check_crate(tcx).is_err() { + FatalError.raise(); + } debug!("pretty printing THIR flat"); for did in tcx.hir().body_owners() { let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_flat(did)); diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 034636bea48..f096f015910 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1,18 +1,22 @@ use crate::snippet::Style; use crate::{ - CodeSuggestion, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, ErrCode, Level, - MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, + CodeSuggestion, DiagCtxt, DiagnosticMessage, ErrCode, ErrorGuaranteed, ExplicitBug, Level, + MultiSpan, StashKey, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, }; use rustc_data_structures::fx::FxIndexMap; use rustc_error_messages::fluent_value_from_str_list_sep_by_and; use rustc_error_messages::FluentValue; use rustc_lint_defs::{Applicability, LintExpectationId}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; use std::borrow::Cow; use std::fmt::{self, Debug}; use std::hash::{Hash, Hasher}; -use std::panic::Location; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; +use std::panic; +use std::thread::panicking; /// Error type for `Diagnostic`'s `suggestions` field, indicating that /// `.disable_suggestions()` was called on the `Diagnostic`. @@ -39,6 +43,86 @@ pub enum DiagnosticArgValue { StrListSepByAnd(Vec<Cow<'static, str>>), } +/// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee" +/// (or "proof") token that the emission happened. +pub trait EmissionGuarantee: Sized { + /// This exists so that bugs and fatal errors can both result in `!` (an + /// abort) when emitted, but have different aborting behaviour. + type EmitResult = Self; + + /// Implementation of `DiagnosticBuilder::emit`, fully controlled by each + /// `impl` of `EmissionGuarantee`, to make it impossible to create a value + /// of `Self::EmitResult` without actually performing the emission. + #[track_caller] + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult; +} + +impl EmissionGuarantee for ErrorGuaranteed { + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_error_guaranteed() + } +} + +impl EmissionGuarantee for () { + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_nothing(); + } +} + +/// Marker type which enables implementation of `create_bug` and `emit_bug` functions for +/// bug diagnostics. +#[derive(Copy, Clone)] +pub struct BugAbort; + +impl EmissionGuarantee for BugAbort { + type EmitResult = !; + + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_nothing(); + panic::panic_any(ExplicitBug); + } +} + +/// Marker type which enables implementation of `create_fatal` and `emit_fatal` functions for +/// fatal diagnostics. +#[derive(Copy, Clone)] +pub struct FatalAbort; + +impl EmissionGuarantee for FatalAbort { + type EmitResult = !; + + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_nothing(); + crate::FatalError.raise() + } +} + +impl EmissionGuarantee for rustc_span::fatal_error::FatalError { + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_nothing(); + rustc_span::fatal_error::FatalError + } +} + +/// Trait implemented by error types. This is rarely implemented manually. Instead, use +/// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic]. +#[rustc_diagnostic_item = "IntoDiagnostic"] +pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> { + /// Write out as a diagnostic out of `DiagCtxt`. + #[must_use] + fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G>; +} + +impl<'a, T, G> IntoDiagnostic<'a, G> for Spanned<T> +where + T: IntoDiagnostic<'a, G>, + G: EmissionGuarantee, +{ + fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> { + self.node.into_diagnostic(dcx, level).with_span(self.span) + } +} + /// Converts a value of a type into a `DiagnosticArg` (typically a field of an `IntoDiagnostic` /// struct). Implemented as a custom trait rather than `From` so that it is implemented on the type /// being converted rather than on `DiagnosticArgValue`, which enables types from other `rustc_*` @@ -71,17 +155,21 @@ where Self: Sized, { /// Add a subdiagnostic to an existing diagnostic. - fn add_to_diagnostic(self, diag: &mut Diagnostic) { + fn add_to_diagnostic<G: EmissionGuarantee>(self, diag: &mut DiagnosticBuilder<'_, G>) { self.add_to_diagnostic_with(diag, |_, m| m); } /// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used /// (to optionally perform eager translation). - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F); + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ); } -pub trait SubdiagnosticMessageOp = - Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage; +pub trait SubdiagnosticMessageOp<G> = + Fn(&mut DiagnosticBuilder<'_, G>, SubdiagnosticMessage) -> SubdiagnosticMessage; /// Trait implemented by lint types. This should not be implemented manually. Instead, use /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. @@ -93,32 +181,6 @@ pub trait DecorateLint<'a, G: EmissionGuarantee> { fn msg(&self) -> DiagnosticMessage; } -#[must_use] -#[derive(Clone, Debug, Encodable, Decodable)] -pub struct Diagnostic { - // NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes, - // outside of what methods in this crate themselves allow. - pub(crate) level: Level, - - pub messages: Vec<(DiagnosticMessage, Style)>, - pub code: Option<ErrCode>, - pub span: MultiSpan, - pub children: Vec<SubDiagnostic>, - pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>, - args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>, - - /// This is not used for highlighting or rendering any error message. Rather, it can be used - /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of - /// `span` if there is one. Otherwise, it is `DUMMY_SP`. - pub sort_span: Span, - - pub is_lint: Option<IsLint>, - - /// With `-Ztrack_diagnostics` enabled, - /// we print where in rustc this error was emitted. - pub(crate) emitted_at: DiagnosticLocation, -} - #[derive(Clone, Debug, Encodable, Decodable)] pub struct DiagnosticLocation { file: Cow<'static, str>, @@ -129,7 +191,7 @@ pub struct DiagnosticLocation { impl DiagnosticLocation { #[track_caller] fn caller() -> Self { - let loc = Location::caller(); + let loc = panic::Location::caller(); DiagnosticLocation { file: loc.file().into(), line: loc.line(), col: loc.column() } } } @@ -148,15 +210,6 @@ pub struct IsLint { has_future_breakage: bool, } -/// A "sub"-diagnostic attached to a parent diagnostic. -/// For example, a note attached to an error. -#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] -pub struct SubDiagnostic { - pub level: Level, - pub messages: Vec<(DiagnosticMessage, Style)>, - pub span: MultiSpan, -} - #[derive(Debug, PartialEq, Eq)] pub struct DiagnosticStyledString(pub Vec<StringPart>); @@ -206,6 +259,36 @@ impl StringPart { } } +/// The main part of a diagnostic. Note that `DiagnosticBuilder`, which wraps +/// this type, is used for most operations, and should be used instead whenever +/// possible. This type should only be used when `DiagnosticBuilder`'s lifetime +/// causes difficulties, e.g. when storing diagnostics within `DiagCtxt`. +#[must_use] +#[derive(Clone, Debug, Encodable, Decodable)] +pub struct Diagnostic { + // NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes, + // outside of what methods in this crate themselves allow. + pub(crate) level: Level, + + pub messages: Vec<(DiagnosticMessage, Style)>, + pub code: Option<ErrCode>, + pub span: MultiSpan, + pub children: Vec<SubDiagnostic>, + pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>, + args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>, + + /// This is not used for highlighting or rendering any error message. Rather, it can be used + /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of + /// `span` if there is one. Otherwise, it is `DUMMY_SP`. + pub sort_span: Span, + + pub is_lint: Option<IsLint>, + + /// With `-Ztrack_diagnostics` enabled, + /// we print where in rustc this error was emitted. + pub(crate) emitted_at: DiagnosticLocation, +} + impl Diagnostic { #[track_caller] pub fn new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self { @@ -289,6 +372,216 @@ impl Diagnostic { } } + // See comment on `DiagnosticBuilder::subdiagnostic_message_to_diagnostic_message`. + pub(crate) fn subdiagnostic_message_to_diagnostic_message( + &self, + attr: impl Into<SubdiagnosticMessage>, + ) -> DiagnosticMessage { + let msg = + self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages"); + msg.with_subdiagnostic_message(attr.into()) + } + + pub(crate) fn sub( + &mut self, + level: Level, + message: impl Into<SubdiagnosticMessage>, + span: MultiSpan, + ) { + let sub = SubDiagnostic { + level, + messages: vec![( + self.subdiagnostic_message_to_diagnostic_message(message), + Style::NoStyle, + )], + span, + }; + self.children.push(sub); + } + + pub(crate) fn arg(&mut self, name: impl Into<DiagnosticArgName>, arg: impl IntoDiagnosticArg) { + self.args.insert(name.into(), arg.into_diagnostic_arg()); + } + + pub fn args(&self) -> impl Iterator<Item = DiagnosticArg<'_>> { + self.args.iter() + } + + pub fn replace_args(&mut self, args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>) { + self.args = args; + } + + /// Fields used for Hash, and PartialEq trait. + fn keys( + &self, + ) -> ( + &Level, + &[(DiagnosticMessage, Style)], + &Option<ErrCode>, + &MultiSpan, + &[SubDiagnostic], + &Result<Vec<CodeSuggestion>, SuggestionsDisabled>, + Vec<(&DiagnosticArgName, &DiagnosticArgValue)>, + &Option<IsLint>, + ) { + ( + &self.level, + &self.messages, + &self.code, + &self.span, + &self.children, + &self.suggestions, + self.args().collect(), + // omit self.sort_span + &self.is_lint, + // omit self.emitted_at + ) + } +} + +impl Hash for Diagnostic { + fn hash<H>(&self, state: &mut H) + where + H: Hasher, + { + self.keys().hash(state); + } +} + +impl PartialEq for Diagnostic { + fn eq(&self, other: &Self) -> bool { + self.keys() == other.keys() + } +} + +/// A "sub"-diagnostic attached to a parent diagnostic. +/// For example, a note attached to an error. +#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] +pub struct SubDiagnostic { + pub level: Level, + pub messages: Vec<(DiagnosticMessage, Style)>, + pub span: MultiSpan, +} + +/// Used for emitting structured error messages and other diagnostic information. +/// Wraps a `Diagnostic`, adding some useful things. +/// - The `dcx` field, allowing it to (a) emit itself, and (b) do a drop check +/// that it has been emitted or cancelled. +/// - The `EmissionGuarantee`, which determines the type returned from `emit`. +/// +/// Each constructed `DiagnosticBuilder` must be consumed by a function such as +/// `emit`, `cancel`, `delay_as_bug`, or `into_diagnostic`. A panic occurrs if a +/// `DiagnosticBuilder` is dropped without being consumed by one of these +/// functions. +/// +/// If there is some state in a downstream crate you would like to +/// access in the methods of `DiagnosticBuilder` here, consider +/// extending `DiagCtxtFlags`. +#[must_use] +pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> { + pub dcx: &'a DiagCtxt, + + /// Why the `Option`? It is always `Some` until the `DiagnosticBuilder` is + /// consumed via `emit`, `cancel`, etc. At that point it is consumed and + /// replaced with `None`. Then `drop` checks that it is `None`; if not, it + /// panics because a diagnostic was built but not used. + /// + /// Why the Box? `Diagnostic` is a large type, and `DiagnosticBuilder` is + /// often used as a return value, especially within the frequently-used + /// `PResult` type. In theory, return value optimization (RVO) should avoid + /// unnecessary copying. In practice, it does not (at the time of writing). + diag: Option<Box<Diagnostic>>, + + _marker: PhantomData<G>, +} + +// Cloning a `DiagnosticBuilder` is a recipe for a diagnostic being emitted +// twice, which would be bad. +impl<G> !Clone for DiagnosticBuilder<'_, G> {} + +rustc_data_structures::static_assert_size!( + DiagnosticBuilder<'_, ()>, + 2 * std::mem::size_of::<usize>() +); + +impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> { + type Target = Diagnostic; + + fn deref(&self) -> &Diagnostic { + self.diag.as_ref().unwrap() + } +} + +impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> { + fn deref_mut(&mut self) -> &mut Diagnostic { + self.diag.as_mut().unwrap() + } +} + +impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.diag.fmt(f) + } +} + +/// `DiagnosticBuilder` impls many `&mut self -> &mut Self` methods. Each one +/// modifies an existing diagnostic, either in a standalone fashion, e.g. +/// `err.code(code);`, or in a chained fashion to make multiple modifications, +/// e.g. `err.code(code).span(span);`. +/// +/// This macro creates an equivalent `self -> Self` method, with a `with_` +/// prefix. This can be used in a chained fashion when making a new diagnostic, +/// e.g. `let err = struct_err(msg).with_code(code);`, or emitting a new +/// diagnostic, e.g. `struct_err(msg).with_code(code).emit();`. +/// +/// Although the latter method can be used to modify an existing diagnostic, +/// e.g. `err = err.with_code(code);`, this should be avoided because the former +/// method gives shorter code, e.g. `err.code(code);`. +/// +/// Note: the `with_` methods are added only when needed. If you want to use +/// one and it's not defined, feel free to add it. +/// +/// Note: any doc comments must be within the `with_fn!` call. +macro_rules! with_fn { + { + $with_f:ident, + $(#[$attrs:meta])* + pub fn $f:ident(&mut $self:ident, $($name:ident: $ty:ty),* $(,)?) -> &mut Self { + $($body:tt)* + } + } => { + // The original function. + $(#[$attrs])* + #[doc = concat!("See [`DiagnosticBuilder::", stringify!($f), "()`].")] + pub fn $f(&mut $self, $($name: $ty),*) -> &mut Self { + $($body)* + } + + // The `with_*` variant. + $(#[$attrs])* + #[doc = concat!("See [`DiagnosticBuilder::", stringify!($f), "()`].")] + pub fn $with_f(mut $self, $($name: $ty),*) -> Self { + $self.$f($($name),*); + $self + } + }; +} + +impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { + #[rustc_lint_diagnostics] + #[track_caller] + pub fn new<M: Into<DiagnosticMessage>>(dcx: &'a DiagCtxt, level: Level, message: M) -> Self { + Self::new_diagnostic(dcx, Diagnostic::new(level, message)) + } + + /// Creates a new `DiagnosticBuilder` with an already constructed + /// diagnostic. + #[track_caller] + pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diag: Diagnostic) -> Self { + debug!("Created new diagnostic"); + Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData } + } + /// Delay emission of this diagnostic as a bug. /// /// This can be useful in contexts where an error indicates a bug but @@ -309,6 +602,7 @@ impl Diagnostic { self.level = Level::DelayedBug; } + with_fn! { with_span_label, /// Appends a labeled span to the diagnostic. /// /// Labels are used to convey additional context for the diagnostic's primary span. They will @@ -323,10 +617,12 @@ impl Diagnostic { /// primary. #[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)); + let msg = self.subdiagnostic_message_to_diagnostic_message(label); + self.span.push_span_label(span, msg); self - } + } } + with_fn! { with_span_labels, /// Labels all the given spans with the provided label. /// See [`Self::span_label()`] for more information. pub fn span_labels(&mut self, spans: impl IntoIterator<Item = Span>, label: &str) -> &mut Self { @@ -334,7 +630,7 @@ impl Diagnostic { self.span_label(span, label.to_string()); } self - } + } } pub fn replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self { let before = self.span.clone(); @@ -412,39 +708,40 @@ impl Diagnostic { self } + with_fn! { with_note, /// Add a note attached to this diagnostic. #[rustc_lint_diagnostics] pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { self.sub(Level::Note, msg, MultiSpan::new()); self - } + } } fn highlighted_note(&mut self, msg: Vec<StringPart>) -> &mut Self { self.sub_with_highlights(Level::Note, msg, MultiSpan::new()); self } - /// Prints the span with a note above it. - /// This is like [`Diagnostic::note()`], but it gets its own span. + /// This is like [`DiagnosticBuilder::note()`], but it's only printed once. pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { self.sub(Level::OnceNote, msg, MultiSpan::new()); self } + with_fn! { with_span_note, /// Prints the span with a note above it. - /// This is like [`Diagnostic::note()`], but it gets its own span. + /// This is like [`DiagnosticBuilder::note()`], but it gets its own span. #[rustc_lint_diagnostics] - pub fn span_note<S: Into<MultiSpan>>( + pub fn span_note( &mut self, - sp: S, + sp: impl Into<MultiSpan>, msg: impl Into<SubdiagnosticMessage>, ) -> &mut Self { self.sub(Level::Note, msg, sp.into()); self - } + } } /// Prints the span with a note above it. - /// This is like [`Diagnostic::note()`], but it gets its own span. + /// This is like [`DiagnosticBuilder::note_once()`], but it gets its own span. pub fn span_note_once<S: Into<MultiSpan>>( &mut self, sp: S, @@ -454,15 +751,16 @@ impl Diagnostic { self } + with_fn! { with_warn, /// Add a warning attached to this diagnostic. #[rustc_lint_diagnostics] pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { self.sub(Level::Warning, msg, MultiSpan::new()); self - } + } } /// Prints the span with a warning above it. - /// This is like [`Diagnostic::warn()`], but it gets its own span. + /// This is like [`DiagnosticBuilder::warn()`], but it gets its own span. #[rustc_lint_diagnostics] pub fn span_warn<S: Into<MultiSpan>>( &mut self, @@ -473,15 +771,15 @@ impl Diagnostic { self } + with_fn! { with_help, /// Add a help message attached to this diagnostic. #[rustc_lint_diagnostics] pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { self.sub(Level::Help, msg, MultiSpan::new()); self - } + } } - /// Prints the span with a help above it. - /// This is like [`Diagnostic::help()`], but it gets its own span. + /// This is like [`DiagnosticBuilder::help()`], but it's only printed once. pub fn help_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { self.sub(Level::OnceHelp, msg, MultiSpan::new()); self @@ -494,7 +792,7 @@ impl Diagnostic { } /// Prints the span with some help above it. - /// This is like [`Diagnostic::help()`], but it gets its own span. + /// This is like [`DiagnosticBuilder::help()`], but it gets its own span. #[rustc_lint_diagnostics] pub fn span_help<S: Into<MultiSpan>>( &mut self, @@ -531,6 +829,7 @@ impl Diagnostic { } } + with_fn! { with_multipart_suggestion, /// Show a suggestion that has multiple parts to it. /// In other words, multiple changes need to be applied as part of this suggestion. pub fn multipart_suggestion( @@ -545,7 +844,7 @@ impl Diagnostic { applicability, SuggestionStyle::ShowCode, ) - } + } } /// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic. /// In other words, multiple changes need to be applied as part of this suggestion. @@ -562,7 +861,8 @@ impl Diagnostic { SuggestionStyle::ShowAlways, ) } - /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. + + /// [`DiagnosticBuilder::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. pub fn multipart_suggestion_with_style( &mut self, msg: impl Into<SubdiagnosticMessage>, @@ -619,6 +919,7 @@ impl Diagnostic { ) } + with_fn! { with_span_suggestion, /// Prints out a message with a suggested edit of the code. /// /// In case of short messages and a simple suggestion, rustc displays it as a label: @@ -651,9 +952,9 @@ impl Diagnostic { SuggestionStyle::ShowCode, ); self - } + } } - /// [`Diagnostic::span_suggestion()`] but you can set the [`SuggestionStyle`]. + /// [`DiagnosticBuilder::span_suggestion()`] but you can set the [`SuggestionStyle`]. pub fn span_suggestion_with_style( &mut self, sp: Span, @@ -677,6 +978,7 @@ impl Diagnostic { self } + with_fn! { with_span_suggestion_verbose, /// Always show the suggested change. pub fn span_suggestion_verbose( &mut self, @@ -693,10 +995,11 @@ impl Diagnostic { SuggestionStyle::ShowAlways, ); self - } + } } + with_fn! { with_span_suggestions, /// Prints out a message with multiple suggested edits of the code. - /// See also [`Diagnostic::span_suggestion()`]. + /// See also [`DiagnosticBuilder::span_suggestion()`]. pub fn span_suggestions( &mut self, sp: Span, @@ -711,9 +1014,8 @@ impl Diagnostic { applicability, SuggestionStyle::ShowCode, ) - } + } } - /// [`Diagnostic::span_suggestions()`] but you can set the [`SuggestionStyle`]. pub fn span_suggestions_with_style( &mut self, sp: Span, @@ -743,7 +1045,7 @@ impl Diagnostic { /// Prints out a message with multiple suggested edits of the code, where each edit consists of /// multiple parts. - /// See also [`Diagnostic::multipart_suggestion()`]. + /// See also [`DiagnosticBuilder::multipart_suggestion()`]. pub fn multipart_suggestions( &mut self, msg: impl Into<SubdiagnosticMessage>, @@ -785,6 +1087,7 @@ impl Diagnostic { self } + with_fn! { with_span_suggestion_short, /// Prints out a message with a suggested edit of the code. If the suggestion is presented /// inline, it will only show the message and not the suggestion. /// @@ -804,7 +1107,7 @@ impl Diagnostic { SuggestionStyle::HideCodeInline, ); self - } + } } /// Prints out a message for a suggestion without showing the suggested code. /// @@ -829,6 +1132,7 @@ impl Diagnostic { self } + with_fn! { with_tool_only_span_suggestion, /// Adds a suggestion to the JSON output that will not be shown in the CLI. /// /// This is intended to be used for suggestions that are *very* obvious in what the changes @@ -849,7 +1153,7 @@ impl Diagnostic { SuggestionStyle::CompletelyHidden, ); self - } + } } /// Add a subdiagnostic from a type that implements `Subdiagnostic` (see /// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages @@ -868,45 +1172,45 @@ impl Diagnostic { self } - pub fn span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self { + with_fn! { with_span, + /// Add a span. + pub fn span(&mut self, sp: impl Into<MultiSpan>) -> &mut Self { self.span = sp.into(); if let Some(span) = self.span.primary_span() { self.sort_span = span; } self - } + } } pub fn is_lint(&mut self, name: String, has_future_breakage: bool) -> &mut Self { self.is_lint = Some(IsLint { name, has_future_breakage }); self } + with_fn! { with_code, + /// Add an error code. pub fn code(&mut self, code: ErrCode) -> &mut Self { self.code = Some(code); self - } + } } + with_fn! { with_primary_message, + /// Add a primary message. pub fn primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { self.messages[0] = (msg.into(), Style::NoStyle); self - } - - pub fn args(&self) -> impl Iterator<Item = DiagnosticArg<'_>> { - self.args.iter() - } + } } + with_fn! { with_arg, + /// Add an argument. pub fn arg( &mut self, name: impl Into<DiagnosticArgName>, arg: impl IntoDiagnosticArg, ) -> &mut Self { - self.args.insert(name.into(), arg.into_diagnostic_arg()); + self.deref_mut().arg(name, arg); self - } - - pub fn replace_args(&mut self, args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>) { - self.args = args; - } + } } /// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by /// combining it with the primary message of the diagnostic (if translatable, otherwise it just @@ -915,9 +1219,7 @@ impl Diagnostic { &self, attr: impl Into<SubdiagnosticMessage>, ) -> DiagnosticMessage { - let msg = - self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages"); - msg.with_subdiagnostic_message(attr.into()) + self.deref().subdiagnostic_message_to_diagnostic_message(attr) } /// Convenience function for internal use, clients should use one of the @@ -925,15 +1227,7 @@ impl Diagnostic { /// /// Used by `proc_macro_server` for implementing `server::Diagnostic`. pub fn sub(&mut self, level: Level, message: impl Into<SubdiagnosticMessage>, span: MultiSpan) { - let sub = SubDiagnostic { - level, - messages: vec![( - self.subdiagnostic_message_to_diagnostic_message(message), - Style::NoStyle, - )], - span, - }; - self.children.push(sub); + self.deref_mut().sub(level, message, span); } /// Convenience function for internal use, clients should use one of the @@ -947,45 +1241,111 @@ impl Diagnostic { self.children.push(sub); } - /// Fields used for Hash, and PartialEq trait - fn keys( - &self, - ) -> ( - &Level, - &[(DiagnosticMessage, Style)], - &Option<ErrCode>, - &MultiSpan, - &[SubDiagnostic], - &Result<Vec<CodeSuggestion>, SuggestionsDisabled>, - Vec<(&DiagnosticArgName, &DiagnosticArgValue)>, - &Option<IsLint>, - ) { - ( - &self.level, - &self.messages, - &self.code, - &self.span, - &self.children, - &self.suggestions, - self.args().collect(), - // omit self.sort_span - &self.is_lint, - // omit self.emitted_at - ) + /// Takes the diagnostic. For use by methods that consume the + /// DiagnosticBuilder: `emit`, `cancel`, etc. Afterwards, `drop` is the + /// only code that will be run on `self`. + fn take_diag(&mut self) -> Diagnostic { + Box::into_inner(self.diag.take().unwrap()) } -} -impl Hash for Diagnostic { - fn hash<H>(&self, state: &mut H) - where - H: Hasher, - { - self.keys().hash(state); + /// Most `emit_producing_guarantee` functions use this as a starting point. + fn emit_producing_nothing(mut self) { + let diag = self.take_diag(); + self.dcx.emit_diagnostic(diag); + } + + /// `ErrorGuaranteed::emit_producing_guarantee` uses this. + fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed { + let diag = self.take_diag(); + + // The only error levels that produce `ErrorGuaranteed` are + // `Error` and `DelayedBug`. But `DelayedBug` should never occur here + // because delayed bugs have their level changed to `Bug` when they are + // actually printed, so they produce an ICE. + // + // (Also, even though `level` isn't `pub`, the whole `Diagnostic` could + // be overwritten with a new one thanks to `DerefMut`. So this assert + // protects against that, too.) + assert!( + matches!(diag.level, Level::Error | Level::DelayedBug), + "invalid diagnostic level ({:?})", + diag.level, + ); + + let guar = self.dcx.emit_diagnostic(diag); + guar.unwrap() + } + + /// Emit and consume the diagnostic. + #[track_caller] + pub fn emit(self) -> G::EmitResult { + G::emit_producing_guarantee(self) + } + + /// Emit the diagnostic unless `delay` is true, + /// in which case the emission will be delayed as a bug. + /// + /// See `emit` and `delay_as_bug` for details. + #[track_caller] + pub fn emit_unless(mut self, delay: bool) -> G::EmitResult { + if delay { + self.downgrade_to_delayed_bug(); + } + self.emit() + } + + /// Cancel and consume the diagnostic. (A diagnostic must either be emitted or + /// cancelled or it will panic when dropped). + pub fn cancel(mut self) { + self.diag = None; + drop(self); + } + + /// Stashes diagnostic for possible later improvement in a different, + /// later stage of the compiler. The diagnostic can be accessed with + /// the provided `span` and `key` through [`DiagCtxt::steal_diagnostic()`]. + pub fn stash(mut self, span: Span, key: StashKey) { + self.dcx.stash_diagnostic(span, key, self.take_diag()); + } + + /// Delay emission of this diagnostic as a bug. + /// + /// This can be useful in contexts where an error indicates a bug but + /// typically this only happens when other compilation errors have already + /// happened. In those cases this can be used to defer emission of this + /// diagnostic as a bug in the compiler only if no other errors have been + /// emitted. + /// + /// In the meantime, though, callsites are required to deal with the "bug" + /// locally in whichever way makes the most sense. + #[track_caller] + pub fn delay_as_bug(mut self) -> G::EmitResult { + self.downgrade_to_delayed_bug(); + self.emit() } } -impl PartialEq for Diagnostic { - fn eq(&self, other: &Self) -> bool { - self.keys() == other.keys() +/// Destructor bomb: every `DiagnosticBuilder` must be consumed (emitted, +/// cancelled, etc.) or we emit a bug. +impl<G: EmissionGuarantee> Drop for DiagnosticBuilder<'_, G> { + fn drop(&mut self) { + match self.diag.take() { + Some(diag) if !panicking() => { + self.dcx.emit_diagnostic(Diagnostic::new( + Level::Bug, + DiagnosticMessage::from("the following error was constructed but not emitted"), + )); + self.dcx.emit_diagnostic(*diag); + panic!("error was constructed but not emitted"); + } + _ => {} + } } } + +#[macro_export] +macro_rules! struct_span_code_err { + ($dcx:expr, $span:expr, $code:expr, $($message:tt)*) => ({ + $dcx.struct_span_err($span, format!($($message)*)).with_code($code) + }) +} diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs deleted file mode 100644 index 0572df69ca9..00000000000 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ /dev/null @@ -1,441 +0,0 @@ -use crate::diagnostic::IntoDiagnosticArg; -use crate::{DiagCtxt, Level, MultiSpan, StashKey}; -use crate::{ - Diagnostic, DiagnosticMessage, DiagnosticStyledString, ErrCode, ErrorGuaranteed, ExplicitBug, - SubdiagnosticMessage, -}; -use rustc_lint_defs::Applicability; -use rustc_span::source_map::Spanned; - -use rustc_span::Span; -use std::borrow::Cow; -use std::fmt::{self, Debug}; -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; -use std::panic; -use std::thread::panicking; - -/// Trait implemented by error types. This is rarely implemented manually. Instead, use -/// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic]. -#[rustc_diagnostic_item = "IntoDiagnostic"] -pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> { - /// Write out as a diagnostic out of `DiagCtxt`. - #[must_use] - fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G>; -} - -impl<'a, T, G> IntoDiagnostic<'a, G> for Spanned<T> -where - T: IntoDiagnostic<'a, G>, - G: EmissionGuarantee, -{ - fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> { - self.node.into_diagnostic(dcx, level).with_span(self.span) - } -} - -/// Used for emitting structured error messages and other diagnostic information. -/// Each constructed `DiagnosticBuilder` must be consumed by a function such as -/// `emit`, `cancel`, `delay_as_bug`, or `into_diagnostic`. A panic occurrs if a -/// `DiagnosticBuilder` is dropped without being consumed by one of these -/// functions. -/// -/// If there is some state in a downstream crate you would like to -/// access in the methods of `DiagnosticBuilder` here, consider -/// extending `DiagCtxtFlags`. -#[must_use] -pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> { - pub dcx: &'a DiagCtxt, - - /// Why the `Option`? It is always `Some` until the `DiagnosticBuilder` is - /// consumed via `emit`, `cancel`, etc. At that point it is consumed and - /// replaced with `None`. Then `drop` checks that it is `None`; if not, it - /// panics because a diagnostic was built but not used. - /// - /// Why the Box? `Diagnostic` is a large type, and `DiagnosticBuilder` is - /// often used as a return value, especially within the frequently-used - /// `PResult` type. In theory, return value optimization (RVO) should avoid - /// unnecessary copying. In practice, it does not (at the time of writing). - diag: Option<Box<Diagnostic>>, - - _marker: PhantomData<G>, -} - -// Cloning a `DiagnosticBuilder` is a recipe for a diagnostic being emitted -// twice, which would be bad. -impl<G> !Clone for DiagnosticBuilder<'_, G> {} - -rustc_data_structures::static_assert_size!( - DiagnosticBuilder<'_, ()>, - 2 * std::mem::size_of::<usize>() -); - -/// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee" -/// (or "proof") token that the emission happened. -pub trait EmissionGuarantee: Sized { - /// This exists so that bugs and fatal errors can both result in `!` (an - /// abort) when emitted, but have different aborting behaviour. - type EmitResult = Self; - - /// Implementation of `DiagnosticBuilder::emit`, fully controlled by each - /// `impl` of `EmissionGuarantee`, to make it impossible to create a value - /// of `Self::EmitResult` without actually performing the emission. - #[track_caller] - fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult; -} - -impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { - /// Takes the diagnostic. For use by methods that consume the - /// DiagnosticBuilder: `emit`, `cancel`, etc. Afterwards, `drop` is the - /// only code that will be run on `self`. - fn take_diag(&mut self) -> Diagnostic { - Box::into_inner(self.diag.take().unwrap()) - } - - /// Most `emit_producing_guarantee` functions use this as a starting point. - fn emit_producing_nothing(mut self) { - let diag = self.take_diag(); - self.dcx.emit_diagnostic(diag); - } - - /// `ErrorGuaranteed::emit_producing_guarantee` uses this. - fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed { - let diag = self.take_diag(); - - // The only error levels that produce `ErrorGuaranteed` are - // `Error` and `DelayedBug`. But `DelayedBug` should never occur here - // because delayed bugs have their level changed to `Bug` when they are - // actually printed, so they produce an ICE. - // - // (Also, even though `level` isn't `pub`, the whole `Diagnostic` could - // be overwritten with a new one thanks to `DerefMut`. So this assert - // protects against that, too.) - assert!( - matches!(diag.level, Level::Error | Level::DelayedBug), - "invalid diagnostic level ({:?})", - diag.level, - ); - - let guar = self.dcx.emit_diagnostic(diag); - guar.unwrap() - } -} - -impl EmissionGuarantee for ErrorGuaranteed { - fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { - db.emit_producing_error_guaranteed() - } -} - -impl EmissionGuarantee for () { - fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { - db.emit_producing_nothing(); - } -} - -/// Marker type which enables implementation of `create_bug` and `emit_bug` functions for -/// bug diagnostics. -#[derive(Copy, Clone)] -pub struct BugAbort; - -impl EmissionGuarantee for BugAbort { - type EmitResult = !; - - fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { - db.emit_producing_nothing(); - panic::panic_any(ExplicitBug); - } -} - -/// Marker type which enables implementation of `create_fatal` and `emit_fatal` functions for -/// fatal diagnostics. -#[derive(Copy, Clone)] -pub struct FatalAbort; - -impl EmissionGuarantee for FatalAbort { - type EmitResult = !; - - fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { - db.emit_producing_nothing(); - crate::FatalError.raise() - } -} - -impl EmissionGuarantee for rustc_span::fatal_error::FatalError { - fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { - db.emit_producing_nothing(); - rustc_span::fatal_error::FatalError - } -} - -/// `DiagnosticBuilder` impls `DerefMut`, which allows access to the fields and -/// methods of the embedded `Diagnostic`. However, that doesn't allow method -/// chaining at the `DiagnosticBuilder` level. Each use of this macro defines -/// two builder methods at that level, both of which wrap the equivalent method -/// in `Diagnostic`. -/// - A `&mut self -> &mut Self` method, with the same name as the underlying -/// `Diagnostic` method. It is mostly to modify existing diagnostics, either -/// in a standalone fashion, e.g. `err.code(code)`, or in a chained fashion -/// to make multiple modifications, e.g. `err.code(code).span(span)`. -/// - A `self -> Self` method, which has a `with_` prefix added. -/// It is mostly used in a chained fashion when producing a new diagnostic, -/// e.g. `let err = struct_err(msg).with_code(code)`, or when emitting a new -/// diagnostic , e.g. `struct_err(msg).with_code(code).emit()`. -/// -/// Although the latter method can be used to modify an existing diagnostic, -/// e.g. `err = err.with_code(code)`, this should be avoided because the former -/// method gives shorter code, e.g. `err.code(code)`. -macro_rules! forward { - ( - ($f:ident, $with_f:ident)($($name:ident: $ty:ty),* $(,)?) - ) => { - #[doc = concat!("See [`Diagnostic::", stringify!($f), "()`].")] - pub fn $f(&mut self, $($name: $ty),*) -> &mut Self { - self.diag.as_mut().unwrap().$f($($name),*); - self - } - #[doc = concat!("See [`Diagnostic::", stringify!($f), "()`].")] - pub fn $with_f(mut self, $($name: $ty),*) -> Self { - self.diag.as_mut().unwrap().$f($($name),*); - self - } - }; -} - -impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> { - type Target = Diagnostic; - - fn deref(&self) -> &Diagnostic { - self.diag.as_ref().unwrap() - } -} - -impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> { - fn deref_mut(&mut self) -> &mut Diagnostic { - self.diag.as_mut().unwrap() - } -} - -impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { - #[rustc_lint_diagnostics] - #[track_caller] - pub fn new<M: Into<DiagnosticMessage>>(dcx: &'a DiagCtxt, level: Level, message: M) -> Self { - Self::new_diagnostic(dcx, Diagnostic::new(level, message)) - } - - /// Creates a new `DiagnosticBuilder` with an already constructed - /// diagnostic. - #[track_caller] - pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diag: Diagnostic) -> Self { - debug!("Created new diagnostic"); - Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData } - } - - /// Emit and consume the diagnostic. - #[track_caller] - pub fn emit(self) -> G::EmitResult { - G::emit_producing_guarantee(self) - } - - /// Emit the diagnostic unless `delay` is true, - /// in which case the emission will be delayed as a bug. - /// - /// See `emit` and `delay_as_bug` for details. - #[track_caller] - pub fn emit_unless(mut self, delay: bool) -> G::EmitResult { - if delay { - self.downgrade_to_delayed_bug(); - } - self.emit() - } - - /// Cancel and consume the diagnostic. (A diagnostic must either be emitted or - /// cancelled or it will panic when dropped). - pub fn cancel(mut self) { - self.diag = None; - drop(self); - } - - /// Stashes diagnostic for possible later improvement in a different, - /// later stage of the compiler. The diagnostic can be accessed with - /// the provided `span` and `key` through [`DiagCtxt::steal_diagnostic()`]. - pub fn stash(mut self, span: Span, key: StashKey) { - self.dcx.stash_diagnostic(span, key, self.take_diag()); - } - - /// Delay emission of this diagnostic as a bug. - /// - /// This can be useful in contexts where an error indicates a bug but - /// typically this only happens when other compilation errors have already - /// happened. In those cases this can be used to defer emission of this - /// diagnostic as a bug in the compiler only if no other errors have been - /// emitted. - /// - /// In the meantime, though, callsites are required to deal with the "bug" - /// locally in whichever way makes the most sense. - #[track_caller] - pub fn delay_as_bug(mut self) -> G::EmitResult { - self.downgrade_to_delayed_bug(); - self.emit() - } - - forward!((span_label, with_span_label)( - span: Span, - label: impl Into<SubdiagnosticMessage>, - )); - forward!((span_labels, with_span_labels)( - spans: impl IntoIterator<Item = Span>, - label: &str, - )); - forward!((note_expected_found, with_note_expected_found)( - expected_label: &dyn fmt::Display, - expected: DiagnosticStyledString, - found_label: &dyn fmt::Display, - found: DiagnosticStyledString, - )); - forward!((note_expected_found_extra, with_note_expected_found_extra)( - expected_label: &dyn fmt::Display, - expected: DiagnosticStyledString, - found_label: &dyn fmt::Display, - found: DiagnosticStyledString, - expected_extra: &dyn fmt::Display, - found_extra: &dyn fmt::Display, - )); - forward!((note, with_note)( - msg: impl Into<SubdiagnosticMessage>, - )); - forward!((note_once, with_note_once)( - msg: impl Into<SubdiagnosticMessage>, - )); - forward!((span_note, with_span_note)( - sp: impl Into<MultiSpan>, - msg: impl Into<SubdiagnosticMessage>, - )); - forward!((span_note_once, with_span_note_once)( - sp: impl Into<MultiSpan>, - msg: impl Into<SubdiagnosticMessage>, - )); - forward!((warn, with_warn)( - msg: impl Into<SubdiagnosticMessage>, - )); - forward!((span_warn, with_span_warn)( - sp: impl Into<MultiSpan>, - msg: impl Into<SubdiagnosticMessage>, - )); - forward!((help, with_help)( - msg: impl Into<SubdiagnosticMessage>, - )); - forward!((help_once, with_help_once)( - msg: impl Into<SubdiagnosticMessage>, - )); - forward!((span_help, with_span_help_once)( - sp: impl Into<MultiSpan>, - msg: impl Into<SubdiagnosticMessage>, - )); - forward!((multipart_suggestion, with_multipart_suggestion)( - msg: impl Into<SubdiagnosticMessage>, - suggestion: Vec<(Span, String)>, - applicability: Applicability, - )); - forward!((multipart_suggestion_verbose, with_multipart_suggestion_verbose)( - msg: impl Into<SubdiagnosticMessage>, - suggestion: Vec<(Span, String)>, - applicability: Applicability, - )); - forward!((tool_only_multipart_suggestion, with_tool_only_multipart_suggestion)( - msg: impl Into<SubdiagnosticMessage>, - suggestion: Vec<(Span, String)>, - applicability: Applicability, - )); - forward!((span_suggestion, with_span_suggestion)( - sp: Span, - msg: impl Into<SubdiagnosticMessage>, - suggestion: impl ToString, - applicability: Applicability, - )); - forward!((span_suggestions, with_span_suggestions)( - sp: Span, - msg: impl Into<SubdiagnosticMessage>, - suggestions: impl IntoIterator<Item = String>, - applicability: Applicability, - )); - forward!((multipart_suggestions, with_multipart_suggestions)( - msg: impl Into<SubdiagnosticMessage>, - suggestions: impl IntoIterator<Item = Vec<(Span, String)>>, - applicability: Applicability, - )); - forward!((span_suggestion_short, with_span_suggestion_short)( - sp: Span, - msg: impl Into<SubdiagnosticMessage>, - suggestion: impl ToString, - applicability: Applicability, - )); - forward!((span_suggestion_verbose, with_span_suggestion_verbose)( - sp: Span, - msg: impl Into<SubdiagnosticMessage>, - suggestion: impl ToString, - applicability: Applicability, - )); - forward!((span_suggestion_hidden, with_span_suggestion_hidden)( - sp: Span, - msg: impl Into<SubdiagnosticMessage>, - suggestion: impl ToString, - applicability: Applicability, - )); - forward!((tool_only_span_suggestion, with_tool_only_span_suggestion)( - sp: Span, - msg: impl Into<SubdiagnosticMessage>, - suggestion: impl ToString, - applicability: Applicability, - )); - forward!((primary_message, with_primary_message)( - msg: impl Into<DiagnosticMessage>, - )); - forward!((span, with_span)( - sp: impl Into<MultiSpan>, - )); - forward!((is_lint, with_is_lint)( - name: String, has_future_breakage: bool, - )); - forward!((code, with_code)( - code: ErrCode, - )); - forward!((arg, with_arg)( - name: impl Into<Cow<'static, str>>, arg: impl IntoDiagnosticArg, - )); - forward!((subdiagnostic, with_subdiagnostic)( - dcx: &DiagCtxt, - subdiagnostic: impl crate::AddToDiagnostic, - )); -} - -impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.diag.fmt(f) - } -} - -/// Destructor bomb: every `DiagnosticBuilder` must be consumed (emitted, -/// cancelled, etc.) or we emit a bug. -impl<G: EmissionGuarantee> Drop for DiagnosticBuilder<'_, G> { - fn drop(&mut self) { - match self.diag.take() { - Some(diag) if !panicking() => { - self.dcx.emit_diagnostic(Diagnostic::new( - Level::Bug, - DiagnosticMessage::from("the following error was constructed but not emitted"), - )); - self.dcx.emit_diagnostic(*diag); - panic!("error was constructed but not emitted"); - } - _ => {} - } - } -} - -#[macro_export] -macro_rules! struct_span_code_err { - ($dcx:expr, $span:expr, $code:expr, $($message:tt)*) => ({ - $dcx.struct_span_err($span, format!($($message)*)).with_code($code) - }) -} diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index eaf75539f59..bc1e81642ff 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -299,7 +299,11 @@ pub struct SingleLabelManySpans { pub label: &'static str, } impl AddToDiagnostic for SingleLabelManySpans { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut crate::Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _: F, + ) { diag.span_labels(self.spans, self.label); } } @@ -312,23 +316,6 @@ pub struct ExpectedLifetimeParameter { pub count: usize, } -#[derive(Subdiagnostic)] -#[note(errors_delayed_at_with_newline)] -pub struct DelayedAtWithNewline { - #[primary_span] - pub span: Span, - pub emitted_at: DiagnosticLocation, - pub note: Backtrace, -} -#[derive(Subdiagnostic)] -#[note(errors_delayed_at_without_newline)] -pub struct DelayedAtWithoutNewline { - #[primary_span] - pub span: Span, - pub emitted_at: DiagnosticLocation, - pub note: Backtrace, -} - impl IntoDiagnosticArg for DiagnosticLocation { fn into_diagnostic_arg(self) -> DiagnosticArgValue { DiagnosticArgValue::Str(Cow::from(self.to_string())) @@ -341,13 +328,6 @@ impl IntoDiagnosticArg for Backtrace { } } -#[derive(Subdiagnostic)] -#[note(errors_invalid_flushed_delayed_diagnostic_level)] -pub struct InvalidFlushedDelayedDiagnosticLevel { - #[primary_span] - pub span: Span, - pub level: Level, -} impl IntoDiagnosticArg for Level { fn into_diagnostic_arg(self) -> DiagnosticArgValue { DiagnosticArgValue::Str(Cow::from(self.to_string())) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index e09c041c1d0..df94b69004b 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -599,7 +599,7 @@ impl Emitter for SilentEmitter { fn emit_diagnostic(&mut self, mut diag: Diagnostic) { if diag.level == Level::Fatal { - diag.note(self.fatal_note.clone()); + diag.sub(Level::Note, self.fatal_note.clone(), MultiSpan::new()); self.fatal_dcx.emit_diagnostic(diag); } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 064ea8d7516..7e3d15ffc92 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -37,16 +37,13 @@ extern crate self as rustc_errors; pub use codes::*; pub use diagnostic::{ - AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgName, - DiagnosticArgValue, DiagnosticStyledString, IntoDiagnosticArg, StringPart, SubDiagnostic, - SubdiagnosticMessageOp, -}; -pub use diagnostic_builder::{ - BugAbort, DiagnosticBuilder, EmissionGuarantee, FatalAbort, IntoDiagnostic, + AddToDiagnostic, BugAbort, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgName, + DiagnosticArgValue, DiagnosticBuilder, DiagnosticStyledString, EmissionGuarantee, FatalAbort, + IntoDiagnostic, IntoDiagnosticArg, StringPart, SubDiagnostic, SubdiagnosticMessageOp, }; pub use diagnostic_impls::{ DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter, - IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, SingleLabelManySpans, + IndicateAnonymousLifetime, SingleLabelManySpans, }; pub use emitter::ColorConfig; pub use rustc_error_messages::{ @@ -62,7 +59,6 @@ pub use snippet::Style; // See https://github.com/rust-lang/rust/pull/115393. pub use termcolor::{Color, ColorSpec, WriteColor}; -use crate::diagnostic_impls::{DelayedAtWithNewline, DelayedAtWithoutNewline}; use emitter::{is_case_difference, DynEmitter, Emitter, HumanEmitter}; use registry::Registry; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; @@ -88,7 +84,6 @@ use Level::*; pub mod annotate_snippet_emitter_writer; pub mod codes; mod diagnostic; -mod diagnostic_builder; mod diagnostic_impls; pub mod emitter; pub mod error; @@ -476,9 +471,10 @@ struct DiagCtxtInner { emitted_diagnostics: FxHashSet<Hash128>, /// Stashed diagnostics emitted in one stage of the compiler that may be - /// stolen by other stages (e.g. to improve them and add more information). - /// The stashed diagnostics count towards the total error count. - /// When `.abort_if_errors()` is called, these are also emitted. + /// stolen and emitted/cancelled by other stages (e.g. to improve them and + /// add more information). All stashed diagnostics must be emitted with + /// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped, + /// otherwise an assertion failure will occur. stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>, future_breakage_diagnostics: Vec<Diagnostic>, @@ -563,7 +559,9 @@ pub struct DiagCtxtFlags { impl Drop for DiagCtxtInner { fn drop(&mut self) { - self.emit_stashed_diagnostics(); + // Any stashed diagnostics should have been handled by + // `emit_stashed_diagnostics` by now. + assert!(self.stashed_diagnostics.is_empty()); if self.err_guars.is_empty() { self.flush_delayed() @@ -755,17 +753,24 @@ impl DiagCtxt { } /// Emit all stashed diagnostics. - pub fn emit_stashed_diagnostics(&self) { + pub fn emit_stashed_diagnostics(&self) -> Option<ErrorGuaranteed> { self.inner.borrow_mut().emit_stashed_diagnostics() } - /// This excludes lint errors, delayed bugs, and stashed errors. + /// This excludes lint errors, delayed bugs and stashed errors. #[inline] - pub fn err_count(&self) -> usize { + pub fn err_count_excluding_lint_errs(&self) -> usize { self.inner.borrow().err_guars.len() } - /// This excludes normal errors, lint errors and delayed bugs. Unless + /// This excludes delayed bugs and stashed errors. + #[inline] + pub fn err_count(&self) -> usize { + let inner = self.inner.borrow(); + inner.err_guars.len() + inner.lint_err_guars.len() + } + + /// This excludes normal errors, lint errors, and delayed bugs. Unless /// absolutely necessary, avoid using this. It's dubious because stashed /// errors can later be cancelled, so the presence of a stashed error at /// some point of time doesn't guarantee anything -- there are no @@ -774,27 +779,29 @@ impl DiagCtxt { self.inner.borrow().stashed_err_count } - /// This excludes lint errors, delayed bugs, and stashed errors. - pub fn has_errors(&self) -> Option<ErrorGuaranteed> { - self.inner.borrow().has_errors() + /// This excludes lint errors, delayed bugs, and stashed errors. Unless + /// absolutely necessary, prefer `has_errors` to this method. + pub fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> { + self.inner.borrow().has_errors_excluding_lint_errors() } - /// This excludes delayed bugs and stashed errors. Unless absolutely - /// necessary, prefer `has_errors` to this method. - pub fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> { - self.inner.borrow().has_errors_or_lint_errors() + /// This excludes delayed bugs and stashed errors. + pub fn has_errors(&self) -> Option<ErrorGuaranteed> { + self.inner.borrow().has_errors() } /// This excludes stashed errors. Unless absolutely necessary, prefer - /// `has_errors` or `has_errors_or_lint_errors` to this method. - pub fn has_errors_or_lint_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> { - self.inner.borrow().has_errors_or_lint_errors_or_delayed_bugs() + /// `has_errors` to this method. + pub fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> { + self.inner.borrow().has_errors_or_delayed_bugs() } pub fn print_error_count(&self, registry: &Registry) { let mut inner = self.inner.borrow_mut(); - inner.emit_stashed_diagnostics(); + // Any stashed diagnostics should have been handled by + // `emit_stashed_diagnostics` by now. + assert!(inner.stashed_diagnostics.is_empty()); if inner.treat_err_as_bug() { return; @@ -869,10 +876,12 @@ impl DiagCtxt { } } + /// This excludes delayed bugs and stashed errors. Used for early aborts + /// after errors occurred -- e.g. because continuing in the face of errors is + /// likely to lead to bad results, such as spurious/uninteresting + /// additional errors -- when returning an error `Result` is difficult. pub fn abort_if_errors(&self) { - let mut inner = self.inner.borrow_mut(); - inner.emit_stashed_diagnostics(); - if !inner.err_guars.is_empty() { + if self.has_errors().is_some() { FatalError.raise(); } } @@ -1273,10 +1282,10 @@ impl DiagCtxt { // `DiagCtxtInner::foo`. impl DiagCtxtInner { /// Emit all stashed diagnostics. - fn emit_stashed_diagnostics(&mut self) { + fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> { + let mut guar = None; let has_errors = !self.err_guars.is_empty(); for (_, diag) in std::mem::take(&mut self.stashed_diagnostics).into_iter() { - // Decrement the count tracking the stash; emitting will increment it. if diag.is_error() { if diag.is_lint.is_none() { self.stashed_err_count -= 1; @@ -1289,8 +1298,9 @@ impl DiagCtxtInner { continue; } } - self.emit_diagnostic(diag); + guar = guar.or(self.emit_diagnostic(diag)); } + guar } // Return value is only `Some` if the level is `Error` or `DelayedBug`. @@ -1334,7 +1344,7 @@ impl DiagCtxtInner { DelayedBug => { // If we have already emitted at least one error, we don't need // to record the delayed bug, because it'll never be used. - return if let Some(guar) = self.has_errors_or_lint_errors() { + return if let Some(guar) = self.has_errors() { Some(guar) } else { let backtrace = std::backtrace::Backtrace::capture(); @@ -1395,9 +1405,8 @@ impl DiagCtxtInner { }; diagnostic.children.extract_if(already_emitted_sub).for_each(|_| {}); if already_emitted { - diagnostic.note( - "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`", - ); + let msg = "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`"; + diagnostic.sub(Level::Note, msg, MultiSpan::new()); } if is_error { @@ -1451,17 +1460,16 @@ impl DiagCtxtInner { .is_some_and(|c| self.err_guars.len() + self.lint_err_guars.len() + 1 >= c.get()) } - fn has_errors(&self) -> Option<ErrorGuaranteed> { + fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> { self.err_guars.get(0).copied() } - fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> { - self.has_errors().or_else(|| self.lint_err_guars.get(0).copied()) + fn has_errors(&self) -> Option<ErrorGuaranteed> { + self.has_errors_excluding_lint_errors().or_else(|| self.lint_err_guars.get(0).copied()) } - fn has_errors_or_lint_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> { - self.has_errors_or_lint_errors() - .or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied()) + fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> { + self.has_errors().or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied()) } /// Translate `message` eagerly with `args` to `SubdiagnosticMessage::Eager`. @@ -1483,7 +1491,22 @@ impl DiagCtxtInner { self.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string() } + fn eagerly_translate_for_subdiag( + &self, + diag: &Diagnostic, + msg: impl Into<SubdiagnosticMessage>, + ) -> SubdiagnosticMessage { + let args = diag.args(); + let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); + self.eagerly_translate(msg, args) + } + fn flush_delayed(&mut self) { + // Stashed diagnostics must be emitted before delayed bugs are flushed. + // Otherwise, we might ICE prematurely when errors would have + // eventually happened. + assert!(self.stashed_diagnostics.is_empty()); + if self.delayed_bugs.is_empty() { return; } @@ -1527,17 +1550,14 @@ impl DiagCtxtInner { if bug.level != DelayedBug { // NOTE(eddyb) not panicking here because we're already producing // an ICE, and the more information the merrier. - let subdiag = InvalidFlushedDelayedDiagnosticLevel { - span: bug.span.primary_span().unwrap(), - level: bug.level, - }; - // FIXME: Cannot use `Diagnostic::subdiagnostic` which takes `DiagCtxt`, but it - // just uses `DiagCtxtInner` functions. - subdiag.add_to_diagnostic_with(&mut bug, |diag, msg| { - let args = diag.args(); - let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); - self.eagerly_translate(msg, args) - }); + // + // We are at the `Diagnostic`/`DiagCtxtInner` level rather than + // the usual `DiagnosticBuilder`/`DiagCtxt` level, so we must + // augment `bug` in a lower-level fashion. + bug.arg("level", bug.level); + let msg = crate::fluent_generated::errors_invalid_flushed_delayed_diagnostic_level; + let msg = self.eagerly_translate_for_subdiag(&bug, msg); // after the `arg` call + bug.sub(Level::Note, msg, bug.span.primary_span().unwrap().into()); } bug.level = Bug; @@ -1571,39 +1591,22 @@ impl DelayedDiagnostic { DelayedDiagnostic { inner: diagnostic, note: backtrace } } - fn decorate(mut self, dcx: &DiagCtxtInner) -> Diagnostic { - // FIXME: Cannot use `Diagnostic::subdiagnostic` which takes `DiagCtxt`, but it - // just uses `DiagCtxtInner` functions. - let subdiag_with = |diag: &mut Diagnostic, msg| { - let args = diag.args(); - let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); - dcx.eagerly_translate(msg, args) - }; - - match self.note.status() { - BacktraceStatus::Captured => { - let inner = &self.inner; - let subdiag = DelayedAtWithNewline { - span: inner.span.primary_span().unwrap_or(DUMMY_SP), - emitted_at: inner.emitted_at.clone(), - note: self.note, - }; - subdiag.add_to_diagnostic_with(&mut self.inner, subdiag_with); - } + fn decorate(self, dcx: &DiagCtxtInner) -> Diagnostic { + // We are at the `Diagnostic`/`DiagCtxtInner` level rather than the + // usual `DiagnosticBuilder`/`DiagCtxt` level, so we must construct + // `diag` in a lower-level fashion. + let mut diag = self.inner; + let msg = match self.note.status() { + BacktraceStatus::Captured => crate::fluent_generated::errors_delayed_at_with_newline, // Avoid the needless newline when no backtrace has been captured, // the display impl should just be a single line. - _ => { - let inner = &self.inner; - let subdiag = DelayedAtWithoutNewline { - span: inner.span.primary_span().unwrap_or(DUMMY_SP), - emitted_at: inner.emitted_at.clone(), - note: self.note, - }; - subdiag.add_to_diagnostic_with(&mut self.inner, subdiag_with); - } - } - - self.inner + _ => crate::fluent_generated::errors_delayed_at_without_newline, + }; + diag.arg("emitted_at", diag.emitted_at.clone()); + diag.arg("note", self.note); + let msg = dcx.eagerly_translate_for_subdiag(&diag, msg); // after the `arg` calls + diag.sub(Level::Note, msg, diag.span.primary_span().unwrap_or(DUMMY_SP).into()); + diag } } @@ -1745,9 +1748,9 @@ impl Level { } // FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite. -pub fn add_elided_lifetime_in_path_suggestion<E: EmissionGuarantee>( +pub fn add_elided_lifetime_in_path_suggestion<G: EmissionGuarantee>( source_map: &SourceMap, - diag: &mut DiagnosticBuilder<'_, E>, + diag: &mut DiagnosticBuilder<'_, G>, n: usize, path_span: Span, incl_angl_brckt: bool, diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 5a3303327db..1f0488130b2 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -22,6 +22,10 @@ expand_collapse_debuginfo_illegal = expand_count_repetition_misplaced = `count` can not be placed inside the inner-most repetition +expand_custom_attribute_panicked = + custom attribute panicked + .help = message: {$message} + expand_duplicate_matcher_binding = duplicate matcher binding .label = duplicate binding .label2 = previous binding @@ -115,6 +119,10 @@ expand_only_one_argument = expand_only_one_word = must only be one word +expand_proc_macro_derive_panicked = + proc-macro derive panicked + .help = message: {$message} + expand_proc_macro_derive_tokens = proc-macro derive produced unparsable tokens diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index f602276aad2..1c8d18bec65 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1176,6 +1176,8 @@ impl<'a> ExtCtxt<'a> { for (span, notes) in self.expansions.iter() { let mut db = self.dcx().create_note(errors::TraceMacro { span: *span }); for note in notes { + // FIXME: make this translatable + #[allow(rustc::untranslatable_diagnostic)] db.note(note.clone()); } db.emit(); diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 781186764fa..435135d1959 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -384,6 +384,7 @@ impl<'a> StripUnconfigured<'a> { ); if attr.is_doc_comment() { + #[allow(rustc::untranslatable_diagnostic)] err.help("`///` is for documentation comments. For a plain comment, use `//`."); } diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 929f3479466..fe901603c73 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -393,6 +393,36 @@ pub(crate) struct ProcMacroPanickedHelp { } #[derive(Diagnostic)] +#[diag(expand_proc_macro_derive_panicked)] +pub(crate) struct ProcMacroDerivePanicked { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub message: Option<ProcMacroDerivePanickedHelp>, +} + +#[derive(Subdiagnostic)] +#[help(expand_help)] +pub(crate) struct ProcMacroDerivePanickedHelp { + pub message: String, +} + +#[derive(Diagnostic)] +#[diag(expand_custom_attribute_panicked)] +pub(crate) struct CustomAttributePanicked { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub message: Option<CustomAttributePanickedHelp>, +} + +#[derive(Subdiagnostic)] +#[help(expand_help)] +pub(crate) struct CustomAttributePanickedHelp { + pub message: String, +} + +#[derive(Diagnostic)] #[diag(expand_proc_macro_derive_tokens)] pub struct ProcMacroDeriveTokens { #[primary_span] diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index e7197c5768b..25af974d326 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -7,7 +7,7 @@ use crate::mbe::{ use rustc_ast::token::{self, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; -use rustc_errors::{Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, DiagnosticMessage}; +use rustc_errors::{Applicability, DiagCtxt, DiagnosticBuilder, DiagnosticMessage}; use rustc_parse::parser::{Parser, Recovery}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::Ident; @@ -285,7 +285,11 @@ pub(super) fn emit_frag_parse_err( e.emit(); } -pub(crate) fn annotate_err_with_kind(err: &mut Diagnostic, kind: AstFragmentKind, span: Span) { +pub(crate) fn annotate_err_with_kind( + err: &mut DiagnosticBuilder<'_>, + kind: AstFragmentKind, + span: Span, +) { match kind { AstFragmentKind::Ty => { err.span_label(span, "this macro call doesn't expand to a type"); @@ -313,7 +317,7 @@ enum ExplainDocComment { pub(super) fn annotate_doc_comment( dcx: &DiagCtxt, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, sm: &SourceMap, span: Span, ) { diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 2233cad2e63..23caf2f193a 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -93,11 +93,12 @@ impl base::AttrProcMacro for AttrProcMacro { let server = proc_macro_server::Rustc::new(ecx); self.client.run(&strategy, server, annotation, annotated, proc_macro_backtrace).map_err( |e| { - let mut err = ecx.dcx().struct_span_err(span, "custom attribute panicked"); - if let Some(s) = e.as_str() { - err.help(format!("message: {s}")); - } - err.emit() + ecx.dcx().emit_err(errors::CustomAttributePanicked { + span, + message: e.as_str().map(|message| errors::CustomAttributePanickedHelp { + message: message.into(), + }), + }) }, ) } @@ -146,11 +147,14 @@ impl MultiItemModifier for DeriveProcMacro { match self.client.run(&strategy, server, input, proc_macro_backtrace) { Ok(stream) => stream, Err(e) => { - let mut err = ecx.dcx().struct_span_err(span, "proc-macro derive panicked"); - if let Some(s) = e.as_str() { - err.help(format!("message: {s}")); - } - err.emit(); + ecx.dcx().emit_err({ + errors::ProcMacroDerivePanicked { + span, + message: e.as_str().map(|message| { + errors::ProcMacroDerivePanickedHelp { message: message.into() } + }), + } + }); return ExpandResult::Ready(vec![]); } } diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 8f31b5801da..b80ecbc9c65 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -10,7 +10,7 @@ use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; -use rustc_errors::{ErrorGuaranteed, MultiSpan, PResult}; +use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult}; use rustc_parse::lexer::nfc_normalize; use rustc_parse::parse_stream_from_source_str; use rustc_session::parse::ParseSess; @@ -509,13 +509,14 @@ impl server::FreeFunctions for Rustc<'_, '_> { } fn emit_diagnostic(&mut self, diagnostic: Diagnostic<Self::Span>) { - let mut diag = - rustc_errors::Diagnostic::new(diagnostic.level.to_internal(), diagnostic.message); + let message = rustc_errors::DiagnosticMessage::from(diagnostic.message); + let mut diag: DiagnosticBuilder<'_, rustc_errors::ErrorGuaranteed> = + DiagnosticBuilder::new(&self.sess().dcx, diagnostic.level.to_internal(), message); diag.span(MultiSpan::from_spans(diagnostic.spans)); for child in diagnostic.children { diag.sub(child.level.to_internal(), child.message, MultiSpan::from_spans(child.spans)); } - self.sess().dcx.emit_diagnostic(diag); + diag.emit(); } } diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index e6050327186..1eaab3d2aca 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs @@ -71,14 +71,21 @@ impl hir::Pat<'_> { /// Call `f` on every "binding" in a pattern, e.g., on `a` in /// `match foo() { Some(a) => (), None => () }`. /// - /// When encountering an or-pattern `p_0 | ... | p_n` only `p_0` will be visited. + /// When encountering an or-pattern `p_0 | ... | p_n` only the first non-never pattern will be + /// visited. If they're all never patterns we visit nothing, which is ok since a never pattern + /// cannot have bindings. pub fn each_binding_or_first( &self, f: &mut impl FnMut(hir::BindingAnnotation, HirId, Span, Ident), ) { self.walk(|p| match &p.kind { PatKind::Or(ps) => { - ps[0].each_binding_or_first(f); + for p in *ps { + if !p.is_never_pattern() { + p.each_binding_or_first(f); + break; + } + } false } PatKind::Binding(bm, _, ident, _) => { diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index 6940b4a5045..6d8a5bc0e90 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -454,9 +454,9 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad // for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok let late_bound_in_projection_ty = - tcx.collect_constrained_late_bound_regions(&projection_ty); + tcx.collect_constrained_late_bound_regions(projection_ty); let late_bound_in_term = - tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(term)); + tcx.collect_referenced_late_bound_regions(trait_ref.rebind(term)); debug!(?late_bound_in_projection_ty); debug!(?late_bound_in_term); diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index ad34c31ef8f..214d9602968 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -9,7 +9,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordMap; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, ErrorGuaranteed, + codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, }; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -371,7 +371,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate. fn note_ambiguous_inherent_assoc_type( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, candidates: Vec<DefId>, span: Span, ) { @@ -429,7 +429,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let tcx = self.tcx(); let adt_did = self_ty.ty_adt_def().map(|def| def.did()); - let add_def_label = |err: &mut Diagnostic| { + let add_def_label = |err: &mut DiagnosticBuilder<'_>| { if let Some(did) = adt_did { err.span_label( tcx.def_span(did), diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index 614e5f9d32b..b20326ae5e1 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -6,7 +6,7 @@ use crate::astconv::{ use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs}; use rustc_ast::ast::ParamKindOrd; use rustc_errors::{ - codes::*, struct_span_code_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, + codes::*, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -47,7 +47,7 @@ fn generic_arg_mismatch_err( } } - let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diagnostic| { + let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut DiagnosticBuilder<'_>| { let suggestions = vec![ (arg.span().shrink_to_lo(), String::from("{ ")), (arg.span().shrink_to_hi(), String::from(" }")), diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs index cee7c84adb2..cee29b152e8 100644 --- a/compiler/rustc_hir_analysis/src/astconv/lint.rs +++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs @@ -1,5 +1,5 @@ use rustc_ast::TraitObjectSyntax; -use rustc_errors::{codes::*, Diagnostic, StashKey}; +use rustc_errors::{codes::*, DiagnosticBuilder, EmissionGuarantee, StashKey}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability}; @@ -10,10 +10,10 @@ use super::AstConv; impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Make sure that we are in the condition to suggest the blanket implementation. - pub(super) fn maybe_lint_blanket_trait_impl( + pub(super) fn maybe_lint_blanket_trait_impl<G: EmissionGuarantee>( &self, self_ty: &hir::Ty<'_>, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_, G>, ) { let tcx = self.tcx(); let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id; @@ -75,7 +75,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } /// Make sure that we are in the condition to suggest `impl Trait`. - fn maybe_lint_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diagnostic) -> bool { + fn maybe_lint_impl_trait( + &self, + self_ty: &hir::Ty<'_>, + diag: &mut DiagnosticBuilder<'_>, + ) -> bool { let tcx = self.tcx(); let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id; let (sig, generics, owner) = match tcx.hir_node_by_def_id(parent_id) { diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 3ccf78567ed..f374b955d9e 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -18,8 +18,8 @@ use crate::require_c_abi_if_c_variadic; use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::{ - codes::*, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, - FatalError, MultiSpan, + codes::*, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, + MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; @@ -1237,8 +1237,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // trait reference. let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else { // A cycle error occurred, most likely. - let guar = tcx.dcx().span_delayed_bug(span, "expected cycle error"); - return Err(guar); + tcx.dcx().span_bug(span, "expected cycle error"); }; self.one_bound_for_assoc_item( @@ -1425,17 +1424,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { vec![] }; - let (impl_, (assoc_item, def_scope)) = - crate::traits::project::with_replaced_escaping_bound_vars( - infcx, - &mut universes, - self_ty, - |self_ty| { - self.select_inherent_assoc_type_candidates( - infcx, name, span, self_ty, param_env, candidates, - ) - }, - )?; + let (impl_, (assoc_item, def_scope)) = crate::traits::with_replaced_escaping_bound_vars( + infcx, + &mut universes, + self_ty, + |self_ty| { + self.select_inherent_assoc_type_candidates( + infcx, name, span, self_ty, param_env, candidates, + ) + }, + )?; self.check_assoc_ty(assoc_item, name, def_scope, block, span); @@ -1725,7 +1723,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { pub fn prohibit_generics<'a>( &self, segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone, - extend: impl Fn(&mut Diagnostic), + extend: impl Fn(&mut DiagnosticBuilder<'_>), ) -> bool { let args = segments.clone().flat_map(|segment| segment.args().args); @@ -2679,9 +2677,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // for<'a> fn(&'a String) -> &'a str <-- 'a is ok let inputs = bare_fn_ty.inputs(); let late_bound_in_args = - tcx.collect_constrained_late_bound_regions(&inputs.map_bound(|i| i.to_owned())); + tcx.collect_constrained_late_bound_regions(inputs.map_bound(|i| i.to_owned())); let output = bare_fn_ty.output(); - let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output); + let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(output); self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| { struct_span_code_err!( diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index c3948b1b6bd..b945eed54d0 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -257,8 +257,7 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) { fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) { let item = tcx.hir().expect_item(def_id); let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else { - tcx.dcx().span_delayed_bug(item.span, "expected opaque item"); - return; + tcx.dcx().span_bug(item.span, "expected opaque item"); }; // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting @@ -382,10 +381,10 @@ fn check_opaque_meets_bounds<'tcx>( Ok(()) => {} Err(ty_err) => { let ty_err = ty_err.to_string(tcx); - return Err(tcx.dcx().span_delayed_bug( + tcx.dcx().span_bug( span, format!("could not unify `{hidden_ty}` with revealed type:\n{ty_err}"), - )); + ); } } @@ -1248,7 +1247,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { fn detect_discriminant_duplicate<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) { // Helper closure to reduce duplicate code. This gets called everytime we detect a duplicate. // Here `idx` refers to the order of which the discriminant appears, and its index in `vs` - let report = |dis: Discr<'tcx>, idx, err: &mut Diagnostic| { + let report = |dis: Discr<'tcx>, idx, err: &mut DiagnosticBuilder<'_>| { let var = adt.variant(idx); // HIR for the duplicate discriminant let (span, display_discr) = match var.discr { ty::VariantDiscr::Explicit(discr_def_id) => { diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 1dd27f0cc53..435e251b130 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -734,11 +734,12 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( remapped_types.insert(def_id, ty::EarlyBinder::bind(ty)); } Err(err) => { - let reported = tcx.dcx().span_delayed_bug( - return_span, - format!("could not fully resolve: {ty} => {err:?}"), - ); - remapped_types.insert(def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, reported))); + // This code path is not reached in any tests, but may be + // reachable. If this is triggered, it should be converted to + // `span_delayed_bug` and the triggering case turned into a + // test. + tcx.dcx() + .span_bug(return_span, format!("could not fully resolve: {ty} => {err:?}")); } } } @@ -917,7 +918,13 @@ impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> { .with_note(format!("hidden type inferred to be `{}`", self.ty)) .emit() } - _ => self.tcx.dcx().delayed_bug("should've been able to remap region"), + _ => { + // This code path is not reached in any tests, but may be + // reachable. If this is triggered, it should be converted + // to `delayed_bug` and the triggering case turned into a + // test. + self.tcx.dcx().bug("should've been able to remap region"); + } }; return Err(guar); }; @@ -1276,9 +1283,10 @@ fn compare_number_of_generics<'tcx>( // inheriting the generics from will also have mismatched arguments, and // we'll report an error for that instead. Delay a bug for safety, though. if trait_.is_impl_trait_in_trait() { - return Err(tcx.dcx().delayed_bug( - "errors comparing numbers of generics of trait/impl functions were not emitted", - )); + // FIXME: no tests trigger this. If you find example code that does + // trigger this, please add it to the test suite. + tcx.dcx() + .bug("errors comparing numbers of generics of trait/impl functions were not emitted"); } let matchings = [ diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index 3d3b21eabb3..7bdbab4325c 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -154,8 +154,10 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>( trait_m_sig.inputs_and_output, )); if !ocx.select_all_or_error().is_empty() { - tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (selection)"); - return; + // This code path is not reached in any tests, but may be reachable. If + // this is triggered, it should be converted to `delayed_bug` and the + // triggering case turned into a test. + tcx.dcx().bug("encountered errors when checking RPITIT refinement (selection)"); } let outlives_env = OutlivesEnvironment::with_bounds( param_env, @@ -163,13 +165,17 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>( ); let errors = infcx.resolve_regions(&outlives_env); if !errors.is_empty() { - tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (regions)"); - return; + // This code path is not reached in any tests, but may be reachable. If + // this is triggered, it should be converted to `delayed_bug` and the + // triggering case turned into a test. + tcx.dcx().bug("encountered errors when checking RPITIT refinement (regions)"); } // Resolve any lifetime variables that may have been introduced during normalization. let Ok((trait_bounds, impl_bounds)) = infcx.fully_resolve((trait_bounds, impl_bounds)) else { - tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (resolution)"); - return; + // This code path is not reached in any tests, but may be reachable. If + // this is triggered, it should be converted to `delayed_bug` and the + // triggering case turned into a test. + tcx.dcx().bug("encountered errors when checking RPITIT refinement (resolution)"); }; // For quicker lookup, use an `IndexSet` (we don't use one earlier because diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 903c98e8317..05fab60fd8d 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -123,7 +123,12 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::variant_count | sym::is_val_statically_known | sym::ptr_mask - | sym::debug_assertions => hir::Unsafety::Normal, + | sym::debug_assertions + | sym::fadd_algebraic + | sym::fsub_algebraic + | sym::fmul_algebraic + | sym::fdiv_algebraic + | sym::frem_algebraic => hir::Unsafety::Normal, _ => hir::Unsafety::Unsafe, }; @@ -405,6 +410,11 @@ pub fn check_intrinsic_type( sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => { (1, 0, vec![param(0), param(0)], param(0)) } + sym::fadd_algebraic + | sym::fsub_algebraic + | sym::fmul_algebraic + | sym::fdiv_algebraic + | sym::frem_algebraic => (1, 0, vec![param(0), param(0)], param(0)), sym::float_to_int_unchecked => (2, 0, vec![param(0)], param(1)), sym::assume => (0, 1, vec![tcx.types.bool], Ty::new_unit(tcx)), diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index b9052672a26..36a92a4cf7c 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -78,7 +78,7 @@ use std::num::NonZero; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::ErrorGuaranteed; -use rustc_errors::{pluralize, struct_span_code_err, Diagnostic, DiagnosticBuilder}; +use rustc_errors::{pluralize, struct_span_code_err, DiagnosticBuilder}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_index::bit_set::BitSet; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index e7506cee60e..f03d0f8a885 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1087,14 +1087,8 @@ fn check_type_defn<'tcx>( packed && { let ty = tcx.type_of(variant.tail().did).instantiate_identity(); let ty = tcx.erase_regions(ty); - if ty.has_infer() { - tcx.dcx() - .span_delayed_bug(item.span, format!("inference variables in {ty:?}")); - // Just treat unresolved type expression as if it needs drop. - true - } else { - ty.needs_drop(tcx, tcx.param_env(item.owner_id)) - } + assert!(!ty.has_infer()); + ty.needs_drop(tcx, tcx.param_env(item.owner_id)) } }; // All fields (except for possibly the last) should be sized. diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 4823472cf96..32f31201254 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -144,7 +144,7 @@ impl<'tcx> InherentCollect<'tcx> { let id = id.owner_id.def_id; let item_span = self.tcx.def_span(id); let self_ty = self.tcx.type_of(id).instantiate_identity(); - let self_ty = peel_off_weak_aliases(self.tcx, self_ty); + let self_ty = self.tcx.peel_off_weak_alias_tys(self_ty); match *self_ty.kind() { ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()), ty::Foreign(did) => self.check_def_id(id, self_ty, did), @@ -186,30 +186,3 @@ impl<'tcx> InherentCollect<'tcx> { } } } - -/// Peel off all weak alias types in this type until there are none left. -/// -/// <div class="warning"> -/// -/// This assumes that `ty` gets normalized later and that any overflows occurring -/// during said normalization get reported. -/// -/// </div> -fn peel_off_weak_aliases<'tcx>(tcx: TyCtxt<'tcx>, mut ty: Ty<'tcx>) -> Ty<'tcx> { - let ty::Alias(ty::Weak, _) = ty.kind() else { return ty }; - - let limit = tcx.recursion_limit(); - let mut depth = 0; - - while let ty::Alias(ty::Weak, alias) = ty.kind() { - if !limit.value_within_limit(depth) { - let guar = tcx.dcx().delayed_bug("overflow expanding weak alias type"); - return Ty::new_error(tcx, guar); - } - - ty = tcx.type_of(alias.def_id).instantiate(tcx, alias.args); - depth += 1; - } - - ty -} diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 45641be52d2..07bbaa1926e 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -1,20 +1,12 @@ //! Orphan checker: every impl either implements a trait defined in this //! crate or pertains to a type defined in this crate. -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{DelayDm, ErrorGuaranteed}; +use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; -use rustc_middle::ty::util::CheckRegions; -use rustc_middle::ty::GenericArgs; -use rustc_middle::ty::{ - self, AliasKind, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, - TypeVisitor, -}; -use rustc_session::lint; -use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_middle::ty::{self, AliasKind, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::def_id::LocalDefId; use rustc_span::Span; use rustc_trait_selection::traits; -use std::ops::ControlFlow; use crate::errors; @@ -26,30 +18,17 @@ pub(crate) fn orphan_check_impl( let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(); trait_ref.error_reported()?; - let ret = do_orphan_check_impl(tcx, trait_ref, impl_def_id); - if tcx.trait_is_auto(trait_ref.def_id) { - lint_auto_trait_impl(tcx, trait_ref, impl_def_id); - } - - ret -} - -fn do_orphan_check_impl<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - def_id: LocalDefId, -) -> Result<(), ErrorGuaranteed> { let trait_def_id = trait_ref.def_id; - match traits::orphan_check(tcx, def_id.to_def_id()) { + match traits::orphan_check(tcx, impl_def_id.to_def_id()) { Ok(()) => {} Err(err) => { - let item = tcx.hir().expect_item(def_id); + let item = tcx.hir().expect_item(impl_def_id); let hir::ItemKind::Impl(impl_) = item.kind else { - bug!("{:?} is not an impl: {:?}", def_id, item); + bug!("{:?} is not an impl: {:?}", impl_def_id, item); }; let tr = impl_.of_trait.as_ref().unwrap(); - let sp = tcx.def_span(def_id); + let sp = tcx.def_span(impl_def_id); emit_orphan_check_error( tcx, @@ -193,7 +172,7 @@ fn do_orphan_check_impl<'tcx>( // impl<T> AutoTrait for T {} // impl<T: ?Sized> AutoTrait for T {} ty::Param(..) => ( - if self_ty.is_sized(tcx, tcx.param_env(def_id)) { + if self_ty.is_sized(tcx, tcx.param_env(impl_def_id)) { LocalImpl::Allow } else { LocalImpl::Disallow { problematic_kind: "generic type" } @@ -250,7 +229,7 @@ fn do_orphan_check_impl<'tcx>( | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) => { - let sp = tcx.def_span(def_id); + let sp = tcx.def_span(impl_def_id); span_bug!(sp, "weird self type for autotrait impl") } @@ -262,7 +241,7 @@ fn do_orphan_check_impl<'tcx>( LocalImpl::Allow => {} LocalImpl::Disallow { problematic_kind } => { return Err(tcx.dcx().emit_err(errors::TraitsWithDefaultImpl { - span: tcx.def_span(def_id), + span: tcx.def_span(impl_def_id), traits: tcx.def_path_str(trait_def_id), problematic_kind, self_ty, @@ -274,13 +253,13 @@ fn do_orphan_check_impl<'tcx>( NonlocalImpl::Allow => {} NonlocalImpl::DisallowBecauseNonlocal => { return Err(tcx.dcx().emit_err(errors::CrossCrateTraitsDefined { - span: tcx.def_span(def_id), + span: tcx.def_span(impl_def_id), traits: tcx.def_path_str(trait_def_id), })); } NonlocalImpl::DisallowOther => { return Err(tcx.dcx().emit_err(errors::CrossCrateTraits { - span: tcx.def_span(def_id), + span: tcx.def_span(impl_def_id), traits: tcx.def_path_str(trait_def_id), self_ty, })); @@ -445,146 +424,3 @@ fn emit_orphan_check_error<'tcx>( } }) } - -/// Lint impls of auto traits if they are likely to have -/// unsound or surprising effects on auto impls. -fn lint_auto_trait_impl<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - impl_def_id: LocalDefId, -) { - if trait_ref.args.len() != 1 { - tcx.dcx().span_delayed_bug( - tcx.def_span(impl_def_id), - "auto traits cannot have generic parameters", - ); - return; - } - let self_ty = trait_ref.self_ty(); - let (self_type_did, args) = match self_ty.kind() { - ty::Adt(def, args) => (def.did(), args), - _ => { - // FIXME: should also lint for stuff like `&i32` but - // considering that auto traits are unstable, that - // isn't too important for now as this only affects - // crates using `nightly`, and std. - return; - } - }; - - // Impls which completely cover a given root type are fine as they - // disable auto impls entirely. So only lint if the args - // are not a permutation of the identity args. - let Err(arg) = tcx.uses_unique_generic_params(args, CheckRegions::No) else { - // ok - return; - }; - - // Ideally: - // - // - compute the requirements for the auto impl candidate - // - check whether these are implied by the non covering impls - // - if not, emit the lint - // - // What we do here is a bit simpler: - // - // - badly check if an auto impl candidate definitely does not apply - // for the given simplified type - // - if so, do not lint - if fast_reject_auto_impl(tcx, trait_ref.def_id, self_ty) { - // ok - return; - } - - tcx.node_span_lint( - lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS, - tcx.local_def_id_to_hir_id(impl_def_id), - tcx.def_span(impl_def_id), - DelayDm(|| { - format!( - "cross-crate traits with a default impl, like `{}`, \ - should not be specialized", - tcx.def_path_str(trait_ref.def_id), - ) - }), - |lint| { - let item_span = tcx.def_span(self_type_did); - let self_descr = tcx.def_descr(self_type_did); - match arg { - ty::util::NotUniqueParam::DuplicateParam(arg) => { - lint.note(format!("`{arg}` is mentioned multiple times")); - } - ty::util::NotUniqueParam::NotParam(arg) => { - lint.note(format!("`{arg}` is not a generic parameter")); - } - } - lint.span_note( - item_span, - format!( - "try using the same sequence of generic parameters as the {self_descr} definition", - ), - ); - }, - ); -} - -fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: Ty<'tcx>) -> bool { - struct DisableAutoTraitVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - self_ty_root: Ty<'tcx>, - seen: FxHashSet<DefId>, - } - - impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for DisableAutoTraitVisitor<'tcx> { - type BreakTy = (); - fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - let tcx = self.tcx; - if ty != self.self_ty_root { - for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, ty) { - match tcx.impl_polarity(impl_def_id) { - ImplPolarity::Negative => return ControlFlow::Break(()), - ImplPolarity::Reservation => {} - // FIXME(@lcnr): That's probably not good enough, idk - // - // We might just want to take the rustdoc code and somehow avoid - // explicit impls for `Self`. - ImplPolarity::Positive => return ControlFlow::Continue(()), - } - } - } - - match ty.kind() { - ty::Adt(def, args) if def.is_phantom_data() => args.visit_with(self), - ty::Adt(def, args) => { - // @lcnr: This is the only place where cycles can happen. We avoid this - // by only visiting each `DefId` once. - // - // This will be is incorrect in subtle cases, but I don't care :) - if self.seen.insert(def.did()) { - for ty in def.all_fields().map(|field| field.ty(tcx, args)) { - ty.visit_with(self)?; - } - } - - ControlFlow::Continue(()) - } - _ => ty.super_visit_with(self), - } - } - } - - let self_ty_root = match self_ty.kind() { - ty::Adt(def, _) => Ty::new_adt(tcx, *def, GenericArgs::identity_for_item(tcx, def.did())), - _ => unimplemented!("unexpected self ty {:?}", self_ty), - }; - - self_ty_root - .visit_with(&mut DisableAutoTraitVisitor { - tcx, - self_ty_root, - trait_def_id, - seen: FxHashSet::default(), - }) - .is_break() -} diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index a5ef1490bce..6a42fdb1079 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -520,7 +520,7 @@ fn get_new_lifetime_name<'tcx>( generics: &hir::Generics<'tcx>, ) -> String { let existing_lifetimes = tcx - .collect_referenced_late_bound_regions(&poly_trait_ref) + .collect_referenced_late_bound_regions(poly_trait_ref) .into_iter() .filter_map(|lt| { if let ty::BoundRegionKind::BrNamed(_, name) = lt { @@ -1630,7 +1630,7 @@ fn early_bound_lifetimes_from_generics<'a, 'tcx: 'a>( #[instrument(level = "debug", skip(tcx))] fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { let mut result = tcx.explicit_predicates_of(def_id); - debug!("predicates_defined_on: explicit_predicates_of({:?}) = {:?}", def_id, result,); + debug!("predicates_defined_on: explicit_predicates_of({:?}) = {:?}", def_id, result); let inferred_outlives = tcx.inferred_outlives_of(def_id); if !inferred_outlives.is_empty() { debug!( diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 9cc6c16c126..410a069f956 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -315,7 +315,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { if is_host_effect { if let Some(idx) = host_effect_index { - tcx.dcx().span_delayed_bug( + tcx.dcx().span_bug( param.span, format!("parent also has host effect param? index: {idx}, def: {def_id:?}"), ); diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 325a0ee9a18..efde4e11c79 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1331,7 +1331,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } } - self.tcx.dcx().span_delayed_bug( + self.tcx.dcx().span_bug( lifetime_ref.ident.span, format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,), ); @@ -1465,10 +1465,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } } - self.tcx.dcx().span_delayed_bug( - self.tcx.hir().span(hir_id), - format!("could not resolve {param_def_id:?}"), - ); + self.tcx + .dcx() + .span_bug(self.tcx.hir().span(hir_id), format!("could not resolve {param_def_id:?}")); } #[instrument(level = "debug", skip(self))] diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index 4ce43bb4887..b8de2e46934 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; +use rustc_type_ir::fold::TypeFoldable; use std::ops::ControlFlow; #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -33,62 +33,47 @@ pub fn parameters_for_impl<'tcx>( impl_trait_ref: Option<ty::TraitRef<'tcx>>, ) -> FxHashSet<Parameter> { let vec = match impl_trait_ref { - Some(tr) => parameters_for(tcx, &tr, false), - None => parameters_for(tcx, &impl_self_ty, false), + Some(tr) => parameters_for(tcx, tr, false), + None => parameters_for(tcx, impl_self_ty, false), }; vec.into_iter().collect() } /// If `include_nonconstraining` is false, returns the list of parameters that are -/// constrained by `t` - i.e., the value of each parameter in the list is -/// uniquely determined by `t` (see RFC 447). If it is true, return the list -/// of parameters whose values are needed in order to constrain `ty` - these +/// constrained by `value` - i.e., the value of each parameter in the list is +/// uniquely determined by `value` (see RFC 447). If it is true, return the list +/// of parameters whose values are needed in order to constrain `value` - these /// differ, with the latter being a superset, in the presence of projections. pub fn parameters_for<'tcx>( tcx: TyCtxt<'tcx>, - t: &impl TypeVisitable<TyCtxt<'tcx>>, + value: impl TypeFoldable<TyCtxt<'tcx>>, include_nonconstraining: bool, ) -> Vec<Parameter> { - let mut collector = - ParameterCollector { tcx, parameters: vec![], include_nonconstraining, depth: 0 }; - t.visit_with(&mut collector); + let mut collector = ParameterCollector { parameters: vec![], include_nonconstraining }; + let value = if !include_nonconstraining { tcx.expand_weak_alias_tys(value) } else { value }; + value.visit_with(&mut collector); collector.parameters } -struct ParameterCollector<'tcx> { - tcx: TyCtxt<'tcx>, +struct ParameterCollector { parameters: Vec<Parameter>, include_nonconstraining: bool, - depth: usize, } -impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector<'tcx> { +impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { match *t.kind() { + // Projections are not injective in general. ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) if !self.include_nonconstraining => { - // Projections are not injective in general. return ControlFlow::Continue(()); } - ty::Alias(ty::Weak, alias) if !self.include_nonconstraining => { - if !self.tcx.recursion_limit().value_within_limit(self.depth) { - // Other constituent types may still constrain some generic params, consider - // `<T> (Overflow, T)` for example. Therefore we want to continue instead of - // breaking. Only affects diagnostics. - return ControlFlow::Continue(()); - } - self.depth += 1; - return ensure_sufficient_stack(|| { - self.tcx - .type_of(alias.def_id) - .instantiate(self.tcx, alias.args) - .visit_with(self) - }); - } - ty::Param(data) => { - self.parameters.push(Parameter::from(data)); + // All weak alias types should've been expanded beforehand. + ty::Alias(ty::Weak, _) if !self.include_nonconstraining => { + bug!("unexpected weak alias type") } + ty::Param(param) => self.parameters.push(Parameter::from(param)), _ => {} } @@ -224,12 +209,12 @@ pub fn setup_constraining_predicates<'tcx>( // `<<T as Bar>::Baz as Iterator>::Output = <U as Iterator>::Output` // Then the projection only applies if `T` is known, but it still // does not determine `U`. - let inputs = parameters_for(tcx, &projection.projection_ty, true); + let inputs = parameters_for(tcx, projection.projection_ty, true); let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(p)); if !relies_only_on_inputs { continue; } - input_parameters.extend(parameters_for(tcx, &projection.term, false)); + input_parameters.extend(parameters_for(tcx, projection.term, false)); } else { continue; } diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index b4cec1d9882..9d7866fe3e0 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -111,7 +111,7 @@ fn enforce_impl_params_are_constrained( match item.kind { ty::AssocKind::Type => { if item.defaultness(tcx).has_value() { - cgp::parameters_for(tcx, &tcx.type_of(def_id).instantiate_identity(), true) + cgp::parameters_for(tcx, tcx.type_of(def_id).instantiate_identity(), true) } else { vec![] } diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index bd4fce81377..be1be1a1354 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -133,7 +133,7 @@ fn check_always_applicable( res = res.and(check_constness(tcx, impl1_def_id, impl2_node, span)); res = res.and(check_static_lifetimes(tcx, &parent_args, span)); - res = res.and(check_duplicate_params(tcx, impl1_args, &parent_args, span)); + res = res.and(check_duplicate_params(tcx, impl1_args, parent_args, span)); res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span)); res @@ -266,15 +266,15 @@ fn unconstrained_parent_impl_args<'tcx>( continue; } - unconstrained_parameters.extend(cgp::parameters_for(tcx, &projection_ty, true)); + unconstrained_parameters.extend(cgp::parameters_for(tcx, projection_ty, true)); - for param in cgp::parameters_for(tcx, &projected_ty, false) { + for param in cgp::parameters_for(tcx, projected_ty, false) { if !unconstrained_parameters.contains(¶m) { constrained_params.insert(param.0); } } - unconstrained_parameters.extend(cgp::parameters_for(tcx, &projected_ty, true)); + unconstrained_parameters.extend(cgp::parameters_for(tcx, projected_ty, true)); } } @@ -309,7 +309,7 @@ fn unconstrained_parent_impl_args<'tcx>( fn check_duplicate_params<'tcx>( tcx: TyCtxt<'tcx>, impl1_args: GenericArgsRef<'tcx>, - parent_args: &Vec<GenericArg<'tcx>>, + parent_args: Vec<GenericArg<'tcx>>, span: Span, ) -> Result<(), ErrorGuaranteed> { let mut base_params = cgp::parameters_for(tcx, parent_args, true); diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index e53f922ad10..7cb103626da 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -198,6 +198,17 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { collect::test_opaque_hidden_types(tcx)?; } + // Make sure we evaluate all static and (non-associated) const items, even if unused. + // If any of these fail to evaluate, we do not want this crate to pass compilation. + tcx.hir().par_body_owners(|item_def_id| { + let def_kind = tcx.def_kind(item_def_id); + match def_kind { + DefKind::Static(_) => tcx.ensure().eval_static_initializer(item_def_id), + DefKind::Const => tcx.ensure().const_eval_poly(item_def_id.into()), + _ => (), + } + }); + // Freeze definitions as we don't add new ones at this point. This improves performance by // allowing lock-free access to them. tcx.untracked().definitions.freeze(); diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index f9d57d402b1..8e0c2ea5ca7 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -1,5 +1,5 @@ use crate::structured_errors::StructuredDiagnostic; -use rustc_errors::{codes::*, pluralize, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan}; +use rustc_errors::{codes::*, pluralize, Applicability, DiagnosticBuilder, MultiSpan}; use rustc_hir as hir; use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt}; use rustc_session::Session; @@ -525,7 +525,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } /// Builds the `expected 1 type argument / supplied 2 type arguments` message. - fn notify(&self, err: &mut Diagnostic) { + fn notify(&self, err: &mut DiagnosticBuilder<'_>) { let (quantifier, bound) = self.get_quantifier_and_bound(); let provided_args = self.num_provided_args(); @@ -577,7 +577,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } - fn suggest(&self, err: &mut Diagnostic) { + fn suggest(&self, err: &mut DiagnosticBuilder<'_>) { debug!( "suggest(self.provided {:?}, self.gen_args.span(): {:?})", self.num_provided_args(), @@ -605,7 +605,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { /// ```text /// type Map = HashMap<String>; /// ``` - fn suggest_adding_args(&self, err: &mut Diagnostic) { + fn suggest_adding_args(&self, err: &mut DiagnosticBuilder<'_>) { if self.gen_args.parenthesized != hir::GenericArgsParentheses::No { return; } @@ -624,7 +624,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } - fn suggest_adding_lifetime_args(&self, err: &mut Diagnostic) { + fn suggest_adding_lifetime_args(&self, err: &mut DiagnosticBuilder<'_>) { debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment); let num_missing_args = self.num_missing_lifetime_args(); let num_params_to_take = num_missing_args; @@ -678,7 +678,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } - fn suggest_adding_type_and_const_args(&self, err: &mut Diagnostic) { + fn suggest_adding_type_and_const_args(&self, err: &mut DiagnosticBuilder<'_>) { let num_missing_args = self.num_missing_type_or_const_args(); let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args)); @@ -738,7 +738,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { /// ```compile_fail /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)` /// ``` - fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diagnostic) { + fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut DiagnosticBuilder<'_>) { let trait_ = match self.tcx.trait_of_item(self.def_id) { Some(def_id) => def_id, None => return, @@ -794,7 +794,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, qpath: &'tcx hir::QPath<'tcx>, msg: String, num_assoc_fn_excess_args: usize, @@ -827,7 +827,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_def_id: DefId, expr: &'tcx hir::Expr<'tcx>, msg: String, @@ -881,7 +881,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { /// ```text /// type Map = HashMap<String, String, String, String>; /// ``` - fn suggest_removing_args_or_generics(&self, err: &mut Diagnostic) { + fn suggest_removing_args_or_generics(&self, err: &mut DiagnosticBuilder<'_>) { let num_provided_lt_args = self.num_provided_lifetime_args(); let num_provided_type_const_args = self.num_provided_type_or_const_args(); let unbound_types = self.get_unbound_associated_types(); @@ -899,7 +899,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let provided_args_matches_unbound_traits = unbound_types.len() == num_redundant_type_or_const_args; - let remove_lifetime_args = |err: &mut Diagnostic| { + let remove_lifetime_args = |err: &mut DiagnosticBuilder<'_>| { let mut lt_arg_spans = Vec::new(); let mut found_redundant = false; for arg in self.gen_args.args { @@ -940,7 +940,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { ); }; - let remove_type_or_const_args = |err: &mut Diagnostic| { + let remove_type_or_const_args = |err: &mut DiagnosticBuilder<'_>| { let mut gen_arg_spans = Vec::new(); let mut found_redundant = false; for arg in self.gen_args.args { @@ -1037,7 +1037,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } /// Builds the `type defined here` message. - fn show_definition(&self, err: &mut Diagnostic) { + fn show_definition(&self, err: &mut DiagnosticBuilder<'_>) { let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) { if self.tcx.sess.source_map().is_span_accessible(def_span) { def_span.into() @@ -1088,7 +1088,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } /// Add note if `impl Trait` is explicitly specified. - fn note_synth_provided(&self, err: &mut Diagnostic) { + fn note_synth_provided(&self, err: &mut DiagnosticBuilder<'_>) { if !self.is_synth_provided() { return; } diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 18adf37d08c..cb131f1d166 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -1,6 +1,6 @@ use crate::coercion::{AsCoercionSite, CoerceMany}; use crate::{Diverges, Expectation, FnCtxt, Needs}; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::{ self as hir, def::{CtorOf, DefKind, Res}, @@ -177,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn explain_never_type_coerced_to_unit( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, arm: &hir::Arm<'tcx>, arm_ty: Ty<'tcx>, prior_arm: Option<(Option<hir::HirId>, Ty<'tcx>, Span)>, @@ -209,7 +209,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_removing_semicolon_for_coerce( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'tcx>, arm_ty: Ty<'tcx>, prior_arm: Option<(Option<hir::HirId>, Ty<'tcx>, Span)>, @@ -303,7 +303,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// `if let PAT = EXPR {}` expressions that could be turned into `let PAT = EXPR;`. fn explain_if_expr( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ret_reason: Option<(Span, String)>, if_span: Span, cond_expr: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 27614634c6b..69bcb6b8c15 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -4,7 +4,7 @@ use super::{Expectation, FnCtxt, TupleArgumentsFlag}; use crate::errors; use rustc_ast::util::parser::PREC_POSTFIX; -use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, StashKey}; +use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, StashKey}; use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; @@ -347,7 +347,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// likely intention is to call the closure, suggest `(||{})()`. (#55851) fn identify_bad_closure_def_and_call( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, hir_id: hir::HirId, callee_node: &hir::ExprKind<'_>, callee_span: Span, @@ -410,7 +410,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// likely intention is to create an array containing tuples. fn maybe_suggest_bad_array_definition( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, call_expr: &'tcx hir::Expr<'tcx>, callee_expr: &'tcx hir::Expr<'tcx>, ) -> bool { @@ -601,7 +601,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// and suggesting the fix if the method probe is successful. fn suggest_call_as_method( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_, ()>, segment: &'tcx hir::PathSegment<'tcx>, arg_exprs: &'tcx [hir::Expr<'tcx>], call_expr: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 042dd576c5b..b662d23c271 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -33,7 +33,7 @@ use super::FnCtxt; use crate::errors; use crate::type_error_struct; use hir::ExprKind; -use rustc_errors::{codes::*, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{codes::*, Applicability, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::mir::Mutability; @@ -139,10 +139,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Never | ty::Dynamic(_, _, ty::DynStar) | ty::Error(_) => { - let guar = self - .dcx() - .span_delayed_bug(span, format!("`{t:?}` should be sized but is not?")); - return Err(guar); + self.dcx().span_bug(span, format!("`{t:?}` should be sized but is not?")); } }) } @@ -983,7 +980,11 @@ impl<'a, 'tcx> CastCheck<'tcx> { /// Attempt to suggest using `.is_empty` when trying to cast from a /// collection type to a boolean. - fn try_suggest_collection_to_bool(&self, fcx: &FnCtxt<'a, 'tcx>, err: &mut Diagnostic) { + fn try_suggest_collection_to_bool( + &self, + fcx: &FnCtxt<'a, 'tcx>, + err: &mut DiagnosticBuilder<'_>, + ) { if self.cast_ty.is_bool() { let derefed = fcx .autoderef(self.expr_span, self.expr_ty) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 6bab8f75d24..9f6175eac13 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -36,9 +36,7 @@ //! ``` use crate::FnCtxt; -use rustc_errors::{ - codes::*, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan, -}; +use rustc_errors::{codes::*, struct_span_code_err, Applicability, DiagnosticBuilder, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; @@ -1439,7 +1437,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { &mut self, fcx: &FnCtxt<'a, 'tcx>, cause: &ObligationCause<'tcx>, - augment_error: impl FnOnce(&mut Diagnostic), + augment_error: impl FnOnce(&mut DiagnosticBuilder<'_>), label_unit_as_expected: bool, ) { self.coerce_inner( @@ -1462,7 +1460,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { cause: &ObligationCause<'tcx>, expression: Option<&'tcx hir::Expr<'tcx>>, mut expression_ty: Ty<'tcx>, - augment_error: impl FnOnce(&mut Diagnostic), + augment_error: impl FnOnce(&mut DiagnosticBuilder<'_>), label_expression_as_expected: bool, ) { // Incorporate whatever type inference information we have @@ -1673,7 +1671,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fn note_unreachable_loop_return( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, tcx: TyCtxt<'tcx>, expr: &hir::Expr<'tcx>, ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>, diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index fdb2cb69ee7..98e3fbb8b11 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,6 +1,6 @@ use crate::FnCtxt; use rustc_errors::MultiSpan; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::intravisit::Visitor; @@ -19,7 +19,7 @@ use super::method::probe; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn emit_type_mismatch_suggestions( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'tcx>, expr_ty: Ty<'tcx>, expected: Ty<'tcx>, @@ -70,7 +70,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn emit_coerce_suggestions( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'tcx>, expr_ty: Ty<'tcx>, expected: Ty<'tcx>, @@ -280,7 +280,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// with some expectation given by `source`. pub fn note_source_of_type_mismatch_constraint( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, source: TypeMismatchSource<'tcx>, ) -> bool { @@ -550,7 +550,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // expected type. pub fn annotate_loop_expected_due_to_inference( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, error: Option<TypeError<'tcx>>, ) { @@ -673,7 +673,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn annotate_expected_due_to_let_ty( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, error: Option<TypeError<'tcx>>, ) { @@ -782,7 +782,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn annotate_alternative_method_deref( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, error: Option<TypeError<'tcx>>, ) { @@ -1029,7 +1029,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn explain_self_literal( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, @@ -1082,7 +1082,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn note_wrong_return_ty_due_to_generic_arg( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, checked_ty: Ty<'tcx>, ) { diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 1af0b75bd23..f609d0f7e8f 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -3,8 +3,8 @@ use std::borrow::Cow; use crate::fluent_generated as fluent; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg, - MultiSpan, SubdiagnosticMessageOp, + codes::*, AddToDiagnostic, Applicability, DiagnosticArgValue, DiagnosticBuilder, + EmissionGuarantee, IntoDiagnosticArg, MultiSpan, SubdiagnosticMessageOp, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; @@ -195,7 +195,11 @@ pub struct TypeMismatchFruTypo { } impl AddToDiagnostic for TypeMismatchFruTypo { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { diag.arg("expr", self.expr.as_deref().unwrap_or("NONE")); // Only explain that `a ..b` is a range if it's split up @@ -370,7 +374,11 @@ pub struct RemoveSemiForCoerce { } impl AddToDiagnostic for RemoveSemiForCoerce { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { let mut multispan: MultiSpan = self.semi.into(); multispan.push_span_label(self.expr, fluent::hir_typeck_remove_semi_for_coerce_expr); multispan.push_span_label(self.ret, fluent::hir_typeck_remove_semi_for_coerce_ret); @@ -541,8 +549,12 @@ pub enum CastUnknownPointerSub { From(Span), } -impl AddToDiagnostic for CastUnknownPointerSub { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F) { +impl rustc_errors::AddToDiagnostic for CastUnknownPointerSub { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { match self { CastUnknownPointerSub::To(span) => { let msg = f(diag, crate::fluent_generated::hir_typeck_label_to); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index a946d59ff2b..89cc46dc5ab 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -26,8 +26,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::unord::UnordMap; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, AddToDiagnostic, Applicability, Diagnostic, - DiagnosticBuilder, ErrorGuaranteed, StashKey, + codes::*, pluralize, struct_span_code_err, AddToDiagnostic, Applicability, DiagnosticBuilder, + ErrorGuaranteed, StashKey, }; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -69,23 +69,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, expr: &'tcx hir::Expr<'tcx>, expected_ty: Ty<'tcx>, - extend_err: impl FnOnce(&mut Diagnostic), + extend_err: impl FnOnce(&mut DiagnosticBuilder<'_>), ) -> Ty<'tcx> { let mut ty = self.check_expr_with_expectation(expr, ExpectHasType(expected_ty)); // While we don't allow *arbitrary* coercions here, we *do* allow // coercions from ! to `expected`. if ty.is_never() { - if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) { - let reported = self.dcx().span_delayed_bug( - expr.span, - "expression with never type wound up being adjusted", - ); - return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] { - target.to_owned() - } else { - Ty::new_error(self.tcx(), reported) - }; + if let Some(_) = self.typeck_results.borrow().adjustments().get(expr.hir_id) { + self.dcx() + .span_bug(expr.span, "expression with never type wound up being adjusted"); } let adj_ty = self.next_ty_var(TypeVariableOrigin { @@ -958,7 +951,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs: &'tcx hir::Expr<'tcx>, code: ErrCode, op_span: Span, - adjust_err: impl FnOnce(&mut Diagnostic), + adjust_err: impl FnOnce(&mut DiagnosticBuilder<'_>), ) { if lhs.is_syntactic_place_expr() { return; @@ -1223,7 +1216,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let lhs_ty = self.check_expr_with_needs(lhs, Needs::MutPlace); - let suggest_deref_binop = |err: &mut Diagnostic, rhs_ty: Ty<'tcx>| { + let suggest_deref_binop = |err: &mut DiagnosticBuilder<'_>, rhs_ty: Ty<'tcx>| { if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { // Can only assign if the type is sized, so if `DerefMut` yields a type that is // unsized, do not suggest dereferencing it. @@ -1322,10 +1315,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the LUB of the breaks (possibly ! if none); else, it // is nil. This makes sense because infinite loops // (which would have type !) are only possible iff we - // permit break with a value [1]. + // permit break with a value. if ctxt.coerce.is_none() && !ctxt.may_break { - // [1] - self.dcx().span_delayed_bug(body.span, "no coercion, but loop may not break"); + self.dcx().span_bug(body.span, "no coercion, but loop may not break"); } ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| Ty::new_unit(self.tcx)) } @@ -2008,7 +2000,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { last_expr_field: &hir::ExprField<'tcx>, variant: &ty::VariantDef, args: GenericArgsRef<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) { // I don't use 'is_range_literal' because only double-sided, half-open ranges count. if let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [range_start, range_end], _) = @@ -2524,7 +2516,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_await_on_field_access( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, field_ident: Ident, base: &'tcx hir::Expr<'tcx>, ty: Ty<'tcx>, @@ -2723,7 +2715,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit() } - fn point_at_param_definition(&self, err: &mut Diagnostic, param: ty::ParamTy) { + fn point_at_param_definition(&self, err: &mut DiagnosticBuilder<'_>, param: ty::ParamTy) { let generics = self.tcx.generics_of(self.body_id); let generic_param = generics.type_param(¶m, self.tcx); if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind { @@ -2742,7 +2734,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn maybe_suggest_array_indexing( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, base: &hir::Expr<'_>, field: Ident, @@ -2766,7 +2758,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_first_deref_field( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, base: &hir::Expr<'_>, field: Ident, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index ce8b62d19b4..986af2f5c9e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -5,7 +5,7 @@ use crate::rvalue_scopes; use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey}; +use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, StashKey}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -1020,7 +1020,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn note_internal_mutation_in_method( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, expected: Option<Ty<'tcx>>, found: Ty<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index d8d3d45dd40..2d9ec9f6bab 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -13,7 +13,7 @@ use itertools::Itertools; use rustc_ast as ast; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{ - codes::*, pluralize, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey, + codes::*, pluralize, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, StashKey, }; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; @@ -740,8 +740,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); // We're done if we found errors, but we already emitted them. - if let Some(reported) = reported { - assert!(errors.is_empty()); + if let Some(reported) = reported + && errors.is_empty() + { return reported; } assert!(!errors.is_empty()); @@ -1935,7 +1936,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn label_fn_like( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, callable_def_id: Option<DefId>, callee_ty: Option<Ty<'tcx>>, call_expr: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index c1a32a1afa3..e57717c25d9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -12,7 +12,7 @@ use core::cmp::min; use core::iter; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; use rustc_data_structures::packed::Pu128; -use rustc_errors::{Applicability, Diagnostic, MultiSpan}; +use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; @@ -48,7 +48,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .copied() } - pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) { + pub(in super::super) fn suggest_semicolon_at_end( + &self, + span: Span, + err: &mut DiagnosticBuilder<'_>, + ) { // This suggestion is incorrect for // fn foo() -> bool { match () { () => true } || match () { () => true } } err.span_suggestion_short( @@ -66,7 +70,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// - Possible missing return type if the return type is the default, and not `fn main()`. pub fn suggest_mismatched_types_on_tail( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, @@ -97,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// ``` pub(crate) fn suggest_fn_call( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, found: Ty<'tcx>, can_satisfy: impl FnOnce(Ty<'tcx>) -> bool, @@ -179,7 +183,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn suggest_two_fn_call( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, lhs_expr: &'tcx hir::Expr<'tcx>, lhs_ty: Ty<'tcx>, rhs_expr: &'tcx hir::Expr<'tcx>, @@ -253,7 +257,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn suggest_remove_last_method_call( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'tcx>, expected: Ty<'tcx>, ) -> bool { @@ -282,7 +286,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn suggest_deref_ref_or_into( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, @@ -540,7 +544,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// in the heap by calling `Box::new()`. pub(in super::super) fn suggest_boxing_when_appropriate( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, span: Span, hir_id: HirId, expected: Ty<'tcx>, @@ -583,7 +587,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// suggest a non-capturing closure pub(in super::super) fn suggest_no_capture_closure( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expected: Ty<'tcx>, found: Ty<'tcx>, ) -> bool { @@ -620,7 +624,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self, err))] pub(in super::super) fn suggest_calling_boxed_future_when_appropriate( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, expected: Ty<'tcx>, found: Ty<'tcx>, @@ -732,7 +736,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`. pub fn suggest_missing_semicolon( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expression: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>, needs_block: bool, @@ -791,7 +795,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(level = "trace", skip(self, err))] pub(in super::super) fn suggest_missing_return_type( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, fn_decl: &hir::FnDecl<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, @@ -909,7 +913,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// ``` fn try_suggest_return_impl_trait( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expected: Ty<'tcx>, found: Ty<'tcx>, fn_id: hir::HirId, @@ -1014,7 +1018,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn suggest_missing_break_or_return_expr( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &'tcx hir::Expr<'tcx>, fn_decl: &hir::FnDecl<'tcx>, expected: Ty<'tcx>, @@ -1118,7 +1122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn suggest_missing_parentheses( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, ) -> bool { let sp = self.tcx.sess.source_map().start_point(expr.span).with_parent(None); @@ -1136,7 +1140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// if it was possibly mistaken array syntax. pub(crate) fn suggest_block_to_brackets_peeling_refs( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, mut expr: &hir::Expr<'_>, mut expr_ty: Ty<'tcx>, mut expected_ty: Ty<'tcx>, @@ -1163,7 +1167,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn suggest_clone_for_ref( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, @@ -1198,7 +1202,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn suggest_copied_cloned_or_as_ref( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, @@ -1248,7 +1252,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn suggest_into( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, @@ -1311,7 +1315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// When expecting a `bool` and finding an `Option`, suggests using `let Some(..)` or `.is_some()` pub(crate) fn suggest_option_to_bool( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, @@ -1368,7 +1372,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`. pub(crate) fn suggest_block_to_brackets( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, blk: &hir::Block<'_>, blk_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, @@ -1408,7 +1412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self, err))] pub(crate) fn suggest_floating_point_literal( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, expected_ty: Ty<'tcx>, ) -> bool { @@ -1479,7 +1483,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self, err))] pub(crate) fn suggest_null_ptr_for_literal_zero_given_to_ptr_arg( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, expected_ty: Ty<'tcx>, ) -> bool { @@ -1517,7 +1521,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn suggest_associated_const( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'tcx>, expected_ty: Ty<'tcx>, ) -> bool { @@ -1611,7 +1615,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// which is a side-effect of autoref. pub(crate) fn note_type_is_not_clone( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, expected_ty: Ty<'tcx>, found_ty: Ty<'tcx>, expr: &hir::Expr<'_>, @@ -1811,7 +1815,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, blk: &'tcx hir::Block<'tcx>, expected_ty: Ty<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) -> bool { if let Some((span_semi, boxed)) = self.err_ctxt().could_remove_semicolon(blk, expected_ty) { if let StatementAsExpression::NeedsBoxing = boxed { @@ -1856,7 +1860,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn suggest_missing_unwrap_expect( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, @@ -1939,7 +1943,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn suggest_coercing_result_via_try_operator( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, @@ -1986,7 +1990,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// sole field is of the found type, suggest such variants. (Issue #42764) pub(crate) fn suggest_compatible_variants( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, expected: Ty<'tcx>, expr_ty: Ty<'tcx>, @@ -2175,7 +2179,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn suggest_non_zero_new_unwrap( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, expected: Ty<'tcx>, expr_ty: Ty<'tcx>, @@ -2719,7 +2723,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn suggest_cast( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, checked_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, @@ -2847,7 +2851,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id); let suggest_fallible_into_or_lhs_from = - |err: &mut Diagnostic, exp_to_found_is_fallible: bool| { + |err: &mut DiagnosticBuilder<'_>, exp_to_found_is_fallible: bool| { // If we know the expression the expected type is derived from, we might be able // to suggest a widening conversion rather than a narrowing one (which may // panic). For example, given x: u8 and y: u32, if we know the span of "x", @@ -2887,7 +2891,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let suggest_to_change_suffix_or_into = - |err: &mut Diagnostic, + |err: &mut DiagnosticBuilder<'_>, found_to_exp_is_fallible: bool, exp_to_found_is_fallible: bool| { let exp_is_lhs = expected_ty_expr.is_some_and(|e| self.tcx.hir().is_lhs(e.hir_id)); @@ -3085,7 +3089,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Identify when the user has written `foo..bar()` instead of `foo.bar()`. pub(crate) fn suggest_method_call_on_range_literal( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, @@ -3163,7 +3167,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// block without a tail expression. pub(crate) fn suggest_return_binding_for_missing_tail_expr( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, checked_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 2dee5093e87..9e3867e630d 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -48,8 +48,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let to = normalize(to); trace!(?from, ?to); if from.has_non_region_infer() || to.has_non_region_infer() { - tcx.dcx().span_delayed_bug(span, "argument to transmute has inference variables"); - return; + // Note: this path is currently not reached in any test, so any + // example that triggers this would be worth minimizing and + // converting into a test. + tcx.dcx().span_bug(span, "argument to transmute has inference variables"); } // Transmutes that are only changing lifetimes are always ok. if from == to { diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index fefaf996725..c300ec7444b 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -570,8 +570,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { _ => { self.tcx() .dcx() - .span_delayed_bug(span, "struct or tuple struct pattern not applied to an ADT"); - Err(()) + .span_bug(span, "struct or tuple struct pattern not applied to an ADT"); } } } @@ -583,8 +582,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { match ty.kind() { ty::Tuple(args) => Ok(args.len()), _ => { - self.tcx().dcx().span_delayed_bug(span, "tuple pattern not applied to a tuple"); - Err(()) + self.tcx().dcx().span_bug(span, "tuple pattern not applied to a tuple"); } } } diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index fb969c82d8a..f8d0911a2eb 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -11,7 +11,7 @@ pub use self::suggest::SelfSource; pub use self::MethodError::*; use crate::FnCtxt; -use rustc_errors::{Applicability, Diagnostic, SubdiagnosticMessage}; +use rustc_errors::{Applicability, DiagnosticBuilder, SubdiagnosticMessage}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace}; use rustc_hir::def_id::DefId; @@ -126,7 +126,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self, err, call_expr))] pub(crate) fn suggest_method_call( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, msg: impl Into<SubdiagnosticMessage> + std::fmt::Debug, method_name: Ident, self_ty: Ty<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index d7edc70bce8..a58e194e20a 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -804,11 +804,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let trait_ref = principal.with_self_ty(self.tcx, self_ty); self.elaborate_bounds(iter::once(trait_ref), |this, new_trait_ref, item| { if new_trait_ref.has_non_region_bound_vars() { - this.dcx().span_delayed_bug( + this.dcx().span_bug( this.span, "tried to select method from HRTB with non-lifetime bound vars", ); - return; } let new_trait_ref = this.instantiate_bound_regions_with_erased(new_trait_ref); diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 4151298b42f..cc111af5d8f 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -12,8 +12,8 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, - MultiSpan, StashKey, + codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, MultiSpan, + StashKey, }; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -34,8 +34,8 @@ use rustc_middle::ty::IsSuggestable; use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::def_id::DefIdSet; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::Symbol; use rustc_span::{edit_distance, ExpnKind, FileName, MacroKind, Span}; +use rustc_span::{Symbol, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote; use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _; @@ -369,6 +369,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Some(file) = file { err.note(format!("the full type name has been written to '{}'", file.display())); + err.note(format!( + "consider using `--verbose` to print the full type name to the console" + )); } err @@ -493,6 +496,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(file) = ty_file { err.note(format!("the full type name has been written to '{}'", file.display(),)); + err.note(format!( + "consider using `--verbose` to print the full type name to the console" + )); } if rcvr_ty.references_error() { err.downgrade_to_delayed_bug(); @@ -1127,7 +1133,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let label_span_not_found = |err: &mut Diagnostic| { + let label_span_not_found = |err: &mut DiagnosticBuilder<'_>| { if unsatisfied_predicates.is_empty() { err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); let is_string_or_ref_str = match rcvr_ty.kind() { @@ -1438,7 +1444,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_source: SelfSource<'tcx>, args: Option<&'tcx [hir::Expr<'tcx>]>, span: Span, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, sources: &mut Vec<CandidateSource>, sugg_span: Option<Span>, ) { @@ -1584,7 +1590,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Look at all the associated functions without receivers in the type's inherent impls /// to look for builders that return `Self`, `Option<Self>` or `Result<Self, _>`. - fn find_builder_fn(&self, err: &mut Diagnostic, rcvr_ty: Ty<'tcx>) { + fn find_builder_fn(&self, err: &mut DiagnosticBuilder<'_>, rcvr_ty: Ty<'tcx>) { let ty::Adt(adt_def, _) = rcvr_ty.kind() else { return; }; @@ -1597,7 +1603,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .filter(|item| matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter) .filter_map(|item| { // Only assoc fns that return `Self`, `Option<Self>` or `Result<Self, _>`. - let ret_ty = self.tcx.fn_sig(item.def_id).skip_binder().output(); + let ret_ty = self + .tcx + .fn_sig(item.def_id) + .instantiate(self.tcx, self.fresh_args_for_item(DUMMY_SP, item.def_id)) + .output(); let ret_ty = self.tcx.instantiate_bound_regions_with_erased(ret_ty); let ty::Adt(def, args) = ret_ty.kind() else { return None; @@ -1665,7 +1675,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// doesn't take a `self` receiver. fn suggest_associated_call_syntax( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, static_candidates: &Vec<CandidateSource>, rcvr_ty: Ty<'tcx>, source: SelfSource<'tcx>, @@ -1809,7 +1819,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty: Ty<'tcx>, expr: &hir::Expr<'_>, item_name: Ident, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) -> bool { let tcx = self.tcx; let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { @@ -2132,7 +2142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()` fn suggest_calling_method_on_field( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, source: SelfSource<'tcx>, span: Span, actual: Ty<'tcx>, @@ -2212,7 +2222,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_unwrapping_inner_self( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, source: SelfSource<'tcx>, actual: Ty<'tcx>, item_name: Ident, @@ -2401,7 +2411,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn note_unmet_impls_on_type( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, errors: Vec<FulfillmentError<'tcx>>, suggest_derive: bool, ) { @@ -2484,7 +2494,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn note_predicate_source_and_get_derives( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, unsatisfied_predicates: &[( ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, @@ -2566,7 +2576,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn suggest_derive( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, unsatisfied_predicates: &[( ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, @@ -2602,7 +2612,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn note_derefed_ty_has_method( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, self_source: SelfSource<'tcx>, rcvr_ty: Ty<'tcx>, item_name: Ident, @@ -2682,7 +2692,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_await_before_method( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, item_name: Ident, ty: Ty<'tcx>, call: &hir::Expr<'_>, @@ -2705,7 +2715,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn suggest_use_candidates(&self, err: &mut Diagnostic, msg: String, candidates: Vec<DefId>) { + fn suggest_use_candidates( + &self, + err: &mut DiagnosticBuilder<'_>, + msg: String, + candidates: Vec<DefId>, + ) { let parent_map = self.tcx.visible_parent_map(()); // Separate out candidates that must be imported with a glob, because they are named `_` @@ -2752,7 +2767,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_valid_traits( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, valid_out_of_scope_traits: Vec<DefId>, explain: bool, ) -> bool { @@ -2793,7 +2808,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_traits_to_import( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, span: Span, rcvr_ty: Ty<'tcx>, item_name: Ident, @@ -3332,7 +3347,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// FIXME: currently not working for suggesting `map_or_else`, see #102408 pub(crate) fn suggest_else_fn_with_closure( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, found: Ty<'tcx>, expected: Ty<'tcx>, @@ -3458,7 +3473,7 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> { fn print_disambiguation_help<'tcx>( tcx: TyCtxt<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, source: SelfSource<'tcx>, args: Option<&'tcx [hir::Expr<'tcx>]>, trait_ref: ty::TraitRef<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 929b3557f52..fba240bf752 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -5,7 +5,7 @@ use super::FnCtxt; use crate::Expectation; use rustc_ast as ast; use rustc_data_structures::packed::Pu128; -use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder}; +use rustc_errors::{codes::*, struct_span_code_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::traits::ObligationCauseCode; @@ -695,7 +695,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_expr: &'tcx hir::Expr<'tcx>, lhs_ty: Ty<'tcx>, rhs_ty: Ty<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, is_assign: IsAssign, op: hir::BinOp, ) -> bool { diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 5026fbe4b80..b15c9ef9018 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -3,8 +3,8 @@ use crate::{errors, FnCtxt, LoweredTy}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, MultiSpan, + codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, + MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -529,7 +529,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } - fn endpoint_has_type(&self, err: &mut Diagnostic, span: Span, ty: Ty<'_>) { + fn endpoint_has_type(&self, err: &mut DiagnosticBuilder<'_>, span: Span, ty: Ty<'_>) { if !ty.references_error() { err.span_label(span, format!("this is of type `{ty}`")); } @@ -683,7 +683,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_adding_missing_ref_or_removing_ref( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, span: Span, expected: Ty<'tcx>, actual: Ty<'tcx>, @@ -715,7 +715,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Precondition: pat is a Ref(_) pattern - fn borrow_pat_suggestion(&self, err: &mut Diagnostic, pat: &Pat<'_>) { + fn borrow_pat_suggestion(&self, err: &mut DiagnosticBuilder<'_>, pat: &Pat<'_>) { let tcx = self.tcx; if let PatKind::Ref(inner, mutbl) = pat.kind && let PatKind::Binding(_, _, binding, ..) = inner.kind @@ -933,7 +933,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn maybe_suggest_range_literal( &self, - e: &mut Diagnostic, + e: &mut DiagnosticBuilder<'_>, opt_def_id: Option<hir::def_id::DefId>, ident: Ident, ) -> bool { @@ -1085,10 +1085,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let variant = match res { Res::Err => { - let e = tcx.dcx().span_delayed_bug(pat.span, "`Res::Err` but no error emitted"); - self.set_tainted_by_errors(e); - on_error(e); - return Ty::new_error(tcx, e); + tcx.dcx().span_bug(pat.span, "`Res::Err` but no error emitted"); } Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => { let e = report_unexpected_res(res); diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 23d29916922..dd9c16d006a 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -312,7 +312,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) { let incr_comp_session_dir: PathBuf = sess.incr_comp_session_dir().clone(); - if sess.dcx().has_errors_or_lint_errors_or_delayed_bugs().is_some() { + if sess.dcx().has_errors_or_delayed_bugs().is_some() { // If there have been any errors during compilation, we don't want to // publish this session directory. Rather, we'll just delete it. diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index ff0c58d09de..32759f5284a 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -32,7 +32,7 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) { return; } // This is going to be deleted in finalize_session_directory, so let's not create it. - if sess.dcx().has_errors_or_lint_errors_or_delayed_bugs().is_some() { + if sess.dcx().has_errors_or_delayed_bugs().is_some() { return; } @@ -87,7 +87,7 @@ pub fn save_work_product_index( return; } // This is going to be deleted in finalize_session_directory, so let's not create it - if sess.dcx().has_errors_or_lint_errors().is_some() { + if sess.dcx().has_errors().is_some() { return; } diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 8bfc05d6a96..f29ba70be98 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1,7 +1,8 @@ use hir::GenericParamKind; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage, - DiagnosticStyledString, IntoDiagnosticArg, MultiSpan, SubdiagnosticMessageOp, + codes::*, AddToDiagnostic, Applicability, DiagnosticBuilder, DiagnosticMessage, + DiagnosticStyledString, EmissionGuarantee, IntoDiagnosticArg, MultiSpan, + SubdiagnosticMessageOp, }; use rustc_hir as hir; use rustc_hir::FnRetTy; @@ -225,7 +226,11 @@ pub enum RegionOriginNote<'a> { } impl AddToDiagnostic for RegionOriginNote<'_> { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { let mut label_or_note = |span, msg: DiagnosticMessage| { let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count(); let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count(); @@ -286,7 +291,11 @@ pub enum LifetimeMismatchLabels { } impl AddToDiagnostic for LifetimeMismatchLabels { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { match self { LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { diag.span_label(param_span, fluent::infer_declared_different); @@ -330,7 +339,11 @@ pub struct AddLifetimeParamsSuggestion<'a> { } impl AddToDiagnostic for AddLifetimeParamsSuggestion<'_> { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { let mut mk_suggestion = || { let ( hir::Ty { kind: hir::TyKind::Ref(lifetime_sub, _), .. }, @@ -428,7 +441,11 @@ pub struct IntroducesStaticBecauseUnmetLifetimeReq { } impl AddToDiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(mut self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + mut self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { self.unmet_requirements .push_span_label(self.binding_span, fluent::infer_msl_introduces_static); diag.span_note(self.unmet_requirements, fluent::infer_msl_unmet_req); @@ -743,7 +760,11 @@ pub struct ConsiderBorrowingParamHelp { } impl AddToDiagnostic for ConsiderBorrowingParamHelp { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { let mut type_param_span: MultiSpan = self.spans.clone().into(); for &span in &self.spans { // Seems like we can't call f() here as Into<DiagnosticMessage> is required @@ -784,7 +805,11 @@ pub struct DynTraitConstraintSuggestion { } impl AddToDiagnostic for DynTraitConstraintSuggestion { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { let mut multi_span: MultiSpan = vec![self.span].into(); multi_span.push_span_label(self.span, fluent::infer_dtcs_has_lifetime_req_label); multi_span.push_span_label(self.ident.span, fluent::infer_dtcs_introduces_requirement); @@ -827,7 +852,11 @@ pub struct ReqIntroducedLocations { } impl AddToDiagnostic for ReqIntroducedLocations { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(mut self, diag: &mut Diagnostic, f: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + mut self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { for sp in self.spans { self.span.push_span_label(sp, fluent::infer_ril_introduced_here); } @@ -846,7 +875,11 @@ pub struct MoreTargeted { } impl AddToDiagnostic for MoreTargeted { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _f: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { diag.code(E0772); diag.primary_message(fluent::infer_more_targeted); diag.arg("ident", self.ident); @@ -1265,7 +1298,11 @@ pub struct SuggestTuplePatternMany { } impl AddToDiagnostic for SuggestTuplePatternMany { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { diag.arg("path", self.path); let message = f(diag, crate::fluent_generated::infer_stp_wrap_many.into()); diag.multipart_suggestions( diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index a59a4df7729..c272aa63b08 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -1,6 +1,9 @@ use crate::fluent_generated as fluent; use crate::infer::error_reporting::nice_region_error::find_anon_type; -use rustc_errors::{AddToDiagnostic, Diagnostic, IntoDiagnosticArg, SubdiagnosticMessageOp}; +use rustc_errors::{ + AddToDiagnostic, DiagnosticBuilder, EmissionGuarantee, IntoDiagnosticArg, + SubdiagnosticMessageOp, +}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{symbol::kw, Span}; @@ -160,7 +163,11 @@ impl RegionExplanation<'_> { } impl AddToDiagnostic for RegionExplanation<'_> { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { diag.arg("pref_kind", self.prefix); diag.arg("suff_kind", self.suffix); diag.arg("desc_kind", self.desc.kind); diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 56a45586c9d..9d2e065afa3 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -12,22 +12,19 @@ use crate::infer::canonical::{ Canonical, CanonicalQueryResponse, CanonicalVarValues, Certainty, OriginalQueryValues, QueryOutlivesConstraint, QueryRegionConstraints, QueryResponse, }; -use crate::infer::nll_relate::{TypeRelating, TypeRelatingDelegate}; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; -use crate::infer::{DefineOpaqueTypes, InferCtxt, InferOk, InferResult, NllRegionVariableOrigin}; +use crate::infer::{DefineOpaqueTypes, InferCtxt, InferOk, InferResult}; use crate::traits::query::NoSolution; use crate::traits::{Obligation, ObligationCause, PredicateObligation}; -use crate::traits::{PredicateObligations, TraitEngine, TraitEngineExt}; +use crate::traits::{TraitEngine, TraitEngineExt}; use rustc_data_structures::captures::Captures; use rustc_index::Idx; use rustc_index::IndexVec; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::relate::TypeRelation; -use rustc_middle::ty::{self, BoundVar, ToPredicate, Ty, TyCtxt}; +use rustc_middle::ty::{self, BoundVar, Ty, TyCtxt}; use rustc_middle::ty::{GenericArg, GenericArgKind}; -use rustc_span::{Span, Symbol}; use std::fmt::Debug; use std::iter; @@ -290,31 +287,19 @@ impl<'tcx> InferCtxt<'tcx> { } (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => { - TypeRelating::new( - self, - QueryTypeRelatingDelegate { - infcx: self, - param_env, - cause, - obligations: &mut obligations, - }, - ty::Variance::Invariant, - ) - .relate(v1, v2)?; + obligations.extend( + self.at(&cause, param_env) + .eq(DefineOpaqueTypes::Yes, v1, v2)? + .into_obligations(), + ); } (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => { - TypeRelating::new( - self, - QueryTypeRelatingDelegate { - infcx: self, - param_env, - cause, - obligations: &mut obligations, - }, - ty::Variance::Invariant, - ) - .relate(v1, v2)?; + obligations.extend( + self.at(&cause, param_env) + .eq(DefineOpaqueTypes::Yes, v1, v2)? + .into_obligations(), + ); } _ => { @@ -405,7 +390,7 @@ impl<'tcx> InferCtxt<'tcx> { /// will instantiate fresh inference variables for each canonical /// variable instead. Therefore, the result of this method must be /// properly unified - #[instrument(level = "debug", skip(self, cause, param_env))] + #[instrument(level = "debug", skip(self, param_env))] fn query_response_instantiation_guess<R>( &self, cause: &ObligationCause<'tcx>, @@ -697,67 +682,3 @@ pub fn make_query_region_constraints<'tcx>( QueryRegionConstraints { outlives, member_constraints: member_constraints.clone() } } - -struct QueryTypeRelatingDelegate<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, - obligations: &'a mut Vec<PredicateObligation<'tcx>>, - param_env: ty::ParamEnv<'tcx>, - cause: &'a ObligationCause<'tcx>, -} - -impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> { - fn span(&self) -> Span { - self.cause.span - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.param_env - } - - fn create_next_universe(&mut self) -> ty::UniverseIndex { - self.infcx.create_next_universe() - } - - fn next_existential_region_var( - &mut self, - from_forall: bool, - _name: Option<Symbol>, - ) -> ty::Region<'tcx> { - let origin = NllRegionVariableOrigin::Existential { from_forall }; - self.infcx.next_nll_region_var(origin) - } - - fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx> { - ty::Region::new_placeholder(self.infcx.tcx, placeholder) - } - - fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { - self.infcx.next_nll_region_var_in_universe( - NllRegionVariableOrigin::Existential { from_forall: false }, - universe, - ) - } - - fn push_outlives( - &mut self, - sup: ty::Region<'tcx>, - sub: ty::Region<'tcx>, - _info: ty::VarianceDiagInfo<'tcx>, - ) { - self.obligations.push(Obligation { - cause: self.cause.clone(), - param_env: self.param_env, - predicate: ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(sup, sub)) - .to_predicate(self.infcx.tcx), - recursion_depth: 0, - }); - } - - fn forbid_inference_vars() -> bool { - true - } - - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { - self.obligations.extend(obligations); - } -} diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 9bfaeb5ee32..505d56cf491 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -60,8 +60,8 @@ use crate::traits::{ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, DiagCtxt, Diagnostic, - DiagnosticBuilder, DiagnosticStyledString, ErrorGuaranteed, IntoDiagnosticArg, + codes::*, pluralize, struct_span_code_err, Applicability, DiagCtxt, DiagnosticBuilder, + DiagnosticStyledString, ErrorGuaranteed, IntoDiagnosticArg, }; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -155,7 +155,7 @@ impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> { pub(super) fn note_and_explain_region<'tcx>( tcx: TyCtxt<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, prefix: &str, region: ty::Region<'tcx>, suffix: &str, @@ -180,7 +180,7 @@ pub(super) fn note_and_explain_region<'tcx>( fn explain_free_region<'tcx>( tcx: TyCtxt<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, prefix: &str, region: ty::Region<'tcx>, suffix: &str, @@ -262,7 +262,7 @@ fn msg_span_from_named_region<'tcx>( } fn emit_msg_span( - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, prefix: &str, description: String, span: Option<Span>, @@ -278,7 +278,7 @@ fn emit_msg_span( } fn label_msg_span( - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, prefix: &str, description: String, span: Option<Span>, @@ -577,7 +577,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } /// Adds a note if the types come from similarly named crates - fn check_and_note_conflicting_crates(&self, err: &mut Diagnostic, terr: TypeError<'tcx>) { + fn check_and_note_conflicting_crates( + &self, + err: &mut DiagnosticBuilder<'_>, + terr: TypeError<'tcx>, + ) { use hir::def_id::CrateNum; use rustc_hir::definitions::DisambiguatedDefPathData; use ty::print::Printer; @@ -651,7 +655,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - let report_path_match = |err: &mut Diagnostic, did1: DefId, did2: DefId| { + let report_path_match = |err: &mut DiagnosticBuilder<'_>, did1: DefId, did2: DefId| { // Only report definitions from different crates. If both definitions // are from a local module we could have false positives, e.g. // let _ = [{struct Foo; Foo}, {struct Foo; Foo}]; @@ -701,7 +705,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn note_error_origin( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, cause: &ObligationCause<'tcx>, exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>, terr: TypeError<'tcx>, @@ -1535,7 +1539,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { )] pub fn note_type_err( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, cause: &ObligationCause<'tcx>, secondary_span: Option<(Span, Cow<'static, str>)>, mut values: Option<ValuePairs<'tcx>>, @@ -1582,14 +1586,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { types_visitor } - fn report(&self, err: &mut Diagnostic) { + fn report(&self, err: &mut DiagnosticBuilder<'_>) { self.add_labels_for_types(err, "expected", &self.expected); self.add_labels_for_types(err, "found", &self.found); } fn add_labels_for_types( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, target: &str, types: &FxIndexMap<TyCategory, FxIndexSet<Span>>, ) { @@ -1803,7 +1807,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { |prim: Ty<'tcx>, shadow: Ty<'tcx>, defid: DefId, - diagnostic: &mut Diagnostic| { + diagnostic: &mut DiagnosticBuilder<'_>| { let name = shadow.sort_string(self.tcx); diagnostic.note(format!( "{prim} and {name} have similar names, but are actually distinct types" @@ -1823,7 +1827,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let diagnose_adts = |expected_adt: ty::AdtDef<'tcx>, found_adt: ty::AdtDef<'tcx>, - diagnostic: &mut Diagnostic| { + diagnostic: &mut DiagnosticBuilder<'_>| { let found_name = values.found.sort_string(self.tcx); let expected_name = values.expected.sort_string(self.tcx); @@ -1931,6 +1935,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { "the full type name has been written to '{}'", path.display(), )); + diag.note(format!("consider using `--verbose` to print the full type name to the console")); } } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs index adb3267d5be..cf8ac544106 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -12,7 +12,7 @@ use crate::infer::SubregionOrigin; use crate::infer::TyCtxt; use rustc_errors::AddToDiagnostic; -use rustc_errors::{Diagnostic, ErrorGuaranteed}; +use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir::Ty; use rustc_middle::ty::Region; @@ -142,7 +142,7 @@ pub fn suggest_adding_lifetime_params<'tcx>( sub: Region<'tcx>, ty_sup: &'tcx Ty<'_>, ty_sub: &'tcx Ty<'_>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) { let suggestion = AddLifetimeParamsSuggestion { tcx, sub, ty_sup, ty_sub, add_note: false }; suggestion.add_to_diagnostic(err); 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 c8fd4e3a692..83e0b763d24 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 @@ -9,7 +9,7 @@ use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{AddToDiagnostic, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ @@ -261,7 +261,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { pub fn suggest_new_region_bound( tcx: TyCtxt<'_>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, fn_returns: Vec<&rustc_hir::Ty<'_>>, lifetime_name: String, arg: Option<String>, @@ -488,7 +488,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// `'static` obligation. Suggest relaxing that implicit bound. fn find_impl_on_dyn_trait( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>, ctxt: &UnifyReceiverContext<'tcx>, ) -> bool { @@ -521,7 +521,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { fn suggest_constrain_dyn_trait_in_impl( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, found_dids: &FxIndexSet<DefId>, ident: Ident, self_ty: &hir::Ty<'_>, 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 bfff00b948e..f1f8314661f 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, Region, Ty, TypeVisitable}; +use rustc_middle::ty::{self, Binder, Region, Ty, TypeFoldable}; use rustc_span::Span; /// Information about the anonymous region we are searching for. @@ -142,10 +142,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { fn includes_region( &self, - ty: Binder<'tcx, impl TypeVisitable<TyCtxt<'tcx>>>, + ty: Binder<'tcx, impl TypeFoldable<TyCtxt<'tcx>>>, region: ty::BoundRegionKind, ) -> bool { - let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(&ty); + let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(ty); // We are only checking is any region meets the condition so order doesn't matter #[allow(rustc::potential_query_instability)] late_bound_regions.iter().any(|r| *r == region) diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index 50ac6235deb..0878505e85e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -5,7 +5,7 @@ use crate::errors::{ use crate::fluent_generated as fluent; use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt}; use crate::infer::{self, SubregionOrigin}; -use rustc_errors::{AddToDiagnostic, Diagnostic, DiagnosticBuilder}; +use rustc_errors::{AddToDiagnostic, DiagnosticBuilder}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::error::TypeError; @@ -15,7 +15,11 @@ use rustc_span::symbol::kw; use super::ObligationCauseAsDiagArg; impl<'tcx> TypeErrCtxt<'_, 'tcx> { - pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) { + pub(super) fn note_region_origin( + &self, + err: &mut DiagnosticBuilder<'_>, + origin: &SubregionOrigin<'tcx>, + ) { match *origin { infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { span: trace.cause.span, @@ -290,7 +294,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, trait_item_def_id: DefId, impl_item_def_id: LocalDefId, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) { // FIXME(compiler-errors): Right now this is only being used for region // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches, diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index 3c42f13141d..9df2f929501 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -1,6 +1,6 @@ use super::TypeErrCtxt; use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; -use rustc_errors::{pluralize, Diagnostic, MultiSpan}; +use rustc_errors::{pluralize, DiagnosticBuilder, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_middle::traits::ObligationCauseCode; @@ -15,7 +15,7 @@ use rustc_span::{def_id::DefId, sym, BytePos, Span, Symbol}; impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub fn note_and_explain_type_err( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, err: TypeError<'tcx>, cause: &ObligationCause<'tcx>, sp: Span, @@ -522,7 +522,7 @@ impl<T> Trait<T> for X { fn suggest_constraint( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, msg: impl Fn() -> String, body_owner_def_id: DefId, proj_ty: &ty::AliasTy<'tcx>, @@ -595,7 +595,7 @@ impl<T> Trait<T> for X { /// fn that returns the type. fn expected_projection( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, proj_ty: &ty::AliasTy<'tcx>, values: ExpectedFound<Ty<'tcx>>, body_owner_def_id: DefId, @@ -705,7 +705,7 @@ fn foo(&self) -> Self::T { String::new() } /// a return type. This can occur when dealing with `TryStream` (#71035). fn suggest_constraining_opaque_associated_type( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, msg: impl Fn() -> String, proj_ty: &ty::AliasTy<'tcx>, ty: Ty<'tcx>, @@ -740,7 +740,7 @@ fn foo(&self) -> Self::T { String::new() } fn point_at_methods_that_satisfy_associated_type( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, assoc_container_id: DefId, current_method_ident: Option<Symbol>, proj_ty_item_def_id: DefId, @@ -798,7 +798,7 @@ fn foo(&self) -> Self::T { String::new() } fn point_at_associated_type( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, body_owner_def_id: DefId, found: Ty<'tcx>, ) -> bool { @@ -879,7 +879,7 @@ fn foo(&self) -> Self::T { String::new() } /// type is defined on a supertrait of the one present in the bounds. fn constrain_generic_bound_associated_type_structured_suggestion( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, trait_ref: &ty::TraitRef<'tcx>, bounds: hir::GenericBounds<'_>, assoc: ty::AssocItem, @@ -916,7 +916,7 @@ fn foo(&self) -> Self::T { String::new() } /// associated type to a given type `ty`. fn constrain_associated_type_structured_suggestion( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, span: Span, assoc: ty::AssocItem, assoc_args: &[ty::GenericArg<'tcx>], diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 15834c78413..f7102ab6205 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -1,7 +1,7 @@ use hir::def::CtorKind; use hir::intravisit::{walk_expr, walk_stmt, Visitor}; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_middle::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, @@ -76,7 +76,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub(super) fn suggest_boxing_for_return_impl_trait( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, return_sp: Span, arm_spans: impl Iterator<Item = Span>, ) { @@ -100,7 +100,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, cause: &ObligationCause<'tcx>, exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, ) { // Heavily inspired by `FnCtxt::suggest_compatible_variants`, with // some modifications due to that being in typeck and this being in infer. @@ -177,7 +177,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { cause: &ObligationCause<'tcx>, exp_span: Span, exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, ) { debug!( "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}", @@ -258,7 +258,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, cause: &ObligationCause<'tcx>, exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, ) { debug!( "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})", @@ -298,7 +298,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { cause: &ObligationCause<'tcx>, span: Span, exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, ) { debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found); let ty::error::ExpectedFound { expected, found } = exp_found; @@ -532,7 +532,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { span: Span, hir: hir::Node<'_>, exp_found: &ty::error::ExpectedFound<ty::PolyTraitRef<'tcx>>, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, ) { // 0. Extract fn_decl from hir let hir::Node::Expr(hir::Expr { @@ -818,7 +818,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, blk: &'tcx hir::Block<'tcx>, expected_ty: Ty<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) -> bool { let diag = self.consider_returning_binding_diag(blk, expected_ty); match diag { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 243558b11a8..8fc71671b27 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -54,7 +54,6 @@ use self::region_constraints::{ RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot, }; pub use self::relate::combine::CombineFields; -pub use self::relate::nll as nll_relate; use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; pub mod at; @@ -713,7 +712,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { reported_trait_errors: Default::default(), reported_signature_mismatch: Default::default(), tainted_by_errors: Cell::new(None), - err_count_on_creation: tcx.dcx().err_count(), + err_count_on_creation: tcx.dcx().err_count_excluding_lint_errs(), stashed_err_count_on_creation: tcx.dcx().stashed_err_count(), universe: Cell::new(ty::UniverseIndex::ROOT), intercrate, @@ -1268,8 +1267,11 @@ impl<'tcx> InferCtxt<'tcx> { pub fn tainted_by_errors(&self) -> Option<ErrorGuaranteed> { if let Some(guar) = self.tainted_by_errors.get() { Some(guar) - } else if self.dcx().err_count() > self.err_count_on_creation { - // Errors reported since this infcx was made. + } else if self.dcx().err_count_excluding_lint_errs() > self.err_count_on_creation { + // Errors reported since this infcx was made. Lint errors are + // excluded to avoid some being swallowed in the presence of + // non-lint errors. (It's arguable whether or not this exclusion is + // important.) let guar = self.dcx().has_errors().unwrap(); self.set_tainted_by_errors(guar); Some(guar) diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index 7edfbf02a68..f7690831c2a 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -23,19 +23,18 @@ //! this should be correctly updated. use super::equate::Equate; -use super::generalize::{self, CombineDelegate, Generalization}; use super::glb::Glb; use super::lub::Lub; use super::sub::Sub; use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace}; use crate::traits::{Obligation, PredicateObligations}; use rustc_middle::infer::canonical::OriginalQueryValues; -use rustc_middle::infer::unify_key::{ConstVariableValue, EffectVarValue}; +use rustc_middle::infer::unify_key::EffectVarValue; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::relate::{RelateResult, TypeRelation}; use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeVisitableExt}; -use rustc_middle::ty::{AliasRelationDirection, TyVar}; use rustc_middle::ty::{IntType, UintType}; +use rustc_span::Span; #[derive(Clone)] pub struct CombineFields<'infcx, 'tcx> { @@ -195,7 +194,7 @@ impl<'tcx> InferCtxt<'tcx> { ty::ConstKind::Infer(InferConst::Var(b_vid)), ) => { self.inner.borrow_mut().const_unification_table().union(a_vid, b_vid); - return Ok(a); + Ok(a) } ( @@ -203,7 +202,7 @@ impl<'tcx> InferCtxt<'tcx> { ty::ConstKind::Infer(InferConst::EffectVar(b_vid)), ) => { self.inner.borrow_mut().effect_unification_table().union(a_vid, b_vid); - return Ok(a); + Ok(a) } // All other cases of inference with other variables are errors. @@ -221,19 +220,21 @@ impl<'tcx> InferCtxt<'tcx> { } (ty::ConstKind::Infer(InferConst::Var(vid)), _) => { - return self.unify_const_variable(vid, b); + self.instantiate_const_var(relation, relation.a_is_expected(), vid, b)?; + Ok(b) } (_, ty::ConstKind::Infer(InferConst::Var(vid))) => { - return self.unify_const_variable(vid, a); + self.instantiate_const_var(relation, !relation.a_is_expected(), vid, a)?; + Ok(a) } (ty::ConstKind::Infer(InferConst::EffectVar(vid)), _) => { - return Ok(self.unify_effect_variable(vid, b)); + Ok(self.unify_effect_variable(vid, b)) } (_, ty::ConstKind::Infer(InferConst::EffectVar(vid))) => { - return Ok(self.unify_effect_variable(vid, a)); + Ok(self.unify_effect_variable(vid, a)) } (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..)) @@ -241,7 +242,7 @@ impl<'tcx> InferCtxt<'tcx> { { let (a, b) = if relation.a_is_expected() { (a, b) } else { (b, a) }; - relation.register_predicates([ty::Binder::dummy(if self.next_trait_solver() { + relation.register_predicates([if self.next_trait_solver() { ty::PredicateKind::AliasRelate( a.into(), b.into(), @@ -249,77 +250,12 @@ impl<'tcx> InferCtxt<'tcx> { ) } else { ty::PredicateKind::ConstEquate(a, b) - })]); + }]); - return Ok(b); + Ok(b) } - _ => {} + _ => ty::relate::structurally_relate_consts(relation, a, b), } - - ty::relate::structurally_relate_consts(relation, a, b) - } - - /// Unifies the const variable `target_vid` with the given constant. - /// - /// This also tests if the given const `ct` contains an inference variable which was previously - /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct` - /// would result in an infinite type as we continuously replace an inference variable - /// in `ct` with `ct` itself. - /// - /// This is especially important as unevaluated consts use their parents generics. - /// They therefore often contain unused args, making these errors far more likely. - /// - /// A good example of this is the following: - /// - /// ```compile_fail,E0308 - /// #![feature(generic_const_exprs)] - /// - /// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] { - /// todo!() - /// } - /// - /// fn main() { - /// let mut arr = Default::default(); - /// arr = bind(arr); - /// } - /// ``` - /// - /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics - /// of `fn bind` (meaning that its args contain `N`). - /// - /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`. - /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`. - /// - /// As `3 + 4` contains `N` in its args, this must not succeed. - /// - /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant. - #[instrument(level = "debug", skip(self))] - fn unify_const_variable( - &self, - target_vid: ty::ConstVid, - ct: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - let span = match self.inner.borrow_mut().const_unification_table().probe_value(target_vid) { - ConstVariableValue::Known { value } => { - bug!("instantiating a known const var: {target_vid:?} {value} {ct}") - } - ConstVariableValue::Unknown { origin, universe: _ } => origin.span, - }; - // FIXME(generic_const_exprs): Occurs check failures for unevaluated - // constants and generic expressions are not yet handled correctly. - let Generalization { value_may_be_infer: value, needs_wf: _ } = generalize::generalize( - self, - &mut CombineDelegate { infcx: self, span }, - ct, - target_vid, - ty::Variance::Invariant, - )?; - - self.inner - .borrow_mut() - .const_unification_table() - .union_value(target_vid, ConstVariableValue::Known { value }); - Ok(value) } fn unify_integral_variable( @@ -383,131 +319,6 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { Glb::new(self, a_is_expected) } - /// Here, `dir` is either `EqTo`, `SubtypeOf`, or `SupertypeOf`. - /// The idea is that we should ensure that the type `a_ty` is equal - /// to, a subtype of, or a supertype of (respectively) the type - /// to which `b_vid` is bound. - /// - /// Since `b_vid` has not yet been instantiated with a type, we - /// will first instantiate `b_vid` with a *generalized* version - /// of `a_ty`. Generalization introduces other inference - /// variables wherever subtyping could occur. - #[instrument(skip(self), level = "debug")] - pub fn instantiate( - &mut self, - a_ty: Ty<'tcx>, - ambient_variance: ty::Variance, - b_vid: ty::TyVid, - a_is_expected: bool, - ) -> RelateResult<'tcx, ()> { - // Get the actual variable that b_vid has been inferred to - debug_assert!(self.infcx.inner.borrow_mut().type_variables().probe(b_vid).is_unknown()); - - // Generalize type of `a_ty` appropriately depending on the - // direction. As an example, assume: - // - // - `a_ty == &'x ?1`, where `'x` is some free region and `?1` is an - // inference variable, - // - and `dir` == `SubtypeOf`. - // - // Then the generalized form `b_ty` would be `&'?2 ?3`, where - // `'?2` and `?3` are fresh region/type inference - // variables. (Down below, we will relate `a_ty <: b_ty`, - // adding constraints like `'x: '?2` and `?1 <: ?3`.) - let Generalization { value_may_be_infer: b_ty, needs_wf } = generalize::generalize( - self.infcx, - &mut CombineDelegate { infcx: self.infcx, span: self.trace.span() }, - a_ty, - b_vid, - ambient_variance, - )?; - - // Constrain `b_vid` to the generalized type `b_ty`. - if let &ty::Infer(TyVar(b_ty_vid)) = b_ty.kind() { - self.infcx.inner.borrow_mut().type_variables().equate(b_vid, b_ty_vid); - } else { - self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty); - } - - if needs_wf { - self.obligations.push(Obligation::new( - self.tcx(), - self.trace.cause.clone(), - self.param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( - b_ty.into(), - ))), - )); - } - - // Finally, relate `b_ty` to `a_ty`, as described in previous comment. - // - // FIXME(#16847): This code is non-ideal because all these subtype - // relations wind up attributed to the same spans. We need - // to associate causes/spans with each of the relations in - // the stack to get this right. - if b_ty.is_ty_var() { - // This happens for cases like `<?0 as Trait>::Assoc == ?0`. - // We can't instantiate `?0` here as that would result in a - // cyclic type. We instead delay the unification in case - // the alias can be normalized to something which does not - // mention `?0`. - if self.infcx.next_trait_solver() { - let (lhs, rhs, direction) = match ambient_variance { - ty::Variance::Invariant => { - (a_ty.into(), b_ty.into(), AliasRelationDirection::Equate) - } - ty::Variance::Covariant => { - (a_ty.into(), b_ty.into(), AliasRelationDirection::Subtype) - } - ty::Variance::Contravariant => { - (b_ty.into(), a_ty.into(), AliasRelationDirection::Subtype) - } - ty::Variance::Bivariant => unreachable!("bivariant generalization"), - }; - self.obligations.push(Obligation::new( - self.tcx(), - self.trace.cause.clone(), - self.param_env, - ty::PredicateKind::AliasRelate(lhs, rhs, direction), - )); - } else { - match a_ty.kind() { - &ty::Alias(ty::Projection, data) => { - // FIXME: This does not handle subtyping correctly, we could - // instead create a new inference variable for `a_ty`, emitting - // `Projection(a_ty, a_infer)` and `a_infer <: b_ty`. - self.obligations.push(Obligation::new( - self.tcx(), - self.trace.cause.clone(), - self.param_env, - ty::ProjectionPredicate { projection_ty: data, term: b_ty.into() }, - )) - } - // The old solver only accepts projection predicates for associated types. - ty::Alias(ty::Inherent | ty::Weak | ty::Opaque, _) => { - return Err(TypeError::CyclicTy(a_ty)); - } - _ => bug!("generalizated `{a_ty:?} to infer, not an alias"), - } - } - } else { - match ambient_variance { - ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty), - ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty), - ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance( - ty::Contravariant, - ty::VarianceDiagInfo::default(), - a_ty, - b_ty, - ), - ty::Variance::Bivariant => unreachable!("bivariant generalization"), - }?; - } - - Ok(()) - } - pub fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { self.obligations.extend(obligations); } @@ -520,6 +331,8 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { } pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> { + fn span(&self) -> Span; + fn param_env(&self) -> ty::ParamEnv<'tcx>; /// Register obligations that must hold in order for this relation to hold diff --git a/compiler/rustc_infer/src/infer/relate/equate.rs b/compiler/rustc_infer/src/infer/relate/equate.rs index cb62f258373..aefa9a5a0d6 100644 --- a/compiler/rustc_infer/src/infer/relate/equate.rs +++ b/compiler/rustc_infer/src/infer/relate/equate.rs @@ -8,6 +8,7 @@ use rustc_middle::ty::TyVar; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_hir::def_id::DefId; +use rustc_span::Span; /// Ensures `a` is made equal to `b`. Returns `a` on success. pub struct Equate<'combine, 'infcx, 'tcx> { @@ -81,12 +82,17 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { infcx.inner.borrow_mut().type_variables().equate(a_id, b_id); } - (&ty::Infer(TyVar(a_id)), _) => { - self.fields.instantiate(b, ty::Invariant, a_id, self.a_is_expected)?; + (&ty::Infer(TyVar(a_vid)), _) => { + infcx.instantiate_ty_var(self, self.a_is_expected, a_vid, ty::Invariant, b)?; } - (_, &ty::Infer(TyVar(b_id))) => { - self.fields.instantiate(a, ty::Invariant, b_id, self.a_is_expected)?; + (_, &ty::Infer(TyVar(b_vid))) => { + infcx.instantiate_ty_var(self, !self.a_is_expected, b_vid, ty::Invariant, a)?; + } + + (&ty::Error(e), _) | (_, &ty::Error(e)) => { + infcx.set_tainted_by_errors(e); + return Ok(Ty::new_error(self.tcx(), e)); } ( @@ -170,6 +176,10 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { } impl<'tcx> ObligationEmittingRelation<'tcx> for Equate<'_, '_, 'tcx> { + fn span(&self) -> Span { + self.fields.trace.span() + } + fn param_env(&self) -> ty::ParamEnv<'tcx> { self.fields.param_env } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index bc16d613ccb..90f10a0eba9 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -1,5 +1,7 @@ use std::mem; +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind, TypeVariableValue}; +use crate::infer::{InferCtxt, ObligationEmittingRelation, RegionVariableOrigin}; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::DefId; @@ -7,98 +9,241 @@ use rustc_middle::infer::unify_key::ConstVariableValue; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::visit::MaxUniverse; -use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{AliasRelationDirection, InferConst, Term, TypeVisitable, TypeVisitableExt}; use rustc_span::Span; -use crate::infer::nll_relate::TypeRelatingDelegate; -use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind, TypeVariableValue}; -use crate::infer::{InferCtxt, RegionVariableOrigin}; - -/// Attempts to generalize `term` for the type variable `for_vid`. -/// This checks for cycles -- that is, whether the type `term` -/// references `for_vid`. -pub fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into<Term<'tcx>> + Relate<'tcx>>( - infcx: &InferCtxt<'tcx>, - delegate: &mut D, - term: T, - for_vid: impl Into<ty::TermVid>, - ambient_variance: ty::Variance, -) -> RelateResult<'tcx, Generalization<T>> { - let (for_universe, root_vid) = match for_vid.into() { - ty::TermVid::Ty(ty_vid) => ( - infcx.probe_ty_var(ty_vid).unwrap_err(), - ty::TermVid::Ty(infcx.inner.borrow_mut().type_variables().sub_root_var(ty_vid)), - ), - ty::TermVid::Const(ct_vid) => ( - infcx.probe_const_var(ct_vid).unwrap_err(), - ty::TermVid::Const(infcx.inner.borrow_mut().const_unification_table().find(ct_vid).vid), - ), - }; - - let mut generalizer = Generalizer { - infcx, - delegate, - ambient_variance, - root_vid, - for_universe, - root_term: term.into(), - in_alias: false, - needs_wf: false, - cache: Default::default(), - }; - - assert!(!term.has_escaping_bound_vars()); - let value_may_be_infer = generalizer.relate(term, term)?; - let needs_wf = generalizer.needs_wf; - Ok(Generalization { value_may_be_infer, needs_wf }) -} - -/// Abstracts the handling of region vars between HIR and MIR/NLL typechecking -/// in the generalizer code. -pub trait GeneralizerDelegate<'tcx> { - fn forbid_inference_vars() -> bool; - - fn span(&self) -> Span; +impl<'tcx> InferCtxt<'tcx> { + /// The idea is that we should ensure that the type variable `target_vid` + /// is equal to, a subtype of, or a supertype of `source_ty`. + /// + /// For this, we will instantiate `target_vid` with a *generalized* version + /// of `source_ty`. Generalization introduces other inference variables wherever + /// subtyping could occur. This also does the occurs checks, detecting whether + /// instantiating `target_vid` would result in a cyclic type. We eagerly error + /// in this case. + /// + /// This is *not* expected to be used anywhere except for an implementation of + /// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all + /// other usecases (i.e. setting the value of a type var). + #[instrument(level = "debug", skip(self, relation, target_is_expected))] + pub fn instantiate_ty_var<R: ObligationEmittingRelation<'tcx>>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: ty::TyVid, + ambient_variance: ty::Variance, + source_ty: Ty<'tcx>, + ) -> RelateResult<'tcx, ()> { + debug_assert!(self.inner.borrow_mut().type_variables().probe(target_vid).is_unknown()); + + // Generalize `source_ty` depending on the current variance. As an example, assume + // `?target <: &'x ?1`, where `'x` is some free region and `?1` is an inference + // variable. + // + // Then the `generalized_ty` would be `&'?2 ?3`, where `'?2` and `?3` are fresh + // region/type inference variables. + // + // We then relate `generalized_ty <: source_ty`,adding constraints like `'x: '?2` and `?1 <: ?3`. + let Generalization { value_may_be_infer: generalized_ty, has_unconstrained_ty_var } = + self.generalize(relation.span(), target_vid, ambient_variance, source_ty)?; + + // Constrain `b_vid` to the generalized type `generalized_ty`. + if let &ty::Infer(ty::TyVar(generalized_vid)) = generalized_ty.kind() { + self.inner.borrow_mut().type_variables().equate(target_vid, generalized_vid); + } else { + self.inner.borrow_mut().type_variables().instantiate(target_vid, generalized_ty); + } - fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; -} + // See the comment on `Generalization::has_unconstrained_ty_var`. + if has_unconstrained_ty_var { + relation.register_predicates([ty::ClauseKind::WellFormed(generalized_ty.into())]); + } -pub struct CombineDelegate<'cx, 'tcx> { - pub infcx: &'cx InferCtxt<'tcx>, - pub span: Span, -} + // Finally, relate `generalized_ty` to `source_ty`, as described in previous comment. + // + // FIXME(#16847): This code is non-ideal because all these subtype + // relations wind up attributed to the same spans. We need + // to associate causes/spans with each of the relations in + // the stack to get this right. + if generalized_ty.is_ty_var() { + // This happens for cases like `<?0 as Trait>::Assoc == ?0`. + // We can't instantiate `?0` here as that would result in a + // cyclic type. We instead delay the unification in case + // the alias can be normalized to something which does not + // mention `?0`. + if self.next_trait_solver() { + let (lhs, rhs, direction) = match ambient_variance { + ty::Variance::Invariant => { + (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Equate) + } + ty::Variance::Covariant => { + (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Subtype) + } + ty::Variance::Contravariant => { + (source_ty.into(), generalized_ty.into(), AliasRelationDirection::Subtype) + } + ty::Variance::Bivariant => unreachable!("bivariant generalization"), + }; -impl<'tcx> GeneralizerDelegate<'tcx> for CombineDelegate<'_, 'tcx> { - fn forbid_inference_vars() -> bool { - false - } + relation.register_predicates([ty::PredicateKind::AliasRelate(lhs, rhs, direction)]); + } else { + match source_ty.kind() { + &ty::Alias(ty::Projection, data) => { + // FIXME: This does not handle subtyping correctly, we could + // instead create a new inference variable `?normalized_source`, emitting + // `Projection(normalized_source, ?ty_normalized)` and `?normalized_source <: generalized_ty`. + relation.register_predicates([ty::ProjectionPredicate { + projection_ty: data, + term: generalized_ty.into(), + }]); + } + // The old solver only accepts projection predicates for associated types. + ty::Alias(ty::Inherent | ty::Weak | ty::Opaque, _) => { + return Err(TypeError::CyclicTy(source_ty)); + } + _ => bug!("generalized `{source_ty:?} to infer, not an alias"), + } + } + } else { + // HACK: make sure that we `a_is_expected` continues to be + // correct when relating the generalized type with the source. + if target_is_expected == relation.a_is_expected() { + relation.relate_with_variance( + ambient_variance, + ty::VarianceDiagInfo::default(), + generalized_ty, + source_ty, + )?; + } else { + relation.relate_with_variance( + ambient_variance.xform(ty::Contravariant), + ty::VarianceDiagInfo::default(), + source_ty, + generalized_ty, + )?; + } + } - fn span(&self) -> Span { - self.span + Ok(()) } - fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { - // FIXME: This is non-ideal because we don't give a - // very descriptive origin for this region variable. - self.infcx - .next_region_var_in_universe(RegionVariableOrigin::MiscVariable(self.span), universe) - } -} + /// Instantiates the const variable `target_vid` with the given constant. + /// + /// This also tests if the given const `ct` contains an inference variable which was previously + /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct` + /// would result in an infinite type as we continuously replace an inference variable + /// in `ct` with `ct` itself. + /// + /// This is especially important as unevaluated consts use their parents generics. + /// They therefore often contain unused args, making these errors far more likely. + /// + /// A good example of this is the following: + /// + /// ```compile_fail,E0308 + /// #![feature(generic_const_exprs)] + /// + /// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] { + /// todo!() + /// } + /// + /// fn main() { + /// let mut arr = Default::default(); + /// arr = bind(arr); + /// } + /// ``` + /// + /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics + /// of `fn bind` (meaning that its args contain `N`). + /// + /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`. + /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`. + /// + /// As `3 + 4` contains `N` in its args, this must not succeed. + /// + /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant. + #[instrument(level = "debug", skip(self, relation))] + pub(super) fn instantiate_const_var<R: ObligationEmittingRelation<'tcx>>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: ty::ConstVid, + source_ct: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ()> { + // FIXME(generic_const_exprs): Occurs check failures for unevaluated + // constants and generic expressions are not yet handled correctly. + let Generalization { value_may_be_infer: generalized_ct, has_unconstrained_ty_var } = + self.generalize(relation.span(), target_vid, ty::Variance::Invariant, source_ct)?; + + debug_assert!(!generalized_ct.is_ct_infer()); + if has_unconstrained_ty_var { + bug!("unconstrained ty var when generalizing `{source_ct:?}`"); + } -impl<'tcx, T> GeneralizerDelegate<'tcx> for T -where - T: TypeRelatingDelegate<'tcx>, -{ - fn forbid_inference_vars() -> bool { - <Self as TypeRelatingDelegate<'tcx>>::forbid_inference_vars() - } + self.inner + .borrow_mut() + .const_unification_table() + .union_value(target_vid, ConstVariableValue::Known { value: generalized_ct }); + + // HACK: make sure that we `a_is_expected` continues to be + // correct when relating the generalized type with the source. + if target_is_expected == relation.a_is_expected() { + relation.relate_with_variance( + ty::Variance::Invariant, + ty::VarianceDiagInfo::default(), + generalized_ct, + source_ct, + )?; + } else { + relation.relate_with_variance( + ty::Variance::Invariant, + ty::VarianceDiagInfo::default(), + source_ct, + generalized_ct, + )?; + } - fn span(&self) -> Span { - <Self as TypeRelatingDelegate<'tcx>>::span(&self) + Ok(()) } - fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { - <Self as TypeRelatingDelegate<'tcx>>::generalize_existential(self, universe) + /// Attempts to generalize `source_term` for the type variable `target_vid`. + /// This checks for cycles -- that is, whether `source_term` references `target_vid`. + fn generalize<T: Into<Term<'tcx>> + Relate<'tcx>>( + &self, + span: Span, + target_vid: impl Into<ty::TermVid>, + ambient_variance: ty::Variance, + source_term: T, + ) -> RelateResult<'tcx, Generalization<T>> { + assert!(!source_term.has_escaping_bound_vars()); + let (for_universe, root_vid) = match target_vid.into() { + ty::TermVid::Ty(ty_vid) => ( + self.probe_ty_var(ty_vid).unwrap_err(), + ty::TermVid::Ty(self.inner.borrow_mut().type_variables().sub_root_var(ty_vid)), + ), + ty::TermVid::Const(ct_vid) => ( + self.probe_const_var(ct_vid).unwrap_err(), + ty::TermVid::Const( + self.inner.borrow_mut().const_unification_table().find(ct_vid).vid, + ), + ), + }; + + let mut generalizer = Generalizer { + infcx: self, + span, + root_vid, + for_universe, + ambient_variance, + root_term: source_term.into(), + in_alias: false, + has_unconstrained_ty_var: false, + cache: Default::default(), + }; + + let value_may_be_infer = generalizer.relate(source_term, source_term)?; + let has_unconstrained_ty_var = generalizer.has_unconstrained_ty_var; + Ok(Generalization { value_may_be_infer, has_unconstrained_ty_var }) } } @@ -115,18 +260,10 @@ where /// establishes `'0: 'x` as a constraint. /// /// [blog post]: https://is.gd/0hKvIr -struct Generalizer<'me, 'tcx, D> { +struct Generalizer<'me, 'tcx> { infcx: &'me InferCtxt<'tcx>, - /// This is used to abstract the behaviors of the three previous - /// generalizer-like implementations (`Generalizer`, `TypeGeneralizer`, - /// and `ConstInferUnifier`). See [`GeneralizerDelegate`] for more - /// information. - delegate: &'me mut D, - - /// After we generalize this type, we are going to relate it to - /// some other type. What will be the variance at this point? - ambient_variance: ty::Variance, + span: Span, /// The vid of the type variable that is in the process of being /// instantiated. If we find this within the value we are folding, @@ -138,6 +275,10 @@ struct Generalizer<'me, 'tcx, D> { /// we reject the relation. for_universe: ty::UniverseIndex, + /// After we generalize this type, we are going to relate it to + /// some other type. What will be the variance at this point? + ambient_variance: ty::Variance, + /// The root term (const or type) we're generalizing. Used for cycle errors. root_term: Term<'tcx>, @@ -150,11 +291,11 @@ struct Generalizer<'me, 'tcx, D> { /// hold by either normalizing the outer or the inner associated type. in_alias: bool, - /// See the field `needs_wf` in `Generalization`. - needs_wf: bool, + /// See the field `has_unconstrained_ty_var` in `Generalization`. + has_unconstrained_ty_var: bool, } -impl<'tcx, D> Generalizer<'_, 'tcx, D> { +impl<'tcx> Generalizer<'_, 'tcx> { /// Create an error that corresponds to the term kind in `root_term` fn cyclic_term_error(&self) -> TypeError<'tcx> { match self.root_term.unpack() { @@ -162,12 +303,52 @@ impl<'tcx, D> Generalizer<'_, 'tcx, D> { ty::TermKind::Const(ct) => TypeError::CyclicConst(ct), } } + + /// An occurs check failure inside of an alias does not mean + /// that the types definitely don't unify. We may be able + /// to normalize the alias after all. + /// + /// We handle this by lazily equating the alias and generalizing + /// it to an inference variable. + /// + /// This is incomplete and will hopefully soon get fixed by #119106. + fn generalize_alias_ty( + &mut self, + alias: ty::AliasTy<'tcx>, + ) -> Result<Ty<'tcx>, TypeError<'tcx>> { + let is_nested_alias = mem::replace(&mut self.in_alias, true); + let result = match self.relate(alias, alias) { + Ok(alias) => Ok(alias.to_ty(self.tcx())), + Err(e) => { + if is_nested_alias { + return Err(e); + } else { + let mut visitor = MaxUniverse::new(); + alias.visit_with(&mut visitor); + let infer_replacement_is_complete = + self.for_universe.can_name(visitor.max_universe()) + && !alias.has_escaping_bound_vars(); + if !infer_replacement_is_complete { + warn!("may incompletely handle alias type: {alias:?}"); + } + + debug!("generalization failure in alias"); + Ok(self.infcx.next_ty_var_in_universe( + TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: self.span, + }, + self.for_universe, + )) + } + } + }; + self.in_alias = is_nested_alias; + result + } } -impl<'tcx, D> TypeRelation<'tcx> for Generalizer<'_, 'tcx, D> -where - D: GeneralizerDelegate<'tcx>, -{ +impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } @@ -236,12 +417,6 @@ where // subtyping. This is basically our "occurs check", preventing // us from creating infinitely sized types. let g = match *t.kind() { - ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) - if D::forbid_inference_vars() => - { - bug!("unexpected inference variable encountered in NLL generalization: {t}"); - } - ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("unexpected infer type: {t}") } @@ -272,11 +447,10 @@ where } } - // Bivariant: make a fresh var, but we - // may need a WF predicate. See - // comment on `needs_wf` field for - // more info. - ty::Bivariant => self.needs_wf = true, + // Bivariant: make a fresh var, but remember that + // it is unconstrained. See the comment in + // `Generalization`. + ty::Bivariant => self.has_unconstrained_ty_var = true, // Co/contravariant: this will be // sufficiently constrained later on. @@ -318,43 +492,7 @@ where } } - ty::Alias(kind, data) => { - // An occurs check failure inside of an alias does not mean - // that the types definitely don't unify. We may be able - // to normalize the alias after all. - // - // We handle this by lazily equating the alias and generalizing - // it to an inference variable. - let is_nested_alias = mem::replace(&mut self.in_alias, true); - let result = match self.relate(data, data) { - Ok(data) => Ok(Ty::new_alias(self.tcx(), kind, data)), - Err(e) => { - if is_nested_alias { - return Err(e); - } else { - let mut visitor = MaxUniverse::new(); - t.visit_with(&mut visitor); - let infer_replacement_is_complete = - self.for_universe.can_name(visitor.max_universe()) - && !t.has_escaping_bound_vars(); - if !infer_replacement_is_complete { - warn!("may incompletely handle alias type: {t:?}"); - } - - debug!("generalization failure in alias"); - Ok(self.infcx.next_ty_var_in_universe( - TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.delegate.span(), - }, - self.for_universe, - )) - } - } - }; - self.in_alias = is_nested_alias; - result - } + ty::Alias(_, data) => self.generalize_alias_ty(data), _ => relate::structurally_relate_tys(self, t, t), }?; @@ -403,7 +541,10 @@ where } } - Ok(self.delegate.generalize_region(self.for_universe)) + Ok(self.infcx.next_region_var_in_universe( + RegionVariableOrigin::MiscVariable(self.span), + self.for_universe, + )) } #[instrument(level = "debug", skip(self, c2), ret)] @@ -415,9 +556,6 @@ where assert_eq!(c, c2); // we are misusing TypeRelation here; both LHS and RHS ought to be == match c.kind() { - ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { - bug!("unexpected inference variable encountered in NLL generalization: {:?}", c); - } ty::ConstKind::Infer(InferConst::Var(vid)) => { // If root const vids are equal, then `root_vid` and // `vid` are related and we'd be inferring an infinitely @@ -452,6 +590,9 @@ where } } ty::ConstKind::Infer(InferConst::EffectVar(_)) => Ok(c), + // FIXME: Unevaluated constants are also not rigid, so the current + // approach of always relating them structurally is incomplete. + // // FIXME: remove this branch once `structurally_relate_consts` is fully // structural. ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => { @@ -511,30 +652,27 @@ pub struct Generalization<T> { /// recursion. pub value_may_be_infer: T, - /// If true, then the generalized type may not be well-formed, - /// even if the source type is well-formed, so we should add an - /// additional check to enforce that it is. This arises in - /// particular around 'bivariant' type parameters that are only - /// constrained by a where-clause. As an example, imagine a type: + /// In general, we do not check whether all types which occur during + /// type checking are well-formed. We only check wf of user-provided types + /// and when actually using a type, e.g. for method calls. + /// + /// This means that when subtyping, we may end up with unconstrained + /// inference variables if a generalized type has bivariant parameters. + /// A parameter may only be bivariant if it is constrained by a projection + /// bound in a where-clause. As an example, imagine a type: /// /// struct Foo<A, B> where A: Iterator<Item = B> { /// data: A /// } /// - /// here, `A` will be covariant, but `B` is - /// unconstrained. However, whatever it is, for `Foo` to be WF, it - /// must be equal to `A::Item`. If we have an input `Foo<?A, ?B>`, - /// then after generalization we will wind up with a type like - /// `Foo<?C, ?D>`. When we enforce that `Foo<?A, ?B> <: Foo<?C, - /// ?D>` (or `>:`), we will wind up with the requirement that `?A - /// <: ?C`, but no particular relationship between `?B` and `?D` - /// (after all, we do not know the variance of the normalized form - /// of `A::Item` with respect to `A`). If we do nothing else, this - /// may mean that `?D` goes unconstrained (as in #41677). So, in - /// this scenario where we create a new type variable in a - /// bivariant context, we set the `needs_wf` flag to true. This - /// will force the calling code to check that `WF(Foo<?C, ?D>)` - /// holds, which in turn implies that `?C::Item == ?D`. So once - /// `?C` is constrained, that should suffice to restrict `?D`. - pub needs_wf: bool, + /// here, `A` will be covariant, but `B` is unconstrained. + /// + /// However, whatever it is, for `Foo` to be WF, it must be equal to `A::Item`. + /// If we have an input `Foo<?A, ?B>`, then after generalization we will wind + /// up with a type like `Foo<?C, ?D>`. When we enforce `Foo<?A, ?B> <: Foo<?C, ?D>`, + /// we will wind up with the requirement that `?A <: ?C`, but no particular + /// relationship between `?B` and `?D` (after all, these types may be completely + /// different). If we do nothing else, this may mean that `?D` goes unconstrained + /// (as in #41677). To avoid this we emit a `WellFormed` obligation in these cases. + pub has_unconstrained_ty_var: bool, } diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs index aa89124301e..6cf51354599 100644 --- a/compiler/rustc_infer/src/infer/relate/glb.rs +++ b/compiler/rustc_infer/src/infer/relate/glb.rs @@ -2,6 +2,7 @@ use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::Span; use super::combine::{CombineFields, ObligationEmittingRelation}; use super::lattice::{self, LatticeDir}; @@ -134,6 +135,10 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, } impl<'tcx> ObligationEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> { + fn span(&self) -> Span { + self.fields.trace.span() + } + fn param_env(&self) -> ty::ParamEnv<'tcx> { self.fields.param_env } diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs index 87d777530c8..5b4f80fd73a 100644 --- a/compiler/rustc_infer/src/infer/relate/lub.rs +++ b/compiler/rustc_infer/src/infer/relate/lub.rs @@ -7,6 +7,7 @@ use crate::traits::{ObligationCause, PredicateObligations}; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::Span; /// "Least upper bound" (common supertype) pub struct Lub<'combine, 'infcx, 'tcx> { @@ -134,6 +135,10 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, } impl<'tcx> ObligationEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> { + fn span(&self) -> Span { + self.fields.trace.span() + } + fn param_env(&self) -> ty::ParamEnv<'tcx> { self.fields.param_env } diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs index f688c2b74a6..1207377e857 100644 --- a/compiler/rustc_infer/src/infer/relate/mod.rs +++ b/compiler/rustc_infer/src/infer/relate/mod.rs @@ -3,10 +3,9 @@ pub(super) mod combine; mod equate; -pub(super) mod generalize; +mod generalize; mod glb; mod higher_ranked; mod lattice; mod lub; -pub mod nll; mod sub; diff --git a/compiler/rustc_infer/src/infer/relate/nll.rs b/compiler/rustc_infer/src/infer/relate/nll.rs deleted file mode 100644 index 5e2d2af9b85..00000000000 --- a/compiler/rustc_infer/src/infer/relate/nll.rs +++ /dev/null @@ -1,720 +0,0 @@ -//! This code is kind of an alternate way of doing subtyping, -//! supertyping, and type equating, distinct from the `combine.rs` -//! code but very similar in its effect and design. Eventually the two -//! ought to be merged. This code is intended for use in NLL and chalk. -//! -//! Here are the key differences: -//! -//! - This code may choose to bypass some checks (e.g., the occurs check) -//! in the case where we know that there are no unbound type inference -//! variables. This is the case for NLL, because at NLL time types are fully -//! inferred up-to regions. -//! - This code uses "universes" to handle higher-ranked regions and -//! not the leak-check. This is "more correct" than what rustc does -//! and we are generally migrating in this direction, but NLL had to -//! get there first. -//! -//! Also, this code assumes that there are no bound types at all, not even -//! free ones. This is ok because: -//! - we are not relating anything quantified over some type variable -//! - we will have instantiated all the bound type vars already (the one -//! thing we relate in chalk are basically domain goals and their -//! constituents) - -use rustc_data_structures::fx::FxHashMap; -use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::fold::FnMutDelegate; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; -use rustc_span::{Span, Symbol}; -use std::fmt::Debug; - -use super::combine::ObligationEmittingRelation; -use super::generalize::{self, Generalization}; -use crate::infer::InferCtxt; -use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::traits::{Obligation, PredicateObligations}; - -pub struct TypeRelating<'me, 'tcx, D> -where - D: TypeRelatingDelegate<'tcx>, -{ - infcx: &'me InferCtxt<'tcx>, - - /// Callback to use when we deduce an outlives relationship. - delegate: D, - - /// How are we relating `a` and `b`? - /// - /// - Covariant means `a <: b`. - /// - Contravariant means `b <: a`. - /// - Invariant means `a == b`. - /// - Bivariant means that it doesn't matter. - ambient_variance: ty::Variance, - - ambient_variance_info: ty::VarianceDiagInfo<'tcx>, -} - -pub trait TypeRelatingDelegate<'tcx> { - fn param_env(&self) -> ty::ParamEnv<'tcx>; - fn span(&self) -> Span; - - /// Push a constraint `sup: sub` -- this constraint must be - /// satisfied for the two types to be related. `sub` and `sup` may - /// be regions from the type or new variables created through the - /// delegate. - fn push_outlives( - &mut self, - sup: ty::Region<'tcx>, - sub: ty::Region<'tcx>, - info: ty::VarianceDiagInfo<'tcx>, - ); - - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>); - - /// Creates a new universe index. Used when instantiating placeholders. - fn create_next_universe(&mut self) -> ty::UniverseIndex; - - /// Creates a new region variable representing a higher-ranked - /// region that is instantiated existentially. This creates an - /// inference variable, typically. - /// - /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then - /// we will invoke this method to instantiate `'a` with an - /// inference variable (though `'b` would be instantiated first, - /// as a placeholder). - fn next_existential_region_var( - &mut self, - was_placeholder: bool, - name: Option<Symbol>, - ) -> ty::Region<'tcx>; - - /// Creates a new region variable representing a - /// higher-ranked region that is instantiated universally. - /// This creates a new region placeholder, typically. - /// - /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then - /// we will invoke this method to instantiate `'b` with a - /// placeholder region. - fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx>; - - /// Creates a new existential region in the given universe. This - /// is used when handling subtyping and type variables -- if we - /// have that `?X <: Foo<'a>`, for example, we would instantiate - /// `?X` with a type like `Foo<'?0>` where `'?0` is a fresh - /// existential variable created by this function. We would then - /// relate `Foo<'?0>` with `Foo<'a>` (and probably add an outlives - /// relation stating that `'?0: 'a`). - fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; - - /// Enables some optimizations if we do not expect inference variables - /// in the RHS of the relation. - fn forbid_inference_vars() -> bool; -} - -impl<'me, 'tcx, D> TypeRelating<'me, 'tcx, D> -where - D: TypeRelatingDelegate<'tcx>, -{ - pub fn new(infcx: &'me InferCtxt<'tcx>, delegate: D, ambient_variance: ty::Variance) -> Self { - Self { - infcx, - delegate, - ambient_variance, - ambient_variance_info: ty::VarianceDiagInfo::default(), - } - } - - fn ambient_covariance(&self) -> bool { - match self.ambient_variance { - ty::Variance::Covariant | ty::Variance::Invariant => true, - ty::Variance::Contravariant | ty::Variance::Bivariant => false, - } - } - - fn ambient_contravariance(&self) -> bool { - match self.ambient_variance { - ty::Variance::Contravariant | ty::Variance::Invariant => true, - ty::Variance::Covariant | ty::Variance::Bivariant => false, - } - } - - /// Push a new outlives requirement into our output set of - /// constraints. - fn push_outlives( - &mut self, - sup: ty::Region<'tcx>, - sub: ty::Region<'tcx>, - info: ty::VarianceDiagInfo<'tcx>, - ) { - debug!("push_outlives({:?}: {:?})", sup, sub); - - self.delegate.push_outlives(sup, sub, info); - } - - /// Relate a type inference variable with a value type. This works - /// by creating a "generalization" G of the value where all the - /// lifetimes are replaced with fresh inference values. This - /// generalization G becomes the value of the inference variable, - /// and is then related in turn to the value. So e.g. if you had - /// `vid = ?0` and `value = &'a u32`, we might first instantiate - /// `?0` to a type like `&'0 u32` where `'0` is a fresh variable, - /// and then relate `&'0 u32` with `&'a u32` (resulting in - /// relations between `'0` and `'a`). - /// - /// The variable `pair` can be either a `(vid, ty)` or `(ty, vid)` - /// -- in other words, it is always an (unresolved) inference - /// variable `vid` and a type `ty` that are being related, but the - /// vid may appear either as the "a" type or the "b" type, - /// depending on where it appears in the tuple. The trait - /// `VidValuePair` lets us work with the vid/type while preserving - /// the "sidedness" when necessary -- the sidedness is relevant in - /// particular for the variance and set of in-scope things. - fn relate_ty_var<PAIR: VidValuePair<'tcx>>( - &mut self, - pair: PAIR, - ) -> RelateResult<'tcx, Ty<'tcx>> { - debug!("relate_ty_var({:?})", pair); - - let vid = pair.vid(); - let value_ty = pair.value_ty(); - - // FIXME(invariance) -- this logic assumes invariance, but that is wrong. - // This only presently applies to chalk integration, as NLL - // doesn't permit type variables to appear on both sides (and - // doesn't use lazy norm). - match *value_ty.kind() { - ty::Infer(ty::TyVar(value_vid)) => { - // Two type variables: just equate them. - self.infcx.inner.borrow_mut().type_variables().equate(vid, value_vid); - return Ok(value_ty); - } - - _ => (), - } - - let generalized_ty = self.generalize(value_ty, vid)?; - debug!("relate_ty_var: generalized_ty = {:?}", generalized_ty); - - if D::forbid_inference_vars() { - // In NLL, we don't have type inference variables - // floating around, so we can do this rather imprecise - // variant of the occurs-check. - assert!(!generalized_ty.has_non_region_infer()); - } - - self.infcx.inner.borrow_mut().type_variables().instantiate(vid, generalized_ty); - - // Relate the generalized kind to the original one. - let result = pair.relate_generalized_ty(self, generalized_ty); - - debug!("relate_ty_var: complete, result = {:?}", result); - result - } - - fn generalize(&mut self, ty: Ty<'tcx>, for_vid: ty::TyVid) -> RelateResult<'tcx, Ty<'tcx>> { - let Generalization { value_may_be_infer: ty, needs_wf: _ } = generalize::generalize( - self.infcx, - &mut self.delegate, - ty, - for_vid, - self.ambient_variance, - )?; - - if ty.is_ty_var() { - span_bug!(self.delegate.span(), "occurs check failure in MIR typeck"); - } - Ok(ty) - } - - fn relate_opaques(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; - let mut generalize = |ty, ty_is_expected| { - let var = self.infcx.next_ty_var_id_in_universe( - TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.delegate.span(), - }, - ty::UniverseIndex::ROOT, - ); - if ty_is_expected { - self.relate_ty_var((ty, var)) - } else { - self.relate_ty_var((var, ty)) - } - }; - let (a, b) = match (a.kind(), b.kind()) { - (&ty::Alias(ty::Opaque, ..), _) => (a, generalize(b, false)?), - (_, &ty::Alias(ty::Opaque, ..)) => (generalize(a, true)?, b), - _ => unreachable!( - "expected at least one opaque type in `relate_opaques`, got {a} and {b}." - ), - }; - let cause = ObligationCause::dummy_with_span(self.delegate.span()); - let obligations = self - .infcx - .handle_opaque_type(a, b, true, &cause, self.delegate.param_env())? - .obligations; - self.delegate.register_obligations(obligations); - trace!(a = ?a.kind(), b = ?b.kind(), "opaque type instantiated"); - Ok(a) - } - - fn enter_forall<T, U>( - &mut self, - binder: ty::Binder<'tcx, T>, - f: impl FnOnce(&mut Self, T) -> U, - ) -> U - where - T: ty::TypeFoldable<TyCtxt<'tcx>> + Copy, - { - let value = if let Some(inner) = binder.no_bound_vars() { - inner - } else { - let mut next_region = { - let nll_delegate = &mut self.delegate; - let mut lazy_universe = None; - - move |br: ty::BoundRegion| { - // The first time this closure is called, create a - // new universe for the placeholders we will make - // from here out. - let universe = lazy_universe.unwrap_or_else(|| { - let universe = nll_delegate.create_next_universe(); - lazy_universe = Some(universe); - universe - }); - - let placeholder = ty::PlaceholderRegion { universe, bound: br }; - debug!(?placeholder); - let placeholder_reg = nll_delegate.next_placeholder_region(placeholder); - debug!(?placeholder_reg); - - placeholder_reg - } - }; - - let delegate = FnMutDelegate { - regions: &mut next_region, - types: &mut |_bound_ty: ty::BoundTy| { - unreachable!("we only replace regions in nll_relate, not types") - }, - consts: &mut |_bound_var: ty::BoundVar, _ty| { - unreachable!("we only replace regions in nll_relate, not consts") - }, - }; - - self.infcx.tcx.replace_bound_vars_uncached(binder, delegate) - }; - - debug!(?value); - f(self, value) - } - - #[instrument(skip(self), level = "debug")] - fn instantiate_binder_with_existentials<T>(&mut self, binder: ty::Binder<'tcx, T>) -> T - where - T: ty::TypeFoldable<TyCtxt<'tcx>> + Copy, - { - if let Some(inner) = binder.no_bound_vars() { - return inner; - } - - let mut next_region = { - let nll_delegate = &mut self.delegate; - let mut reg_map = FxHashMap::default(); - - move |br: ty::BoundRegion| { - if let Some(ex_reg_var) = reg_map.get(&br) { - return *ex_reg_var; - } else { - let ex_reg_var = - nll_delegate.next_existential_region_var(true, br.kind.get_name()); - debug!(?ex_reg_var); - reg_map.insert(br, ex_reg_var); - - ex_reg_var - } - } - }; - - let delegate = FnMutDelegate { - regions: &mut next_region, - types: &mut |_bound_ty: ty::BoundTy| { - unreachable!("we only replace regions in nll_relate, not types") - }, - consts: &mut |_bound_var: ty::BoundVar, _ty| { - unreachable!("we only replace regions in nll_relate, not consts") - }, - }; - - let replaced = self.infcx.tcx.replace_bound_vars_uncached(binder, delegate); - debug!(?replaced); - - replaced - } -} - -/// When we instantiate an inference variable with a value in -/// `relate_ty_var`, we always have the pair of a `TyVid` and a `Ty`, -/// but the ordering may vary (depending on whether the inference -/// variable was found on the `a` or `b` sides). Therefore, this trait -/// allows us to factor out common code, while preserving the order -/// when needed. -trait VidValuePair<'tcx>: Debug { - /// Extract the inference variable (which could be either the - /// first or second part of the tuple). - fn vid(&self) -> ty::TyVid; - - /// Extract the value it is being related to (which will be the - /// opposite part of the tuple from the vid). - fn value_ty(&self) -> Ty<'tcx>; - - /// Given a generalized type G that should replace the vid, relate - /// G to the value, putting G on whichever side the vid would have - /// appeared. - fn relate_generalized_ty<D>( - &self, - relate: &mut TypeRelating<'_, 'tcx, D>, - generalized_ty: Ty<'tcx>, - ) -> RelateResult<'tcx, Ty<'tcx>> - where - D: TypeRelatingDelegate<'tcx>; -} - -impl<'tcx> VidValuePair<'tcx> for (ty::TyVid, Ty<'tcx>) { - fn vid(&self) -> ty::TyVid { - self.0 - } - - fn value_ty(&self) -> Ty<'tcx> { - self.1 - } - - fn relate_generalized_ty<D>( - &self, - relate: &mut TypeRelating<'_, 'tcx, D>, - generalized_ty: Ty<'tcx>, - ) -> RelateResult<'tcx, Ty<'tcx>> - where - D: TypeRelatingDelegate<'tcx>, - { - relate.relate(generalized_ty, self.value_ty()) - } -} - -// In this case, the "vid" is the "b" type. -impl<'tcx> VidValuePair<'tcx> for (Ty<'tcx>, ty::TyVid) { - fn vid(&self) -> ty::TyVid { - self.1 - } - - fn value_ty(&self) -> Ty<'tcx> { - self.0 - } - - fn relate_generalized_ty<D>( - &self, - relate: &mut TypeRelating<'_, 'tcx, D>, - generalized_ty: Ty<'tcx>, - ) -> RelateResult<'tcx, Ty<'tcx>> - where - D: TypeRelatingDelegate<'tcx>, - { - relate.relate(self.value_ty(), generalized_ty) - } -} - -impl<'tcx, D> TypeRelation<'tcx> for TypeRelating<'_, 'tcx, D> -where - D: TypeRelatingDelegate<'tcx>, -{ - fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn tag(&self) -> &'static str { - "nll::subtype" - } - - fn a_is_expected(&self) -> bool { - true - } - - #[instrument(skip(self, info), level = "trace", ret)] - fn relate_with_variance<T: Relate<'tcx>>( - &mut self, - variance: ty::Variance, - info: ty::VarianceDiagInfo<'tcx>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - let old_ambient_variance = self.ambient_variance; - self.ambient_variance = self.ambient_variance.xform(variance); - self.ambient_variance_info = self.ambient_variance_info.xform(info); - - debug!(?self.ambient_variance); - // In a bivariant context this always succeeds. - let r = - if self.ambient_variance == ty::Variance::Bivariant { a } else { self.relate(a, b)? }; - - self.ambient_variance = old_ambient_variance; - - Ok(r) - } - - #[instrument(skip(self), level = "debug")] - fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - let infcx = self.infcx; - - let a = self.infcx.shallow_resolve(a); - - if !D::forbid_inference_vars() { - b = self.infcx.shallow_resolve(b); - } - - if a == b { - return Ok(a); - } - - match (a.kind(), b.kind()) { - (_, &ty::Infer(ty::TyVar(vid))) => { - if D::forbid_inference_vars() { - // Forbid inference variables in the RHS. - bug!("unexpected inference var {:?}", b) - } else { - self.relate_ty_var((a, vid)) - } - } - - (&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var((vid, b)), - - ( - &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), - &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), - ) if a_def_id == b_def_id || infcx.next_trait_solver() => { - infcx.super_combine_tys(self, a, b).or_else(|err| { - // This behavior is only there for the old solver, the new solver - // shouldn't ever fail. Instead, it unconditionally emits an - // alias-relate goal. - assert!(!self.infcx.next_trait_solver()); - self.tcx().dcx().span_delayed_bug( - self.delegate.span(), - "failure to relate an opaque to itself should result in an error later on", - ); - if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) } - }) - } - (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) - | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) - if def_id.is_local() && !self.infcx.next_trait_solver() => - { - self.relate_opaques(a, b) - } - - _ => { - debug!(?a, ?b, ?self.ambient_variance); - - // Will also handle unification of `IntVar` and `FloatVar`. - self.infcx.super_combine_tys(self, a, b) - } - } - } - - #[instrument(skip(self), level = "trace")] - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!(?self.ambient_variance); - - if self.ambient_covariance() { - // Covariant: &'a u8 <: &'b u8. Hence, `'a: 'b`. - self.push_outlives(a, b, self.ambient_variance_info); - } - - if self.ambient_contravariance() { - // Contravariant: &'b u8 <: &'a u8. Hence, `'b: 'a`. - self.push_outlives(b, a, self.ambient_variance_info); - } - - Ok(a) - } - - fn consts( - &mut self, - a: ty::Const<'tcx>, - mut b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - let a = self.infcx.shallow_resolve(a); - - if !D::forbid_inference_vars() { - b = self.infcx.shallow_resolve(b); - } - - match b.kind() { - ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { - // Forbid inference variables in the RHS. - self.infcx.dcx().span_delayed_bug( - self.delegate.span(), - format!("unexpected inference var {b:?}",), - ); - Ok(a) - } - // FIXME(invariance): see the related FIXME above. - _ => self.infcx.super_combine_consts(self, a, b), - } - } - - #[instrument(skip(self), level = "trace")] - fn binders<T>( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate<'tcx>, - { - // We want that - // - // ``` - // for<'a> fn(&'a u32) -> &'a u32 <: - // fn(&'b u32) -> &'b u32 - // ``` - // - // but not - // - // ``` - // fn(&'a u32) -> &'a u32 <: - // for<'b> fn(&'b u32) -> &'b u32 - // ``` - // - // We therefore proceed as follows: - // - // - Instantiate binders on `b` universally, yielding a universe U1. - // - Instantiate binders on `a` existentially in U1. - - debug!(?self.ambient_variance); - - if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) { - // Fast path for the common case. - self.relate(a, b)?; - return Ok(ty::Binder::dummy(a)); - } - - if self.ambient_covariance() { - // Covariance, so we want `for<..> A <: for<..> B` -- - // therefore we compare any instantiation of A (i.e., A - // instantiated with existentials) against every - // instantiation of B (i.e., B instantiated with - // universals). - - // Reset the ambient variance to covariant. This is needed - // to correctly handle cases like - // - // for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32) - // - // Somewhat surprisingly, these two types are actually - // **equal**, even though the one on the right looks more - // polymorphic. The reason is due to subtyping. To see it, - // consider that each function can call the other: - // - // - The left function can call the right with `'b` and - // `'c` both equal to `'a` - // - // - The right function can call the left with `'a` set to - // `{P}`, where P is the point in the CFG where the call - // itself occurs. Note that `'b` and `'c` must both - // include P. At the point, the call works because of - // subtyping (i.e., `&'b u32 <: &{P} u32`). - let variance = std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant); - - // Note: the order here is important. Create the placeholders first, otherwise - // we assign the wrong universe to the existential! - self.enter_forall(b, |this, b| { - let a = this.instantiate_binder_with_existentials(a); - this.relate(a, b) - })?; - - self.ambient_variance = variance; - } - - if self.ambient_contravariance() { - // Contravariance, so we want `for<..> A :> for<..> B` - // -- therefore we compare every instantiation of A (i.e., - // A instantiated with universals) against any - // instantiation of B (i.e., B instantiated with - // existentials). Opposite of above. - - // Reset ambient variance to contravariance. See the - // covariant case above for an explanation. - let variance = - std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant); - - self.enter_forall(a, |this, a| { - let b = this.instantiate_binder_with_existentials(b); - this.relate(a, b) - })?; - - self.ambient_variance = variance; - } - - Ok(a) - } -} - -impl<'tcx, D> ObligationEmittingRelation<'tcx> for TypeRelating<'_, 'tcx, D> -where - D: TypeRelatingDelegate<'tcx>, -{ - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.delegate.param_env() - } - - fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) { - self.delegate.register_obligations( - obligations - .into_iter() - .map(|to_pred| { - Obligation::new(self.tcx(), ObligationCause::dummy(), self.param_env(), to_pred) - }) - .collect(), - ); - } - - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { - self.delegate.register_obligations(obligations); - } - - fn alias_relate_direction(&self) -> ty::AliasRelationDirection { - unreachable!("manually overridden to handle ty::Variance::Contravariant ambient variance") - } - - fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { - self.register_predicates([ty::Binder::dummy(match self.ambient_variance { - ty::Variance::Covariant => ty::PredicateKind::AliasRelate( - a.into(), - b.into(), - ty::AliasRelationDirection::Subtype, - ), - // a :> b is b <: a - ty::Variance::Contravariant => ty::PredicateKind::AliasRelate( - b.into(), - a.into(), - ty::AliasRelationDirection::Subtype, - ), - ty::Variance::Invariant => ty::PredicateKind::AliasRelate( - a.into(), - b.into(), - ty::AliasRelationDirection::Equate, - ), - // FIXME(deferred_projection_equality): Implement this when we trigger it. - // Probably just need to do nothing here. - ty::Variance::Bivariant => { - unreachable!("cannot defer an alias-relate goal with Bivariant variance (yet?)") - } - })]); - } -} diff --git a/compiler/rustc_infer/src/infer/relate/sub.rs b/compiler/rustc_infer/src/infer/relate/sub.rs index 36876acd7c0..5bd3a238a67 100644 --- a/compiler/rustc_infer/src/infer/relate/sub.rs +++ b/compiler/rustc_infer/src/infer/relate/sub.rs @@ -6,6 +6,7 @@ use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::TyVar; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::Span; use std::mem; /// Ensures `a` is made a subtype of `b`. Returns `a` on success. @@ -103,12 +104,12 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { Ok(a) } - (&ty::Infer(TyVar(a_id)), _) => { - self.fields.instantiate(b, ty::Contravariant, a_id, !self.a_is_expected)?; + (&ty::Infer(TyVar(a_vid)), _) => { + infcx.instantiate_ty_var(self, self.a_is_expected, a_vid, ty::Covariant, b)?; Ok(a) } - (_, &ty::Infer(TyVar(b_id))) => { - self.fields.instantiate(a, ty::Covariant, b_id, self.a_is_expected)?; + (_, &ty::Infer(TyVar(b_vid))) => { + infcx.instantiate_ty_var(self, !self.a_is_expected, b_vid, ty::Contravariant, a)?; Ok(a) } @@ -199,6 +200,10 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { } impl<'tcx> ObligationEmittingRelation<'tcx> for Sub<'_, '_, 'tcx> { + fn span(&self) -> Span { + self.fields.trace.span() + } + fn param_env(&self) -> ty::ParamEnv<'tcx> { self.fields.param_env } diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index a238eacda44..0e90836145e 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -libloading = "0.8.0" rustc-rayon = { version = "0.5.0", optional = true } rustc-rayon-core = { version = "0.5.0", optional = true } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 8a4705e0056..cd7957c3bce 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -423,18 +423,43 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se Compiler { sess, codegen_backend, override_queries: config.override_queries }; rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || { - let r = { - let _sess_abort_error = defer(|| { - compiler.sess.finish_diagnostics(&config.registry); + // There are two paths out of `f`. + // - Normal exit. + // - Panic, e.g. triggered by `abort_if_errors`. + // + // We must run `finish_diagnostics` in both cases. + let res = { + // If `f` panics, `finish_diagnostics` will run during + // unwinding because of the `defer`. + let mut guar = None; + let sess_abort_guard = defer(|| { + guar = compiler.sess.finish_diagnostics(&config.registry); }); - f(&compiler) + let res = f(&compiler); + + // If `f` doesn't panic, `finish_diagnostics` will run + // normally when `sess_abort_guard` is dropped. + drop(sess_abort_guard); + + // If `finish_diagnostics` emits errors (e.g. stashed + // errors) we can't return an error directly, because the + // return type of this function is `R`, not `Result<R, E>`. + // But we need to communicate the errors' existence to the + // caller, otherwise the caller might mistakenly think that + // no errors occurred and return a zero exit code. So we + // abort (panic) instead, similar to if `f` had panicked. + if guar.is_some() { + compiler.sess.dcx().abort_if_errors(); + } + + res }; let prof = compiler.sess.prof.clone(); - prof.generic_activity("drop_compiler").run(move || drop(compiler)); - r + + res }) }, ) diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 24c2e290534..d0ce23dacb5 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,5 +1,4 @@ #![feature(decl_macro)] -#![feature(error_iter)] #![feature(generic_nonzero)] #![feature(lazy_cell)] #![feature(let_chains)] diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 60d13f02ad7..66140168759 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -772,12 +772,11 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { // lot of annoying errors in the ui tests (basically, // lint warnings and so on -- kindck used to do this abort, but // kindck is gone now). -nmatsakis - if let Some(reported) = sess.dcx().has_errors() { - return Err(reported); - } else if sess.dcx().stashed_err_count() > 0 { - // Without this case we sometimes get delayed bug ICEs and I don't - // understand why. -nnethercote - return Err(sess.dcx().delayed_bug("some stashed error is waiting for use")); + // + // But we exclude lint errors from this, because lint errors are typically + // less serious and we're more likely to want to continue (#87337). + if let Some(guar) = sess.dcx().has_errors_excluding_lint_errors() { + return Err(guar); } sess.time("misc_checking_3", || { @@ -937,9 +936,7 @@ pub fn start_codegen<'tcx>( if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) { if let Err(error) = rustc_mir_transform::dump_mir::emit_mir(tcx) { - let dcx = tcx.dcx(); - dcx.emit_err(errors::CantEmitMIR { error }); - dcx.abort_if_errors(); + tcx.dcx().emit_fatal(errors::CantEmitMIR { error }); } } diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 211bcb9da94..86858bfe41d 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -222,12 +222,12 @@ impl<'tcx> Queries<'tcx> { pub fn codegen_and_build_linker(&'tcx self) -> Result<Linker> { self.global_ctxt()?.enter(|tcx| { - // Don't do code generation if there were any errors - self.compiler.sess.compile_status()?; - - // If we have any delayed bugs, for example because we created TyKind::Error earlier, - // it's likely that codegen will only cause more ICEs, obscuring the original problem - self.compiler.sess.dcx().flush_delayed(); + // Don't do code generation if there were any errors. Likewise if + // there were any delayed bugs, because codegen will likely cause + // more ICEs, obscuring the original problem. + if let Some(guar) = self.compiler.sess.dcx().has_errors_or_delayed_bugs() { + return Err(guar); + } // Hook for UI tests. Self::check_for_rustc_errors_attr(tcx); @@ -261,7 +261,9 @@ impl Linker { let (codegen_results, work_products) = codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames); - sess.compile_status()?; + if let Some(guar) = sess.dcx().has_errors() { + return Err(guar); + } sess.time("serialize_work_products", || { rustc_incremental::save_work_product_index(sess, &self.dep_graph, work_products) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 087c43075f1..823614e1f06 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -1,10 +1,10 @@ use crate::errors; use info; -use libloading::Library; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; #[cfg(parallel_compiler)] use rustc_data_structures::sync; +use rustc_metadata::{load_symbol_from_dylib, DylibError}; use rustc_parse::validate_attr; use rustc_session as session; use rustc_session::config::{self, Cfg, CrateType, OutFileName, OutputFilenames, OutputTypes}; @@ -17,7 +17,6 @@ use rustc_span::symbol::{sym, Symbol}; use session::EarlyDiagCtxt; use std::env; use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; -use std::mem; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::OnceLock; @@ -162,29 +161,19 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>( } fn load_backend_from_dylib(early_dcx: &EarlyDiagCtxt, path: &Path) -> MakeBackendFn { - fn format_err(e: &(dyn std::error::Error + 'static)) -> String { - e.sources().map(|e| format!(": {e}")).collect() - } - let lib = unsafe { Library::new(path) }.unwrap_or_else(|err| { - let err = format!("couldn't load codegen backend {path:?}{}", format_err(&err)); - early_dcx.early_fatal(err); - }); - - let backend_sym = unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") } - .unwrap_or_else(|e| { + match unsafe { load_symbol_from_dylib::<MakeBackendFn>(path, "__rustc_codegen_backend") } { + Ok(backend_sym) => backend_sym, + Err(DylibError::DlOpen(path, err)) => { + let err = format!("couldn't load codegen backend {path}{err}"); + early_dcx.early_fatal(err); + } + Err(DylibError::DlSym(_path, err)) => { let e = format!( - "`__rustc_codegen_backend` symbol lookup in the codegen backend failed{}", - format_err(&e) + "`__rustc_codegen_backend` symbol lookup in the codegen backend failed{err}", ); early_dcx.early_fatal(e); - }); - - // Intentionally leak the dynamic library. We can't ever unload it - // since the library can make things that will live arbitrarily long. - let backend_sym = unsafe { backend_sym.into_raw() }; - mem::forget(lib); - - *backend_sym + } + } } /// Get the codegen backend based on the name and specified sysroot. diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 785895e0ab8..1548646c04a 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -72,8 +72,11 @@ lint_builtin_explicit_outlives = outlives requirements can be inferred lint_builtin_export_name_fn = declaration of a function with `export_name` lint_builtin_export_name_method = declaration of a method with `export_name` - lint_builtin_export_name_static = declaration of a static with `export_name` + +lint_builtin_global_asm = usage of `core::arch::global_asm` +lint_builtin_global_macro_unsafety = using this macro is unsafe even though it does not need an `unsafe` block + lint_builtin_impl_unsafe_method = implementation of an `unsafe` method lint_builtin_incomplete_features = the feature `{$name}` is incomplete and may not be safe to use and/or cause compiler crashes diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index faa35f51cd4..92f7497362e 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -393,6 +393,10 @@ impl EarlyLintPass for UnsafeCode { } } + ast::ItemKind::GlobalAsm(..) => { + self.report_unsafe(cx, it.span, BuiltinUnsafe::GlobalAsm); + } + _ => {} } } @@ -1542,32 +1546,6 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { } } -declare_lint_pass!( - /// Lint constants that are erroneous. - /// Without this lint, we might not get any diagnostic if the constant is - /// unused within this crate, even though downstream crates can't use it - /// without producing an error. - UnusedBrokenConst => [] -); - -impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst { - fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - match it.kind { - hir::ItemKind::Const(_, _, body_id) => { - let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id(); - // trigger the query once for all constants since that will already report the errors - // FIXME(generic_const_items): Does this work properly with generic const items? - cx.tcx.ensure().const_eval_poly(def_id); - } - hir::ItemKind::Static(_, _, body_id) => { - let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id(); - cx.tcx.ensure().eval_static_initializer(def_id); - } - _ => {} - } - } -} - declare_lint! { /// The `trivial_bounds` lint detects trait bounds that don't depend on /// any type parameters. diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 5af2b6daec1..86434002e2c 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -1,3 +1,6 @@ +#![allow(rustc::diagnostic_outside_of_impl)] +#![allow(rustc::untranslatable_diagnostic)] + use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_errors::{add_elided_lifetime_in_path_suggestion, DiagnosticBuilder}; use rustc_errors::{Applicability, SuggestionStyle}; diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs index 21d4b6fa65b..bcff20fc260 100644 --- a/compiler/rustc_lint/src/errors.rs +++ b/compiler/rustc_lint/src/errors.rs @@ -1,5 +1,7 @@ use crate::fluent_generated as fluent; -use rustc_errors::{codes::*, AddToDiagnostic, Diagnostic, SubdiagnosticMessageOp}; +use rustc_errors::{ + codes::*, AddToDiagnostic, DiagnosticBuilder, EmissionGuarantee, SubdiagnosticMessageOp, +}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::lint::Level; use rustc_span::{Span, Symbol}; @@ -24,7 +26,11 @@ pub enum OverruledAttributeSub { } impl AddToDiagnostic for OverruledAttributeSub { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { match self { OverruledAttributeSub::DefaultSource { id } => { diag.note(fluent::lint_default_source); diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 40fb12b2107..c1a083bde8d 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1062,6 +1062,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { if self.lint_added_lints { let lint = builtin::UNKNOWN_LINTS; let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS); + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] lint_level( self.sess, lint, diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index e50f4ca338b..d8e12c04f75 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -213,8 +213,6 @@ late_lint_methods!( ExplicitOutlivesRequirements: ExplicitOutlivesRequirements, InvalidValue: InvalidValue, DerefNullPtr: DerefNullPtr, - // May Depend on constants elsewhere - UnusedBrokenConst: UnusedBrokenConst, UnstableFeatures: UnstableFeatures, UngatedAsyncFnTrackCaller: UngatedAsyncFnTrackCaller, ArrayIntoIter: ArrayIntoIter::default(), @@ -524,6 +522,11 @@ fn register_builtins(store: &mut LintStore) { "no longer needed, see RFC #3535 \ <https://rust-lang.github.io/rfcs/3535-constants-in-patterns.html> for more information", ); + store.register_removed( + "suspicious_auto_trait_impls", + "no longer needed, see #93367 \ + <https://github.com/rust-lang/rust/issues/93367> for more information", + ); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index c204c67fc1f..f067c365170 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -5,8 +5,8 @@ use std::num::NonZero; use crate::errors::RequestedLevel; use crate::fluent_generated as fluent; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, DecorateLint, Diagnostic, DiagnosticBuilder, - DiagnosticMessage, DiagnosticStyledString, SubdiagnosticMessageOp, SuggestionStyle, + codes::*, AddToDiagnostic, Applicability, DecorateLint, DiagnosticBuilder, DiagnosticMessage, + DiagnosticStyledString, EmissionGuarantee, SubdiagnosticMessageOp, SuggestionStyle, }; use rustc_hir::def_id::DefId; use rustc_macros::{LintDiagnostic, Subdiagnostic}; @@ -114,6 +114,9 @@ pub enum BuiltinUnsafe { DeclUnsafeMethod, #[diag(lint_builtin_impl_unsafe_method)] ImplUnsafeMethod, + #[diag(lint_builtin_global_asm)] + #[note(lint_builtin_global_macro_unsafety)] + GlobalAsm, } #[derive(LintDiagnostic)] @@ -267,17 +270,21 @@ pub struct SuggestChangingAssocTypes<'a, 'b> { pub ty: &'a rustc_hir::Ty<'b>, } -impl AddToDiagnostic for SuggestChangingAssocTypes<'_, '_> { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { +impl<'a, 'b> AddToDiagnostic for SuggestChangingAssocTypes<'a, 'b> { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { // Access to associates types should use `<T as Bound>::Assoc`, which does not need a // bound. Let's see if this type does that. // We use a HIR visitor to walk the type. use rustc_hir::intravisit::{self, Visitor}; - struct WalkAssocTypes<'a> { - err: &'a mut Diagnostic, + struct WalkAssocTypes<'a, 'b, G: EmissionGuarantee> { + err: &'a mut DiagnosticBuilder<'b, G>, } - impl Visitor<'_> for WalkAssocTypes<'_> { + impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for WalkAssocTypes<'a, 'b, G> { fn visit_qpath( &mut self, qpath: &rustc_hir::QPath<'_>, @@ -320,7 +327,11 @@ pub struct BuiltinTypeAliasGenericBoundsSuggestion { } impl AddToDiagnostic for BuiltinTypeAliasGenericBoundsSuggestion { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { diag.multipart_suggestion( fluent::lint_suggestion, self.suggestions, @@ -437,7 +448,11 @@ pub struct BuiltinUnpermittedTypeInitSub { } impl AddToDiagnostic for BuiltinUnpermittedTypeInitSub { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { let mut err = self.err; loop { if let Some(span) = err.span { @@ -488,7 +503,11 @@ pub struct BuiltinClashingExternSub<'a> { } impl AddToDiagnostic for BuiltinClashingExternSub<'_> { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { let mut expected_str = DiagnosticStyledString::new(); expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false); let mut found_str = DiagnosticStyledString::new(); @@ -766,7 +785,11 @@ pub struct HiddenUnicodeCodepointsDiagLabels { } impl AddToDiagnostic for HiddenUnicodeCodepointsDiagLabels { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { for (c, span) in self.spans { diag.span_label(span, format!("{c:?}")); } @@ -780,7 +803,11 @@ pub enum HiddenUnicodeCodepointsDiagSub { // Used because of multiple multipart_suggestion and note impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { match self { HiddenUnicodeCodepointsDiagSub::Escape { spans } => { diag.multipart_suggestion_with_style( @@ -928,7 +955,11 @@ pub struct NonBindingLetSub { } impl AddToDiagnostic for NonBindingLetSub { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { let can_suggest_binding = self.drop_fn_start_end.is_some() || !self.is_assign_desugar; if can_suggest_binding { @@ -1208,7 +1239,11 @@ pub enum NonSnakeCaseDiagSub { } impl AddToDiagnostic for NonSnakeCaseDiagSub { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { match self { NonSnakeCaseDiagSub::Label { span } => { diag.span_label(span, fluent::lint_label); @@ -1401,7 +1436,11 @@ pub enum OverflowingBinHexSign { } impl AddToDiagnostic for OverflowingBinHexSign { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { match self { OverflowingBinHexSign::Positive => { diag.note(fluent::lint_positive_note); @@ -1543,7 +1582,8 @@ pub enum AmbiguousWidePointerComparisons<'a> { #[multipart_suggestion( lint_addr_metadata_suggestion, style = "verbose", - applicability = "machine-applicable" + // FIXME(#53934): make machine-applicable again + applicability = "maybe-incorrect" )] pub struct AmbiguousWidePointerComparisonsAddrMetadataSuggestion<'a> { pub ne: &'a str, @@ -1562,7 +1602,8 @@ pub enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> { #[multipart_suggestion( lint_addr_suggestion, style = "verbose", - applicability = "machine-applicable" + // FIXME(#53934): make machine-applicable again + applicability = "maybe-incorrect" )] AddrEq { ne: &'a str, @@ -1578,7 +1619,8 @@ pub enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> { #[multipart_suggestion( lint_addr_suggestion, style = "verbose", - applicability = "machine-applicable" + // FIXME(#53934): make machine-applicable again + applicability = "maybe-incorrect" )] Cast { deref_left: &'a str, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 3f5d3c25971..84a050a242a 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -90,7 +90,6 @@ declare_lint_pass! { SOFT_UNSTABLE, STABLE_FEATURES, STATIC_MUT_REFS, - SUSPICIOUS_AUTO_TRAIT_IMPLS, TEST_UNSTABLE_LINT, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, TRIVIAL_CASTS, @@ -1503,7 +1502,7 @@ declare_lint! { Warn, "distinct impls distinguished only by the leak-check code", @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reason: FutureIncompatibilityReason::Custom("the behavior may change in a future release"), reference: "issue #56105 <https://github.com/rust-lang/rust/issues/56105>", }; } @@ -4033,40 +4032,6 @@ declare_lint! { } declare_lint! { - /// The `suspicious_auto_trait_impls` lint checks for potentially incorrect - /// implementations of auto traits. - /// - /// ### Example - /// - /// ```rust - /// struct Foo<T>(T); - /// - /// unsafe impl<T> Send for Foo<*const T> {} - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// A type can implement auto traits, e.g. `Send`, `Sync` and `Unpin`, - /// in two different ways: either by writing an explicit impl or if - /// all fields of the type implement that auto trait. - /// - /// The compiler disables the automatic implementation if an explicit one - /// exists for given type constructor. The exact rules governing this - /// were previously unsound, quite subtle, and have been recently modified. - /// This change caused the automatic implementation to be disabled in more - /// cases, potentially breaking some code. - pub SUSPICIOUS_AUTO_TRAIT_IMPLS, - Warn, - "the rules governing auto traits have recently changed resulting in potential breakage", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange, - reference: "issue #93367 <https://github.com/rust-lang/rust/issues/93367>", - }; -} - -declare_lint! { /// The `deprecated_where_clause_location` lint detects when a where clause in front of the equals /// in an associated type. /// diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 09b1f03f151..d4e5c78c492 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -227,8 +227,8 @@ impl Level { } /// Converts a lower-case string to a level. This will never construct the expect - /// level as that would require a [`LintExpectationId`] - pub fn from_str(x: &str) -> Option<Level> { + /// level as that would require a [`LintExpectationId`]. + pub fn from_str(x: &str) -> Option<Self> { match x { "allow" => Some(Level::Allow), "warn" => Some(Level::Warn), @@ -238,17 +238,21 @@ impl Level { } } - /// Converts a symbol to a level. - pub fn from_attr(attr: &Attribute) -> Option<Level> { - match attr.name_or_empty() { - sym::allow => Some(Level::Allow), - sym::expect => Some(Level::Expect(LintExpectationId::Unstable { - attr_id: attr.id, - lint_index: None, - })), - sym::warn => Some(Level::Warn), - sym::deny => Some(Level::Deny), - sym::forbid => Some(Level::Forbid), + /// Converts an `Attribute` to a level. + pub fn from_attr(attr: &Attribute) -> Option<Self> { + Self::from_symbol(attr.name_or_empty(), Some(attr.id)) + } + + /// Converts a `Symbol` to a level. + pub fn from_symbol(s: Symbol, id: Option<AttrId>) -> Option<Self> { + match (s, id) { + (sym::allow, _) => Some(Level::Allow), + (sym::expect, Some(attr_id)) => { + Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None })) + } + (sym::warn, _) => Some(Level::Warn), + (sym::deny, _) => Some(Level::Deny), + (sym::forbid, _) => Some(Level::Forbid), _ => None, } } diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index b45706fd1e5..7326f2e8e2a 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -418,7 +418,11 @@ extern "C" LLVMAttributeRef LLVMRustCreateMemoryEffectsAttr(LLVMContextRef C, } } -// Enable a fast-math flag +// Enable all fast-math flags, including those which will cause floating-point operations +// to return poison for some well-defined inputs. This function can only be used to build +// unsafe Rust intrinsics. That unsafety does permit additional optimizations, but at the +// time of writing, their value is not well-understood relative to those enabled by +// LLVMRustSetAlgebraicMath. // // https://llvm.org/docs/LangRef.html#fast-math-flags extern "C" void LLVMRustSetFastMath(LLVMValueRef V) { @@ -427,6 +431,25 @@ extern "C" void LLVMRustSetFastMath(LLVMValueRef V) { } } +// Enable fast-math flags which permit algebraic transformations that are not allowed by +// IEEE floating point. For example: +// a + (b + c) = (a + b) + c +// and +// a / b = a * (1 / b) +// Note that this does NOT enable any flags which can cause a floating-point operation on +// well-defined inputs to return poison, and therefore this function can be used to build +// safe Rust intrinsics (such as fadd_algebraic). +// +// https://llvm.org/docs/LangRef.html#fast-math-flags +extern "C" void LLVMRustSetAlgebraicMath(LLVMValueRef V) { + if (auto I = dyn_cast<Instruction>(unwrap<Value>(V))) { + I->setHasAllowReassoc(true); + I->setHasAllowContract(true); + I->setHasAllowReciprocal(true); + I->setHasNoSignedZeros(true); + } +} + extern "C" LLVMValueRef LLVMRustBuildAtomicLoad(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Source, const char *Name, LLVMAtomicOrdering Order) { diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 3a5f289559e..323614c222f 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -87,9 +87,13 @@ impl SubdiagnosticDeriveBuilder { let f = &self.f; let ret = structure.gen_impl(quote! { gen impl rustc_errors::AddToDiagnostic for @Self { - fn add_to_diagnostic_with<__F>(self, #diag: &mut rustc_errors::Diagnostic, #f: __F) - where - __F: rustc_errors::SubdiagnosticMessageOp, + fn add_to_diagnostic_with<__G, __F>( + self, + #diag: &mut rustc_errors::DiagnosticBuilder<'_, __G>, + #f: __F + ) where + __G: rustc_errors::EmissionGuarantee, + __F: rustc_errors::SubdiagnosticMessageOp<__G>, { #implementation } diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 619f93c8a53..14e6a06839e 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -41,6 +41,20 @@ pub fn symbols(input: TokenStream) -> TokenStream { symbols::symbols(input.into()).into() } +/// Derive an extension trait for a given impl block. The trait name +/// goes into the parenthesized args of the macro, for greppability. +/// For example: +/// ``` +/// use rustc_macros::extension; +/// #[extension(pub trait Foo)] +/// impl i32 { fn hello() {} } +/// ``` +/// +/// expands to: +/// ``` +/// pub trait Foo { fn hello(); } +/// impl Foo for i32 { fn hello() {} } +/// ``` #[proc_macro_attribute] pub fn extension(attr: TokenStream, input: TokenStream) -> TokenStream { extension::extension(attr, input) diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index 8da6f0007f0..a1c6fba4d43 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -45,7 +45,7 @@ metadata_crate_not_panic_runtime = the crate `{$crate_name}` is not a panic runtime metadata_dl_error = - {$err} + {$path}{$err} metadata_empty_link_name = link name must not be empty diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index c18f0e7b7b9..f65fe1a29c7 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -692,20 +692,8 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { path: &Path, stable_crate_id: StableCrateId, ) -> Result<&'static [ProcMacro], CrateError> { - // Make sure the path contains a / or the linker will search for it. - let path = try_canonicalize(path).unwrap(); - let lib = load_dylib(&path, 5).map_err(|err| CrateError::DlOpen(err))?; - let sym_name = self.sess.generate_proc_macro_decls_symbol(stable_crate_id); - let sym = unsafe { lib.get::<*const &[ProcMacro]>(sym_name.as_bytes()) } - .map_err(|err| CrateError::DlSym(err.to_string()))?; - - // Intentionally leak the dynamic library. We can't ever unload it - // since the library can make things that will live arbitrarily long. - let sym = unsafe { sym.into_raw() }; - std::mem::forget(lib); - - Ok(unsafe { **sym }) + Ok(unsafe { *load_symbol_from_dylib::<*const &[ProcMacro]>(path, &sym_name)? }) } fn inject_panic_runtime(&mut self, krate: &ast::Crate) { @@ -926,7 +914,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { what: &str, needs_dep: &dyn Fn(&CrateMetadata) -> bool, ) { - // don't perform this validation if the session has errors, as one of + // Don't perform this validation if the session has errors, as one of // those errors may indicate a circular dependency which could cause // this to stack overflow. if self.dcx().has_errors().is_some() { @@ -1070,16 +1058,6 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { pub fn maybe_process_path_extern(&mut self, name: Symbol) -> Option<CrateNum> { self.maybe_resolve_crate(name, CrateDepKind::Explicit, None).ok() } - - pub fn unload_unused_crates(&mut self) { - for opt_cdata in &mut self.cstore.metas { - if let Some(cdata) = opt_cdata - && !cdata.used() - { - *opt_cdata = None; - } - } - } } fn global_allocator_spans(krate: &ast::Crate) -> Vec<Span> { @@ -1126,6 +1104,10 @@ fn alloc_error_handler_spans(krate: &ast::Crate) -> Vec<Span> { f.spans } +fn format_dlopen_err(e: &(dyn std::error::Error + 'static)) -> String { + e.sources().map(|e| format!(": {e}")).collect() +} + // On Windows the compiler would sometimes intermittently fail to open the // proc-macro DLL with `Error::LoadLibraryExW`. It is suspected that something in the // system still holds a lock on the file, so we retry a few times before calling it @@ -1164,9 +1146,43 @@ fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, S let last_error = last_error.unwrap(); let message = if let Some(src) = last_error.source() { - format!("{last_error} ({src}) (retried {max_attempts} times)") + format!("{} ({src}) (retried {max_attempts} times)", format_dlopen_err(&last_error)) } else { - format!("{last_error} (retried {max_attempts} times)") + format!("{} (retried {max_attempts} times)", format_dlopen_err(&last_error)) }; Err(message) } + +pub enum DylibError { + DlOpen(String, String), + DlSym(String, String), +} + +impl From<DylibError> for CrateError { + fn from(err: DylibError) -> CrateError { + match err { + DylibError::DlOpen(path, err) => CrateError::DlOpen(path, err), + DylibError::DlSym(path, err) => CrateError::DlSym(path, err), + } + } +} + +pub unsafe fn load_symbol_from_dylib<T: Copy>( + path: &Path, + sym_name: &str, +) -> Result<T, DylibError> { + // Make sure the path contains a / or the linker will search for it. + let path = try_canonicalize(path).unwrap(); + let lib = + load_dylib(&path, 5).map_err(|err| DylibError::DlOpen(path.display().to_string(), err))?; + + let sym = unsafe { lib.get::<T>(sym_name.as_bytes()) } + .map_err(|err| DylibError::DlSym(path.display().to_string(), format_dlopen_err(&err)))?; + + // Intentionally leak the dynamic library. We can't ever unload it + // since the library can make things that will live arbitrarily long. + let sym = unsafe { sym.into_raw() }; + std::mem::forget(lib); + + Ok(*sym) +} diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index d17bf0cf708..9a05d9ac0de 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -505,6 +505,8 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for MultipleCandidates { diag.code(E0464); diag.span(self.span); for (i, candidate) in self.candidates.iter().enumerate() { + // FIXME: make this translatable + #[allow(rustc::untranslatable_diagnostic)] diag.note(format!("candidate #{}: {}", i + 1, candidate.display())); } diag @@ -533,6 +535,7 @@ pub struct StableCrateIdCollision { pub struct DlError { #[primary_span] pub span: Span, + pub path: String, pub err: String, } @@ -601,6 +604,8 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for InvalidMetadataFiles { diag.code(E0786); diag.span(self.span); for crate_rejection in self.crate_rejections { + // FIXME: make this translatable + #[allow(rustc::untranslatable_diagnostic)] diag.note(crate_rejection); } diag diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 70ad8598957..f133a2f5f73 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -3,6 +3,7 @@ #![feature(rustdoc_internals)] #![allow(internal_features)] #![feature(decl_macro)] +#![feature(error_iter)] #![feature(extract_if)] #![feature(coroutines)] #![feature(generic_nonzero)] @@ -39,6 +40,7 @@ pub mod errors; pub mod fs; pub mod locator; +pub use creader::{load_symbol_from_dylib, DylibError}; pub use fs::{emit_wrapper_file, METADATA_FILENAME}; pub use native_libs::find_native_static_library; pub use rmeta::{encode_metadata, rendered_const, EncodedMetadata, METADATA_HEADER}; diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 1ab965e2876..15f56db6278 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -921,8 +921,8 @@ pub(crate) enum CrateError { MultipleCandidates(Symbol, CrateFlavor, Vec<PathBuf>), SymbolConflictsCurrent(Symbol), StableCrateIdCollision(Symbol, Symbol), - DlOpen(String), - DlSym(String), + DlOpen(String, String), + DlSym(String, String), LocatorCombined(Box<CombinedLocatorError>), NotFound(Symbol), } @@ -967,8 +967,8 @@ impl CrateError { CrateError::StableCrateIdCollision(crate_name0, crate_name1) => { dcx.emit_err(errors::StableCrateIdCollision { span, crate_name0, crate_name1 }); } - CrateError::DlOpen(s) | CrateError::DlSym(s) => { - dcx.emit_err(errors::DlError { span, err: s }); + CrateError::DlOpen(path, err) | CrateError::DlSym(path, err) => { + dcx.emit_err(errors::DlError { span, path, err }); } CrateError::LocatorCombined(locator) => { let crate_name = locator.crate_name; diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 9df28799f4f..2f507b258fe 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -519,6 +519,16 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { tcx.untracked().cstore.freeze(); tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).iter_crate_data().map(|(cnum, _)| cnum)) }, + used_crates: |tcx, ()| { + // The list of loaded crates is now frozen in query cache, + // so make sure cstore is not mutably accessed from here on. + tcx.untracked().cstore.freeze(); + tcx.arena.alloc_from_iter( + CStore::from_tcx(tcx) + .iter_crate_data() + .filter_map(|(cnum, data)| data.used().then_some(cnum)), + ) + }, ..providers.queries }; provide_extern(&mut providers.extern_queries); diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index c648b2bfe9b..a532635669d 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -47,7 +47,7 @@ macro_rules! arena_types { rustc_middle::traits::query::DropckOutlivesResult<'tcx> > >, - [] normalize_projection_ty: + [] normalize_canonicalized_projection_ty: rustc_middle::infer::canonical::Canonical<'tcx, rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::traits::query::NormalizationResult<'tcx> diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 2a6f473cd32..1e9e9947db5 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -2,7 +2,7 @@ use std::cmp; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; -use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticMessage, MultiSpan}; +use rustc_errors::{DiagnosticBuilder, DiagnosticMessage, MultiSpan}; use rustc_hir::{HirId, ItemLocalId}; use rustc_session::lint::{ builtin::{self, FORBIDDEN_LINT_GROUPS}, @@ -204,7 +204,7 @@ pub fn explain_lint_level_source( lint: &'static Lint, level: Level, src: LintLevelSource, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, ()>, ) { let name = lint.name_lower(); if let Level::Allow = level { @@ -359,7 +359,7 @@ pub fn lint_level( // Lint diagnostics that are covered by the expect level will not be emitted outside // the compiler. It is therefore not necessary to add any information for the user. // This will therefore directly call the decorate function which will in turn emit - // the `Diagnostic`. + // the diagnostic. if let Level::Expect(_) = level { decorate(&mut err); err.emit(); @@ -401,7 +401,7 @@ pub fn lint_level( // Finally, run `decorate`. decorate(&mut err); - explain_lint_level_source(lint, level, src, &mut *err); + explain_lint_level_source(lint, level, src, &mut err); err.emit() } lint_level_impl(sess, lint, level, src, span, msg, Box::new(decorate)) diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 2f624ab0527..5f3ecf34416 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -9,7 +9,7 @@ use rustc_attr::{ self as attr, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability, }; use rustc_data_structures::unord::UnordMap; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_feature::GateIssue; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap}; @@ -125,7 +125,7 @@ pub fn report_unstable( } pub fn deprecation_suggestion( - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_, ()>, kind: &str, suggestion: Option<Symbol>, span: Span, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 5be45c33e11..e87bc581e6e 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -31,7 +31,7 @@ use crate::query::plumbing::{ }; use crate::thir; use crate::traits::query::{ - CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, + CanonicalAliasGoal, CanonicalPredicateGoal, CanonicalTyGoal, CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, NoSolution, }; @@ -1872,6 +1872,13 @@ rustc_queries! { eval_always desc { "fetching all foreign CrateNum instances" } } + // Crates that are loaded non-speculatively (not for diagnostics or doc links). + // FIXME: This is currently only used for collecting lang items, but should be used instead of + // `crates` in most other cases too. + query used_crates(_: ()) -> &'tcx [CrateNum] { + eval_always + desc { "fetching `CrateNum`s for all crates loaded non-speculatively" } + } /// A list of all traits in a crate, used by rustdoc and error reporting. query traits(_: CrateNum) -> &'tcx [DefId] { @@ -1931,9 +1938,13 @@ rustc_queries! { arena_cache } - /// Do not call this query directly: invoke `normalize` instead. - query normalize_projection_ty( - goal: CanonicalProjectionGoal<'tcx> + /// <div class="warning"> + /// + /// Do not call this query directly: Invoke `normalize` instead. + /// + /// </div> + query normalize_canonicalized_projection_ty( + goal: CanonicalAliasGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution, @@ -1941,9 +1952,13 @@ rustc_queries! { desc { "normalizing `{}`", goal.value.value } } - /// Do not call this query directly: invoke `normalize` instead. - query normalize_weak_ty( - goal: CanonicalProjectionGoal<'tcx> + /// <div class="warning"> + /// + /// Do not call this query directly: Invoke `normalize` instead. + /// + /// </div> + query normalize_canonicalized_weak_ty( + goal: CanonicalAliasGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution, @@ -1951,9 +1966,13 @@ rustc_queries! { desc { "normalizing `{}`", goal.value.value } } - /// Do not call this query directly: invoke `normalize` instead. - query normalize_inherent_projection_ty( - goal: CanonicalProjectionGoal<'tcx> + /// <div class="warning"> + /// + /// Do not call this query directly: Invoke `normalize` instead. + /// + /// </div> + query normalize_canonicalized_inherent_projection_ty( + goal: CanonicalAliasGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution, diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 119e0a49acf..b3c09d1d152 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -16,7 +16,7 @@ use crate::ty::GenericArgsRef; use crate::ty::{self, AdtKind, Ty}; use rustc_data_structures::sync::Lrc; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, DiagnosticBuilder, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_span::def_id::{LocalDefId, CRATE_DEF_ID}; @@ -908,7 +908,7 @@ pub enum ObjectSafetyViolationSolution { } impl ObjectSafetyViolationSolution { - pub fn add_to(self, err: &mut Diagnostic) { + pub fn add_to<G: EmissionGuarantee>(self, err: &mut DiagnosticBuilder<'_, G>) { match self { ObjectSafetyViolationSolution::None => {} ObjectSafetyViolationSolution::AddSelfOrMakeSized { diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 61d96cad57d..12e4f70ba57 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -67,7 +67,7 @@ pub mod type_op { } } -pub type CanonicalProjectionGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>; +pub type CanonicalAliasGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>; pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>; @@ -177,10 +177,10 @@ pub struct MethodAutoderefBadTy<'tcx> { pub ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, } -/// Result from the `normalize_projection_ty` query. +/// Result of the `normalize_canonicalized_{{,inherent_}projection,weak}_ty` queries. #[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct NormalizationResult<'tcx> { - /// Result of normalization. + /// Result of the normalization. pub normalized_ty: Ty<'tcx>, } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index cc734e7157f..9d59f779470 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -153,11 +153,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) -> Self::Const { Const::new_bound(self, debruijn, var, ty) } - - fn expect_error_or_delayed_bug() { - let has_errors = ty::tls::with(|tcx| tcx.dcx().has_errors_or_lint_errors_or_delayed_bugs()); - assert!(has_errors.is_some()); - } } type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>; diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 7cb326ce696..f379cf27a5f 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -11,7 +11,7 @@ use crate::ty::{ }; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg}; +use rustc_errors::{Applicability, DiagnosticArgValue, DiagnosticBuilder, IntoDiagnosticArg}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -111,7 +111,7 @@ where pub fn suggest_arbitrary_trait_bound<'tcx>( tcx: TyCtxt<'tcx>, generics: &hir::Generics<'_>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: PolyTraitPredicate<'tcx>, associated_ty: Option<(&'static str, Ty<'tcx>)>, ) -> bool { @@ -216,7 +216,7 @@ fn suggest_changing_unsized_bound( pub fn suggest_constraining_type_param( tcx: TyCtxt<'_>, generics: &hir::Generics<'_>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, param_name: &str, constraint: &str, def_id: Option<DefId>, @@ -235,7 +235,7 @@ pub fn suggest_constraining_type_param( pub fn suggest_constraining_type_params<'a>( tcx: TyCtxt<'_>, generics: &hir::Generics<'_>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>, span_to_replace: Option<Span>, ) -> bool { diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 80b763d1469..e15f0378846 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -351,7 +351,7 @@ impl<'tcx> TyCtxt<'tcx> { }) .expect("could not write to `String`"); - if !self.sess.opts.unstable_opts.write_long_types_to_disk { + if !self.sess.opts.unstable_opts.write_long_types_to_disk || self.sess.opts.verbose { return regular; } diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 0f4b5fe228c..18cf5445e56 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -181,9 +181,10 @@ impl FlagComputation { &ty::Alias(kind, data) => { self.add_flags(match kind { - ty::Weak | ty::Projection => TypeFlags::HAS_TY_PROJECTION, - ty::Inherent => TypeFlags::HAS_TY_INHERENT, + ty::Projection => TypeFlags::HAS_TY_PROJECTION, + ty::Weak => TypeFlags::HAS_TY_WEAK, ty::Opaque => TypeFlags::HAS_TY_OPAQUE, + ty::Inherent => TypeFlags::HAS_TY_INHERENT, }); self.add_alias_ty(data); diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index a03ee78a29a..4287b382604 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -410,8 +410,7 @@ impl<'tcx> TypeckResults<'tcx> { pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> Option<BindingMode> { self.pat_binding_modes().get(id).copied().or_else(|| { - s.dcx().span_delayed_bug(sp, "missing binding mode"); - None + s.dcx().span_bug(sp, "missing binding mode"); }) } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 3f539945841..72a1905c147 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -11,6 +11,7 @@ use crate::ty::{GenericArgKind, GenericArgsRef}; use rustc_apfloat::Float as _; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher}; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; @@ -867,6 +868,63 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_args_from_iter(args.into_iter().map(|arg| arg.into()).chain(opt_const_param)) } + + /// Expand any [weak alias types][weak] contained within the given `value`. + /// + /// This should be used over other normalization routines in situations where + /// it's important not to normalize other alias types and where the predicates + /// on the corresponding type alias shouldn't be taken into consideration. + /// + /// Whenever possible **prefer not to use this function**! Instead, use standard + /// normalization routines or if feasible don't normalize at all. + /// + /// This function comes in handy if you want to mimic the behavior of eager + /// type alias expansion in a localized manner. + /// + /// <div class="warning"> + /// This delays a bug on overflow! Therefore you need to be certain that the + /// contained types get fully normalized at a later stage. Note that even on + /// overflow all well-behaved weak alias types get expanded correctly, so the + /// result is still useful. + /// </div> + /// + /// [weak]: ty::Weak + pub fn expand_weak_alias_tys<T: TypeFoldable<TyCtxt<'tcx>>>(self, value: T) -> T { + value.fold_with(&mut WeakAliasTypeExpander { tcx: self, depth: 0 }) + } + + /// Peel off all [weak alias types] in this type until there are none left. + /// + /// This only expands weak alias types in “head” / outermost positions. It can + /// be used over [expand_weak_alias_tys] as an optimization in situations where + /// one only really cares about the *kind* of the final aliased type but not + /// the types the other constituent types alias. + /// + /// <div class="warning"> + /// This delays a bug on overflow! Therefore you need to be certain that the + /// type gets fully normalized at a later stage. + /// </div> + /// + /// [weak]: ty::Weak + /// [expand_weak_alias_tys]: Self::expand_weak_alias_tys + pub fn peel_off_weak_alias_tys(self, mut ty: Ty<'tcx>) -> Ty<'tcx> { + let ty::Alias(ty::Weak, _) = ty.kind() else { return ty }; + + let limit = self.recursion_limit(); + let mut depth = 0; + + while let ty::Alias(ty::Weak, alias) = ty.kind() { + if !limit.value_within_limit(depth) { + let guar = self.dcx().delayed_bug("overflow expanding weak alias type"); + return Ty::new_error(self, guar); + } + + ty = self.type_of(alias.def_id).instantiate(self, alias.args); + depth += 1; + } + + ty + } } struct OpaqueTypeExpander<'tcx> { @@ -1002,6 +1060,42 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> { } } +struct WeakAliasTypeExpander<'tcx> { + tcx: TyCtxt<'tcx>, + depth: usize, +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for WeakAliasTypeExpander<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !ty.has_type_flags(ty::TypeFlags::HAS_TY_WEAK) { + return ty; + } + let ty::Alias(ty::Weak, alias) = ty.kind() else { + return ty.super_fold_with(self); + }; + if !self.tcx.recursion_limit().value_within_limit(self.depth) { + let guar = self.tcx.dcx().delayed_bug("overflow expanding weak alias type"); + return Ty::new_error(self.tcx, guar); + } + + self.depth += 1; + ensure_sufficient_stack(|| { + self.tcx.type_of(alias.def_id).instantiate(self.tcx, alias.args).fold_with(self) + }) + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if !ct.ty().has_type_flags(ty::TypeFlags::HAS_TY_WEAK) { + return ct; + } + ct.super_fold_with(self) + } +} + impl<'tcx> Ty<'tcx> { /// Returns the `Size` for primitive types (bool, uint, int, char, float). pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size { diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 59292a281ed..59d09c3dc78 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -2,6 +2,7 @@ use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sso::SsoHashSet; +use rustc_type_ir::fold::TypeFoldable; use std::ops::ControlFlow; pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; @@ -109,10 +110,10 @@ impl<'tcx> TyCtxt<'tcx> { /// variables will also be equated. pub fn collect_constrained_late_bound_regions<T>( self, - value: &Binder<'tcx, T>, + value: Binder<'tcx, T>, ) -> FxHashSet<ty::BoundRegionKind> where - T: TypeVisitable<TyCtxt<'tcx>>, + T: TypeFoldable<TyCtxt<'tcx>>, { self.collect_late_bound_regions(value, true) } @@ -120,24 +121,26 @@ impl<'tcx> TyCtxt<'tcx> { /// 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>, + value: Binder<'tcx, T>, ) -> FxHashSet<ty::BoundRegionKind> where - T: TypeVisitable<TyCtxt<'tcx>>, + T: TypeFoldable<TyCtxt<'tcx>>, { self.collect_late_bound_regions(value, false) } fn collect_late_bound_regions<T>( self, - value: &Binder<'tcx, T>, - just_constraint: bool, + value: Binder<'tcx, T>, + just_constrained: bool, ) -> FxHashSet<ty::BoundRegionKind> where - T: TypeVisitable<TyCtxt<'tcx>>, + T: TypeFoldable<TyCtxt<'tcx>>, { - let mut collector = LateBoundRegionsCollector::new(just_constraint); - let result = value.as_ref().skip_binder().visit_with(&mut collector); + let mut collector = LateBoundRegionsCollector::new(just_constrained); + let value = value.skip_binder(); + let value = if just_constrained { self.expand_weak_alias_tys(value) } else { value }; + let result = value.visit_with(&mut collector); assert!(result.is_continue()); // should never have stopped early collector.regions } @@ -258,11 +261,7 @@ struct LateBoundRegionsCollector { impl LateBoundRegionsCollector { fn new(just_constrained: bool) -> Self { - LateBoundRegionsCollector { - current_index: ty::INNERMOST, - regions: Default::default(), - just_constrained, - } + Self { current_index: ty::INNERMOST, regions: Default::default(), just_constrained } } } @@ -278,12 +277,16 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector { } 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::Alias(..) = t.kind() { - return ControlFlow::Continue(()); + match t.kind() { + // If we are only looking for "constrained" regions, we have to ignore the + // inputs to a projection as they may not appear in the normalized form. + ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) => { + return ControlFlow::Continue(()); + } + // All weak alias types should've been expanded beforehand. + ty::Alias(ty::Weak, _) => bug!("unexpected weak alias type"), + _ => {} } } diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs index 7c3d2671d59..a557f61b016 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -110,15 +110,12 @@ fn lit_to_mir_constant<'tcx>( let LitToConstInput { lit, ty, neg } = lit_input; let trunc = |n| { let param_ty = ty::ParamEnv::reveal_all().and(ty); - let width = - tcx.layout_of(param_ty) - .map_err(|_| { - LitToConstError::Reported(tcx.dcx().delayed_bug(format!( - "couldn't compute width of literal: {:?}", - lit_input.lit - ))) - })? - .size; + let width = match tcx.layout_of(param_ty) { + Ok(layout) => layout.size, + Err(_) => { + tcx.dcx().bug(format!("couldn't compute width of literal: {:?}", lit_input.lit)) + } + }; trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); let result = width.truncate(n); trace!("trunc result: {}", result); diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 35f5a6bfac5..88a5eae281b 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -22,8 +22,6 @@ use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty}; use rustc_span::symbol::Symbol; use rustc_span::{BytePos, Pos, Span}; use rustc_target::abi::VariantIdx; -use smallvec::{smallvec, SmallVec}; - // helper functions, broken out by category: mod simplify; mod test; @@ -949,12 +947,16 @@ struct Candidate<'pat, 'tcx> { has_guard: bool, /// All of these must be satisfied... - match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>, + // Invariant: all the `MatchPair`s are recursively simplified. + // Invariant: or-patterns must be sorted at the end. + match_pairs: Vec<MatchPair<'pat, 'tcx>>, /// ...these bindings established... + // Invariant: not mutated outside `Candidate::new()`. bindings: Vec<Binding<'tcx>>, /// ...and these types asserted... + // Invariant: not mutated outside `Candidate::new()`. ascriptions: Vec<Ascription<'tcx>>, /// ...and if this is non-empty, one of these subcandidates also has to match... @@ -974,19 +976,27 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, has_guard: bool, - cx: &Builder<'_, 'tcx>, + cx: &mut Builder<'_, 'tcx>, ) -> Self { - Candidate { + let mut candidate = Candidate { span: pattern.span, has_guard, - match_pairs: smallvec![MatchPair::new(place, pattern, cx)], + match_pairs: vec![MatchPair::new(place, pattern, cx)], bindings: Vec::new(), ascriptions: Vec::new(), subcandidates: Vec::new(), otherwise_block: None, pre_binding_block: None, next_candidate_pre_binding_block: None, - } + }; + + cx.simplify_match_pairs( + &mut candidate.match_pairs, + &mut candidate.bindings, + &mut candidate.ascriptions, + ); + + candidate } /// Visit the leaf candidates (those with no subcandidates) contained in @@ -1042,13 +1052,18 @@ struct Ascription<'tcx> { variance: ty::Variance, } -#[derive(Clone, Debug)] +#[derive(Debug, Clone)] pub(crate) struct MatchPair<'pat, 'tcx> { - // this place... + // This place... place: PlaceBuilder<'tcx>, // ... must match this pattern. + // Invariant: after creation and simplification in `Candidate::new()`, all match pairs must be + // simplified, i.e. require a test. pattern: &'pat Pat<'tcx>, + + /// Precomputed sub-match pairs of `pattern`. + subpairs: Vec<Self>, } /// See [`Test`] for more. @@ -1165,12 +1180,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidates: &mut [&mut Candidate<'pat, 'tcx>], fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) { - // Start by simplifying candidates. Once this process is complete, all - // the match pairs which remain require some form of test, whether it - // be a switch or pattern comparison. let mut split_or_candidate = false; for candidate in &mut *candidates { - split_or_candidate |= self.simplify_candidate(candidate); + if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place, .. }] = + &*candidate.match_pairs + { + // Split a candidate in which the only match-pair is an or-pattern into multiple + // candidates. This is so that + // + // match x { + // 0 | 1 => { ... }, + // 2 | 3 => { ... }, + // } + // + // only generates a single switch. + candidate.subcandidates = + self.create_or_subcandidates(place, pats, candidate.has_guard); + candidate.match_pairs.pop(); + split_or_candidate = true; + } } ensure_sufficient_stack(|| { @@ -1207,53 +1235,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, span: Span, scrutinee_span: Span, - start_block: BasicBlock, + mut start_block: BasicBlock, otherwise_block: BasicBlock, candidates: &mut [&mut Candidate<'_, 'tcx>], fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) { - // The candidates are sorted by priority. Check to see whether the - // higher priority candidates (and hence at the front of the slice) - // have satisfied all their match pairs. - let fully_matched = candidates.iter().take_while(|c| c.match_pairs.is_empty()).count(); - debug!("match_candidates: {:?} candidates fully matched", fully_matched); - let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched); - - let block = if !matched_candidates.is_empty() { - let otherwise_block = - self.select_matched_candidates(matched_candidates, start_block, fake_borrows); - - if let Some(last_otherwise_block) = otherwise_block { - last_otherwise_block - } else { - // Any remaining candidates are unreachable. - if unmatched_candidates.is_empty() { - return; - } - self.cfg.start_new_block() + match candidates { + [] => { + // If there are no candidates that still need testing, we're done. Since all matches are + // exhaustive, execution should never reach this point. + let source_info = self.source_info(span); + self.cfg.goto(start_block, source_info, otherwise_block); + } + [first, remaining @ ..] if first.match_pairs.is_empty() => { + // The first candidate has satisfied all its match pairs; we link it up and continue + // with the remaining candidates. + start_block = self.select_matched_candidate(first, start_block, fake_borrows); + self.match_simplified_candidates( + span, + scrutinee_span, + start_block, + otherwise_block, + remaining, + fake_borrows, + ) + } + candidates => { + // The first candidate has some unsatisfied match pairs; we proceed to do more tests. + self.test_candidates_with_or( + span, + scrutinee_span, + candidates, + start_block, + otherwise_block, + fake_borrows, + ); } - } else { - start_block - }; - - // If there are no candidates that still need testing, we're - // done. Since all matches are exhaustive, execution should - // never reach this point. - if unmatched_candidates.is_empty() { - let source_info = self.source_info(span); - self.cfg.goto(block, source_info, otherwise_block); - return; } - - // Test for the remaining candidates. - self.test_candidates_with_or( - span, - scrutinee_span, - unmatched_candidates, - block, - otherwise_block, - fake_borrows, - ); } /// Link up matched candidates. @@ -1275,47 +1293,40 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// * the [otherwise block] of the third pattern to a block with an /// [`Unreachable` terminator](TerminatorKind::Unreachable). /// - /// In addition, we add fake edges from the otherwise blocks to the + /// In addition, we later add fake edges from the otherwise blocks to the /// pre-binding block of the next candidate in the original set of /// candidates. /// /// [pre-binding block]: Candidate::pre_binding_block /// [otherwise block]: Candidate::otherwise_block - fn select_matched_candidates( + fn select_matched_candidate( &mut self, - matched_candidates: &mut [&mut Candidate<'_, 'tcx>], + candidate: &mut Candidate<'_, 'tcx>, start_block: BasicBlock, fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, - ) -> Option<BasicBlock> { - debug_assert!( - !matched_candidates.is_empty(), - "select_matched_candidates called with no candidates", - ); - debug_assert!( - matched_candidates.iter().all(|c| c.subcandidates.is_empty()), - "subcandidates should be empty in select_matched_candidates", - ); + ) -> BasicBlock { + assert!(candidate.otherwise_block.is_none()); + assert!(candidate.pre_binding_block.is_none()); + assert!(candidate.subcandidates.is_empty()); - // Insert a borrows of prefixes of places that are bound and are - // behind a dereference projection. - // - // These borrows are taken to avoid situations like the following: - // - // match x[10] { - // _ if { x = &[0]; false } => (), - // y => (), // Out of bounds array access! - // } - // - // match *x { - // // y is bound by reference in the guard and then by copy in the - // // arm, so y is 2 in the arm! - // y if { y == 1 && (x = &2) == () } => y, - // _ => 3, - // } if let Some(fake_borrows) = fake_borrows { - for Binding { source, .. } in - matched_candidates.iter().flat_map(|candidate| &candidate.bindings) - { + // Insert a borrows of prefixes of places that are bound and are + // behind a dereference projection. + // + // These borrows are taken to avoid situations like the following: + // + // match x[10] { + // _ if { x = &[0]; false } => (), + // y => (), // Out of bounds array access! + // } + // + // match *x { + // // y is bound by reference in the guard and then by copy in the + // // arm, so y is 2 in the arm! + // y if { y == 1 && (x = &2) == () } => y, + // _ => 3, + // } + for Binding { source, .. } in &candidate.bindings { if let Some(i) = source.projection.iter().rposition(|elem| elem == ProjectionElem::Deref) { @@ -1329,38 +1340,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - let fully_matched_with_guard = matched_candidates - .iter() - .position(|c| !c.has_guard) - .unwrap_or(matched_candidates.len() - 1); - - let (reachable_candidates, unreachable_candidates) = - matched_candidates.split_at_mut(fully_matched_with_guard + 1); - - let mut next_prebinding = start_block; - - for candidate in reachable_candidates.iter_mut() { - assert!(candidate.otherwise_block.is_none()); - assert!(candidate.pre_binding_block.is_none()); - candidate.pre_binding_block = Some(next_prebinding); - if candidate.has_guard { - // Create the otherwise block for this candidate, which is the - // pre-binding block for the next candidate. - next_prebinding = self.cfg.start_new_block(); - candidate.otherwise_block = Some(next_prebinding); - } - } - - debug!( - "match_candidates: add pre_binding_blocks for unreachable {:?}", - unreachable_candidates, - ); - for candidate in unreachable_candidates { - assert!(candidate.pre_binding_block.is_none()); - candidate.pre_binding_block = Some(self.cfg.start_new_block()); + candidate.pre_binding_block = Some(start_block); + let otherwise_block = self.cfg.start_new_block(); + if candidate.has_guard { + // Create the otherwise block for this candidate, which is the + // pre-binding block for the next candidate. + candidate.otherwise_block = Some(otherwise_block); } - - reachable_candidates.last_mut().unwrap().otherwise_block + otherwise_block } /// Tests a candidate where there are only or-patterns left to test, or @@ -1421,51 +1408,66 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, scrutinee_span: Span, candidates: &mut [&mut Candidate<'_, 'tcx>], - block: BasicBlock, + start_block: BasicBlock, otherwise_block: BasicBlock, fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) { let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap(); - - // All of the or-patterns have been sorted to the end, so if the first - // pattern is an or-pattern we only have or-patterns. - match first_candidate.match_pairs[0].pattern.kind { - PatKind::Or { .. } => (), - _ => { - self.test_candidates( - span, - scrutinee_span, - candidates, - block, - otherwise_block, - fake_borrows, - ); - return; - } + assert!(first_candidate.subcandidates.is_empty()); + if !matches!(first_candidate.match_pairs[0].pattern.kind, PatKind::Or { .. }) { + self.test_candidates( + span, + scrutinee_span, + candidates, + start_block, + otherwise_block, + fake_borrows, + ); + return; } let match_pairs = mem::take(&mut first_candidate.match_pairs); - first_candidate.pre_binding_block = Some(block); + let (first_match_pair, remaining_match_pairs) = match_pairs.split_first().unwrap(); + let PatKind::Or { ref pats } = &first_match_pair.pattern.kind else { unreachable!() }; let remainder_start = self.cfg.start_new_block(); - for match_pair in match_pairs { - let PatKind::Or { ref pats } = &match_pair.pattern.kind else { - bug!("Or-patterns should have been sorted to the end"); - }; - let or_span = match_pair.pattern.span; + let or_span = first_match_pair.pattern.span; + // Test the alternatives of this or-pattern. + self.test_or_pattern( + first_candidate, + start_block, + remainder_start, + pats, + or_span, + &first_match_pair.place, + fake_borrows, + ); + if !remaining_match_pairs.is_empty() { + // If more match pairs remain, test them after each subcandidate. + // We could add them to the or-candidates before the call to `test_or_pattern` but this + // would make it impossible to detect simplifiable or-patterns. That would guarantee + // exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`. first_candidate.visit_leaves(|leaf_candidate| { - self.test_or_pattern( - leaf_candidate, - remainder_start, - pats, - or_span, - &match_pair.place, + assert!(leaf_candidate.match_pairs.is_empty()); + leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned()); + let or_start = leaf_candidate.pre_binding_block.unwrap(); + // In a case like `(a | b, c | d)`, if `a` succeeds and `c | d` fails, we know `(b, + // c | d)` will fail too. If there is no guard, we skip testing of `b` by branching + // directly to `remainder_start`. If there is a guard, we have to try `(b, c | d)`. + let or_otherwise = leaf_candidate.otherwise_block.unwrap_or(remainder_start); + self.test_candidates_with_or( + span, + scrutinee_span, + &mut [leaf_candidate], + or_start, + or_otherwise, fake_borrows, ); }); } + // Test the remaining candidates. self.match_candidates( span, scrutinee_span, @@ -1473,17 +1475,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise_block, remaining_candidates, fake_borrows, - ) + ); } #[instrument( - skip(self, otherwise, or_span, place, fake_borrows, candidate, pats), + skip(self, start_block, otherwise_block, or_span, place, fake_borrows, candidate, pats), level = "debug" )] fn test_or_pattern<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, - otherwise: BasicBlock, + start_block: BasicBlock, + otherwise_block: BasicBlock, pats: &'pat [Box<Pat<'tcx>>], or_span: Span, place: &PlaceBuilder<'tcx>, @@ -1495,16 +1498,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard, self)) .collect(); let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect(); - let otherwise = if let Some(otherwise_block) = candidate.otherwise_block { - otherwise_block - } else { - otherwise - }; self.match_candidates( or_span, or_span, - candidate.pre_binding_block.unwrap(), - otherwise, + start_block, + otherwise_block, &mut or_candidate_refs, fake_borrows, ); diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index dba8e8b6499..83922dce327 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -6,7 +6,7 @@ //! - `place @ (P1, P2)` can be simplified to `[place.0 @ P1, place.1 @ P2]` //! - `place @ x` can be simplified to `[]` by binding `x` to `place` //! -//! The `simplify_candidate` routine just repeatedly applies these +//! The `simplify_match_pairs` routine just repeatedly applies these //! sort of simplifications until there is nothing left to //! simplify. Match pairs cannot be simplified if they require some //! sort of test: for example, testing which variant an enum is, or @@ -22,25 +22,15 @@ use rustc_middle::ty; use std::mem; impl<'a, 'tcx> Builder<'a, 'tcx> { - /// Simplify a candidate so that all match pairs require a test. - /// - /// This method will also split a candidate, in which the only - /// match-pair is an or-pattern, into multiple candidates. - /// This is so that - /// - /// match x { - /// 0 | 1 => { ... }, - /// 2 | 3 => { ... }, - /// } - /// - /// only generates a single switch. If this happens this method returns - /// `true`. - #[instrument(skip(self, candidate), level = "debug")] - pub(super) fn simplify_candidate<'pat>( + /// Simplify a list of match pairs so they all require a test. Stores relevant bindings and + /// ascriptions in the provided `Vec`s. + #[instrument(skip(self), level = "debug")] + pub(super) fn simplify_match_pairs<'pat>( &mut self, - candidate: &mut Candidate<'pat, 'tcx>, - ) -> bool { - debug!("{candidate:#?}"); + match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>, + candidate_bindings: &mut Vec<Binding<'tcx>>, + candidate_ascriptions: &mut Vec<Ascription<'tcx>>, + ) { // In order to please the borrow checker, in a pattern like `x @ pat` we must lower the // bindings in `pat` before `x`. E.g. (#69971): // @@ -68,105 +58,96 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // bindings in iter 2: [6, 7] // // final bindings: [6, 7, 4, 5, 1, 2, 3] - let mut accumulated_bindings = mem::take(&mut candidate.bindings); - // Repeatedly simplify match pairs until fixed point is reached + let mut accumulated_bindings = mem::take(candidate_bindings); + let mut simplified_match_pairs = Vec::new(); + // Repeatedly simplify match pairs until we're left with only unsimplifiable ones. loop { - let mut changed = false; - for match_pair in mem::take(&mut candidate.match_pairs) { - match self.simplify_match_pair(match_pair, candidate) { - Ok(()) => { - changed = true; - } - Err(match_pair) => { - candidate.match_pairs.push(match_pair); - } + for match_pair in mem::take(match_pairs) { + if let Err(match_pair) = self.simplify_match_pair( + match_pair, + candidate_bindings, + candidate_ascriptions, + match_pairs, + ) { + simplified_match_pairs.push(match_pair); } } // This does: accumulated_bindings = candidate.bindings.take() ++ accumulated_bindings - candidate.bindings.extend_from_slice(&accumulated_bindings); - mem::swap(&mut candidate.bindings, &mut accumulated_bindings); - candidate.bindings.clear(); + candidate_bindings.extend_from_slice(&accumulated_bindings); + mem::swap(candidate_bindings, &mut accumulated_bindings); + candidate_bindings.clear(); - if !changed { - // If we were not able to simplify anymore, done. + if match_pairs.is_empty() { break; } } - // Store computed bindings back in `candidate`. - mem::swap(&mut candidate.bindings, &mut accumulated_bindings); - - let did_expand_or = - if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place }] = - &*candidate.match_pairs - { - candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats); - candidate.match_pairs.clear(); - true - } else { - false - }; + // Store computed bindings back in `candidate_bindings`. + mem::swap(candidate_bindings, &mut accumulated_bindings); + // Store simplified match pairs back in `match_pairs`. + mem::swap(match_pairs, &mut simplified_match_pairs); // Move or-patterns to the end, because they can result in us // creating additional candidates, so we want to test them as // late as possible. - candidate.match_pairs.sort_by_key(|pair| matches!(pair.pattern.kind, PatKind::Or { .. })); - debug!(simplified = ?candidate, "simplify_candidate"); - - did_expand_or + match_pairs.sort_by_key(|pair| matches!(pair.pattern.kind, PatKind::Or { .. })); + debug!(simplified = ?match_pairs, "simplify_match_pairs"); } - /// Given `candidate` that has a single or-pattern for its match-pairs, - /// creates a fresh candidate for each of its input subpatterns passed via - /// `pats`. - fn create_or_subcandidates<'pat>( + /// Create a new candidate for each pattern in `pats`, and recursively simplify tje + /// single-or-pattern case. + pub(super) fn create_or_subcandidates<'pat>( &mut self, - candidate: &Candidate<'pat, 'tcx>, place: &PlaceBuilder<'tcx>, pats: &'pat [Box<Pat<'tcx>>], + has_guard: bool, ) -> Vec<Candidate<'pat, 'tcx>> { pats.iter() .map(|box pat| { - let mut candidate = Candidate::new(place.clone(), pat, candidate.has_guard, self); - self.simplify_candidate(&mut candidate); + let mut candidate = Candidate::new(place.clone(), pat, has_guard, self); + if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place, .. }] = + &*candidate.match_pairs + { + candidate.subcandidates = + self.create_or_subcandidates(place, pats, candidate.has_guard); + candidate.match_pairs.pop(); + } candidate }) .collect() } - /// Tries to simplify `match_pair`, returning `Ok(())` if - /// successful. If successful, new match pairs and bindings will - /// have been pushed into the candidate. If no simplification is - /// possible, `Err` is returned and no changes are made to - /// candidate. + /// Tries to simplify `match_pair`, returning `Ok(())` if successful. If successful, new match + /// pairs and bindings will have been pushed into the respective `Vec`s. If no simplification is + /// possible, `Err` is returned. fn simplify_match_pair<'pat>( &mut self, - match_pair: MatchPair<'pat, 'tcx>, - candidate: &mut Candidate<'pat, 'tcx>, + mut match_pair: MatchPair<'pat, 'tcx>, + bindings: &mut Vec<Binding<'tcx>>, + ascriptions: &mut Vec<Ascription<'tcx>>, + match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>, ) -> Result<(), MatchPair<'pat, 'tcx>> { match match_pair.pattern.kind { + PatKind::Leaf { .. } + | PatKind::Deref { .. } + | PatKind::Array { .. } + | PatKind::Never + | PatKind::Wild + | PatKind::Error(_) => {} + PatKind::AscribeUserType { - ref subpattern, ascription: thir::Ascription { ref annotation, variance }, + .. } => { // Apply the type ascription to the value at `match_pair.place` if let Some(source) = match_pair.place.try_to_place(self) { - candidate.ascriptions.push(Ascription { + ascriptions.push(Ascription { annotation: annotation.clone(), source, variance, }); } - - candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern, self)); - - Ok(()) - } - - PatKind::Wild | PatKind::Error(_) => { - // nothing left to do - Ok(()) } PatKind::Binding { @@ -175,35 +156,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { mode, var, ty: _, - ref subpattern, + subpattern: _, is_primary: _, } => { if let Some(source) = match_pair.place.try_to_place(self) { - candidate.bindings.push(Binding { + bindings.push(Binding { span: match_pair.pattern.span, source, var_id: var, binding_mode: mode, }); } - - if let Some(subpattern) = subpattern.as_ref() { - // this is the `x @ P` case; have to keep matching against `P` now - candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern, self)); - } - - Ok(()) - } - - PatKind::Never => { - // A never pattern acts like a load from the place. - // FIXME(never_patterns): load from the place - Ok(()) - } - - PatKind::Constant { .. } => { - // FIXME normalize patterns when possible - Err(match_pair) } PatKind::InlineConstant { subpattern: ref pattern, def } => { @@ -232,42 +195,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span, user_ty: Box::new(user_ty), }; - candidate.ascriptions.push(Ascription { + ascriptions.push(Ascription { annotation, source, variance: ty::Contravariant, }); } - candidate.match_pairs.push(MatchPair::new(match_pair.place, pattern, self)); + } - Ok(()) + PatKind::Constant { .. } => { + // FIXME normalize patterns when possible + return Err(match_pair); } PatKind::Range(ref range) => { - if let Some(true) = range.is_full_range(self.tcx) { - // Irrefutable pattern match. - return Ok(()); + if range.is_full_range(self.tcx) != Some(true) { + return Err(match_pair); } - Err(match_pair) } PatKind::Slice { ref prefix, ref slice, ref suffix } => { - if prefix.is_empty() && slice.is_some() && suffix.is_empty() { - // irrefutable - self.prefix_slice_suffix( - &mut candidate.match_pairs, - &match_pair.place, - prefix, - slice, - suffix, - ); - Ok(()) - } else { - Err(match_pair) + if !(prefix.is_empty() && slice.is_some() && suffix.is_empty()) { + self.simplify_match_pairs(&mut match_pair.subpairs, bindings, ascriptions); + return Err(match_pair); } } - PatKind::Variant { adt_def, args, variant_index, ref subpatterns } => { + PatKind::Variant { adt_def, args, variant_index, subpatterns: _ } => { let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| { i == variant_index || { (self.tcx.features().exhaustive_patterns @@ -279,41 +233,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } }) && (adt_def.did().is_local() || !adt_def.is_variant_list_non_exhaustive()); - if irrefutable { - let place_builder = match_pair.place.downcast(adt_def, variant_index); - candidate - .match_pairs - .extend(self.field_match_pairs(place_builder, subpatterns)); - Ok(()) - } else { - Err(match_pair) + if !irrefutable { + self.simplify_match_pairs(&mut match_pair.subpairs, bindings, ascriptions); + return Err(match_pair); } } - PatKind::Array { ref prefix, ref slice, ref suffix } => { - self.prefix_slice_suffix( - &mut candidate.match_pairs, - &match_pair.place, - prefix, - slice, - suffix, - ); - Ok(()) - } - - PatKind::Leaf { ref subpatterns } => { - // tuple struct, match subpats (if any) - candidate.match_pairs.extend(self.field_match_pairs(match_pair.place, subpatterns)); - Ok(()) - } - - PatKind::Deref { ref subpattern } => { - let place_builder = match_pair.place.deref(); - candidate.match_pairs.push(MatchPair::new(place_builder, subpattern, self)); - Ok(()) - } - - PatKind::Or { .. } => Err(match_pair), + PatKind::Or { .. } => return Err(match_pair), } + + // Simplifiable pattern; we replace it with its subpairs. + match_pairs.append(&mut match_pair.subpairs); + Ok(()) } } diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 990be30b2d6..ae9ebe7170b 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -590,25 +590,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let (match_pair_index, match_pair) = candidate.match_pairs.iter().enumerate().find(|&(_, mp)| mp.place == *test_place)?; - match (&test.kind, &match_pair.pattern.kind) { + let fully_matched; + let ret = match (&test.kind, &match_pair.pattern.kind) { // If we are performing a variant switch, then this // informs variant patterns, but nothing else. ( &TestKind::Switch { adt_def: tested_adt_def, .. }, - &PatKind::Variant { adt_def, variant_index, ref subpatterns, .. }, + &PatKind::Variant { adt_def, variant_index, .. }, ) => { assert_eq!(adt_def, tested_adt_def); - self.candidate_after_variant_switch( - match_pair_index, - adt_def, - variant_index, - subpatterns, - candidate, - ); + fully_matched = true; Some(variant_index.as_usize()) } - - (&TestKind::Switch { .. }, _) => None, + (&TestKind::Switch { .. }, _) => { + fully_matched = false; + None + } // If we are performing a switch over integers, then this informs integer // equality, but nothing else. @@ -618,12 +615,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (TestKind::SwitchInt { switch_ty: _, options }, PatKind::Constant { value }) if is_switch_ty(match_pair.pattern.ty) => { + fully_matched = true; let index = options.get_index_of(value).unwrap(); - self.candidate_without_match_pair(match_pair_index, candidate); Some(index) } - (TestKind::SwitchInt { switch_ty: _, options }, PatKind::Range(range)) => { + fully_matched = false; let not_contained = self.values_not_contained_in_range(&*range, options).unwrap_or(false); @@ -633,8 +630,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { options.len() }) } - - (&TestKind::SwitchInt { .. }, _) => None, + (&TestKind::SwitchInt { .. }, _) => { + fully_matched = false; + None + } ( &TestKind::Len { len: test_len, op: BinOp::Eq }, @@ -645,34 +644,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (Ordering::Equal, &None) => { // on true, min_len = len = $actual_length, // on false, len != $actual_length - self.candidate_after_slice_test( - match_pair_index, - candidate, - prefix, - slice, - suffix, - ); + fully_matched = true; Some(0) } (Ordering::Less, _) => { // test_len < pat_len. If $actual_len = test_len, // then $actual_len < pat_len and we don't have // enough elements. + fully_matched = false; Some(1) } (Ordering::Equal | Ordering::Greater, &Some(_)) => { // This can match both if $actual_len = test_len >= pat_len, // and if $actual_len > test_len. We can't advance. + fully_matched = false; None } (Ordering::Greater, &None) => { // test_len != pat_len, so if $actual_len = test_len, then // $actual_len != pat_len. + fully_matched = false; Some(1) } } } - ( &TestKind::Len { len: test_len, op: BinOp::Ge }, PatKind::Slice { prefix, slice, suffix }, @@ -683,29 +678,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (Ordering::Equal, &Some(_)) => { // $actual_len >= test_len = pat_len, // so we can match. - self.candidate_after_slice_test( - match_pair_index, - candidate, - prefix, - slice, - suffix, - ); + fully_matched = true; Some(0) } (Ordering::Less, _) | (Ordering::Equal, &None) => { // test_len <= pat_len. If $actual_len < test_len, // then it is also < pat_len, so the test passing is // necessary (but insufficient). + fully_matched = false; Some(0) } (Ordering::Greater, &None) => { // test_len > pat_len. If $actual_len >= test_len > pat_len, // then we know we won't have a match. + fully_matched = false; Some(1) } (Ordering::Greater, &Some(_)) => { // test_len < pat_len, and is therefore less // strict. This can still go both ways. + fully_matched = false; None } } @@ -713,16 +705,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (TestKind::Range(test), PatKind::Range(pat)) => { if test == pat { - self.candidate_without_match_pair(match_pair_index, candidate); - return Some(0); + fully_matched = true; + Some(0) + } else { + fully_matched = false; + // If the testing range does not overlap with pattern range, + // the pattern can be matched only if this test fails. + if !test.overlaps(pat, self.tcx, self.param_env)? { Some(1) } else { None } } - - // If the testing range does not overlap with pattern range, - // the pattern can be matched only if this test fails. - if !test.overlaps(pat, self.tcx, self.param_env)? { Some(1) } else { None } } - (TestKind::Range(range), &PatKind::Constant { value }) => { + fully_matched = false; if !range.contains(value, self.tcx, self.param_env)? { // `value` is not contained in the testing range, // so `value` can be matched only if this test fails. @@ -731,8 +724,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None } } - - (&TestKind::Range { .. }, _) => None, + (&TestKind::Range { .. }, _) => { + fully_matched = false; + None + } (&TestKind::Eq { .. } | &TestKind::Len { .. }, _) => { // The call to `self.test(&match_pair)` below is not actually used to generate any @@ -751,64 +746,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // FIXME(#29623) we can be more clever here let pattern_test = self.test(match_pair); if pattern_test.kind == test.kind { - self.candidate_without_match_pair(match_pair_index, candidate); + fully_matched = true; Some(0) } else { + fully_matched = false; None } } - } - } - - fn candidate_without_match_pair( - &mut self, - match_pair_index: usize, - candidate: &mut Candidate<'_, 'tcx>, - ) { - candidate.match_pairs.remove(match_pair_index); - } + }; - fn candidate_after_slice_test<'pat>( - &mut self, - match_pair_index: usize, - candidate: &mut Candidate<'pat, 'tcx>, - prefix: &'pat [Box<Pat<'tcx>>], - opt_slice: &'pat Option<Box<Pat<'tcx>>>, - suffix: &'pat [Box<Pat<'tcx>>], - ) { - let removed_place = candidate.match_pairs.remove(match_pair_index).place; - self.prefix_slice_suffix( - &mut candidate.match_pairs, - &removed_place, - prefix, - opt_slice, - suffix, - ); - } + if fully_matched { + // Replace the match pair by its sub-pairs. + let match_pair = candidate.match_pairs.remove(match_pair_index); + candidate.match_pairs.extend(match_pair.subpairs); + // Move or-patterns to the end. + candidate + .match_pairs + .sort_by_key(|pair| matches!(pair.pattern.kind, PatKind::Or { .. })); + } - fn candidate_after_variant_switch<'pat>( - &mut self, - match_pair_index: usize, - adt_def: ty::AdtDef<'tcx>, - variant_index: VariantIdx, - subpatterns: &'pat [FieldPat<'tcx>], - candidate: &mut Candidate<'pat, 'tcx>, - ) { - let match_pair = candidate.match_pairs.remove(match_pair_index); - - // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`, - // we want to create a set of derived match-patterns like - // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`. - let downcast_place = match_pair.place.downcast(adt_def, variant_index); // `(x as Variant)` - let consequent_match_pairs = subpatterns.iter().map(|subpattern| { - // e.g., `(x as Variant).0` - let place = downcast_place - .clone_project(PlaceElem::Field(subpattern.field, subpattern.pattern.ty)); - // e.g., `(x as Variant).0 @ P1` - MatchPair::new(place, &subpattern.pattern, self) - }); - - candidate.match_pairs.extend(consequent_match_pairs); + ret } fn error_simplifiable<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> ! { diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index c34105174ef..e42d764147c 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -6,7 +6,6 @@ use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty; use rustc_middle::ty::TypeVisitableExt; -use smallvec::SmallVec; impl<'a, 'tcx> Builder<'a, 'tcx> { pub(crate) fn field_match_pairs<'pat>( @@ -26,7 +25,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub(crate) fn prefix_slice_suffix<'pat>( &mut self, - match_pairs: &mut SmallVec<[MatchPair<'pat, 'tcx>; 1]>, + match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>, place: &PlaceBuilder<'tcx>, prefix: &'pat [Box<Pat<'tcx>>], opt_slice: &'pat Option<Box<Pat<'tcx>>>, @@ -97,7 +96,7 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { pub(in crate::build) fn new( mut place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, - cx: &Builder<'_, 'tcx>, + cx: &mut Builder<'_, 'tcx>, ) -> MatchPair<'pat, 'tcx> { // Force the place type to the pattern's type. // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks? @@ -117,6 +116,51 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { if may_need_cast { place = place.project(ProjectionElem::OpaqueCast(pattern.ty)); } - MatchPair { place, pattern } + + let mut subpairs = Vec::new(); + match pattern.kind { + PatKind::Constant { .. } + | PatKind::Range(_) + | PatKind::Or { .. } + | PatKind::Never + | PatKind::Wild + | PatKind::Error(_) => {} + + PatKind::AscribeUserType { ref subpattern, .. } => { + subpairs.push(MatchPair::new(place.clone(), subpattern, cx)); + } + + PatKind::Binding { ref subpattern, .. } => { + if let Some(subpattern) = subpattern.as_ref() { + // this is the `x @ P` case; have to keep matching against `P` now + subpairs.push(MatchPair::new(place.clone(), subpattern, cx)); + } + } + + PatKind::InlineConstant { subpattern: ref pattern, .. } => { + subpairs.push(MatchPair::new(place.clone(), pattern, cx)); + } + + PatKind::Slice { ref prefix, ref slice, ref suffix } + | PatKind::Array { ref prefix, ref slice, ref suffix } => { + cx.prefix_slice_suffix(&mut subpairs, &place, prefix, slice, suffix); + } + + PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { + let downcast_place = place.clone().downcast(adt_def, variant_index); // `(x as Variant)` + subpairs = cx.field_match_pairs(downcast_place, subpatterns); + } + + PatKind::Leaf { ref subpatterns } => { + subpairs = cx.field_match_pairs(place.clone(), subpatterns); + } + + PatKind::Deref { ref subpattern } => { + let place_builder = place.clone().deref(); + subpairs.push(MatchPair::new(place_builder, subpattern, cx)); + } + } + + MatchPair { place, pattern, subpairs } } } diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 0bc5fe6ef89..2a42dae289b 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1,7 +1,7 @@ use crate::fluent_generated as fluent; use rustc_errors::DiagnosticArgValue; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, + codes::*, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic, Level, MultiSpan, SubdiagnosticMessageOp, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; @@ -420,7 +420,11 @@ pub struct UnsafeNotInheritedLintNote { } impl AddToDiagnostic for UnsafeNotInheritedLintNote { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { diag.span_note(self.signature_span, fluent::mir_build_unsafe_fn_safe_body); let body_start = self.body_span.shrink_to_lo(); let body_end = self.body_span.shrink_to_hi(); @@ -863,7 +867,11 @@ pub struct Variant { } impl<'tcx> AddToDiagnostic for AdtDefinedHere<'tcx> { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { diag.arg("ty", self.ty); let mut spans = MultiSpan::from(self.adt_def_span); diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index d444de8b28e..65cc13286af 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -12,15 +12,12 @@ pub(crate) fn lit_to_const<'tcx>( let trunc = |n| { let param_ty = ParamEnv::reveal_all().and(ty); - let width = - tcx.layout_of(param_ty) - .map_err(|_| { - LitToConstError::Reported(tcx.dcx().delayed_bug(format!( - "couldn't compute width of literal: {:?}", - lit_input.lit - ))) - })? - .size; + let width = match tcx.layout_of(param_ty) { + Ok(layout) => layout.size, + Err(_) => { + tcx.dcx().bug(format!("couldn't compute width of literal: {:?}", lit_input.lit)) + } + }; trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); let result = width.truncate(n); trace!("trunc result: {}", result); @@ -59,15 +56,11 @@ pub(crate) fn lit_to_const<'tcx>( } (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()), (ast::LitKind::Float(n, _), ty::Float(fty)) => { - let bits = - parse_float_into_scalar(*n, *fty, neg) - .ok_or_else(|| { - LitToConstError::Reported(tcx.dcx().delayed_bug(format!( - "couldn't parse float literal: {:?}", - lit_input.lit - ))) - })? - .assert_int(); + let bits = parse_float_into_scalar(*n, *fty, neg) + .ok_or_else(|| { + tcx.dcx().bug(format!("couldn't parse float literal: {:?}", lit_input.lit)) + })? + .assert_int(); ty::ValTree::from_scalar_int(bits) } (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int((*c).into()), diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 1a5cf13e289..0329e1d3096 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -175,7 +175,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { ) -> Result<PatKind<'tcx>, ErrorGuaranteed> { if lo_expr.is_none() && hi_expr.is_none() { let msg = "found twice-open range pattern (`..`) outside of error recovery"; - return Err(self.tcx.dcx().span_delayed_bug(span, msg)); + self.tcx.dcx().span_bug(span, msg); } let (lo, lo_ascr, lo_inline) = self.lower_pattern_range_endpoint(lo_expr)?; diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index a0851aa557b..54b13a40e92 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1615,11 +1615,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { (args.discr_ty(tcx), coroutine_kind.movability() == hir::Movability::Movable) } _ => { - tcx.dcx().span_delayed_bug( - body.span, - format!("unexpected coroutine type {coroutine_ty}"), - ); - return; + tcx.dcx().span_bug(body.span, format!("unexpected coroutine type {coroutine_ty}")); } }; diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 934e77e7deb..98fb1d8e1c9 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -61,7 +61,7 @@ pub(super) fn generate_coverage_spans( hir_info, basic_coverage_blocks, ); - let coverage_spans = SpansRefiner::refine_sorted_spans(basic_coverage_blocks, sorted_spans); + let coverage_spans = SpansRefiner::refine_sorted_spans(sorted_spans); mappings.extend(coverage_spans.into_iter().map(|RefinedCovspan { bcb, span, .. }| { // Each span produced by the generator represents an ordinary code region. BcbMapping { kind: BcbMappingKind::Code(bcb), span } @@ -88,8 +88,6 @@ pub(super) fn generate_coverage_spans( #[derive(Debug)] struct CurrCovspan { - /// This is used as the basis for [`PrevCovspan::original_span`], so it must - /// not be modified. span: Span, bcb: BasicCoverageBlock, is_closure: bool, @@ -102,7 +100,7 @@ impl CurrCovspan { fn into_prev(self) -> PrevCovspan { let Self { span, bcb, is_closure } = self; - PrevCovspan { original_span: span, span, bcb, merged_spans: vec![span], is_closure } + PrevCovspan { span, bcb, merged_spans: vec![span], is_closure } } fn into_refined(self) -> RefinedCovspan { @@ -115,7 +113,6 @@ impl CurrCovspan { #[derive(Debug)] struct PrevCovspan { - original_span: Span, span: Span, bcb: BasicCoverageBlock, /// List of all the original spans from MIR that have been merged into this @@ -135,42 +132,17 @@ impl PrevCovspan { self.merged_spans.push(other.span); } - fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) { + fn cutoff_statements_at(mut self, cutoff_pos: BytePos) -> Option<RefinedCovspan> { self.merged_spans.retain(|span| span.hi() <= cutoff_pos); if let Some(max_hi) = self.merged_spans.iter().map(|span| span.hi()).max() { self.span = self.span.with_hi(max_hi); } - } - - fn into_dup(self) -> DuplicateCovspan { - let Self { original_span, span, bcb, merged_spans: _, is_closure } = self; - // Only unmodified spans end up in `pending_dups`. - debug_assert_eq!(original_span, span); - DuplicateCovspan { span, bcb, is_closure } - } - - fn refined_copy(&self) -> RefinedCovspan { - let &Self { original_span: _, span, bcb, merged_spans: _, is_closure } = self; - RefinedCovspan { span, bcb, is_closure } - } - fn into_refined(self) -> RefinedCovspan { - self.refined_copy() + if self.merged_spans.is_empty() { None } else { Some(self.into_refined()) } } -} - -#[derive(Debug)] -struct DuplicateCovspan { - span: Span, - bcb: BasicCoverageBlock, - is_closure: bool, -} -impl DuplicateCovspan { - /// Returns a copy of this covspan, as a [`RefinedCovspan`]. - /// Should only be called in places that would otherwise clone this covspan. fn refined_copy(&self) -> RefinedCovspan { - let &Self { span, bcb, is_closure } = self; + let &Self { span, bcb, merged_spans: _, is_closure } = self; RefinedCovspan { span, bcb, is_closure } } @@ -205,10 +177,7 @@ impl RefinedCovspan { /// * Merge spans that represent continuous (both in source code and control flow), non-branching /// execution /// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures) -struct SpansRefiner<'a> { - /// The BasicCoverageBlock Control Flow Graph (BCB CFG). - basic_coverage_blocks: &'a CoverageGraph, - +struct SpansRefiner { /// The initial set of coverage spans, sorted by `Span` (`lo` and `hi`) and by relative /// dominance between the `BasicCoverageBlock`s of equal `Span`s. sorted_spans_iter: std::vec::IntoIter<SpanFromMir>, @@ -223,36 +192,22 @@ struct SpansRefiner<'a> { /// If that `curr` was discarded, `prev` retains its value from the previous iteration. some_prev: Option<PrevCovspan>, - /// One or more coverage spans with the same `Span` but different `BasicCoverageBlock`s, and - /// no `BasicCoverageBlock` in this list dominates another `BasicCoverageBlock` in the list. - /// If a new `curr` span also fits this criteria (compared to an existing list of - /// `pending_dups`), that `curr` moves to `prev` before possibly being added to - /// the `pending_dups` list, on the next iteration. As a result, if `prev` and `pending_dups` - /// have the same `Span`, the criteria for `pending_dups` holds for `prev` as well: a `prev` - /// with a matching `Span` does not dominate any `pending_dup` and no `pending_dup` dominates a - /// `prev` with a matching `Span`) - pending_dups: Vec<DuplicateCovspan>, - /// The final coverage spans to add to the coverage map. A `Counter` or `Expression` /// will also be injected into the MIR for each BCB that has associated spans. refined_spans: Vec<RefinedCovspan>, } -impl<'a> SpansRefiner<'a> { +impl SpansRefiner { /// Takes the initial list of (sorted) spans extracted from MIR, and "refines" /// them by merging compatible adjacent spans, removing redundant spans, /// and carving holes in spans when they overlap in unwanted ways. - fn refine_sorted_spans( - basic_coverage_blocks: &'a CoverageGraph, - sorted_spans: Vec<SpanFromMir>, - ) -> Vec<RefinedCovspan> { + fn refine_sorted_spans(sorted_spans: Vec<SpanFromMir>) -> Vec<RefinedCovspan> { + let sorted_spans_len = sorted_spans.len(); let this = Self { - basic_coverage_blocks, sorted_spans_iter: sorted_spans.into_iter(), some_curr: None, some_prev: None, - pending_dups: Vec::new(), - refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2), + refined_spans: Vec::with_capacity(sorted_spans_len), }; this.to_refined_spans() @@ -292,21 +247,11 @@ impl<'a> SpansRefiner<'a> { self.take_curr(); // Discards curr. } else if curr.is_closure { self.carve_out_span_for_closure(); - } else if prev.original_span == prev.span && prev.span == curr.span { - // Prev and curr have the same span, and prev's span hasn't - // been modified by other spans. - self.update_pending_dups(); } else { self.cutoff_prev_at_overlapping_curr(); } } - // Drain any remaining dups into the output. - for dup in self.pending_dups.drain(..) { - debug!(" ...adding at least one pending dup={:?}", dup); - self.refined_spans.push(dup.into_refined()); - } - // There is usually a final span remaining in `prev` after the loop ends, // so add it to the output as well. if let Some(prev) = self.some_prev.take() { @@ -359,36 +304,6 @@ impl<'a> SpansRefiner<'a> { self.some_prev.take().unwrap_or_else(|| bug!("some_prev is None (take_prev)")) } - /// If there are `pending_dups` but `prev` is not a matching dup (`prev.span` doesn't match the - /// `pending_dups` spans), then one of the following two things happened during the previous - /// iteration: - /// * the previous `curr` span (which is now `prev`) was not a duplicate of the pending_dups - /// (in which case there should be at least two spans in `pending_dups`); or - /// * the `span` of `prev` was modified by `curr_mut().merge_from(prev)` (in which case - /// `pending_dups` could have as few as one span) - /// In either case, no more spans will match the span of `pending_dups`, so - /// add the `pending_dups` if they don't overlap `curr`, and clear the list. - fn maybe_flush_pending_dups(&mut self) { - let Some(last_dup) = self.pending_dups.last() else { return }; - if last_dup.span == self.prev().span { - return; - } - - debug!( - " SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \ - previous iteration, or prev started a new disjoint span" - ); - if last_dup.span.hi() <= self.curr().span.lo() { - for dup in self.pending_dups.drain(..) { - debug!(" ...adding at least one pending={:?}", dup); - self.refined_spans.push(dup.into_refined()); - } - } else { - self.pending_dups.clear(); - } - assert!(self.pending_dups.is_empty()); - } - /// Advance `prev` to `curr` (if any), and `curr` to the next coverage span in sorted order. fn next_coverage_span(&mut self) -> bool { if let Some(curr) = self.some_curr.take() { @@ -408,7 +323,6 @@ impl<'a> SpansRefiner<'a> { ); } else { self.some_curr = Some(CurrCovspan::new(curr.span, curr.bcb, curr.is_closure)); - self.maybe_flush_pending_dups(); return true; } } @@ -433,13 +347,6 @@ impl<'a> SpansRefiner<'a> { let mut pre_closure = self.prev().refined_copy(); pre_closure.span = pre_closure.span.with_hi(left_cutoff); debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure); - - for mut dup in self.pending_dups.iter().map(DuplicateCovspan::refined_copy) { - dup.span = dup.span.with_hi(left_cutoff); - debug!(" ...and at least one pre_closure dup={:?}", dup); - self.refined_spans.push(dup); - } - self.refined_spans.push(pre_closure); } @@ -448,58 +355,9 @@ impl<'a> SpansRefiner<'a> { self.prev_mut().span = self.prev().span.with_lo(right_cutoff); debug!(" Mutated prev.span to start after the closure. prev={:?}", self.prev()); - for dup in &mut self.pending_dups { - debug!(" ...and at least one overlapping dup={:?}", dup); - dup.span = dup.span.with_lo(right_cutoff); - } - // Prevent this curr from becoming prev. let closure_covspan = self.take_curr().into_refined(); self.refined_spans.push(closure_covspan); // since self.prev() was already updated - } else { - self.pending_dups.clear(); - } - } - - /// Called if `curr.span` equals `prev.original_span` (and potentially equal to all - /// `pending_dups` spans, if any). Keep in mind, `prev.span()` may have been changed. - /// If prev.span() was merged into other spans (with matching BCB, for instance), - /// `prev.span.hi()` will be greater than (further right of) `prev.original_span.hi()`. - /// If prev.span() was split off to the right of a closure, prev.span().lo() will be - /// greater than prev.original_span.lo(). The actual span of `prev.original_span` is - /// not as important as knowing that `prev()` **used to have the same span** as `curr()`, - /// which means their sort order is still meaningful for determining the dominator - /// relationship. - /// - /// When two coverage spans have the same `Span`, dominated spans can be discarded; but if - /// neither coverage span dominates the other, both (or possibly more than two) are held, - /// until their disposition is determined. In this latter case, the `prev` dup is moved into - /// `pending_dups` so the new `curr` dup can be moved to `prev` for the next iteration. - fn update_pending_dups(&mut self) { - let prev_bcb = self.prev().bcb; - let curr_bcb = self.curr().bcb; - - // Equal coverage spans are ordered by dominators before dominated (if any), so it should be - // impossible for `curr` to dominate any previous coverage span. - debug_assert!(!self.basic_coverage_blocks.dominates(curr_bcb, prev_bcb)); - - // `prev` is a duplicate of `curr`, so add it to the list of pending dups. - // If it dominates `curr`, it will be removed by the subsequent discard step. - let prev = self.take_prev().into_dup(); - debug!(?prev, "adding prev to pending dups"); - self.pending_dups.push(prev); - - let initial_pending_count = self.pending_dups.len(); - if initial_pending_count > 0 { - self.pending_dups - .retain(|dup| !self.basic_coverage_blocks.dominates(dup.bcb, curr_bcb)); - - let n_discarded = initial_pending_count - self.pending_dups.len(); - if n_discarded > 0 { - debug!( - " discarded {n_discarded} of {initial_pending_count} pending_dups that dominated curr", - ); - } } } @@ -516,19 +374,13 @@ impl<'a> SpansRefiner<'a> { if it has statements that end before curr; prev={:?}", self.prev() ); - if self.pending_dups.is_empty() { - let curr_span = self.curr().span; - self.prev_mut().cutoff_statements_at(curr_span.lo()); - if self.prev().merged_spans.is_empty() { - debug!(" ... no non-overlapping statements to add"); - } else { - debug!(" ... adding modified prev={:?}", self.prev()); - let prev = self.take_prev().into_refined(); - self.refined_spans.push(prev); - } + + let curr_span = self.curr().span; + if let Some(prev) = self.take_prev().cutoff_statements_at(curr_span.lo()) { + debug!("after cutoff, adding {prev:?}"); + self.refined_spans.push(prev); } else { - // with `pending_dups`, `prev` cannot have any statements that don't overlap - self.pending_dups.clear(); + debug!("prev was eliminated by cutoff"); } } } diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 2db358379fe..b91ab811918 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -52,14 +52,19 @@ pub(super) fn mir_to_initial_sorted_coverage_spans( // - Span A extends further left, or // - Both have the same start and span A extends further right .then_with(|| Ord::cmp(&a.span.hi(), &b.span.hi()).reverse()) - // If both spans are equal, sort the BCBs in dominator order, - // so that dominating BCBs come before other BCBs they dominate. - .then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb)) - // If two spans are otherwise identical, put closure spans first, - // as this seems to be what the refinement step expects. + // If two spans have the same lo & hi, put closure spans first, + // as they take precedence over non-closure spans. .then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse()) + // After deduplication, we want to keep only the most-dominated BCB. + .then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb).reverse()) }); + // Among covspans with the same span, keep only one. Closure spans take + // precedence, otherwise keep the one with the most-dominated BCB. + // (Ideally we should try to preserve _all_ non-dominating BCBs, but that + // requires a lot more complexity in the span refiner, for little benefit.) + initial_spans.dedup_by(|b, a| a.span.source_equal(b.span)); + initial_spans } diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 8575f552f0a..03d952abad1 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -380,7 +380,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { LookupResult::Parent(None) => {} LookupResult::Parent(Some(_)) => { if !replace { - self.tcx.dcx().span_delayed_bug( + self.tcx.dcx().span_bug( terminator.source_info.span, format!("drop of untracked value {bb:?}"), ); diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index ff4918df9a2..af2d7d4946f 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -87,6 +87,9 @@ pub(crate) struct RequiresUnsafeDetail { } impl RequiresUnsafeDetail { + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] fn add_subdiagnostics<G: EmissionGuarantee>(&self, diag: &mut DiagnosticBuilder<'_, G>) { use UnsafetyViolationDetails::*; match self.violation { diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 6f2ef8f9a4f..a9cd688c315 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -1,5 +1,8 @@ -//! Propagates constants for early reporting of statically known -//! assertion failures +//! A lint that checks for known panics like +//! overflows, division by zero, +//! out-of-bound access etc. +//! Uses const propagation to determine the +//! values of operands during checks. use std::fmt::Debug; @@ -21,9 +24,9 @@ use crate::dataflow_const_prop::DummyMachine; use crate::errors::{AssertLint, AssertLintKind}; use crate::MirLint; -pub struct ConstPropLint; +pub struct KnownPanicsLint; -impl<'tcx> MirLint<'tcx> for ConstPropLint { +impl<'tcx> MirLint<'tcx> for KnownPanicsLint { fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { if body.tainted_by_errors.is_some() { return; @@ -37,31 +40,28 @@ impl<'tcx> MirLint<'tcx> for ConstPropLint { // Only run const prop on functions, methods, closures and associated constants if !is_fn_like && !is_assoc_const { // skip anon_const/statics/consts because they'll be evaluated by miri anyway - trace!("ConstPropLint skipped for {:?}", def_id); + trace!("KnownPanicsLint skipped for {:?}", def_id); return; } // FIXME(welseywiser) const prop doesn't work on coroutines because of query cycles // computing their layout. if tcx.is_coroutine(def_id.to_def_id()) { - trace!("ConstPropLint skipped for coroutine {:?}", def_id); + trace!("KnownPanicsLint skipped for coroutine {:?}", def_id); return; } - trace!("ConstPropLint starting for {:?}", def_id); + trace!("KnownPanicsLint starting for {:?}", def_id); - // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold - // constants, instead of just checking for const-folding succeeding. - // That would require a uniform one-def no-mutation analysis - // and RPO (or recursing when needing the value of a local). let mut linter = ConstPropagator::new(body, tcx); linter.visit_body(body); - trace!("ConstPropLint done for {:?}", def_id); + trace!("KnownPanicsLint done for {:?}", def_id); } } -/// Finds optimization opportunities on the MIR. +/// Visits MIR nodes, performs const propagation +/// and runs lint checks as it goes struct ConstPropagator<'mir, 'tcx> { ecx: InterpCx<'mir, 'tcx, DummyMachine>, tcx: TyCtxt<'tcx>, @@ -238,7 +238,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // dedicated error variants should be introduced instead. assert!( !error.kind().formatted_string(), - "const-prop encountered formatting error: {}", + "known panics lint encountered formatting error: {}", format_interp_error(self.ecx.tcx.dcx(), error), ); None @@ -253,7 +253,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } - // Normalization needed b/c const prop lint runs in + // Normalization needed b/c known panics lint runs in // `mir_drops_elaborated_and_const_checked`, which happens before // optimized MIR. Only after optimizing the MIR can we guarantee // that the `RevealAll` pass has happened and that the body's consts @@ -864,6 +864,8 @@ pub enum ConstPropMode { NoPropagation, } +/// A visitor that determines locals in a MIR body +/// that can be const propagated pub struct CanConstProp { can_const_prop: IndexVec<Local, ConstPropMode>, // False at the beginning. Once set, no more assignments are allowed to that local. diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 74b36eb5ee8..945c3c662a6 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -59,7 +59,6 @@ mod remove_place_mention; mod add_subtyping_projections; pub mod cleanup_post_borrowck; mod const_debuginfo; -mod const_prop_lint; mod copy_prop; mod coroutine; mod cost_checker; @@ -83,6 +82,7 @@ mod gvn; pub mod inline; mod instsimplify; mod jump_threading; +mod known_panics_lint; mod large_enums; mod lint; mod lower_intrinsics; @@ -533,7 +533,7 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &elaborate_box_derefs::ElaborateBoxDerefs, &coroutine::StateTransform, &add_retag::AddRetag, - &Lint(const_prop_lint::ConstPropLint), + &Lint(known_panics_lint::KnownPanicsLint), ]; pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial))); } diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index bd89874b5cc..e4c30679146 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -56,6 +56,7 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for UnusedGenericParamsHint { // FIXME: I can figure out how to do a label with a fluent string with a fixed message, // or a label with a dynamic value in a hard-coded string, but I haven't figured out // how to combine the two. 😢 + #[allow(rustc::untranslatable_diagnostic)] diag.span_label(span, format!("generic parameter `{name}` is unused")); } diag diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 7c2ecf34c17..55baf6f9f2e 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -27,6 +27,8 @@ parse_async_bound_modifier_in_2015 = `async` trait bounds are only allowed in Ru parse_async_fn_in_2015 = `async fn` is not permitted in Rust 2015 .label = to use `async fn`, switch to Rust 2018 or later +parse_async_impl = `async` trait implementations are unsupported + parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 2018 or later parse_async_move_order_incorrect = the order of `move` and `async` is incorrect diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 674f7218ea6..2d4447a42c2 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use rustc_ast::token::Token; use rustc_ast::{Path, Visibility}; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, + codes::*, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic, Level, SubdiagnosticMessageOp, }; use rustc_macros::{Diagnostic, Subdiagnostic}; @@ -1475,7 +1475,11 @@ pub(crate) struct FnTraitMissingParen { } impl AddToDiagnostic for FnTraitMissingParen { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _: F, + ) { diag.span_label(self.span, crate::fluent_generated::parse_fn_trait_missing_paren); let applicability = if self.machine_applicable { Applicability::MachineApplicable @@ -2971,3 +2975,10 @@ pub(crate) struct ArrayIndexInOffsetOf(#[primary_span] pub Span); #[derive(Diagnostic)] #[diag(parse_invalid_offset_of)] pub(crate) struct InvalidOffsetOf(#[primary_span] pub Span); + +#[derive(Diagnostic)] +#[diag(parse_async_impl)] +pub(crate) struct AsyncImpl { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs index b1bd4ac75e5..52a029b20f6 100644 --- a/compiler/rustc_parse/src/lexer/diagnostics.rs +++ b/compiler/rustc_parse/src/lexer/diagnostics.rs @@ -1,6 +1,6 @@ use super::UnmatchedDelim; use rustc_ast::token::Delimiter; -use rustc_errors::Diagnostic; +use rustc_errors::DiagnosticBuilder; use rustc_span::source_map::SourceMap; use rustc_span::Span; @@ -31,7 +31,7 @@ pub fn same_indentation_level(sm: &SourceMap, open_sp: Span, close_sp: Span) -> // When we get a `)` or `]` for `{`, we should emit help message here // it's more friendly compared to report `unmatched error` in later phase pub fn report_missing_open_delim( - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, unmatched_delims: &[UnmatchedDelim], ) -> bool { let mut reported_missing_open = false; @@ -55,7 +55,7 @@ pub fn report_missing_open_delim( } pub fn report_suspicious_mismatch_block( - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, diag_info: &TokenTreeDiagInfo, sm: &SourceMap, delim: Delimiter, diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 98e062dd784..6545429b95b 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -8,7 +8,7 @@ use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle use rustc_ast as ast; use rustc_ast::attr; use rustc_ast::token::{self, Delimiter, Nonterminal}; -use rustc_errors::{codes::*, Diagnostic, PResult}; +use rustc_errors::{codes::*, DiagnosticBuilder, PResult}; use rustc_span::{sym, BytePos, Span}; use thin_vec::ThinVec; use tracing::debug; @@ -141,7 +141,7 @@ impl<'a> Parser<'a> { fn annotate_following_item_if_applicable( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, span: Span, attr_type: OuterAttributeType, ) -> Option<Span> { diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 659716548d9..0cc2170714c 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -34,8 +34,8 @@ use rustc_ast::{ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ - pluralize, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, FatalError, PErr, PResult, + pluralize, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, ErrorGuaranteed, + FatalError, PErr, PResult, }; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; @@ -208,11 +208,11 @@ struct MultiSugg { } impl MultiSugg { - fn emit(self, err: &mut Diagnostic) { + fn emit(self, err: &mut DiagnosticBuilder<'_>) { err.multipart_suggestion(self.msg, self.patches, self.applicability); } - fn emit_verbose(self, err: &mut Diagnostic) { + fn emit_verbose(self, err: &mut DiagnosticBuilder<'_>) { err.multipart_suggestion_verbose(self.msg, self.patches, self.applicability); } } @@ -846,7 +846,7 @@ impl<'a> Parser<'a> { err.emit(); } - fn check_too_many_raw_str_terminators(&mut self, err: &mut Diagnostic) -> bool { + fn check_too_many_raw_str_terminators(&mut self, err: &mut DiagnosticBuilder<'_>) -> bool { let sm = self.sess.source_map(); match (&self.prev_token.kind, &self.token.kind) { ( @@ -2179,7 +2179,7 @@ impl<'a> Parser<'a> { pub(super) fn parameter_without_type( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, pat: P<ast::Pat>, require_name: bool, first_param: bool, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 20b9581f2ef..8826c06bebd 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -25,9 +25,7 @@ use rustc_ast::{Arm, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLim use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::{ - AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, PResult, StashKey, -}; +use rustc_errors::{AddToDiagnostic, Applicability, DiagnosticBuilder, PResult, StashKey}; use rustc_lexer::unescape::unescape_char; use rustc_macros::Subdiagnostic; use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded}; @@ -865,7 +863,7 @@ impl<'a> Parser<'a> { ); let mut err = self.dcx().struct_span_err(span, msg); - let suggest_parens = |err: &mut Diagnostic| { + let suggest_parens = |err: &mut DiagnosticBuilder<'_>| { let suggestions = vec![ (span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), ")".to_string()), @@ -3437,7 +3435,7 @@ impl<'a> Parser<'a> { let mut recover_async = false; let in_if_guard = self.restrictions.contains(Restrictions::IN_IF_GUARD); - let mut async_block_err = |e: &mut Diagnostic, span: Span| { + let mut async_block_err = |e: &mut DiagnosticBuilder<'_>, span: Span| { recover_async = true; errors::AsyncBlockIn2015 { span }.add_to_diagnostic(e); errors::HelpUseLatestEdition::new().add_to_diagnostic(e); diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index e7b9076bd3c..77381ef4626 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -562,6 +562,15 @@ impl<'a> Parser<'a> { self.sess.gated_spans.gate(sym::const_trait_impl, span); } + // Parse stray `impl async Trait` + if (self.token.uninterpolated_span().at_least_rust_2018() + && self.token.is_keyword(kw::Async)) + || self.is_kw_followed_by_ident(kw::Async) + { + self.bump(); + self.dcx().emit_err(errors::AsyncImpl { span: self.prev_token.span }); + } + let polarity = self.parse_polarity(); // Parse both types and traits as a type, then reinterpret if necessary. @@ -592,22 +601,10 @@ impl<'a> Parser<'a> { // We need to report this error after `cfg` expansion for compatibility reasons self.bump(); // `..`, do not add it to expected tokens - // FIXME(nnethercote): AST validation later detects this - // `TyKind::Err` and emits an errors. So why the unchecked - // ErrorGuaranteed? - // - A `span_delayed_bug` doesn't work here, because rustfmt can - // hit this path but then not hit the follow-up path in the AST - // validator that issues the error, which results in ICEs. - // - `TyKind::Dummy` doesn't work, because it ends up reaching HIR - // lowering, which results in ICEs. Changing `TyKind::Dummy` to - // `TyKind::Err` during AST validation might fix that, but that's - // not possible because AST validation doesn't allow mutability. - // - // #121072 will hopefully remove all this special handling of the - // obsolete `impl Trait for ..` and then this can go away. - #[allow(deprecated)] - let guar = rustc_errors::ErrorGuaranteed::unchecked_error_guaranteed(); - Some(self.mk_ty(self.prev_token.span, TyKind::Err(guar))) + // AST validation later detects this `TyKind::Dummy` and emits an + // error. (#121072 will hopefully remove all this special handling + // of the obsolete `impl Trait for ..` and then this can go away.) + Some(self.mk_ty(self.prev_token.span, TyKind::Dummy)) } else if has_for || self.token.can_begin_type() { Some(self.parse_ty()?) } else { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index f79f2a813b2..23a92e6dd3d 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -778,9 +778,10 @@ impl<'a> Parser<'a> { || self.check(&token::Not) || self.check(&token::Question) || self.check(&token::Tilde) - || self.check_keyword(kw::Const) || self.check_keyword(kw::For) || self.check(&token::OpenDelim(Delimiter::Parenthesis)) + || self.check_keyword(kw::Const) + || self.check_keyword(kw::Async) } /// Parses a bound according to the grammar: @@ -882,11 +883,13 @@ impl<'a> Parser<'a> { BoundConstness::Never }; - let asyncness = if self.token.span.at_least_rust_2018() && self.eat_keyword(kw::Async) { + let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() + && self.eat_keyword(kw::Async) + { self.sess.gated_spans.gate(sym::async_closure, self.prev_token.span); BoundAsyncness::Async(self.prev_token.span) } else if self.may_recover() - && self.token.span.is_rust_2015() + && self.token.uninterpolated_span().is_rust_2015() && self.is_kw_followed_by_ident(kw::Async) { self.bump(); // eat `async` diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 3676eb92a3f..02792491f5e 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -156,9 +156,12 @@ impl<'tcx> CheckConstVisitor<'tcx> { // is a pretty narrow case, however. if tcx.sess.is_nightly_build() { for gate in missing_secondary { - let note = - format!("add `#![feature({gate})]` to the crate attributes to enable",); - err.help(note); + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] + err.help(format!( + "add `#![feature({gate})]` to the crate attributes to enable" + )); } } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 486396b0677..a3106856a67 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -237,7 +237,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { ty::Adt(adt, _) => adt.variant_of_res(res), - _ => span_bug!(lhs.span, "non-ADT in tuple struct pattern"), + _ => { + self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern"); + return; + } }; let dotdot = dotdot.as_opt_usize().unwrap_or(pats.len()); let first_n = pats.iter().enumerate().take(dotdot); diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index a732bdbca51..982def54d30 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -6,9 +6,8 @@ use std::{ use crate::fluent_generated as fluent; use rustc_ast::Label; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, - DiagnosticSymbolList, EmissionGuarantee, IntoDiagnostic, Level, MultiSpan, - SubdiagnosticMessageOp, + codes::*, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, DiagnosticSymbolList, + EmissionGuarantee, IntoDiagnostic, Level, MultiSpan, SubdiagnosticMessageOp, }; use rustc_hir::{self as hir, ExprKind, Target}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; @@ -1754,7 +1753,11 @@ pub struct UnusedVariableStringInterp { } impl AddToDiagnostic for UnusedVariableStringInterp { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { diag.span_label(self.lit, crate::fluent_generated::passes_maybe_string_interpolation); diag.multipart_suggestion( crate::fluent_generated::passes_string_interpolation_only_works, diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 9a21397789d..d3e3e183845 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -250,7 +250,7 @@ fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems { let mut collector = LanguageItemCollector::new(tcx, resolver); // Collect lang items in other crates. - for &cnum in tcx.crates(()).iter() { + for &cnum in tcx.used_crates(()).iter() { for &(def_id, lang_item) in tcx.defined_lang_items(cnum).iter() { collector.collect_item(lang_item, def_id, None); } diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 3a8dc377520..487407014d1 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -526,8 +526,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } fn define_bindings_in_pat(&mut self, pat: &hir::Pat<'_>, mut succ: LiveNode) -> LiveNode { - // In an or-pattern, only consider the first pattern; any later patterns - // must have the same bindings, and we also consider the first pattern + // In an or-pattern, only consider the first non-never pattern; any later patterns + // must have the same bindings, and we also consider that pattern // to be the "authoritative" set of ids. pat.each_binding_or_first(&mut |_, hir_id, pat_sp, ident| { let ln = self.live_node(hir_id, pat_sp); diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index 2dffdc9846c..27619b74a66 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -1,4 +1,4 @@ -use rustc_errors::{AddToDiagnostic, Diagnostic, SubdiagnosticMessageOp}; +use rustc_errors::{AddToDiagnostic, DiagnosticBuilder, EmissionGuarantee, SubdiagnosticMessageOp}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::thir::Pat; use rustc_middle::ty::Ty; @@ -62,7 +62,11 @@ pub struct Overlap<'tcx> { } impl<'tcx> AddToDiagnostic for Overlap<'tcx> { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _: F, + ) { let Overlap { span, range } = self; // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]` diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index d35b0248e41..a24da448766 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -760,9 +760,6 @@ impl<'a, Cx: TypeCx> PlaceCtxt<'a, Cx> { fn ctor_arity(&self, ctor: &Constructor<Cx>) -> usize { self.cx.ctor_arity(ctor, self.ty) } - fn ctors_for_ty(&self) -> Result<ConstructorSet<Cx>, Cx::Error> { - self.cx.ctors_for_ty(self.ty) - } fn wild_from_ctor(&self, ctor: Constructor<Cx>) -> WitnessPat<Cx> { WitnessPat::wild_from_ctor(self.cx, ctor, self.ty.clone()) } @@ -815,7 +812,8 @@ impl fmt::Display for ValidityConstraint { } } -/// Data about a place under investigation. +/// Data about a place under investigation. Its methods contain a lot of the logic used to analyze +/// the constructors in the matrix. struct PlaceInfo<Cx: TypeCx> { /// The type of the place. ty: Cx::Ty, @@ -826,6 +824,8 @@ struct PlaceInfo<Cx: TypeCx> { } impl<Cx: TypeCx> PlaceInfo<Cx> { + /// Given a constructor for the current place, we return one `PlaceInfo` for each field of the + /// constructor. fn specialize<'a>( &'a self, cx: &'a Cx, @@ -839,6 +839,77 @@ impl<Cx: TypeCx> PlaceInfo<Cx> { is_scrutinee: false, }) } + + /// This analyzes a column of constructors corresponding to the current place. It returns a pair + /// `(split_ctors, missing_ctors)`. + /// + /// `split_ctors` is a splitted list of constructors that cover the whole type. This will be + /// used to specialize the matrix. + /// + /// `missing_ctors` is a list of the constructors not found in the column, for reporting + /// purposes. + fn split_column_ctors<'a>( + &self, + cx: &Cx, + ctors: impl Iterator<Item = &'a Constructor<Cx>> + Clone, + ) -> Result<(SmallVec<[Constructor<Cx>; 1]>, Vec<Constructor<Cx>>), Cx::Error> + where + Cx: 'a, + { + let ctors_for_ty = cx.ctors_for_ty(&self.ty)?; + + // We treat match scrutinees of type `!` or `EmptyEnum` differently. + let is_toplevel_exception = + self.is_scrutinee && matches!(ctors_for_ty, ConstructorSet::NoConstructors); + // Whether empty patterns are counted as useful or not. We only warn an empty arm unreachable if + // it is guaranteed unreachable by the opsem (i.e. if the place is `known_valid`). + let empty_arms_are_unreachable = self.validity.is_known_valid() + && (is_toplevel_exception + || cx.is_exhaustive_patterns_feature_on() + || cx.is_min_exhaustive_patterns_feature_on()); + // Whether empty patterns can be omitted for exhaustiveness. We ignore place validity in the + // toplevel exception and `exhaustive_patterns` cases for backwards compatibility. + let can_omit_empty_arms = empty_arms_are_unreachable + || is_toplevel_exception + || cx.is_exhaustive_patterns_feature_on(); + + // Analyze the constructors present in this column. + let mut split_set = ctors_for_ty.split(ctors); + let all_missing = split_set.present.is_empty(); + + // Build the set of constructors we will specialize with. It must cover the whole type, so + // we add `Missing` to represent the missing ones. This is explained under "Constructor + // Splitting" at the top of this file. + let mut split_ctors = split_set.present; + if !(split_set.missing.is_empty() + && (split_set.missing_empty.is_empty() || empty_arms_are_unreachable)) + { + split_ctors.push(Constructor::Missing); + } + + // Which empty constructors are considered missing. We ensure that + // `!missing_ctors.is_empty() => split_ctors.contains(Missing)`. The converse usually holds + // except when `!self.validity.is_known_valid()`. + let mut missing_ctors = split_set.missing; + if !can_omit_empty_arms { + missing_ctors.append(&mut split_set.missing_empty); + } + + // Whether we should report "Enum::A and Enum::C are missing" or "_ is missing". At the top + // level we prefer to list all constructors. + let report_individual_missing_ctors = self.is_scrutinee || !all_missing; + if !missing_ctors.is_empty() && !report_individual_missing_ctors { + // Report `_` as missing. + missing_ctors = vec![Constructor::Wildcard]; + } else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) { + // We need to report a `_` anyway, so listing other constructors would be redundant. + // `NonExhaustive` is displayed as `_` just like `Wildcard`, but it will be picked + // up by diagnostics to add a note about why `_` is required here. + missing_ctors = vec![Constructor::NonExhaustive]; + } + + Ok((split_ctors, missing_ctors)) + } } impl<Cx: TypeCx> Clone for PlaceInfo<Cx> { @@ -1314,40 +1385,23 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> { pcx: &PlaceCtxt<'_, Cx>, missing_ctors: &[Constructor<Cx>], ctor: &Constructor<Cx>, - report_individual_missing_ctors: bool, ) { if self.is_empty() { return; } if matches!(ctor, Constructor::Missing) { // We got the special `Missing` constructor that stands for the constructors not present - // in the match. - if missing_ctors.is_empty() { - // Nothing to report. - *self = Self::empty(); - } else if !report_individual_missing_ctors { - // Report `_` as missing. - let pat = pcx.wild_from_ctor(Constructor::Wildcard); - self.push_pattern(pat); - } else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) { - // We need to report a `_` anyway, so listing other constructors would be redundant. - // `NonExhaustive` is displayed as `_` just like `Wildcard`, but it will be picked - // up by diagnostics to add a note about why `_` is required here. - let pat = pcx.wild_from_ctor(Constructor::NonExhaustive); - self.push_pattern(pat); - } else { - // For each missing constructor `c`, we add a `c(_, _, _)` witness appropriately - // filled with wildcards. - let mut ret = Self::empty(); - for ctor in missing_ctors { - let pat = pcx.wild_from_ctor(ctor.clone()); - // Clone `self` and add `c(_, _, _)` to each of its witnesses. - let mut wit_matrix = self.clone(); - wit_matrix.push_pattern(pat); - ret.extend(wit_matrix); - } - *self = ret; + // in the match. For each missing constructor `c`, we add a `c(_, _, _)` witness + // appropriately filled with wildcards. + let mut ret = Self::empty(); + for ctor in missing_ctors { + let pat = pcx.wild_from_ctor(ctor.clone()); + // Clone `self` and add `c(_, _, _)` to each of its witnesses. + let mut wit_matrix = self.clone(); + wit_matrix.push_pattern(pat); + ret.extend(wit_matrix); } + *self = ret; } else { // Any other constructor we unspecialize as expected. for witness in self.0.iter_mut() { @@ -1479,51 +1533,13 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( }; }; - let ty = &place.ty.clone(); // Clone it out so we can mutate `matrix` later. - let pcx = &PlaceCtxt { cx: mcx.tycx, ty }; - debug!("ty: {:?}", pcx.ty); - let ctors_for_ty = pcx.ctors_for_ty()?; - - // We treat match scrutinees of type `!` or `EmptyEnum` differently. - let is_toplevel_exception = - place.is_scrutinee && matches!(ctors_for_ty, ConstructorSet::NoConstructors); - // Whether empty patterns are counted as useful or not. We only warn an empty arm unreachable if - // it is guaranteed unreachable by the opsem (i.e. if the place is `known_valid`). - let empty_arms_are_unreachable = place.validity.is_known_valid() - && (is_toplevel_exception - || mcx.tycx.is_exhaustive_patterns_feature_on() - || mcx.tycx.is_min_exhaustive_patterns_feature_on()); - // Whether empty patterns can be omitted for exhaustiveness. We ignore place validity in the - // toplevel exception and `exhaustive_patterns` cases for backwards compatibility. - let can_omit_empty_arms = empty_arms_are_unreachable - || is_toplevel_exception - || mcx.tycx.is_exhaustive_patterns_feature_on(); - // Analyze the constructors present in this column. + debug!("ty: {:?}", place.ty); let ctors = matrix.heads().map(|p| p.ctor()); - let mut split_set = ctors_for_ty.split(ctors); - let all_missing = split_set.present.is_empty(); - // Build the set of constructors we will specialize with. It must cover the whole type. - // We need to iterate over a full set of constructors, so we add `Missing` to represent the - // missing ones. This is explained under "Constructor Splitting" at the top of this file. - let mut split_ctors = split_set.present; - if !(split_set.missing.is_empty() - && (split_set.missing_empty.is_empty() || empty_arms_are_unreachable)) - { - split_ctors.push(Constructor::Missing); - } - - // Whether we should report "Enum::A and Enum::C are missing" or "_ is missing". At the top - // level we prefer to list all constructors. - let report_individual_missing_ctors = place.is_scrutinee || !all_missing; - // Which constructors are considered missing. We ensure that `!missing_ctors.is_empty() => - // split_ctors.contains(Missing)`. The converse usually holds except when - // `!place_validity.is_known_valid()`. - let mut missing_ctors = split_set.missing; - if !can_omit_empty_arms { - missing_ctors.append(&mut split_set.missing_empty); - } + let (split_ctors, missing_ctors) = place.split_column_ctors(mcx.tycx, ctors)?; + let ty = &place.ty.clone(); // Clone it out so we can mutate `matrix` later. + let pcx = &PlaceCtxt { cx: mcx.tycx, ty }; let mut ret = WitnessMatrix::empty(); for ctor in split_ctors { // Dig into rows that match `ctor`. @@ -1538,7 +1554,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( })?; // Transform witnesses for `spec_matrix` into witnesses for `matrix`. - witnesses.apply_constructor(pcx, &missing_ctors, &ctor, report_individual_missing_ctors); + witnesses.apply_constructor(pcx, &missing_ctors, &ctor); // Accumulate the found witnesses. ret.extend(witnesses); diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index b6ac54a9ab5..7dc1a1f7917 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -817,7 +817,7 @@ impl<D: Deps> DepGraphData<D> { None => {} } - if let None = qcx.dep_context().sess().dcx().has_errors_or_lint_errors_or_delayed_bugs() { + if let None = qcx.dep_context().sess().dcx().has_errors_or_delayed_bugs() { panic!("try_mark_previous_green() - Forcing the DepNode should have set its color") } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 4b978fefa10..d64a3b43aad 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -7,7 +7,7 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ codes::*, pluralize, report_ambiguity_error, struct_span_code_err, Applicability, DiagCtxt, - Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, SuggestionStyle, + DiagnosticBuilder, ErrorGuaranteed, MultiSpan, SuggestionStyle, }; use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::def::Namespace::{self, *}; @@ -360,7 +360,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { /// ``` fn add_suggestion_for_rename_of_use( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, name: Symbol, import: Import<'_>, binding_span: Span, @@ -436,7 +436,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { /// as characters expected by span manipulations won't be present. fn add_suggestion_for_duplicate_nested_use( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, import: Import<'_>, binding_span: Span, ) { @@ -1399,7 +1399,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { pub(crate) fn unresolved_macro_suggestions( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, macro_kind: MacroKind, parent_scope: &ParentScope<'a>, ident: Ident, @@ -1515,7 +1515,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { pub(crate) fn add_typo_suggestion( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, suggestion: Option<TypoSuggestion>, span: Span, ) -> bool { @@ -2461,7 +2461,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { /// Finds a cfg-ed out item inside `module` with the matching name. pub(crate) fn find_cfg_stripped( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, segment: &Symbol, module: DefId, ) { @@ -2670,7 +2670,7 @@ pub(crate) enum DiagnosticMode { pub(crate) fn import_candidates( tcx: TyCtxt<'_>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, // This is `None` if all placement locations are inside expansions use_placement_span: Option<Span>, candidates: &[ImportSuggestion], @@ -2696,7 +2696,7 @@ pub(crate) fn import_candidates( /// found and suggested, returns `true`, otherwise returns `false`. fn show_candidates( tcx: TyCtxt<'_>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, // This is `None` if all placement locations are inside expansions use_placement_span: Option<Span>, candidates: &[ImportSuggestion], diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 1cf3fecc289..2f4da29133f 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -23,7 +23,6 @@ use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::{PrimTy, TraitCandidate}; -use rustc_metadata::creader::CStore; use rustc_middle::middle::resolve_bound_vars::Set1; use rustc_middle::{bug, span_bug}; use rustc_session::config::{CrateType, ResolveDocLinks}; @@ -3683,12 +3682,12 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } Res::SelfCtor(_) => { // We resolve `Self` in pattern position as an ident sometimes during recovery, - // so delay a bug instead of ICEing. - self.r.dcx().span_delayed_bug( + // so delay a bug instead of ICEing. (Note: is this no longer true? We now ICE. If + // this triggers, please convert to a delayed bug and add a test.) + self.r.dcx().span_bug( ident.span, "unexpected `SelfCtor` in pattern, expected identifier" ); - None } _ => span_bug!( ident.span, @@ -4574,10 +4573,6 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // Encoding foreign def ids in proc macro crate metadata will ICE. return None; } - // Doc paths should be resolved speculatively and should not produce any - // diagnostics, but if they are indeed resolved, then we need to keep the - // corresponding crate alive. - CStore::from_tcx_mut(self.r.tcx).set_used_recursively(def_id.krate); } res }); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index f71f7ccecab..335bf0949d6 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1,3 +1,5 @@ +// ignore-tidy-filelength + use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind}; use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseSet}; @@ -16,8 +18,8 @@ use rustc_ast::{ use rustc_ast_pretty::pprust::where_bound_predicate_to_string; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, MultiSpan, SuggestionStyle, + codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, + MultiSpan, SuggestionStyle, }; use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; @@ -496,7 +498,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fn detect_assoc_type_constraint_meant_as_path( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, base_error: &BaseError, ) { let Some(ty) = self.diagnostic_metadata.current_type_path else { @@ -537,7 +539,12 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } } - fn suggest_self_or_self_ref(&mut self, err: &mut Diagnostic, path: &[Segment], span: Span) { + fn suggest_self_or_self_ref( + &mut self, + err: &mut DiagnosticBuilder<'_>, + path: &[Segment], + span: Span, + ) { if !self.self_type_is_available() { return; } @@ -582,7 +589,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fn try_lookup_name_relaxed( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, source: PathSource<'_>, path: &[Segment], following_seg: Option<&Segment>, @@ -786,7 +793,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fn suggest_trait_and_bounds( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, source: PathSource<'_>, res: Option<Res>, span: Span, @@ -863,7 +870,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fn suggest_typo( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, source: PathSource<'_>, path: &[Segment], following_seg: Option<&Segment>, @@ -903,7 +910,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fn suggest_shadowed( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, source: PathSource<'_>, path: &[Segment], following_seg: Option<&Segment>, @@ -936,7 +943,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fn err_code_special_cases( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, source: PathSource<'_>, path: &[Segment], span: Span, @@ -981,7 +988,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { /// Emit special messages for unresolved `Self` and `self`. fn suggest_self_ty( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, source: PathSource<'_>, path: &[Segment], span: Span, @@ -1008,7 +1015,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fn suggest_self_value( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, source: PathSource<'_>, path: &[Segment], span: Span, @@ -1090,7 +1097,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fn suggest_at_operator_in_slice_pat_with_range( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, path: &[Segment], ) { let Some(pat) = self.diagnostic_metadata.current_pat else { return }; @@ -1129,7 +1136,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fn suggest_swapping_misplaced_self_ty_and_trait( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, source: PathSource<'_>, res: Option<Res>, span: Span, @@ -1155,7 +1162,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } } - fn suggest_bare_struct_literal(&mut self, err: &mut Diagnostic) { + fn suggest_bare_struct_literal(&mut self, err: &mut DiagnosticBuilder<'_>) { if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal { err.multipart_suggestion( "you might have meant to write a `struct` literal", @@ -1170,7 +1177,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fn suggest_changing_type_to_const_param( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, res: Option<Res>, source: PathSource<'_>, span: Span, @@ -1222,7 +1229,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fn suggest_pattern_match_with_let( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, source: PathSource<'_>, span: Span, ) -> bool { @@ -1277,7 +1284,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } /// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`. - fn restrict_assoc_type_in_where_clause(&mut self, span: Span, err: &mut Diagnostic) -> bool { + fn restrict_assoc_type_in_where_clause( + &mut self, + span: Span, + err: &mut DiagnosticBuilder<'_>, + ) -> bool { // Detect that we are actually in a `where` predicate. let (bounded_ty, bounds, where_span) = if let Some(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { @@ -1410,7 +1421,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { /// Returns `true` if able to provide context-dependent help. fn smart_resolve_context_dependent_help( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, span: Span, source: PathSource<'_>, path: &[Segment], @@ -1421,50 +1432,52 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let ns = source.namespace(); let is_expected = &|res| source.is_expected(res); - let path_sep = |this: &mut Self, err: &mut Diagnostic, expr: &Expr, kind: DefKind| { - const MESSAGE: &str = "use the path separator to refer to an item"; + let path_sep = + |this: &mut Self, err: &mut DiagnosticBuilder<'_>, expr: &Expr, kind: DefKind| { + const MESSAGE: &str = "use the path separator to refer to an item"; - let (lhs_span, rhs_span) = match &expr.kind { - ExprKind::Field(base, ident) => (base.span, ident.span), - ExprKind::MethodCall(box MethodCall { receiver, span, .. }) => { - (receiver.span, *span) - } - _ => return false, - }; + let (lhs_span, rhs_span) = match &expr.kind { + ExprKind::Field(base, ident) => (base.span, ident.span), + ExprKind::MethodCall(box MethodCall { receiver, span, .. }) => { + (receiver.span, *span) + } + _ => return false, + }; - if lhs_span.eq_ctxt(rhs_span) { - err.span_suggestion( - lhs_span.between(rhs_span), - MESSAGE, - "::", - Applicability::MaybeIncorrect, - ); - true - } else if kind == DefKind::Struct - && let Some(lhs_source_span) = lhs_span.find_ancestor_inside(expr.span) - && let Ok(snippet) = this.r.tcx.sess.source_map().span_to_snippet(lhs_source_span) - { - // The LHS is a type that originates from a macro call. - // We have to add angle brackets around it. + if lhs_span.eq_ctxt(rhs_span) { + err.span_suggestion( + lhs_span.between(rhs_span), + MESSAGE, + "::", + Applicability::MaybeIncorrect, + ); + true + } else if kind == DefKind::Struct + && let Some(lhs_source_span) = lhs_span.find_ancestor_inside(expr.span) + && let Ok(snippet) = + this.r.tcx.sess.source_map().span_to_snippet(lhs_source_span) + { + // The LHS is a type that originates from a macro call. + // We have to add angle brackets around it. - err.span_suggestion_verbose( - lhs_source_span.until(rhs_span), - MESSAGE, - format!("<{snippet}>::"), - Applicability::MaybeIncorrect, - ); - true - } else { - // Either we were unable to obtain the source span / the snippet or - // the LHS originates from a macro call and it is not a type and thus - // there is no way to replace `.` with `::` and still somehow suggest - // valid Rust code. + err.span_suggestion_verbose( + lhs_source_span.until(rhs_span), + MESSAGE, + format!("<{snippet}>::"), + Applicability::MaybeIncorrect, + ); + true + } else { + // Either we were unable to obtain the source span / the snippet or + // the LHS originates from a macro call and it is not a type and thus + // there is no way to replace `.` with `::` and still somehow suggest + // valid Rust code. - false - } - }; + false + } + }; - let find_span = |source: &PathSource<'_>, err: &mut Diagnostic| { + let find_span = |source: &PathSource<'_>, err: &mut DiagnosticBuilder<'_>| { match source { PathSource::Expr(Some(Expr { span, kind: ExprKind::Call(_, _), .. })) | PathSource::TupleStruct(span, _) => { @@ -1820,7 +1833,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fn suggest_alternative_construction_methods( &mut self, def_id: DefId, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, path_span: Span, call_span: Span, args: &[P<Expr>], @@ -2250,7 +2263,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { // try to give a suggestion for this pattern: `name = blah`, which is common in other languages // suggest `let name = blah` to introduce a new binding - fn let_binding_suggestion(&mut self, err: &mut Diagnostic, ident_span: Span) -> bool { + fn let_binding_suggestion( + &mut self, + err: &mut DiagnosticBuilder<'_>, + ident_span: Span, + ) -> bool { if let Some(Expr { kind: ExprKind::Assign(lhs, ..), .. }) = self.diagnostic_metadata.in_assignment && let ast::ExprKind::Path(None, ref path) = lhs.kind @@ -2351,7 +2368,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { /// Adds a suggestion for using an enum's variant when an enum is used instead. fn suggest_using_enum_variant( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, source: PathSource<'_>, def_id: DefId, span: Span, @@ -2727,9 +2744,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fn suggest_introducing_lifetime( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, name: Option<&str>, - suggest: impl Fn(&mut Diagnostic, bool, Span, Cow<'static, str>, String) -> bool, + suggest: impl Fn(&mut DiagnosticBuilder<'_>, bool, Span, Cow<'static, str>, String) -> bool, ) { let mut suggest_note = true; for rib in self.lifetime_ribs.iter().rev() { @@ -2887,7 +2904,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fn add_missing_lifetime_specifiers_label( &mut self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, lifetime_refs: Vec<MissingLifetime>, function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>, ) { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 1c625f49e3e..a9b6703f3ab 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1651,7 +1651,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.tcx .sess .time("resolve_postprocess", || self.crate_loader(|c| c.postprocess(krate))); - self.crate_loader(|c| c.unload_unused_crates()); }); // Make sure we don't mutate the cstore from here on. diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index ef0512bf686..0a330da87b0 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2468,6 +2468,9 @@ pub fn parse_externs( )); let adjusted_name = name.replace('-', "_"); if is_ascii_ident(&adjusted_name) { + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] error.help(format!( "consider replacing the dashes with underscores: `{adjusted_name}`" )); diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index db976b30404..74d26237f24 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -6,6 +6,7 @@ use crate::errors::{ }; use crate::Session; use rustc_ast::{self as ast, attr}; +use rustc_errors::FatalError; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; use std::path::Path; @@ -115,7 +116,7 @@ pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) { } if err_count > 0 { - sess.dcx().abort_if_errors(); + FatalError.raise(); } } diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 701c5bf375f..b011ca4dd50 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -15,7 +15,8 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::sync::{AppendOnlyVec, Lock, Lrc}; use rustc_errors::{emitter::SilentEmitter, DiagCtxt}; use rustc_errors::{ - fallback_fluent_bundle, Diagnostic, DiagnosticBuilder, DiagnosticMessage, MultiSpan, StashKey, + fallback_fluent_bundle, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, MultiSpan, + StashKey, }; use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures}; use rustc_span::edition::Edition; @@ -156,7 +157,11 @@ pub fn feature_warn_issue( } /// Adds the diagnostics for a feature to an existing error. -pub fn add_feature_diagnostics(err: &mut Diagnostic, sess: &Session, feature: Symbol) { +pub fn add_feature_diagnostics<G: EmissionGuarantee>( + err: &mut DiagnosticBuilder<'_, G>, + sess: &Session, + feature: Symbol, +) { add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false); } @@ -165,8 +170,8 @@ pub fn add_feature_diagnostics(err: &mut Diagnostic, sess: &Session, feature: Sy /// This variant allows you to control whether it is a library or language feature. /// Almost always, you want to use this for a language feature. If so, prefer /// `add_feature_diagnostics`. -pub fn add_feature_diagnostics_for_issue( - err: &mut Diagnostic, +pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>( + err: &mut DiagnosticBuilder<'_, G>, sess: &Session, feature: Symbol, issue: GateIssue, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 9d1133c487f..02c7a0c6371 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -258,7 +258,8 @@ impl Session { } } - fn check_miri_unleashed_features(&self) { + fn check_miri_unleashed_features(&self) -> Option<ErrorGuaranteed> { + let mut guar = None; let unleashed_features = self.miri_unleashed_features.lock(); if !unleashed_features.is_empty() { let mut must_err = false; @@ -279,18 +280,22 @@ impl Session { // If we should err, make sure we did. if must_err && self.dcx().has_errors().is_none() { // We have skipped a feature gate, and not run into other errors... reject. - self.dcx().emit_err(errors::NotCircumventFeature); + guar = Some(self.dcx().emit_err(errors::NotCircumventFeature)); } } + guar } /// Invoked all the way at the end to finish off diagnostics printing. - pub fn finish_diagnostics(&self, registry: &Registry) { - self.check_miri_unleashed_features(); + pub fn finish_diagnostics(&self, registry: &Registry) -> Option<ErrorGuaranteed> { + let mut guar = None; + guar = guar.or(self.check_miri_unleashed_features()); + guar = guar.or(self.dcx().emit_stashed_diagnostics()); self.dcx().print_error_count(registry); if self.opts.json_future_incompat { self.dcx().emit_future_breakage_report(); } + guar } /// Returns true if the crate is a testing one. @@ -312,16 +317,6 @@ impl Session { err } - pub fn compile_status(&self) -> Result<(), ErrorGuaranteed> { - // We must include lint errors here. - if let Some(reported) = self.dcx().has_errors_or_lint_errors() { - self.dcx().emit_stashed_diagnostics(); - Err(reported) - } else { - Ok(()) - } - } - /// Record the fact that we called `trimmed_def_paths`, and do some /// checking about whether its cost was justified. pub fn record_trimmed_def_paths(&self) { @@ -1410,10 +1405,6 @@ impl EarlyDiagCtxt { Self { dcx: DiagCtxt::with_emitter(emitter) } } - pub fn abort_if_errors(&self) { - self.dcx.abort_if_errors() - } - /// Swap out the underlying dcx once we acquire the user's preference on error emission /// format. Any errors prior to that will cause an abort and all stashed diagnostics of the /// previous dcx will be emitted. diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 6bb8c5452b9..6e870728baf 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -347,8 +347,14 @@ macro_rules! run_driver { Err(CompilerError::Interrupted(value)) } (Ok(Ok(_)), None) => Err(CompilerError::Skipped), - (Ok(Err(_)), _) => Err(CompilerError::CompilationFailed), - (Err(_), _) => Err(CompilerError::ICE), + // Two cases here: + // - `run` finished normally and returned `Err` + // - `run` panicked with `FatalErr` + // You might think that normal compile errors cause the former, and + // ICEs cause the latter. But some normal compiler errors also cause + // the latter. So we can't meaningfully distinguish them, and group + // them together. + (Ok(Err(_)), _) | (Err(_), _) => Err(CompilerError::Failed), } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 29c88783357..181ab0d4d56 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -764,8 +764,10 @@ symbols! { f64_nan, fabsf32, fabsf64, + fadd_algebraic, fadd_fast, fake_variadic, + fdiv_algebraic, fdiv_fast, feature, fence, @@ -785,6 +787,7 @@ symbols! { fmaf32, fmaf64, fmt, + fmul_algebraic, fmul_fast, fn_align, fn_delegation, @@ -810,6 +813,7 @@ symbols! { format_unsafe_arg, freeze, freg, + frem_algebraic, frem_fast, from, from_desugaring, @@ -823,6 +827,7 @@ symbols! { from_usize, from_yeet, fs_create_dir, + fsub_algebraic, fsub_fast, fundamental, future, diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index aee5d60626e..dd75377ead2 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -97,28 +97,39 @@ fn pre_link_args(os: &'static str, arch: Arch, abi: &'static str) -> LinkArgs { _ => os.into(), }; - let platform_version: StaticCow<str> = match os { - "ios" => ios_lld_platform_version(arch), - "tvos" => tvos_lld_platform_version(), - "watchos" => watchos_lld_platform_version(), - "macos" => macos_lld_platform_version(arch), - _ => unreachable!(), - } - .into(); - - let arch = arch.target_name(); + let min_version: StaticCow<str> = { + let (major, minor) = match os { + "ios" => ios_deployment_target(arch, abi), + "tvos" => tvos_deployment_target(), + "watchos" => watchos_deployment_target(), + "macos" => macos_deployment_target(arch), + _ => unreachable!(), + }; + format!("{major}.{minor}").into() + }; + let sdk_version = min_version.clone(); let mut args = TargetOptions::link_args( LinkerFlavor::Darwin(Cc::No, Lld::No), - &["-arch", arch, "-platform_version"], + &["-arch", arch.target_name(), "-platform_version"], ); add_link_args_iter( &mut args, LinkerFlavor::Darwin(Cc::No, Lld::No), - [platform_name, platform_version.clone(), platform_version].into_iter(), + [platform_name, min_version, sdk_version].into_iter(), ); if abi != "macabi" { - add_link_args(&mut args, LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-arch", arch]); + add_link_args( + &mut args, + LinkerFlavor::Darwin(Cc::Yes, Lld::No), + &["-arch", arch.target_name()], + ); + } else { + add_link_args_iter( + &mut args, + LinkerFlavor::Darwin(Cc::Yes, Lld::No), + ["-target".into(), mac_catalyst_llvm_target(arch).into()].into_iter(), + ); } args @@ -131,7 +142,7 @@ pub fn opts(os: &'static str, arch: Arch) -> TargetOptions { abi: abi.into(), os: os.into(), cpu: arch.target_cpu().into(), - link_env_remove: link_env_remove(arch, os), + link_env_remove: link_env_remove(os), vendor: "apple".into(), linker_flavor: LinkerFlavor::Darwin(Cc::Yes, Lld::No), // macOS has -dead_strip, which doesn't rely on function_sections @@ -220,16 +231,13 @@ pub fn deployment_target(target: &Target) -> Option<(u32, u32)> { }; macos_deployment_target(arch) } - "ios" => match &*target.abi { - "macabi" => mac_catalyst_deployment_target(), - _ => { - let arch = match target.arch.as_ref() { - "arm64e" => Arm64e, - _ => Arm64, - }; - ios_deployment_target(arch) - } - }, + "ios" => { + let arch = match target.arch.as_ref() { + "arm64e" => Arm64e, + _ => Arm64, + }; + ios_deployment_target(arch, &target.abi) + } "watchos" => watchos_deployment_target(), "tvos" => tvos_deployment_target(), _ => return None, @@ -260,17 +268,12 @@ fn macos_deployment_target(arch: Arch) -> (u32, u32) { .unwrap_or_else(|| macos_default_deployment_target(arch)) } -fn macos_lld_platform_version(arch: Arch) -> String { - let (major, minor) = macos_deployment_target(arch); - format!("{major}.{minor}") -} - pub fn macos_llvm_target(arch: Arch) -> String { let (major, minor) = macos_deployment_target(arch); format!("{}-apple-macosx{}.{}.0", arch.target_name(), major, minor) } -fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]> { +fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow<str>]> { // Apple platforms only officially support macOS as a host for any compilation. // // If building for macOS, we go ahead and remove any erroneous environment state @@ -298,29 +301,23 @@ fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]> env_remove.push("TVOS_DEPLOYMENT_TARGET".into()); env_remove.into() } else { - // Otherwise if cross-compiling for a different OS/SDK, remove any part + // Otherwise if cross-compiling for a different OS/SDK (including Mac Catalyst), remove any part // of the linking environment that's wrong and reversed. - match arch { - Armv7k | Armv7s | Arm64 | Arm64e | Arm64_32 | I386 | I386_sim | I686 | X86_64 - | X86_64_sim | X86_64h | Arm64_sim => { - cvs!["MACOSX_DEPLOYMENT_TARGET"] - } - X86_64_macabi | Arm64_macabi => cvs!["IPHONEOS_DEPLOYMENT_TARGET"], - } + cvs!["MACOSX_DEPLOYMENT_TARGET"] } } -fn ios_deployment_target(arch: Arch) -> (u32, u32) { +fn ios_deployment_target(arch: Arch, abi: &str) -> (u32, u32) { // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. - let (major, minor) = if arch == Arm64e { (14, 0) } else { (10, 0) }; + let (major, minor) = match (arch, abi) { + (Arm64e, _) => (14, 0), + // Mac Catalyst defaults to 13.1 in Clang. + (_, "macabi") => (13, 1), + _ => (10, 0), + }; from_set_deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((major, minor)) } -fn mac_catalyst_deployment_target() -> (u32, u32) { - // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. - from_set_deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((14, 0)) -} - pub fn ios_llvm_target(arch: Arch) -> String { // Modern iOS tooling extracts information about deployment target // from LC_BUILD_VERSION. This load command will only be emitted when @@ -328,17 +325,17 @@ pub fn ios_llvm_target(arch: Arch) -> String { // set high enough. Luckily one LC_BUILD_VERSION is enough, for Xcode // to pick it up (since std and core are still built with the fallback // of version 7.0 and hence emit the old LC_IPHONE_MIN_VERSION). - let (major, minor) = ios_deployment_target(arch); + let (major, minor) = ios_deployment_target(arch, ""); format!("{}-apple-ios{}.{}.0", arch.target_name(), major, minor) } -fn ios_lld_platform_version(arch: Arch) -> String { - let (major, minor) = ios_deployment_target(arch); - format!("{major}.{minor}") +pub fn mac_catalyst_llvm_target(arch: Arch) -> String { + let (major, minor) = ios_deployment_target(arch, "macabi"); + format!("{}-apple-ios{}.{}.0-macabi", arch.target_name(), major, minor) } pub fn ios_sim_llvm_target(arch: Arch) -> String { - let (major, minor) = ios_deployment_target(arch); + let (major, minor) = ios_deployment_target(arch, "sim"); format!("{}-apple-ios{}.{}.0-simulator", arch.target_name(), major, minor) } @@ -347,11 +344,6 @@ fn tvos_deployment_target() -> (u32, u32) { from_set_deployment_target("TVOS_DEPLOYMENT_TARGET").unwrap_or((10, 0)) } -fn tvos_lld_platform_version() -> String { - let (major, minor) = tvos_deployment_target(); - format!("{major}.{minor}") -} - pub fn tvos_llvm_target(arch: Arch) -> String { let (major, minor) = tvos_deployment_target(); format!("{}-apple-tvos{}.{}.0", arch.target_name(), major, minor) @@ -367,11 +359,6 @@ fn watchos_deployment_target() -> (u32, u32) { from_set_deployment_target("WATCHOS_DEPLOYMENT_TARGET").unwrap_or((5, 0)) } -fn watchos_lld_platform_version() -> String { - let (major, minor) = watchos_deployment_target(); - format!("{major}.{minor}") -} - pub fn watchos_sim_llvm_target(arch: Arch) -> String { let (major, minor) = watchos_deployment_target(); format!("{}-apple-watchos{}.{}.0-simulator", arch.target_name(), major, minor) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs index 78067a138a9..300e3014079 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs @@ -1,16 +1,13 @@ -use crate::spec::base::apple::{opts, Arch}; -use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, SanitizerSet, Target, TargetOptions}; +use crate::spec::base::apple::{mac_catalyst_llvm_target, opts, Arch}; +use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions}; pub fn target() -> Target { - let llvm_target = "arm64-apple-ios14.0-macabi"; - let arch = Arch::Arm64_macabi; let mut base = opts("ios", arch); - base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-target", llvm_target]); base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD; Target { - llvm_target: llvm_target.into(), + llvm_target: mac_catalyst_llvm_target(arch).into(), pointer_width: 64, data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(), arch: arch.target_arch(), diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs index f4350708986..234270c999b 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs @@ -1,4 +1,4 @@ -use crate::spec::{base, CodeModel, Target, TargetOptions}; +use crate::spec::{base, Target, TargetOptions}; pub fn target() -> Target { Target { @@ -7,7 +7,6 @@ pub fn target() -> Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(), arch: "loongarch64".into(), options: TargetOptions { - code_model: Some(CodeModel::Medium), cpu: "generic".into(), features: "+f,+d".into(), llvm_abiname: "lp64d".into(), diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs index f448017a2a5..3b1ea8e206f 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs @@ -16,7 +16,7 @@ pub fn target() -> Target { max_atomic_width: Some(64), relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, - code_model: Some(CodeModel::Medium), + code_model: Some(CodeModel::Small), ..Default::default() }, } diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs index d636c9599a7..ab9300ef9c7 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs @@ -17,7 +17,7 @@ pub fn target() -> Target { max_atomic_width: Some(64), relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, - code_model: Some(CodeModel::Medium), + code_model: Some(CodeModel::Small), ..Default::default() }, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs index ff21e489333..e59f41185de 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs @@ -1,16 +1,13 @@ -use crate::spec::base::apple::{opts, Arch}; -use crate::spec::{Cc, LinkerFlavor, Lld, SanitizerSet, Target, TargetOptions}; +use crate::spec::base::apple::{mac_catalyst_llvm_target, opts, Arch}; +use crate::spec::{SanitizerSet, Target, TargetOptions}; pub fn target() -> Target { - let llvm_target = "x86_64-apple-ios14.0-macabi"; - let arch = Arch::X86_64_macabi; let mut base = opts("ios", arch); - base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-target", llvm_target]); base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD; Target { - llvm_target: llvm_target.into(), + llvm_target: mac_catalyst_llvm_target(arch).into(), pointer_width: 64, data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 407fff03e15..10911c0d002 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,7 +1,7 @@ use crate::fluent_generated as fluent; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, - EmissionGuarantee, IntoDiagnostic, Level, SubdiagnosticMessageOp, + codes::*, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, EmissionGuarantee, + IntoDiagnostic, Level, SubdiagnosticMessageOp, }; use rustc_macros::Diagnostic; use rustc_middle::ty::{self, ClosureKind, PolyTraitRef, Ty}; @@ -102,7 +102,11 @@ pub enum AdjustSignatureBorrow { } impl AddToDiagnostic for AdjustSignatureBorrow { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { match self { AdjustSignatureBorrow::Borrow { to_borrow } => { diag.arg("len", to_borrow.len()); diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 63555a305d8..97f715b6386 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -109,7 +109,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { let mut errors = Vec::new(); for i in 0.. { if !infcx.tcx.recursion_limit().value_within_limit(i) { - unimplemented!("overflowed on pending obligations: {:?}", self.obligations); + // Only return true errors that we have accumulated while processing; + // keep ambiguities around, *including overflows*, because they shouldn't + // be considered true errors. + return errors; } let mut has_changed = false; diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index d87cc89954a..91312c9fdd6 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -1,6 +1,6 @@ use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; +use crate::traits::{BoundVarReplacer, PlaceholderReplacer}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::at::At; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -85,25 +85,16 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { ), ); - // Do not emit an error if normalization is known to fail but instead - // keep the projection unnormalized. This is the case for projections - // with a `T: Trait` where-clause and opaque types outside of the defining - // scope. - let result = if infcx.predicate_may_hold(&obligation) { - self.fulfill_cx.register_predicate_obligation(infcx, obligation); - let errors = self.fulfill_cx.select_all_or_error(infcx); - if !errors.is_empty() { - return Err(errors); - } - let ty = infcx.resolve_vars_if_possible(new_infer_ty); - - // Alias is guaranteed to be fully structurally resolved, - // so we can super fold here. - ty.try_super_fold_with(self)? - } else { - alias_ty.try_super_fold_with(self)? - }; + self.fulfill_cx.register_predicate_obligation(infcx, obligation); + let errors = self.fulfill_cx.select_all_or_error(infcx); + if !errors.is_empty() { + return Err(errors); + } + // Alias is guaranteed to be fully structurally resolved, + // so we can super fold here. + let ty = infcx.resolve_vars_if_possible(new_infer_ty); + let result = ty.try_super_fold_with(self)?; self.depth -= 1; Ok(result) } @@ -178,6 +169,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> { Ok(t) } + #[instrument(level = "debug", skip(self), ret)] fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> { let infcx = self.at.infcx; debug_assert_eq!(ty, infcx.shallow_resolve(ty)); @@ -204,11 +196,11 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> { } } + #[instrument(level = "debug", skip(self), ret)] fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> { - let reveal = self.at.param_env.reveal(); let infcx = self.at.infcx; debug_assert_eq!(ct, infcx.shallow_resolve(ct)); - if !needs_normalization(&ct, reveal) { + if !ct.has_projections() { return Ok(ct); } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs new file mode 100644 index 00000000000..911462f4b9a --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs @@ -0,0 +1,25 @@ +use crate::solve::EvalCtxt; +use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; +use rustc_middle::ty; + +impl<'tcx> EvalCtxt<'_, 'tcx> { + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn normalize_anon_const( + &mut self, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, + ) -> QueryResult<'tcx> { + if let Some(normalized_const) = self.try_const_eval_resolve( + goal.param_env, + ty::UnevaluatedConst::new(goal.predicate.alias.def_id, goal.predicate.alias.args), + self.tcx() + .type_of(goal.predicate.alias.def_id) + .no_bound_vars() + .expect("const ty should not rely on other generics"), + ) { + self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index d177109c420..5f625831156 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -18,8 +18,9 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP}; +mod anon_const; mod inherent; -mod opaques; +mod opaque_types; mod weak_types; impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -31,34 +32,34 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let def_id = goal.predicate.def_id(); match self.tcx().def_kind(def_id) { DefKind::AssocTy | DefKind::AssocConst => { - // To only compute normalization once for each projection we only - // assemble normalization candidates if the expected term is an - // unconstrained inference variable. - // - // Why: For better cache hits, since if we have an unconstrained RHS then - // there are only as many cache keys as there are (canonicalized) alias - // types in each normalizes-to goal. This also weakens inference in a - // forwards-compatible way so we don't use the value of the RHS term to - // affect candidate assembly for projections. - // - // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal - // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for - // `U` and equate it with `u32`. This means that we don't need a separate - // projection cache in the solver, since we're piggybacking off of regular - // goal caching. - if self.term_is_fully_unconstrained(goal) { - match self.tcx().associated_item(def_id).container { - ty::AssocItemContainer::TraitContainer => { + match self.tcx().associated_item(def_id).container { + ty::AssocItemContainer::TraitContainer => { + // To only compute normalization once for each projection we only + // assemble normalization candidates if the expected term is an + // unconstrained inference variable. + // + // Why: For better cache hits, since if we have an unconstrained RHS then + // there are only as many cache keys as there are (canonicalized) alias + // types in each normalizes-to goal. This also weakens inference in a + // forwards-compatible way so we don't use the value of the RHS term to + // affect candidate assembly for projections. + // + // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal + // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for + // `U` and equate it with `u32`. This means that we don't need a separate + // projection cache in the solver, since we're piggybacking off of regular + // goal caching. + if self.term_is_fully_unconstrained(goal) { let candidates = self.assemble_and_evaluate_candidates(goal); self.merge_candidates(candidates) + } else { + self.set_normalizes_to_hack_goal(goal); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - ty::AssocItemContainer::ImplContainer => { - self.normalize_inherent_associated_type(goal) - } } - } else { - self.set_normalizes_to_hack_goal(goal); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ty::AssocItemContainer::ImplContainer => { + self.normalize_inherent_associated_type(goal) + } } } DefKind::AnonConst => self.normalize_anon_const(goal), @@ -67,26 +68,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)), } } - - #[instrument(level = "debug", skip(self), ret)] - fn normalize_anon_const( - &mut self, - goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, - ) -> QueryResult<'tcx> { - if let Some(normalized_const) = self.try_const_eval_resolve( - goal.param_env, - ty::UnevaluatedConst::new(goal.predicate.alias.def_id, goal.predicate.alias.args), - self.tcx() - .type_of(goal.predicate.alias.def_id) - .no_bound_vars() - .expect("const ty should not rely on other generics"), - ) { - self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else { - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - } - } } impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs index 356c3776c04..356c3776c04 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index f663f02f872..3619d02438d 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -18,7 +18,7 @@ use crate::traits::{ Obligation, ObligationCause, PredicateObligation, PredicateObligations, SelectionContext, }; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::Diagnostic; +use rustc_errors::{DiagnosticBuilder, EmissionGuarantee}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; @@ -58,7 +58,7 @@ pub struct OverlapResult<'tcx> { pub involves_placeholder: bool, } -pub fn add_placeholder_note(err: &mut Diagnostic) { +pub fn add_placeholder_note<G: EmissionGuarantee>(err: &mut DiagnosticBuilder<'_, G>) { err.note( "this behavior recently changed as a result of a bug fix; \ see rust-lang/rust#56105 for details", diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 1edf6b11fc3..189e1ba54bc 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -62,14 +62,11 @@ pub fn is_const_evaluatable<'tcx>( match unexpanded_ct.kind() { ty::ConstKind::Expr(_) => { - // FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete, - // but currently it is not possible to evaluate `ConstKind::Expr` so we are unable - // to tell if it is evaluatable or not. For now we just ICE until this is - // implemented. - Err(NotConstEvaluatable::Error(tcx.dcx().span_delayed_bug( - span, - "evaluating `ConstKind::Expr` is not currently supported", - ))) + // FIXME(generic_const_exprs): we have a fully concrete `ConstKind::Expr`, but + // haven't implemented evaluating `ConstKind::Expr` yet, so we are unable to tell + // if it is evaluatable or not. As this is unreachable for now, we can simple ICE + // here. + tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported"); } ty::ConstKind::Unevaluated(uv) => { let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); 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 2d85f84f480..8ae31392b40 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -13,7 +13,7 @@ use hir::def::CtorOf; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, + codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, EmissionGuarantee, MultiSpan, Style, SuggestionStyle, }; use rustc_hir as hir; @@ -116,12 +116,12 @@ fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) - /// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but /// it can also be an `impl Trait` param that needs to be decomposed to a type /// param for cleaner code. -pub fn suggest_restriction<'tcx>( +pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( tcx: TyCtxt<'tcx>, item_id: LocalDefId, hir_generics: &hir::Generics<'tcx>, msg: &str, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, fn_sig: Option<&hir::FnSig<'_>>, projection: Option<&ty::AliasTy<'_>>, trait_pred: ty::PolyTraitPredicate<'tcx>, @@ -240,7 +240,7 @@ pub fn suggest_restriction<'tcx>( impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_restricting_param_bound( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, associated_ty: Option<(&'static str, Ty<'tcx>)>, mut body_id: LocalDefId, @@ -450,7 +450,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_dereferences( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { let mut code = obligation.cause.code(); @@ -743,22 +743,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn get_closure_name( &self, def_id: DefId, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, msg: Cow<'static, str>, ) -> Option<Symbol> { - let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<Symbol> { - // Get the local name of this closure. This can be inaccurate because - // of the possibility of reassignment, but this should be good enough. - match &kind { - hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, ident, None) => { - Some(ident.name) - } - _ => { - err.note(msg); - None + let get_name = + |err: &mut DiagnosticBuilder<'_>, kind: &hir::PatKind<'_>| -> Option<Symbol> { + // Get the local name of this closure. This can be inaccurate because + // of the possibility of reassignment, but this should be good enough. + match &kind { + hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, ident, None) => { + Some(ident.name) + } + _ => { + err.note(msg); + None + } } - } - }; + }; let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?); match self.tcx.parent_hir_node(hir_id) { @@ -778,7 +779,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_fn_call( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { // It doesn't make sense to make this suggestion outside of typeck... @@ -894,7 +895,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn check_for_binding_assigned_block_without_tail_expression( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) { let mut span = obligation.cause.span; @@ -971,7 +972,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_add_clone_to_arg( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); @@ -1156,7 +1157,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_add_reference_to_arg( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, poly_trait_pred: ty::PolyTraitPredicate<'tcx>, has_custom_message: bool, ) -> bool { @@ -1282,6 +1283,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { "the full type name has been written to '{}'", file.display() )); + err.note(format!( + "consider using `--verbose` to print full type name to the console" + )); } if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred { @@ -1374,7 +1378,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Suggest borrowing the type fn suggest_borrowing_for_object_cast( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, self_ty: Ty<'tcx>, target_ty: Ty<'tcx>, @@ -1410,7 +1414,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_remove_reference( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { let mut span = obligation.cause.span; @@ -1529,7 +1533,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { false } - fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) { + fn suggest_remove_await( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'_>, + ) { let hir = self.tcx.hir(); if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives() && let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id) @@ -1599,7 +1607,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_change_mut( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) { let points_at_arg = matches!( @@ -1677,7 +1685,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_semicolon_removal( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, span: Span, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { @@ -1731,7 +1739,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// emitted. fn suggest_impl_trait( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { @@ -1913,7 +1921,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn note_conflicting_fn_args( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, cause: &ObligationCauseCode<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, @@ -2120,7 +2128,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_fully_qualified_path( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, item_def_id: DefId, span: Span, trait_ref: DefId, @@ -2185,9 +2193,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// /// Returns `true` if an async-await specific note was added to the diagnostic. #[instrument(level = "debug", skip_all, fields(?obligation.predicate, ?obligation.cause.span))] - fn maybe_note_obligation_cause_for_async_await( + fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, obligation: &PredicateObligation<'tcx>, ) -> bool { let hir = self.tcx.hir(); @@ -2421,9 +2429,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// Unconditionally adds the diagnostic note described in /// `maybe_note_obligation_cause_for_async_await`'s documentation comment. #[instrument(level = "debug", skip_all)] - fn note_obligation_cause_for_async_await( + fn note_obligation_cause_for_async_await<G: EmissionGuarantee>( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, interior_or_upvar_span: CoroutineInteriorOrUpvar, is_async: bool, outer_coroutine: Option<DefId>, @@ -2656,10 +2664,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); } - fn note_obligation_cause_code<T>( + fn note_obligation_cause_code<G: EmissionGuarantee, T>( &self, body_id: LocalDefId, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, predicate: T, param_env: ty::ParamEnv<'tcx>, cause_code: &ObligationCauseCode<'tcx>, @@ -2861,6 +2869,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { "the full name for the type has been written to '{}'", file.display(), )); + err.note(format!( + "consider using `--verbose` to print the full type name to the console" + )); } } ObligationCauseCode::RepeatElementCopy { @@ -3328,6 +3339,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { "the full type name has been written to '{}'", file.display(), )); + err.note(format!( + "consider using `--verbose` to print the full type name to the console" + )); } let mut parent_predicate = parent_trait_pred; let mut data = &data.derived; @@ -3381,6 +3395,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { "the full type name has been written to '{}'", file.display(), )); + err.note(format!( + "consider using `--verbose` to print the full type name to the console" + )); } } // #74711: avoid a stack overflow @@ -3507,7 +3524,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { )] fn suggest_await_before_try( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, trait_pred: ty::PolyTraitPredicate<'tcx>, span: Span, @@ -3565,7 +3582,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_floating_point_literal( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_ref: &ty::PolyTraitRef<'tcx>, ) { let rhs_span = match obligation.cause.code() { @@ -3589,7 +3606,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_derive( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) { let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else { @@ -3655,7 +3672,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_dereferencing_index( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) { if let ObligationCauseCode::ImplDerivedObligation(_) = obligation.cause.code() @@ -3675,10 +3692,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - fn note_function_argument_obligation( + fn note_function_argument_obligation<G: EmissionGuarantee>( &self, body_id: LocalDefId, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, arg_hir_id: HirId, parent_code: &ObligationCauseCode<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -3860,11 +3877,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - fn suggest_option_method_if_applicable( + fn suggest_option_method_if_applicable<G: EmissionGuarantee>( &self, failed_pred: ty::Predicate<'tcx>, param_env: ty::ParamEnv<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, expr: &hir::Expr<'_>, ) { let tcx = self.tcx; @@ -3934,7 +3951,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - fn look_for_iterator_item_mistakes( + fn look_for_iterator_item_mistakes<G: EmissionGuarantee>( &self, assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>], typeck_results: &TypeckResults<'tcx>, @@ -3942,7 +3959,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { param_env: ty::ParamEnv<'tcx>, path_segment: &hir::PathSegment<'_>, args: &[hir::Expr<'_>], - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, ) { let tcx = self.tcx; // Special case for iterator chains, we look at potential failures of `Iterator::Item` @@ -4037,13 +4054,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - fn point_at_chain( + fn point_at_chain<G: EmissionGuarantee>( &self, expr: &hir::Expr<'_>, typeck_results: &TypeckResults<'tcx>, type_diffs: Vec<TypeError<'tcx>>, param_env: ty::ParamEnv<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, ) { let mut primary_spans = vec![]; let mut span_labels = vec![]; @@ -4279,7 +4296,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// the array into a slice. fn suggest_convert_to_slice( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, candidate_impls: &[ImplCandidate<'tcx>], @@ -4351,7 +4368,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn explain_hrtb_projection( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, pred: ty::PolyTraitPredicate<'tcx>, param_env: ty::ParamEnv<'tcx>, cause: &ObligationCause<'tcx>, @@ -4417,7 +4434,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_desugaring_async_fn_in_trait( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_ref: ty::PolyTraitRef<'tcx>, ) { // Don't suggest if RTN is active -- we should prefer a where-clause bound instead. @@ -4508,7 +4525,7 @@ fn hint_missing_borrow<'tcx>( found: Ty<'tcx>, expected: Ty<'tcx>, found_node: Node<'_>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) { if matches!(found_node, Node::TraitItem(..)) { return; @@ -4867,9 +4884,9 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( /// On `impl` evaluation cycles, look for `Self::AssocTy` restrictions in `where` clauses, explain /// they are not allowed and if possible suggest alternatives. -fn point_at_assoc_type_restriction( +fn point_at_assoc_type_restriction<G: EmissionGuarantee>( tcx: TyCtxt<'_>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, self_ty_str: &str, trait_name: &str, predicate: ty::Predicate<'_>, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 67bd18d7404..7186b96b40d 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -21,8 +21,8 @@ use crate::traits::{ }; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, MultiSpan, StashKey, StringPart, + codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, + FatalError, MultiSpan, StashKey, StringPart, }; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace, Res}; @@ -185,7 +185,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { predicate: &T, span: Span, suggest_increasing_limit: bool, - mutate: impl FnOnce(&mut Diagnostic), + mutate: impl FnOnce(&mut DiagnosticBuilder<'_>), ) -> ! where T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>, @@ -193,14 +193,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit); mutate(&mut err); err.emit(); - - self.dcx().abort_if_errors(); - // FIXME: this should be something like `build_overflow_error_fatal`, which returns - // `DiagnosticBuilder<', !>`. Then we don't even need anything after that `emit()`. - unreachable!( - "did not expect compilation to continue after `abort_if_errors`, \ - since an error was definitely emitted!" - ); + FatalError.raise(); } fn build_overflow_error<T>( @@ -272,7 +265,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); } - fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) { + fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>) { let suggested_limit = match self.tcx.recursion_limit() { Limit(0) => Limit(2), limit => limit * 2, @@ -1020,7 +1013,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, trait_ref: ty::TraitRef<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) -> bool { let span = obligation.cause.span; struct V<'v> { @@ -1810,7 +1803,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { impl_candidates: &[ImplCandidate<'tcx>], trait_ref: ty::PolyTraitRef<'tcx>, body_def_id: LocalDefId, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, other: bool, param_env: ty::ParamEnv<'tcx>, ) -> bool { @@ -1897,7 +1890,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let other = if other { "other " } else { "" }; - let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| { + let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut DiagnosticBuilder<'_>| { if candidates.is_empty() { return false; } @@ -2032,7 +2025,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { obligation: &PredicateObligation<'tcx>, trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, body_def_id: LocalDefId, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) { // This is *almost* equivalent to // `obligation.cause.code().peel_derives()`, but it gives us the @@ -2103,7 +2096,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// a probable version mismatch is added to `err` fn note_version_mismatch( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_ref: &ty::PolyTraitRef<'tcx>, ) -> bool { let get_trait_impls = |trait_def_id| { @@ -2572,7 +2565,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn annotate_source_of_ambiguity( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ambiguities: &[ambiguity::Ambiguity], predicate: ty::Predicate<'tcx>, ) { @@ -2715,7 +2708,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }) } - fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>) { + fn note_obligation_cause( + &self, + err: &mut DiagnosticBuilder<'_>, + obligation: &PredicateObligation<'tcx>, + ) { // First, attempt to add note to this error with an async-await-specific // message, and fall back to regular note otherwise. if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { @@ -2744,7 +2741,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { #[instrument(level = "debug", skip_all)] fn suggest_unsized_bound_if_applicable( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, ) { let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = @@ -2770,7 +2767,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } #[instrument(level = "debug", skip_all)] - fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>) { + fn maybe_suggest_unsized_generics( + &self, + err: &mut DiagnosticBuilder<'_>, + span: Span, + node: Node<'tcx>, + ) { let Some(generics) = node.generics() else { return; }; @@ -2822,7 +2824,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn maybe_indirection_for_unsized( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, item: &Item<'tcx>, param: &GenericParam<'tcx>, ) -> bool { @@ -3016,7 +3018,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn add_tuple_trait_message( &self, obligation_cause_code: &ObligationCauseCode<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) { match obligation_cause_code { ObligationCauseCode::RustCall => { @@ -3041,7 +3043,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { obligation: &PredicateObligation<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, trait_predicate: &ty::PolyTraitPredicate<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, span: Span, is_fn_trait: bool, suggested: bool, @@ -3122,7 +3124,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn add_help_message_for_fn_trait( &self, trait_ref: ty::PolyTraitRef<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, implemented_kind: ty::ClosureKind, params: ty::Binder<'tcx, Ty<'tcx>>, ) { @@ -3178,7 +3180,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn maybe_add_note_for_unsatisfied_const( &self, _trait_predicate: &ty::PolyTraitPredicate<'tcx>, - _err: &mut Diagnostic, + _err: &mut DiagnosticBuilder<'_>, _span: Span, ) -> UnsatisfiedConst { let unsatisfied_const = UnsatisfiedConst(false); @@ -3304,7 +3306,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { expected_trait_ref.self_ty().error_reported()?; let Some(found_trait_ty) = found_trait_ref.self_ty().no_bound_vars() else { - return Err(self.dcx().delayed_bug("bound vars outside binder")); + self.dcx().bug("bound vars outside binder"); }; let found_did = match *found_trait_ty.kind() { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 7ad94274634..fd981130af8 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -1,5 +1,6 @@ use crate::infer::{InferCtxt, TyOrConstInferVar}; use crate::traits::error_reporting::TypeErrCtxtExt; +use crate::traits::normalize::normalize_with_depth_to; use rustc_data_structures::captures::Captures; use rustc_data_structures::obligation_forest::ProcessResult; use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome}; @@ -312,7 +313,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { if obligation.predicate.has_projections() { let mut obligations = Vec::new(); - let predicate = crate::traits::project::try_normalize_with_depth_to( + let predicate = normalize_with_depth_to( &mut self.selcx, obligation.param_env, obligation.cause.clone(), diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 7caaccab63b..cac53796747 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -9,6 +9,7 @@ mod engine; pub mod error_reporting; mod fulfill; pub mod misc; +pub mod normalize; mod object_safety; pub mod outlives_bounds; pub mod project; @@ -40,17 +41,15 @@ use rustc_span::Span; use std::fmt::Debug; use std::ops::ControlFlow; -pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; - pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls}; pub use self::coherence::{OrphanCheckErr, OverlapResult}; pub use self::engine::{ObligationCtxt, TraitEngineExt}; pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation}; +pub use self::normalize::NormalizeExt; pub use self::object_safety::astconv_object_safety_violations; pub use self::object_safety::is_vtable_safe_method; pub use self::object_safety::object_safety_violations_for_assoc_item; pub use self::object_safety::ObjectSafetyViolation; -pub use self::project::NormalizeExt; pub use self::project::{normalize_inherent_projection, normalize_projection_type}; pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; @@ -68,6 +67,7 @@ pub use self::util::{ }; pub use self::util::{expand_trait_aliases, TraitAliasExpander}; pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices}; +pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; pub use rustc_infer::traits::*; @@ -119,9 +119,7 @@ pub fn predicates_for_generics<'tcx>( /// Determines whether the type `ty` is known to meet `bound` and /// returns true if so. Returns false if `ty` either does not meet -/// `bound` or is not known to meet bound (note that this is -/// conservative towards *no impl*, which is the opposite of the -/// `evaluate` methods). +/// `bound` or is not known to meet bound. pub fn type_known_to_meet_bound_modulo_regions<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -129,50 +127,8 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>( def_id: DefId, ) -> bool { let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]); - pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref) -} - -/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist? -/// -/// Ping me on zulip if you want to use this method and need help with finding -/// an appropriate replacement. -#[instrument(level = "debug", skip(infcx, param_env, pred), ret)] -fn pred_known_to_hold_modulo_regions<'tcx>( - infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - pred: impl ToPredicate<'tcx>, -) -> bool { - let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred); - - let result = infcx.evaluate_obligation_no_overflow(&obligation); - debug!(?result); - - if result.must_apply_modulo_regions() { - true - } else if result.may_apply() { - // Sometimes obligations are ambiguous because the recursive evaluator - // is not smart enough, so we fall back to fulfillment when we're not certain - // that an obligation holds or not. Even still, we must make sure that - // the we do no inference in the process of checking this obligation. - let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env)); - infcx.probe(|_| { - let ocx = ObligationCtxt::new(infcx); - ocx.register_obligation(obligation); - - let errors = ocx.select_all_or_error(); - match errors.as_slice() { - // Only known to hold if we did no inference. - [] => infcx.shallow_resolve(goal) == goal, - - errors => { - debug!(?errors); - false - } - } - }) - } else { - false - } + let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, trait_ref); + infcx.predicate_must_hold_modulo_regions(&obligation) } #[instrument(level = "debug", skip(tcx, elaborated_env))] @@ -216,7 +172,9 @@ fn do_normalize_predicates<'tcx>( // the normalized predicates. let errors = infcx.resolve_regions(&outlives_env); if !errors.is_empty() { - tcx.dcx().span_delayed_bug( + // @lcnr: Let's still ICE here for now. I want a test case + // for that. + tcx.dcx().span_bug( span, format!("failed region resolution while normalizing {elaborated_env:?}: {errors:?}"), ); diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs new file mode 100644 index 00000000000..429e5a5d7a4 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -0,0 +1,419 @@ +//! Deeply normalize types using the old trait solver. +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_infer::infer::at::At; +use rustc_infer::infer::InferOk; +use rustc_infer::traits::PredicateObligation; +use rustc_infer::traits::{FulfillmentError, Normalized, Obligation, TraitEngine}; +use rustc_middle::traits::{ObligationCause, ObligationCauseCode, Reveal}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder}; +use rustc_middle::ty::{TypeFoldable, TypeSuperFoldable, TypeVisitable, TypeVisitableExt}; + +use super::error_reporting::TypeErrCtxtExt; +use super::SelectionContext; +use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; + +#[extension(pub trait NormalizeExt<'tcx>)] +impl<'tcx> At<'_, 'tcx> { + /// Normalize a value using the `AssocTypeNormalizer`. + /// + /// This normalization should be used when the type contains inference variables or the + /// projection may be fallible. + fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> { + if self.infcx.next_trait_solver() { + InferOk { value, obligations: Vec::new() } + } else { + let mut selcx = SelectionContext::new(self.infcx); + let Normalized { value, obligations } = + normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value); + InferOk { value, obligations } + } + } + + /// Deeply normalizes `value`, replacing all aliases which can by normalized in + /// the current environment. In the new solver this errors in case normalization + /// fails or is ambiguous. + /// + /// In the old solver this simply uses `normalizes` and adds the nested obligations + /// to the `fulfill_cx`. This is necessary as we otherwise end up recomputing the + /// same goals in both a temporary and the shared context which negatively impacts + /// performance as these don't share caching. + /// + /// FIXME(-Znext-solver): This has the same behavior as `traits::fully_normalize` + /// in the new solver, but because of performance reasons, we currently reuse an + /// existing fulfillment context in the old solver. Once we also eagerly prove goals with + /// the old solver or have removed the old solver, remove `traits::fully_normalize` and + /// rename this function to `At::fully_normalize`. + fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>( + self, + value: T, + fulfill_cx: &mut dyn TraitEngine<'tcx>, + ) -> Result<T, Vec<FulfillmentError<'tcx>>> { + if self.infcx.next_trait_solver() { + crate::solve::deeply_normalize(self, value) + } else { + let value = self + .normalize(value) + .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); + let errors = fulfill_cx.select_where_possible(self.infcx); + let value = self.infcx.resolve_vars_if_possible(value); + if errors.is_empty() { Ok(value) } else { Err(errors) } + } + } +} + +/// As `normalize`, but with a custom depth. +pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + value: T, +) -> Normalized<'tcx, T> +where + T: TypeFoldable<TyCtxt<'tcx>>, +{ + let mut obligations = Vec::new(); + let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations); + Normalized { value, obligations } +} + +#[instrument(level = "info", skip(selcx, param_env, cause, obligations))] +pub(crate) fn normalize_with_depth_to<'a, 'b, 'tcx, T>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + value: T, + obligations: &mut Vec<PredicateObligation<'tcx>>, +) -> T +where + T: TypeFoldable<TyCtxt<'tcx>>, +{ + debug!(obligations.len = obligations.len()); + let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations); + let result = ensure_sufficient_stack(|| normalizer.fold(value)); + debug!(?result, obligations.len = normalizer.obligations.len()); + debug!(?normalizer.obligations,); + result +} + +pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>( + value: &T, + reveal: Reveal, +) -> bool { + let mut flags = ty::TypeFlags::HAS_TY_PROJECTION + | ty::TypeFlags::HAS_TY_WEAK + | ty::TypeFlags::HAS_TY_INHERENT + | ty::TypeFlags::HAS_CT_PROJECTION; + + match reveal { + Reveal::UserFacing => {} + Reveal::All => flags |= ty::TypeFlags::HAS_TY_OPAQUE, + } + + value.has_type_flags(flags) +} + +struct AssocTypeNormalizer<'a, 'b, 'tcx> { + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + obligations: &'a mut Vec<PredicateObligation<'tcx>>, + depth: usize, + universes: Vec<Option<ty::UniverseIndex>>, +} + +impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { + fn new( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &'a mut Vec<PredicateObligation<'tcx>>, + ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { + debug_assert!(!selcx.infcx.next_trait_solver()); + AssocTypeNormalizer { selcx, param_env, cause, obligations, depth, universes: vec![] } + } + + fn fold<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T { + let value = self.selcx.infcx.resolve_vars_if_possible(value); + debug!(?value); + + assert!( + !value.has_escaping_bound_vars(), + "Normalizing {value:?} without wrapping in a `Binder`" + ); + + if !needs_normalization(&value, self.param_env.reveal()) { + value + } else { + value.fold_with(self) + } + } +} + +impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.selcx.tcx() + } + + fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + self.universes.push(None); + let t = t.super_fold_with(self); + self.universes.pop(); + t + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !needs_normalization(&ty, self.param_env.reveal()) { + return ty; + } + + let (kind, data) = match *ty.kind() { + ty::Alias(kind, alias_ty) => (kind, alias_ty), + _ => return ty.super_fold_with(self), + }; + + // We try to be a little clever here as a performance optimization in + // cases where there are nested projections under binders. + // For example: + // ``` + // for<'a> fn(<T as Foo>::One<'a, Box<dyn Bar<'a, Item=<T as Foo>::Two<'a>>>>) + // ``` + // We normalize the args on the projection before the projecting, but + // if we're naive, we'll + // replace bound vars on inner, project inner, replace placeholders on inner, + // replace bound vars on outer, project outer, replace placeholders on outer + // + // However, if we're a bit more clever, we can replace the bound vars + // on the entire type before normalizing nested projections, meaning we + // replace bound vars on outer, project inner, + // project outer, replace placeholders on outer + // + // This is possible because the inner `'a` will already be a placeholder + // when we need to normalize the inner projection + // + // On the other hand, this does add a bit of complexity, since we only + // replace bound vars if the current type is a `Projection` and we need + // to make sure we don't forget to fold the args regardless. + + match kind { + ty::Opaque => { + // Only normalize `impl Trait` outside of type inference, usually in codegen. + match self.param_env.reveal() { + Reveal::UserFacing => ty.super_fold_with(self), + + Reveal::All => { + let recursion_limit = self.interner().recursion_limit(); + if !recursion_limit.value_within_limit(self.depth) { + self.selcx.infcx.err_ctxt().report_overflow_error( + &ty, + self.cause.span, + true, + |_| {}, + ); + } + + let args = data.args.fold_with(self); + let generic_ty = self.interner().type_of(data.def_id); + let concrete_ty = generic_ty.instantiate(self.interner(), args); + self.depth += 1; + let folded_ty = self.fold_ty(concrete_ty); + self.depth -= 1; + folded_ty + } + } + } + + ty::Projection if !data.has_escaping_bound_vars() => { + // This branch is *mostly* just an optimization: when we don't + // have escaping bound vars, we don't need to replace them with + // placeholders (see branch below). *Also*, we know that we can + // register an obligation to *later* project, since we know + // there won't be bound vars there. + let data = data.fold_with(self); + let normalized_ty = project::normalize_projection_type( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ); + debug!( + ?self.depth, + ?ty, + ?normalized_ty, + obligations.len = ?self.obligations.len(), + "AssocTypeNormalizer: normalized type" + ); + normalized_ty.ty().unwrap() + } + + ty::Projection => { + // If there are escaping bound vars, we temporarily replace the + // bound vars with placeholders. Note though, that in the case + // that we still can't project for whatever reason (e.g. self + // type isn't known enough), we *can't* register an obligation + // and return an inference variable (since then that obligation + // would have bound vars and that's a can of worms). Instead, + // we just give up and fall back to pretending like we never tried! + // + // Note: this isn't necessarily the final approach here; we may + // want to figure out how to register obligations with escaping vars + // or handle this some other way. + + let infcx = self.selcx.infcx; + let (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + let data = data.fold_with(self); + let normalized_ty = project::opt_normalize_projection_type( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ) + .ok() + .flatten() + .map(|term| term.ty().unwrap()) + .map(|normalized_ty| { + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + normalized_ty, + ) + }) + .unwrap_or_else(|| ty.super_fold_with(self)); + + debug!( + ?self.depth, + ?ty, + ?normalized_ty, + obligations.len = ?self.obligations.len(), + "AssocTypeNormalizer: normalized type" + ); + normalized_ty + } + ty::Weak => { + let recursion_limit = self.interner().recursion_limit(); + if !recursion_limit.value_within_limit(self.depth) { + self.selcx.infcx.err_ctxt().report_overflow_error( + &ty, + self.cause.span, + false, + |diag| { + diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow); + }, + ); + } + + let infcx = self.selcx.infcx; + self.obligations.extend( + infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map( + |(mut predicate, span)| { + if data.has_escaping_bound_vars() { + (predicate, ..) = BoundVarReplacer::replace_bound_vars( + infcx, + &mut self.universes, + predicate, + ); + } + let mut cause = self.cause.clone(); + cause.map_code(|code| { + ObligationCauseCode::TypeAlias(code, span, data.def_id) + }); + Obligation::new(infcx.tcx, cause, self.param_env, predicate) + }, + ), + ); + self.depth += 1; + let res = infcx + .tcx + .type_of(data.def_id) + .instantiate(infcx.tcx, data.args) + .fold_with(self); + self.depth -= 1; + res + } + + ty::Inherent if !data.has_escaping_bound_vars() => { + // This branch is *mostly* just an optimization: when we don't + // have escaping bound vars, we don't need to replace them with + // placeholders (see branch below). *Also*, we know that we can + // register an obligation to *later* project, since we know + // there won't be bound vars there. + + let data = data.fold_with(self); + + project::normalize_inherent_projection( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ) + } + + ty::Inherent => { + let infcx = self.selcx.infcx; + let (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + let data = data.fold_with(self); + let ty = project::normalize_inherent_projection( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ); + + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + ty, + ) + } + } + } + + #[instrument(skip(self), level = "debug")] + fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { + let tcx = self.selcx.tcx(); + if tcx.features().generic_const_exprs + || !needs_normalization(&constant, self.param_env.reveal()) + { + constant + } else { + let constant = constant.super_fold_with(self); + debug!(?constant, ?self.param_env); + with_replaced_escaping_bound_vars( + self.selcx.infcx, + &mut self.universes, + constant, + |constant| constant.normalize(tcx, self.param_env), + ) + } + } + + #[inline] + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) { + p.super_fold_with(self) + } else { + p + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 6825dd4ac71..c4110df45db 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -62,9 +62,10 @@ fn implied_outlives_bounds<'a, 'tcx>( }; let mut constraints = QueryRegionConstraints::default(); + let span = infcx.tcx.def_span(body_id); let Ok(InferOk { value: mut bounds, obligations }) = infcx .instantiate_nll_query_response_and_region_obligations( - &ObligationCause::dummy(), + &ObligationCause::dummy_with_span(span), param_env, &canonical_var_values, canonical_result, @@ -80,8 +81,6 @@ fn implied_outlives_bounds<'a, 'tcx>( bounds.retain(|bound| !bound.has_placeholders()); if !constraints.is_empty() { - let span = infcx.tcx.def_span(body_id); - debug!(?constraints); if !constraints.member_constraints.is_empty() { span_bug!(span, "{:#?}", constraints.member_constraints); @@ -101,7 +100,7 @@ fn implied_outlives_bounds<'a, 'tcx>( let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.dcx().span_delayed_bug( + infcx.dcx().span_bug( span, "implied_outlives_bounds failed to solve obligations from instantiation", ); diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 279c0003187..f8de19043e1 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1,5 +1,7 @@ //! Code for projecting associated types out of trait references. +use std::ops::ControlFlow; + use super::check_args_compatible; use super::specialization_graph; use super::translate_args; @@ -18,8 +20,9 @@ use rustc_middle::traits::ImplSourceUserDefinedData; use crate::errors::InherentProjectionNormalizationOverflow; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::infer::{BoundRegionConversionTime, InferCtxt, InferOk}; -use crate::traits::error_reporting::TypeErrCtxtExt as _; +use crate::infer::{BoundRegionConversionTime, InferOk}; +use crate::traits::normalize::normalize_with_depth; +use crate::traits::normalize::normalize_with_depth_to; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::select::ProjectionMatchesProjection; use rustc_data_structures::sso::SsoHashSet; @@ -27,21 +30,14 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::at::At; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::infer::DefineOpaqueTypes; -use rustc_infer::traits::FulfillmentError; -use rustc_infer::traits::ObligationCauseCode; -use rustc_infer::traits::TraitEngine; use rustc_middle::traits::select::OverflowError; -use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; use rustc_span::symbol::sym; -use std::collections::BTreeMap; -use std::ops::ControlFlow; - pub use rustc_middle::traits::Reveal; pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>; @@ -52,55 +48,6 @@ pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::AliasTy<'tcx>>; pub(super) struct InProgress; -#[extension(pub trait NormalizeExt<'tcx>)] -impl<'tcx> At<'_, 'tcx> { - /// Normalize a value using the `AssocTypeNormalizer`. - /// - /// This normalization should be used when the type contains inference variables or the - /// projection may be fallible. - fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> { - if self.infcx.next_trait_solver() { - InferOk { value, obligations: Vec::new() } - } else { - let mut selcx = SelectionContext::new(self.infcx); - let Normalized { value, obligations } = - normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value); - InferOk { value, obligations } - } - } - - /// Deeply normalizes `value`, replacing all aliases which can by normalized in - /// the current environment. In the new solver this errors in case normalization - /// fails or is ambiguous. This only normalizes opaque types with `Reveal::All`. - /// - /// In the old solver this simply uses `normalizes` and adds the nested obligations - /// to the `fulfill_cx`. This is necessary as we otherwise end up recomputing the - /// same goals in both a temporary and the shared context which negatively impacts - /// performance as these don't share caching. - /// - /// FIXME(-Znext-solver): This has the same behavior as `traits::fully_normalize` - /// in the new solver, but because of performance reasons, we currently reuse an - /// existing fulfillment context in the old solver. Once we also eagerly prove goals with - /// the old solver or have removed the old solver, remove `traits::fully_normalize` and - /// rename this function to `At::fully_normalize`. - fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>( - self, - value: T, - fulfill_cx: &mut dyn TraitEngine<'tcx>, - ) -> Result<T, Vec<FulfillmentError<'tcx>>> { - if self.infcx.next_trait_solver() { - crate::solve::deeply_normalize(self, value) - } else { - let value = self - .normalize(value) - .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); - let errors = fulfill_cx.select_where_possible(self.infcx); - let value = self.infcx.resolve_vars_if_possible(value); - if errors.is_empty() { Ok(value) } else { Err(errors) } - } - } -} - /// When attempting to resolve `<T as TraitRef>::Name` ... #[derive(Debug)] pub enum ProjectionError<'tcx> { @@ -338,770 +285,6 @@ fn project_and_unify_type<'cx, 'tcx>( } } -/// As `normalize`, but with a custom depth. -pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - value: T, -) -> Normalized<'tcx, T> -where - T: TypeFoldable<TyCtxt<'tcx>>, -{ - let mut obligations = Vec::new(); - let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations); - Normalized { value, obligations } -} - -#[instrument(level = "info", skip(selcx, param_env, cause, obligations))] -pub(crate) fn normalize_with_depth_to<'a, 'b, 'tcx, T>( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - value: T, - obligations: &mut Vec<PredicateObligation<'tcx>>, -) -> T -where - T: TypeFoldable<TyCtxt<'tcx>>, -{ - debug!(obligations.len = obligations.len()); - let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations); - let result = ensure_sufficient_stack(|| normalizer.fold(value)); - debug!(?result, obligations.len = normalizer.obligations.len()); - debug!(?normalizer.obligations,); - result -} - -#[instrument(level = "info", skip(selcx, param_env, cause, obligations))] -pub(crate) fn try_normalize_with_depth_to<'a, 'b, 'tcx, T>( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - value: T, - obligations: &mut Vec<PredicateObligation<'tcx>>, -) -> T -where - T: TypeFoldable<TyCtxt<'tcx>>, -{ - debug!(obligations.len = obligations.len()); - let mut normalizer = AssocTypeNormalizer::new_without_eager_inference_replacement( - selcx, - param_env, - cause, - depth, - obligations, - ); - let result = ensure_sufficient_stack(|| normalizer.fold(value)); - debug!(?result, obligations.len = normalizer.obligations.len()); - debug!(?normalizer.obligations,); - result -} - -pub(crate) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>( - value: &T, - reveal: Reveal, -) -> bool { - match reveal { - Reveal::UserFacing => value.has_type_flags( - ty::TypeFlags::HAS_TY_PROJECTION - | ty::TypeFlags::HAS_TY_INHERENT - | ty::TypeFlags::HAS_CT_PROJECTION, - ), - Reveal::All => value.has_type_flags( - ty::TypeFlags::HAS_TY_PROJECTION - | ty::TypeFlags::HAS_TY_INHERENT - | ty::TypeFlags::HAS_TY_OPAQUE - | ty::TypeFlags::HAS_CT_PROJECTION, - ), - } -} - -struct AssocTypeNormalizer<'a, 'b, 'tcx> { - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - obligations: &'a mut Vec<PredicateObligation<'tcx>>, - depth: usize, - universes: Vec<Option<ty::UniverseIndex>>, - /// If true, when a projection is unable to be completed, an inference - /// variable will be created and an obligation registered to project to that - /// inference variable. Also, constants will be eagerly evaluated. - eager_inference_replacement: bool, -} - -impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { - fn new( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - obligations: &'a mut Vec<PredicateObligation<'tcx>>, - ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { - debug_assert!(!selcx.infcx.next_trait_solver()); - AssocTypeNormalizer { - selcx, - param_env, - cause, - obligations, - depth, - universes: vec![], - eager_inference_replacement: true, - } - } - - fn new_without_eager_inference_replacement( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - obligations: &'a mut Vec<PredicateObligation<'tcx>>, - ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { - AssocTypeNormalizer { - selcx, - param_env, - cause, - obligations, - depth, - universes: vec![], - eager_inference_replacement: false, - } - } - - fn fold<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T { - let value = self.selcx.infcx.resolve_vars_if_possible(value); - debug!(?value); - - assert!( - !value.has_escaping_bound_vars(), - "Normalizing {value:?} without wrapping in a `Binder`" - ); - - if !needs_normalization(&value, self.param_env.reveal()) { - value - } else { - value.fold_with(self) - } - } -} - -impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.selcx.tcx() - } - - fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( - &mut self, - t: ty::Binder<'tcx, T>, - ) -> ty::Binder<'tcx, T> { - self.universes.push(None); - let t = t.super_fold_with(self); - self.universes.pop(); - t - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if !needs_normalization(&ty, self.param_env.reveal()) { - return ty; - } - - let (kind, data) = match *ty.kind() { - ty::Alias(kind, alias_ty) => (kind, alias_ty), - _ => return ty.super_fold_with(self), - }; - - // We try to be a little clever here as a performance optimization in - // cases where there are nested projections under binders. - // For example: - // ``` - // for<'a> fn(<T as Foo>::One<'a, Box<dyn Bar<'a, Item=<T as Foo>::Two<'a>>>>) - // ``` - // We normalize the args on the projection before the projecting, but - // if we're naive, we'll - // replace bound vars on inner, project inner, replace placeholders on inner, - // replace bound vars on outer, project outer, replace placeholders on outer - // - // However, if we're a bit more clever, we can replace the bound vars - // on the entire type before normalizing nested projections, meaning we - // replace bound vars on outer, project inner, - // project outer, replace placeholders on outer - // - // This is possible because the inner `'a` will already be a placeholder - // when we need to normalize the inner projection - // - // On the other hand, this does add a bit of complexity, since we only - // replace bound vars if the current type is a `Projection` and we need - // to make sure we don't forget to fold the args regardless. - - match kind { - ty::Opaque => { - // Only normalize `impl Trait` outside of type inference, usually in codegen. - match self.param_env.reveal() { - Reveal::UserFacing => ty.super_fold_with(self), - - Reveal::All => { - let recursion_limit = self.interner().recursion_limit(); - if !recursion_limit.value_within_limit(self.depth) { - self.selcx.infcx.err_ctxt().report_overflow_error( - &ty, - self.cause.span, - true, - |_| {}, - ); - } - - let args = data.args.fold_with(self); - let generic_ty = self.interner().type_of(data.def_id); - let concrete_ty = generic_ty.instantiate(self.interner(), args); - self.depth += 1; - let folded_ty = self.fold_ty(concrete_ty); - self.depth -= 1; - folded_ty - } - } - } - - ty::Projection if !data.has_escaping_bound_vars() => { - // This branch is *mostly* just an optimization: when we don't - // have escaping bound vars, we don't need to replace them with - // placeholders (see branch below). *Also*, we know that we can - // register an obligation to *later* project, since we know - // there won't be bound vars there. - let data = data.fold_with(self); - let normalized_ty = if self.eager_inference_replacement { - normalize_projection_type( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ) - } else { - opt_normalize_projection_type( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ) - .ok() - .flatten() - .unwrap_or_else(|| ty.super_fold_with(self).into()) - }; - debug!( - ?self.depth, - ?ty, - ?normalized_ty, - obligations.len = ?self.obligations.len(), - "AssocTypeNormalizer: normalized type" - ); - normalized_ty.ty().unwrap() - } - - ty::Projection => { - // If there are escaping bound vars, we temporarily replace the - // bound vars with placeholders. Note though, that in the case - // that we still can't project for whatever reason (e.g. self - // type isn't known enough), we *can't* register an obligation - // and return an inference variable (since then that obligation - // would have bound vars and that's a can of worms). Instead, - // we just give up and fall back to pretending like we never tried! - // - // Note: this isn't necessarily the final approach here; we may - // want to figure out how to register obligations with escaping vars - // or handle this some other way. - - let infcx = self.selcx.infcx; - let (data, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); - let data = data.fold_with(self); - let normalized_ty = opt_normalize_projection_type( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ) - .ok() - .flatten() - .map(|term| term.ty().unwrap()) - .map(|normalized_ty| { - PlaceholderReplacer::replace_placeholders( - infcx, - mapped_regions, - mapped_types, - mapped_consts, - &self.universes, - normalized_ty, - ) - }) - .unwrap_or_else(|| ty.super_fold_with(self)); - - debug!( - ?self.depth, - ?ty, - ?normalized_ty, - obligations.len = ?self.obligations.len(), - "AssocTypeNormalizer: normalized type" - ); - normalized_ty - } - ty::Weak => { - let recursion_limit = self.interner().recursion_limit(); - if !recursion_limit.value_within_limit(self.depth) { - self.selcx.infcx.err_ctxt().report_overflow_error( - &ty, - self.cause.span, - false, - |diag| { - diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow); - }, - ); - } - - let infcx = self.selcx.infcx; - self.obligations.extend( - infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map( - |(mut predicate, span)| { - if data.has_escaping_bound_vars() { - (predicate, ..) = BoundVarReplacer::replace_bound_vars( - infcx, - &mut self.universes, - predicate, - ); - } - let mut cause = self.cause.clone(); - cause.map_code(|code| { - ObligationCauseCode::TypeAlias(code, span, data.def_id) - }); - Obligation::new(infcx.tcx, cause, self.param_env, predicate) - }, - ), - ); - self.depth += 1; - let res = infcx - .tcx - .type_of(data.def_id) - .instantiate(infcx.tcx, data.args) - .fold_with(self); - self.depth -= 1; - res - } - - ty::Inherent if !data.has_escaping_bound_vars() => { - // This branch is *mostly* just an optimization: when we don't - // have escaping bound vars, we don't need to replace them with - // placeholders (see branch below). *Also*, we know that we can - // register an obligation to *later* project, since we know - // there won't be bound vars there. - - let data = data.fold_with(self); - - // FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement` - // here like `ty::Projection`? - normalize_inherent_projection( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ) - } - - ty::Inherent => { - let infcx = self.selcx.infcx; - let (data, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); - let data = data.fold_with(self); - let ty = normalize_inherent_projection( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ); - - PlaceholderReplacer::replace_placeholders( - infcx, - mapped_regions, - mapped_types, - mapped_consts, - &self.universes, - ty, - ) - } - } - } - - #[instrument(skip(self), level = "debug")] - fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { - let tcx = self.selcx.tcx(); - if tcx.features().generic_const_exprs - || !needs_normalization(&constant, self.param_env.reveal()) - { - constant - } else { - let constant = constant.super_fold_with(self); - debug!(?constant, ?self.param_env); - with_replaced_escaping_bound_vars( - self.selcx.infcx, - &mut self.universes, - constant, - |constant| constant.normalize(tcx, self.param_env), - ) - } - } - - #[inline] - fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { - if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) { - p.super_fold_with(self) - } else { - p - } - } -} - -pub struct BoundVarReplacer<'me, 'tcx> { - infcx: &'me InferCtxt<'tcx>, - // These three maps track the bound variable that were replaced by placeholders. It might be - // nice to remove these since we already have the `kind` in the placeholder; we really just need - // the `var` (but we *could* bring that into scope if we were to track them as we pass them). - mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, - mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, - mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, - // The current depth relative to *this* folding, *not* the entire normalization. In other words, - // the depth of binders we've passed here. - current_index: ty::DebruijnIndex, - // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy: - // we don't actually create a universe until we see a bound var we have to replace. - universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>, -} - -/// Executes `f` on `value` after replacing all escaping bound variables with placeholders -/// and then replaces these placeholders with the original bound variables in the result. -/// -/// In most places, bound variables should be replaced right when entering a binder, making -/// this function unnecessary. However, normalization currently does not do that, so we have -/// to do this lazily. -/// -/// You should not add any additional uses of this function, at least not without first -/// discussing it with t-types. -/// -/// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during -/// normalization as well, at which point this function will be unnecessary and can be removed. -pub fn with_replaced_escaping_bound_vars< - 'a, - 'tcx, - T: TypeFoldable<TyCtxt<'tcx>>, - R: TypeFoldable<TyCtxt<'tcx>>, ->( - infcx: &'a InferCtxt<'tcx>, - universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>, - value: T, - f: impl FnOnce(T) -> R, -) -> R { - if value.has_escaping_bound_vars() { - let (value, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value); - let result = f(value); - PlaceholderReplacer::replace_placeholders( - infcx, - mapped_regions, - mapped_types, - mapped_consts, - universe_indices, - result, - ) - } else { - f(value) - } -} - -impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { - /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that - /// use a binding level above `universe_indices.len()`, we fail. - pub fn replace_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>( - infcx: &'me InferCtxt<'tcx>, - universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>, - value: T, - ) -> ( - T, - BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, - BTreeMap<ty::PlaceholderType, ty::BoundTy>, - BTreeMap<ty::PlaceholderConst, ty::BoundVar>, - ) { - let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new(); - let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new(); - let mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar> = BTreeMap::new(); - - let mut replacer = BoundVarReplacer { - infcx, - mapped_regions, - mapped_types, - mapped_consts, - current_index: ty::INNERMOST, - universe_indices, - }; - - let value = value.fold_with(&mut replacer); - - (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts) - } - - fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex { - let infcx = self.infcx; - let index = - self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1; - let universe = self.universe_indices[index].unwrap_or_else(|| { - for i in self.universe_indices.iter_mut().take(index + 1) { - *i = i.or_else(|| Some(infcx.create_next_universe())) - } - self.universe_indices[index].unwrap() - }); - universe - } -} - -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( - &mut self, - t: ty::Binder<'tcx, T>, - ) -> ty::Binder<'tcx, T> { - self.current_index.shift_in(1); - let t = t.super_fold_with(self); - self.current_index.shift_out(1); - t - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - ty::ReBound(debruijn, _) - if debruijn.as_usize() - >= self.current_index.as_usize() + self.universe_indices.len() => - { - bug!( - "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}", - self.universe_indices - ); - } - ty::ReBound(debruijn, br) if debruijn >= self.current_index => { - let universe = self.universe_for(debruijn); - let p = ty::PlaceholderRegion { universe, bound: br }; - self.mapped_regions.insert(p, br); - ty::Region::new_placeholder(self.infcx.tcx, p) - } - _ => r, - } - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match *t.kind() { - ty::Bound(debruijn, _) - if debruijn.as_usize() + 1 - > self.current_index.as_usize() + self.universe_indices.len() => - { - bug!( - "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}", - self.universe_indices - ); - } - ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { - let universe = self.universe_for(debruijn); - let p = ty::PlaceholderType { universe, bound: bound_ty }; - self.mapped_types.insert(p, bound_ty); - Ty::new_placeholder(self.infcx.tcx, p) - } - _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), - _ => t, - } - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - match ct.kind() { - ty::ConstKind::Bound(debruijn, _) - if debruijn.as_usize() + 1 - > self.current_index.as_usize() + self.universe_indices.len() => - { - bug!( - "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}", - self.universe_indices - ); - } - ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => { - let universe = self.universe_for(debruijn); - let p = ty::PlaceholderConst { universe, bound: bound_const }; - self.mapped_consts.insert(p, bound_const); - ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty()) - } - _ => ct.super_fold_with(self), - } - } - - fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { - if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } - } -} - -/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. -pub struct PlaceholderReplacer<'me, 'tcx> { - infcx: &'me InferCtxt<'tcx>, - mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, - mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, - mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, - universe_indices: &'me [Option<ty::UniverseIndex>], - current_index: ty::DebruijnIndex, -} - -impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { - pub fn replace_placeholders<T: TypeFoldable<TyCtxt<'tcx>>>( - infcx: &'me InferCtxt<'tcx>, - mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, - mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, - mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, - universe_indices: &'me [Option<ty::UniverseIndex>], - value: T, - ) -> T { - let mut replacer = PlaceholderReplacer { - infcx, - mapped_regions, - mapped_types, - mapped_consts, - universe_indices, - current_index: ty::INNERMOST, - }; - value.fold_with(&mut replacer) - } -} - -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( - &mut self, - t: ty::Binder<'tcx, T>, - ) -> ty::Binder<'tcx, T> { - if !t.has_placeholders() && !t.has_infer() { - return t; - } - self.current_index.shift_in(1); - let t = t.super_fold_with(self); - self.current_index.shift_out(1); - t - } - - fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> { - let r1 = match *r0 { - ty::ReVar(vid) => self - .infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .opportunistic_resolve_var(self.infcx.tcx, vid), - _ => r0, - }; - - let r2 = match *r1 { - ty::RePlaceholder(p) => { - let replace_var = self.mapped_regions.get(&p); - match replace_var { - Some(replace_var) => { - let index = self - .universe_indices - .iter() - .position(|u| matches!(u, Some(pu) if *pu == p.universe)) - .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); - let db = ty::DebruijnIndex::from_usize( - self.universe_indices.len() - index + self.current_index.as_usize() - 1, - ); - ty::Region::new_bound(self.interner(), db, *replace_var) - } - None => r1, - } - } - _ => r1, - }; - - debug!(?r0, ?r1, ?r2, "fold_region"); - - r2 - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty = self.infcx.shallow_resolve(ty); - match *ty.kind() { - ty::Placeholder(p) => { - let replace_var = self.mapped_types.get(&p); - match replace_var { - Some(replace_var) => { - let index = self - .universe_indices - .iter() - .position(|u| matches!(u, Some(pu) if *pu == p.universe)) - .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); - let db = ty::DebruijnIndex::from_usize( - self.universe_indices.len() - index + self.current_index.as_usize() - 1, - ); - Ty::new_bound(self.infcx.tcx, db, *replace_var) - } - None => { - if ty.has_infer() { - ty.super_fold_with(self) - } else { - ty - } - } - } - } - - _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self), - _ => ty, - } - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - let ct = self.infcx.shallow_resolve(ct); - if let ty::ConstKind::Placeholder(p) = ct.kind() { - let replace_var = self.mapped_consts.get(&p); - match replace_var { - Some(replace_var) => { - let index = self - .universe_indices - .iter() - .position(|u| matches!(u, Some(pu) if *pu == p.universe)) - .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); - let db = ty::DebruijnIndex::from_usize( - self.universe_indices.len() - index + self.current_index.as_usize() - 1, - ); - ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty()) - } - None => { - if ct.has_infer() { - ct.super_fold_with(self) - } else { - ct - } - } - } - } else { - ct.super_fold_with(self) - } - } -} - /// The guts of `normalize`: normalize a specific projection like `<T /// as Trait>::Item`. The result is always a type (and possibly /// additional obligations). If ambiguity arises, which implies that @@ -1146,7 +329,7 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>( /// function takes an obligations vector and appends to it directly, which is /// slightly uglier but avoids the need for an extra short-lived allocation. #[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] -fn opt_normalize_projection_type<'a, 'b, 'tcx>( +pub(super) fn opt_normalize_projection_type<'a, 'b, 'tcx>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::AliasTy<'tcx>, @@ -1250,14 +433,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term); let mut result = if projected_term.has_projections() { - let mut normalizer = AssocTypeNormalizer::new( + let normalized_ty = normalize_with_depth_to( selcx, param_env, cause, depth + 1, + projected_term, &mut projected_obligations, ); - let normalized_ty = normalizer.fold(projected_term); Normalized { value: normalized_ty, obligations: projected_obligations } } else { @@ -1462,11 +645,9 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>( match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) { Ok(mut ok) => obligations.append(&mut ok.obligations), Err(_) => { - tcx.dcx().span_delayed_bug( + tcx.dcx().span_bug( cause.span, - format!( - "{self_ty:?} was a subtype of {impl_ty:?} during selection but now it is not" - ), + format!("{self_ty:?} was equal to {impl_ty:?} during selection but now it is not"), ); } } @@ -2011,7 +1192,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( obligation.cause.span, format!("Cannot project an associated type from `{impl_source:?}`"), ); - return Err(()); + return Err(()) } }; diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 0f6c0abd280..70fd0b7e50b 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -1,12 +1,13 @@ //! Code for the 'normalization' query. This consists of a wrapper //! which folds deeply, invoking the underlying -//! `normalize_projection_ty` query when it encounters projections. +//! `normalize_canonicalized_projection_ty` query when it encounters projections. use crate::infer::at::At; use crate::infer::canonical::OriginalQueryValues; use crate::infer::{InferCtxt, InferOk}; use crate::traits::error_reporting::TypeErrCtxtExt; -use crate::traits::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; +use crate::traits::normalize::needs_normalization; +use crate::traits::{BoundVarReplacer, PlaceholderReplacer}; use crate::traits::{ObligationCause, PredicateObligation, Reveal}; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -270,9 +271,9 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> debug!("QueryNormalizer: c_data = {:#?}", c_data); debug!("QueryNormalizer: orig_values = {:#?}", orig_values); let result = match kind { - ty::Projection => tcx.normalize_projection_ty(c_data), - ty::Weak => tcx.normalize_weak_ty(c_data), - ty::Inherent => tcx.normalize_inherent_projection_ty(c_data), + ty::Projection => tcx.normalize_canonicalized_projection_ty(c_data), + ty::Weak => tcx.normalize_canonicalized_weak_ty(c_data), + ty::Inherent => tcx.normalize_canonicalized_inherent_projection_ty(c_data), kind => unreachable!("did not expect {kind:?} due to match arm above"), }?; // We don't expect ambiguity. @@ -307,10 +308,10 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> } else { result.normalized_ty }; - // `tcx.normalize_projection_ty` may normalize to a type that still has - // unevaluated consts, so keep normalizing here if that's the case. - // Similarly, `tcx.normalize_weak_ty` will only unwrap one layer of type - // and we need to continue folding it to reveal the TAIT behind it. + // `tcx.normalize_canonicalized_projection_ty` may normalize to a type that + // still has unevaluated consts, so keep normalizing here if that's the case. + // Similarly, `tcx.normalize_canonicalized_weak_ty` will only unwrap one layer + // of type and we need to continue folding it to reveal the TAIT behind it. if res != ty && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Weak) { @@ -335,7 +336,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> let constant = constant.try_super_fold_with(self)?; debug!(?constant, ?self.param_env); - Ok(crate::traits::project::with_replaced_escaping_bound_vars( + Ok(crate::traits::with_replaced_escaping_bound_vars( self.infcx, &mut self.universes, constant, diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index fb09f094d37..12ee778ee05 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -190,10 +190,9 @@ where } } if !progress { - return Err(infcx.dcx().span_delayed_bug( - span, - format!("ambiguity processing {obligations:?} from {self:?}"), - )); + infcx + .dcx() + .span_bug(span, format!("ambiguity processing {obligations:?} from {self:?}")); } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index e4a70c537d2..f76be876948 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::{ }; use rustc_span::def_id::DefId; -use crate::traits::project::{normalize_with_depth, normalize_with_depth_to}; +use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::util::{self, closure_trait_ref_and_return_type}; use crate::traits::vtable::{ count_own_vtable_entries, prepare_vtable_segments, vtable_trait_first_method_offset, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 5bcf46a96ed..1146f869fc1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -8,7 +8,6 @@ use self::SelectionCandidate::*; use super::coherence::{self, Conflict}; use super::const_evaluatable; use super::project; -use super::project::normalize_with_depth_to; use super::project::ProjectionTyObligation; use super::util; use super::util::closure_trait_ref_and_return_type; @@ -22,14 +21,15 @@ use super::{ use crate::infer::{InferCtxt, InferOk, TypeFreshener}; use crate::solve::InferCtxtSelectExt; use crate::traits::error_reporting::TypeErrCtxtExt; -use crate::traits::project::try_normalize_with_depth_to; +use crate::traits::normalize::normalize_with_depth; +use crate::traits::normalize::normalize_with_depth_to; use crate::traits::project::ProjectAndUnifyResult; use crate::traits::project::ProjectionCacheKeyExt; use crate::traits::ProjectionCacheKey; use crate::traits::Unimplemented; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::Diagnostic; +use rustc_errors::{DiagnosticBuilder, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::BoundRegionConversionTime; @@ -70,7 +70,10 @@ pub enum IntercrateAmbiguityCause<'tcx> { impl<'tcx> IntercrateAmbiguityCause<'tcx> { /// Emits notes when the overlap is caused by complex intercrate ambiguities. /// See #23980 for details. - pub fn add_intercrate_ambiguity_hint(&self, err: &mut Diagnostic) { + pub fn add_intercrate_ambiguity_hint<G: EmissionGuarantee>( + &self, + err: &mut DiagnosticBuilder<'_, G>, + ) { err.note(self.intercrate_ambiguity_hint()); } @@ -1070,7 +1073,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { && fresh_trait_pred.is_global() { let mut nested_obligations = Vec::new(); - let predicate = try_normalize_with_depth_to( + let predicate = normalize_with_depth_to( this, param_env, obligation.cause.clone(), @@ -1662,7 +1665,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } let Normalized { value: trait_bound, obligations: _ } = ensure_sufficient_stack(|| { - project::normalize_with_depth( + normalize_with_depth( self, obligation.param_env, obligation.cause.clone(), @@ -1718,7 +1721,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); let infer_projection = if potentially_unnormalized_candidates { ensure_sufficient_stack(|| { - project::normalize_with_depth_to( + normalize_with_depth_to( self, obligation.param_env, obligation.cause.clone(), @@ -2383,7 +2386,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { let placeholder_ty = self.infcx.enter_forall_and_leak_universe(ty); let Normalized { value: normalized_ty, mut obligations } = ensure_sufficient_stack(|| { - project::normalize_with_depth( + normalize_with_depth( self, param_env, cause.clone(), @@ -2480,7 +2483,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } = ensure_sufficient_stack(|| { - project::normalize_with_depth( + normalize_with_depth( self, obligation.param_env, obligation.cause.clone(), diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index e1a49ecf1b6..56bc2f2cf25 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -20,7 +20,7 @@ use crate::traits::{ self, coherence, FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt, }; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{codes::*, DelayDm, Diagnostic}; +use rustc_errors::{codes::*, DelayDm, DiagnosticBuilder, EmissionGuarantee}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{GenericArgs, GenericArgsRef}; @@ -395,11 +395,11 @@ fn report_conflicting_impls<'tcx>( // Work to be done after we've built the DiagnosticBuilder. We have to define it // now because the lint emit methods don't return back the DiagnosticBuilder // that's passed in. - fn decorate<'tcx>( + fn decorate<'tcx, G: EmissionGuarantee>( tcx: TyCtxt<'tcx>, overlap: &OverlapError<'tcx>, impl_span: Span, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, ) { if (overlap.trait_ref, overlap.self_ty).references_error() { err.downgrade_to_delayed_bug(); diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index af172eb0713..d66c4004ef5 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -1,11 +1,14 @@ +use std::collections::BTreeMap; + use super::NormalizeExt; use super::{ObligationCause, PredicateObligation, SelectionContext}; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::Diagnostic; +use rustc_errors::DiagnosticBuilder; use rustc_hir::def_id::DefId; -use rustc_infer::infer::InferOk; +use rustc_infer::infer::{InferCtxt, InferOk}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_span::Span; use smallvec::SmallVec; @@ -43,7 +46,7 @@ impl<'tcx> TraitAliasExpansionInfo<'tcx> { /// trait aliases. pub fn label_with_exp_info( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, top_label: &'static str, use_desc: &str, ) { @@ -382,3 +385,336 @@ pub fn check_args_compatible<'tcx>( let args = &args[0..generics.count().min(args.len())]; check_args_compatible_inner(tcx, generics, args) } + +/// Executes `f` on `value` after replacing all escaping bound variables with placeholders +/// and then replaces these placeholders with the original bound variables in the result. +/// +/// In most places, bound variables should be replaced right when entering a binder, making +/// this function unnecessary. However, normalization currently does not do that, so we have +/// to do this lazily. +/// +/// You should not add any additional uses of this function, at least not without first +/// discussing it with t-types. +/// +/// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during +/// normalization as well, at which point this function will be unnecessary and can be removed. +pub fn with_replaced_escaping_bound_vars< + 'a, + 'tcx, + T: TypeFoldable<TyCtxt<'tcx>>, + R: TypeFoldable<TyCtxt<'tcx>>, +>( + infcx: &'a InferCtxt<'tcx>, + universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>, + value: T, + f: impl FnOnce(T) -> R, +) -> R { + if value.has_escaping_bound_vars() { + let (value, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value); + let result = f(value); + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universe_indices, + result, + ) + } else { + f(value) + } +} + +pub struct BoundVarReplacer<'me, 'tcx> { + infcx: &'me InferCtxt<'tcx>, + // These three maps track the bound variable that were replaced by placeholders. It might be + // nice to remove these since we already have the `kind` in the placeholder; we really just need + // the `var` (but we *could* bring that into scope if we were to track them as we pass them). + mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, + mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, + // The current depth relative to *this* folding, *not* the entire normalization. In other words, + // the depth of binders we've passed here. + current_index: ty::DebruijnIndex, + // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy: + // we don't actually create a universe until we see a bound var we have to replace. + universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>, +} + +impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { + /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that + /// use a binding level above `universe_indices.len()`, we fail. + pub fn replace_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>( + infcx: &'me InferCtxt<'tcx>, + universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>, + value: T, + ) -> ( + T, + BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + BTreeMap<ty::PlaceholderType, ty::BoundTy>, + BTreeMap<ty::PlaceholderConst, ty::BoundVar>, + ) { + let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new(); + let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new(); + let mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar> = BTreeMap::new(); + + let mut replacer = BoundVarReplacer { + infcx, + mapped_regions, + mapped_types, + mapped_consts, + current_index: ty::INNERMOST, + universe_indices, + }; + + let value = value.fold_with(&mut replacer); + + (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts) + } + + fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex { + let infcx = self.infcx; + let index = + self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1; + let universe = self.universe_indices[index].unwrap_or_else(|| { + for i in self.universe_indices.iter_mut().take(index + 1) { + *i = i.or_else(|| Some(infcx.create_next_universe())) + } + self.universe_indices[index].unwrap() + }); + universe + } +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + ty::ReBound(debruijn, _) + if debruijn.as_usize() + >= self.current_index.as_usize() + self.universe_indices.len() => + { + bug!( + "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); + } + ty::ReBound(debruijn, br) if debruijn >= self.current_index => { + let universe = self.universe_for(debruijn); + let p = ty::PlaceholderRegion { universe, bound: br }; + self.mapped_regions.insert(p, br); + ty::Region::new_placeholder(self.infcx.tcx, p) + } + _ => r, + } + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match *t.kind() { + ty::Bound(debruijn, _) + if debruijn.as_usize() + 1 + > self.current_index.as_usize() + self.universe_indices.len() => + { + bug!( + "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); + } + ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { + let universe = self.universe_for(debruijn); + let p = ty::PlaceholderType { universe, bound: bound_ty }; + self.mapped_types.insert(p, bound_ty); + Ty::new_placeholder(self.infcx.tcx, p) + } + _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), + _ => t, + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + match ct.kind() { + ty::ConstKind::Bound(debruijn, _) + if debruijn.as_usize() + 1 + > self.current_index.as_usize() + self.universe_indices.len() => + { + bug!( + "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); + } + ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => { + let universe = self.universe_for(debruijn); + let p = ty::PlaceholderConst { universe, bound: bound_const }; + self.mapped_consts.insert(p, bound_const); + ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty()) + } + _ => ct.super_fold_with(self), + } + } + + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } + } +} + +/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. +pub struct PlaceholderReplacer<'me, 'tcx> { + infcx: &'me InferCtxt<'tcx>, + mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, + mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, + universe_indices: &'me [Option<ty::UniverseIndex>], + current_index: ty::DebruijnIndex, +} + +impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { + pub fn replace_placeholders<T: TypeFoldable<TyCtxt<'tcx>>>( + infcx: &'me InferCtxt<'tcx>, + mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, + mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, + universe_indices: &'me [Option<ty::UniverseIndex>], + value: T, + ) -> T { + let mut replacer = PlaceholderReplacer { + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universe_indices, + current_index: ty::INNERMOST, + }; + value.fold_with(&mut replacer) + } +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + if !t.has_placeholders() && !t.has_infer() { + return t; + } + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> { + let r1 = match *r0 { + ty::ReVar(vid) => self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.infcx.tcx, vid), + _ => r0, + }; + + let r2 = match *r1 { + ty::RePlaceholder(p) => { + let replace_var = self.mapped_regions.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + ty::Region::new_bound(self.interner(), db, *replace_var) + } + None => r1, + } + } + _ => r1, + }; + + debug!(?r0, ?r1, ?r2, "fold_region"); + + r2 + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + let ty = self.infcx.shallow_resolve(ty); + match *ty.kind() { + ty::Placeholder(p) => { + let replace_var = self.mapped_types.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + Ty::new_bound(self.infcx.tcx, db, *replace_var) + } + None => { + if ty.has_infer() { + ty.super_fold_with(self) + } else { + ty + } + } + } + } + + _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self), + _ => ty, + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + let ct = self.infcx.shallow_resolve(ct); + if let ty::ConstKind::Placeholder(p) = ct.kind() { + let replace_var = self.mapped_consts.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty()) + } + None => { + if ct.has_infer() { + ct.super_fold_with(self) + } else { + ct + } + } + } + } else { + ct.super_fold_with(self) + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index c727ef53d55..15059bc6613 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -313,7 +313,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // Don't normalize the whole obligation, the param env is either // already normalized, or we're currently normalizing the // param_env. Either way we should only normalize the predicate. - let normalized_predicate = traits::project::normalize_with_depth_to( + let normalized_predicate = traits::normalize::normalize_with_depth_to( &mut selcx, param_env, cause.clone(), diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index 07089d5f19e..92a19fb9119 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -5,7 +5,7 @@ use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::query::{ - normalize::NormalizationResult, CanonicalProjectionGoal, NoSolution, + normalize::NormalizationResult, CanonicalAliasGoal, NoSolution, }; use rustc_trait_selection::traits::{ self, FulfillmentErrorCode, ObligationCause, SelectionContext, @@ -13,22 +13,23 @@ use rustc_trait_selection::traits::{ pub(crate) fn provide(p: &mut Providers) { *p = Providers { - normalize_projection_ty, - normalize_weak_ty, - normalize_inherent_projection_ty, + normalize_canonicalized_projection_ty, + normalize_canonicalized_weak_ty, + normalize_canonicalized_inherent_projection_ty, ..*p }; } -fn normalize_projection_ty<'tcx>( +fn normalize_canonicalized_projection_ty<'tcx>( tcx: TyCtxt<'tcx>, - goal: CanonicalProjectionGoal<'tcx>, + goal: CanonicalAliasGoal<'tcx>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> { - debug!("normalize_provider(goal={:#?})", goal); + debug!("normalize_canonicalized_projection_ty(goal={:#?})", goal); tcx.infer_ctxt().enter_canonical_trait_query( &goal, |ocx, ParamEnvAnd { param_env, value: goal }| { + debug_assert!(!ocx.infcx.next_trait_solver()); let selcx = &mut SelectionContext::new(ocx.infcx); let cause = ObligationCause::dummy(); let mut obligations = vec![]; @@ -45,36 +46,35 @@ fn normalize_projection_ty<'tcx>( // are recursive (given some generic parameters of the opaque's type variables). // In that case, we may only realize a cycle error when calling // `normalize_erasing_regions` in mono. - if !ocx.infcx.next_trait_solver() { - let errors = ocx.select_where_possible(); - if !errors.is_empty() { - // Rustdoc may attempt to normalize type alias types which are not - // well-formed. Rustdoc also normalizes types that are just not - // well-formed, since we don't do as much HIR analysis (checking - // that impl vars are constrained by the signature, for example). - if !tcx.sess.opts.actually_rustdoc { - for error in &errors { - if let FulfillmentErrorCode::Cycle(cycle) = &error.code { - ocx.infcx.err_ctxt().report_overflow_obligation_cycle(cycle); - } + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + // Rustdoc may attempt to normalize type alias types which are not + // well-formed. Rustdoc also normalizes types that are just not + // well-formed, since we don't do as much HIR analysis (checking + // that impl vars are constrained by the signature, for example). + if !tcx.sess.opts.actually_rustdoc { + for error in &errors { + if let FulfillmentErrorCode::Cycle(cycle) = &error.code { + ocx.infcx.err_ctxt().report_overflow_obligation_cycle(cycle); } } - return Err(NoSolution); } + return Err(NoSolution); } - // FIXME(associated_const_equality): All users of normalize_projection_ty expected - // a type, but there is the possibility it could've been a const now. Maybe change - // it to a Term later? + + // FIXME(associated_const_equality): All users of normalize_canonicalized_projection_ty + // expected a type, but there is the possibility it could've been a const now. + // Maybe change it to a Term later? Ok(NormalizationResult { normalized_ty: answer.ty().unwrap() }) }, ) } -fn normalize_weak_ty<'tcx>( +fn normalize_canonicalized_weak_ty<'tcx>( tcx: TyCtxt<'tcx>, - goal: CanonicalProjectionGoal<'tcx>, + goal: CanonicalAliasGoal<'tcx>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> { - debug!("normalize_provider(goal={:#?})", goal); + debug!("normalize_canonicalized_weak_ty(goal={:#?})", goal); tcx.infer_ctxt().enter_canonical_trait_query( &goal, @@ -96,11 +96,11 @@ fn normalize_weak_ty<'tcx>( ) } -fn normalize_inherent_projection_ty<'tcx>( +fn normalize_canonicalized_inherent_projection_ty<'tcx>( tcx: TyCtxt<'tcx>, - goal: CanonicalProjectionGoal<'tcx>, + goal: CanonicalAliasGoal<'tcx>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> { - debug!("normalize_provider(goal={:#?})", goal); + debug!("normalize_canonicalized_inherent_projection_ty(goal={:#?})", goal); tcx.infer_ctxt().enter_canonical_trait_query( &goal, diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 5fc93d666ab..3e3bccce47f 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -246,7 +246,7 @@ fn resolve_associated_item<'tcx>( span: tcx.def_span(trait_item_id), }) } - } else if tcx.fn_trait_kind_from_def_id(trait_ref.def_id).is_some() { + } else if let Some(target_kind) = tcx.fn_trait_kind_from_def_id(trait_ref.def_id) { // FIXME: This doesn't check for malformed libcore that defines, e.g., // `trait Fn { fn call_once(&self) { .. } }`. This is mostly for extension // methods. @@ -265,13 +265,7 @@ fn resolve_associated_item<'tcx>( } match *rcvr_args.type_at(0).kind() { ty::Closure(closure_def_id, args) => { - let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap(); - Some(Instance::resolve_closure( - tcx, - closure_def_id, - args, - trait_closure_kind, - )) + Some(Instance::resolve_closure(tcx, closure_def_id, args, target_kind)) } ty::FnDef(..) | ty::FnPtr(..) => Some(Instance { def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_args.type_at(0)), @@ -324,13 +318,7 @@ fn resolve_associated_item<'tcx>( } } ty::Closure(closure_def_id, args) => { - let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap(); - Some(Instance::resolve_closure( - tcx, - closure_def_id, - args, - trait_closure_kind, - )) + Some(Instance::resolve_closure(tcx, closure_def_id, args, target_kind)) } ty::FnDef(..) | ty::FnPtr(..) => Some(Instance { def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_args.type_at(0)), diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 96f8148bf72..b00330d335e 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -90,8 +90,7 @@ fn univariant_uninterned<'tcx>( let dl = cx.data_layout(); let pack = repr.pack; if pack.is_some() && repr.align.is_some() { - cx.tcx.dcx().delayed_bug("struct cannot be packed and aligned"); - return Err(cx.tcx.arena.alloc(LayoutError::Unknown(ty))); + cx.tcx.dcx().bug("struct cannot be packed and aligned"); } cx.univariant(dl, fields, repr, kind).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty))) diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 329cf32cad5..86c7551882a 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -337,7 +337,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInAssocTypeCollector<'tcx> { .instantiate(self.0.tcx, impl_args) .visit_with(self); } else { - self.0.tcx.dcx().span_delayed_bug( + self.0.tcx.dcx().span_bug( self.0.tcx.def_span(assoc.def_id), "item had incorrect args", ); diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 1da26cfc242..b38ef2ad84d 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -69,32 +69,35 @@ bitflags! { /// Does this have `Projection`? const HAS_TY_PROJECTION = 1 << 10; - /// Does this have `Inherent`? - const HAS_TY_INHERENT = 1 << 11; + /// Does this have `Weak`? + const HAS_TY_WEAK = 1 << 11; /// Does this have `Opaque`? const HAS_TY_OPAQUE = 1 << 12; + /// Does this have `Inherent`? + const HAS_TY_INHERENT = 1 << 13; /// Does this have `ConstKind::Unevaluated`? - const HAS_CT_PROJECTION = 1 << 13; + const HAS_CT_PROJECTION = 1 << 14; /// Could this type be normalized further? const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits() + | TypeFlags::HAS_TY_WEAK.bits() | TypeFlags::HAS_TY_OPAQUE.bits() | TypeFlags::HAS_TY_INHERENT.bits() | TypeFlags::HAS_CT_PROJECTION.bits(); /// Is an error type/const reachable? - const HAS_ERROR = 1 << 14; + const HAS_ERROR = 1 << 15; /// Does this have any region that "appears free" in the type? /// Basically anything but `ReBound` and `ReErased`. - const HAS_FREE_REGIONS = 1 << 15; + const HAS_FREE_REGIONS = 1 << 16; /// Does this have any `ReBound` regions? - const HAS_RE_BOUND = 1 << 16; + const HAS_RE_BOUND = 1 << 17; /// Does this have any `Bound` types? - const HAS_TY_BOUND = 1 << 17; + const HAS_TY_BOUND = 1 << 18; /// Does this have any `ConstKind::Bound` consts? - const HAS_CT_BOUND = 1 << 18; + const HAS_CT_BOUND = 1 << 19; /// Does this have any bound variables? /// Used to check if a global bound is safe to evaluate. const HAS_BOUND_VARS = TypeFlags::HAS_RE_BOUND.bits() @@ -102,22 +105,22 @@ bitflags! { | TypeFlags::HAS_CT_BOUND.bits(); /// Does this have any `ReErased` regions? - const HAS_RE_ERASED = 1 << 19; + const HAS_RE_ERASED = 1 << 20; /// Does this value have parameters/placeholders/inference variables which could be /// replaced later, in a way that would change the results of `impl` specialization? - const STILL_FURTHER_SPECIALIZABLE = 1 << 20; + const STILL_FURTHER_SPECIALIZABLE = 1 << 21; /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`? - const HAS_TY_FRESH = 1 << 21; + const HAS_TY_FRESH = 1 << 22; /// Does this value have `InferConst::Fresh`? - const HAS_CT_FRESH = 1 << 22; + const HAS_CT_FRESH = 1 << 23; /// Does this have `Coroutine` or `CoroutineWitness`? - const HAS_TY_COROUTINE = 1 << 23; + const HAS_TY_COROUTINE = 1 << 24; /// Does this have any binders with bound vars (e.g. that need to be anonymized)? - const HAS_BINDER_VARS = 1 << 24; + const HAS_BINDER_VARS = 1 << 25; } } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 7728ee0e842..7d2c42a6dbe 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -95,9 +95,6 @@ pub trait Interner: Sized { fn mk_bound_ty(self, debruijn: DebruijnIndex, var: BoundVar) -> Self::Ty; fn mk_bound_region(self, debruijn: DebruijnIndex, var: BoundVar) -> Self::Region; fn mk_bound_const(self, debruijn: DebruijnIndex, var: BoundVar, ty: Self::Ty) -> Self::Const; - - /// Assert that an error has been delayed or emitted. - fn expect_error_or_delayed_bug(); } /// Common capabilities of placeholder kinds diff --git a/compiler/stable_mir/src/error.rs b/compiler/stable_mir/src/error.rs index 7085fa937c9..9e3f4936944 100644 --- a/compiler/stable_mir/src/error.rs +++ b/compiler/stable_mir/src/error.rs @@ -15,10 +15,8 @@ macro_rules! error { /// An error type used to represent an error that has already been reported by the compiler. #[derive(Clone, Copy, PartialEq, Eq)] pub enum CompilerError<T> { - /// Internal compiler error (I.e.: Compiler crashed). - ICE, - /// Compilation failed. - CompilationFailed, + /// Compilation failed, either due to normal errors or ICE. + Failed, /// Compilation was interrupted. Interrupted(T), /// Compilation skipped. This happens when users invoke rustc to retrieve information such as @@ -54,8 +52,7 @@ where { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - CompilerError::ICE => write!(f, "Internal Compiler Error"), - CompilerError::CompilationFailed => write!(f, "Compilation Failed"), + CompilerError::Failed => write!(f, "Compilation Failed"), CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason}"), CompilerError::Skipped => write!(f, "Compilation Skipped"), } @@ -68,8 +65,7 @@ where { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - CompilerError::ICE => write!(f, "Internal Compiler Error"), - CompilerError::CompilationFailed => write!(f, "Compilation Failed"), + CompilerError::Failed => write!(f, "Compilation Failed"), CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason:?}"), CompilerError::Skipped => write!(f, "Compilation Skipped"), } |
