librustc: Implement sugar for the `FnMut` trait

This commit is contained in:
Patrick Walton 2014-06-01 18:41:46 -07:00
parent 907d961876
commit f02b6f3a8b
12 changed files with 343 additions and 46 deletions

View File

@ -57,6 +57,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
("linkage", Active),
("struct_inherit", Active),
("overloaded_calls", Active),
("unboxed_closure_sugar", Active),
("quad_precision_float", Active),
@ -291,6 +292,11 @@ impl<'a> Visitor<()> for Context<'a> {
},
ast::TyBox(_) => { self.gate_box(t.span); }
ast::TyUnboxedFn(_) => {
self.gate_feature("unboxed_closure_sugar",
t.span,
"unboxed closure trait sugar is experimental");
}
_ => {}
}

View File

@ -3856,14 +3856,20 @@ impl<'a> Resolver<'a> {
}
fn resolve_type_parameter_bound(&mut self,
id: NodeId,
type_parameter_bound: &TyParamBound) {
id: NodeId,
type_parameter_bound: &TyParamBound) {
match *type_parameter_bound {
TraitTyParamBound(ref tref) => {
self.resolve_trait_reference(id, tref, TraitBoundingTypeParameter)
}
StaticRegionTyParamBound => {}
OtherRegionTyParamBound(_) => {}
UnboxedFnTyParamBound(ref unboxed_function) => {
for argument in unboxed_function.decl.inputs.iter() {
self.resolve_type(argument.ty);
}
self.resolve_type(unboxed_function.decl.output);
}
StaticRegionTyParamBound | OtherRegionTyParamBound(_) => {}
}
}

View File

