@@ -68,9 +68,12 @@ use rustc_hir as hir;
68
68
use rustc_hir:: def_id:: DefId ;
69
69
use rustc_hir:: Node ;
70
70
71
- use errors:: { struct_span_err, Applicability , DiagnosticBuilder , DiagnosticStyledString } ;
71
+ use errors:: {
72
+ pluralize, struct_span_err, Applicability , DiagnosticBuilder , DiagnosticStyledString ,
73
+ } ;
74
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
72
75
use rustc_error_codes:: * ;
73
- use rustc_span:: { Pos , Span } ;
76
+ use rustc_span:: { DesugaringKind , Pos , Span } ;
74
77
use rustc_target:: spec:: abi;
75
78
use std:: { cmp, fmt} ;
76
79
@@ -1289,6 +1292,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
1289
1292
mut values : Option < ValuePairs < ' tcx > > ,
1290
1293
terr : & TypeError < ' tcx > ,
1291
1294
) {
1295
+ let span = cause. span ( self . tcx ) ;
1296
+
1292
1297
// For some types of errors, expected-found does not make
1293
1298
// sense, so just ignore the values we were given.
1294
1299
match terr {
@@ -1298,6 +1303,100 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
1298
1303
_ => { }
1299
1304
}
1300
1305
1306
+ struct OpaqueTypesVisitor < ' tcx > {
1307
+ types : FxHashMap < TyCategory , FxHashSet < Span > > ,
1308
+ expected : FxHashMap < TyCategory , FxHashSet < Span > > ,
1309
+ found : FxHashMap < TyCategory , FxHashSet < Span > > ,
1310
+ ignore_span : Span ,
1311
+ tcx : TyCtxt < ' tcx > ,
1312
+ }
1313
+
1314
+ impl < ' tcx > OpaqueTypesVisitor < ' tcx > {
1315
+ fn visit_expected_found (
1316
+ tcx : TyCtxt < ' tcx > ,
1317
+ expected : Ty < ' tcx > ,
1318
+ found : Ty < ' tcx > ,
1319
+ ignore_span : Span ,
1320
+ ) -> Self {
1321
+ let mut types_visitor = OpaqueTypesVisitor {
1322
+ types : Default :: default ( ) ,
1323
+ expected : Default :: default ( ) ,
1324
+ found : Default :: default ( ) ,
1325
+ ignore_span,
1326
+ tcx,
1327
+ } ;
1328
+ // The visitor puts all the relevant encountered types in `self.types`, but in
1329
+ // here we want to visit two separate types with no relation to each other, so we
1330
+ // move the results from `types` to `expected` or `found` as appropriate.
1331
+ expected. visit_with ( & mut types_visitor) ;
1332
+ std:: mem:: swap ( & mut types_visitor. expected , & mut types_visitor. types ) ;
1333
+ found. visit_with ( & mut types_visitor) ;
1334
+ std:: mem:: swap ( & mut types_visitor. found , & mut types_visitor. types ) ;
1335
+ types_visitor
1336
+ }
1337
+
1338
+ fn report ( & self , err : & mut DiagnosticBuilder < ' _ > ) {
1339
+ self . add_labels_for_types ( err, "expected" , & self . expected ) ;
1340
+ self . add_labels_for_types ( err, "found" , & self . found ) ;
1341
+ }
1342
+
1343
+ fn add_labels_for_types (
1344
+ & self ,
1345
+ err : & mut DiagnosticBuilder < ' _ > ,
1346
+ target : & str ,
1347
+ types : & FxHashMap < TyCategory , FxHashSet < Span > > ,
1348
+ ) {
1349
+ for ( key, values) in types. iter ( ) {
1350
+ let count = values. len ( ) ;
1351
+ let kind = key. descr ( ) ;
1352
+ for sp in values {
1353
+ err. span_label (
1354
+ * sp,
1355
+ format ! (
1356
+ "{}{}{} {}{}" ,
1357
+ if sp. is_desugaring( DesugaringKind :: Async ) {
1358
+ "the `Output` of this `async fn`'s "
1359
+ } else if count == 1 {
1360
+ "the "
1361
+ } else {
1362
+ ""
1363
+ } ,
1364
+ if count > 1 { "one of the " } else { "" } ,
1365
+ target,
1366
+ kind,
1367
+ pluralize!( count) ,
1368
+ ) ,
1369
+ ) ;
1370
+ }
1371
+ }
1372
+ }
1373
+ }
1374
+
1375
+ impl < ' tcx > ty:: fold:: TypeVisitor < ' tcx > for OpaqueTypesVisitor < ' tcx > {
1376
+ fn visit_ty ( & mut self , t : Ty < ' tcx > ) -> bool {
1377
+ if let Some ( ( kind, def_id) ) = TyCategory :: from_ty ( t) {
1378
+ let span = self . tcx . def_span ( def_id) ;
1379
+ // Avoid cluttering the output when the "found" and error span overlap:
1380
+ //
1381
+ // error[E0308]: mismatched types
1382
+ // --> $DIR/issue-20862.rs:2:5
1383
+ // |
1384
+ // LL | |y| x + y
1385
+ // | ^^^^^^^^^
1386
+ // | |
1387
+ // | the found closure
1388
+ // | expected `()`, found closure
1389
+ // |
1390
+ // = note: expected unit type `()`
1391
+ // found closure `[closure@$DIR/issue-20862.rs:2:5: 2:14 x:_]`
1392
+ if !self . ignore_span . overlaps ( span) {
1393
+ self . types . entry ( kind) . or_default ( ) . insert ( span) ;
1394
+ }
1395
+ }
1396
+ t. super_visit_with ( self )
1397
+ }
1398
+ }
1399
+
1301
1400
debug ! ( "note_type_err(diag={:?})" , diag) ;
1302
1401
let ( expected_found, exp_found, is_simple_error) = match values {
1303
1402
None => ( None , None , false ) ,
@@ -1306,6 +1405,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
1306
1405
ValuePairs :: Types ( exp_found) => {
1307
1406
let is_simple_err =
1308
1407
exp_found. expected . is_simple_text ( ) && exp_found. found . is_simple_text ( ) ;
1408
+ OpaqueTypesVisitor :: visit_expected_found (
1409
+ self . tcx ,
1410
+ exp_found. expected ,
1411
+ exp_found. found ,
1412
+ span,
1413
+ )
1414
+ . report ( diag) ;
1309
1415
1310
1416
( is_simple_err, Some ( exp_found) )
1311
1417
}
@@ -1323,8 +1429,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
1323
1429
}
1324
1430
} ;
1325
1431
1326
- let span = cause. span ( self . tcx ) ;
1327
-
1328
1432
// Ignore msg for object safe coercion
1329
1433
// since E0038 message will be printed
1330
1434
match terr {
@@ -1336,7 +1440,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
1336
1440
}
1337
1441
}
1338
1442
} ;
1339
-
1340
1443
if let Some ( ( expected, found) ) = expected_found {
1341
1444
let expected_label = exp_found. map_or ( "type" . into ( ) , |ef| ef. expected . prefix_string ( ) ) ;
1342
1445
let found_label = exp_found. map_or ( "type" . into ( ) , |ef| ef. found . prefix_string ( ) ) ;
@@ -1933,3 +2036,34 @@ impl<'tcx> ObligationCause<'tcx> {
1933
2036
}
1934
2037
}
1935
2038
}
2039
+
2040
+ /// This is a bare signal of what kind of type we're dealing with. `ty::TyKind` tracks
2041
+ /// extra information about each type, but we only care about the category.
2042
+ #[ derive( Clone , Copy , PartialEq , Eq , Hash ) ]
2043
+ crate enum TyCategory {
2044
+ Closure ,
2045
+ Opaque ,
2046
+ Generator ,
2047
+ Foreign ,
2048
+ }
2049
+
2050
+ impl TyCategory {
2051
+ fn descr ( & self ) -> & ' static str {
2052
+ match self {
2053
+ Self :: Closure => "closure" ,
2054
+ Self :: Opaque => "opaque type" ,
2055
+ Self :: Generator => "generator" ,
2056
+ Self :: Foreign => "foreign type" ,
2057
+ }
2058
+ }
2059
+
2060
+ pub fn from_ty ( ty : Ty < ' _ > ) -> Option < ( Self , DefId ) > {
2061
+ match ty. kind {
2062
+ ty:: Closure ( def_id, _) => Some ( ( Self :: Closure , def_id) ) ,
2063
+ ty:: Opaque ( def_id, _) => Some ( ( Self :: Opaque , def_id) ) ,
2064
+ ty:: Generator ( def_id, ..) => Some ( ( Self :: Generator , def_id) ) ,
2065
+ ty:: Foreign ( def_id) => Some ( ( Self :: Foreign , def_id) ) ,
2066
+ _ => None ,
2067
+ }
2068
+ }
2069
+ }
0 commit comments