@@ -58,13 +58,15 @@ use rustc_hir::def_id::DefId;
58
58
use rustc_hir:: intravisit:: Visitor ;
59
59
use rustc_hir:: lang_items:: LangItem ;
60
60
use rustc_hir:: { self as hir} ;
61
+ use rustc_infer:: infer:: DefineOpaqueTypes ;
62
+ use rustc_infer:: traits:: Obligation ;
61
63
use rustc_macros:: extension;
62
64
use rustc_middle:: bug;
63
65
use rustc_middle:: dep_graph:: DepContext ;
64
66
use rustc_middle:: ty:: error:: { ExpectedFound , TypeError , TypeErrorToStringExt } ;
65
67
use rustc_middle:: ty:: print:: { PrintError , PrintTraitRefExt as _, with_forced_trimmed_paths} ;
66
68
use rustc_middle:: ty:: {
67
- self , List , Region , Ty , TyCtxt , TypeFoldable , TypeSuperVisitable , TypeVisitable ,
69
+ self , List , ParamEnv , Region , Ty , TyCtxt , TypeFoldable , TypeSuperVisitable , TypeVisitable ,
68
70
TypeVisitableExt ,
69
71
} ;
70
72
use rustc_span:: { BytePos , DesugaringKind , Pos , Span , sym} ;
@@ -76,8 +78,10 @@ use crate::infer;
76
78
use crate :: infer:: relate:: { self , RelateResult , TypeRelation } ;
77
79
use crate :: infer:: { InferCtxt , TypeTrace , ValuePairs } ;
78
80
use crate :: solve:: deeply_normalize_for_diagnostics;
81
+ use crate :: traits:: query:: evaluate_obligation:: InferCtxtExt ;
79
82
use crate :: traits:: {
80
83
IfExpressionCause , MatchExpressionArmCause , ObligationCause , ObligationCauseCode ,
84
+ ObligationCtxt ,
81
85
} ;
82
86
83
87
mod note_and_explain;
@@ -339,9 +343,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
339
343
cause : & ObligationCause < ' tcx > ,
340
344
exp_found : Option < ty:: error:: ExpectedFound < Ty < ' tcx > > > ,
341
345
terr : TypeError < ' tcx > ,
346
+ param_env : Option < ParamEnv < ' tcx > > ,
342
347
) {
343
348
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
+ } => {
345
355
let ty = self . resolve_vars_if_possible ( root_ty) ;
346
356
if !matches ! ( ty. kind( ) , ty:: Infer ( ty:: InferTy :: TyVar ( _) | ty:: InferTy :: FreshTy ( _) ) )
347
357
{
@@ -359,15 +369,35 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
359
369
}
360
370
}
361
371
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)
364
373
{
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
+ }
371
401
}
372
402
}
373
403
ObligationCauseCode :: Pattern { origin_expr : false , span : Some ( span) , .. } => {
@@ -524,6 +554,86 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
524
554
}
525
555
}
526
556
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
+
527
637
/// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`
528
638
/// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and
529
639
/// populate `other_value` with `other_ty`.
@@ -1209,6 +1319,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
1209
1319
mut values : Option < ty:: ParamEnvAnd < ' tcx , ValuePairs < ' tcx > > > ,
1210
1320
terr : TypeError < ' tcx > ,
1211
1321
prefer_label : bool ,
1322
+ param_env : Option < ty:: ParamEnv < ' tcx > > ,
1212
1323
) {
1213
1324
let span = cause. span ;
1214
1325
@@ -1693,7 +1804,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
1693
1804
1694
1805
// It reads better to have the error origin as the final
1695
1806
// thing.
1696
- self . note_error_origin ( diag, cause, exp_found, terr) ;
1807
+ self . note_error_origin ( diag, cause, exp_found, terr, param_env ) ;
1697
1808
1698
1809
debug ! ( ?diag) ;
1699
1810
}
@@ -1868,6 +1979,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
1868
1979
Some ( param_env. and ( trace. values ) ) ,
1869
1980
terr,
1870
1981
false ,
1982
+ Some ( param_env) ,
1871
1983
) ;
1872
1984
diag
1873
1985
}
0 commit comments