Skip to content

Commit 85c0558

Browse files
committedOct 28, 2021
Auto merge of #90218 - JakobDegen:adt_significant_drop_fix, r=nikomatsakis
Fixes incorrect handling of ADT's drop requirements Fixes #90024 and a bunch of duplicates. The main issue was just that the contract of `NeedsDropTypes::adt_components` was inconsistent; the list of types it might return were the generic parameters themselves or the fields of the ADT, depending on the nature of the drop impl. This meant that the caller could not determine whether a `.subst()` call was still needed on those types; it called `.subst()` in all cases, and this led to ICEs when the returned types were the generic params. First contribution of more than a few lines, so feedback definitely appreciated.
2 parents c4ff03f + aff37f8 commit 85c0558

File tree

2 files changed

+93
-45
lines changed

2 files changed

+93
-45
lines changed
 

‎compiler/rustc_ty_utils/src/needs_drop.rs

+56-45
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,12 @@ use rustc_span::{sym, DUMMY_SP};
1212
type NeedsDropResult<T> = Result<T, AlwaysRequiresDrop>;
1313

1414
fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
15-
let adt_components =
16-
move |adt_def: &ty::AdtDef, _| tcx.adt_drop_tys(adt_def.did).map(|tys| tys.iter());
17-
1815
// If we don't know a type doesn't need drop, for example if it's a type
1916
// parameter without a `Copy` bound, then we conservatively return that it
2017
// needs drop.
21-
let res =
22-
NeedsDropTypes::new(tcx, query.param_env, query.value, adt_components).next().is_some();
18+
let adt_has_dtor =
19+
|adt_def: &ty::AdtDef| adt_def.destructor(tcx).map(|_| DtorType::Significant);
20+
let res = drop_tys_helper(tcx, query.value, query.param_env, adt_has_dtor).next().is_some();
2321

2422
debug!("needs_drop_raw({:?}) = {:?}", query, res);
2523
res
@@ -29,12 +27,10 @@ fn has_significant_drop_raw<'tcx>(
2927
tcx: TyCtxt<'tcx>,
3028
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
3129
) -> bool {
32-
let significant_drop_fields = move |adt_def: &ty::AdtDef, _| {
33-
tcx.adt_significant_drop_tys(adt_def.did).map(|tys| tys.iter())
34-
};
35-
let res = NeedsDropTypes::new(tcx, query.param_env, query.value, significant_drop_fields)
36-
.next()
37-
.is_some();
30+
let res =
31+
drop_tys_helper(tcx, query.value, query.param_env, adt_consider_insignificant_dtor(tcx))
32+
.next()
33+
.is_some();
3834
debug!("has_significant_drop_raw({:?}) = {:?}", query, res);
3935
res
4036
}
@@ -145,10 +141,8 @@ where
145141
Ok(tys) => tys,
146142
};
147143
for required_ty in tys {
148-
let subst_ty = tcx.normalize_erasing_regions(
149-
self.param_env,
150-
required_ty.subst(tcx, substs),
151-
);
144+
let subst_ty =
145+
tcx.normalize_erasing_regions(self.param_env, required_ty);
152146
queue_type(self, subst_ty);
153147
}
154148
}
@@ -187,23 +181,24 @@ enum DtorType {
187181
// Depending on the implentation of `adt_has_dtor`, it is used to check if the
188182
// ADT has a destructor or if the ADT only has a significant destructor. For
189183
// understanding significant destructor look at `adt_significant_drop_tys`.
190-
fn adt_drop_tys_helper<'tcx>(
184+
fn drop_tys_helper<'tcx>(
191185
tcx: TyCtxt<'tcx>,
192-
def_id: DefId,
186+
ty: Ty<'tcx>,
187+
param_env: rustc_middle::ty::ParamEnv<'tcx>,
193188
adt_has_dtor: impl Fn(&ty::AdtDef) -> Option<DtorType>,
194-
) -> Result<&ty::List<Ty<'tcx>>, AlwaysRequiresDrop> {
189+
) -> impl Iterator<Item = NeedsDropResult<Ty<'tcx>>> {
195190
let adt_components = move |adt_def: &ty::AdtDef, substs: SubstsRef<'tcx>| {
196191
if adt_def.is_manually_drop() {
197-
debug!("adt_drop_tys: `{:?}` is manually drop", adt_def);
192+
debug!("drop_tys_helper: `{:?}` is manually drop", adt_def);
198193
return Ok(Vec::new().into_iter());
199194
} else if let Some(dtor_info) = adt_has_dtor(adt_def) {
200195
match dtor_info {
201196
DtorType::Significant => {
202-
debug!("adt_drop_tys: `{:?}` implements `Drop`", adt_def);
197+
debug!("drop_tys_helper: `{:?}` implements `Drop`", adt_def);
203198
return Err(AlwaysRequiresDrop);
204199
}
205200
DtorType::Insignificant => {
206-
debug!("adt_drop_tys: `{:?}` drop is insignificant", adt_def);
201+
debug!("drop_tys_helper: `{:?}` drop is insignificant", adt_def);
207202

208203
// Since the destructor is insignificant, we just want to make sure all of
209204
// the passed in type parameters are also insignificant.
@@ -212,34 +207,27 @@ fn adt_drop_tys_helper<'tcx>(
212207
}
213208
}
214209
} else if adt_def.is_union() {
215-
debug!("adt_drop_tys: `{:?}` is a union", adt_def);
210+
debug!("drop_tys_helper: `{:?}` is a union", adt_def);
216211
return Ok(Vec::new().into_iter());
217212
}
218-
Ok(adt_def.all_fields().map(|field| tcx.type_of(field.did)).collect::<Vec<_>>().into_iter())
213+
Ok(adt_def
214+
.all_fields()
215+
.map(|field| {
216+
let r = tcx.type_of(field.did).subst(tcx, substs);
217+
debug!("drop_tys_helper: Subst into {:?} with {:?} gettng {:?}", field, substs, r);
218+
r
219+
})
220+
.collect::<Vec<_>>()
221+
.into_iter())
219222
};
220223

