Skip to content

Commit c0100ce

Browse files
committed
Auto merge of #25113 - pnkfelix:dropck-itemless-traits, r=huonw
Generalize dropck to ignore item-less traits Fix #24805. (This is the reopened + rebased version of PR #24898)
2 parents 31e3cb7 + f40d9d9 commit c0100ce

File tree

4 files changed

+238
-35
lines changed

4 files changed

+238
-35
lines changed

Diff for: src/librustc_typeck/check/dropck.rs

+47-35
Original file line numberDiff line numberDiff line change
@@ -450,45 +450,57 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
450450

451451
let dtor_typescheme = ty::lookup_item_type(rcx.tcx(), impl_did);
452452
let dtor_generics = dtor_typescheme.generics;
453-
let dtor_predicates = ty::lookup_predicates(rcx.tcx(), impl_did);
454-
455-
let has_pred_of_interest = dtor_predicates.predicates.iter().any(|pred| {
456-
// In `impl<T> Drop where ...`, we automatically
457-
// assume some predicate will be meaningful and thus
458-
// represents a type through which we could reach
459-
// borrowed data. However, there can be implicit
460-
// predicates (namely for Sized), and so we still need
461-
// to walk through and filter out those cases.
462-
463-
let result = match *pred {
464-
ty::Predicate::Trait(ty::Binder(ref t_pred)) => {
465-
let def_id = t_pred.trait_ref.def_id;
466-
match rcx.tcx().lang_items.to_builtin_kind(def_id) {
467-
// Issue 24895: deliberately do not include `BoundCopy` here.
468-
Some(ty::BoundSend) |
469-
Some(ty::BoundSized) |
470-
Some(ty::BoundSync) => false,
471-
_ => true,
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
472473
}
473-
}
474-
ty::Predicate::Equate(..) |
475-
ty::Predicate::RegionOutlives(..) |
476-
ty::Predicate::TypeOutlives(..) |
477-
ty::Predicate::Projection(..) => {
478-
// we assume all of these where-clauses may
479-
// give the drop implementation the capabilty
480-
// to access borrowed data.
481-
true
482-
}
483-
};
484474

485-
if result {
486-
debug!("typ: {} has interesting dtor due to generic preds, e.g. {}",
487-
typ.repr(rcx.tcx()), pred.repr(rcx.tcx()));
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+
}
488500
}
489501

490-
result
491-
});
502+
seen_items.push(item_def_id);
503+
}
492504

