Skip to content

Commit f3c0c12

Browse files
committed
Use MIR body to compare "equivalences"
1 parent e265617 commit f3c0c12

File tree

7 files changed

+56
-94
lines changed

7 files changed

+56
-94
lines changed

compiler/rustc_lint/src/default_could_be_derived.rs

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use rustc_data_structures::fx::FxHashMap;
33
use rustc_errors::Applicability;
44
use rustc_hir as hir;
55
use rustc_hir::def::{CtorKind, DefKind, Res};
6-
use rustc_middle::ty::Ty;
6+
use rustc_middle::mir;
7+
use rustc_middle::ty::{self, Instance, Ty};
78
use rustc_session::{declare_lint, declare_lint_pass};
89
use rustc_span::Span;
910
use rustc_span::symbol::{kw, sym};
@@ -391,42 +392,73 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
391392
}
392393
}
393394

395+
/// For the `Default` impl for this type, we see if it has a `Default::default()` body composed
396+
/// only of a path, ctor or function call with no arguments. If so, we compare that `DefId`
397+
/// against the `DefId` of this field's value if it is also a call/path/ctor.
398+
/// If there's a match, it means that the contents of that type's `Default` impl are the
399+
/// same to what the user wrote on *their* `Default` impl for this field.
394400
fn check_path<'tcx>(
395401
cx: &LateContext<'tcx>,
396402
path: &hir::QPath<'_>,
397403
hir_id: hir::HirId,
398404
ty: Ty<'tcx>,
399405
) -> bool {
400-
let Some(default_def_id) = cx.tcx.get_diagnostic_item(sym::Default) else {
401-
return false;
402-
};
403406
let res = cx.qpath_res(&path, hir_id);
404407
let Some(def_id) = res.opt_def_id() else { return false };
405-
if cx.tcx.is_diagnostic_item(sym::default_fn, def_id) {
408+
let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
409+
return false;
410+
};
411+
if default_fn_def_id == def_id {
406412
// We have `field: Default::default(),`. This is what the derive would do already.
407413
return true;
408414
}
409-
// For every `Default` impl for this type (there should be a single one), we see if it
410-
// has a "canonical" `DefId` for a fn call with no arguments, or a path. If it does, we
411-
// check that `DefId` with the `DefId` of this field's value if it is also a call/path.
412-
// If there's a match, it means that the contents of that type's `Default` impl are the
413-
// same to what the user wrote on *their* `Default` impl for this field.
414-
let mut equivalents = vec![];
415-
cx.tcx.for_each_relevant_impl(default_def_id, ty, |impl_def_id| {
416-
let equivalent = match impl_def_id.as_local() {
417-
None => cx.tcx.get_default_impl_equivalent(impl_def_id),
418-
Some(local) => {
419-
let def_kind = cx.tcx.def_kind(impl_def_id);
420-
cx.tcx.get_default_equivalent(def_kind, local)
421-
}
422-
};
423-
if let Some(did) = equivalent {
424-
equivalents.push(did);
415+
416+
let args = ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
417+
if let ty::GenericParamDefKind::Lifetime = param.kind {
418+
cx.tcx.lifetimes.re_erased.into()
419+
} else if param.index == 0 && param.name == kw::SelfUpper {
420+
ty.into()
421+
} else {
422+
param.to_error(cx.tcx)
425423
}
426424
});
427-
for did in equivalents {
428-
if did == def_id {
425+
let instance = Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
426+
427+
let Ok(Some(instance)) = instance else { return false };
428+
// Get the MIR Body for the `<Ty as Default>::default()` function.
429+
// If it is a value or call (either fn or ctor), we compare its DefId against the one for the
430+
// resolution of the expression we had in the path.
431+
let body = cx.tcx.instance_mir(instance.def);
432+
for block_data in body.basic_blocks.iter() {
433+
if block_data.statements.len() == 1
434+
&& let mir::StatementKind::Assign(assign) = &block_data.statements[0].kind
435+
&& let mir::Rvalue::Aggregate(kind, _places) = &assign.1
436+
&& let mir::AggregateKind::Adt(did, variant_index, _, _, _) = &**kind
437+
&& let def = cx.tcx.adt_def(did)
438+
&& let variant = &def.variant(*variant_index)
439+
&& variant.fields.is_empty()
440+
&& let Some((_, did)) = variant.ctor
441+
&& did == def_id
442+
{
429443
return true;
444+
} else if block_data.statements.len() == 0
445+
&& let Some(term) = &block_data.terminator
446+
{
447+
match &term.kind {
448+
mir::TerminatorKind::Call { func: mir::Operand::Constant(c), .. }
449+
if let ty::FnDef(did, _args) = c.ty().kind()
450+
&& *did == def_id =>
451+
{
452+
return true;
453+
}
454+
mir::TerminatorKind::TailCall { func: mir::Operand::Constant(c), .. }
455+
if let ty::FnDef(did, _args) = c.ty().kind()
456+
&& *did == def_id =>
457+
{
458+
return true;
459+
}
460+
_ => {}
461+
}
430462
}
431463
}
432464
false

compiler/rustc_metadata/src/rmeta/decoder.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,12 +1174,6 @@ impl<'a> CrateMetadataRef<'a> {
11741174
self.root.tables.default_fields.get(self, id).map(|d| d.decode(self))
11751175
}
11761176

1177-
/// For a given `impl Default for Ty`, return the "equivalence" for it, namely the `DefId` of
1178-
/// a path or function with no arguments that get's called from `<Ty as Default>::default()`.
1179-
fn get_default_impl_equivalent(self, id: DefIndex) -> Option<DefId> {
1180-
self.root.tables.default_impl_equivalent.get(self, id).map(|d| d.decode(self))
1181-
}
1182-
11831177
fn get_trait_item_def_id(self, id: DefIndex) -> Option<DefId> {
11841178
self.root.tables.trait_item_def_id.get(self, id).map(|d| d.decode_from_cdata(self))
11851179
}

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,6 @@ provide! { tcx, def_id, other, cdata,
423423
syms
424424
}
425425

426-
get_default_impl_equivalent => { cdata.get_default_impl_equivalent(def_id.index) }
427-
428426
crate_extern_paths => { cdata.source().paths().cloned().collect() }
429427
expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) }
430428
is_doc_hidden => { cdata.get_attr_flags(def_id.index).contains(AttrFlags::IS_DOC_HIDDEN) }

compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,10 +1568,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
15681568
let table = tcx.associated_types_for_impl_traits_in_associated_fn(def_id);
15691569
record_defaulted_array!(self.tables.associated_types_for_impl_traits_in_associated_fn[def_id] <- table);
15701570
}
1571-
1572-
if let Some(path_def_id) = tcx.get_default_equivalent(def_kind, local_id) {
1573-
record!(self.tables.default_impl_equivalent[def_id] <- path_def_id);
1574-
}
15751571
}
15761572

