Skip to content

Commit a7e1cec

Browse files
committed
add new attribute rustc_insignificant_dtor and a query to check if a type has a significant drop
1 parent 1025db8 commit a7e1cec

File tree

11 files changed

+332
-12
lines changed

11 files changed

+332
-12
lines changed

compiler/rustc_feature/src/active.rs

+1
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
700700
sym::native_link_modifiers_verbatim,
701701
sym::native_link_modifiers_whole_archive,
702702
sym::native_link_modifiers_as_needed,
703+
sym::rustc_insignificant_dtor,
703704
];
704705

705706
/// Some features are not allowed to be used together at the same time, if

compiler/rustc_feature/src/builtin_attrs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
556556

557557
rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)),
558558
rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)),
559+
rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word)),
559560
rustc_attr!(TEST, rustc_variance, Normal, template!(Word)),
560561
rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")),
561562
rustc_attr!(TEST, rustc_regions, Normal, template!(Word)),

compiler/rustc_middle/src/query/mod.rs

+15
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,10 @@ rustc_queries! {
10351035
query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
10361036
desc { "computing whether `{}` needs drop", env.value }
10371037
}
1038+
/// Query backing `TyS::has_significant_drop_raw`.
1039+
query has_significant_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
1040+
desc { "computing whether `{}` has a significant drop", env.value }
1041+
}
10381042

10391043
/// Query backing `TyS::is_structural_eq_shallow`.
10401044
///
@@ -1055,6 +1059,17 @@ rustc_queries! {
10551059
cache_on_disk_if { true }
10561060
}
10571061

1062+
/// A list of types where the ADT requires drop if and only if any of those types
1063+
/// has significant drop. A type marked with the attribute `rustc_insignificant_dtor`
1064+
/// is considered to not be significant. A drop is significant if it is implemented
1065+
/// by the user or does anything that will have any observable behavior (other than
1066+
/// freeing up memory). If the ADT is known to have a significant destructor then
1067+
/// `Err(AlwaysRequiresDrop)` is returned.
1068+
query adt_significant_drop_tys(def_id: DefId) -> Result<&'tcx ty::List<Ty<'tcx>>, AlwaysRequiresDrop> {
1069+
desc { |tcx| "computing when `{}` has a significant destructor", tcx.def_path_str(def_id) }
1070+
cache_on_disk_if { false }
1071+
}
1072+
10581073
query layout_raw(
10591074
env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
10601075
) -> Result<&'tcx rustc_target::abi::Layout, ty::layout::LayoutError<'tcx>> {

compiler/rustc_middle/src/ty/util.rs

+33
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,39 @@ impl<'tcx> ty::TyS<'tcx> {
791791
}
792792
}
793793

794+
/// Checks if `ty` has has a significant drop.
795+
///
796+
/// Note that this method can return false even if `ty` has a destructor
797+
/// attached; even if that is the case then the adt has been marked with
798+
/// the attribute `rustc_insignificant_dtor`.
799+
///
800+
/// Note that this method is used to check for change in drop order for
801+
/// 2229 drop reorder migration analysis.
802+
#[inline]
803+
pub fn has_significant_drop(
804+
&'tcx self,
805+
tcx: TyCtxt<'tcx>,
806+
param_env: ty::ParamEnv<'tcx>,
807+
) -> bool {
808+
// Avoid querying in simple cases.
809+
match needs_drop_components(self, &tcx.data_layout) {
810+
Err(AlwaysRequiresDrop) => true,
811+
Ok(components) => {
812+
let query_ty = match *components {
813+
[] => return false,
814+
// If we've got a single component, call the query with that
815+
// to increase the chance that we hit the query cache.
816+
[component_ty] => component_ty,
817+
_ => self,
818+
};
819+
// This doesn't depend on regions, so try to minimize distinct
820+
// query keys used.
821+
let erased = tcx.normalize_erasing_regions(param_env, query_ty);
822+
tcx.has_significant_drop_raw(param_env.and(erased))
823+
}
824+
}
825+
}
826+
794827
/// Returns `true` if equality for this type is both reflexive and structural.
795828
///
796829
/// Reflexive equality for a type is indicated by an `Eq` impl for that type.

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,7 @@ symbols! {
10151015
rustc_expected_cgu_reuse,
10161016
rustc_if_this_changed,
10171017
rustc_inherit_overflow_checks,
1018+
rustc_insignificant_dtor,
10181019
rustc_layout,
10191020
rustc_layout_scalar_valid_range_end,
10201021
rustc_layout_scalar_valid_range_start,

compiler/rustc_ty_utils/src/needs_drop.rs

+49-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rustc_middle::ty::subst::Subst;
66
use rustc_middle::ty::util::{needs_drop_components, AlwaysRequiresDrop};
77
use rustc_middle::ty::{self, Ty, TyCtxt};
88
use rustc_session::Limit;
9-
use rustc_span::DUMMY_SP;
9+
use rustc_span::{sym, DUMMY_SP};
1010

1111
type NeedsDropResult<T> = Result<T, AlwaysRequiresDrop>;
1212

@@ -21,6 +21,19 @@ fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>
2121
res
2222
}
2323