493505
// In `impl<'a> Drop ...`, we automatically assume
494506
// `'a` is meaningful and thus represents a bound
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Check that child trait who only has items via its *parent* trait
12+
// does cause dropck to inject extra region constraints.
13+
14+
#![allow(non_camel_case_types)]
15+
16+
trait Parent { fn foo(&self); }
17+
trait Child: Parent { }
18+
19+
impl Parent for i32 { fn foo(&self) { } }
20+
impl<'a> Parent for &'a D_Child<i32> {
21+
fn foo(&self) {
22+
println!("accessing child value: {}", self.0);
23+
}
24+
}
25+
26+
impl Child for i32 { }
27+
impl<'a> Child for &'a D_Child<i32> { }
28+
29+
struct D_Child<T:Child>(T);
30+
impl <T:Child> Drop for D_Child<T> { fn drop(&mut self) { self.0.foo() } }
31+
32+
fn f_child() {
33+
// `_d` and `d1` are assigned the *same* lifetime by region inference ...
34+
let (_d, d1);
35+
36+
d1 = D_Child(1);
37+
// ... we store a reference to `d1` within `_d` ...
38+
_d = D_Child(&d1); //~ ERROR `d1` does not live long enough
39+
40+
// ... dropck *should* complain, because Drop of _d could (and
41+
// does) access the already dropped `d1` via the `foo` method.
42+
}
43+
44+
fn main() {
45+
f_child();
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Check that traits with various kinds of associated items cause
12+
// dropck to inject extra region constraints.
13+
14+
#![allow(non_camel_case_types)]
15+
16+
trait HasSelfMethod { fn m1(&self) { } }
17+
trait HasMethodWithSelfArg { fn m2(x: &Self) { } }
18+
trait HasType { type Something; }
19+
20+
impl HasSelfMethod for i32 { }
21+
impl HasMethodWithSelfArg for i32 { }
22+
impl HasType for i32 { type Something = (); }
23+
24+
impl<'a,T> HasSelfMethod for &'a T { }
25+
impl<'a,T> HasMethodWithSelfArg for &'a T { }
26+
impl<'a,T> HasType for &'a T { type Something = (); }
27+
28+
// e.g. `impl_drop!(Send, D_Send)` expands to:
29+
// ```rust
30+
// struct D_Send<T:Send>(T);
31+
// impl<T:Send> Drop for D_Send<T> { fn drop(&mut self) { } }
32+
// ```
33+
macro_rules! impl_drop {
34+
($Bound:ident, $Id:ident) => {
35+
struct $Id<T:$Bound>(T);
36+
impl <T:$Bound> Drop for $Id<T> { fn drop(&mut self) { } }
37+
}
38+
}
39+
40+
impl_drop!{HasSelfMethod, D_HasSelfMethod}
41+
impl_drop!{HasMethodWithSelfArg, D_HasMethodWithSelfArg}
42+
impl_drop!{HasType, D_HasType}
43+
44+
fn f_sm() {
45+
let (_d, d1);
46+
d1 = D_HasSelfMethod(1);
47+
_d = D_HasSelfMethod(&d1); //~ ERROR `d1` does not live long enough
48+
}
49+
fn f_mwsa() {
50+
let (_d, d1);
51+
d1 = D_HasMethodWithSelfArg(1);
52+
_d = D_HasMethodWithSelfArg(&d1); //~ ERROR `d1` does not live long enough
53+
}
54+
fn f_t() {
55+
let (_d, d1);
56+
d1 = D_HasType(1);
57+
_d = D_HasType(&d1); //~ ERROR `d1` does not live long enough
58+
}
59+
60+
fn main() {
61+
f_sm();
62+
f_mwsa();
63+
f_t();
64+
}

Diff for: src/test/run-pass/issue-24805-dropck-itemless.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Check that item-less traits do not cause dropck to inject extra
12+
// region constraints.
13+
14+
#![allow(non_camel_case_types)]
15+
16+
trait UserDefined { }
17+
18+
impl UserDefined for i32 { }
19+
impl<'a, T> UserDefined for &'a T { }
20+
21+
// e.g. `impl_drop!(Send, D_Send)` expands to:
22+
// ```rust
23+
// struct D_Send<T:Send>(T);
24+
// impl<T:Send> Drop for D_Send<T> { fn drop(&mut self) { } }
25+
// ```
26+
macro_rules! impl_drop {
27+
($Bound:ident, $Id:ident) => {
28+
struct $Id<T:$Bound>(T);
29+
impl <T:$Bound> Drop for $Id<T> { fn drop(&mut self) { } }
30+
}
31+
}
32+
33+
impl_drop!{Send, D_Send}
34+
impl_drop!{Sized, D_Sized}
35+
36+
// See note below regarding Issue 24895
37+
// impl_drop!{Copy, D_Copy}
38+
39+
impl_drop!{Sync, D_Sync}
40+
impl_drop!{UserDefined, D_UserDefined}
41+
42+
macro_rules! body {
43+
($id:ident) => { {
44+
// `_d` and `d1` are assigned the *same* lifetime by region inference ...
45+
let (_d, d1);
46+
47+
d1 = $id(1);
48+
// ... we store a reference to `d1` within `_d` ...
49+
_d = $id(&d1);
50+
51+
// ... a *conservative* dropck will thus complain, because it
52+
// thinks Drop of _d could access the already dropped `d1`.
53+
} }
54+
}
55+
56+
fn f_send() { body!(D_Send) }
57+
fn f_sized() { body!(D_Sized) }
58+
fn f_sync() { body!(D_Sync) }
59+
60+
// Issue 24895: Copy: Clone implies `impl<T:Copy> Drop for ...` can
61+
// access a user-defined clone() method, which causes this test case
62+
// to fail.
63+
//
64+
// If 24895 is resolved by removing the `Copy: Clone` relationship,
65+
// then this definition and the call below should be uncommented. If
66+
// it is resolved by deciding to keep the `Copy: Clone` relationship,
67+
// then this comment and the associated bits of code can all be
68+
// removed.
69+
70+
// fn f_copy() { body!(D_Copy) }
71+
72+
fn f_userdefined() { body!(D_UserDefined) }
73+
74+
fn main() {
75+
f_send();
76+
f_sized();
77+
// See note above regarding Issue 24895.
78+
// f_copy();
79+
f_sync();
80+
f_userdefined();
81+
}

0 commit comments

Comments
 (0)