Skip to content

Commit bd57421

Browse files
committed
Handle more cases in is_normalizable
By assuming that a recursive type is normalizable within the deeper calls to `is_normalizable_helper()`, more cases can be handled by this function. In order to fix stack overflows, a recursion limit has also been added for recursive generic type instantiations.
1 parent 42a0263 commit bd57421

File tree

6 files changed

+174
-8
lines changed

6 files changed

+174
-8
lines changed

clippy_utils/src/ty/mod.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -351,20 +351,26 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
351351
/// Checks if `Ty` is normalizable. This function is useful
352352
/// to avoid crashes on `layout_of`.
353353
pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
354-
is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default())
354+
is_normalizable_helper(cx, param_env, ty, 0, &mut FxHashMap::default())
355355
}
356356

357357
fn is_normalizable_helper<'tcx>(
358358
cx: &LateContext<'tcx>,
359359
param_env: ParamEnv<'tcx>,
360360
ty: Ty<'tcx>,
361+
depth: usize,
361362
cache: &mut FxHashMap<Ty<'tcx>, bool>,
362363
) -> bool {
363364
if let Some(&cached_result) = cache.get(&ty) {
364365
return cached_result;
365366
}
366-
// prevent recursive loops, false-negative is better than endless loop leading to stack overflow
367-
cache.insert(ty, false);
367+
if !cx.tcx.recursion_limit().value_within_limit(depth) {
368+
return false;
369+
}
370+
// Prevent recursive loops by answering `true` to recursive requests with the same
371+
// type. This will be adjusted when the outermost call analyzes all the type
372+
// components.
373+
cache.insert(ty, true);
368374
let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode());
369375
let cause = ObligationCause::dummy();
370376
let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() {
@@ -373,11 +379,11 @@ fn is_normalizable_helper<'tcx>(
373379
variant
374380
.fields
375381
.iter()
376-
.all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, args), cache))
382+
.all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, args), depth + 1, cache))
377383
}),
378384
_ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
379385
GenericArgKind::Type(inner_ty) if inner_ty != ty => {
380-
is_normalizable_helper(cx, param_env, inner_ty, cache)
386+
is_normalizable_helper(cx, param_env, inner_ty, depth + 1, cache)
381387
},
382388
_ => true, // if inner_ty == ty, we've already checked it
383389
}),

tests/ui/crashes/ice-10508a.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Used to overflow in `is_normalizable`
2+
3+
use std::marker::PhantomData;
4+
5+
struct Node<T: 'static> {
6+
m: PhantomData<&'static T>,
7+
}
8+
9+
struct Digit<T> {
10+
elem: T,
11+
}
12+
13+
enum FingerTree<T: 'static> {
14+
Single(T),
15+
16+
Deep(Digit<T>, Box<FingerTree<Node<T>>>),
17+
}
18+
19+
fn main() {}

tests/ui/crashes/ice-10508b.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use std::marker::PhantomData;
2+
3+
struct Digit<T> {
4+
elem: T,
5+
}
6+
7+
struct Node<T: 'static> {
8+
m: PhantomData<&'static T>,
9+
}
10+
11+
enum FingerTree<T: 'static> {
12+
Single(T),
13+
14+
Deep(Digit<T>, Node<FingerTree<Node<T>>>),
15+
}
16+
17+
enum Wrapper<T: 'static> {
18+
Simple,
19+
Other(FingerTree<T>),
20+
}
21+
22+
fn main() {
23+
let w = Some(Wrapper::Simple::<u32>);
24+
}

tests/ui/crashes/ice-10508c.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#[derive(Debug)]
2+
struct S<T> {
3+
t: T,
4+
s: Box<S<fn(u: T)>>,
5+
}
6+
7+
fn main() {}

tests/ui/large_enum_variant.64bit.stderr

+51-3
Original file line numberDiff line numberDiff line change
@@ -283,14 +283,62 @@ LL | / enum WithRecursion {
283283
LL | | Large([u64; 64]),
284284
| | ---------------- the largest variant contains at least 512 bytes
285285
LL | | Recursive(Box<WithRecursion>),
286-
| | ----------------------------- the second-largest variant contains at least 0 bytes
286+
| | ----------------------------- the second-largest variant contains at least 8 bytes
287287
LL | | }
288-
| |_^ the entire enum is at least 0 bytes
288+
| |_^ the entire enum is at least 520 bytes
289289
|
290290
help: consider boxing the large fields to reduce the total size of the enum
291291
|
292292
LL | Large(Box<[u64; 64]>),
293293
| ~~~~~~~~~~~~~~
294294