24+
fn has_significant_drop_raw<'tcx>(
25+
tcx: TyCtxt<'tcx>,
26+
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
27+
) -> bool {
28+
let significant_drop_fields =
29+
move |adt_def: &ty::AdtDef| tcx.adt_significant_drop_tys(adt_def.did).map(|tys| tys.iter());
30+
let res = NeedsDropTypes::new(tcx, query.param_env, query.value, significant_drop_fields)
31+
.next()
32+
.is_some();
33+
debug!("has_significant_drop_raw({:?}) = {:?}", query, res);
34+
res
35+
}
36+
2437
struct NeedsDropTypes<'tcx, F> {
2538
tcx: TyCtxt<'tcx>,
2639
param_env: ty::ParamEnv<'tcx>,
@@ -155,12 +168,20 @@ where
155168
}
156169
}
157170

158-
fn adt_drop_tys(tcx: TyCtxt<'_>, def_id: DefId) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
171+
// This is a helper function for `adt_drop_tys` and `adt_significant_drop_tys`.
172+
// Depending on the implentation of `adt_has_dtor`, it is used to check if the
173+
// ADT has a destructor or if the ADT only has a significant destructor. For
174+
// understanding significant destructor look at `adt_significant_drop_tys`.
175+
fn adt_drop_tys_helper(
176+
tcx: TyCtxt<'_>,
177+
def_id: DefId,
178+
adt_has_dtor: impl Fn(&ty::AdtDef) -> bool,
179+
) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
159180
let adt_components = move |adt_def: &ty::AdtDef| {
160181
if adt_def.is_manually_drop() {
161182
debug!("adt_drop_tys: `{:?}` is manually drop", adt_def);
162183
return Ok(Vec::new().into_iter());
163-
} else if adt_def.destructor(tcx).is_some() {
184+
} else if adt_has_dtor(adt_def) {
164185
debug!("adt_drop_tys: `{:?}` implements `Drop`", adt_def);
165186
return Err(AlwaysRequiresDrop);
166187
} else if adt_def.is_union() {
@@ -179,6 +200,30 @@ fn adt_drop_tys(tcx: TyCtxt<'_>, def_id: DefId) -> Result<&ty::List<Ty<'_>>, Alw
179200
res.map(|components| tcx.intern_type_list(&components))
180201
}
181202

203+
fn adt_drop_tys(tcx: TyCtxt<'_>, def_id: DefId) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
204+
let adt_has_dtor = |adt_def: &ty::AdtDef| adt_def.destructor(tcx).is_some();
205+
adt_drop_tys_helper(tcx, def_id, adt_has_dtor)
206+
}
207+
208+
fn adt_significant_drop_tys(
209+
tcx: TyCtxt<'_>,
210+
def_id: DefId,
211+
) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
212+
let adt_has_dtor = |adt_def: &ty::AdtDef| {
213+
adt_def
214+
.destructor(tcx)
215+
.map(|dtor| !tcx.has_attr(dtor.did, sym::rustc_insignificant_dtor))
216+
.unwrap_or(false)
217+
};
218+
adt_drop_tys_helper(tcx, def_id, adt_has_dtor)
219+
}
220+
182221
pub(crate) fn provide(providers: &mut ty::query::Providers) {
183-
*providers = ty::query::Providers { needs_drop_raw, adt_drop_tys, ..*providers };
222+
*providers = ty::query::Providers {
223+
needs_drop_raw,
224+
has_significant_drop_raw,
225+
adt_drop_tys,
226+
adt_significant_drop_tys,
227+
..*providers
228+
};
184229
}

compiler/rustc_typeck/src/check/upvar.rs

+6-8
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
706706
/// enabled, **and**
707707
/// - It wasn't completely captured by the closure, **and**
708708
/// - One of the paths starting at this root variable, that is not captured needs Drop.
709+
///
710+
/// This function only returns true for significant drops. A type is considerent to have a
711+
/// significant drop if it's Drop implementation is not annotated by `rustc_insignificant_dtor`.
709712
fn compute_2229_migrations_for_drop(
710713
&self,
711714
closure_def_id: DefId,
@@ -716,7 +719,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
716719
) -> bool {
717720
let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id));
718721

719-
if !ty.needs_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) {
722+
if !ty.has_significant_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) {
720723
return false;
721724
}
722725

@@ -835,11 +838,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
835838
/// using list of `Projection` slices), it returns true if there is a path that is not
836839
/// captured starting at this root variable that implements Drop.
837840
///
838-
/// FIXME(project-rfc-2229#35): This should return true only for significant drops.
839-
/// A drop is significant if it's implemented by the user or does
840-
/// anything that will have any observable behavior (other than
841-
/// freeing up memory).
842-
///
843841
/// The way this function works is at a given call it looks at type `base_path_ty` of some base
844842
/// path say P and then list of projection slices which represent the different captures moved
845843
/// into the closure starting off of P.
@@ -895,7 +893,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
895893
/// (Ty((w.p).x), [ &[] ]) (Ty((w.p).y), []) // IMP 2
896894
/// | |
897895
/// v v
898-
/// false NeedsDrop(Ty(w.p.y))
896+
/// false NeedsSignificantDrop(Ty(w.p.y))
899897
/// |
900898
/// v
901899
/// true
@@ -939,7 +937,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
939937
captured_by_move_projs: Vec<&[Projection<'tcx>]>,
940938
) -> bool {
941939
let needs_drop = |ty: Ty<'tcx>| {
942-
ty.needs_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local()))
940+
ty.has_significant_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local()))
943941
};
944942

