Skip to content

Trait/fn bounds part 2: Inferred default bounds and looser restrictions on unbounded trait/fns. #7354

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
6 changes: 5 additions & 1 deletion src/libextra/io_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

use core::io::{Reader, BytesReader};
use core::io;
use core::cast;

/// An implementation of the io::Reader interface which reads a buffer of bytes
pub struct BufReader {
Expand All @@ -29,10 +30,13 @@ impl BufReader {
}

fn as_bytes_reader<A>(&self, f: &fn(&BytesReader) -> A) -> A {
// XXX FIXME(#5723)
let bytes = ::core::util::id::<&[u8]>(self.buf);
let bytes: &'static [u8] = unsafe { cast::transmute(bytes) };
// Recreating the BytesReader state every call since
// I can't get the borrowing to work correctly
let bytes_reader = BytesReader {
bytes: ::core::util::id::<&[u8]>(self.buf),
bytes: bytes,
pos: @mut *self.pos
};

Expand Down
2 changes: 1 addition & 1 deletion src/libextra/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ type MonitorMsg = (TestDesc, TestResult);

fn run_tests(opts: &TestOpts,
tests: ~[TestDescAndFn],
callback: @fn(e: TestEvent)) {
callback: &fn(e: TestEvent)) {

let filtered_tests = filter_tests(opts, tests);
let filtered_descs = filtered_tests.map(|t| copy t.desc);
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -959,7 +959,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
encode_attributes(ebml_w, item.attrs);
match ty.node {
ast::ty_path(path, bounds, _) if path.idents.len() == 1 => {
assert!(bounds.is_empty());
assert!(bounds.is_none());
encode_impl_type_basename(ecx, ebml_w,
ast_util::path_to_ident(path));
}
Expand Down
21 changes: 4 additions & 17 deletions src/librustc/middle/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ fn check_item(item: @item, (cx, visitor): (Context, visit::vt<Context>)) {
// Yes, it's a destructor.
match self_type.node {
ty_path(_, bounds, path_node_id) => {
assert!(bounds.is_empty());
assert!(bounds.is_none());
let struct_def = cx.tcx.def_map.get_copy(
&path_node_id);
let struct_did =
Expand Down Expand Up @@ -169,10 +169,6 @@ fn with_appropriate_checker(cx: Context, id: node_id,
let id = ast_util::def_id_of_def(fv.def).node;
let var_t = ty::node_id_to_type(cx.tcx, id);

// FIXME(#3569): Once closure capabilities are restricted based on their
// incoming bounds, make this check conditional based on the bounds.
if !check_owned(cx, var_t, fv.span) { return; }

// check that only immutable variables are implicitly copied in
check_imm_free_var(cx, fv.def, fv.span);

Expand All @@ -184,10 +180,6 @@ fn with_appropriate_checker(cx: Context, id: node_id,
let id = ast_util::def_id_of_def(fv.def).node;
let var_t = ty::node_id_to_type(cx.tcx, id);

// FIXME(#3569): Once closure capabilities are restricted based on their
// incoming bounds, make this check conditional based on the bounds.
if !check_durable(cx.tcx, var_t, fv.span) { return; }

// check that only immutable variables are implicitly copied in
check_imm_free_var(cx, fv.def, fv.span);

Expand Down Expand Up @@ -293,9 +285,9 @@ pub fn check_expr(e: @expr, (cx, v): (Context, visit::vt<Context>)) {
expr_cast(source, _) => {
check_cast_for_escaping_regions(cx, source, e);
match ty::get(ty::expr_ty(cx.tcx, e)).sty {
ty::ty_trait(_, _, store, _, bounds) => {
ty::ty_trait(_, _, _, _, bounds) => {
let source_ty = ty::expr_ty(cx.tcx, source);
check_trait_cast_bounds(cx, e.span, source_ty, bounds, store)
check_trait_cast_bounds(cx, e.span, source_ty, bounds)
}
_ => { }
}
Expand Down Expand Up @@ -391,19 +383,14 @@ pub fn check_freevar_bounds(cx: Context, sp: span, ty: ty::t,
}

pub fn check_trait_cast_bounds(cx: Context, sp: span, ty: ty::t,
bounds: ty::BuiltinBounds, store: ty::TraitStore) {
bounds: ty::BuiltinBounds) {
do check_builtin_bounds(cx, ty, bounds) |missing| {
cx.tcx.sess.span_err(sp,
fmt!("cannot pack type `%s`, which does not fulfill \
`%s`, as a trait bounded by %s",
ty_to_str(cx.tcx, ty), missing.user_string(cx.tcx),
bounds.user_string(cx.tcx)));
}
// FIXME(#3569): Remove this check when the corresponding restriction
// is made with type contents.
if store == ty::UniqTraitStore && !ty::type_is_owned(cx.tcx, ty) {
cx.tcx.sess.span_err(sp, "uniquely-owned trait objects must be sendable");
}
}

fn is_nullary_variant(cx: Context, ex: @expr) -> bool {
Expand Down
16 changes: 10 additions & 6 deletions src/librustc/middle/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4195,15 +4195,19 @@ impl Resolver {
}
}

for bounds.iter().advance |bound| {
self.resolve_type_parameter_bound(bound, visitor);
}
do bounds.map |bound_vec| {
for bound_vec.iter().advance |bound| {
self.resolve_type_parameter_bound(bound, visitor);
}
};
}

ty_closure(c) => {
for c.bounds.iter().advance |bound| {
self.resolve_type_parameter_bound(bound, visitor);
}
do c.bounds.map |bounds| {
for bounds.iter().advance |bound| {
self.resolve_type_parameter_bound(bound, visitor);
}
};
visit_ty(ty, ((), visitor));
}

Expand Down
70 changes: 48 additions & 22 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2063,20 +2063,8 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
TC_MANAGED + statically_sized(nonowned(tc_mt(cx, mt, cache)))
}

ty_trait(_, _, UniqTraitStore, _, _bounds) => {
// FIXME(#3569): Make this conditional on the trait's bounds.
TC_NONCOPY_TRAIT + TC_OWNED_POINTER
}

ty_trait(_, _, BoxTraitStore, mutbl, _bounds) => {
match mutbl {
ast::m_mutbl => TC_MANAGED + TC_MUTABLE,
_ => TC_MANAGED
}
}

ty_trait(_, _, RegionTraitStore(r), mutbl, _bounds) => {
borrowed_contents(r, mutbl)
ty_trait(_, _, store, mutbl, bounds) => {
trait_contents(store, mutbl, bounds)
}

ty_rptr(r, mt) => {
Expand Down Expand Up @@ -2261,23 +2249,59 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
}

fn closure_contents(cty: &ClosureTy) -> TypeContents {
// Closure contents are just like trait contents, but with potentially
// even more stuff.
let st = match cty.sigil {
ast::BorrowedSigil => TC_BORROWED_POINTER,
ast::ManagedSigil => TC_MANAGED,
ast::OwnedSigil => if cty.bounds.contains_elem(BoundCopy) {
TC_OWNED_POINTER
} else {
TC_OWNED_POINTER + TC_NONCOPY_TRAIT
}
ast::BorrowedSigil =>
trait_contents(RegionTraitStore(cty.region), m_imm, cty.bounds)
+ TC_BORROWED_POINTER, // might be an env packet even if static
ast::ManagedSigil =>
trait_contents(BoxTraitStore, m_imm, cty.bounds),
ast::OwnedSigil =>
trait_contents(UniqTraitStore, m_imm, cty.bounds),
};
// FIXME(#3569): This borrowed_contents call should be taken care of in
// trait_contents, after ~Traits and @Traits can have region bounds too.
// This one here is redundant for &fns but important for ~fns and @fns.
let rt = borrowed_contents(cty.region, m_imm);
// This also prohibits "@once fn" from being copied, which allows it to
// be called. Neither way really makes much sense.
let ot = match cty.onceness {
ast::Once => TC_ONCE_CLOSURE,
ast::Many => TC_NONE
};
st + rt + ot
}

fn trait_contents(store: TraitStore, mutbl: ast::mutability,
bounds: BuiltinBounds) -> TypeContents {
let st = match store {
UniqTraitStore => TC_OWNED_POINTER,
BoxTraitStore => TC_MANAGED,
RegionTraitStore(r) => borrowed_contents(r, mutbl),
};
let mt = match mutbl { ast::m_mutbl => TC_MUTABLE, _ => TC_NONE };
// We get additional "special type contents" for each bound that *isn't*
// on the trait. So iterate over the inverse of the bounds that are set.
// This is like with typarams below, but less "pessimistic" and also
// dependent on the trait store.
let mut bt = TC_NONE;
for (AllBuiltinBounds() - bounds).each |bound| {
bt = bt + match bound {
BoundCopy if store == UniqTraitStore
=> TC_NONCOPY_TRAIT,
BoundCopy => TC_NONE, // @Trait/&Trait are copyable either way
BoundStatic if bounds.contains_elem(BoundOwned)
=> TC_NONE, // Owned bound implies static bound.
BoundStatic => TC_BORROWED_POINTER, // Useful for "@Trait:'static"
BoundOwned => TC_NON_OWNED,
BoundConst => TC_MUTABLE,
BoundSized => TC_NONE, // don't care if interior is sized
};
}
st + mt + bt
}

fn type_param_def_to_contents(cx: ctxt,
type_param_def: &TypeParameterDef) -> TypeContents
{
Expand Down Expand Up @@ -4497,7 +4521,9 @@ pub fn visitor_object_ty(tcx: ctxt) -> (@TraitRef, t) {
};
let trait_lang_item = tcx.lang_items.ty_visitor();
let trait_ref = @TraitRef { def_id: trait_lang_item, substs: substs };
let mut static_trait_bound = EmptyBuiltinBounds();
static_trait_bound.add(BoundStatic);
(trait_ref,
mk_trait(tcx, trait_ref.def_id, copy trait_ref.substs,
BoxTraitStore, ast::m_imm, EmptyBuiltinBounds()))
BoxTraitStore, ast::m_imm, static_trait_bound))
}
75 changes: 50 additions & 25 deletions src/librustc/middle/typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:region_scope + Copy + 'static>(
ty::BoxTraitStore
}
};
let bounds = conv_builtin_bounds(this.tcx(), bounds);
let bounds = conv_builtin_bounds(this.tcx(), bounds, trait_store);
return ty::mk_trait(tcx,
result.def_id,
copy result.substs,
Expand Down Expand Up @@ -386,7 +386,13 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:region_scope + Copy + 'static>(
bf.abis, &bf.lifetimes, &bf.decl))
}
ast::ty_closure(ref f) => {
let bounds = conv_builtin_bounds(this.tcx(), &f.bounds);
let bounds = conv_builtin_bounds(this.tcx(), &f.bounds, match f.sigil {
// Use corresponding trait store to figure out default bounds
// if none were specified.
ast::BorrowedSigil => ty::RegionTraitStore(ty::re_empty), // dummy region
ast::OwnedSigil => ty::UniqTraitStore,
ast::ManagedSigil => ty::BoxTraitStore,
});
let fn_decl = ty_of_closure(this,
rscope,
f.sigil,
Expand All @@ -411,7 +417,7 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:region_scope + Copy + 'static>(
match a_def {
// But don't emit the error if the user meant to do a trait anyway.
ast::def_trait(*) => { },
_ if !bounds.is_empty() =>
_ if bounds.is_some() =>
tcx.sess.span_err(ast_ty.span,
"kind bounds can only be used on trait types"),
_ => { },
Expand Down Expand Up @@ -741,41 +747,60 @@ pub fn ty_of_closure<AC:AstConv,RS:region_scope + Copy + 'static>(
}
}

fn conv_builtin_bounds(tcx: ty::ctxt,
ast_bounds: &OptVec<ast::TyParamBound>)
fn conv_builtin_bounds(tcx: ty::ctxt, ast_bounds: &Option<OptVec<ast::TyParamBound>>,
store: ty::TraitStore)
-> ty::BuiltinBounds {
//! Converts a list of bounds from the AST into a `BuiltinBounds`
//! struct. Reports an error if any of the bounds that appear
//! in the AST refer to general traits and not the built-in traits
//! like `Copy` or `Owned`. Used to translate the bounds that
//! appear in closure and trait types, where only builtin bounds are
//! legal.

let mut builtin_bounds = ty::EmptyBuiltinBounds();
for ast_bounds.iter().advance |ast_bound| {
match *ast_bound {
ast::TraitTyParamBound(b) => {
match lookup_def_tcx(tcx, b.path.span, b.ref_id) {
ast::def_trait(trait_did) => {
if try_add_builtin_trait(tcx,
trait_did,
&mut builtin_bounds) {
loop; // success
//! If no bounds were specified, we choose a "default" bound based on
//! the allocation type of the fn/trait, as per issue #7264. The user can
//! override this with an empty bounds list, e.g. "~fn:()" or "~Trait:".

match (ast_bounds, store) {
(&Some(ref bound_vec), _) => {
let mut builtin_bounds = ty::EmptyBuiltinBounds();
for bound_vec.iter().advance |ast_bound| {
match *ast_bound {
ast::TraitTyParamBound(b) => {
match lookup_def_tcx(tcx, b.path.span, b.ref_id) {
ast::def_trait(trait_did) => {
if try_add_builtin_trait(tcx,
trait_did,
&mut builtin_bounds) {
loop; // success
}
}
_ => { }
}
tcx.sess.span_fatal(
b.path.span,
fmt!("only the builtin traits can be used \
as closure or object bounds"));
}
ast::RegionTyParamBound => {
builtin_bounds.add(ty::BoundStatic);
}
_ => { }
}
tcx.sess.span_fatal(
b.path.span,
fmt!("only the builtin traits can be used \
as closure or object bounds"));
}
ast::RegionTyParamBound => {
builtin_bounds.add(ty::BoundStatic);
}
builtin_bounds
},
// ~Trait is sugar for ~Trait:Owned.
(&None, ty::UniqTraitStore) => {
let mut set = ty::EmptyBuiltinBounds(); set.add(ty::BoundOwned); set
}
// @Trait is sugar for @Trait:'static.
// &'static Trait is sugar for &'static Trait:'static.
(&None, ty::BoxTraitStore) |
(&None, ty::RegionTraitStore(ty::re_static)) => {
let mut set = ty::EmptyBuiltinBounds(); set.add(ty::BoundStatic); set
}
// &'r Trait is sugar for &'r Trait:<no-bounds>.
(&None, ty::RegionTraitStore(*)) => ty::EmptyBuiltinBounds(),
}
builtin_bounds
}

pub fn try_add_builtin_trait(tcx: ty::ctxt,
Expand Down
13 changes: 10 additions & 3 deletions src/libstd/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1042,12 +1042,14 @@ pub fn file_reader(path: &Path) -> Result<@Reader, ~str> {


// Byte readers
pub struct BytesReader<'self> {
bytes: &'self [u8],
pub struct BytesReader {
// FIXME(#5723) see other FIXME below
// FIXME(#7268) this should also be parameterized over <'self>
bytes: &'static [u8],
pos: @mut uint
}

impl<'self> Reader for BytesReader<'self> {
impl Reader for BytesReader {
fn read(&self, bytes: &mut [u8], len: uint) -> uint {
let count = uint::min(len, self.bytes.len() - *self.pos);

Expand Down Expand Up @@ -1084,13 +1086,18 @@ impl<'self> Reader for BytesReader<'self> {
}

pub fn with_bytes_reader<T>(bytes: &[u8], f: &fn(@Reader) -> T) -> T {
// XXX XXX XXX this is glaringly unsound
// FIXME(#5723) Use a &Reader for the callback's argument. Should be:
// fn with_bytes_reader<'r, T>(bytes: &'r [u8], f: &fn(&'r Reader) -> T) -> T
let bytes: &'static [u8] = unsafe { cast::transmute(bytes) };
f(@BytesReader {
bytes: bytes,
pos: @mut 0
} as @Reader)
}

pub fn with_str_reader<T>(s: &str, f: &fn(@Reader) -> T) -> T {
// FIXME(#5723): As above.
with_bytes_reader(s.as_bytes(), f)
}

Expand Down
Loading