295-
error: aborting due to 17 previous errors
295+
error: large size difference between variants
296+
--> tests/ui/large_enum_variant.rs:168:1
297+
|
298+
LL | / enum LargeEnumWithGenericsAndRecursive {
299+
LL | | Ok(),
300+
| | ---- the second-largest variant carries no data at all
301+
LL | | Error(WithRecursionAndGenerics<u64>),
302+
| | ------------------------------------ the largest variant contains at least 520 bytes
303+
LL | | }
304+
| |_^ the entire enum is at least 520 bytes
305+
|
306+
help: consider boxing the large fields to reduce the total size of the enum
307+
|
308+
LL | Error(Box<WithRecursionAndGenerics<u64>>),
309+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
310+
311+
error: large size difference between variants
312+
--> tests/ui/large_enum_variant.rs:203:5
313+
|
314+
LL | / enum NoWarnings {
315+
LL | | BigBoi(PublishWithBytes),
316+
| | ------------------------ the largest variant contains at least 296 bytes
317+
LL | | _SmallBoi(u8),
318+
| | ------------- the second-largest variant contains at least 1 bytes
319+
LL | | }
320+
| |_____^ the entire enum is at least 296 bytes
321+
|
322+
help: consider boxing the large fields to reduce the total size of the enum
323+
|
324+
LL | BigBoi(Box<PublishWithBytes>),
325+
| ~~~~~~~~~~~~~~~~~~~~~
326+
327+
error: large size difference between variants
328+
--> tests/ui/large_enum_variant.rs:208:5
329+
|
330+
LL | / enum MakesClippyAngry {
331+
LL | | BigBoi(PublishWithVec),
332+
| | ---------------------- the largest variant contains at least 224 bytes
333+
LL | | _SmallBoi(u8),
334+
| | ------------- the second-largest variant contains at least 1 bytes
335+
LL | | }
336+
| |_____^ the entire enum is at least 224 bytes
337+
|
338+
help: consider boxing the large fields to reduce the total size of the enum
339+
|
340+
LL | BigBoi(Box<PublishWithVec>),
341+
| ~~~~~~~~~~~~~~~~~~~
342+
343+
error: aborting due to 20 previous errors
296344

tests/ui/large_enum_variant.rs

+62
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,65 @@ fn main() {
178178
}
179179
);
180180
}
181+
182+
mod issue11915 {
183+
use std::sync::atomic::AtomicPtr;
184+
185+
pub struct Bytes {
186+
ptr: *const u8,
187+
len: usize,
188+
// inlined "trait object"
189+
data: AtomicPtr<()>,
190+
vtable: &'static Vtable,
191+
}
192+
pub(crate) struct Vtable {
193+
/// fn(data, ptr, len)
194+
pub clone: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> Bytes,
195+
/// fn(data, ptr, len)
196+
///
197+
/// takes `Bytes` to value
198+
pub to_vec: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> Vec<u8>,
199+
/// fn(data, ptr, len)
200+
pub drop: unsafe fn(&mut AtomicPtr<()>, *const u8, usize),
201+
}
202+
203+
enum NoWarnings {
204+
BigBoi(PublishWithBytes),
205+
_SmallBoi(u8),
206+
}
207+
208+
enum MakesClippyAngry {
209+
BigBoi(PublishWithVec),
210+
_SmallBoi(u8),
211+
}
212+
213+
struct PublishWithBytes {
214+
_dup: bool,
215+
_retain: bool,
216+
_topic: Bytes,
217+
__topic: Bytes,
218+
___topic: Bytes,
219+
____topic: Bytes,
220+
_pkid: u16,
221+
_payload: Bytes,
222+
__payload: Bytes,
223+
___payload: Bytes,
224+
____payload: Bytes,
225+
_____payload: Bytes,
226+
}
227+
228+
struct PublishWithVec {
229+
_dup: bool,
230+
_retain: bool,
231+
_topic: Vec<u8>,
232+
__topic: Vec<u8>,
233+
___topic: Vec<u8>,
234+
____topic: Vec<u8>,
235+
_pkid: u16,
236+
_payload: Vec<u8>,
237+
__payload: Vec<u8>,
238+
___payload: Vec<u8>,
239+
____payload: Vec<u8>,
240+
_____payload: Vec<u8>,
241+
}
242+
}

0 commit comments

Comments
 (0)