@ -51,13 +51,14 @@
use middle::const_eval;
use middle::def;
use middle::subst;
use middle::lang_items::FnMutTraitLangItem;
use middle::subst::{Subst, Substs};
use middle::ty::{ty_param_substs_and_ty};
use middle::subst;
use middle::ty::ty_param_substs_and_ty;
use middle::ty;
use middle::typeck::rscope;
use middle::typeck::rscope::{RegionScope};
use middle::typeck::lookup_def_tcx;
use middle::typeck::rscope::RegionScope;
use middle::typeck::rscope;
use util::ppaux::Repr;
use std::rc::Rc;
@ -469,6 +470,38 @@ fn ast_ty_to_mt<AC:AstConv, RS:RegionScope>(this: &AC,
ty::mt {ty: ast_ty_to_ty(this, rscope, ty), mutbl: ast::MutImmutable}
}
pub fn trait_ref_for_unboxed_function<AC:AstConv,
RS:RegionScope>(
this: &AC,
rscope: &RS,
unboxed_function: &ast::UnboxedFnTy)
-> ty::TraitRef {
let fn_mut_trait_did = this.tcx()
.lang_items
.require(FnMutTraitLangItem)
.unwrap();
let input_types =
unboxed_function.decl
.inputs
.iter()
.map(|input| {
ast_ty_to_ty(this, rscope, input.ty)
}).collect::<Vec<_>>();
let input_tuple = ty::mk_tup(this.tcx(), input_types);
let output_type = ast_ty_to_ty(this,
rscope,
unboxed_function.decl.output);
let substs = subst::Substs {
self_ty: None,
tps: vec!(input_tuple, output_type),
regions: subst::NonerasedRegions(Vec::new()),
};
ty::TraitRef {
def_id: fn_mut_trait_did,
substs: substs,
}
}
// Handle `~`, `Box`, and `&` being able to mean strs and vecs.
// If a_seq_ty is a str or a vec, make it a str/vec.
// Also handle first-class trait types.
@ -491,6 +524,32 @@ fn mk_pointer<AC:AstConv,
}
return constr(ty::mk_vec(tcx, mt, None));
}
ast::TyUnboxedFn(ref unboxed_function) => {
let trait_store = match ptr_ty {
Uniq => ty::UniqTraitStore,
RPtr(r) => {
ty::RegionTraitStore(r, a_seq_ty.mutbl)
}
_ => {
tcx.sess.span_err(
a_seq_ty.ty.span,
"~trait or &trait are the only supported \
forms of casting-to-trait");
return ty::mk_err();
}
};
let ty::TraitRef {
def_id,
substs
} = trait_ref_for_unboxed_function(this,
rscope,
*unboxed_function);
return ty::mk_trait(this.tcx(),
def_id,
substs,
trait_store,
ty::empty_builtin_bounds());
}
ast::TyPath(ref path, ref bounds, id) => {
// Note that the "bounds must be empty if path is not a trait"
// restriction is enforced in the below case for ty_path, which
@ -528,7 +587,10 @@ fn mk_pointer<AC:AstConv,
return ty::mk_err();
}
};
let bounds = conv_builtin_bounds(this.tcx(), bounds, trait_store);
let bounds = conv_builtin_bounds(this.tcx(),
path.span,
bounds,
trait_store);
return ty::mk_trait(tcx,
result.def_id,
result.substs.clone(),
@ -621,7 +683,10 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>(
// Use corresponding trait store to figure out default bounds
// if none were specified.
let bounds = conv_builtin_bounds(this.tcx(), &f.bounds, store);
let bounds = conv_builtin_bounds(this.tcx(),
ast_ty.span,
&f.bounds,
store);
let fn_decl = ty_of_closure(this,
ast_ty.id,
@ -636,7 +701,10 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>(
ast::TyProc(ref f) => {
// Use corresponding trait store to figure out default bounds
// if none were specified.
let bounds = conv_builtin_bounds(this.tcx(), &f.bounds, ty::UniqTraitStore);
let bounds = conv_builtin_bounds(this.tcx(),
ast_ty.span,
&f.bounds,
ty::UniqTraitStore);
let fn_decl = ty_of_closure(this,
ast_ty.id,
@ -648,6 +716,11 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>(
None);
ty::mk_closure(tcx, fn_decl)
}
ast::TyUnboxedFn(_) => {
tcx.sess.span_err(ast_ty.span,
"cannot use unboxed functions here");
ty::mk_err()
}
ast::TyPath(ref path, ref bounds, id) => {
let a_def = match tcx.def_map.borrow().find(&id) {
None => {
@ -891,7 +964,9 @@ pub fn ty_of_closure<AC:AstConv>(
}
}
fn conv_builtin_bounds(tcx: &ty::ctxt, ast_bounds: &Option<OwnedSlice<ast::TyParamBound>>,
fn conv_builtin_bounds(tcx: &ty::ctxt,
span: Span,
ast_bounds: &Option<OwnedSlice<ast::TyParamBound>>,
store: ty::TraitStore)
-> ty::BuiltinBounds {
//! Converts a list of bounds from the AST into a `BuiltinBounds`
@ -928,6 +1003,11 @@ fn conv_builtin_bounds(tcx: &ty::ctxt, ast_bounds: &Option<OwnedSlice<ast::TyPar
ast::StaticRegionTyParamBound => {
builtin_bounds.add(ty::BoundStatic);
}
ast::UnboxedFnTyParamBound(_) => {
tcx.sess.span_err(span,
"unboxed functions are not allowed \
here");
}
ast::OtherRegionTyParamBound(span) => {
if !tcx.sess.features.issue_5723_bootstrap.get() {
tcx.sess.span_err(

View File

@ -48,22 +48,21 @@ use middle::typeck::{CrateCtxt, lookup_def_tcx, no_params, write_ty_to_tcx};
use util::ppaux;
use util::ppaux::Repr;
use std::rc::Rc;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use syntax::abi;
use syntax::ast::{StaticRegionTyParamBound, OtherRegionTyParamBound,
TraitTyParamBound};
use syntax::ast::{StaticRegionTyParamBound, OtherRegionTyParamBound};
use syntax::ast::{TraitTyParamBound, UnboxedFnTyParamBound};
use syntax::ast;
use syntax::ast_map;
use syntax::ast_util::{local_def, split_trait_methods};
use syntax::codemap::Span;
use syntax::codemap;
use syntax::owned_slice::OwnedSlice;
use syntax::parse::token::special_idents;
use syntax::parse::token;
use syntax::print::pprust::{path_to_str};
use syntax::visit;
use syntax::owned_slice::OwnedSlice;
struct CollectItemTypesVisitor<'a> {
ccx: &'a CrateCtxt<'a>
@ -1114,6 +1113,20 @@ fn ty_generics(ccx: &CrateCtxt,
param_bounds.builtin_bounds.add(ty::BoundStatic);
}
UnboxedFnTyParamBound(ref unboxed_function) => {
let rscope = ExplicitRscope;
let mut trait_ref =
astconv::trait_ref_for_unboxed_function(
ccx,
&rscope,
unboxed_function);
let self_ty = ty::mk_param(ccx.tcx,
param_ty.idx,
param_ty.def_id);
trait_ref.substs.self_ty = Some(self_ty);
param_bounds.trait_bounds.push(Rc::new(trait_ref));
}
OtherRegionTyParamBound(span) => {
if !ccx.tcx.sess.features.issue_5723_bootstrap.get() {
ccx.tcx.sess.span_err(

View File

@ -909,6 +909,9 @@ impl<'a> Rebuilder<'a> {
match tpb {
&ast::StaticRegionTyParamBound => ast::StaticRegionTyParamBound,
&ast::OtherRegionTyParamBound(s) => ast::OtherRegionTyParamBound(s),
&ast::UnboxedFnTyParamBound(unboxed_function_type) => {
ast::UnboxedFnTyParamBound(unboxed_function_type)
}
&ast::TraitTyParamBound(ref tr) => {
let last_seg = tr.path.segments.last().unwrap();
let mut insert = Vec::new();

View File

@ -477,6 +477,10 @@ impl Clean<TyParamBound> for ast::TyParamBound {
match *self {
ast::StaticRegionTyParamBound => RegionBound,
ast::OtherRegionTyParamBound(_) => RegionBound,
ast::UnboxedFnTyParamBound(_) => {
// FIXME(pcwalton): Wrong.
RegionBound
}
ast::TraitTyParamBound(ref t) => TraitBound(t.clean()),
}
}

View File

@ -175,6 +175,7 @@ pub static DUMMY_NODE_ID: NodeId = -1;
pub enum TyParamBound {
TraitTyParamBound(TraitRef),
StaticRegionTyParamBound,
UnboxedFnTyParamBound(UnboxedFnTy),
OtherRegionTyParamBound(Span) // FIXME -- just here until work for #5723 lands
}
@ -769,6 +770,11 @@ pub struct BareFnTy {
pub decl: P<FnDecl>
}
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)]
pub struct UnboxedFnTy {
pub decl: P<FnDecl>,
}
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)]
pub enum Ty_ {
TyNil,
@ -782,6 +788,7 @@ pub enum Ty_ {
TyClosure(@ClosureTy, Option<Lifetime>),
TyProc(@ClosureTy),
TyBareFn(@BareFnTy),
TyUnboxedFn(@UnboxedFnTy),
TyTup(Vec<P<Ty>> ),
TyPath(Path, Option<OwnedSlice<TyParamBound>>, NodeId), // for #7264; see above
TyTypeof(@Expr),

View File

@ -185,6 +185,11 @@ pub trait Folder {
decl: self.fold_fn_decl(f.decl)
})
}
TyUnboxedFn(ref f) => {
TyUnboxedFn(@UnboxedFnTy {
decl: self.fold_fn_decl(f.decl),
})
}
TyTup(ref tys) => TyTup(tys.iter().map(|&ty| self.fold_ty(ty)).collect()),
TyPath(ref path, ref bounds, id) => {
let id = self.new_id(id);
@ -440,6 +445,11 @@ fn fold_ty_param_bound<T: Folder>(tpb: &TyParamBound, fld: &mut T)
match *tpb {
TraitTyParamBound(ref ty) => TraitTyParamBound(fold_trait_ref(ty, fld)),
StaticRegionTyParamBound => StaticRegionTyParamBound,
UnboxedFnTyParamBound(ref unboxed_function_type) => {
UnboxedFnTyParamBound(UnboxedFnTy {
decl: fld.fold_fn_decl(unboxed_function_type.decl),
})
}
OtherRegionTyParamBound(s) => OtherRegionTyParamBound(s)
}
}

View File

@ -52,9 +52,9 @@ use ast::{TTNonterminal, TupleVariantKind, Ty, Ty_, TyBot, TyBox};
use ast::{TypeField, TyFixedLengthVec, TyClosure, TyProc, TyBareFn};
use ast::{TyTypeof, TyInfer, TypeMethod};
use ast::{TyNil, TyParam, TyParamBound, TyPath, TyPtr, TyRptr};
use ast::{TyTup, TyU32, TyUniq, TyVec, UnUniq};
use ast::{UnnamedField, UnsafeBlock, UnsafeFn, ViewItem};
use ast::{ViewItem_, ViewItemExternCrate, ViewItemUse};
use ast::{TyTup, TyU32, TyUnboxedFn, TyUniq, TyVec, UnUniq};
use ast::{UnboxedFnTy, UnboxedFnTyParamBound, UnnamedField, UnsafeBlock};
use ast::{UnsafeFn, ViewItem, ViewItem_, ViewItemExternCrate, ViewItemUse};
use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
use ast::Visibility;
use ast;
@ -1058,15 +1058,27 @@ impl<'a> Parser<'a> {
Vec::new()
};
let inputs = if self.eat(&token::OROR) {
Vec::new()
let (is_unboxed, inputs) = if self.eat(&token::OROR) {
(false, Vec::new())
} else {
self.expect_or();
let is_unboxed = self.token == token::BINOP(token::AND) &&
self.look_ahead(1, |t| {
token::is_keyword(keywords::Mut, t)
}) &&
self.look_ahead(2, |t| *t == token::COLON);
if is_unboxed {
self.bump();
self.bump();
self.bump();
}
let inputs = self.parse_seq_to_before_or(
&token::COMMA,
|p| p.parse_arg_general(false));
self.expect_or();
inputs
(is_unboxed, inputs)
};
let (region, bounds) = self.parse_optional_ty_param_bounds(true);
@ -1079,13 +1091,19 @@ impl<'a> Parser<'a> {
variadic: false
});
TyClosure(@ClosureTy {
fn_style: fn_style,
onceness: onceness,
bounds: bounds,
decl: decl,
lifetimes: lifetimes,
}, region)
if is_unboxed {
TyUnboxedFn(@UnboxedFnTy {
decl: decl,
})
} else {
TyClosure(@ClosureTy {
fn_style: fn_style,
onceness: onceness,
bounds: bounds,
decl: decl,
lifetimes: lifetimes,
}, region)
}
}
pub fn parse_unsafety(&mut self) -> FnStyle {
@ -3345,6 +3363,41 @@ impl<'a> Parser<'a> {
})
}
fn parse_unboxed_function_type(&mut self) -> UnboxedFnTy {
let inputs = if self.eat(&token::OROR) {
Vec::new()
} else {
self.expect_or();
if self.token == token::BINOP(token::AND) &&
self.look_ahead(1, |t| {
token::is_keyword(keywords::Mut, t)
}) &&
self.look_ahead(2, |t| *t == token::COLON) {
self.bump();
self.bump();
self.bump();
}
let inputs = self.parse_seq_to_before_or(&token::COMMA,
|p| {
p.parse_arg_general(false)
});
self.expect_or();
inputs
};
let (return_style, output) = self.parse_ret_ty();
UnboxedFnTy {
decl: P(FnDecl {
inputs: inputs,
output: output,
cf: return_style,
variadic: false,
})
}
}
// matches optbounds = ( ( : ( boundseq )? )? )
// where boundseq = ( bound + boundseq ) | bound
// and bound = 'static | ty
@ -3394,6 +3447,11 @@ impl<'a> Parser<'a> {
let tref = self.parse_trait_ref();
result.push(TraitTyParamBound(tref));
}
token::BINOP(token::OR) | token::OROR => {
let unboxed_function_type =
self.parse_unboxed_function_type();
result.push(UnboxedFnTyParamBound(unboxed_function_type));
}
_ => break,
}

View File

@ -10,7 +10,7 @@
use abi;
use ast::{P, StaticRegionTyParamBound, OtherRegionTyParamBound,
TraitTyParamBound, Required, Provided};
TraitTyParamBound, UnboxedFnTyParamBound, Required, Provided};
use ast;
use ast_util;
use owned_slice::OwnedSlice;
@ -505,27 +505,64 @@ impl<'a> State<'a> {
lifetimes: f.lifetimes.clone(),
ty_params: OwnedSlice::empty()
};
try!(self.print_ty_fn(Some(f.abi), None, &None,
f.fn_style, ast::Many, f.decl, None, &None,
Some(&generics), None));
try!(self.print_ty_fn(Some(f.abi),
None,
&None,
f.fn_style,
ast::Many,
f.decl,
None,
&None,
Some(&generics),
None,
false));
}
ast::TyClosure(f, ref region) => {
let generics = ast::Generics {
lifetimes: f.lifetimes.clone(),
ty_params: OwnedSlice::empty()
};
try!(self.print_ty_fn(None, Some('&'), region, f.fn_style,
f.onceness, f.decl, None, &f.bounds,
Some(&generics), None));
try!(self.print_ty_fn(None,
Some('&'),
region,
f.fn_style,
f.onceness,
f.decl,
None,
&f.bounds,
Some(&generics),
None,
false));
}
ast::TyProc(f) => {
let generics = ast::Generics {
lifetimes: f.lifetimes.clone(),
ty_params: OwnedSlice::empty()
};
try!(self.print_ty_fn(None, Some('~'), &None, f.fn_style,
f.onceness, f.decl, None, &f.bounds,
Some(&generics), None));
try!(self.print_ty_fn(None,
Some('~'),
&None,
f.fn_style,
f.onceness,
f.decl,
None,
&f.bounds,
Some(&generics),
None,
false));
}
ast::TyUnboxedFn(f) => {
try!(self.print_ty_fn(None,
None,
&None,
ast::NormalFn,
ast::Many,
f.decl,
None,
&None,
None,
None,
true));
}
ast::TyPath(ref path, ref bounds, _) => {
try!(self.print_bounded_path(path, bounds));
@ -930,7 +967,8 @@ impl<'a> State<'a> {
Some(m.ident),
&None,
Some(&m.generics),
Some(m.explicit_self.node)));
Some(m.explicit_self.node),
false));
word(&mut self.s, ";")
}
@ -1925,6 +1963,19 @@ impl<'a> State<'a> {
try!(match *bound {
TraitTyParamBound(ref tref) => self.print_trait_ref(tref),
StaticRegionTyParamBound => word(&mut self.s, "'static"),
UnboxedFnTyParamBound(ref unboxed_function_type) => {
self.print_ty_fn(None,
None,
&None,
ast::NormalFn,
ast::Many,
unboxed_function_type.decl,
None,
&None,
None,
None,
true)
}
OtherRegionTyParamBound(_) => Ok(())
})
}
@ -2112,8 +2163,9 @@ impl<'a> State<'a> {
id: Option<ast::Ident>,
opt_bounds: &Option<OwnedSlice<ast::TyParamBound>>,
generics: Option<&ast::Generics>,
opt_explicit_self: Option<ast::ExplicitSelf_>)
-> IoResult<()> {
opt_explicit_self: Option<ast::ExplicitSelf_>,
is_unboxed: bool)
-> IoResult<()> {
try!(self.ibox(indent_unit));
// Duplicates the logic in `print_fn_header_info()`. This is because that
@ -2129,7 +2181,9 @@ impl<'a> State<'a> {
try!(self.print_fn_style(fn_style));
try!(self.print_opt_abi_and_extern_if_nondefault(opt_abi));
try!(self.print_onceness(onceness));
try!(word(&mut self.s, "fn"));
if !is_unboxed {
try!(word(&mut self.s, "fn"));
}
}
match id {
@ -2143,15 +2197,20 @@ impl<'a> State<'a> {
match generics { Some(g) => try!(self.print_generics(g)), _ => () }
try!(zerobreak(&mut self.s));
if opt_sigil == Some('&') {
if is_unboxed || opt_sigil == Some('&') {
try!(word(&mut self.s, "|"));
} else {
try!(self.popen());
}
if is_unboxed {
try!(word(&mut self.s, "&mut"));
try!(self.word_space(":"));
}
try!(self.print_fn_args(decl, opt_explicit_self));
if opt_sigil == Some('&') {
if is_unboxed || opt_sigil == Some('&') {
try!(word(&mut self.s, "|"));
} else {
if decl.variadic {

View File

@ -383,6 +383,12 @@ pub fn walk_ty<E: Clone, V: Visitor<E>>(visitor: &mut V, typ: &Ty, env: E) {
walk_lifetime_decls(visitor, &function_declaration.lifetimes,
env.clone());
}
TyUnboxedFn(ref function_declaration) => {
for argument in function_declaration.decl.inputs.iter() {
visitor.visit_ty(argument.ty, env.clone())
}
visitor.visit_ty(function_declaration.decl.output, env.clone());
}
TyPath(ref path, ref bounds, id) => {
visitor.visit_path(path, id, env.clone());
for bounds in bounds.iter() {
@ -501,6 +507,13 @@ pub fn walk_ty_param_bounds<E: Clone, V: Visitor<E>>(visitor: &mut V,
walk_trait_ref_helper(visitor, typ, env.clone())
}
StaticRegionTyParamBound => {}
UnboxedFnTyParamBound(ref function_declaration) => {
for argument in function_declaration.decl.inputs.iter() {
visitor.visit_ty(argument.ty, env.clone())
}
visitor.visit_ty(function_declaration.decl.output,
env.clone());
}
OtherRegionTyParamBound(..) => {}
}
}

View File

@ -0,0 +1,38 @@
// Copyright 2014 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.
#![feature(unboxed_closure_sugar)]
use std::ops::FnMut;
struct S;
impl FnMut<(int,),int> for S {
fn call_mut(&mut self, (x,): (int,)) -> int {
x * x
}
}
fn call_it<F:|int|->int>(mut f: F, x: int) -> int {
f.call_mut((x,)) + 3
}
fn call_box(f: &mut |&mut: int|->int, x: int) -> int {
f.call_mut((x,)) + 3
}
fn main() {
let x = call_it(S, 1);
let y = call_box(&mut S, 1);
assert!(x == 4);
assert!(y == 4);
}