Skip to content

Commit e8d30bf

Browse files
authored
Rollup merge of #75554 - jumbatm:fix-clashing-extern-decl-overflow, r=lcnr
Fix clashing_extern_declarations stack overflow for recursive types. Fixes #75512. Adds a seen set to `structurally_same_type` to avoid recursing indefinitely for types which contain values of the same type through a pointer or reference.
2 parents ac264b5 + bc15dd6 commit e8d30bf

File tree

2 files changed

+251
-102
lines changed

2 files changed

+251
-102
lines changed

src/librustc_lint/builtin.rs

+132-102
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use rustc_ast::visit::{FnCtxt, FnKind};
2929
use rustc_ast::{self as ast, *};
3030
use rustc_ast_pretty::pprust::{self, expr_to_string};
3131
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
32+
use rustc_data_structures::stack::ensure_sufficient_stack;
3233
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
3334
use rustc_feature::{deprecated_attributes, AttributeGate, AttributeTemplate, AttributeType};
3435
use rustc_feature::{GateIssue, Stability};
@@ -2153,123 +2154,152 @@ impl ClashingExternDeclarations {
21532154
b: Ty<'tcx>,
21542155
ckind: CItemKind,
21552156
) -> 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-
};
2157+
fn structurally_same_type_impl<'tcx>(
2158+
seen_types: &mut FxHashSet<(Ty<'tcx>, Ty<'tcx>)>,
2159+
cx: &LateContext<'tcx>,
2160+
a: Ty<'tcx>,
2161+
b: Ty<'tcx>,
2162+
ckind: CItemKind,
2163+
) -> bool {
2164+
debug!("structurally_same_type_impl(cx, a = {:?}, b = {:?})", a, b);
2165+
if !seen_types.insert((a, b)) {
2166+
// We've encountered a cycle. There's no point going any further -- the types are
2167+
// structurally the same.
2168+
return true;
2169+
}
2170+
let tcx = cx.tcx;
2171+
if a == b || rustc_middle::ty::TyS::same_type(a, b) {
2172+
// All nominally-same types are structurally same, too.
2173+
true
2174+
} else {
2175+
// Do a full, depth-first comparison between the two.
2176+
use rustc_middle::ty::TyKind::*;
2177+
let a_kind = &a.kind;
2178+
let b_kind = &b.kind;
2179+
2180+
let compare_layouts = |a, b| -> bool {
2181+
let a_layout = &cx.layout_of(a).unwrap().layout.abi;
2182+
let b_layout = &cx.layout_of(b).unwrap().layout.abi;
2183+
debug!("{:?} == {:?} = {}", a_layout, b_layout, a_layout == b_layout);
2184+
a_layout == b_layout
2185+
};
2186+
2187+
#[allow(rustc::usage_of_ty_tykind)]
2188+
let is_primitive_or_pointer = |kind: &ty::TyKind<'_>| {
2189+
kind.is_primitive() || matches!(kind, RawPtr(..) | Ref(..))
2190+
};
21732191

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)
2192+
ensure_sufficient_stack(|| {
2193+
match (a_kind, b_kind) {
2194+
(Adt(a_def, a_substs), Adt(b_def, b_substs)) => {
2195+
let a = a.subst(cx.tcx, a_substs);
2196+
let b = b.subst(cx.tcx, b_substs);
2197+
debug!("Comparing {:?} and {:?}", a, b);
2198+
2199+
// Grab a flattened representation of all fields.
2200+
let a_fields = a_def.variants.iter().flat_map(|v| v.fields.iter());
2201+
let b_fields = b_def.variants.iter().flat_map(|v| v.fields.iter());
2202+
compare_layouts(a, b)
21892203
&& a_fields.eq_by(
21902204
b_fields,
21912205
|&ty::FieldDef { did: a_did, .. },
21922206
&ty::FieldDef { did: b_did, .. }| {
2193-
Self::structurally_same_type(
2207+
structurally_same_type_impl(
2208+
seen_types,
21942209
cx,
21952210
tcx.type_of(a_did),
21962211
tcx.type_of(b_did),
21972212
ckind,
21982213
)
21992214
},
22002215
)
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)
2216+
}
2217+
(Array(a_ty, a_const), Array(b_ty, b_const)) => {
2218+
// For arrays, we also check the constness of the type.
2219+
a_const.val == b_const.val
2220+
&& structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind)
2221+
}
2222+
(Slice(a_ty), Slice(b_ty)) => {
2223+
structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind)
2224+
}
2225+
(RawPtr(a_tymut), RawPtr(b_tymut)) => {
2226+
a_tymut.mutbl == b_tymut.mutbl
2227+
&& structurally_same_type_impl(
2228+
seen_types,
2229+
cx,
2230+
&a_tymut.ty,
2231+
&b_tymut.ty,
2232+
ckind,
2233+
)
2234+
}
2235+
(Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => {
2236+
// For structural sameness, we don't need the region to be same.
2237+
a_mut == b_mut
2238+
&& structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind)
2239+
}
2240+
(FnDef(..), FnDef(..)) => {
2241+
let a_poly_sig = a.fn_sig(tcx);
2242+
let b_poly_sig = b.fn_sig(tcx);
2243+
2244+
// As we don't compare regions, skip_binder is fine.
2245+
let a_sig = a_poly_sig.skip_binder();
2246+
let b_sig = b_poly_sig.skip_binder();
2247+
2248+
(a_sig.abi, a_sig.unsafety, a_sig.c_variadic)
2249+
== (b_sig.abi, b_sig.unsafety, b_sig.c_variadic)
2250+
&& a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| {
2251+
structurally_same_type_impl(seen_types, cx, a, b, ckind)
2252+
})
2253+
&& structurally_same_type_impl(
2254+
seen_types,
2255+
cx,
2256+
a_sig.output(),
2257+
b_sig.output(),
2258+
ckind,
2259+
)
2260+
}
2261+
(Tuple(a_substs), Tuple(b_substs)) => {
2262+
a_substs.types().eq_by(b_substs.types(), |a_ty, b_ty| {
2263+
structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind)
2264+
})
2265+
}
2266+
// For these, it's not quite as easy to define structural-sameness quite so easily.
2267+
// For the purposes of this lint, take the conservative approach and mark them as
2268+
// not structurally same.
2269+
(Dynamic(..), Dynamic(..))
2270+
| (Error(..), Error(..))
2271+
| (Closure(..), Closure(..))
2272+
| (Generator(..), Generator(..))
2273+
| (GeneratorWitness(..), GeneratorWitness(..))
2274+
| (Projection(..), Projection(..))
2275+
| (Opaque(..), Opaque(..)) => false,
2276+
2277+
// These definitely should have been caught above.
2278+
(Bool, Bool) | (Char, Char) | (Never, Never) | (Str, Str) => unreachable!(),
2279+
2280+
// An Adt and a primitive or pointer type. This can be FFI-safe if non-null
2281+
// enum layout optimisation is being applied.
2282+
(Adt(..), other_kind) | (other_kind, Adt(..))
2283+
if is_primitive_or_pointer(other_kind) =>
2284+
{
2285+
let (primitive, adt) =
2286+
if is_primitive_or_pointer(&a.kind) { (a, b) } else { (b, a) };
2287+
if let Some(ty) = crate::types::repr_nullable_ptr(cx, adt, ckind) {
2288+
ty == primitive
2289+
} else {
2290+
compare_layouts(a, b)
2291+
}
2292+
}
2293+
// Otherwise, just compare the layouts. This may fail to lint for some
2294+
// incompatible types, but at the very least, will stop reads into
2295+
// uninitialised memory.
2296+
_ => compare_layouts(a, b),
22652297
}
2266-
}
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),
2298+
})
22712299
}
22722300
}
2301+
let mut seen_types = FxHashSet::default();
2302+
structurally_same_type_impl(&mut seen_types, cx, a, b, ckind)
22732303
}
22742304
}
22752305

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// check-pass
2+
//
3+
// This tests checks that clashing_extern_declarations handles types that are recursive through a
4+
// pointer or ref argument. See #75512.
5+
6+
#![crate_type = "lib"]
7+
8+
mod raw_ptr_recursion {
9+
mod a {
10+
#[repr(C)]
11+
struct Pointy {
12+
pointy: *const Pointy,
13+
}
14+
15+
extern "C" {
16+
fn run_pointy(pointy: Pointy);
17+
}
18+
}
19+
mod b {
20+
#[repr(C)]
21+
struct Pointy {
22+
pointy: *const Pointy,
23+
}
24+
25+
extern "C" {
26+
fn run_pointy(pointy: Pointy);
27+
}
28+
}
29+
}
30+
31+
mod raw_ptr_recursion_once_removed {
32+
mod a {
33+
#[repr(C)]
34+
struct Pointy1 {
35+
pointy_two: *const Pointy2,
36+
}
37+
38+
#[repr(C)]
39+
struct Pointy2 {
40+
pointy_one: *const Pointy1,
41+
}
42+
43+
extern "C" {
44+
fn run_pointy2(pointy: Pointy2);
45+
}
46+
}
47+
48+
mod b {
49+
#[repr(C)]
50+
struct Pointy1 {
51+
pointy_two: *const Pointy2,
52+
}
53+
54+
#[repr(C)]
55+
struct Pointy2 {
56+
pointy_one: *const Pointy1,
57+
}
58+
59+
extern "C" {
60+
fn run_pointy2(pointy: Pointy2);
61+
}
62+
}
63+
}
64+
65+
mod ref_recursion {
66+
mod a {
67+
#[repr(C)]
68+
struct Reffy<'a> {
69+
reffy: &'a Reffy<'a>,
70+
}
71+
72+
extern "C" {
73+
fn reffy_recursion(reffy: Reffy);
74+
}
75+
}
76+
mod b {
77+
#[repr(C)]
78+
struct Reffy<'a> {
79+
reffy: &'a Reffy<'a>,
80+
}
81+
82+
extern "C" {
83+
fn reffy_recursion(reffy: Reffy);
84+
}
85+
}
86+
}
87+
88+
mod ref_recursion_once_removed {
89+
mod a {
90+
#[repr(C)]
91+
struct Reffy1<'a> {
92+
reffy: &'a Reffy2<'a>,
93+
}
94+
95+
struct Reffy2<'a> {
96+
reffy: &'a Reffy1<'a>,
97+
}
98+
99+
extern "C" {
100+
#[allow(improper_ctypes)]
101+
fn reffy_once_removed(reffy: Reffy1);
102+
}
103+
}
104+
mod b {
105+
#[repr(C)]
106+
struct Reffy1<'a> {
107+
reffy: &'a Reffy2<'a>,
108+
}
109+
110+
struct Reffy2<'a> {
111+
reffy: &'a Reffy1<'a>,
112+
}
113+
114+
extern "C" {
115+
#[allow(improper_ctypes)]
116+
fn reffy_once_removed(reffy: Reffy1);
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)