diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/messages.ftl | 2 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/errors.rs | 24 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/validity.rs | 85 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/lib.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/method/suggest.rs | 96 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/mir/interpret/error.rs | 9 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/lints.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/late.rs | 18 | ||||
| -rw-r--r-- | compiler/rustc_session/src/config.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs | 9 |
11 files changed, 193 insertions, 67 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index b3d684086c2..65643e93d27 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -698,7 +698,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ), .. }) => { - let hir::Ty { span, .. } = inputs[local.index() - 1]; + let hir::Ty { span, .. } = *inputs.get(local.index() - 1)?; Some(span) } _ => None, diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 4a6d4fe930c..c456e40d7c1 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -453,7 +453,7 @@ const_eval_validation_invalid_fn_ptr = {$front_matter}: encountered {$value}, bu const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer -const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in a `const` or `static` +const_eval_validation_mutable_ref_in_const_or_static = {$front_matter}: encountered mutable reference in a `const` or `static` const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!` const_eval_validation_null_box = {$front_matter}: encountered a null box diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 11679ab77e3..2fd34b3c7fc 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -603,18 +603,18 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { PtrToUninhabited { ptr_kind: PointerKind::Box, .. } => { const_eval_validation_box_to_uninhabited } - PtrToUninhabited { ptr_kind: PointerKind::Ref, .. } => { + PtrToUninhabited { ptr_kind: PointerKind::Ref(_), .. } => { const_eval_validation_ref_to_uninhabited } PtrToStatic { ptr_kind: PointerKind::Box } => const_eval_validation_box_to_static, - PtrToStatic { ptr_kind: PointerKind::Ref } => const_eval_validation_ref_to_static, + PtrToStatic { ptr_kind: PointerKind::Ref(_) } => const_eval_validation_ref_to_static, PointerAsInt { .. } => const_eval_validation_pointer_as_int, PartialPointer => const_eval_validation_partial_pointer, ConstRefToMutable => const_eval_validation_const_ref_to_mutable, ConstRefToExtern => const_eval_validation_const_ref_to_extern, - MutableRefInConst => const_eval_validation_mutable_ref_in_const, + MutableRefInConstOrStatic => const_eval_validation_mutable_ref_in_const_or_static, MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable, NullFnPtr => const_eval_validation_null_fn_ptr, NeverVal => const_eval_validation_never_val, @@ -630,37 +630,39 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Box } => { const_eval_validation_invalid_box_slice_meta } - InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Ref } => { + InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Ref(_) } => { const_eval_validation_invalid_ref_slice_meta } InvalidMetaTooLarge { ptr_kind: PointerKind::Box } => { const_eval_validation_invalid_box_meta } - InvalidMetaTooLarge { ptr_kind: PointerKind::Ref } => { + InvalidMetaTooLarge { ptr_kind: PointerKind::Ref(_) } => { const_eval_validation_invalid_ref_meta } - UnalignedPtr { ptr_kind: PointerKind::Ref, .. } => const_eval_validation_unaligned_ref, + UnalignedPtr { ptr_kind: PointerKind::Ref(_), .. } => { + const_eval_validation_unaligned_ref + } UnalignedPtr { ptr_kind: PointerKind::Box, .. } => const_eval_validation_unaligned_box, NullPtr { ptr_kind: PointerKind::Box } => const_eval_validation_null_box, - NullPtr { ptr_kind: PointerKind::Ref } => const_eval_validation_null_ref, + NullPtr { ptr_kind: PointerKind::Ref(_) } => const_eval_validation_null_ref, DanglingPtrNoProvenance { ptr_kind: PointerKind::Box, .. } => { const_eval_validation_dangling_box_no_provenance } - DanglingPtrNoProvenance { ptr_kind: PointerKind::Ref, .. } => { + DanglingPtrNoProvenance { ptr_kind: PointerKind::Ref(_), .. } => { const_eval_validation_dangling_ref_no_provenance } DanglingPtrOutOfBounds { ptr_kind: PointerKind::Box } => { const_eval_validation_dangling_box_out_of_bounds } - DanglingPtrOutOfBounds { ptr_kind: PointerKind::Ref } => { + DanglingPtrOutOfBounds { ptr_kind: PointerKind::Ref(_) } => { const_eval_validation_dangling_ref_out_of_bounds } DanglingPtrUseAfterFree { ptr_kind: PointerKind::Box } => { const_eval_validation_dangling_box_use_after_free } - DanglingPtrUseAfterFree { ptr_kind: PointerKind::Ref } => { + DanglingPtrUseAfterFree { ptr_kind: PointerKind::Ref(_) } => { const_eval_validation_dangling_ref_use_after_free } InvalidBool { .. } => const_eval_validation_invalid_bool, @@ -766,7 +768,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { } NullPtr { .. } | PtrToStatic { .. } - | MutableRefInConst + | MutableRefInConstOrStatic | ConstRefToMutable | ConstRefToExtern | MutableRefToImmutable diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 5360c51c48d..792e1c9e736 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -445,22 +445,22 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // Determine whether this pointer expects to be pointing to something mutable. let ptr_expected_mutbl = match ptr_kind { PointerKind::Box => Mutability::Mut, - PointerKind::Ref => { - let tam = value.layout.ty.builtin_deref(false).unwrap(); - // ZST never require mutability. We do not take into account interior mutability - // here since we cannot know if there really is an `UnsafeCell` inside - // `Option<UnsafeCell>` -- so we check that in the recursive descent behind this - // reference. - if size == Size::ZERO { Mutability::Not } else { tam.mutbl } + PointerKind::Ref(mutbl) => { + // We do not take into account interior mutability here since we cannot know if + // there really is an `UnsafeCell` inside `Option<UnsafeCell>` -- so we check + // that in the recursive descent behind this reference (controlled by + // `allow_immutable_unsafe_cell`). + mutbl } }; // Proceed recursively even for ZST, no reason to skip them! // `!` is a ZST and we want to validate it. if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) { + let mut skip_recursive_check = false; // Let's see what kind of memory this points to. // `unwrap` since dangling pointers have already been handled. let alloc_kind = self.ecx.tcx.try_get_global_alloc(alloc_id).unwrap(); - match alloc_kind { + let alloc_actual_mutbl = match alloc_kind { GlobalAlloc::Static(did) => { // Special handling for pointers to statics (irrespective of their type). assert!(!self.ecx.tcx.is_thread_local_static(did)); @@ -474,12 +474,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' .no_bound_vars() .expect("statics should not have generic parameters") .is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all()); - // Mutability check. - if ptr_expected_mutbl == Mutability::Mut { - if !is_mut { - throw_validation_failure!(self.path, MutableRefToImmutable); - } - } // Mode-specific checks match self.ctfe_mode { Some( @@ -494,15 +488,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // trigger cycle errors if we try to compute the value of the other static // and that static refers back to us (potentially through a promoted). // This could miss some UB, but that's fine. - return Ok(()); + skip_recursive_check = true; } Some(CtfeValidationMode::Const { .. }) => { - // For consts on the other hand we have to recursively check; - // pattern matching assumes a valid value. However we better make - // sure this is not mutable. - if is_mut { - throw_validation_failure!(self.path, ConstRefToMutable); - } // We can't recursively validate `extern static`, so we better reject them. if self.ecx.tcx.is_foreign_item(did) { throw_validation_failure!(self.path, ConstRefToExtern); @@ -510,26 +498,39 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } None => {} } + // Return alloc mutability + if is_mut { Mutability::Mut } else { Mutability::Not } } - GlobalAlloc::Memory(alloc) => { - if alloc.inner().mutability == Mutability::Mut - && matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) - { - throw_validation_failure!(self.path, ConstRefToMutable); - } - if ptr_expected_mutbl == Mutability::Mut - && alloc.inner().mutability == Mutability::Not - { - throw_validation_failure!(self.path, MutableRefToImmutable); - } - } + GlobalAlloc::Memory(alloc) => alloc.inner().mutability, GlobalAlloc::Function(..) | GlobalAlloc::VTable(..) => { // These are immutable, we better don't allow mutable pointers here. - if ptr_expected_mutbl == Mutability::Mut { - throw_validation_failure!(self.path, MutableRefToImmutable); - } + Mutability::Not + } + }; + // Mutability check. + // If this allocation has size zero, there is no actual mutability here. + let (size, _align, _alloc_kind) = self.ecx.get_alloc_info(alloc_id); + if size != Size::ZERO { + if ptr_expected_mutbl == Mutability::Mut + && alloc_actual_mutbl == Mutability::Not + { + throw_validation_failure!(self.path, MutableRefToImmutable); + } + if ptr_expected_mutbl == Mutability::Mut + && self.ctfe_mode.is_some_and(|c| !c.may_contain_mutable_ref()) + { + throw_validation_failure!(self.path, MutableRefInConstOrStatic); + } + if alloc_actual_mutbl == Mutability::Mut + && matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) + { + throw_validation_failure!(self.path, ConstRefToMutable); } } + // Potentially skip recursive check. + if skip_recursive_check { + return Ok(()); + } } let path = &self.path; ref_tracking.track(place, || { @@ -598,16 +599,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } Ok(true) } - ty::Ref(_, ty, mutbl) => { - if self.ctfe_mode.is_some_and(|c| !c.may_contain_mutable_ref()) - && *mutbl == Mutability::Mut - { - let layout = self.ecx.layout_of(*ty)?; - if !layout.is_zst() { - throw_validation_failure!(self.path, MutableRefInConst); - } - } - self.check_safe_pointer(value, PointerKind::Ref)?; + ty::Ref(_, _ty, mutbl) => { + self.check_safe_pointer(value, PointerKind::Ref(*mutbl))?; Ok(true) } ty::FnPtr(_sig) => { diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 7c6caf442dc..b9b257856e6 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -526,6 +526,7 @@ pub enum StashKey { MaybeFruTypo, CallAssocMethod, TraitMissingMethod, + AssociatedTypeSuggestion, OpaqueHiddenTypeMismatch, MaybeForgetReturn, /// Query cycle detected, stashing in favor of a better error. diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 729ce1f00cd..f39b496154b 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -109,6 +109,93 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.autoderef(span, ty).any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) } + fn impl_into_iterator_should_be_iterator( + &self, + ty: Ty<'tcx>, + span: Span, + unsatisfied_predicates: &Vec<( + ty::Predicate<'_>, + Option<ty::Predicate<'_>>, + Option<ObligationCause<'_>>, + )>, + ) -> bool { + fn predicate_bounds_generic_param<'tcx>( + predicate: ty::Predicate<'_>, + generics: &'tcx ty::Generics, + generic_param: &ty::GenericParamDef, + tcx: TyCtxt<'tcx>, + ) -> bool { + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) = + predicate.kind().as_ref().skip_binder() + { + let ty::TraitPredicate { trait_ref: ty::TraitRef { args, .. }, .. } = trait_pred; + if args.is_empty() { + return false; + } + let Some(arg_ty) = args[0].as_type() else { + return false; + }; + let ty::Param(param) = arg_ty.kind() else { + return false; + }; + // Is `generic_param` the same as the arg for this trait predicate? + generic_param.index == generics.type_param(¶m, tcx).index + } else { + false + } + } + + fn is_iterator_predicate(predicate: ty::Predicate<'_>, tcx: TyCtxt<'_>) -> bool { + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) = + predicate.kind().as_ref().skip_binder() + { + tcx.is_diagnostic_item(sym::Iterator, trait_pred.trait_ref.def_id) + } else { + false + } + } + + // Does the `ty` implement `IntoIterator`? + let Some(into_iterator_trait) = self.tcx.get_diagnostic_item(sym::IntoIterator) else { + return false; + }; + let trait_ref = ty::TraitRef::new(self.tcx, into_iterator_trait, [ty]); + let cause = ObligationCause::new(span, self.body_id, ObligationCauseCode::MiscObligation); + let obligation = Obligation::new(self.tcx, cause, self.param_env, trait_ref); + if !self.predicate_must_hold_modulo_regions(&obligation) { + return false; + } + + match ty.kind() { + ty::Param(param) => { + let generics = self.tcx.generics_of(self.body_id); + let generic_param = generics.type_param(¶m, self.tcx); + for unsatisfied in unsatisfied_predicates.iter() { + // The parameter implements `IntoIterator` + // but it has called a method that requires it to implement `Iterator` + if predicate_bounds_generic_param( + unsatisfied.0, + generics, + generic_param, + self.tcx, + ) && is_iterator_predicate(unsatisfied.0, self.tcx) + { + return true; + } + } + } + ty::Alias(ty::AliasKind::Opaque, _) => { + for unsatisfied in unsatisfied_predicates.iter() { + if is_iterator_predicate(unsatisfied.0, self.tcx) { + return true; + } + } + } + _ => return false, + } + false + } + #[instrument(level = "debug", skip(self))] pub fn report_method_error( &self, @@ -555,6 +642,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement" )); } + } else if self.impl_into_iterator_should_be_iterator(rcvr_ty, span, unsatisfied_predicates) + { + err.span_label(span, format!("`{rcvr_ty}` is not an iterator")); + err.multipart_suggestion_verbose( + "call `.into_iter()` first", + vec![(span.shrink_to_lo(), format!("into_iter()."))], + Applicability::MaybeIncorrect, + ); + return Some(err); } else if !unsatisfied_predicates.is_empty() && matches!(rcvr_ty.kind(), ty::Param(_)) { // We special case the situation where we are looking for `_` in // `<TypeParam as _>::method` because otherwise the machinery will look for blanket diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 9d4ec7d25bb..125fac48df8 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -12,6 +12,7 @@ use rustc_macros::HashStable; use rustc_session::CtfeBacktrace; use rustc_span::{def_id::DefId, Span, DUMMY_SP}; use rustc_target::abi::{call, Align, Size, VariantIdx, WrappingRange}; +use rustc_type_ir::Mutability; use std::borrow::Cow; use std::{any::Any, backtrace::Backtrace, fmt}; @@ -367,7 +368,7 @@ pub enum UndefinedBehaviorInfo<'tcx> { #[derive(Debug, Clone, Copy)] pub enum PointerKind { - Ref, + Ref(Mutability), Box, } @@ -375,7 +376,7 @@ impl IntoDiagnosticArg for PointerKind { fn into_diagnostic_arg(self) -> DiagnosticArgValue { DiagnosticArgValue::Str( match self { - Self::Ref => "ref", + Self::Ref(_) => "ref", Self::Box => "box", } .into(), @@ -408,7 +409,7 @@ impl From<PointerKind> for ExpectedKind { fn from(x: PointerKind) -> ExpectedKind { match x { PointerKind::Box => ExpectedKind::Box, - PointerKind::Ref => ExpectedKind::Reference, + PointerKind::Ref(_) => ExpectedKind::Reference, } } } @@ -419,7 +420,7 @@ pub enum ValidationErrorKind<'tcx> { PartialPointer, PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> }, PtrToStatic { ptr_kind: PointerKind }, - MutableRefInConst, + MutableRefInConstOrStatic, ConstRefToMutable, ConstRefToExtern, MutableRefToImmutable, diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index 508936be29d..3f2b7c482a6 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -137,7 +137,9 @@ impl<'tcx> TerminatorClassifier<'tcx> for CallRecursion<'tcx> { let func_ty = func.ty(body, tcx); if let ty::FnDef(callee, args) = *func_ty.kind() { - let normalized_args = tcx.normalize_erasing_regions(param_env, args); + let Ok(normalized_args) = tcx.try_normalize_erasing_regions(param_env, args) else { + return false; + }; let (callee, call_args) = if let Ok(Some(instance)) = Instance::resolve(tcx, param_env, callee, normalized_args) { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 6bd221ff058..3ea4df1d2a4 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -18,6 +18,7 @@ use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::{ codes::*, struct_span_code_err, Applicability, DiagnosticArgValue, ErrCode, IntoDiagnosticArg, + StashKey, }; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS}; @@ -3890,6 +3891,23 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { finalize, ) { Ok(Some(partial_res)) if let Some(res) = partial_res.full_res() => { + // if we also have an associated type that matches the ident, stash a suggestion + if let Some(items) = self.diagnostic_metadata.current_trait_assoc_items + && let [Segment { ident, .. }] = path + && items.iter().any(|item| { + item.ident == *ident && matches!(item.kind, AssocItemKind::Type(_)) + }) + { + let mut diag = self.r.tcx.dcx().struct_allow(""); + diag.span_suggestion_verbose( + path_span.shrink_to_lo(), + "there is an associated type with the same name", + "Self::", + Applicability::MaybeIncorrect, + ); + diag.stash(path_span, StashKey::AssociatedTypeSuggestion); + } + if source.is_expected(res) || res == Res::Err { partial_res } else { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b89dfab2ca9..ef0512bf686 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1435,12 +1435,16 @@ impl CheckCfg { // // When adding a new config here you should also update // `tests/ui/check-cfg/well-known-values.rs`. + // + // Don't forget to update `src/doc/unstable-book/src/compiler-flags/check-cfg.md` + // in the unstable book as well! ins!(sym::debug_assertions, no_values); - // These three are never set by rustc, but we set them anyway: they - // should not trigger a lint because `cargo doc`, `cargo test`, and - // `cargo miri run` (respectively) can set them. + // These four are never set by rustc, but we set them anyway: they + // should not trigger a lint because `cargo clippy`, `cargo doc`, + // `cargo test` and `cargo miri run` (respectively) can set them. + ins!(sym::clippy, no_values); ins!(sym::doc, no_values); ins!(sym::doctest, no_values); ins!(sym::miri, no_values); 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 68b1a0d4e61..661a444d049 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 @@ -2993,6 +2993,15 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { &mut Default::default(), ); self.suggest_unsized_bound_if_applicable(err, obligation); + if let Some(span) = err.span.primary_span() + && let Some(mut diag) = + self.tcx.dcx().steal_diagnostic(span, StashKey::AssociatedTypeSuggestion) + && let Ok(ref mut s1) = err.suggestions + && let Ok(ref mut s2) = diag.suggestions + { + s1.append(s2); + diag.cancel() + } } } |
