Non-parametric dropck; instead trust an unsafe attribute (RFC 1238).

Implement cannot-assume-parametricity (CAP) from RFC 1238, and add the
UGEH attribute.

----

Note that we check for the attribute attached to the dtor method, not
the Drop impl.

(This is just to match the specification of RFC and the tests; I am
not wedded to this approach.)
This commit is contained in:
Felix S. Klock II 2015-07-16 14:56:03 +02:00
parent c298efdb1f
commit 9868df2fd5
4 changed files with 87 additions and 25 deletions

View file

@ -2022,6 +2022,20 @@ macro scope.
- `simd` - on certain tuple structs, derive the arithmetic operators, which
lower to the target's SIMD instructions, if any; the `simd` feature gate
is necessary to use this attribute.
- `unsafe_destructor_blind_to_params` - on `Drop::drop` method, asserts that the
destructor code (and all potential specializations of that code) will
never attempt to read from nor write to any references with lifetimes
that come in via generic parameters. This is a constraint we cannot
currently express via the type system, and therefore we rely on the
programmer to assert that it holds. Adding this to a Drop impl causes
the associated destructor to be considered "uninteresting" by the
Drop-Check rule, and thus it can help sidestep data ordering
constraints that would otherwise be introduced by the Drop-Check
rule. Such sidestepping of the constraints, if done incorrectly, can
lead to undefined behavior (in the form of reading or writing to data
outside of its dynamic extent), and thus this attribute has the word
"unsafe" in its name. To use this, the
`unsafe_destructor_blind_to_params` feature gate must be enabled.
- `unsafe_no_drop_flag` - on structs, remove the flag that prevents
destructors from being run twice. Destructors might be run multiple times on
the same object with this attribute. To use this, the `unsafe_no_drop_flag` feature

View file

