Skip to content

Commit b5b5a17

Browse files
committed
dropck: must assume Box<Trait + 'a> has a destructor of interest.
Implements this (previously overlooked) note from [RFC 769]: > (Note: When encountering a D of the form `Box<Trait+'b>`, we > conservatively assume that such a type has a Drop implementation > parametric in 'b.) Fix rust-lang#25199. [breaking-change] The breakage here falls into both obvious and non-obvious cases. The obvious case: if you were relying on the unsoundness this exposes (namely being able to reference dead storage from a destructor, by doing it via a boxed trait object bounded by the lifetime of the dead storage), then this change disallows that. The non-obvious cases: The way dropck works, it causes lifetimes to be extended to longer extents than they covered before. I.e. lifetimes that are attached as trait-bounds may become longer than they were previously. * This includes lifetimes that are only *implicitly* attached as trait-bounds (due to [RFC 599]). So you may have code that was e.g. taking a parameter of type `&'a Box<Trait>` (which expands to `&'a Box<Trait+'a>`), that now may need to be assigned type `&'a Box<Trait+'static>` to ensure that `'a` is not inadvertantly inferred to a region that is actually too long. (See earlier commit in this PR for an example of this.) [RFC 769]: https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#the-drop-check-rule [RFC 599]: https://github.com/rust-lang/rfcs/blob/master/text/0599-default-object-bound.md
1 parent ee06263 commit b5b5a17

File tree

1 file changed

+153
-106
lines changed

1 file changed

+153
-106
lines changed

src/librustc_typeck/check/dropck.rs

+153-106
Original file line numberDiff line numberDiff line change
@@ -396,19 +396,24 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
396396
}
397397
};
398398

399-
let opt_type_did = match typ.sty {
400-
ty::ty_struct(struct_did, _) => Some(struct_did),
401-
ty::ty_enum(enum_did, _) => Some(enum_did),
402-
_ => None,
399+
let dtor_kind = match typ.sty {
400+
ty::ty_enum(def_id, _) |
401+
ty::ty_struct(def_id, _) => {
402+
match destructor_for_type.get(&def_id) {
403+
Some(def_id) => DtorKind::KnownDropMethod(*def_id),
404+
None => DtorKind::PureRecur,
405+
}
406+
}
407+
ty::ty_trait(ref ty_trait) => {
408+
DtorKind::Unknown(ty_trait.bounds.clone())
409+
}
410+
_ => DtorKind::PureRecur,
403411
};
404412

405-
let opt_dtor =
406-
opt_type_did.and_then(|did| destructor_for_type.get(&did));
407-
408413
debug!("iterate_over_potentially_unsafe_regions_in_type \
409-
{}typ: {} scope: {:?} opt_dtor: {:?} xref: {}",
414+
{}typ: {} scope: {:?} xref: {}",
410415
(0..depth).map(|_| ' ').collect::<String>(),
411-
typ.repr(rcx.tcx()), scope, opt_dtor, xref_depth);
416+
typ.repr(rcx.tcx()), scope, xref_depth);
412417

413418
// If `typ` has a destructor, then we must ensure that all
414419
// borrowed data reachable via `typ` must outlive the parent
@@ -439,102 +444,7 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
439444
// simply skip the `type_must_outlive` call entirely (but
440445
// resume the recursive checking of the type-substructure).
441446