221-
let adt_ty = tcx.type_of(def_id);
222-
let param_env = tcx.param_env(def_id);
223-
let res: Result<Vec<_>, _> =
224-
NeedsDropTypes::new(tcx, param_env, adt_ty, adt_components).collect();
225-
226-
debug!("adt_drop_tys(`{}`) = `{:?}`", tcx.def_path_str(def_id), res);
227-
res.map(|components| tcx.intern_type_list(&components))
224+
NeedsDropTypes::new(tcx, param_env, ty, adt_components)
228225
}
229226

230-
fn adt_drop_tys(tcx: TyCtxt<'_>, def_id: DefId) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
231-
// This is for the "needs_drop" query, that considers all `Drop` impls, therefore all dtors are
232-
// significant.
233-
let adt_has_dtor =
234-
|adt_def: &ty::AdtDef| adt_def.destructor(tcx).map(|_| DtorType::Significant);
235-
adt_drop_tys_helper(tcx, def_id, adt_has_dtor)
236-
}
237-
238-
fn adt_significant_drop_tys(
239-
tcx: TyCtxt<'_>,
240-
def_id: DefId,
241-
) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
242-
let adt_has_dtor = |adt_def: &ty::AdtDef| {
227+
fn adt_consider_insignificant_dtor<'tcx>(
228+
tcx: TyCtxt<'tcx>,
229+
) -> impl Fn(&ty::AdtDef) -> Option<DtorType> + 'tcx {
230+
move |adt_def: &ty::AdtDef| {
243231
let is_marked_insig = tcx.has_attr(adt_def.did, sym::rustc_insignificant_dtor);
244232
if is_marked_insig {
245233
// In some cases like `std::collections::HashMap` where the struct is a wrapper around
@@ -256,8 +244,31 @@ fn adt_significant_drop_tys(
256244
// treat this as the simple case of Drop impl for type.
257245
None
258246
}
259-
};
260-
adt_drop_tys_helper(tcx, def_id, adt_has_dtor)
247+
}
248+
}
249+
250+
fn adt_drop_tys(tcx: TyCtxt<'_>, def_id: DefId) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
251+
// This is for the "adt_drop_tys" query, that considers all `Drop` impls, therefore all dtors are
252+
// significant.
253+
let adt_has_dtor =
254+
|adt_def: &ty::AdtDef| adt_def.destructor(tcx).map(|_| DtorType::Significant);
255+
drop_tys_helper(tcx, tcx.type_of(def_id), tcx.param_env(def_id), adt_has_dtor)
256+
.collect::<Result<Vec<_>, _>>()
257+
.map(|components| tcx.intern_type_list(&components))
258+
}
259+
260+
fn adt_significant_drop_tys(
261+
tcx: TyCtxt<'_>,
262+
def_id: DefId,
263+
) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
264+
drop_tys_helper(
265+
tcx,
266+
tcx.type_of(def_id),
267+
tcx.param_env(def_id),
268+
adt_consider_insignificant_dtor(tcx),
269+
)
270+
.collect::<Result<Vec<_>, _>>()
271+
.map(|components| tcx.intern_type_list(&components))
261272
}
262273

263274
pub(crate) fn provide(providers: &mut ty::query::Providers) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Test that rustc doesn't ICE as in #90024.
2+
// check-pass
3+
// edition=2018
4+
5+
#![warn(rust_2021_incompatible_closure_captures)]
6+
7+
// Checks there's no double-subst into the generic args, otherwise we get OOB
8+
// MCVE by @lqd
9+
pub struct Graph<N, E, Ix> {
10+
_edges: E,
11+
_nodes: N,
12+
_ix: Vec<Ix>,
13+
}
14+
fn graph<N, E>() -> Graph<N, E, i32> {
15+
todo!()
16+
}
17+
fn first_ice() {
18+
let g = graph::<i32, i32>();
19+
let _ = || g;
20+
}
21+
22+
// Checks that there is a subst into the fields, otherwise we get normalization error
23+
// MCVE by @cuviper
24+
use std::iter::Empty;
25+
struct Foo<I: Iterator> {
26+
data: Vec<I::Item>,
27+
}
28+
pub fn second_ice() {
29+
let v = Foo::<Empty<()>> { data: vec![] };
30+
31+
(|| v.data[0])();
32+
}
33+
34+
pub fn main() {
35+
first_ice();
36+
second_ice();
37+
}

0 commit comments

Comments
 (0)
Please sign in to comment.