Skip to content

Commit 373fcd1

Browse files
authored
Rollup merge of rust-lang#37117 - pnkfelix:may-dangle-attr, r=nikomatsakis
`#[may_dangle]` attribute `#[may_dangle]` attribute Second step of rust-lang#34761. Last big hurdle before we can work in earnest towards Allocator integration (rust-lang#32838) Note: I am not clear if this is *also* a syntax-breaking change that needs to be part of a breaking-batch.
2 parents 6e3a72d + 10a58ac commit 373fcd1

27 files changed

+1098
-36
lines changed

src/librustc/hir/lowering.rs

+2
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ impl<'a> LoweringContext<'a> {
401401
bounds: self.lower_bounds(&tp.bounds),
402402
default: tp.default.as_ref().map(|x| self.lower_ty(x)),
403403
span: tp.span,
404+
pure_wrt_drop: tp.attrs.iter().any(|attr| attr.check_name("may_dangle")),
404405
}
405406
}
406407

@@ -420,6 +421,7 @@ impl<'a> LoweringContext<'a> {
420421
hir::LifetimeDef {
421422
lifetime: self.lower_lifetime(&l.lifetime),
422423
bounds: self.lower_lifetimes(&l.bounds),
424+
pure_wrt_drop: l.attrs.iter().any(|attr| attr.check_name("may_dangle")),
423425
}
424426
}
425427

src/librustc/hir/mod.rs

+32
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ impl fmt::Debug for Lifetime {
9595
pub struct LifetimeDef {
9696
pub lifetime: Lifetime,
9797
pub bounds: HirVec<Lifetime>,
98+
pub pure_wrt_drop: bool,
9899
}
99100

100101
/// A "Path" is essentially Rust's notion of a name; for instance:
@@ -290,6 +291,7 @@ pub struct TyParam {
290291
pub bounds: TyParamBounds,
291292
pub default: Option<P<Ty>>,
292293
pub span: Span,
294+
pub pure_wrt_drop: bool,
293295
}
294296

295297
/// Represents lifetimes and type parameters attached to a declaration
@@ -328,6 +330,36 @@ impl Generics {
328330
}
329331
}
330332