945943
let is_drop_defined_for_ty = |ty: Ty<'tcx>| {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// run-rustfix
2+
3+
#![deny(disjoint_capture_migration)]
4+
//~^ NOTE: the lint level is defined here
5+
6+
#![feature(rustc_attrs)]
7+
#![allow(unused)]
8+
9+
struct InsignificantDropPoint {
10+
x: i32,
11+
y: i32,
12+
}
13+
14+
impl Drop for InsignificantDropPoint {
15+
#[rustc_insignificant_dtor]
16+
fn drop(&mut self) {}
17+
}
18+
19+
struct SigDrop;
20+
21+
impl Drop for SigDrop {
22+
fn drop(&mut self) {}
23+
}
24+
25+
struct GenericStruct<T>(T, T);
26+
27+
struct Wrapper<T>(GenericStruct<T>, i32);
28+
29+
impl<T> Drop for GenericStruct<T> {
30+
#[rustc_insignificant_dtor]
31+
fn drop(&mut self) {}
32+
}
33+
34+
// `SigDrop` implements drop and therefore needs to be migrated.
35+
fn significant_drop_needs_migration() {
36+
let t = (SigDrop {}, SigDrop {});
37+
38+
let c = || { let _ = &t;
39+
//~^ ERROR: drop order affected for closure because of `capture_disjoint_fields`
40+
//~| HELP: add a dummy let to cause `t` to be fully captured
41+
let _t = t.0;
42+
};
43+
44+
c();
45+
}
46+
47+
// Even if a type implements an insignificant drop, if it's
48+
// elements have a significant drop then the overall type is
49+
// consdered to have an significant drop. Since the elements
50+
// of `GenericStruct` implement drop, migration is required.
51+
fn generic_struct_with_significant_drop_needs_migration() {
52+
let t = Wrapper(GenericStruct(SigDrop {}, SigDrop {}), 5);
53+
54+
// move is used to force i32 to be copied instead of being a ref
55+
let c = move || { let _ = &t;
56+
//~^ ERROR: drop order affected for closure because of `capture_disjoint_fields`
57+
//~| HELP: add a dummy let to cause `t` to be fully captured
58+
let _t = t.1;
59+
};
60+
61+
c();
62+
}
63+
64+
fn main() {
65+
significant_drop_needs_migration();
66+
generic_struct_with_significant_drop_needs_migration();
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// run-rustfix
2+
3+
#![deny(disjoint_capture_migration)]
4+
//~^ NOTE: the lint level is defined here
5+
6+
#![feature(rustc_attrs)]
7+
#![allow(unused)]
8+
9+
struct InsignificantDropPoint {
10+
x: i32,
11+
y: i32,
12+
}
13+
14+
impl Drop for InsignificantDropPoint {
15+
#[rustc_insignificant_dtor]
16+
fn drop(&mut self) {}
17+
}
18+
19+
struct SigDrop;
20+
21+
impl Drop for SigDrop {
22+
fn drop(&mut self) {}
23+
}
24+
25+
struct GenericStruct<T>(T, T);
26+
27+
struct Wrapper<T>(GenericStruct<T>, i32);
28+
29+
impl<T> Drop for GenericStruct<T> {
30+
#[rustc_insignificant_dtor]
31+
fn drop(&mut self) {}
32+
}
33+
34+
// `SigDrop` implements drop and therefore needs to be migrated.
35+
fn significant_drop_needs_migration() {
36+
let t = (SigDrop {}, SigDrop {});
37+
38+
let c = || {
39+
//~^ ERROR: drop order affected for closure because of `capture_disjoint_fields`
40+
//~| HELP: add a dummy let to cause `t` to be fully captured
41+
let _t = t.0;
42+
};
43+
44+
c();
45+
}
46+
47+
// Even if a type implements an insignificant drop, if it's
48+
// elements have a significant drop then the overall type is
49+
// consdered to have an significant drop. Since the elements
50+
// of `GenericStruct` implement drop, migration is required.
51+
fn generic_struct_with_significant_drop_needs_migration() {
52+
let t = Wrapper(GenericStruct(SigDrop {}, SigDrop {}), 5);
53+
54+
// move is used to force i32 to be copied instead of being a ref
55+
let c = move || {
56+
//~^ ERROR: drop order affected for closure because of `capture_disjoint_fields`
57+
//~| HELP: add a dummy let to cause `t` to be fully captured
58+
let _t = t.1;
59+
};
60+
61+
c();
62+
}
63+
64+
fn main() {
65+
significant_drop_needs_migration();
66+
generic_struct_with_significant_drop_needs_migration();
67+
}

0 commit comments

Comments
 (0)