442-
let has_dtor_of_interest;
443-
444-
if let Some(&dtor_method_did) = opt_dtor {
445-
let impl_did = ty::impl_of_method(rcx.tcx(), dtor_method_did)
446-
.unwrap_or_else(|| {
447-
rcx.tcx().sess.span_bug(
448-
span, "no Drop impl found for drop method")
449-
});
450-
451-
let dtor_typescheme = ty::lookup_item_type(rcx.tcx(), impl_did);
452-
let dtor_generics = dtor_typescheme.generics;
453-
454-
let mut has_pred_of_interest = false;
455-
456-
let mut seen_items = Vec::new();
457-
let mut items_to_inspect = vec![impl_did];
458-
'items: while let Some(item_def_id) = items_to_inspect.pop() {
459-
if seen_items.contains(&item_def_id) {
460-
continue;
461-
}
462-
463-
for pred in ty::lookup_predicates(rcx.tcx(), item_def_id).predicates {
464-
let result = match pred {
465-
ty::Predicate::Equate(..) |
466-
ty::Predicate::RegionOutlives(..) |
467-
ty::Predicate::TypeOutlives(..) |
468-
ty::Predicate::Projection(..) => {
469-
// For now, assume all these where-clauses
470-
// may give drop implementation capabilty
471-
// to access borrowed data.
472-
true
473-
}
474-
475-
ty::Predicate::Trait(ty::Binder(ref t_pred)) => {
476-
let def_id = t_pred.trait_ref.def_id;
477-
if ty::trait_items(rcx.tcx(), def_id).len() != 0 {
478-
// If trait has items, assume it adds
479-
// capability to access borrowed data.
480-
true
481-
} else {
482-
// Trait without items is itself
483-
// uninteresting from POV of dropck.
484-
//
485-
// However, may have parent w/ items;
486-
// so schedule checking of predicates,
487-
items_to_inspect.push(def_id);
488-
// and say "no capability found" for now.
489-
false
490-
}
491-
}
492-
};
493-
494-
if result {
495-
has_pred_of_interest = true;
496-
debug!("typ: {} has interesting dtor due to generic preds, e.g. {}",
497-
typ.repr(rcx.tcx()), pred.repr(rcx.tcx()));
498-
break 'items;
499-
}
500-
}
501-
502-
seen_items.push(item_def_id);
503-
}
504-
505-
// In `impl<'a> Drop ...`, we automatically assume
506-
// `'a` is meaningful and thus represents a bound
507-
// through which we could reach borrowed data.
508-
//
509-
// FIXME (pnkfelix): In the future it would be good to
510-
// extend the language to allow the user to express,
511-
// in the impl signature, that a lifetime is not
512-
// actually used (something like `where 'a: ?Live`).
513-
let has_region_param_of_interest =
514-
dtor_generics.has_region_params(subst::TypeSpace);
515-
516-
has_dtor_of_interest =
517-
has_region_param_of_interest ||
518-
has_pred_of_interest;
519-
520-
if has_dtor_of_interest {
521-
debug!("typ: {} has interesting dtor, due to \
522-
region params: {} or pred: {}",
523-
typ.repr(rcx.tcx()),
524-
has_region_param_of_interest,
525-
has_pred_of_interest);
526-
} else {
527-
debug!("typ: {} has dtor, but it is uninteresting",
528-
typ.repr(rcx.tcx()));
529-
}
530-
531-
} else {
532-
debug!("typ: {} has no dtor, and thus is uninteresting",
533-
typ.repr(rcx.tcx()));
534-
has_dtor_of_interest = false;
535-
}
536-
537-
if has_dtor_of_interest {
447+
if has_dtor_of_interest(rcx.tcx(), dtor_kind, typ, span) {
538448
// If `typ` has a destructor, then we must ensure that all
539449
// borrowed data reachable via `typ` must outlive the
540450
// parent of `scope`. (It does not suffice for it to
@@ -620,7 +530,7 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
620530

621531
ty::ty_rptr(..) | ty::ty_ptr(_) | ty::ty_bare_fn(..) => {
622532
// Don't recurse, since references, pointers,
623-
// boxes, and bare functions don't own instances
533+
// and bare functions don't own instances
624534
// of the types appearing within them.
625535
walker.skip_current_subtree();
626536
}
@@ -639,3 +549,140 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
639549

640550
return Ok(());
641551
}
552+
553+
enum DtorKind<'tcx> {
554+
// Type has an associated drop method with this def id
555+
KnownDropMethod(ast::DefId),
556+
557+
// Type has no destructor (or its dtor is known to be pure
558+
// with respect to lifetimes), though its *substructure*
559+
// may carry a destructor.
560+
PureRecur,
561+
562+
// Type may have impure destructor that is unknown;
563+
// e.g. `Box<Trait+'a>`
564+
Unknown(ty::ExistentialBounds<'tcx>),
565+
}
566+
567+
fn has_dtor_of_interest<'tcx>(tcx: &ty::ctxt<'tcx>,
568+
dtor_kind: DtorKind,
569+
typ: ty::Ty<'tcx>,
570+
span: Span) -> bool {
571+
let has_dtor_of_interest: bool;
572+
573+
match dtor_kind {
574+
DtorKind::PureRecur => {
575+
has_dtor_of_interest = false;
576+
debug!("typ: {} has no dtor, and thus is uninteresting",
577+
typ.repr(tcx));
578+
}
579+
DtorKind::Unknown(bounds) => {
580+
match bounds.region_bound {
581+
ty::ReStatic => {
582+
debug!("trait: {} has 'static bound, and thus is uninteresting",
583+
typ.repr(tcx));
584+
has_dtor_of_interest = false;
585+
}
586+
ty::ReEmpty => {
587+
debug!("trait: {} has empty region bound, and thus is uninteresting",
588+
typ.repr(tcx));
589+
has_dtor_of_interest = false;
590+
}
591+
r => {
592+
debug!("trait: {} has non-static bound: {}; assumed interesting",
593+
typ.repr(tcx), r.repr(tcx));
594+
has_dtor_of_interest = true;
595+
}
596+
}
597+
}
598+
DtorKind::KnownDropMethod(dtor_method_did) => {
599+
let impl_did = ty::impl_of_method(tcx, dtor_method_did)
600+
.unwrap_or_else(|| {
601+
tcx.sess.span_bug(
602+
span, "no Drop impl found for drop method")
603+
});
604+
605+
let dtor_typescheme = ty::lookup_item_type(tcx, impl_did);
606+
let dtor_generics = dtor_typescheme.generics;
607+
608+
let mut has_pred_of_interest = false;
609+
610+
let mut seen_items = Vec::new();
611+
let mut items_to_inspect = vec![impl_did];
612+
'items: while let Some(item_def_id) = items_to_inspect.pop() {
613+
if seen_items.contains(&item_def_id) {
614+
continue;
615+
}
616+
617+
for pred in ty::lookup_predicates(tcx, item_def_id).predicates {
618+
let result = match pred {
619+
ty::Predicate::Equate(..) |
620+
ty::Predicate::RegionOutlives(..) |
621+
ty::Predicate::TypeOutlives(..) |
622+
ty::Predicate::Projection(..) => {
623+
// For now, assume all these where-clauses
624+
// may give drop implementation capabilty
625+
// to access borrowed data.
626+
true
627+
}
628+
629+
ty::Predicate::Trait(ty::Binder(ref t_pred)) => {
630+
let def_id = t_pred.trait_ref.def_id;
631+
if ty::trait_items(tcx, def_id).len() != 0 {
632+
// If trait has items, assume it adds
633+
// capability to access borrowed data.
634+
true
635+
} else {
636+
// Trait without items is itself
637+
// uninteresting from POV of dropck.
638+
//
639+
// However, may have parent w/ items;
640+
// so schedule checking of predicates,
641+
items_to_inspect.push(def_id);
642+
// and say "no capability found" for now.
643+
false
644+
}
645+
}
646+
};
647+
648+
if result {
649+
has_pred_of_interest = true;
650+
debug!("typ: {} has interesting dtor due to generic preds, e.g. {}",
651+
typ.repr(tcx), pred.repr(tcx));
652+
break 'items;
653+
}
654+
}
655+
656+
seen_items.push(item_def_id);
657+
}
658+
659+
// In `impl<'a> Drop ...`, we automatically assume
660+
// `'a` is meaningful and thus represents a bound
661+
// through which we could reach borrowed data.
662+
//
663+
// FIXME (pnkfelix): In the future it would be good to
664+
// extend the language to allow the user to express,
665+
// in the impl signature, that a lifetime is not
666+
// actually used (something like `where 'a: ?Live`).
667+
let has_region_param_of_interest =
668+
dtor_generics.has_region_params(subst::TypeSpace);
669+
670+
has_dtor_of_interest =
671+
has_region_param_of_interest ||
672+
has_pred_of_interest;
673+
674+
if has_dtor_of_interest {
675+
debug!("typ: {} has interesting dtor, due to \
676+
region params: {} or pred: {}",
677+
typ.repr(tcx),
678+
has_region_param_of_interest,
679+
has_pred_of_interest);
680+
} else {
681+
debug!("typ: {} has dtor, but it is uninteresting",
682+
typ.repr(tcx));
683+
}
684+
}
685+
}
686+
687+
return has_dtor_of_interest;
688+
}

0 commit comments

Comments
 (0)