Skip to content

Commit ffe9525

Browse files
committed
Auto merge of #10954 - y21:issue10158, r=llogiq
[`derivable_impls`]: don't lint if `default()` call expr unsize-coerces to trait object Fixes #10158. This fixes a FP where the derive-generated Default impl would have different behavior because of unsize coercion from `Box<T>` to `Box<dyn Trait>`: ```rs struct S { x: Box<dyn std::fmt::Debug> } impl Default for S { fn default() -> Self { Self { x: Box::<()>::default() // ^~ Box<()> coerces to Box<dyn Debug> // #[derive(Default)] would call Box::<dyn Debug>::default() } } } ``` (this intentionally only looks for trait objects `dyn` specifically, and not any unsize coercion, e.g. `&[i32; 5]` to `&[i32]`, because that breaks existing tests and isn't actually problematic, as far as I can tell) changelog: [`derivable_impls`]: don't lint if `default()` call expression unsize-coerces to trait object
2 parents 1d0d686 + 4795c91 commit ffe9525

File tree

3 files changed

+74
-7
lines changed

3 files changed

+74
-7
lines changed

clippy_lints/src/derivable_impls.rs

+32-7
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ use clippy_utils::source::indent_of;
44
use clippy_utils::{is_default_equivalent, peel_blocks};
55
use rustc_errors::Applicability;
66
use rustc_hir::{
7+
self as hir,
78
def::{CtorKind, CtorOf, DefKind, Res},
8-
Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind,
9+
Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
910
};
1011
use rustc_lint::{LateContext, LateLintPass};
11-
use rustc_middle::ty::{Adt, AdtDef, SubstsRef};
12+
use rustc_middle::ty::adjustment::{Adjust, PointerCast};
13+
use rustc_middle::ty::{self, Adt, AdtDef, SubstsRef, Ty, TypeckResults};
1214
use rustc_session::{declare_tool_lint, impl_lint_pass};
1315
use rustc_span::sym;
1416

@@ -75,13 +77,23 @@ fn is_path_self(e: &Expr<'_>) -> bool {
7577
}
7678
}
7779

80+
fn contains_trait_object(ty: Ty<'_>) -> bool {
81+
match ty.kind() {
82+
ty::Ref(_, ty, _) => contains_trait_object(*ty),
83+
ty::Adt(def, substs) => def.is_box() && substs[0].as_type().map_or(false, contains_trait_object),
84+
ty::Dynamic(..) => true,
85+
_ => false,
86+
}
87+
}
88+
7889
fn check_struct<'tcx>(
7990
cx: &LateContext<'tcx>,
8091
item: &'tcx Item<'_>,
81-
self_ty: &Ty<'_>,
92+
self_ty: &hir::Ty<'_>,
8293
func_expr: &Expr<'_>,
8394
adt_def: AdtDef<'_>,
8495
substs: SubstsRef<'_>,
96+
typeck_results: &'tcx TypeckResults<'tcx>,
8597
) {
8698
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
8799
if let Some(PathSegment { args, .. }) = p.segments.last() {
@@ -96,10 +108,23 @@ fn check_struct<'tcx>(
96108
}
97109
}
98110
}
111+
112+
// the default() call might unsize coerce to a trait object (e.g. Box<T> to Box<dyn Trait>),
113+
// which would not be the same if derived (see #10158).
114+
// this closure checks both if the expr is equivalent to a `default()` call and does not
115+
// have such coercions.
116+
let is_default_without_adjusts = |expr| {
117+
is_default_equivalent(cx, expr)
118+
&& typeck_results.expr_adjustments(expr).iter().all(|adj| {
119+
!matches!(adj.kind, Adjust::Pointer(PointerCast::Unsize)
120+
if contains_trait_object(adj.target))
121+
})
122+
};
123+
99124
let should_emit = match peel_blocks(func_expr).kind {
100-
ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)),
101-
ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)),
102-
ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)),
125+
ExprKind::Tup(fields) => fields.iter().all(is_default_without_adjusts),
126+
ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(is_default_without_adjusts),
127+
ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_without_adjusts(ef.expr)),
103128
_ => false,
104129
};
105130

@@ -197,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
197222

198223
then {
199224
if adt_def.is_struct() {
200-
check_struct(cx, item, self_ty, func_expr, adt_def, substs);
225+
check_struct(cx, item, self_ty, func_expr, adt_def, substs, cx.tcx.typeck_body(*b));
201226
} else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) {
202227
check_enum(cx, item, func_expr, adt_def);
203228
}

tests/ui/derivable_impls.fixed

+21
Original file line numberDiff line numberDiff line change
@@ -268,4 +268,25 @@ impl Default for OtherGenericType {
268268
}
269269
}
270270

271+
mod issue10158 {
272+
pub trait T {}
273+
274+
#[derive(Default)]
275+
pub struct S {}
276+
impl T for S {}
277+
278+
pub struct Outer {
279+
pub inner: Box<dyn T>,
280+
}
281+
282+
impl Default for Outer {
283+
fn default() -> Self {
284+
Outer {
285+
// Box::<S>::default() adjusts to Box<dyn T>
286+
inner: Box::<S>::default(),
287+
}
288+
}
289+
}
290+
}
291+
271292
fn main() {}

tests/ui/derivable_impls.rs

+21
Original file line numberDiff line numberDiff line change
@@ -304,4 +304,25 @@ impl Default for OtherGenericType {
304304
}
305305
}
306306

307+
mod issue10158 {
308+
pub trait T {}
309+
310+
#[derive(Default)]
311+
pub struct S {}
312+
impl T for S {}
313+
314+
pub struct Outer {
315+
pub inner: Box<dyn T>,
316+
}
317+
318+
impl Default for Outer {
319+
fn default() -> Self {
320+
Outer {
321+
// Box::<S>::default() adjusts to Box<dyn T>
322+
inner: Box::<S>::default(),
323+
}
324+
}
325+
}
326+
}
327+
307328
fn main() {}

0 commit comments

Comments
 (0)