From 3550068b531703bc492b0f97331c6a2bcafecf37 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 23 Jul 2014 19:57:30 -0700 Subject: [PATCH] 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. --- src/libcore/ops.rs | 34 +++++++++++++++++ src/librustc/middle/typeck/check/mod.rs | 7 ++++ src/libsyntax/parse/parser.rs | 8 +++- .../run-pass/bare-fn-implements-fn-mut.rs | 37 +++++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/test/run-pass/bare-fn-implements-fn-mut.rs 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 { fn call_once(self, args: Args) -> Result; } +macro_rules! def_fn_mut( + ($($args:ident)*) => ( + #[cfg(not(stage0))] + impl + 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]) -> 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>) -> Gc { + already_parsed_attrs: Option>) + -> Gc { 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 or the MIT license +// , 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>(mut f: F) { + f(); +} + +fn f() { + println!("hello"); +} + +fn call_g>(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"); +} +