//! Check whether a type is representable. use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; use std::cmp; /// Describes whether a type is representable. For types that are not /// representable, 'SelfRecursive' and 'ContainsRecursive' are used to /// distinguish between types that are recursive with themselves and types that /// contain a different recursive type. These cases can therefore be treated /// differently when reporting errors. /// /// The ordering of the cases is significant. They are sorted so that cmp::max /// will keep the "more erroneous" of two values. #[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)] pub enum Representability { Representable, ContainsRecursive, /// Return a list of types that are included in themselves: /// the spans where they are self-included, and (if found) /// the HirId of the FieldDef that defines the self-inclusion. SelfRecursive(Vec<(Span, Option)>), } /// Check whether a type is representable. This means it cannot contain unboxed /// structural recursion. This check is needed for structs and enums. pub fn ty_is_representable<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, sp: Span, field_id: Option, ) -> Representability { debug!("is_type_representable: {:?}", ty); // To avoid a stack overflow when checking an enum variant or struct that // contains a different, structurally recursive type, maintain a stack of // seen types and check recursion for each of them (issues #3008, #3779, // #74224, #84611). `shadow_seen` contains the full stack and `seen` only // the one for the current type (e.g. if we have structs A and B, B contains // a field of type A, and we're currently looking at B, then `seen` will be // cleared when recursing to check A, but `shadow_seen` won't, so that we // can catch cases of mutual recursion where A also contains B). let mut seen: Vec> = Vec::new(); let mut shadow_seen: Vec> = Vec::new(); let mut representable_cache = FxHashMap::default(); let mut force_result = false; let r = is_type_structurally_recursive( tcx, &mut seen, &mut shadow_seen, &mut representable_cache, ty, sp, field_id, &mut force_result, ); debug!("is_type_representable: {:?} is {:?}", ty, r); r } // Iterate until something non-representable is found fn fold_repr>(iter: It) -> Representability { iter.fold(Representability::Representable, |r1, r2| match (r1, r2) { (Representability::SelfRecursive(v1), Representability::SelfRecursive(v2)) => { Representability::SelfRecursive(v1.into_iter().chain(v2).collect()) } (r1, r2) => cmp::max(r1, r2), }) } fn are_inner_types_recursive<'tcx>( tcx: TyCtxt<'tcx>, seen: &mut Vec>, shadow_seen: &mut Vec>, representable_cache: &mut FxHashMap, Representability>, ty: Ty<'tcx>, sp: Span, field_id: Option, force_result: &mut bool, ) -> Representability { debug!("are_inner_types_recursive({:?}, {:?}, {:?})", ty, seen, shadow_seen); match ty.kind() { ty::Tuple(fields) => { // Find non representable fold_repr(fields.iter().map(|ty| { is_type_structurally_recursive( tcx, seen, shadow_seen, representable_cache, ty, sp, field_id, force_result, ) })) } // Fixed-length vectors. // FIXME(#11924) Behavior undecided for zero-length vectors. ty::Array(ty, _) => is_type_structurally_recursive( tcx, seen, shadow_seen, representable_cache, *ty, sp, field_id, force_result, ), ty::Adt(def, substs) => { // Find non representable fields with their spans fold_repr(def.all_fields().map(|field| { let ty = field.ty(tcx, substs); let (sp, field_id) = match field .did .as_local() .map(|id| tcx.hir().local_def_id_to_hir_id(id)) .and_then(|id| tcx.hir().find(id)) { Some(hir::Node::Field(field)) => (field.ty.span, Some(field.hir_id)), _ => (sp, field_id), }; let mut result = None; // First, we check whether the field type per se is representable. // This catches cases as in #74224 and #84611. There is a special // case related to mutual recursion, though; consider this example: // // struct A { // z: T, // x: B, // } // // struct B { // y: A // } // // Here, without the following special case, both A and B are // ContainsRecursive, which is a problem because we only report // errors for SelfRecursive. We fix this by detecting this special // case (shadow_seen.first() is the type we are originally // interested in, and if we ever encounter the same AdtDef again, // we know that it must be SelfRecursive) and "forcibly" returning // SelfRecursive (by setting force_result, which tells the calling // invocations of are_inner_types_representable to forward the // result without adjusting). if shadow_seen.len() > seen.len() && shadow_seen.first() == Some(def) { *force_result = true; result = Some(Representability::SelfRecursive(vec![(sp, field_id)])); } if result == None { result = Some(Representability::Representable); // Now, we check whether the field types per se are representable, e.g. // for struct Foo { x: Option }, we first check whether Option<_> // by itself is representable (which it is), and the nesting of Foo // will be detected later. This is necessary for #74224 and #84611. // If we have encountered an ADT definition that we have not seen // before (no need to check them twice), recurse to see whether that // definition is SelfRecursive. If so, we must be ContainsRecursive. if shadow_seen.len() > 1 && !shadow_seen .iter() .take(shadow_seen.len() - 1) .any(|seen_def| seen_def == def) { let adt_def_id = def.did(); let raw_adt_ty = tcx.type_of(adt_def_id); debug!("are_inner_types_recursive: checking nested type: {:?}", raw_adt_ty); // Check independently whether the ADT is SelfRecursive. If so, // we must be ContainsRecursive (except for the special case // mentioned above). let mut nested_seen: Vec> = vec![]; result = Some( match is_type_structurally_recursive( tcx, &mut nested_seen, shadow_seen, representable_cache, raw_adt_ty, sp, field_id, force_result, ) { Representability::SelfRecursive(_) => { if *force_result { Representability::SelfRecursive(vec![(sp, field_id)]) } else { Representability::ContainsRecursive } } x => x, }, ); } // We only enter the following block if the type looks representable // so far. This is necessary for cases such as this one (#74224): // // struct A { // x: T, // y: A>, // } // // struct B { // z: A // } // // When checking B, we recurse into A and check field y of type // A>. We haven't seen this exact type before, so we recurse // into A>, which contains, A>>, and so forth, // ad infinitum. We can prevent this from happening by first checking // A separately (the code above) and only checking for nested Bs if // A actually looks representable (which it wouldn't in this example). if result == Some(Representability::Representable) { // Now, even if the type is representable (e.g. Option<_>), // it might still contribute to a recursive type, e.g.: // struct Foo { x: Option> } // These cases are handled by passing the full `seen` // stack to is_type_structurally_recursive (instead of the // empty `nested_seen` above): result = Some( match is_type_structurally_recursive( tcx, seen, shadow_seen, representable_cache, ty, sp, field_id, force_result, ) { Representability::SelfRecursive(_) => { Representability::SelfRecursive(vec![(sp, field_id)]) } x => x, }, ); } } result.unwrap() })) } ty::Closure(..) => { // this check is run on type definitions, so we don't expect // to see closure types bug!("requires check invoked on inapplicable type: {:?}", ty) } _ => Representability::Representable, } } fn same_adt<'tcx>(ty: Ty<'tcx>, def: ty::AdtDef<'tcx>) -> bool { match *ty.kind() { ty::Adt(ty_def, _) => ty_def == def, _ => false, } } // Does the type `ty` directly (without indirection through a pointer) // contain any types on stack `seen`? fn is_type_structurally_recursive<'tcx>( tcx: TyCtxt<'tcx>, seen: &mut Vec>, shadow_seen: &mut Vec>, representable_cache: &mut FxHashMap, Representability>, ty: Ty<'tcx>, sp: Span, field_id: Option, force_result: &mut bool, ) -> Representability { debug!("is_type_structurally_recursive: {:?} {:?} {:?}", ty, sp, field_id); if let Some(representability) = representable_cache.get(&ty) { debug!( "is_type_structurally_recursive: {:?} {:?} {:?} - (cached) {:?}", ty, sp, field_id, representability ); return representability.clone(); } let representability = is_type_structurally_recursive_inner( tcx, seen, shadow_seen, representable_cache, ty, sp, field_id, force_result, ); representable_cache.insert(ty, representability.clone()); representability } fn is_type_structurally_recursive_inner<'tcx>( tcx: TyCtxt<'tcx>, seen: &mut Vec>, shadow_seen: &mut Vec>, representable_cache: &mut FxHashMap, Representability>, ty: Ty<'tcx>, sp: Span, field_id: Option, force_result: &mut bool, ) -> Representability { match ty.kind() { ty::Adt(def, _) => { { debug!("is_type_structurally_recursive_inner: adt: {:?}, seen: {:?}", ty, seen); // Iterate through stack of previously seen types. let mut iter = seen.iter(); // The first item in `seen` is the type we are actually curious about. // We want to return SelfRecursive if this type contains itself. // It is important that we DON'T take generic parameters into account // for this check, so that Bar in this example counts as SelfRecursive: // // struct Foo; // struct Bar { x: Bar } if let Some(&seen_adt) = iter.next() { if same_adt(seen_adt, *def) { debug!("SelfRecursive: {:?} contains {:?}", seen_adt, ty); return Representability::SelfRecursive(vec![(sp, field_id)]); } } // We also need to know whether the first item contains other types // that are structurally recursive. If we don't catch this case, we // will recurse infinitely for some inputs. // // It is important that we DO take generic parameters into account // here, because nesting e.g. Options is allowed (as long as the // definition of Option doesn't itself include an Option field, which // would be a case of SelfRecursive above). The following, too, counts // as SelfRecursive: // // struct Foo { Option> } for &seen_adt in iter { if ty == seen_adt { debug!("ContainsRecursive: {:?} contains {:?}", seen_adt, ty); return Representability::ContainsRecursive; } } } // For structs and enums, track all previously seen types by pushing them // onto the 'seen' stack. seen.push(ty); shadow_seen.push(*def); let out = are_inner_types_recursive( tcx, seen, shadow_seen, representable_cache, ty, sp, field_id, force_result, ); shadow_seen.pop(); seen.pop(); out } _ => { // No need to push in other cases. are_inner_types_recursive( tcx, seen, shadow_seen, representable_cache, ty, sp, field_id, force_result, ) } } }