Skip to content

Commit 2d27bfa

Browse files
committed
auto merge of #17704 : nick29581/rust/object-safety, r=nikomatsakis
r? @nikomatsakis
2 parents d1fc2de + 88a250d commit 2d27bfa

20 files changed

+309
-394
lines changed

src/librustc/diagnostics.rs

-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ register_diagnostics!(
5353
E0035,
5454
E0036,
5555
E0038,
56-
E0039,
5756
E0040,
5857
E0044,
5958
E0045,

src/librustc/middle/typeck/check/method.rs

+6-82
Original file line numberDiff line numberDiff line change
@@ -619,14 +619,12 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
619619

620620
let tcx = self.tcx();
621621

622-
// It is illegal to invoke a method on a trait instance that
623-
// refers to the `Self` type. An error will be reported by
624-
// `enforce_object_limitations()` if the method refers to the
625-
// `Self` type anywhere other than the receiver. Here, we use
626-
// a substitution that replaces `Self` with the object type
627-
// itself. Hence, a `&self` method will wind up with an
628-
// argument type like `&Trait`.
622+
// It is illegal to invoke a method on a trait instance that refers to
623+
// the `Self` type. Here, we use a substitution that replaces `Self`
624+
// with the object type itself. Hence, a `&self` method will wind up
625+
// with an argument type like `&Trait`.
629626
let rcvr_substs = substs.with_self_ty(self_ty);
627+
630628
let trait_ref = Rc::new(TraitRef {
631629
def_id: did,
632630
substs: rcvr_substs.clone()
@@ -1336,16 +1334,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
13361334
self.ty_to_string(rcvr_ty),
13371335
candidate.repr(self.tcx()));
13381336

1339-
let mut rcvr_substs = candidate.rcvr_substs.clone();
1340-
1341-
if !self.enforce_object_limitations(candidate) {
1342-
// Here we change `Self` from `Trait` to `err` in the case that
1343-
// this is an illegal object method. This is necessary to prevent
1344-
// the user from getting strange, derivative errors when the method
1345-
// takes an argument/return-type of type `Self` etc.
1346-
rcvr_substs.types.get_mut_slice(SelfSpace)[0] = ty::mk_err();
1347-
}
1348-
1337+
let rcvr_substs = candidate.rcvr_substs.clone();
13491338
self.enforce_drop_trait_limitations(candidate);
13501339

13511340
// Determine the values for the generic parameters of the method.
@@ -1554,71 +1543,6 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
15541543
}
15551544
}
15561545