@ -578,6 +578,16 @@ impl<'tcx> ty::ctxt<'tcx> {
});
let generics = adt.type_scheme(self).generics;
// RFC 1238: if the destructor method is tagged with the
// attribute `unsafe_destructor_blind_to_params`, then the
// compiler is being instructed to *assume* that the
// destructor will not access borrowed data via a type
// parameter, even if such data is otherwise reachable.
if self.has_attr(dtor_method, "unsafe_destructor_blind_to_params") {
debug!("typ: {:?} assumed blind and thus is dtorck-safe", adt);
return false;
}
// In `impl<'a> Drop ...`, we automatically assume
// `'a` is meaningful and thus represents a bound
// through which we could reach borrowed data.
@ -592,6 +602,14 @@ impl<'tcx> ty::ctxt<'tcx> {
return true;
}
// RFC 1238: *any* type parameter at all makes this a dtor of
// interest (i.e. cannot-assume-parametricity from RFC 1238.)
if generics.has_type_params(subst::TypeSpace) {
debug!("typ: {:?} has interesting dtor due to type params",
adt);
return true;
}
let mut seen_items = Vec::new();
let mut items_to_inspect = vec![impl_did];
while let Some(item_def_id) = items_to_inspect.pop() {

View file

@ -217,19 +217,16 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
///
/// ----
///
/// The Drop Check Rule is the following:
/// The simplified (*) Drop Check Rule is the following:
///
/// Let `v` be some value (either temporary or named) and 'a be some
/// lifetime (scope). If the type of `v` owns data of type `D`, where
///
/// * (1.) `D` has a lifetime- or type-parametric Drop implementation, and
/// * (2.) the structure of `D` can reach a reference of type `&'a _`, and
/// * (3.) either:
/// * (A.) the Drop impl for `D` instantiates `D` at 'a directly,
/// i.e. `D<'a>`, or,
/// * (B.) the Drop impl for `D` has some type parameter with a
/// trait bound `T` where `T` is a trait that has at least
/// one method,
/// * (1.) `D` has a lifetime- or type-parametric Drop implementation,
/// (where that `Drop` implementation does not opt-out of
/// this check via the `unsafe_destructor_blind_to_params`
/// attribute), and
/// * (2.) the structure of `D` can reach a reference of type `&'a _`,
///
/// then 'a must strictly outlive the scope of v.
///
@ -237,6 +234,35 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
///
/// This function is meant to by applied to the type for every
/// expression in the program.
///
/// ----
///
/// (*) The qualifier "simplified" is attached to the above
/// definition of the Drop Check Rule, because it is a simplification
/// of the original Drop Check rule, which attempted to prove that
/// some `Drop` implementations could not possibly access data even if
/// it was technically reachable, due to parametricity.
///
/// However, (1.) parametricity on its own turned out to be a
/// necessary but insufficient condition, and (2.) future changes to
/// the language are expected to make it impossible to ensure that a
/// `Drop` implementation is actually parametric with respect to any
/// particular type parameter. (In particular, impl specialization is
/// expected to break the needed parametricity property beyond
/// repair.)
///
/// Therefore we have scaled back Drop-Check to a more conservative
/// rule that does not attempt to deduce whether a `Drop`
/// implementation could not possible access data of a given lifetime;
/// instead Drop-Check now simply assumes that if a destructor has
/// access (direct or indirect) to a lifetime parameter, then that
/// lifetime must be forced to outlive that destructor's dynamic
/// extent. We then provide the `unsafe_destructor_blind_to_params`
/// attribute as a way for destructor implementations to opt-out of
/// this conservative assumption (and thus assume the obligation of
/// ensuring that they do not access data nor invoke methods of
/// values that have been previously dropped).
///
pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
typ: ty::Ty<'tcx>,
span: Span,
@ -356,13 +382,18 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
// borrowed data reachable via `typ` must outlive the parent
// of `scope`. This is handled below.
//
// However, there is an important special case: by
// parametricity, any generic type parameters have *no* trait
// bounds in the Drop impl can not be used in any way (apart
// from being dropped), and thus we can treat data borrowed
// via such type parameters remains unreachable.
// However, there is an important special case: for any Drop
// impl that is tagged as "blind" to their parameters,
// we assume that data borrowed via such type parameters
// remains unreachable via that Drop impl.
//
// For example, consider:
//
// ```rust
// #[unsafe_destructor_blind_to_params]
// impl<T> Drop for Vec<T> { ... }
// ```
//
// For example, consider `impl<T> Drop for Vec<T> { ... }`,
// which does have to be able to drop instances of `T`, but
// otherwise cannot read data from `T`.
//
@ -370,16 +401,6 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
// unbounded type parameter `T`, we must resume the recursive
// analysis on `T` (since it would be ignored by
// type_must_outlive).
//
// FIXME (pnkfelix): Long term, we could be smart and actually
// feed which generic parameters can be ignored *into* `fn
// type_must_outlive` (or some generalization thereof). But
// for the short term, it probably covers most cases of
// interest to just special case Drop impls where: (1.) there
// are no generic lifetime parameters and (2.) *all* generic
// type parameters are unbounded. If both conditions hold, we
// simply skip the `type_must_outlive` call entirely (but
// resume the recursive checking of the type-substructure).
if has_dtor_of_interest(tcx, ty) {
debug!("iterate_over_potentially_unsafe_regions_in_type \
{}ty: {} - is a dtorck type!",

View file

@ -136,6 +136,10 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
// switch to Accepted; see RFC 320)
("unsafe_no_drop_flag", "1.0.0", None, Active),
// Allows using the unsafe_destructor_blind_to_params attribute
// (Needs an RFC link)
("unsafe_destructor_blind_to_params", "1.3.0", Some(28498), Active),
// Allows the use of custom attributes; RFC 572
("custom_attribute", "1.0.0", None, Active),
@ -339,6 +343,11 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
("unsafe_no_drop_flag", Whitelisted, Gated("unsafe_no_drop_flag",
"unsafe_no_drop_flag has unstable semantics \
and may be removed in the future")),
("unsafe_destructor_blind_to_params",
Normal,
Gated("unsafe_destructor_blind_to_params",
"unsafe_destructor_blind_to_params has unstable semantics \
and may be removed in the future")),
("unwind", Whitelisted, Gated("unwind_attributes", "#[unwind] is experimental")),
// used in resolve