Skip to content

Commit db75313

Browse files
committed
Fix stack overflow for recursive types.
Adds a seen set to structurally_same_type to avoid recursing indefinitely when a reference or pointer member introduces a cycle in the visited types.
1 parent b0eb55a commit db75313

File tree

1 file changed

+187
-102
lines changed

1 file changed

+187
-102
lines changed

src/librustc_lint/builtin.rs

Lines changed: 187 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -2153,123 +2153,208 @@ impl ClashingExternDeclarations {
21532153
b: Ty<'tcx>,
21542154
ckind: CItemKind,
21552155
) -> bool {
2156-
debug!("structurally_same_type(cx, a = {:?}, b = {:?})", a, b);
2157-
let tcx = cx.tcx;
2158-
if a == b || rustc_middle::ty::TyS::same_type(a, b) {
2159-
// All nominally-same types are structurally same, too.
2160-
true
2161-
} else {
2162-
// Do a full, depth-first comparison between the two.
2163-
use rustc_middle::ty::TyKind::*;
2164-
let a_kind = &a.kind;
2165-
let b_kind = &b.kind;
2166-
2167-
let compare_layouts = |a, b| -> bool {
2168-
let a_layout = &cx.layout_of(a).unwrap().layout.abi;
2169-
let b_layout = &cx.layout_of(b).unwrap().layout.abi;
2170-
debug!("{:?} == {:?} = {}", a_layout, b_layout, a_layout == b_layout);
2171-
a_layout == b_layout
2172-
};
2156+
// In order to avoid endlessly recursing on recursive types, we maintain a "seen" set.
2157+
// We'll need to store every combination of types we encounter anyway, so we also memoize
2158+
// the result.
2159+
struct SeenSet<'tcx>(FxHashMap<(Ty<'tcx>, Ty<'tcx>), Option<bool>>);
2160+
2161+
enum SeenSetResult {
2162+
/// We've never seen this combination of types.
2163+
Unseen,
2164+
/// We've seen this combination of types, but are still computing the result.
2165+
Computing,
2166+
/// We've seen this combination of types, and have already computed the result.
2167+
Computed(bool),
2168+
}
2169+
2170+
impl<'tcx> SeenSet<'tcx> {
2171+
fn new() -> Self {
2172+
SeenSet(FxHashMap::default())
2173+
}
2174+
/// Mark (a, b) as `Computing`.
2175+
fn mark_computing(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
2176+
self.0.insert((a, b), None);
2177+
}
2178+
/// Mark (a, b) as `Computed(result)`.
2179+
fn mark_computed(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, result: bool) {
2180+
*self.0.get_mut(&(a, b)).expect("Missing prior call to mark_computing") =
2181+
Some(result);
2182+
}
2183+
fn get(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> SeenSetResult {
2184+
match self.0.get(&(a, b)) {
2185+
None => SeenSetResult::Unseen,
2186+
Some(None) => SeenSetResult::Computing,
2187+
Some(Some(b)) => SeenSetResult::Computed(*b),
2188+
}
2189+
}
2190+
}
2191+
fn structurally_same_type_impl<'tcx>(
2192+
seen_types: &mut SeenSet<'tcx>,
2193+
cx: &LateContext<'tcx>,
2194+
a: Ty<'tcx>,
2195+
b: Ty<'tcx>,
2196+
ckind: CItemKind,
2197+
) -> bool {
2198+
debug!("structurally_same_type_impl(cx, a = {:?}, b = {:?})", a, b);
2199+
match seen_types.get(a, b) {
2200+
// If we've already computed the result, just return the memoized result.
2201+
SeenSetResult::Computed(result) => result,
2202+
// We are already in the process of computing structural sameness for this type,
2203+
// meaning we've found a cycle. The types are structurally same, then.
2204+
SeenSetResult::Computing => true,
2205+
// We haven't seen this combination of types at all -- compute their sameness.
2206+
SeenSetResult::Unseen => {
2207+
seen_types.mark_computing(a, b);
2208+
let tcx = cx.tcx;
2209+
let result = if a == b || rustc_middle::ty::TyS::same_type(a, b) {
2210+
// All nominally-same types are structurally same, too.
2211+
true
2212+
} else {
2213+
// Do a full, depth-first comparison between the two.
2214+
use rustc_middle::ty::TyKind::*;
2215+
let a_kind = &a.kind;
2216+
let b_kind = &b.kind;
2217+
2218+
let compare_layouts = |a, b| -> bool {
2219+
let a_layout = &cx.layout_of(a).unwrap().layout.abi;
2220+
let b_layout = &cx.layout_of(b).unwrap().layout.abi;
2221+
debug!("{:?} == {:?} = {}", a_layout, b_layout, a_layout == b_layout);
2222+
a_layout == b_layout
2223+
};
2224+
2225+
#[allow(rustc::usage_of_ty_tykind)]
2226+
let is_primitive_or_pointer = |kind: &ty::TyKind<'_>| {
2227+
kind.is_primitive() || matches!(kind, RawPtr(..))
2228+
};
21732229

2174-
#[allow(rustc::usage_of_ty_tykind)]
2175-
let is_primitive_or_pointer =
2176-
|kind: &ty::TyKind<'_>| kind.is_primitive() || matches!(kind, RawPtr(..));
2177-
2178-
match (a_kind, b_kind) {
2179-
(Adt(_, a_substs), Adt(_, b_substs)) => {
2180-
let a = a.subst(cx.tcx, a_substs);
2181-
let b = b.subst(cx.tcx, b_substs);
2182-
debug!("Comparing {:?} and {:?}", a, b);
2183-
2184-
if let (Adt(a_def, ..), Adt(b_def, ..)) = (&a.kind, &b.kind) {
2185-
// Grab a flattened representation of all fields.
2186-
let a_fields = a_def.variants.iter().flat_map(|v| v.fields.iter());
2187-
let b_fields = b_def.variants.iter().flat_map(|v| v.fields.iter());
2188-
compare_layouts(a, b)
2230+
match (a_kind, b_kind) {
2231+
(Adt(_, a_substs), Adt(_, b_substs)) => {
2232+
let a = a.subst(cx.tcx, a_substs);
2233+
let b = b.subst(cx.tcx, b_substs);
2234+
debug!("Comparing {:?} and {:?}", a, b);
2235+
2236+
if let (Adt(a_def, ..), Adt(b_def, ..)) = (&a.kind, &b.kind) {
2237+
// Grab a flattened representation of all fields.
2238+
let a_fields =
2239+
a_def.variants.iter().flat_map(|v| v.fields.iter());
2240+
let b_fields =
2241+
b_def.variants.iter().flat_map(|v| v.fields.iter());
2242+
compare_layouts(a, b)
21892243
&& a_fields.eq_by(
21902244
b_fields,
21912245
|&ty::FieldDef { did: a_did, .. },
21922246
&ty::FieldDef { did: b_did, .. }| {
2193-
Self::structurally_same_type(
2247+
structurally_same_type_impl(
2248+
seen_types,
21942249
cx,
21952250
tcx.type_of(a_did),
21962251
tcx.type_of(b_did),
21972252
ckind,
21982253
)
21992254
},
22002255
)
2201-
} else {
2202-
unreachable!()
2203-
}
2204-
}
2205-
(Array(a_ty, a_const), Array(b_ty, b_const)) => {
2206-
// For arrays, we also check the constness of the type.
2207-
a_const.val == b_const.val
2208-
&& Self::structurally_same_type(cx, a_const.ty, b_const.ty, ckind)
2209-
&& Self::structurally_same_type(cx, a_ty, b_ty, ckind)
2210-
}
2211-
(Slice(a_ty), Slice(b_ty)) => Self::structurally_same_type(cx, a_ty, b_ty, ckind),
2212-
(RawPtr(a_tymut), RawPtr(b_tymut)) => {
2213-
a_tymut.mutbl == b_tymut.mutbl
2214-
&& Self::structurally_same_type(cx, &a_tymut.ty, &b_tymut.ty, ckind)
2215-
}
2216-
(Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => {
2217-
// For structural sameness, we don't need the region to be same.
2218-
a_mut == b_mut && Self::structurally_same_type(cx, a_ty, b_ty, ckind)
2219-
}
2220-
(FnDef(..), FnDef(..)) => {
2221-
let a_poly_sig = a.fn_sig(tcx);
2222-
let b_poly_sig = b.fn_sig(tcx);
2223-
2224-
// As we don't compare regions, skip_binder is fine.
2225-
let a_sig = a_poly_sig.skip_binder();
2226-
let b_sig = b_poly_sig.skip_binder();
2227-
2228-
(a_sig.abi, a_sig.unsafety, a_sig.c_variadic)
2229-
== (b_sig.abi, b_sig.unsafety, b_sig.c_variadic)
2230-
&& a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| {
2231-
Self::structurally_same_type(cx, a, b, ckind)
2232-
})
2233-
&& Self::structurally_same_type(cx, a_sig.output(), b_sig.output(), ckind)
2234-
}
2235-
(Tuple(a_substs), Tuple(b_substs)) => {
2236-
a_substs.types().eq_by(b_substs.types(), |a_ty, b_ty| {
2237-
Self::structurally_same_type(cx, a_ty, b_ty, ckind)
2238-
})
2239-
}
2240-
// For these, it's not quite as easy to define structural-sameness quite so easily.
2241-
// For the purposes of this lint, take the conservative approach and mark them as
2242-
// not structurally same.
2243-
(Dynamic(..), Dynamic(..))
2244-
| (Error(..), Error(..))
2245-
| (Closure(..), Closure(..))
2246-
| (Generator(..), Generator(..))
2247-
| (GeneratorWitness(..), GeneratorWitness(..))
2248-
| (Projection(..), Projection(..))
2249-
| (Opaque(..), Opaque(..)) => false,
2250-
2251-
// These definitely should have been caught above.
2252-
(Bool, Bool) | (Char, Char) | (Never, Never) | (Str, Str) => unreachable!(),
2253-
2254-
// An Adt and a primitive type. This can be FFI-safe is the ADT is an enum with a
2255-
// non-null field.
2256-
(Adt(..), other_kind) | (other_kind, Adt(..))
2257-
if is_primitive_or_pointer(other_kind) =>
2258-
{
2259-
let (primitive, adt) =
2260-
if is_primitive_or_pointer(&a.kind) { (a, b) } else { (b, a) };
2261-
if let Some(ty) = crate::types::repr_nullable_ptr(cx, adt, ckind) {
2262-
ty == primitive
2263-
} else {
2264-
compare_layouts(a, b)
2265-
}
2256+
} else {
2257+
unreachable!()
2258+
}
2259+
}
2260+
(Array(a_ty, a_const), Array(b_ty, b_const)) => {
2261+
// For arrays, we also check the constness of the type.
2262+
a_const.val == b_const.val
2263+
&& structurally_same_type_impl(
2264+
seen_types, cx, a_const.ty, b_const.ty, ckind,
2265+
)
2266+
&& structurally_same_type_impl(
2267+
seen_types, cx, a_ty, b_ty, ckind,
2268+
)
2269+
}
2270+
(Slice(a_ty), Slice(b_ty)) => {
2271+
structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind)
2272+
}
2273+
(RawPtr(a_tymut), RawPtr(b_tymut)) => {
2274+
a_tymut.mutbl == b_tymut.mutbl
2275+
&& structurally_same_type_impl(
2276+
seen_types,
2277+
cx,
2278+
&a_tymut.ty,
2279+
&b_tymut.ty,
2280+
ckind,
2281+
)
2282+
}
2283+
(Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => {
2284+
// For structural sameness, we don't need the region to be same.
2285+
a_mut == b_mut
2286+
&& structurally_same_type_impl(
2287+
seen_types, cx, a_ty, b_ty, ckind,
2288+
)
2289+
}
2290+
(FnDef(..), FnDef(..)) => {
2291+
let a_poly_sig = a.fn_sig(tcx);
2292+
let b_poly_sig = b.fn_sig(tcx);
2293+
2294+
// As we don't compare regions, skip_binder is fine.
2295+
let a_sig = a_poly_sig.skip_binder();
2296+
let b_sig = b_poly_sig.skip_binder();
2297+
2298+
(a_sig.abi, a_sig.unsafety, a_sig.c_variadic)
2299+
== (b_sig.abi, b_sig.unsafety, b_sig.c_variadic)
2300+
&& a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| {
2301+
structurally_same_type_impl(seen_types, cx, a, b, ckind)
2302+
})
2303+
&& structurally_same_type_impl(
2304+
seen_types,
2305+
cx,
2306+
a_sig.output(),
2307+
b_sig.output(),
2308+
ckind,
2309+
)
2310+
}
2311+
(Tuple(a_substs), Tuple(b_substs)) => {
2312+
a_substs.types().eq_by(b_substs.types(), |a_ty, b_ty| {
2313+
structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind)
2314+
})
2315+
}
2316+
// For these, it's not quite as easy to define structural-sameness quite so easily.
2317+
// For the purposes of this lint, take the conservative approach and mark them as
2318+
// not structurally same.
2319+
(Dynamic(..), Dynamic(..))
2320+
| (Error(..), Error(..))
2321+
| (Closure(..), Closure(..))
2322+
| (Generator(..), Generator(..))
2323+
| (GeneratorWitness(..), GeneratorWitness(..))
2324+
| (Projection(..), Projection(..))
2325+
| (Opaque(..), Opaque(..)) => false,
2326+
2327+
// These definitely should have been caught above.
2328+
(Bool, Bool) | (Char, Char) | (Never, Never) | (Str, Str) => {
2329+
unreachable!()
2330+
}
2331+
2332+
// An Adt and a primitive type. This can be FFI-safe is the ADT is an enum with a
2333+
// non-null field.
2334+
(Adt(..), other_kind) | (other_kind, Adt(..))
2335+
if is_primitive_or_pointer(other_kind) =>
2336+
{
2337+
let (primitive, adt) =
2338+
if is_primitive_or_pointer(&a.kind) { (a, b) } else { (b, a) };
2339+
if let Some(ty) = crate::types::repr_nullable_ptr(cx, adt, ckind) {
2340+
ty == primitive
2341+
} else {
2342+
compare_layouts(a, b)
2343+
}
2344+
}
2345+
// Otherwise, just compare the layouts. This may fail to lint for some
2346+
// incompatible types, but at the very least, will stop reads into
2347+
// uninitialised memory.
2348+
_ => compare_layouts(a, b),
2349+
}
2350+
};
2351+
seen_types.mark_computed(a, b, result);
2352+
result
22662353
}
2267-
// Otherwise, just compare the layouts. This may fail to lint for some
2268-
// incompatible types, but at the very least, will stop reads into
2269-
// uninitialised memory.
2270-
_ => compare_layouts(a, b),
22712354
}
22722355
}
2356+
let mut seen_types = SeenSet::new();
2357+
structurally_same_type_impl(&mut seen_types, cx, a, b, ckind)
22732358
}
22742359
}
22752360

0 commit comments

Comments
 (0)