@@ -85,6 +85,7 @@ use rustc_trait_selection::traits::query::normalize::AtExt;
8585use smallvec:: SmallVec ;
8686
8787use crate :: consts:: { constant, Constant } ;
88+ use std:: collections:: HashMap ;
8889
8990pub fn parse_msrv ( msrv : & str , sess : Option < & Session > , span : Option < Span > ) -> Option < RustcVersion > {
9091 if let Ok ( version) = RustcVersion :: parse ( msrv) {
@@ -1494,13 +1495,49 @@ pub fn match_function_call<'tcx>(
14941495 None
14951496}
14961497
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
14971501/// Checks if `Ty` is normalizable. This function is useful
14981502/// to avoid crashes on `layout_of`.
14991503pub 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| {
15011519 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
15041541}
15051542
15061543pub fn match_def_path < ' tcx > ( cx : & LateContext < ' tcx > , did : DefId , syms : & [ & str ] ) -> bool {
0 commit comments