1557-
fn enforce_object_limitations(&self, candidate: &Candidate) -> bool {
1558-
/*!
1559-
* There are some limitations to calling functions through an
1560-
* object, because (a) the self type is not known
1561-
* (that's the whole point of a trait instance, after all, to
1562-
* obscure the self type) and (b) the call must go through a
1563-
* vtable and hence cannot be monomorphized.
1564-
*/
1565-
1566-
match candidate.origin {
1567-
MethodStatic(..) |
1568-
MethodTypeParam(..) |
1569-
MethodStaticUnboxedClosure(..) => {
1570-
return true; // not a call to a trait instance
1571-
}
1572-
MethodTraitObject(..) => {}
1573-
}
1574-
1575-
match candidate.method_ty.explicit_self {
1576-
ty::StaticExplicitSelfCategory => { // reason (a) above
1577-
self.tcx().sess.span_err(
1578-
self.span,
1579-
"cannot call a method without a receiver \
1580-
through an object");
1581-
return false;
1582-
}
1583-
1584-
ty::ByValueExplicitSelfCategory |
1585-
ty::ByReferenceExplicitSelfCategory(..) |
1586-
ty::ByBoxExplicitSelfCategory => {}
1587-
}
1588-
1589-
// reason (a) above
1590-
let check_for_self_ty = |ty| -> bool {
1591-
if ty::type_has_self(ty) {
1592-
span_err!(self.tcx().sess, self.span, E0038,
1593-
"cannot call a method whose type contains a \
1594-
self-type through an object");
1595-
false
1596-
} else {
1597-
true
1598-
}
1599-
};
1600-
let ref sig = candidate.method_ty.fty.sig;
1601-
for &input_ty in sig.inputs[1..].iter() {
1602-
if !check_for_self_ty(input_ty) {
1603-
return false;
1604-
}
1605-
}
1606-
if let ty::FnConverging(result_type) = sig.output {
1607-
if !check_for_self_ty(result_type) {
1608-
return false;
1609-
}
1610-
}
1611-
1612-
if candidate.method_ty.generics.has_type_params(subst::FnSpace) {
1613-
// reason (b) above
1614-
span_err!(self.tcx().sess, self.span, E0039,
1615-
"cannot call a generic method through an object");
1616-
return false;
1617-
}
1618-
1619-
true
1620-
}
1621-
16221546
fn enforce_drop_trait_limitations(&self, candidate: &Candidate) {
16231547
// No code can call the finalize method explicitly.
16241548
let bad = match candidate.origin {

src/librustc/middle/typeck/check/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1687,6 +1687,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16871687
self.register_unsize_obligations(span, &**u)
16881688
}
16891689
ty::UnsizeVtable(ref ty_trait, self_ty) => {
1690+
vtable::check_object_safety(self.tcx(), ty_trait, span);
16901691
// If the type is `Foo+'a`, ensures that the type
16911692
// being cast to `Foo+'a` implements `Foo`:
16921693
vtable::register_object_cast_obligations(self,

src/librustc/middle/typeck/check/vtable.rs

+102-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use middle::subst::{SelfSpace};
11+
use middle::subst::{SelfSpace, FnSpace};
1212
use middle::traits;
1313
use middle::traits::{SelectionError, OutputTypeParameterMismatch, Overflow, Unimplemented};
1414
use middle::traits::{Obligation, obligation_for_builtin_bound};
@@ -21,8 +21,7 @@ use middle::typeck::infer;
2121
use std::rc::Rc;
2222
use syntax::ast;
2323
use syntax::codemap::Span;
24-
use util::ppaux::UserString;
25-
use util::ppaux::Repr;
24+
use util::ppaux::{UserString, Repr, ty_to_string};
2625

2726
pub fn check_object_cast(fcx: &FnCtxt,
2827
cast_expr: &ast::Expr,
@@ -46,6 +45,7 @@ pub fn check_object_cast(fcx: &FnCtxt,
4645

4746
// Ensure that if ~T is cast to ~Trait, then T : Trait
4847
push_cast_obligation(fcx, cast_expr, object_trait, referent_ty);
48+
check_object_safety(fcx.tcx(), object_trait, source_expr.span);
4949
}
5050

5151
(&ty::ty_rptr(referent_region, ty::mt { ty: referent_ty,
@@ -68,6 +68,8 @@ pub fn check_object_cast(fcx: &FnCtxt,
6868
infer::RelateObjectBound(source_expr.span),
6969
target_region,
7070
referent_region);
71+
72+
check_object_safety(fcx.tcx(), object_trait, source_expr.span);
7173
}
7274
}
7375

@@ -128,6 +130,103 @@ pub fn check_object_cast(fcx: &FnCtxt,
128130
}
129131
}
130132

133+
// Check that a trait is 'object-safe'. This should be checked whenever a trait object
134+
// is created (by casting or coercion, etc.). A trait is object-safe if all its
135+
// methods are object-safe. A trait method is object-safe if it does not take
136+
// self by value, has no type parameters and does not use the `Self` type, except
137+
// in self position.
138+
pub fn check_object_safety(tcx: &ty::ctxt, object_trait: &ty::TyTrait, span: Span) {
139+
// Skip the fn_once lang item trait since only the compiler should call
140+
// `call_once` which is the method which takes self by value. What could go
141+
// wrong?
142+
match tcx.lang_items.fn_once_trait() {
143+
Some(def_id) if def_id == object_trait.def_id => return,
144+
_ => {}
145+
}
146+
147+
let trait_items = ty::trait_items(tcx, object_trait.def_id);
148+
149+
let mut errors = Vec::new();
150+
for item in trait_items.iter() {
151+
match *item {
152+
ty::MethodTraitItem(ref m) => {
153+
errors.push(check_object_safety_of_method(tcx, &**m))
154+
}
155+
ty::TypeTraitItem(_) => {}
156+
}
157+
}
158+
159+
let mut errors = errors.iter().flat_map(|x| x.iter()).peekable();
160+
if errors.peek().is_some() {
161+
let trait_name = ty::item_path_str(tcx, object_trait.def_id);
162+
span_err!(tcx.sess, span, E0038,
163+
"cannot convert to a trait object because trait `{}` is not object-safe",
164+
trait_name);
165+
166+
for msg in errors {
167+
tcx.sess.note(msg.as_slice());
168+
}
169+
}
170+
171+
// Returns a vec of error messages. If hte vec is empty - no errors!
172+
fn check_object_safety_of_method(tcx: &ty::ctxt, method: &ty::Method) -> Vec<String> {
173+
/*!
174+
* There are some limitations to calling functions through an
175+
* object, because (a) the self type is not known
176+
* (that's the whole point of a trait instance, after all, to
177+
* obscure the self type) and (b) the call must go through a
178+
* vtable and hence cannot be monomorphized.
179+
*/
180+
let mut msgs = Vec::new();
181+
182+
let method_name = method.name.repr(tcx);
183+
184+
match method.explicit_self {
185+
ty::ByValueExplicitSelfCategory => { // reason (a) above
186+
msgs.push(format!("cannot call a method (`{}`) with a by-value \
187+
receiver through a trait object", method_name))
188+
}
189+
190+
ty::StaticExplicitSelfCategory |
191+
ty::ByReferenceExplicitSelfCategory(..) |
192+
ty::ByBoxExplicitSelfCategory => {}
193+
}
194+
195+
// reason (a) above
196+
let check_for_self_ty = |ty| {
197+
if ty::type_has_self(ty) {
198+
Some(format!(
199+
"cannot call a method (`{}`) whose type contains \
200+
a self-type (`{}`) through a trait object",
201+
method_name, ty_to_string(tcx, ty)))
202+
} else {
203+
None
204+
}
205+
};
206+
let ref sig = method.fty.sig;
207+
for &input_ty in sig.inputs[1..].iter() {
208+
match check_for_self_ty(input_ty) {
209+
Some(msg) => msgs.push(msg),
210+
_ => {}
211+
}
212+
}
213+
if let ty::FnConverging(result_type) = sig.output {
214+
match check_for_self_ty(result_type) {
215+
Some(msg) => msgs.push(msg),
216+
_ => {}
217+
}
218+
}
219+
220+
if method.generics.has_type_params(FnSpace) {
221+
// reason (b) above
222+
msgs.push(format!("cannot call a generic method (`{}`) through a trait object",
223+
method_name));
224+
}
225+
226+
msgs
227+
}
228+
}
229+
131230
pub fn register_object_cast_obligations(fcx: &FnCtxt,
132231
span: Span,
133232
object_trait: &ty::TyTrait,

src/libstd/io/extensions.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ pub fn u64_from_be_bytes(data: &[u8], start: uint, size: uint) -> u64 {
172172
mod test {
173173
use prelude::*;
174174
use io;
175-
use io::{MemReader, MemWriter};
175+
use io::{MemReader, MemWriter, BytesReader};
176176

177177
struct InitialZeroByteReader {
178178
count: int,

src/libstd/io/mod.rs

+36-14
Original file line numberDiff line numberDiff line change
@@ -712,17 +712,6 @@ pub trait Reader {
712712
})
713713
}
714714

715-
/// Create an iterator that reads a single byte on
716-
/// each iteration, until EOF.
717-
///
718-
/// # Error
719-
///
720-
/// Any error other than `EndOfFile` that is produced by the underlying Reader
721-
/// is returned by the iterator and should be handled by the caller.
722-
fn bytes<'r>(&'r mut self) -> extensions::Bytes<'r, Self> {
723-
extensions::Bytes::new(self)
724-
}
725-
726715
// Byte conversion helpers
727716

728717
/// Reads `n` little-endian unsigned integer bytes.
@@ -932,16 +921,41 @@ pub trait Reader {
932921
fn read_i8(&mut self) -> IoResult<i8> {
933922
self.read_byte().map(|i| i as i8)
934923
}
924+
}
935925

926+
/// A reader which can be converted to a RefReader.
927+
pub trait AsRefReader {
936928
/// Creates a wrapper around a mutable reference to the reader.
937929
///
938930
/// This is useful to allow applying adaptors while still
939931
/// retaining ownership of the original value.
940-
fn by_ref<'a>(&'a mut self) -> RefReader<'a, Self> {
932+
fn by_ref<'a>(&'a mut self) -> RefReader<'a, Self>;
933+
}
934+
935+
impl<T: Reader> AsRefReader for T {
936+
fn by_ref<'a>(&'a mut self) -> RefReader<'a, T> {
941937
RefReader { inner: self }
942938
}
943939
}
944940

941+
/// A reader which can be converted to bytes.
942+
pub trait BytesReader {
943+
/// Create an iterator that reads a single byte on
944+
/// each iteration, until EOF.
945+
///
946+
/// # Error
947+
///
948+
/// Any error other than `EndOfFile` that is produced by the underlying Reader
949+
/// is returned by the iterator and should be handled by the caller.
950+
fn bytes<'r>(&'r mut self) -> extensions::Bytes<'r, Self>;
951+
}
952+
953+
impl<T: Reader> BytesReader for T {
954+
fn bytes<'r>(&'r mut self) -> extensions::Bytes<'r, T> {
955+
extensions::Bytes::new(self)
956+
}
957+
}
958+
945959
impl<'a> Reader for Box<Reader+'a> {
946960
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
947961
let reader: &mut Reader = &mut **self;
@@ -986,6 +1000,7 @@ unsafe fn slice_vec_capacity<'a, T>(v: &'a mut Vec<T>, start: uint, end: uint) -
9861000
/// # fn process_input<R: Reader>(r: R) {}
9871001
/// # fn foo() {
9881002
/// use std::io;
1003+
/// use std::io::AsRefReader;
9891004
/// use std::io::util::LimitReader;
9901005
///
9911006
/// let mut stream = io::stdin();
@@ -1268,13 +1283,20 @@ pub trait Writer {
12681283
fn write_i8(&mut self, n: i8) -> IoResult<()> {
12691284
self.write([n as u8])
12701285
}
1286+
}
12711287

1288+
/// A writer which can be converted to a RefWriter.
1289+
pub trait AsRefWriter {
12721290
/// Creates a wrapper around a mutable reference to the writer.
12731291
///
12741292
/// This is useful to allow applying wrappers while still
12751293
/// retaining ownership of the original value.
12761294
#[inline]
1277-
fn by_ref<'a>(&'a mut self) -> RefWriter<'a, Self> {
1295+
fn by_ref<'a>(&'a mut self) -> RefWriter<'a, Self>;
1296+
}
1297+
1298+
impl<T: Writer> AsRefWriter for T {
1299+
fn by_ref<'a>(&'a mut self) -> RefWriter<'a, T> {
12781300
RefWriter { inner: self }
12791301
}
12801302
}
@@ -1309,7 +1331,7 @@ impl<'a> Writer for &'a mut Writer+'a {
13091331
/// # fn process_input<R: Reader>(r: R) {}
13101332
/// # fn foo () {
13111333
/// use std::io::util::TeeReader;
1312-
/// use std::io::{stdin, MemWriter};
1334+
/// use std::io::{stdin, MemWriter, AsRefWriter};
13131335
///
13141336
/// let mut output = MemWriter::new();
13151337
///

src/libstd/io/util.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ impl<T: Iterator<u8>> Reader for IterReader<T> {
265265

266266
#[cfg(test)]
267267
mod test {
268-
use io::{MemReader, MemWriter, BufReader};
268+
use io::{MemReader, MemWriter, BufReader, AsRefReader};
269269
use io;
270270
use boxed::Box;
271271
use super::*;

0 commit comments

Comments
 (0)