333+
pub enum UnsafeGeneric {
334+
Region(LifetimeDef, &'static str),
335+
Type(TyParam, &'static str),
336+
}
337+
338+
impl UnsafeGeneric {
339+
pub fn attr_name(&self) -> &'static str {
340+
match *self {
341+
UnsafeGeneric::Region(_, s) => s,
342+
UnsafeGeneric::Type(_, s) => s,
343+
}
344+
}
345+
}
346+
347+
impl Generics {
348+
pub fn carries_unsafe_attr(&self) -> Option<UnsafeGeneric> {
349+
for r in &self.lifetimes {
350+
if r.pure_wrt_drop {
351+
return Some(UnsafeGeneric::Region(r.clone(), "may_dangle"));
352+
}
353+
}
354+
for t in &self.ty_params {
355+
if t.pure_wrt_drop {
356+
return Some(UnsafeGeneric::Type(t.clone(), "may_dangle"));
357+
}
358+
}
359+
return None;
360+
}
361+
}
362+
331363
/// A `where` clause in a definition
332364
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
333365
pub struct WhereClause {

src/librustc/infer/error_reporting.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -1231,16 +1231,17 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> {
12311231
lifetime: hir::Lifetime,
12321232
region_names: &HashSet<ast::Name>)
12331233
-> hir::HirVec<hir::TyParam> {
1234-
ty_params.iter().map(|ty_param| {
1235-
let bounds = self.rebuild_ty_param_bounds(ty_param.bounds.clone(),
1234+
ty_params.into_iter().map(|ty_param| {
1235+
let bounds = self.rebuild_ty_param_bounds(ty_param.bounds,
12361236
lifetime,
12371237
region_names);
12381238
hir::TyParam {
12391239
name: ty_param.name,
12401240
id: ty_param.id,
12411241
bounds: bounds,
1242-
default: ty_param.default.clone(),
1242+
default: ty_param.default,
12431243
span: ty_param.span,
1244+
pure_wrt_drop: ty_param.pure_wrt_drop,
12441245
}
12451246
}).collect()
12461247
}
@@ -1299,8 +1300,11 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> {
12991300
-> hir::Generics {
13001301
let mut lifetimes = Vec::new();
13011302
for lt in add {
1302-
lifetimes.push(hir::LifetimeDef { lifetime: *lt,
1303-
bounds: hir::HirVec::new() });
1303+
lifetimes.push(hir::LifetimeDef {
1304+
lifetime: *lt,
1305+
bounds: hir::HirVec::new(),
1306+
pure_wrt_drop: false,
1307+
});
13041308
}
13051309
for lt in &generics.lifetimes {
13061310
if keep.contains(&lt.lifetime.name) ||

src/librustc/ty/mod.rs

+18
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,11 @@ pub struct TypeParameterDef<'tcx> {
680680
pub default_def_id: DefId, // for use in error reporing about defaults
681681
pub default: Option<Ty<'tcx>>,
682682
pub object_lifetime_default: ObjectLifetimeDefault<'tcx>,
683+
684+
/// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute
685+
/// on generic parameter `T`, asserts data behind the parameter
686+
/// `T` won't be accessed during the parent type's `Drop` impl.
687+
pub pure_wrt_drop: bool,
683688
}
684689

685690
#[derive(Clone, RustcEncodable, RustcDecodable)]
@@ -688,6 +693,11 @@ pub struct RegionParameterDef<'tcx> {
688693
pub def_id: DefId,
689694
pub index: u32,
690695
pub bounds: Vec<&'tcx ty::Region>,
696+
697+
/// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute
698+
/// on generic parameter `'a`, asserts data of lifetime `'a`
699+
/// won't be accessed during the parent type's `Drop` impl.
700+
pub pure_wrt_drop: bool,
691701
}
692702

693703
impl<'tcx> RegionParameterDef<'tcx> {
@@ -732,6 +742,14 @@ impl<'tcx> Generics<'tcx> {
732742
pub fn count(&self) -> usize {
733743
self.parent_count() + self.own_count()
734744
}
745+
746+
pub fn region_param(&self, param: &EarlyBoundRegion) -> &RegionParameterDef<'tcx> {
747+
&self.regions[param.index as usize - self.has_self as usize]
748+
}
749+
750+
pub fn type_param(&self, param: &ParamTy) -> &TypeParameterDef<'tcx> {
751+
&self.types[param.idx as usize - self.has_self as usize - self.regions.len()]
752+
}
735753
}
736754

737755
/// Bounds on generics.

src/librustc/ty/structural_impls.rs

+2
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TypeParameterDef<'tcx> {
716716
default: self.default.fold_with(folder),
717717
default_def_id: self.default_def_id,
718718
object_lifetime_default: self.object_lifetime_default.fold_with(folder),
719+
pure_wrt_drop: self.pure_wrt_drop,
719720
}
720721
}
721722

@@ -754,6 +755,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::RegionParameterDef<'tcx> {
754755
def_id: self.def_id,
755756
index: self.index,
756757
bounds: self.bounds.fold_with(folder),
758+
pure_wrt_drop: self.pure_wrt_drop,
757759
}
758760
}
759761

src/librustc_typeck/check/dropck.rs

+157-18
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
7171
ccx: &CrateCtxt<'a, 'tcx>,
7272
drop_impl_did: DefId,
7373
drop_impl_ty: Ty<'tcx>,
74-
self_type_did: DefId) -> Result<(), ()>
74+
self_type_did: DefId)
75+
-> Result<(), ()>
7576
{
7677
let tcx = ccx.tcx;
7778
let drop_impl_node_id = tcx.map.as_local_node_id(drop_impl_did).unwrap();
@@ -123,7 +124,9 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'a, 'tcx>(
123124
drop_impl_did: DefId,
124125
dtor_predicates: &ty::GenericPredicates<'tcx>,
125126
self_type_did: DefId,
126-
self_to_impl_substs: &Substs<'tcx>) -> Result<(), ()> {
127+
self_to_impl_substs: &Substs<'tcx>)
128+
-> Result<(), ()>
129+
{
127130

128131
// Here is an example, analogous to that from
129132
// `compare_impl_method`.
@@ -350,7 +353,8 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'gcx, 'tcx>(
350353
cx: &mut DropckContext<'a, 'b, 'gcx, 'tcx>,
351354
context: TypeContext,
352355
ty: Ty<'tcx>,
353-
depth: usize) -> Result<(), Error<'tcx>>
356+
depth: usize)
357+
-> Result<(), Error<'tcx>>
354358
{
355359
let tcx = cx.rcx.tcx;
356360
// Issue #22443: Watch out for overflow. While we are careful to
@@ -402,16 +406,27 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'gcx, 'tcx>(
402406
// unbounded type parameter `T`, we must resume the recursive
403407
// analysis on `T` (since it would be ignored by
404408
// type_must_outlive).
405-
if has_dtor_of_interest(tcx, ty) {
406-
debug!("iterate_over_potentially_unsafe_regions_in_type \
407-
{}ty: {} - is a dtorck type!",
408-
(0..depth).map(|_| ' ').collect::<String>(),
409-
ty);
410-
411-
cx.rcx.type_must_outlive(infer::SubregionOrigin::SafeDestructor(cx.span),
412-
ty, tcx.mk_region(ty::ReScope(cx.parent_scope)));
413-
414-
return Ok(());
409+
let dropck_kind = has_dtor_of_interest(tcx, ty);
410+
debug!("iterate_over_potentially_unsafe_regions_in_type \
411+
ty: {:?} dropck_kind: {:?}", ty, dropck_kind);
412+
match dropck_kind {
413+
DropckKind::NoBorrowedDataAccessedInMyDtor => {
414+
// The maximally blind attribute.
415+
}
416+
DropckKind::BorrowedDataMustStrictlyOutliveSelf => {
417+
cx.rcx.type_must_outlive(infer::SubregionOrigin::SafeDestructor(cx.span),
418+
ty, tcx.mk_region(ty::ReScope(cx.parent_scope)));
419+
return Ok(());
420+
}
421+
DropckKind::RevisedSelf(revised_ty) => {
422+
cx.rcx.type_must_outlive(infer::SubregionOrigin::SafeDestructor(cx.span),
423+
revised_ty, tcx.mk_region(ty::ReScope(cx.parent_scope)));
424+
// Do not return early from this case; we want
425+
// to recursively process the internal structure of Self
426+
// (because even though the Drop for Self has been asserted
427+
// safe, the types instantiated for the generics of Self
428+
// may themselves carry dropck constraints.)
429+
}
415430
}
416431

417432
debug!("iterate_over_potentially_unsafe_regions_in_type \
@@ -492,16 +507,140 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'gcx, 'tcx>(
492507
}
493508
}
494509

510+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
511+
enum DropckKind<'tcx> {
512+
/// The "safe" kind; i.e. conservatively assume any borrow
513+
/// accessed by dtor, and therefore such data must strictly
514+
/// outlive self.
515+
///
516+
/// Equivalent to RevisedTy with no change to the self type.
517+
BorrowedDataMustStrictlyOutliveSelf,
518+
519+
/// The nearly completely-unsafe kind.
520+
///
521+
/// Equivalent to RevisedSelf with *all* parameters remapped to ()
522+
/// (maybe...?)
523+
NoBorrowedDataAccessedInMyDtor,
524+
525+
/// Assume all borrowed data access by dtor occurs as if Self has the
526+
/// type carried by this variant. In practice this means that some
527+
/// of the type parameters are remapped to `()` (and some lifetime
528+
/// parameters remapped to `'static`), because the developer has asserted
529+
/// that the destructor will not access their contents.
530+
RevisedSelf(Ty<'tcx>),
531+
}
532+
533+
/// Returns the classification of what kind of check should be applied
534+
/// to `ty`, which may include a revised type where some of the type
535+
/// parameters are re-mapped to `()` to reflect the destructor's
536+
/// "purity" with respect to their actual contents.
495537
fn has_dtor_of_interest<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
496-
ty: Ty<'tcx>) -> bool {
538+
ty: Ty<'tcx>)
539+
-> DropckKind<'tcx> {
497540
match ty.sty {
498-
ty::TyAdt(def, _) => {
499-
def.is_dtorck(tcx)
541+
ty::TyAdt(adt_def, substs) => {
542+
if !adt_def.is_dtorck(tcx) {
543+
return DropckKind::NoBorrowedDataAccessedInMyDtor;
544+
}
545+
546+
// Find the `impl<..> Drop for _` to inspect any
547+
// attributes attached to the impl's generics.
548+
let dtor_method = adt_def.destructor()
549+
.expect("dtorck type without destructor impossible");
550+
let method = tcx.impl_or_trait_item(dtor_method);
551+
let impl_id: DefId = method.container().id();
552+
let revised_ty = revise_self_ty(tcx, adt_def, impl_id, substs);
553+
return DropckKind::RevisedSelf(revised_ty);
500554
}
501555
ty::TyTrait(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
502556
debug!("ty: {:?} isn't known, and therefore is a dropck type", ty);
503-
true
557+
return DropckKind::BorrowedDataMustStrictlyOutliveSelf;
504558
},
505-
_ => false
559+
_ => {
560+
return DropckKind::NoBorrowedDataAccessedInMyDtor;
561+
}
506562
}
507563
}
564+
565+
// Constructs new Ty just like the type defined by `adt_def` coupled
566+
// with `substs`, except each type and lifetime parameter marked as
567+
// `#[may_dangle]` in the Drop impl (identified by `impl_id`) is
568+
// respectively mapped to `()` or `'static`.
569+
//
570+
// For example: If the `adt_def` maps to:
571+
//
572+
// enum Foo<'a, X, Y> { ... }
573+
//
574+
// and the `impl_id` maps to:
575+
//
576+
// impl<#[may_dangle] 'a, X, #[may_dangle] Y> Drop for Foo<'a, X, Y> { ... }
577+
//
578+
// then revises input: `Foo<'r,i64,&'r i64>` to: `Foo<'static,i64,()>`
579+
fn revise_self_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
580+
adt_def: ty::AdtDef<'tcx>,
581+
impl_id: DefId,
582+
substs: &Substs<'tcx>)
583+
-> Ty<'tcx> {
584+
// Get generics for `impl Drop` to query for `#[may_dangle]` attr.
585+
let impl_bindings = tcx.lookup_generics(impl_id);
586+
587+
// Get Substs attached to Self on `impl Drop`; process in parallel
588+
// with `substs`, replacing dangling entries as appropriate.
589+
let self_substs = {
590+
let impl_self_ty: Ty<'tcx> = tcx.lookup_item_type(impl_id).ty;
591+
if let ty::TyAdt(self_adt_def, self_substs) = impl_self_ty.sty {
592+
assert_eq!(adt_def, self_adt_def);
593+
self_substs
594+
} else {
595+
bug!("Self in `impl Drop for _` must be an Adt.");
596+
}
597+
};
598+
599+
// Walk `substs` + `self_substs`, build new substs appropriate for
600+
// `adt_def`; each non-dangling param reuses entry from `substs`.
601+
//
602+
// Note: The manner we map from a right-hand side (i.e. Region or
603+
// Ty) for a given `def` to generic parameter associated with that
604+
// right-hand side is tightly coupled to `Drop` impl constraints.
605+
//
606+
// E.g. we know such a Ty must be `TyParam`, because a destructor
607+
// for `struct Foo<X>` is defined via `impl<Y> Drop for Foo<Y>`,
608+
// and never by (for example) `impl<Z> Drop for Foo<Vec<Z>>`.
609+
let substs = Substs::for_item(
610+
tcx,
611+
adt_def.did,
612+
|def, _| {
613+
let r_orig = substs.region_for_def(def);
614+
let impl_self_orig = self_substs.region_for_def(def);
615+
let r = if let ty::Region::ReEarlyBound(ref ebr) = *impl_self_orig {
616+
if impl_bindings.region_param(ebr).pure_wrt_drop {
617+
tcx.mk_region(ty::ReStatic)
618+
} else {
619+
r_orig
620+
}
621+
} else {
622+
bug!("substs for an impl must map regions to ReEarlyBound");
623+
};
624+
debug!("has_dtor_of_interest mapping def {:?} orig {:?} to {:?}",
625+
def, r_orig, r);
626+
r
627+
},
628+
|def, _| {
629+
let t_orig = substs.type_for_def(def);
630+
let impl_self_orig = self_substs.type_for_def(def);
631+
let t = if let ty::TypeVariants::TyParam(ref pt) = impl_self_orig.sty {
632+
if impl_bindings.type_param(pt).pure_wrt_drop {
633+
tcx.mk_nil()
634+
} else {
635+
t_orig
636+
}
637+
} else {
638+
bug!("substs for an impl must map types to TyParam");
639+
};
640+
debug!("has_dtor_of_interest mapping def {:?} orig {:?} {:?} to {:?} {:?}",
641+
def, t_orig, t_orig.sty, t, t.sty);
642+
t
643+
});
644+
645+
return tcx.mk_adt(adt_def, &substs);
646+
}

0 commit comments

Comments
 (0)