about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2014-07-23 19:57:30 -0700
committerAlex Crichton <alex@alexcrichton.com>2014-07-24 07:26:22 -0700
commit3550068b531703bc492b0f97331c6a2bcafecf37 (patch)
tree4f804314e83b3ed0f59dc56bd3e8405de3b353b2
parent31ac8a90f1fbe66c3ad34ef0e5f48bc5f7026059 (diff)
downloadrust-3550068b531703bc492b0f97331c6a2bcafecf37.tar.gz
rust-3550068b531703bc492b0f97331c6a2bcafecf37.zip
librustc: Make bare functions implement the `FnMut` trait.
This is done entirely in the libraries for functions up to 16 arguments.
A macro is used so that more arguments can be easily added if we need.
Note that I had to adjust the overloaded call algorithm to not try
calling the overloaded call operator if the callee is a built-in
function type, to prevent loops.

Closes #15448.
-rw-r--r--src/libcore/ops.rs34
-rw-r--r--src/librustc/middle/typeck/check/mod.rs7
-rw-r--r--src/libsyntax/parse/parser.rs8
-rw-r--r--src/test/run-pass/bare-fn-implements-fn-mut.rs37
4 files changed, 85 insertions, 1 deletions
diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs
index 0ebb6e94b9a..839243970ac 100644
--- a/src/libcore/ops.rs
+++ b/src/libcore/ops.rs
@@ -769,3 +769,37 @@ pub trait FnOnce<Args,Result> {
     fn call_once(self, args: Args) -> Result;
 }
 
+macro_rules! def_fn_mut(
+    ($($args:ident)*) => (
+        #[cfg(not(stage0))]
+        impl<Result$(,$args)*>
+        FnMut<($($args,)*),Result>
+        for extern "Rust" fn($($args: $args,)*) -> Result {
+            #[rust_call_abi_hack]
+            #[allow(uppercase_variables)]
+            fn call_mut(&mut self, args: ($($args,)*)) -> Result {
+                let ($($args,)*) = args;
+                (*self)($($args,)*)
+            }
+        }
+    )
+)
+
+def_fn_mut!()
+def_fn_mut!(A0)
+def_fn_mut!(A0 A1)
+def_fn_mut!(A0 A1 A2)
+def_fn_mut!(A0 A1 A2 A3)
+def_fn_mut!(A0 A1 A2 A3 A4)
+def_fn_mut!(A0 A1 A2 A3 A4 A5)
+def_fn_mut!(A0 A1 A2 A3 A4 A5 A6)
+def_fn_mut!(A0 A1 A2 A3 A4 A5 A6 A7)
+def_fn_mut!(A0 A1 A2 A3 A4 A5 A6 A7 A8)
+def_fn_mut!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9)
+def_fn_mut!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10)
+def_fn_mut!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11)
+def_fn_mut!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12)
+def_fn_mut!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13)
+def_fn_mut!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14)
+def_fn_mut!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15)
+
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 557fd522fa9..39e79040e83 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -1541,6 +1541,13 @@ fn try_overloaded_call(fcx: &FnCtxt,
                        callee_type: ty::t,
                        args: &[Gc<ast::Expr>])
                        -> bool {
+    // Bail out if the callee is a bare function or a closure. We check those
+    // manually.
+    match *structure_of(fcx, callee.span, callee_type) {
+        ty::ty_bare_fn(_) | ty::ty_closure(_) => return false,
+        _ => {}
+    }
+
     // Try `FnOnce`, then `FnMut`, then `Fn`.
     for &(maybe_function_trait, method_name) in [
         (fcx.tcx().lang_items.fn_once_trait(), token::intern("call_once")),
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index ac363163673..0116518d537 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -4045,7 +4045,8 @@ impl<'a> Parser<'a> {
 
     /// Parse a method in a trait impl, starting with `attrs` attributes.
     pub fn parse_method(&mut self,
-                    already_parsed_attrs: Option<Vec<Attribute>>) -> Gc<Method> {
+                        already_parsed_attrs: Option<Vec<Attribute>>)
+                        -> Gc<Method> {
         let next_attrs = self.parse_outer_attributes();
         let attrs = match already_parsed_attrs {
             Some(mut a) => { a.push_all_move(next_attrs); a }
@@ -4083,6 +4084,11 @@ impl<'a> Parser<'a> {
                 let visa = self.parse_visibility();
                 let abi = if self.eat_keyword(keywords::Extern) {
                     self.parse_opt_abi().unwrap_or(abi::C)
+                } else if attr::contains_name(attrs.as_slice(),
+                                              "rust_call_abi_hack") {
+                    // FIXME(stage0, pcwalton): Remove this awful hack after a
+                    // snapshot, and change to `extern "rust-call" fn`.
+                    abi::RustCall
                 } else {
                     abi::Rust
                 };
diff --git a/src/test/run-pass/bare-fn-implements-fn-mut.rs b/src/test/run-pass/bare-fn-implements-fn-mut.rs
new file mode 100644
index 00000000000..37c551734de
--- /dev/null
+++ b/src/test/run-pass/bare-fn-implements-fn-mut.rs
@@ -0,0 +1,37 @@
+// 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(overloaded_calls)]
+
+use std::ops::FnMut;
+
+fn call_f<F:FnMut<(),()>>(mut f: F) {
+    f();
+}
+
+fn f() {
+    println!("hello");
+}
+
+fn call_g<G:FnMut<(String,String),String>>(mut g: G, x: String, y: String)
+          -> String {
+    g(x, y)
+}
+
+fn g(x: String, y: String) -> String {
+    x.append(y.as_slice())
+}
+
+fn main() {
+    call_f(f);
+    assert_eq!(call_g(g, "foo".to_string(), "bar".to_string()).as_slice(),
+               "foobar");
+}
+