auto merge of #6457 : nikomatsakis/rust/issue-6308-closure-bounds, r=brson

Add BuiltinBounds to closure type: parse and handle subtyping,
but do not integrate with kindck etc (requires a snapshot first)

r? @brson
This commit is contained in:
bors 2013-05-16 11:31:46 -07:00
commit cf8341fc9e
19 changed files with 261 additions and 61 deletions

View File

@ -470,12 +470,14 @@ fn parse_closure_ty(st: @mut PState, conv: conv_did) -> ty::ClosureTy {
let purity = parse_purity(next(st));
let onceness = parse_onceness(next(st));
let region = parse_region(st);
let bounds = parse_bounds(st, conv);
let sig = parse_sig(st, conv);
ty::ClosureTy {
purity: purity,
sigil: sigil,
onceness: onceness,
region: region,
bounds: bounds.builtin_bounds,
sig: sig
}
}
@ -540,10 +542,10 @@ pub fn parse_type_param_def_data(data: @~[u8], start: uint,
fn parse_type_param_def(st: @mut PState, conv: conv_did) -> ty::TypeParameterDef {
ty::TypeParameterDef {def_id: parse_def(st, NominalType, conv),
bounds: parse_bounds(st, conv)}
bounds: @parse_bounds(st, conv)}
}
fn parse_bounds(st: @mut PState, conv: conv_did) -> @ty::ParamBounds {
fn parse_bounds(st: @mut PState, conv: conv_did) -> ty::ParamBounds {
let mut param_bounds = ty::ParamBounds {
builtin_bounds: ty::EmptyBuiltinBounds(),
trait_bounds: ~[]
@ -566,7 +568,7 @@ fn parse_bounds(st: @mut PState, conv: conv_did) -> @ty::ParamBounds {
param_bounds.trait_bounds.push(@parse_trait_ref(st, conv));
}
'.' => {
return @param_bounds;
return param_bounds;
}
_ => {
fail!("parse_bounds: bad bounds")

View File

@ -380,6 +380,9 @@ fn enc_closure_ty(w: @io::Writer, cx: @ctxt, ft: &ty::ClosureTy) {
enc_purity(w, ft.purity);
enc_onceness(w, ft.onceness);
enc_region(w, cx, ft.region);
let bounds = ty::ParamBounds {builtin_bounds: ft.bounds,
trait_bounds: ~[]};
enc_bounds(w, cx, &bounds);
enc_fn_sig(w, cx, &ft.sig);
}
@ -392,7 +395,7 @@ fn enc_fn_sig(w: @io::Writer, cx: @ctxt, fsig: &ty::FnSig) {
enc_ty(w, cx, fsig.output);
}
fn enc_bounds(w: @io::Writer, cx: @ctxt, bs: @ty::ParamBounds) {
fn enc_bounds(w: @io::Writer, cx: @ctxt, bs: &ty::ParamBounds) {
for bs.builtin_bounds.each |bound| {
match bound {
ty::BoundOwned => w.write_char('S'),

View File

@ -20,6 +20,7 @@
use middle::lint::{get_lint_level, get_lint_settings_level};
use middle::pat_util::pat_bindings;
use syntax::ast::{TyParamBound, ty_closure};
use syntax::ast::{RegionTyParamBound, TraitTyParamBound, _mod, add, arm};
use syntax::ast::{binding_mode, bitand, bitor, bitxor, blk};
use syntax::ast::{bind_infer, bind_by_ref, bind_by_copy};
@ -3732,16 +3733,22 @@ fn resolve_type_parameters(@mut self,
type_parameters: &OptVec<TyParam>,
visitor: ResolveVisitor) {
for type_parameters.each |type_parameter| {
for type_parameter.bounds.each |&bound| {
match bound {
for type_parameter.bounds.each |bound| {
self.resolve_type_parameter_bound(bound, visitor);
}
}
}
fn resolve_type_parameter_bound(@mut self,
type_parameter_bound: &TyParamBound,
visitor: ResolveVisitor) {
match *type_parameter_bound {
TraitTyParamBound(tref) => {
self.resolve_trait_reference(tref, visitor)
}
RegionTyParamBound => {}
}
}
}
}
fn resolve_trait_reference(@mut self,
trait_reference: &trait_ref,
@ -4070,6 +4077,13 @@ fn resolve_type(@mut self, ty: @Ty, visitor: ResolveVisitor) {
}
}
ty_closure(c) => {
for c.bounds.each |bound| {
self.resolve_type_parameter_bound(bound, visitor);
}
visit_ty(ty, (), visitor);
}
_ => {
// Just resolve embedded types.
visit_ty(ty, (), visitor);

View File

@ -818,6 +818,7 @@ pub fn trans_intrinsic(ccx: @CrateContext,
sigil: ast::BorrowedSigil,
onceness: ast::Many,
region: ty::re_bound(ty::br_anon(0)),
bounds: ty::EmptyBuiltinBounds(),
sig: FnSig {
bound_lifetime_names: opt_vec::Empty,
inputs: ~[ star_u8 ],

View File

@ -330,6 +330,7 @@ fn normalized_closure_ty(tcx: ty::ctxt,
sigil: sigil,
onceness: ast::Many,
region: ty::re_static,
bounds: ty::EmptyBuiltinBounds(),
sig: ty::FnSig {bound_lifetime_names: opt_vec::Empty,
inputs: ~[],
output: ty::mk_nil()}})

View File

@ -22,7 +22,7 @@
use middle;
use util::ppaux::{note_and_explain_region, bound_region_to_str};
use util::ppaux::{trait_store_to_str, ty_to_str, vstore_to_str};
use util::ppaux::Repr;
use util::ppaux::{Repr, UserString};
use util::common::{indenter};
use util::enum_set::{EnumSet, CLike};
@ -390,7 +390,8 @@ pub struct ClosureTy {
sigil: ast::Sigil,
onceness: ast::Onceness,
region: Region,
sig: FnSig
bounds: BuiltinBounds,
sig: FnSig,
}
/**
@ -685,6 +686,7 @@ pub enum type_err {
terr_int_mismatch(expected_found<IntVarValue>),
terr_float_mismatch(expected_found<ast::float_ty>),
terr_traits(expected_found<ast::def_id>),
terr_builtin_bounds(expected_found<BuiltinBounds>),
}
#[deriving(Eq, IterBytes)]
@ -707,6 +709,15 @@ pub fn EmptyBuiltinBounds() -> BuiltinBounds {
EnumSet::empty()
}
pub fn AllBuiltinBounds() -> BuiltinBounds {
let mut set = EnumSet::empty();
set.add(BoundCopy);
set.add(BoundStatic);
set.add(BoundOwned);
set.add(BoundConst);
set
}
impl CLike for BuiltinBound {
pub fn to_uint(&self) -> uint {
*self as uint
@ -3169,6 +3180,7 @@ pub fn adjust_ty(cx: ctxt,
sigil: s,
onceness: ast::Many,
region: r,
bounds: ty::AllBuiltinBounds(),
sig: copy b.sig})
}
ref b => {
@ -3697,6 +3709,19 @@ fn terr_vstore_kind_to_str(k: terr_vstore_kind) -> ~str {
item_path_str(cx, values.expected),
item_path_str(cx, values.found))
}
terr_builtin_bounds(values) => {
if values.expected.is_empty() {
fmt!("expected no bounds but found `%s`",
values.found.user_string(cx))
} else if values.found.is_empty() {
fmt!("expected bounds `%s` but found no bounds",
values.expected.user_string(cx))
} else {
fmt!("expected bounds `%s` but found bounds `%s`",
values.expected.user_string(cx),
values.found.user_string(cx))
}
}
terr_self_substs => {
~"inconsistent self substitution" // XXX this is more of a bug
}

View File

@ -59,6 +59,7 @@
use middle::typeck::rscope::in_binding_rscope;
use middle::typeck::rscope::{region_scope, RegionError};
use middle::typeck::rscope::RegionParamNames;
use middle::typeck::lookup_def_tcx;
use syntax::abi::AbiSet;
use syntax::{ast, ast_util};
@ -220,7 +221,6 @@ pub fn ast_path_to_trait_ref<AC:AstConv,RS:region_scope + Copy + 'static>(
return trait_ref;
}
pub fn ast_path_to_ty<AC:AstConv,RS:region_scope + Copy + 'static>(
this: &AC,
rscope: &RS,
@ -377,11 +377,13 @@ fn check_path_args(tcx: ty::ctxt,
bf.abis, &bf.lifetimes, &bf.decl))
}
ast::ty_closure(ref f) => {
let bounds = conv_builtin_bounds(this.tcx(), &f.bounds);
let fn_decl = ty_of_closure(this,
rscope,
f.sigil,
f.purity,
f.onceness,
bounds,
f.region,
&f.decl,
None,
@ -651,6 +653,7 @@ pub fn ty_of_closure<AC:AstConv,RS:region_scope + Copy + 'static>(
sigil: ast::Sigil,
purity: ast::purity,
onceness: ast::Onceness,
bounds: ty::BuiltinBounds,
opt_lifetime: Option<@ast::Lifetime>,
decl: &ast::fn_decl,
expected_sig: Option<ty::FnSig>,
@ -713,8 +716,69 @@ pub fn ty_of_closure<AC:AstConv,RS:region_scope + Copy + 'static>(
sigil: sigil,
onceness: onceness,
region: bound_region,
bounds: bounds,
sig: ty::FnSig {bound_lifetime_names: bound_lifetime_names,
inputs: input_tys,
output: output_ty}
}
}
fn conv_builtin_bounds(tcx: ty::ctxt,
ast_bounds: &OptVec<ast::TyParamBound>)
-> 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.each |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);
}
}
}
builtin_bounds
}
pub fn try_add_builtin_trait(tcx: ty::ctxt,
trait_def_id: ast::def_id,
builtin_bounds: &mut ty::BuiltinBounds) -> bool {
//! Checks whether `trait_ref` refers to one of the builtin
//! traits, like `Copy` or `Owned`, and adds the corresponding
//! bound to the set `builtin_bounds` if so. Returns true if `trait_ref`
//! is a builtin trait.
let li = &tcx.lang_items;
if trait_def_id == li.owned_trait() {
builtin_bounds.add(ty::BoundOwned);
true
} else if trait_def_id == li.copy_trait() {
builtin_bounds.add(ty::BoundCopy);
true
} else if trait_def_id == li.const_trait() {
builtin_bounds.add(ty::BoundConst);
true
} else {
false
}
}

View File

@ -1661,7 +1661,8 @@ fn check_expr_fn(fcx: @mut FnCtxt,
let (expected_sig,
expected_purity,
expected_sigil,
expected_onceness) = {
expected_onceness,
expected_bounds) = {
match expected_sty {
Some(ty::ty_closure(ref cenv)) => {
let id = expr.id;
@ -1669,11 +1670,13 @@ fn check_expr_fn(fcx: @mut FnCtxt,
replace_bound_regions_in_fn_sig(
tcx, @Nil, None, &cenv.sig,
|br| ty::re_bound(ty::br_cap_avoid(id, @br)));
(Some(sig), cenv.purity, cenv.sigil, cenv.onceness)
(Some(sig), cenv.purity, cenv.sigil,
cenv.onceness, cenv.bounds)
}
_ => {
// Not an error! Means we're inferring the closure type
(None, ast::impure_fn, ast::BorrowedSigil, ast::Many)
(None, ast::impure_fn, ast::BorrowedSigil,
ast::Many, ty::EmptyBuiltinBounds())
}
}
};
@ -1691,6 +1694,7 @@ fn check_expr_fn(fcx: @mut FnCtxt,
sigil,
purity,
expected_onceness,
expected_bounds,
None,
decl,
expected_sig,
@ -3526,6 +3530,7 @@ fn param(ccx: @mut CrateCtxt, n: uint) -> ty::t {
sigil: ast::BorrowedSigil,
onceness: ast::Once,
region: ty::re_bound(ty::br_anon(0)),
bounds: ty::EmptyBuiltinBounds(),
sig: ty::FnSig {
bound_lifetime_names: opt_vec::Empty,
inputs: ~[ty::mk_imm_ptr(ccx.tcx, ty::mk_mach_uint(ast::ty_u8))],

View File

@ -1207,25 +1207,21 @@ fn compute_bounds(
builtin_bounds: ty::EmptyBuiltinBounds(),
trait_bounds: ~[]
};
for ast_bounds.each |b| {
match b {
&TraitTyParamBound(b) => {
let li = &ccx.tcx.lang_items;
for ast_bounds.each |ast_bound| {
match *ast_bound {
TraitTyParamBound(b) => {
let ty = ty::mk_param(ccx.tcx, param_ty.idx, param_ty.def_id);
let trait_ref = instantiate_trait_ref(ccx, b, rp, generics, ty);
if trait_ref.def_id == li.owned_trait() {
param_bounds.builtin_bounds.add(ty::BoundOwned);
} else if trait_ref.def_id == li.copy_trait() {
param_bounds.builtin_bounds.add(ty::BoundCopy);
} else if trait_ref.def_id == li.const_trait() {
param_bounds.builtin_bounds.add(ty::BoundConst);
} else {
if !astconv::try_add_builtin_trait(
ccx.tcx, trait_ref.def_id,
&mut param_bounds.builtin_bounds)
{
// Must be a user-defined trait
param_bounds.trait_bounds.push(trait_ref);
}
}
&RegionTyParamBound => {
RegionTyParamBound => {
param_bounds.builtin_bounds.add(ty::BoundStatic);
}
}

View File

@ -56,6 +56,7 @@
use middle::ty::{FloatVar, FnSig, IntVar, TyVar};
use middle::ty::{IntType, UintType, substs};
use middle::ty::{BuiltinBounds};
use middle::ty;
use middle::typeck::infer::glb::Glb;
use middle::typeck::infer::lub::Lub;
@ -100,6 +101,7 @@ fn closure_tys(&self, a: &ty::ClosureTy,
fn purities(&self, a: purity, b: purity) -> cres<purity>;
fn abis(&self, a: AbiSet, b: AbiSet) -> cres<AbiSet>;
fn oncenesses(&self, a: Onceness, b: Onceness) -> cres<Onceness>;
fn bounds(&self, a: BuiltinBounds, b: BuiltinBounds) -> cres<BuiltinBounds>;
fn contraregions(&self, a: ty::Region, b: ty::Region)
-> cres<ty::Region>;
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region>;
@ -372,11 +374,13 @@ pub fn super_closure_tys<C:Combine>(
let r = if_ok!(this.contraregions(a_f.region, b_f.region));
let purity = if_ok!(this.purities(a_f.purity, b_f.purity));
let onceness = if_ok!(this.oncenesses(a_f.onceness, b_f.onceness));
let bounds = if_ok!(this.bounds(a_f.bounds, b_f.bounds));
let sig = if_ok!(this.fn_sigs(&a_f.sig, &b_f.sig));
Ok(ty::ClosureTy {purity: purity,
sigil: p,
onceness: onceness,
region: r,
bounds: bounds,
sig: sig})
}

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use middle::ty::{BuiltinBounds};
use middle::ty::RegionVid;
use middle::ty;
use middle::typeck::infer::combine::*;
@ -114,6 +115,12 @@ fn oncenesses(&self, a: Onceness, b: Onceness) -> cres<Onceness> {
}
}
fn bounds(&self, a: BuiltinBounds, b: BuiltinBounds) -> cres<BuiltinBounds> {
// More bounds is a subtype of fewer bounds, so
// the GLB (mutual subtype) is the union.
Ok(a.union(b))
}
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
debug!("%s.regions(%?, %?)",
self.tag(),

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use middle::ty::{BuiltinBounds};
use middle::ty::RegionVid;
use middle::ty;
use middle::typeck::infer::combine::*;
@ -100,6 +101,12 @@ fn oncenesses(&self, a: Onceness, b: Onceness) -> cres<Onceness> {
}
}
fn bounds(&self, a: BuiltinBounds, b: BuiltinBounds) -> cres<BuiltinBounds> {
// More bounds is a subtype of fewer bounds, so
// the LUB (mutual supertype) is the intersection.
Ok(a.intersection(b))
}
fn contraregions(&self, a: ty::Region, b: ty::Region)
-> cres<ty::Region> {
return Glb(**self).regions(a, b);

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use middle::ty::{BuiltinBounds};
use middle::ty;
use middle::ty::TyVar;
use middle::typeck::check::regionmanip::replace_bound_regions_in_fn_sig;
@ -99,6 +100,19 @@ fn oncenesses(&self, a: Onceness, b: Onceness) -> cres<Onceness> {
})
}
fn bounds(&self, a: BuiltinBounds, b: BuiltinBounds) -> cres<BuiltinBounds> {
// More bounds is a subtype of fewer bounds.
//
// e.g., fn:Copy() <: fn(), because the former is a function
// that only closes over copyable things, but the latter is
// any function at all.
if a.contains(b) {
Ok(a)
} else {
Err(ty::terr_builtin_bounds(expected_found(self, a, b)))
}
}
fn tys(&self, a: ty::t, b: ty::t) -> cres<ty::t> {
debug!("%s.tys(%s, %s)", self.tag(),
a.inf_str(self.infcx), b.inf_str(self.infcx));

View File

@ -12,7 +12,9 @@
#[deriving(Eq, IterBytes)]
pub struct EnumSet<E> {
bits: uint
// We must maintain the invariant that no bits are set
// for which no variant exists
priv bits: uint
}
pub trait CLike {
@ -37,10 +39,18 @@ fn intersects(&self, e: EnumSet<E>) -> bool {
(self.bits & e.bits) != 0
}
fn intersection(&self, e: EnumSet<E>) -> EnumSet<E> {
EnumSet {bits: self.bits & e.bits}
}
fn contains(&self, e: EnumSet<E>) -> bool {
(self.bits & e.bits) == e.bits
}
fn union(&self, e: EnumSet<E>) -> EnumSet<E> {
EnumSet {bits: self.bits | e.bits}
}
fn add(&mut self, e: E) {
self.bits |= bit(e);
}

View File

@ -828,7 +828,8 @@ pub struct TyClosure {
lifetimes: OptVec<Lifetime>,
purity: purity,
onceness: Onceness,
decl: fn_decl
decl: fn_decl,
bounds: OptVec<TyParamBound>
}
#[deriving(Eq, Encodable, Decodable)]

View File

@ -589,6 +589,7 @@ fn fold_field(f: ty_field, fld: @ast_fold) -> ty_field {
purity: f.purity,
region: f.region,
onceness: f.onceness,
bounds: f.bounds.map(|x| fold_ty_param_bound(x, fld)),
decl: fold_fn_decl(&f.decl, fld),
lifetimes: copy f.lifetimes,
})

View File

@ -395,12 +395,13 @@ fn parse_ty_closure(&self,
-> ty_ {
/*
(&|~|@) ['r] [pure|unsafe] [once] fn <'lt> (S) -> T
^~~~~~^ ^~~^ ^~~~~~~~~~~~^ ^~~~~^ ^~~~^ ^~^ ^
| | | | | | |
| | | | | | Return type
| | | | | Argument types
| | | | Lifetimes
(&|~|@) ['r] [pure|unsafe] [once] fn [:Bounds] <'lt> (S) -> T
^~~~~~^ ^~~^ ^~~~~~~~~~~~^ ^~~~~^ ^~~~~~~~^ ^~~~^ ^~^ ^
| | | | | | | |
| | | | | | | Return type
| | | | | | Argument types
| | | | | Lifetimes
| | | | Closure bounds
| | | Once-ness (a.k.a., affine)
| | Purity
| Lifetime bound
@ -414,6 +415,7 @@ fn parse_ty_closure(&self,
let purity = self.parse_unsafety();
let onceness = parse_onceness(self);
self.expect_keyword("fn");
let bounds = self.parse_optional_ty_param_bounds();
if self.parse_fn_ty_sigil().is_some() {
self.obsolete(*self.span, ObsoletePostFnTySigil);
@ -426,6 +428,7 @@ fn parse_ty_closure(&self,
region: region,
purity: purity,
onceness: onceness,
bounds: bounds,
decl: decl,
lifetimes: lifetimes,
});
@ -2851,9 +2854,9 @@ fn parse_optional_onceness(&self) -> ast::Onceness {
// matches optbounds = ( ( : ( boundseq )? )? )
// where boundseq = ( bound + boundseq ) | bound
// and bound = 'static | ty
fn parse_optional_ty_param_bounds(&self) -> @OptVec<TyParamBound> {
fn parse_optional_ty_param_bounds(&self) -> OptVec<TyParamBound> {
if !self.eat(&token::COLON) {
return @opt_vec::Empty;
return opt_vec::Empty;
}
let mut result = opt_vec::Empty;
@ -2907,13 +2910,13 @@ fn parse_optional_ty_param_bounds(&self) -> @OptVec<TyParamBound> {
}
}
return @result;
return 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 bounds = @self.parse_optional_ty_param_bounds();
ast::TyParam { ident: ident, id: self.get_id(), bounds: bounds }
}

View File

@ -0,0 +1,8 @@
trait Foo {}
fn take(f: &fn:Foo()) {
//~^ ERROR only the builtin traits can be used as closure or object bounds
}
fn main() {}

View File

@ -0,0 +1,34 @@
fn take_any(_: &fn()) {
}
fn take_copyable(_: &fn:Copy()) {
}
fn take_copyable_owned(_: &fn:Copy+Owned()) {
}
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
}
fn give_copyable(f: &fn:Copy()) {
take_any(f);
take_copyable(f);
take_copyable_owned(f); //~ ERROR expected bounds `Copy+Owned` but found bounds `Copy`
}
fn give_owned(f: &fn:Owned()) {
take_any(f);
take_copyable(f); //~ ERROR expected bounds `Copy` but found bounds `Owned`
take_copyable_owned(f); //~ ERROR expected bounds `Copy+Owned` but found bounds `Owned`
}
fn give_copyable_owned(f: &fn:Copy+Owned()) {
take_any(f);
take_copyable(f);
take_copyable_owned(f);
}
fn main() {}