@@ -85,6 +85,7 @@ use rustc_trait_selection::traits::query::normalize::AtExt;
85
85
use smallvec:: SmallVec ;
86
86
87
87
use crate :: consts:: { constant, Constant } ;
88
+ use std:: collections:: HashMap ;
88
89
89
90
pub fn parse_msrv ( msrv : & str , sess : Option < & Session > , span : Option < Span > ) -> Option < RustcVersion > {
90
91
if let Ok ( version) = RustcVersion :: parse ( msrv) {
@@ -1494,13 +1495,49 @@ pub fn match_function_call<'tcx>(
1494
1495
None
1495
1496
}
1496
1497
1498
+ // FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
1499
+ // this function can be removed once the `normalizie` method does not panic when normalization does
1500
+ // not succeed
1497
1501
/// Checks if `Ty` is normalizable. This function is useful
1498
1502
/// to avoid crashes on `layout_of`.
1499
1503
pub fn is_normalizable < ' tcx > ( cx : & LateContext < ' tcx > , param_env : ty:: ParamEnv < ' tcx > , ty : Ty < ' tcx > ) -> bool {
1500
- cx. tcx . infer_ctxt ( ) . enter ( |infcx| {
1504
+ is_normalizable_helper ( cx, param_env, ty, & mut HashMap :: new ( ) )
1505
+ }
1506
+
1507
+ fn is_normalizable_helper < ' tcx > (
1508
+ cx : & LateContext < ' tcx > ,
1509
+ param_env : ty:: ParamEnv < ' tcx > ,
1510
+ ty : Ty < ' tcx > ,
1511
+ cache : & mut HashMap < Ty < ' tcx > , bool > ,
1512
+ ) -> bool {
1513
+ if let Some ( & cached_result) = cache. get ( ty) {
1514
+ return cached_result;
1515
+ }
1516
+ // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
1517
+ cache. insert ( ty, false ) ;
1518
+ let result = cx. tcx . infer_ctxt ( ) . enter ( |infcx| {
1501
1519
let cause = rustc_middle:: traits:: ObligationCause :: dummy ( ) ;
1502
- infcx. at ( & cause, param_env) . normalize ( ty) . is_ok ( )
1503
- } )
1520
+ if infcx. at ( & cause, param_env) . normalize ( ty) . is_ok ( ) {
1521
+ match ty. kind ( ) {
1522
+ ty:: Adt ( def, substs) => def. variants . iter ( ) . all ( |variant| {
1523
+ variant
1524
+ . fields
1525
+ . iter ( )
1526
+ . all ( |field| is_normalizable_helper ( cx, param_env, field. ty ( cx. tcx , substs) , cache) )
1527
+ } ) ,
1528
+ _ => ty. walk ( ) . all ( |generic_arg| match generic_arg. unpack ( ) {
1529
+ GenericArgKind :: Type ( inner_ty) if inner_ty != ty => {
1530
+ is_normalizable_helper ( cx, param_env, inner_ty, cache)
1531
+ } ,
1532
+ _ => true , // if inner_ty == ty, we've already checked it
1533
+ } ) ,
1534
+ }
1535
+ } else {
1536
+ false
1537
+ }
1538
+ } ) ;
1539
+ cache. insert ( ty, result) ;
1540
+ result
1504
1541
}
1505
1542
1506
1543
pub fn match_def_path < ' tcx > ( cx : & LateContext < ' tcx > , did : DefId , syms : & [ & str ] ) -> bool {
0 commit comments