librustc: Implement |A| -> B
syntax for closures and make bare fn
work
This commit is contained in:
parent
e976de32dc
commit
f27272d60f
@ -308,10 +308,11 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str {
|
||||
ident: Option<ast::Ident>,
|
||||
sig: &ty::FnSig)
|
||||
-> ~str {
|
||||
let mut s = ~"extern ";
|
||||
|
||||
s.push_str(abis.to_str());
|
||||
s.push_char(' ');
|
||||
let mut s = if abis.is_rust() {
|
||||
~""
|
||||
} else {
|
||||
format!("extern {} ", abis.to_str())
|
||||
};
|
||||
|
||||
match purity {
|
||||
ast::impure_fn => {}
|
||||
@ -331,16 +332,16 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str {
|
||||
_ => { }
|
||||
}
|
||||
|
||||
push_sig_to_str(cx, &mut s, sig);
|
||||
push_sig_to_str(cx, &mut s, '(', ')', sig);
|
||||
|
||||
return s;
|
||||
}
|
||||
fn closure_to_str(cx: ctxt, cty: &ty::ClosureTy) -> ~str
|
||||
{
|
||||
fn closure_to_str(cx: ctxt, cty: &ty::ClosureTy) -> ~str {
|
||||
let is_proc =
|
||||
(cty.sigil, cty.onceness) == (ast::OwnedSigil, ast::Once);
|
||||
let is_borrowed_closure = cty.sigil == ast::BorrowedSigil;
|
||||
|
||||
let mut s = if is_proc {
|
||||
let mut s = if is_proc || is_borrowed_closure {
|
||||
~""
|
||||
} else {
|
||||
cty.sigil.to_str()
|
||||
@ -374,23 +375,42 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str {
|
||||
}
|
||||
};
|
||||
|
||||
s.push_str("fn");
|
||||
if !is_borrowed_closure {
|
||||
s.push_str("fn");
|
||||
}
|
||||
}
|
||||
|
||||
if !cty.bounds.is_empty() {
|
||||
s.push_str(":");
|
||||
}
|
||||
s.push_str(cty.bounds.repr(cx));
|
||||
if !is_borrowed_closure {
|
||||
// Print bounds before `fn` if this is not a borrowed closure.
|
||||
if !cty.bounds.is_empty() {
|
||||
s.push_str(":");
|
||||
s.push_str(cty.bounds.repr(cx));
|
||||
}
|
||||
|
||||
push_sig_to_str(cx, &mut s, &cty.sig);
|
||||
push_sig_to_str(cx, &mut s, '(', ')', &cty.sig);
|
||||
} else {
|
||||
// Print bounds after the signature if this is a borrowed closure.
|
||||
push_sig_to_str(cx, &mut s, '|', '|', &cty.sig);
|
||||
|
||||
if is_borrowed_closure {
|
||||
if !cty.bounds.is_empty() {
|
||||
s.push_str(":");
|
||||
s.push_str(cty.bounds.repr(cx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
fn push_sig_to_str(cx: ctxt, s: &mut ~str, sig: &ty::FnSig) {
|
||||
s.push_char('(');
|
||||
fn push_sig_to_str(cx: ctxt,
|
||||
s: &mut ~str,
|
||||
bra: char,
|
||||
ket: char,
|
||||
sig: &ty::FnSig) {
|
||||
s.push_char(bra);
|
||||
let strs = sig.inputs.map(|a| fn_input_to_str(cx, *a));
|
||||
s.push_str(strs.connect(", "));
|
||||
s.push_char(')');
|
||||
s.push_char(ket);
|
||||
if ty::get(sig.output).sty != ty_nil {
|
||||
s.push_str(" -> ");
|
||||
if ty::type_is_bot(sig.output) {
|
||||
|
@ -561,6 +561,45 @@ impl Parser {
|
||||
}
|
||||
}
|
||||
|
||||
// Expect and consume a `|`. If `||` is seen, replace it with a single
|
||||
// `|` and continue. If a `|` is not seen, signal an error.
|
||||
fn expect_or(&self) {
|
||||
match *self.token {
|
||||
token::BINOP(token::OR) => self.bump(),
|
||||
token::OROR => {
|
||||
self.replace_token(token::BINOP(token::OR),
|
||||
self.span.lo + BytePos(1),
|
||||
self.span.hi)
|
||||
}
|
||||
_ => {
|
||||
let found_token = self.token_to_str(&token::BINOP(token::OR));
|
||||
self.fatal(format!("expected `{}`, found `{}`",
|
||||
found_token,
|
||||
self.this_token_to_str()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a sequence bracketed by `|` and `|`, stopping before the `|`.
|
||||
fn parse_seq_to_before_or<T>(&self,
|
||||
sep: &token::Token,
|
||||
f: &fn(&Parser) -> T)
|
||||
-> ~[T] {
|
||||
let mut first = true;
|
||||
let mut vector = ~[];
|
||||
while *self.token != token::BINOP(token::OR) &&
|
||||
*self.token != token::OROR {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
self.expect(sep)
|
||||
}
|
||||
|
||||
vector.push(f(self))
|
||||
}
|
||||
vector
|
||||
}
|
||||
|
||||
// expect and consume a GT. if a >> is seen, replace it
|
||||
// with a single > and continue. If a GT is not seen,
|
||||
// signal an error.
|
||||
@ -761,11 +800,33 @@ impl Parser {
|
||||
get_ident_interner().get(id.name)
|
||||
}
|
||||
|
||||
// is this one of the keywords that signals a closure type?
|
||||
pub fn token_is_closure_keyword(&self, tok: &token::Token) -> bool {
|
||||
token::is_keyword(keywords::Unsafe, tok) ||
|
||||
token::is_keyword(keywords::Once, tok) ||
|
||||
token::is_keyword(keywords::Fn, tok)
|
||||
// Is the current token one of the keywords that signals a bare function
|
||||
// type?
|
||||
pub fn token_is_bare_fn_keyword(&self) -> bool {
|
||||
if token::is_keyword(keywords::Fn, self.token) {
|
||||
return true
|
||||
}
|
||||
|
||||
if token::is_keyword(keywords::Unsafe, self.token) ||
|
||||
token::is_keyword(keywords::Once, self.token) {
|
||||
return self.look_ahead(1, |t| token::is_keyword(keywords::Fn, t))
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
// Is the current token one of the keywords that signals a closure type?
|
||||
pub fn token_is_closure_keyword(&self) -> bool {
|
||||
token::is_keyword(keywords::Unsafe, self.token) ||
|
||||
token::is_keyword(keywords::Once, self.token)
|
||||
}
|
||||
|
||||
// Is the current token one of the keywords that signals an old-style
|
||||
// closure type (with explicit sigil)?
|
||||
pub fn token_is_old_style_closure_keyword(&self) -> bool {
|
||||
token::is_keyword(keywords::Unsafe, self.token) ||
|
||||
token::is_keyword(keywords::Once, self.token) ||
|
||||
token::is_keyword(keywords::Fn, self.token)
|
||||
}
|
||||
|
||||
pub fn token_is_lifetime(&self, tok: &token::Token) -> bool {
|
||||
@ -786,15 +847,15 @@ impl Parser {
|
||||
pub fn parse_ty_bare_fn(&self) -> ty_ {
|
||||
/*
|
||||
|
||||
extern "ABI" [unsafe] fn <'lt> (S) -> T
|
||||
^~~~^ ^~~~~~~^ ^~~~^ ^~^ ^
|
||||
| | | | |
|
||||
| | | | Return type
|
||||
| | | Argument types
|
||||
| | Lifetimes
|
||||
| |
|
||||
| Purity
|
||||
ABI
|
||||
[extern "ABI"] [unsafe] fn <'lt> (S) -> T
|
||||
^~~~^ ^~~~~~~^ ^~~~^ ^~^ ^
|
||||
| | | | |
|
||||
| | | | Return type
|
||||
| | | Argument types
|
||||
| | Lifetimes
|
||||
| |
|
||||
| Purity
|
||||
ABI
|
||||
|
||||
*/
|
||||
|
||||
@ -828,8 +889,8 @@ impl Parser {
|
||||
|
||||
// parse a ty_closure type
|
||||
pub fn parse_ty_closure(&self,
|
||||
sigil: ast::Sigil,
|
||||
region: Option<ast::Lifetime>)
|
||||
opt_sigil: Option<ast::Sigil>,
|
||||
mut region: Option<ast::Lifetime>)
|
||||
-> ty_ {
|
||||
/*
|
||||
|
||||
@ -852,10 +913,58 @@ impl Parser {
|
||||
|
||||
let purity = self.parse_unsafety();
|
||||
let onceness = parse_onceness(self);
|
||||
self.expect_keyword(keywords::Fn);
|
||||
let bounds = self.parse_optional_ty_param_bounds();
|
||||
|
||||
let (decl, lifetimes) = self.parse_ty_fn_decl();
|
||||
let (sigil, decl, lifetimes, bounds) = match opt_sigil {
|
||||
Some(sigil) => {
|
||||
// Old-style closure syntax (`fn(A)->B`).
|
||||
self.expect_keyword(keywords::Fn);
|
||||
let bounds = self.parse_optional_ty_param_bounds();
|
||||
let (decl, lifetimes) = self.parse_ty_fn_decl();
|
||||
(sigil, decl, lifetimes, bounds)
|
||||
}
|
||||
None => {
|
||||
// New-style closure syntax (`<'lt>|A|:K -> B`).
|
||||
let lifetimes = if self.eat(&token::LT) {
|
||||
let lifetimes = self.parse_lifetimes();
|
||||
self.expect_gt();
|
||||
|
||||
// Re-parse the region here. What a hack.
|
||||
if region.is_some() {
|
||||
self.span_err(*self.last_span,
|
||||
"lifetime declarations must precede \
|
||||
the lifetime associated with a \
|
||||
closure");
|
||||
}
|
||||
region = self.parse_opt_lifetime();
|
||||
|
||||
lifetimes
|
||||
} else {
|
||||
opt_vec::Empty
|
||||
};
|
||||
|
||||
let inputs = if self.eat(&token::OROR) {
|
||||
~[]
|
||||
} else {
|
||||
self.expect_or();
|
||||
let inputs = self.parse_seq_to_before_or(
|
||||
&token::COMMA,
|
||||
|p| p.parse_arg_general(false));
|
||||
self.expect_or();
|
||||
inputs
|
||||
};
|
||||
|
||||
let bounds = self.parse_optional_ty_param_bounds();
|
||||
|
||||
let (return_style, output) = self.parse_ret_ty();
|
||||
let decl = ast::fn_decl {
|
||||
inputs: inputs,
|
||||
output: output,
|
||||
cf: return_style,
|
||||
};
|
||||
|
||||
(BorrowedSigil, decl, lifetimes, bounds)
|
||||
}
|
||||
};
|
||||
|
||||
return ty_closure(@TyClosure {
|
||||
sigil: sigil,
|
||||
@ -1120,13 +1229,23 @@ impl Parser {
|
||||
// BORROWED POINTER
|
||||
self.bump();
|
||||
self.parse_borrowed_pointee()
|
||||
} else if self.eat_keyword(keywords::Extern) {
|
||||
// EXTERN FUNCTION
|
||||
} else if self.is_keyword(keywords::Extern) ||
|
||||
self.token_is_bare_fn_keyword() {
|
||||
// BARE FUNCTION
|
||||
self.parse_ty_bare_fn()
|
||||
} else if self.token_is_closure_keyword(self.token) {
|
||||
} else if self.token_is_closure_keyword() ||
|
||||
*self.token == token::BINOP(token::OR) ||
|
||||
*self.token == token::OROR ||
|
||||
*self.token == token::LT ||
|
||||
self.token_is_lifetime(self.token) {
|
||||
// CLOSURE
|
||||
let result = self.parse_ty_closure(ast::BorrowedSigil, None);
|
||||
self.obsolete(*self.last_span, ObsoleteBareFnType);
|
||||
//
|
||||
// XXX(pcwalton): Eventually `token::LT` will not unambiguously
|
||||
// introduce a closure, once procs can have lifetime bounds. We
|
||||
// will need to refactor the grammar a little bit at that point.
|
||||
|
||||
let lifetime = self.parse_opt_lifetime();
|
||||
let result = self.parse_ty_closure(None, lifetime);
|
||||
result
|
||||
} else if self.eat_keyword(keywords::Typeof) {
|
||||
// TYPEOF
|
||||
@ -1161,12 +1280,12 @@ impl Parser {
|
||||
match *self.token {
|
||||
token::LIFETIME(*) => {
|
||||
let lifetime = self.parse_lifetime();
|
||||
return self.parse_ty_closure(sigil, Some(lifetime));
|
||||
return self.parse_ty_closure(Some(sigil), Some(lifetime));
|
||||
}
|
||||
|
||||
token::IDENT(*) => {
|
||||
if self.token_is_closure_keyword(self.token) {
|
||||
return self.parse_ty_closure(sigil, None);
|
||||
if self.token_is_old_style_closure_keyword() {
|
||||
return self.parse_ty_closure(Some(sigil), None);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -1187,8 +1306,8 @@ impl Parser {
|
||||
// look for `&'lt` or `&'foo ` and interpret `foo` as the region name:
|
||||
let opt_lifetime = self.parse_opt_lifetime();
|
||||
|
||||
if self.token_is_closure_keyword(self.token) {
|
||||
return self.parse_ty_closure(BorrowedSigil, opt_lifetime);
|
||||
if self.token_is_old_style_closure_keyword() {
|
||||
return self.parse_ty_closure(Some(BorrowedSigil), opt_lifetime);
|
||||
}
|
||||
|
||||
let mt = self.parse_mt();
|
||||
@ -4390,8 +4509,13 @@ impl Parser {
|
||||
}
|
||||
}
|
||||
|
||||
// parse a string as an ABI spec on an extern type or module
|
||||
// Parses a string as an ABI spec on an extern type or module. Consumes
|
||||
// the `extern` keyword, if one is found.
|
||||
fn parse_opt_abis(&self) -> Option<AbiSet> {
|
||||
if !self.eat_keyword(keywords::Extern) {
|
||||
return None
|
||||
}
|
||||
|
||||
match *self.token {
|
||||
token::LIT_STR(s)
|
||||
| token::LIT_STR_RAW(s, _) => {
|
||||
@ -4467,7 +4591,7 @@ impl Parser {
|
||||
});
|
||||
}
|
||||
// either a view item or an item:
|
||||
if self.eat_keyword(keywords::Extern) {
|
||||
if self.is_keyword(keywords::Extern) {
|
||||
let opt_abis = self.parse_opt_abis();
|
||||
|
||||
if self.eat_keyword(keywords::Fn) {
|
||||
|
@ -2015,20 +2015,39 @@ pub fn print_ty_fn(s: @ps,
|
||||
// function prints the sigil in the wrong place. That should be fixed.
|
||||
if opt_sigil == Some(ast::OwnedSigil) && onceness == ast::Once {
|
||||
word(s.s, "proc");
|
||||
} else {
|
||||
} else if opt_sigil == Some(ast::BorrowedSigil) {
|
||||
print_extern_opt_abis(s, opt_abis);
|
||||
for lifetime in opt_region.iter() {
|
||||
print_lifetime(s, lifetime);
|
||||
}
|
||||
print_purity(s, purity);
|
||||
print_onceness(s, onceness);
|
||||
} else {
|
||||
print_opt_abis_and_extern_if_nondefault(s, opt_abis);
|
||||
print_opt_sigil(s, opt_sigil);
|
||||
print_opt_lifetime(s, opt_region);
|
||||
print_purity(s, purity);
|
||||
print_onceness(s, onceness);
|
||||
word(s.s, "fn");
|
||||
}
|
||||
|
||||
match id { Some(id) => { word(s.s, " "); print_ident(s, id); } _ => () }
|
||||
do opt_bounds.as_ref().map |bounds| { print_bounds(s, bounds, true); };
|
||||
|
||||
if opt_sigil != Some(ast::BorrowedSigil) {
|
||||
do opt_bounds.as_ref().map |bounds| {
|
||||
print_bounds(s, bounds, true);
|
||||
};
|
||||
}
|
||||
|
||||
match generics { Some(g) => print_generics(s, g), _ => () }
|
||||
zerobreak(s.s);
|
||||
|
||||
popen(s);
|
||||
if opt_sigil == Some(ast::BorrowedSigil) {
|
||||
word(s.s, "|");
|
||||
} else {
|
||||
popen(s);
|
||||
}
|
||||
|
||||
// It is unfortunate to duplicate the commasep logic, but we want the
|
||||
// self type and the args all in the same box.
|
||||
box(s, 0u, inconsistent);
|
||||
@ -2041,7 +2060,14 @@ pub fn print_ty_fn(s: @ps,
|
||||
print_arg(s, arg);
|
||||
}
|
||||
end(s);
|
||||
pclose(s);
|
||||
|
||||
if opt_sigil == Some(ast::BorrowedSigil) {
|
||||
word(s.s, "|");
|
||||
|
||||
opt_bounds.as_ref().map(|bounds| print_bounds(s, bounds, true));
|
||||
} else {
|
||||
pclose(s);
|
||||
}
|
||||
|
||||
maybe_print_comment(s, decl.output.span.lo);
|
||||
|
||||
@ -2274,6 +2300,17 @@ pub fn print_opt_purity(s: @ps, opt_purity: Option<ast::purity>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_opt_abis_and_extern_if_nondefault(s: @ps,
|
||||
opt_abis: Option<AbiSet>) {
|
||||
match opt_abis {
|
||||
Some(abis) if !abis.is_rust() => {
|
||||
word_nbsp(s, "extern");
|
||||
word_nbsp(s, abis.to_str());
|
||||
}
|
||||
Some(_) | None => {}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn print_extern_opt_abis(s: @ps, opt_abis: Option<AbiSet>) {
|
||||
match opt_abis {
|
||||
Some(abis) => {
|
||||
|
@ -19,5 +19,5 @@ fn main() {
|
||||
}
|
||||
|
||||
f(g);
|
||||
//~^ ERROR mismatched types: expected `extern "Rust" fn(extern "Rust" fn(extern "Rust" fn()))`
|
||||
//~^ ERROR mismatched types: expected `fn(fn(fn()))`
|
||||
}
|
||||
|
13
src/test/compile-fail/closure-reform-bad.rs
Normal file
13
src/test/compile-fail/closure-reform-bad.rs
Normal file
@ -0,0 +1,13 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
fn call_bare(f: fn(&str)) {
|
||||
f("Hello ");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let string = "world!";
|
||||
let f: |&str| = |s| println(s + string);
|
||||
call_bare(f) //~ ERROR mismatched types
|
||||
}
|
||||
|
17
src/test/pretty/closure-reform-pretty.rs
Normal file
17
src/test/pretty/closure-reform-pretty.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
// pp-exact
|
||||
|
||||
fn call_it(f: proc(~str) -> ~str) { }
|
||||
|
||||
fn call_this(f: |&str|: Send) { }
|
||||
|
||||
fn call_that(f: <'a>|&'a int, &'a int|: -> int) { }
|
||||
|
||||
fn call_extern(f: fn() -> int) { }
|
||||
|
||||
fn call_abid_extern(f: extern "C" fn() -> int) { }
|
||||
|
||||
pub fn main() { }
|
||||
|
@ -14,7 +14,7 @@
|
||||
// preserved. They are needed to disambiguate `{return n+1}; - 0` from
|
||||
// `({return n+1}-0)`.
|
||||
|
||||
fn id(f: &fn() -> int) -> int { f() }
|
||||
fn id(f: || -> int) -> int { f() }
|
||||
|
||||
fn wsucc(_n: int) -> int { (do id || { 1 }) - 0 }
|
||||
fn main() { }
|
||||
|
@ -10,6 +10,6 @@
|
||||
|
||||
// pp-exact
|
||||
|
||||
fn f(f: &fn(int)) { f(10) }
|
||||
fn f(f: |int|) { f(10) }
|
||||
|
||||
fn main() { do f |i| { assert!(i == 10) } }
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
// pp-exact
|
||||
|
||||
fn from_foreign_fn(_x: extern "Rust" fn()) { }
|
||||
fn from_stack_closure(_x: &fn()) { }
|
||||
fn from_foreign_fn(_x: fn()) { }
|
||||
fn from_stack_closure(_x: ||) { }
|
||||
fn from_unique_closure(_x: ~fn()) { }
|
||||
fn main() { }
|
||||
|
@ -1,11 +1,42 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
use std::cast;
|
||||
|
||||
fn call_it(f: proc(~str) -> ~str) {
|
||||
println(f(~"Fred"))
|
||||
}
|
||||
|
||||
fn call_a_thunk(f: ||) {
|
||||
f();
|
||||
}
|
||||
|
||||
fn call_this(f: |&str|:Send) {
|
||||
f("Hello!");
|
||||
}
|
||||
|
||||
fn call_that(f: <'a>|&'a int, &'a int|: -> int) {
|
||||
let (ten, forty_two) = (10, 42);
|
||||
println!("Your lucky number is {}", f(&ten, &forty_two));
|
||||
}
|
||||
|
||||
fn call_cramped(f:||->uint,g:<'a>||->&'a uint) {
|
||||
let number = f();
|
||||
let other_number = *g();
|
||||
println!("Ticket {} wins an all-expenses-paid trip to Mountain View", number + other_number);
|
||||
}
|
||||
|
||||
fn call_bare(f: fn(&str)) {
|
||||
f("Hello world!")
|
||||
}
|
||||
|
||||
fn call_bare_again(f: extern "Rust" fn(&str)) {
|
||||
f("Goodbye world!")
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
// Procs
|
||||
|
||||
let greeting = ~"Hi ";
|
||||
do call_it |s| {
|
||||
greeting + s
|
||||
@ -23,5 +54,26 @@ pub fn main() {
|
||||
call_it(proc(s: ~str) -> ~str {
|
||||
greeting + s
|
||||
});
|
||||
|
||||
// Closures
|
||||
|
||||
call_a_thunk(|| println("Hello world!"));
|
||||
|
||||
call_this(|s| println(s));
|
||||
|
||||
call_that(|x, y| *x + *y);
|
||||
|
||||
let z = 100;
|
||||
call_that(|x, y| *x + *y - z);
|
||||
|
||||
call_cramped(|| 1, || unsafe {
|
||||
cast::transmute(&100)
|
||||
});
|
||||
|
||||
// External functions
|
||||
|
||||
call_bare(println);
|
||||
|
||||
call_bare_again(println);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user