15771573
for (def_id, impls) in &tcx.crate_inherent_impls(()).0.inherent_impls {

compiler/rustc_metadata/src/rmeta/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,6 @@ define_tables! {
451451
trait_item_def_id: Table<DefIndex, RawDefId>,
452452
expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>,
453453
default_fields: Table<DefIndex, LazyValue<DefId>>,
454-
default_impl_equivalent: Table<DefIndex, LazyValue<DefId>>,
455454
params_in_repr: Table<DefIndex, LazyValue<BitSet<u32>>>,
456455
repr_options: Table<DefIndex, LazyValue<ReprOptions>>,
457456
// `def_keys` and `def_path_hashes` represent a lazy version of a

compiler/rustc_middle/src/query/mod.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,11 +1807,6 @@ rustc_queries! {
18071807
separate_provide_extern
18081808
}
18091809

1810-
query get_default_impl_equivalent(def_id: DefId) -> Option<DefId> {
1811-
desc { |tcx| "looking up the unit variant or fn call with no arguments equivalence for the `Default::default()` of `{}`", tcx.def_path_str(def_id) }
1812-
separate_provide_extern
1813-
}
1814-
18151810
query late_bound_vars_map(owner_id: hir::OwnerId)
18161811
-> &'tcx SortedMap<ItemLocalId, Vec<ty::BoundVariableKind>> {
18171812
desc { |tcx| "looking up late bound vars inside `{}`", tcx.def_path_str(owner_id) }

compiler/rustc_middle/src/ty/context.rs

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use rustc_data_structures::unord::UnordSet;
2929
use rustc_errors::{
3030
Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan,
3131
};
32-
use rustc_hir::def::{CtorKind, DefKind, Res};
32+
use rustc_hir::def::{CtorKind, DefKind};
3333
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
3434
use rustc_hir::definitions::Definitions;
3535
use rustc_hir::intravisit::Visitor;
@@ -3205,58 +3205,6 @@ impl<'tcx> TyCtxt<'tcx> {
32053205
}
32063206
}
32073207

3208-
fn get_expr_equivalent(self, expr: &hir::Expr<'_>) -> Option<DefId> {
3209-
match &expr.kind {
3210-
hir::ExprKind::Path(qpath) => {
3211-
if self.has_typeck_results(expr.hir_id.owner.def_id) {
3212-
let res = self.typeck(expr.hir_id.owner.def_id).qpath_res(&qpath, expr.hir_id);
3213-
return res.opt_def_id();
3214-
}
3215-
}
3216-
hir::ExprKind::Call(hir::Expr { kind: hir::ExprKind::Path(qpath), hir_id, .. }, []) => {
3217-
if self.has_typeck_results(expr.hir_id.owner.def_id) {
3218-
let res = self.typeck(expr.hir_id.owner.def_id).qpath_res(&qpath, *hir_id);
3219-
return res.opt_def_id();
3220-
}
3221-
}
3222-
_ => {}
3223-
}
3224-
None
3225-
}
3226-
3227-
/// Given an `impl Default for Ty` item, return the `DefId` of the single path or fn call within
3228-
/// the `<Ty as Default::default()` function. This is used to compare between a type's
3229-
/// `Default::defaut()` implementation and what a user has used in their manual `Default` impl
3230-
/// for a type they control that has a field of type `Ty`.
3231-
pub fn get_default_equivalent(self, def_kind: DefKind, local_id: LocalDefId) -> Option<DefId> {
3232-
if (DefKind::Impl { of_trait: true }) == def_kind
3233-
&& let hir::Node::Item(item) = self.hir_node_by_def_id(local_id)
3234-
&& let hir::ItemKind::Impl(data) = item.kind
3235-
&& let Some(trait_ref) = data.of_trait
3236-
&& let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res
3237-
&& let Some(default_def_id) = self.get_diagnostic_item(sym::Default)
3238-
&& Some(trait_def_id) == Some(default_def_id)
3239-
{
3240-
let hir = self.hir();
3241-
for assoc in data.items {
3242-
let hir::AssocItemKind::Fn { has_self: false } = assoc.kind else { continue };
3243-
if assoc.ident.name != kw::Default {
3244-
continue;
3245-
}
3246-
let assoc = hir.impl_item(assoc.id);
3247-
let hir::ImplItemKind::Fn(_ty, body) = assoc.kind else { continue };
3248-
let body = hir.body(body);
3249-
let hir::ExprKind::Block(hir::Block { stmts: [], expr: Some(expr), .. }, None) =
3250-
body.value.kind
3251-
else {
3252-
continue;
3253-
};
3254-
return self.get_expr_equivalent(&expr);
3255-
}
3256-
}
3257-
None
3258-
}
3259-
32603208
/// Whether this is a trait implementation that has `#[diagnostic::do_not_recommend]`
32613209
pub fn do_not_recommend_impl(self, def_id: DefId) -> bool {
32623210
self.get_diagnostic_attr(def_id, sym::do_not_recommend).is_some()

0 commit comments

Comments
 (0)