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));
}
// 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 =
}
}
- 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));
}
};
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))
}
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,
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,
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"),
_ => { },
}
}
-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
//! 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,
purity: purity,
onceness: Onceness,
decl: fn_decl,
- bounds: OptVec<TyParamBound>
+ // Optional optvec distinguishes between "fn()" and "fn:()" so we can
+ // implement issue #7264. None means "fn()", which means infer a default
+ // bound based on pointer sigil during typeck. Some(Empty) means "fn:()",
+ // which means use no bounds (e.g., not even Owned on a ~fn()).
+ bounds: Option<OptVec<TyParamBound>>,
}
#[deriving(Eq, Encodable, Decodable)]
ty_closure(@TyClosure),
ty_bare_fn(@TyBareFn),
ty_tup(~[@Ty]),
- ty_path(@Path, @OptVec<TyParamBound>, node_id),
+ ty_path(@Path, @Option<OptVec<TyParamBound>>, node_id), // for #7264; see above
ty_mac(mac),
// ty_infer means the type should be inferred instead of it having been
// specified. This should only appear at the "top level" of a type and not
fn ty_mt(&self, ty: @ast::Ty, mutbl: ast::mutability) -> ast::mt;
fn ty(&self, span: span, ty: ast::ty_) -> @ast::Ty;
- fn ty_path(&self, @ast::Path, @OptVec<ast::TyParamBound>) -> @ast::Ty;
+ fn ty_path(&self, @ast::Path, @Option<OptVec<ast::TyParamBound>>) -> @ast::Ty;
fn ty_ident(&self, span: span, idents: ast::ident) -> @ast::Ty;
fn ty_rptr(&self, span: span,
}
}
- fn ty_path(&self, path: @ast::Path, bounds: @OptVec<ast::TyParamBound>)
+ fn ty_path(&self, path: @ast::Path, bounds: @Option<OptVec<ast::TyParamBound>>)
-> @ast::Ty {
self.ty(path.span,
ast::ty_path(path, bounds, self.next_id()))
// to generate a bounded existential trait type.
fn ty_ident(&self, span: span, ident: ast::ident)
-> @ast::Ty {
- self.ty_path(self.path_ident(span, ident), @opt_vec::Empty)
+ self.ty_path(self.path_ident(span, ident), @None)
}
fn ty_rptr(&self,
],
None,
~[ ty ]),
- @opt_vec::Empty)
+ @None)
}
fn ty_field_imm(&self, span: span, name: ident, ty: @ast::Ty) -> ast::ty_field {
fn ty_vars_global(&self, ty_params: &OptVec<ast::TyParam>) -> ~[@ast::Ty] {
opt_vec::take_vec(
ty_params.map(|p| self.ty_path(
- self.path_global(dummy_sp(), ~[p.ident]), @opt_vec::Empty)))
+ self.path_global(dummy_sp(), ~[p.ident]), @None)))
}
fn strip_bounds(&self, generics: &Generics) -> Generics {
// Create the type of `self`.
let self_type = cx.ty_path(cx.path_all(span, false, ~[ type_ident ], self_lifetime,
opt_vec::take_vec(self_ty_params)),
- @opt_vec::Empty);
+ @None);
let doc_attr = cx.attribute(
span,
self_generics: &Generics)
-> @ast::Ty {
cx.ty_path(self.to_path(cx, span,
- self_ty, self_generics), @opt_vec::Empty)
+ self_ty, self_generics), @None)
}
pub fn to_path(&self,
cx: @ExtCtxt,
Literal(ref p) => { p.to_ty(cx, span, self_ty, self_generics) }
Self => {
cx.ty_path(self.to_path(cx, span, self_ty, self_generics),
- @opt_vec::Empty)
+ @None)
}
Tuple(ref fields) => {
let ty = if fields.is_empty() {
let pipe_ty = cx.ty_path(
path(~[this.data_name()], span)
- .add_tys(cx.ty_vars(&this.generics.ty_params)), @opt_vec::Empty);
+ .add_tys(cx.ty_vars(&this.generics.ty_params)), @None);
let args_ast = vec::append(
~[cx.arg(span, cx.ident_of("pipe"), pipe_ty)],
args_ast);
let mut rty = cx.ty_path(path(~[next.data_name()],
span)
- .add_tys(copy next_state.tys), @opt_vec::Empty);
+ .add_tys(copy next_state.tys), @None);
if try {
rty = cx.ty_option(rty);
}
cx.ty_path(
path(~[this.data_name()], span)
.add_tys(cx.ty_vars(
- &this.generics.ty_params)), @opt_vec::Empty))],
+ &this.generics.ty_params)), @None))],
args_ast);
let message_args = if arg_names.len() == 0 {
fn to_ty(&mut self, cx: @ExtCtxt) -> @ast::Ty {
cx.ty_path(path(~[cx.ident_of(self.name())], self.span())
- .add_tys(cx.ty_vars(&self.get_generics().ty_params)), @opt_vec::Empty)
+ .add_tys(cx.ty_vars(&self.get_generics().ty_params)), @None)
}
}
cx.ty_path(
path(~[cx.ident_of(dir),
cx.ident_of(next_name)], span)
- .add_tys(copy next_state.tys), @opt_vec::Empty))
+ .add_tys(copy next_state.tys), @None))
}
None => tys
};
self.data_name()],
dummy_sp())
.add_tys(cx.ty_vars(
- &self.generics.ty_params)), @opt_vec::Empty)),
- @opt_vec::Empty),
+ &self.generics.ty_params)), @None)),
+ @None),
cx.strip_bounds(&self.generics)));
}
else {
self.data_name()],
dummy_sp())
.add_tys(cx.ty_vars_global(
- &self.generics.ty_params)), @opt_vec::Empty),
- self.proto.buffer_ty_path(cx)]), @opt_vec::Empty),
+ &self.generics.ty_params)), @None),
+ self.proto.buffer_ty_path(cx)]), @None),
cx.strip_bounds(&self.generics)));
};
items
cx.ty_path(path(~[cx.ident_of("super"),
cx.ident_of("__Buffer")],
copy self.span)
- .add_tys(cx.ty_vars_global(¶ms)), @opt_vec::Empty)
+ .add_tys(cx.ty_vars_global(¶ms)), @None)
}
fn gen_buffer_type(&self, cx: @ExtCtxt) -> @ast::item {
use ext::base::ExtCtxt;
use ext::build::AstBuilder;
use ext::pipes::ast_builder::{append_types, path};
-use opt_vec;
#[deriving(Eq)]
pub enum direction { send, recv }
pub fn to_ty(&self, cx: @ExtCtxt) -> @ast::Ty {
cx.ty_path
(path(~[cx.ident_of(self.name)],self.span).add_tys(
- cx.ty_vars(&self.generics.ty_params)), @opt_vec::Empty)
+ cx.ty_vars(&self.generics.ty_params)), @None)
}
/// Iterate over the states that can be reached in one message
span: fld.new_span(f.span),
}
}
+ fn fold_opt_bounds(b: &Option<OptVec<TyParamBound>>, fld: @ast_fold)
+ -> Option<OptVec<TyParamBound>> {
+ do b.map |bounds| {
+ do bounds.map |bound| { fold_ty_param_bound(bound, fld) }
+ }
+ }
match *t {
ty_nil | ty_bot | ty_infer => copy *t,
ty_box(ref mt) => ty_box(fold_mt(mt, fld)),
purity: f.purity,
region: f.region,
onceness: f.onceness,
- bounds: f.bounds.map(|x| fold_ty_param_bound(x, fld)),
+ bounds: fold_opt_bounds(&f.bounds, fld),
decl: fold_fn_decl(&f.decl, fld),
lifetimes: copy f.lifetimes,
})
}
ty_tup(ref tys) => ty_tup(tys.map(|ty| fld.fold_ty(*ty))),
ty_path(path, bounds, id) =>
- ty_path(fld.fold_path(path),
- @bounds.map(|x| fold_ty_param_bound(x, fld)), fld.new_id(id)),
+ ty_path(fld.fold_path(path), @fold_opt_bounds(bounds, fld), fld.new_id(id)),
ty_fixed_length_vec(ref mt, e) => {
ty_fixed_length_vec(
fold_mt(mt, fld),
idents:~[str_to_ident("int")],
rp: None,
types: ~[]},
- @opt_vec::Empty, 2),
+ @None, 2),
span:sp(4,7)},
pat: @ast::pat{id:1,
node: ast::pat_ident(ast::bind_infer,
idents:~[str_to_ident("int")],
rp: None,
types: ~[]},
- @opt_vec::Empty, 2),
+ @None, 2),
span:sp(10,13)},
pat: @ast::pat{id:1, // fixme
node: ast::pat_ident(
// Like the above, but can also parse kind bounds in the case of a
// path to be used as a type that might be a trait.
- pub fn parse_type_path(&self) -> (@ast::Path, OptVec<TyParamBound>) {
- let mut bounds = opt_vec::Empty;
+ pub fn parse_type_path(&self) -> (@ast::Path, Option<OptVec<TyParamBound>>) {
+ let mut bounds = None;
let path = self.parse_bounded_path_with_tps(false, Some(|| {
// Note: this closure might not even get called in the case of a
// macro-generated path. But that's the macro parser's job.
// matches optbounds = ( ( : ( boundseq )? )? )
// where boundseq = ( bound + boundseq ) | bound
// and bound = 'static | ty
- fn parse_optional_ty_param_bounds(&self) -> OptVec<TyParamBound> {
+ // Returns "None" if there's no colon (e.g. "T");
+ // Returns "Some(Empty)" if there's a colon but nothing after (e.g. "T:")
+ // Returns "Some(stuff)" otherwise (e.g. "T:stuff").
+ // NB: The None/Some distinction is important for issue #7264.
+ fn parse_optional_ty_param_bounds(&self) -> Option<OptVec<TyParamBound>> {
if !self.eat(&token::COLON) {
- return opt_vec::Empty;
+ return None;
}
let mut result = opt_vec::Empty;
}
}
- return result;
+ return Some(result);
}
// matches typaram = IDENT optbounds
fn parse_ty_param(&self) -> TyParam {
let ident = self.parse_ident();
- let bounds = @self.parse_optional_ty_param_bounds();
+ let opt_bounds = self.parse_optional_ty_param_bounds();
+ // For typarams we don't care about the difference b/w "<T>" and "<T:>".
+ let bounds = @opt_bounds.get_or_default(opt_vec::Empty);
ast::TyParam { ident: ident, id: self.get_id(), bounds: bounds }
}
let opt_trait = if could_be_trait && self.eat_keyword(keywords::For) {
// New-style trait. Reinterpret the type as a trait.
let opt_trait_ref = match ty.node {
- ty_path(path, @opt_vec::Empty, node_id) => {
+ ty_path(path, @None, node_id) => {
Some(@trait_ref {
path: path,
ref_id: node_id
}
fn print_path_(s: @ps, path: @ast::Path, colons_before_params: bool,
- opt_bounds: Option<@OptVec<ast::TyParamBound>>) {
+ opt_bounds: &Option<OptVec<ast::TyParamBound>>) {
maybe_print_comment(s, path.span.lo);
if path.global { word(s.s, "::"); }
let mut first = true;
if first { first = false; } else { word(s.s, "::"); }
print_ident(s, *id);
}
- do opt_bounds.map_consume |bounds| {
+ do opt_bounds.map |bounds| {
print_bounds(s, bounds);
};
if path.rp.is_some() || !path.types.is_empty() {
}
pub fn print_path(s: @ps, path: @ast::Path, colons_before_params: bool) {
- print_path_(s, path, colons_before_params, None)
+ print_path_(s, path, colons_before_params, &None)
}
pub fn print_bounded_path(s: @ps, path: @ast::Path,
- bounds: @OptVec<ast::TyParamBound>) {
- print_path_(s, path, false, Some(bounds))
+ bounds: &Option<OptVec<ast::TyParamBound>>) {
+ print_path_(s, path, false, bounds)
}
pub fn print_irrefutable_pat(s: @ps, pat: @ast::pat) {
maybe_print_comment(s, decl.output.span.lo);
}
-pub fn print_bounds(s: @ps, bounds: @OptVec<ast::TyParamBound>) {
+pub fn print_bounds(s: @ps, bounds: &OptVec<ast::TyParamBound>) {
if !bounds.is_empty() {
word(s.s, ":");
let mut first = true;
ty_closure(ref f) => {
for f.decl.inputs.iter().advance |a| { (v.visit_ty)(a.ty, (copy e, v)); }
(v.visit_ty)(f.decl.output, (copy e, v));
- visit_ty_param_bounds(&f.bounds, (e, v));
+ do f.bounds.map |bounds| {
+ visit_ty_param_bounds(bounds, (copy e, v));
+ };
},
ty_bare_fn(ref f) => {
for f.decl.inputs.iter().advance |a| { (v.visit_ty)(a.ty, (copy e, v)); }
},
ty_path(p, bounds, _) => {
visit_path(p, (copy e, v));
- visit_ty_param_bounds(bounds, (e, v));
+ do bounds.map |bounds| {
+ visit_ty_param_bounds(bounds, (copy e, v));
+ };
},
ty_fixed_length_vec(ref mt, ex) => {
(v.visit_ty)(mt.ty, (copy e, v));
field: @fn:Copy(),
}
-fn foo(blk: @fn()) -> X {
+fn foo(blk: @fn:()) -> X {
return X { field: blk }; //~ ERROR expected bounds `Copy` but found no bounds
}
-fn take_any(_: &fn()) {
+fn take_any(_: &fn:()) {
}
fn take_copyable(_: &fn:Copy()) {
fn take_const_owned(_: &fn:Const+Owned()) {
}
-fn give_any(f: &fn()) {
+fn give_any(f: &fn:()) {
take_any(f);
take_copyable(f); //~ ERROR expected bounds `Copy` but found no bounds
take_copyable_owned(f); //~ ERROR expected bounds `Copy+Owned` but found no bounds
b(x); //~ ERROR expected bounds `Copy+Owned`
}
-fn d(x: ~Foo) {
+fn d(x: ~Foo:) {
a(x); //~ ERROR found no bounds
}
--- /dev/null
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Tests for "default" bounds inferred for traits with no bounds list.
+
+trait Foo {
+}
+
+fn a(_x: ~Foo) { // should be same as ~Foo:Owned
+}
+
+fn b(_x: @Foo) { // should be same as ~Foo:'static
+}
+
+fn c(_x: &'static Foo) { // should be same as &'static Foo:'static
+}
+
+fn d(x: ~Foo:Const) {
+ a(x); //~ ERROR expected bounds `Owned`
+}
+
+fn e(x: @Foo:Const) {
+ b(x); //~ ERROR expected bounds `'static`
+}
+
+fn f(x: &'static Foo:Const) {
+ c(x); //~ ERROR expected bounds `'static`
+}
+
+fn main() { }
// Tests correct copying of heap closures' environments.
-fn foo(x: ~fn:Copy()) -> (~fn(), ~fn()) {
+fn foo(x: ~fn:Copy()) -> (~fn:(), ~fn:()) {
(copy x, x)
}
fn main() {
fn bar<T: Copy>(x: T) -> (T, T) {
(copy x, x)
}
-fn foo(x: ~fn:Copy()) -> (~fn(), ~fn()) {
+fn foo(x: ~fn:Copy()) -> (~fn:(), ~fn:()) {
bar(x)
}
fn main() {
fn get(&self) -> A { copy **self }
}
-fn repeater<A:Copy>(v: @A) -> @repeat<A> {
+fn repeater<A:Copy>(v: @A) -> @repeat:<A> {
// Note: owned kind is not necessary as A appears in the trait type
- @v as @repeat<A> // No
+ @v as @repeat:<A> // No
}
pub fn main() {
}
}
-fn get_v(gc: @get_ctxt) -> uint {
+fn get_v(gc: @get_ctxt:) -> uint {
gc.get_ctxt().v
}
let ctxt = Ctxt { v: 22 };
let hc = HasCtxt { c: &ctxt };
- assert_eq!(get_v(@hc as @get_ctxt), 22);
+ assert_eq!(get_v(@hc as @get_ctxt:), 22);
}
trait Foo {
}
-fn a(_x: ~Foo) {
+fn a(_x: ~Foo:) {
}
fn b(_x: ~Foo:Owned) {
b(x);
}
+fn e(x: ~Foo) { // sugar for ~Foo:Owned
+ b(x);
+}
+
fn main() { }