@@ -58,13 +58,15 @@ use rustc_hir::def_id::DefId;
5858use rustc_hir:: intravisit:: Visitor ;
5959use rustc_hir:: lang_items:: LangItem ;
6060use rustc_hir:: { self as hir} ;
61+ use rustc_infer:: infer:: DefineOpaqueTypes ;
62+ use rustc_infer:: traits:: Obligation ;
6163use rustc_macros:: extension;
6264use rustc_middle:: bug;
6365use rustc_middle:: dep_graph:: DepContext ;
6466use rustc_middle:: ty:: error:: { ExpectedFound , TypeError , TypeErrorToStringExt } ;
6567use rustc_middle:: ty:: print:: { PrintError , PrintTraitRefExt as _, with_forced_trimmed_paths} ;
6668use rustc_middle:: ty:: {
67- self , List , Region , Ty , TyCtxt , TypeFoldable , TypeSuperVisitable , TypeVisitable ,
69+ self , List , ParamEnv , Region , Ty , TyCtxt , TypeFoldable , TypeSuperVisitable , TypeVisitable ,
6870 TypeVisitableExt ,
6971} ;
7072use rustc_span:: { BytePos , DesugaringKind , Pos , Span , sym} ;
@@ -76,8 +78,10 @@ use crate::infer;
7678use crate :: infer:: relate:: { self , RelateResult , TypeRelation } ;
7779use crate :: infer:: { InferCtxt , TypeTrace , ValuePairs } ;
7880use crate :: solve:: deeply_normalize_for_diagnostics;
81+ use crate :: traits:: query:: evaluate_obligation:: InferCtxtExt ;
7982use crate :: traits:: {
8083 IfExpressionCause , MatchExpressionArmCause , ObligationCause , ObligationCauseCode ,
84+ ObligationCtxt ,
8185} ;
8286
8387mod note_and_explain;
@@ -339,9 +343,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
339343 cause : & ObligationCause < ' tcx > ,
340344 exp_found : Option < ty:: error:: ExpectedFound < Ty < ' tcx > > > ,
341345 terr : TypeError < ' tcx > ,
346+ param_env : Option < ParamEnv < ' tcx > > ,
342347 ) {
343348 match * cause. code ( ) {
344- ObligationCauseCode :: Pattern { origin_expr : true , span : Some ( span) , root_ty } => {
349+ ObligationCauseCode :: Pattern {
350+ origin_expr : true ,
351+ span : Some ( span) ,
352+ root_ty,
353+ prefix_suggestion_parentheses,
354+ } => {
345355 let ty = self . resolve_vars_if_possible ( root_ty) ;
346356 if !matches ! ( ty. kind( ) , ty:: Infer ( ty:: InferTy :: TyVar ( _) | ty:: InferTy :: FreshTy ( _) ) )
347357 {
@@ -359,15 +369,35 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
359369 }
360370 }
361371 if let Some ( ty:: error:: ExpectedFound { found, .. } ) = exp_found
362- && ty. boxed_ty ( ) == Some ( found)
363- && let Ok ( snippet) = self . tcx . sess . source_map ( ) . span_to_snippet ( span)
372+ && let Ok ( mut snippet) = self . tcx . sess . source_map ( ) . span_to_snippet ( span)
364373 {
365- err. span_suggestion (
366- span,
367- "consider dereferencing the boxed value" ,
368- format ! ( "*{snippet}" ) ,
369- Applicability :: MachineApplicable ,
370- ) ;
374+ // Parentheses are needed for cases like as casts.
375+ snippet = if prefix_suggestion_parentheses {
376+ format ! ( "({snippet})" )
377+ } else {
378+ snippet
379+ } ;
380+
381+ // Try giving a box suggestion first, as it is a special case of the
382+ // deref suggestion.
383+ if ty. boxed_ty ( ) == Some ( found) {
384+ err. span_suggestion (
385+ span,
386+ "consider dereferencing the boxed value" ,
387+ format ! ( "*{snippet}" ) ,
388+ Applicability :: MachineApplicable ,
389+ ) ;
390+ } else if let Some ( param_env) = param_env
391+ && let Some ( prefix) =
392+ self . should_deref_suggestion_on_mismatch ( cause, param_env, found, ty)
393+ {
394+ err. span_suggestion (
395+ span,
396+ "consider dereferencing to access the inner value" ,
397+ format ! ( "{prefix}{snippet}" ) ,
398+ Applicability :: MaybeIncorrect ,
399+ ) ;
400+ }
371401 }
372402 }
373403 ObligationCauseCode :: Pattern { origin_expr : false , span : Some ( span) , .. } => {
@@ -524,6 +554,86 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
524554 }
525555 }
526556
557+ /// Determines whether deref_to == <deref_from as Deref>::Target, and if so,
558+ /// returns a prefix that should be added to deref_from as a suggestion.
559+ fn should_deref_suggestion_on_mismatch (
560+ & self ,
561+ cause : & ObligationCause < ' tcx > ,
562+ param_env : ParamEnv < ' tcx > ,
563+ deref_to : Ty < ' tcx > ,
564+ deref_from : Ty < ' tcx > ,
565+ ) -> Option < String > {
566+ let tcx = self . infcx . tcx ;
567+
568+ let deref_trait = tcx. lang_items ( ) . deref_trait ( ) ?;
569+ let deref_mut_trait = tcx. lang_items ( ) . deref_mut_trait ( ) ?;
570+ let deref_target = tcx. lang_items ( ) . deref_target ( ) ?;
571+
572+ // Count the number of references in deref_from, since we need
573+ // that plus 1 to invoke Deref.
574+ // This is also needed to ensure we aren't checking for Deref on a reference.
575+ let mut deref_from_cur = deref_from;
576+ // Extra required dereference to invoke Deref.
577+ let mut num_refs = 1 ;
578+ while let ty:: Ref ( _, inner_ty, _) = deref_from_cur. kind ( ) {
579+ deref_from_cur = * inner_ty;
580+ num_refs += 1 ;
581+ }
582+
583+ // <deref_from_cur as Deref>
584+ let trait_ref = ty:: TraitRef :: new ( tcx, deref_trait, [ deref_from_cur] ) ;
585+ let obligation =
586+ Obligation :: new ( tcx, cause. clone ( ) , param_env, ty:: Binder :: dummy ( trait_ref) ) ;
587+ if !self . infcx . predicate_may_hold ( & obligation) {
588+ return None ;
589+ }
590+
591+ let ocx = ObligationCtxt :: new ( self . infcx ) ;
592+
593+ // <deref_from_cur as Deref>::Target
594+ let target = Ty :: new_projection ( tcx, deref_target, [ deref_from_cur] ) ;
595+
596+ let target_normalized = ocx. structurally_normalize ( cause, param_env, target) . ok ( ) ?;
597+ let expected_normalized = ocx. structurally_normalize ( cause, param_env, deref_to) . ok ( ) ?;
598+
599+ // Determine whether target_normalized == expected_normalized with inference.
600+ let types_eq = self . infcx . probe ( |_| {
601+ // FIXME: I don't know what DefineOpaqueTypes should be used here.
602+ self . infcx
603+ . at ( cause, param_env)
604+ . eq ( DefineOpaqueTypes :: No , expected_normalized, target_normalized)
605+ . is_ok ( )
606+ } ) ;
607+
608+ if !types_eq {
609+ return None ;
610+ }
611+
612+ let deref_part = "*" . repeat ( num_refs) ;
613+
614+ // FIXME: Edge case: how should this handle double references?
615+ // Currently, they're dereferenced correctly, but only the
616+ // outer reference is added back after the dereferences.
617+
618+ // Check if we have DerefMut, as it's required to create a mut reference.
619+ let trait_ref_deref_mut = ty:: TraitRef :: new ( tcx, deref_mut_trait, [ deref_from_cur] ) ;
620+ let obligation_deref_mut =
621+ Obligation :: new ( tcx, cause. clone ( ) , param_env, ty:: Binder :: dummy ( trait_ref_deref_mut) ) ;
622+ let has_deref_mut = self . infcx . predicate_may_hold ( & obligation_deref_mut) ;
623+
624+ // Try to give a suggestion with the same type of reference as the original code.
625+ // For example, if deref_from was a reference, then make the suggestion a reference as well.
626+ let valid_mutability = deref_from
627+ . ref_mutability ( )
628+ . map ( |v| if has_deref_mut { v } else { hir:: Mutability :: Not } ) ;
629+
630+ match valid_mutability {
631+ Some ( hir:: Mutability :: Mut ) => Some ( format ! ( "&mut {deref_part}" ) ) ,
632+ Some ( hir:: Mutability :: Not ) => Some ( format ! ( "&{deref_part}" ) ) ,
633+ None => Some ( deref_part) ,
634+ }
635+ }
636+
527637 /// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`
528638 /// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and
529639 /// populate `other_value` with `other_ty`.
@@ -1209,6 +1319,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
12091319 mut values : Option < ty:: ParamEnvAnd < ' tcx , ValuePairs < ' tcx > > > ,
12101320 terr : TypeError < ' tcx > ,
12111321 prefer_label : bool ,
1322+ param_env : Option < ty:: ParamEnv < ' tcx > > ,
12121323 ) {
12131324 let span = cause. span ;
12141325
@@ -1693,7 +1804,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
16931804
16941805 // It reads better to have the error origin as the final
16951806 // thing.
1696- self . note_error_origin ( diag, cause, exp_found, terr) ;
1807+ self . note_error_origin ( diag, cause, exp_found, terr, param_env ) ;
16971808
16981809 debug ! ( ?diag) ;
16991810 }
@@ -1868,6 +1979,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
18681979 Some ( param_env. and ( trace. values ) ) ,
18691980 terr,
18701981 false ,
1982+ Some ( param_env) ,
18711983 ) ;
18721984 diag
18731985 }
0 commit comments