summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2013-03-13 22:25:28 -0400
committerBrian Anderson <banderson@mozilla.com>2013-03-29 18:36:20 -0700
commit6965fe4bceea836586bd8e7aa01a92a35b467f78 (patch)
treed95089f050cd67db2a5171a799763faa09f5b0a8 /src/libsyntax
parentf864934f548be9f03d2c0512de8d7e908469e2ae (diff)
downloadrust-6965fe4bceea836586bd8e7aa01a92a35b467f78.tar.gz
rust-6965fe4bceea836586bd8e7aa01a92a35b467f78.zip
Add AbiSet and integrate it into the AST.
I believe this patch incorporates all expected syntax changes from extern
function reform (#3678). You can now write things like:

    extern "<abi>" fn foo(s: S) -> T { ... }
    extern "<abi>" mod { ... }
    extern "<abi>" fn(S) -> T

The ABI for foreign functions is taken from this syntax (rather than from an
annotation).  We support the full ABI specification I described on the mailing
list.  The correct ABI is chosen based on the target architecture.

Calls by pointer to C functions are not yet supported, and the Rust type of
crust fns is still *u8.
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/abi.rs427
-rw-r--r--src/libsyntax/ast.rs41
-rw-r--r--src/libsyntax/ast_map.rs11
-rw-r--r--src/libsyntax/ast_util.rs2
-rw-r--r--src/libsyntax/attr.rs21
-rw-r--r--src/libsyntax/ext/pipes/ast_builder.rs4
-rw-r--r--src/libsyntax/fold.rs7
-rw-r--r--src/libsyntax/parse/parser.rs113
-rw-r--r--src/libsyntax/print/pprust.rs64
-rw-r--r--src/libsyntax/syntax.rc1
-rw-r--r--src/libsyntax/visit.rs30
11 files changed, 588 insertions, 133 deletions
diff --git a/src/libsyntax/abi.rs b/src/libsyntax/abi.rs
new file mode 100644
index 00000000000..076c09e446d
--- /dev/null
+++ b/src/libsyntax/abi.rs
@@ -0,0 +1,427 @@
+// Copyright 2012-2013 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.
+
+use core::prelude::*;
+use core::to_bytes;
+use core::to_str::ToStr;
+
+#[deriving(Eq)]
+pub enum Abi {
+    // NB: This ordering MUST match the AbiDatas array below.
+    // (This is ensured by the test indices_are_correct().)
+
+    // Single platform ABIs come first (`for_arch()` relies on this)
+    Cdecl,
+    Stdcall,
+    Fastcall,
+    Aapcs,
+
+    // Multiplatform ABIs second
+    Rust,
+    C,
+    RustIntrinsic,
+}
+
+#[deriving(Eq)]
+pub enum Architecture {
+    // NB. You cannot change the ordering of these
+    // constants without adjusting IntelBits below.
+    // (This is ensured by the test indices_are_correct().)
+    X86,
+    X86_64,
+    Arm,
+    Mips
+}
+
+// FIXME(#5423) After a snapshot, we can change these constants:
+// const IntelBits: u32 = (1 << (X86 as uint)) | (1 << X86_64 as uint));
+// const ArmBits: u32 = (1 << (Arm as uint));
+static IntelBits: u32 = 1 | 2;
+static ArmBits: u32 = 4;
+
+struct AbiData {
+    abi: Abi,
+
+    // Name of this ABI as we like it called.
+    name: &'static str,
+
+    // Is it specific to a platform? If so, which one?  Also, what is
+    // the name that LLVM gives it (in case we disagree)
+    abi_arch: AbiArchitecture
+}
+
+enum AbiArchitecture {
+    RustArch,   // Not a real ABI (e.g., intrinsic)
+    AllArch,    // An ABI that specifies cross-platform defaults (e.g., "C")
+    Archs(u32)  // Multiple architectures (bitset)
+}
+
+#[auto_encode]
+#[auto_decode]
+#[deriving(Eq)]
+pub struct AbiSet {
+    priv bits: u32   // each bit represents one of the abis below
+}
+
+static AbiDatas: &'static [AbiData] = &[
+    // Platform-specific ABIs
+    AbiData {abi: Cdecl, name: "cdecl", abi_arch: Archs(IntelBits)},
+    AbiData {abi: Stdcall, name: "stdcall", abi_arch: Archs(IntelBits)},
+    AbiData {abi: Fastcall, name:"fastcall", abi_arch: Archs(IntelBits)},
+    AbiData {abi: Aapcs, name: "aapcs", abi_arch: Archs(ArmBits)},
+
+    // Cross-platform ABIs
+    //
+    // NB: Do not adjust this ordering without
+    // adjusting the indices below.
+    AbiData {abi: Rust, name: "Rust", abi_arch: RustArch},
+    AbiData {abi: C, name: "C", abi_arch: AllArch},
+    AbiData {abi: RustIntrinsic, name: "rust-intrinsic", abi_arch: RustArch},
+];
+
+fn each_abi(op: &fn(abi: Abi) -> bool) {
+    /*!
+     *
+     * Iterates through each of the defined ABIs.
+     */
+
+    for AbiDatas.each |abi_data| {
+        if !op(abi_data.abi) {
+            return;
+        }
+    }
+}
+
+pub fn lookup(name: &str) -> Option<Abi> {
+    /*!
+     *
+     * Returns the ABI with the given name (if any).
+     */
+
+    for each_abi |abi| {
+        if name == abi.data().name {
+            return Some(abi);
+        }
+    }
+    return None;
+}
+
+pub fn all_names() -> ~[&'static str] {
+    AbiDatas.map(|d| d.name)
+}
+
+pub impl Abi {
+    #[inline]
+    fn index(&self) -> uint {
+        *self as uint
+    }
+
+    #[inline]
+    fn data(&self) -> &'static AbiData {
+        &AbiDatas[self.index()]
+    }
+
+    fn name(&self) -> &'static str {
+        self.data().name
+    }
+}
+
+impl Architecture {
+    fn bit(&self) -> u32 {
+        1 << (*self as u32)
+    }
+}
+
+pub impl AbiSet {
+    fn from(abi: Abi) -> AbiSet {
+        AbiSet { bits: (1 << abi.index()) }
+    }
+
+    #[inline]
+    fn Rust() -> AbiSet {
+        AbiSet::from(Rust)
+    }
+
+    #[inline]
+    fn C() -> AbiSet {
+        AbiSet::from(C)
+    }
+
+    #[inline]
+    fn Intrinsic() -> AbiSet {
+        AbiSet::from(RustIntrinsic)
+    }
+
+    fn default() -> AbiSet {
+        AbiSet::C()
+    }
+
+    fn empty() -> AbiSet {
+        AbiSet { bits: 0 }
+    }
+
+    #[inline]
+    fn is_rust(&self) -> bool {
+        self.bits == 1 << Rust.index()
+    }
+
+    #[inline]
+    fn is_c(&self) -> bool {
+        self.bits == 1 << C.index()
+    }
+
+    #[inline]
+    fn is_intrinsic(&self) -> bool {
+        self.bits == 1 << RustIntrinsic.index()
+    }
+
+    fn contains(&self, abi: Abi) -> bool {
+        (self.bits & (1 << abi.index())) != 0
+    }
+
+    fn subset_of(&self, other_abi_set: AbiSet) -> bool {
+        (self.bits & other_abi_set.bits) == self.bits
+    }
+
+    fn add(&mut self, abi: Abi) {
+        self.bits |= (1 << abi.index());
+    }
+
+    fn each(&self, op: &fn(abi: Abi) -> bool) {
+        for each_abi |abi| {
+            if self.contains(abi) {
+                if !op(abi) {
+                    return;
+                }
+            }
+        }
+    }
+
+    fn is_empty(&self) -> bool {
+        self.bits == 0
+    }
+
+    fn for_arch(&self, arch: Architecture) -> Option<Abi> {
+        // NB---Single platform ABIs come first
+        for self.each |abi| {
+            let data = abi.data();
+            match data.abi_arch {
+                Archs(a) if (a & arch.bit()) != 0 => { return Some(abi); }
+                Archs(_) => { }
+                RustArch | AllArch => { return Some(abi); }
+            }
+        }
+
+        None
+    }
+
+    fn check_valid(&self) -> Option<(Abi, Abi)> {
+        let mut abis = ~[];
+        for self.each |abi| { abis.push(abi); }
+
+        for abis.eachi |i, abi| {
+            let data = abi.data();
+            for abis.slice(0, i).each |other_abi| {
+                let other_data = other_abi.data();
+                debug!("abis=(%?,%?) datas=(%?,%?)",
+                       abi, data.abi_arch,
+                       other_abi, other_data.abi_arch);
+                match (&data.abi_arch, &other_data.abi_arch) {
+                    (&AllArch, &AllArch) => {
+                        // Two cross-architecture ABIs
+                        return Some((*abi, *other_abi));
+                    }
+                    (_, &RustArch) |
+                    (&RustArch, _) => {
+                        // Cannot combine Rust or Rust-Intrinsic with
+                        // anything else.
+                        return Some((*abi, *other_abi));
+                    }
+                    (&Archs(is), &Archs(js)) if (is & js) != 0 => {
+                        // Two ABIs for same architecture
+                        return Some((*abi, *other_abi));
+                    }
+                    _ => {}
+                }
+            }
+        }
+
+        return None;
+    }
+}
+
+impl to_bytes::IterBytes for Abi {
+    fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
+        self.index().iter_bytes(lsb0, f)
+    }
+}
+
+impl to_bytes::IterBytes for AbiSet {
+    fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
+        self.bits.iter_bytes(lsb0, f)
+    }
+}
+
+impl ToStr for Abi {
+    fn to_str(&self) -> ~str {
+        self.data().name.to_str()
+    }
+}
+
+impl ToStr for AbiSet {
+    fn to_str(&self) -> ~str {
+        unsafe { // so we can push to strs.
+            let mut strs = ~[];
+            for self.each |abi| {
+                strs.push(abi.data().name);
+            }
+            fmt!("\"%s\"", str::connect_slices(strs, " "))
+        }
+    }
+}
+
+#[test]
+fn lookup_Rust() {
+    let abi = lookup("Rust");
+    assert!(abi.is_some() && abi.get().data().name == "Rust");
+}
+
+#[test]
+fn lookup_cdecl() {
+    let abi = lookup("cdecl");
+    assert!(abi.is_some() && abi.get().data().name == "cdecl");
+}
+
+#[test]
+fn lookup_baz() {
+    let abi = lookup("baz");
+    assert!(abi.is_none());
+}
+
+#[cfg(test)]
+fn cannot_combine(n: Abi, m: Abi) {
+    let mut set = AbiSet::empty();
+    set.add(n);
+    set.add(m);
+    match set.check_valid() {
+        Some((a, b)) => {
+            assert!((n == a && m == b) ||
+                         (m == a && n == b));
+        }
+        None => {
+            fail!(~"Invalid match not detected");
+        }
+    }
+}
+
+#[cfg(test)]
+fn can_combine(n: Abi, m: Abi) {
+    let mut set = AbiSet::empty();
+    set.add(n);
+    set.add(m);
+    match set.check_valid() {
+        Some((_, _)) => {
+            fail!(~"Valid match declared invalid");
+        }
+        None => {}
+    }
+}
+
+#[test]
+fn cannot_combine_cdecl_and_stdcall() {
+    cannot_combine(Cdecl, Stdcall);
+}
+
+#[test]
+fn cannot_combine_c_and_rust() {
+    cannot_combine(C, Rust);
+}
+
+#[test]
+fn cannot_combine_rust_and_cdecl() {
+    cannot_combine(Rust, Cdecl);
+}
+
+#[test]
+fn cannot_combine_rust_intrinsic_and_cdecl() {
+    cannot_combine(RustIntrinsic, Cdecl);
+}
+
+#[test]
+fn can_combine_c_and_stdcall() {
+    can_combine(C, Stdcall);
+}
+
+#[test]
+fn can_combine_aapcs_and_stdcall() {
+    can_combine(Aapcs, Stdcall);
+}
+
+#[test]
+fn abi_to_str_stdcall_aaps() {
+    let mut set = AbiSet::empty();
+    set.add(Aapcs);
+    set.add(Stdcall);
+    assert!(set.to_str() == ~"\"stdcall aapcs\"");
+}
+
+#[test]
+fn abi_to_str_c_aaps() {
+    let mut set = AbiSet::empty();
+    set.add(Aapcs);
+    set.add(C);
+    debug!("set = %s", set.to_str());
+    assert!(set.to_str() == ~"\"aapcs C\"");
+}
+
+#[test]
+fn abi_to_str_rust() {
+    let mut set = AbiSet::empty();
+    set.add(Rust);
+    debug!("set = %s", set.to_str());
+    assert!(set.to_str() == ~"\"Rust\"");
+}
+
+#[test]
+fn indices_are_correct() {
+    for AbiDatas.eachi |i, abi_data| {
+        assert!(i == abi_data.abi.index());
+    }
+
+    let bits = 1 << (X86 as u32);
+    let bits = bits | 1 << (X86_64 as u32);
+    assert!(IntelBits == bits);
+
+    let bits = 1 << (Arm as u32);
+    assert!(ArmBits == bits);
+}
+
+#[cfg(test)]
+fn check_arch(abis: &[Abi], arch: Architecture, expect: Option<Abi>) {
+    let mut set = AbiSet::empty();
+    for abis.each |&abi| {
+        set.add(abi);
+    }
+    let r = set.for_arch(arch);
+    assert!(r == expect);
+}
+
+#[test]
+fn pick_multiplatform() {
+    check_arch([C, Cdecl], X86, Some(Cdecl));
+    check_arch([C, Cdecl], X86_64, Some(Cdecl));
+    check_arch([C, Cdecl], Arm, Some(C));
+}
+
+#[test]
+fn pick_uniplatform() {
+    check_arch([Stdcall], X86, Some(Stdcall));
+    check_arch([Stdcall], Arm, None);
+}
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index d7fc2f2f264..db04c46ea59 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -10,7 +10,9 @@
 
 // The Rust abstract syntax tree.
 
-use codemap::{span, spanned};
+use codemap::{span, FileName, spanned};
+use abi::AbiSet;
+use opt_vec::OptVec;
 
 use core::cast;
 use core::option::{None, Option, Some};
@@ -19,7 +21,6 @@ use core::to_bytes;
 use core::to_str::ToStr;
 use std::serialize::{Encodable, Decodable, Encoder, Decoder};
 
-use opt_vec::OptVec;
 
 /* can't import macros yet, so this is copied from token.rs. See its comment
  * there. */
@@ -328,27 +329,6 @@ impl to_bytes::IterBytes for mutability {
 #[auto_encode]
 #[auto_decode]
 #[deriving(Eq)]
-pub enum Abi {
-    RustAbi
-}
-
-impl to_bytes::IterBytes for Abi {
-    fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
-        (*self as uint).iter_bytes(lsb0, f)
-    }
-}
-
-impl ToStr for Abi {
-    fn to_str(&self) -> ~str {
-        match *self {
-            RustAbi => ~"\"rust\""
-        }
-    }
-}
-
-#[auto_encode]
-#[auto_decode]
-#[deriving(Eq)]
 pub enum Sigil {
     BorrowedSigil,
     OwnedSigil,
@@ -893,7 +873,7 @@ pub struct TyClosure {
 #[deriving(Eq)]
 pub struct TyBareFn {
     purity: purity,
-    abi: Abi,
+    abis: AbiSet,
     lifetimes: OptVec<Lifetime>,
     decl: fn_decl
 }
@@ -1057,15 +1037,6 @@ pub struct _mod {
     items: ~[@item],
 }
 
-#[auto_encode]
-#[auto_decode]
-#[deriving(Eq)]
-pub enum foreign_abi {
-    foreign_abi_rust_intrinsic,
-    foreign_abi_cdecl,
-    foreign_abi_stdcall,
-}
-
 // Foreign mods can be named or anonymous
 #[auto_encode]
 #[auto_decode]
@@ -1077,7 +1048,7 @@ pub enum foreign_mod_sort { named, anonymous }
 #[deriving(Eq)]
 pub struct foreign_mod {
     sort: foreign_mod_sort,
-    abi: ident,
+    abis: AbiSet,
     view_items: ~[@view_item],
     items: ~[@foreign_item],
 }
@@ -1267,7 +1238,7 @@ pub struct item {
 #[deriving(Eq)]
 pub enum item_ {
     item_const(@Ty, @expr),
-    item_fn(fn_decl, purity, Generics, blk),
+    item_fn(fn_decl, purity, AbiSet, Generics, blk),
     item_mod(_mod),
     item_foreign_mod(foreign_mod),
     item_ty(@Ty, Generics),
diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs
index 11178054b4c..ff5cbbe9f23 100644
--- a/src/libsyntax/ast_map.rs
+++ b/src/libsyntax/ast_map.rs
@@ -10,6 +10,7 @@
 
 use core::prelude::*;
 
+use abi::AbiSet;
 use ast::*;
 use ast;
 use ast_util::{inlined_item_utils, stmt_id};
@@ -87,7 +88,7 @@ pub fn path_elt_to_str(pe: path_elt, itr: @ident_interner) -> ~str {
 
 pub enum ast_node {
     node_item(@item, @path),
-    node_foreign_item(@foreign_item, foreign_abi, visibility, @path),
+    node_foreign_item(@foreign_item, AbiSet, visibility, @path),
     node_trait_method(@trait_method, def_id /* trait did */,
                       @path /* path to the trait */),
     node_method(@method, def_id /* impl did */, @path /* path to the impl */),
@@ -171,7 +172,7 @@ pub fn map_decoded_item(diag: @span_handler,
       ii_item(*) | ii_dtor(*) => { /* fallthrough */ }
       ii_foreign(i) => {
         cx.map.insert(i.id, node_foreign_item(i,
-                                              foreign_abi_rust_intrinsic,
+                                              AbiSet::Intrinsic(),
                                               i.vis,    // Wrong but OK
                                               @path));
       }
@@ -274,10 +275,6 @@ pub fn map_item(i: @item, &&cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
             }
         }
         item_foreign_mod(ref nm) => {
-            let abi = match attr::foreign_abi(i.attrs) {
-                Left(ref msg) => cx.diag.span_fatal(i.span, (*msg)),
-                Right(abi) => abi
-            };
             for nm.items.each |nitem| {
                 // Compute the visibility for this native item.
                 let visibility = match nitem.vis {
@@ -289,7 +286,7 @@ pub fn map_item(i: @item, &&cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
                 cx.map.insert(nitem.id,
                     node_foreign_item(
                         *nitem,
-                        abi,
+                        nm.abis,
                         visibility,
                         // FIXME (#2543)
                         if nm.sort == ast::named {
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index 131b6616df6..208ed1e35fe 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -465,7 +465,7 @@ pub fn id_visitor(vfn: @fn(node_id)) -> visit::vt<()> {
                     vfn(self_id);
                     vfn(parent_id.node);
                 }
-                visit::fk_item_fn(_, generics, _) => {
+                visit::fk_item_fn(_, generics, _, _) => {
                     visit_generics(generics);
                 }
                 visit::fk_method(_, generics, m) => {
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 90e8c0aa616..72355d04456 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -303,27 +303,6 @@ pub fn find_linkage_metas(attrs: &[ast::attribute]) -> ~[@ast::meta_item] {
     }
 }
 
-pub fn foreign_abi(attrs: &[ast::attribute])
-                -> Either<~str, ast::foreign_abi> {
-    return match attr::first_attr_value_str_by_name(attrs, ~"abi") {
-        None => {
-            Right(ast::foreign_abi_cdecl)
-        }
-        Some(@~"rust-intrinsic") => {
-            Right(ast::foreign_abi_rust_intrinsic)
-        }
-        Some(@~"cdecl") => {
-            Right(ast::foreign_abi_cdecl)
-        }
-        Some(@~"stdcall") => {
-            Right(ast::foreign_abi_stdcall)
-        }
-        Some(t) => {
-            Left(~"unsupported abi: " + *t)
-        }
-    };
-}
-
 #[deriving(Eq)]
 pub enum inline_attr {
     ia_none,
diff --git a/src/libsyntax/ext/pipes/ast_builder.rs b/src/libsyntax/ext/pipes/ast_builder.rs
index 075474a2a0d..72e6c22dbc8 100644
--- a/src/libsyntax/ext/pipes/ast_builder.rs
+++ b/src/libsyntax/ext/pipes/ast_builder.rs
@@ -15,7 +15,8 @@
 
 use core::prelude::*;
 
-use ast::ident;
+use abi::AbiSet;
+use ast::{ident, node_id};
 use ast;
 use ast_util;
 use codemap::{span, respan, dummy_sp, spanned};
@@ -272,6 +273,7 @@ impl ext_ctxt_ast_builder for @ext_ctxt {
                   dummy_sp(),
                   ast::item_fn(self.fn_decl(inputs, output),
                                ast::impure_fn,
+                               AbiSet::Rust(),
                                generics,
                                body))
     }
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index f43e541052e..0a473b1cebe 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -236,10 +236,11 @@ fn noop_fold_struct_field(sf: @struct_field, fld: @ast_fold)
 pub fn noop_fold_item_underscore(i: &item_, fld: @ast_fold) -> item_ {
     match *i {
         item_const(t, e) => item_const(fld.fold_ty(t), fld.fold_expr(e)),
-        item_fn(ref decl, purity, ref generics, ref body) => {
+        item_fn(ref decl, purity, abi, ref generics, ref body) => {
             item_fn(
                 fold_fn_decl(decl, fld),
                 purity,
+                abi,
                 fold_generics(generics, fld),
                 fld.fold_block(body)
             )
@@ -612,7 +613,7 @@ pub fn noop_fold_ty(t: &ty_, fld: @ast_fold) -> ty_ {
             ty_bare_fn(@TyBareFn {
                 lifetimes: f.lifetimes,
                 purity: f.purity,
-                abi: f.abi,
+                abis: f.abis,
                 decl: fold_fn_decl(&f.decl, fld)
             })
         }
@@ -639,7 +640,7 @@ pub fn noop_fold_mod(m: &_mod, fld: @ast_fold) -> _mod {
 fn noop_fold_foreign_mod(nm: &foreign_mod, fld: @ast_fold) -> foreign_mod {
     ast::foreign_mod {
         sort: nm.sort,
-        abi: nm.abi,
+        abis: nm.abis,
         view_items: vec::map(nm.view_items, |x| fld.fold_view_item(*x)),
         items: vec::map(nm.items, |x| fld.fold_foreign_item(*x)),
     }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index ac0e42fc65d..353e3014cf7 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -10,7 +10,9 @@
 
 use core::prelude::*;
 
-use ast::{Sigil, BorrowedSigil, ManagedSigil, OwnedSigil, RustAbi};
+use abi;
+use abi::AbiSet;
+use ast::{Sigil, BorrowedSigil, ManagedSigil, OwnedSigil};
 use ast::{CallSugar, NoSugar, DoSugar, ForSugar};
 use ast::{TyBareFn, TyClosure};
 use ast::{RegionTyParamBound, TraitTyParamBound};
@@ -361,11 +363,13 @@ pub impl Parser {
 
         */
 
+        let opt_abis = self.parse_opt_abis();
+        let abis = opt_abis.get_or_default(AbiSet::Rust());
         let purity = self.parse_purity();
         self.expect_keyword(&~"fn");
         let (decl, lifetimes) = self.parse_ty_fn_decl();
         return ty_bare_fn(@TyBareFn {
-            abi: RustAbi,
+            abis: abis,
             purity: purity,
             lifetimes: lifetimes,
             decl: decl
@@ -3041,11 +3045,13 @@ pub impl Parser {
                      span: mk_sp(lo, hi) }
     }
 
-    fn parse_item_fn(&self, purity: purity) -> item_info {
+    fn parse_item_fn(&self, purity: purity, abis: AbiSet) -> item_info {
         let (ident, generics) = self.parse_fn_header();
         let decl = self.parse_fn_decl(|p| p.parse_arg());
         let (inner_attrs, body) = self.parse_inner_attrs_and_block(true);
-        (ident, item_fn(decl, purity, generics, body), Some(inner_attrs))
+        (ident,
+         item_fn(decl, purity, abis, generics, body),
+         Some(inner_attrs))
     }
 
     fn parse_method(&self) -> @method {
@@ -3607,7 +3613,7 @@ pub impl Parser {
     }
 
     fn parse_foreign_mod_items(&self, sort: ast::foreign_mod_sort,
-                               +abi: ast::ident,
+                               +abis: AbiSet,
                                +first_item_attrs: ~[attribute])
                             -> foreign_mod {
         // Shouldn't be any view items since we've already parsed an item attr
@@ -3630,30 +3636,20 @@ pub impl Parser {
         }
         ast::foreign_mod {
             sort: sort,
-            abi: abi,
+            abis: abis,
             view_items: view_items,
             items: items
         }
     }
 
-    fn parse_item_foreign_mod(&self, lo: BytePos,
+    fn parse_item_foreign_mod(&self,
+                              lo: BytePos,
+                              opt_abis: Option<AbiSet>,
                               visibility: visibility,
                               attrs: ~[attribute],
                               items_allowed: bool)
-                           -> item_or_view_item {
-
-        // Parse the ABI.
-        let abi_opt;
-        match *self.token {
-            token::LIT_STR(copy found_abi) => {
-                self.bump();
-                abi_opt = Some(found_abi);
-            }
-            _ => {
-                abi_opt = None;
-            }
-        }
-
+                           -> item_or_view_item
+    {
         let mut must_be_named_mod = false;
         if self.is_keyword(&~"mod") {
             must_be_named_mod = true;
@@ -3688,14 +3684,10 @@ pub impl Parser {
 
         // extern mod { ... }
         if items_allowed && self.eat(&token::LBRACE) {
-            let abi;
-            match abi_opt {
-                Some(found_abi) => abi = found_abi,
-                None => abi = special_idents::c_abi,
-            }
+            let abis = opt_abis.get_or_default(AbiSet::C());
 
             let (inner, next) = self.parse_inner_attrs_and_next();
-            let m = self.parse_foreign_mod_items(sort, abi, next);
+            let m = self.parse_foreign_mod_items(sort, abis, next);
             self.expect(&token::RBRACE);
 
             return iovi_item(self.mk_item(lo, self.last_span.hi, ident,
@@ -3704,12 +3696,8 @@ pub impl Parser {
                                                        Some(inner))));
         }
 
-        match abi_opt {
-            None => {}  // OK.
-            Some(_) => {
-                self.span_err(*self.span, ~"an ABI may not be specified \
-                                                here");
-            }
+        if opt_abis.is_some() {
+            self.span_err(*self.span, ~"an ABI may not be specified here");
         }
 
         // extern mod foo;
@@ -3913,6 +3901,49 @@ pub impl Parser {
         }
     }
 
+    fn parse_opt_abis(&self) -> Option<AbiSet> {
+        match *self.token {
+            token::LIT_STR(s) => {
+                self.bump();
+                let the_string = self.id_to_str(s);
+                let mut words = ~[];
+                for str::each_word(*the_string) |s| { words.push(s) }
+                let mut abis = AbiSet::empty();
+                for words.each |word| {
+                    match abi::lookup(*word) {
+                        Some(abi) => {
+                            if abis.contains(abi) {
+                                self.span_err(
+                                    *self.span,
+                                    fmt!("ABI `%s` appears twice",
+                                         *word));
+                            } else {
+                                abis.add(abi);
+                            }
+                        }
+
+                        None => {
+                            self.span_err(
+                                *self.span,
+                                fmt!("illegal ABI: \
+                                      expected one of [%s], \
+                                      found `%s`",
+                                     str::connect_slices(
+                                         abi::all_names(),
+                                         ", "),
+                                     *word));
+                        }
+                    }
+                }
+                Some(abis)
+            }
+
+            _ => {
+                None
+            }
+        }
+    }
+
     // parse one of the items or view items allowed by the
     // flags; on failure, return iovi_none.
     fn parse_item_or_view_item(
@@ -3961,7 +3992,8 @@ pub impl Parser {
             self.is_keyword(&~"fn") &&
             !self.fn_expr_lookahead(self.look_ahead(1u)) {
             self.bump();
-            let (ident, item_, extra_attrs) = self.parse_item_fn(impure_fn);
+            let (ident, item_, extra_attrs) =
+                self.parse_item_fn(impure_fn, AbiSet::Rust());
             return iovi_item(self.mk_item(lo, self.last_span.hi, ident, item_,
                                           visibility,
                                           maybe_append(attrs, extra_attrs)));
@@ -3970,7 +4002,8 @@ pub impl Parser {
             // PURE FUNCTION ITEM
             self.obsolete(*self.last_span, ObsoletePurity);
             self.expect_keyword(&~"fn");
-            let (ident, item_, extra_attrs) = self.parse_item_fn(impure_fn);
+            let (ident, item_, extra_attrs) =
+                self.parse_item_fn(impure_fn, AbiSet::Rust());
             return iovi_item(self.mk_item(lo, self.last_span.hi, ident, item_,
                                           visibility,
                                           maybe_append(attrs, extra_attrs)));
@@ -3987,16 +4020,20 @@ pub impl Parser {
             // UNSAFE FUNCTION ITEM (where items are allowed)
             self.bump();
             self.expect_keyword(&~"fn");
-            let (ident, item_, extra_attrs) = self.parse_item_fn(unsafe_fn);
+            let (ident, item_, extra_attrs) =
+                self.parse_item_fn(unsafe_fn, AbiSet::Rust());
             return iovi_item(self.mk_item(lo, self.last_span.hi, ident, item_,
                                           visibility,
                                           maybe_append(attrs, extra_attrs)));
         }
         if self.eat_keyword(&~"extern") {
+            let opt_abis = self.parse_opt_abis();
+
             if items_allowed && self.eat_keyword(&~"fn") {
                 // EXTERN FUNCTION ITEM
+                let abis = opt_abis.get_or_default(AbiSet::C());
                 let (ident, item_, extra_attrs) =
-                    self.parse_item_fn(extern_fn);
+                    self.parse_item_fn(extern_fn, abis);
                 return iovi_item(self.mk_item(lo, self.last_span.hi, ident,
                                               item_, visibility,
                                               maybe_append(attrs,
@@ -4004,7 +4041,7 @@ pub impl Parser {
             }
             if !foreign_items_allowed {
                 // EXTERN MODULE ITEM
-                return self.parse_item_foreign_mod(lo, visibility, attrs,
+                return self.parse_item_foreign_mod(lo, opt_abis, visibility, attrs,
                                                    items_allowed);
             }
         }
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 7ca5fba4c81..0ec7bdba3d1 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -10,6 +10,8 @@
 
 use core::prelude::*;
 
+use abi::AbiSet;
+use abi;
 use ast::{RegionTyParamBound, TraitTyParamBound, required, provided};
 use ast;
 use ast_util;
@@ -186,7 +188,8 @@ pub fn fun_to_str(decl: &ast::fn_decl, purity: ast::purity, name: ast::ident,
                   generics: &ast::Generics, intr: @ident_interner) -> ~str {
     do io::with_str_writer |wr| {
         let s = rust_printer(wr, intr);
-        print_fn(s, decl, purity, name, generics, opt_self_ty, ast::inherited);
+        print_fn(s, decl, Some(purity), AbiSet::Rust(),
+                 name, generics, opt_self_ty, ast::inherited);
         end(s); // Close the head box
         end(s); // Close the outer box
         eof(s.s);
@@ -405,7 +408,7 @@ pub fn print_type(s: @ps, &&ty: @ast::Ty) {
       ast::ty_bare_fn(f) => {
           let generics = ast::Generics {lifetimes: copy f.lifetimes,
                                         ty_params: opt_vec::Empty};
-          print_ty_fn(s, Some(f.abi), None, None,
+          print_ty_fn(s, Some(f.abis), None, None,
                       f.purity, ast::Many, &f.decl, None,
                       Some(&generics), None);
       }
@@ -446,7 +449,7 @@ pub fn print_foreign_item(s: @ps, item: @ast::foreign_item) {
     print_outer_attributes(s, item.attrs);
     match item.node {
       ast::foreign_item_fn(ref decl, purity, ref generics) => {
-        print_fn(s, decl, purity, item.ident, generics, None,
+        print_fn(s, decl, Some(purity), AbiSet::Rust(), item.ident, generics, None,
                  ast::inherited);
         end(s); // end head-ibox
         word(s.s, ~";");
@@ -485,11 +488,12 @@ pub fn print_item(s: @ps, &&item: @ast::item) {
         end(s); // end the outer cbox
 
       }
-      ast::item_fn(ref decl, purity, ref typarams, ref body) => {
+      ast::item_fn(ref decl, purity, abi, ref typarams, ref body) => {
         print_fn(
             s,
             decl,
-            purity,
+            Some(purity),
+            abi,
             item.ident,
             typarams,
             None,
@@ -508,8 +512,7 @@ pub fn print_item(s: @ps, &&item: @ast::item) {
       }
       ast::item_foreign_mod(ref nmod) => {
         head(s, visibility_qualified(item.vis, ~"extern"));
-        print_string(s, *s.intr.get(nmod.abi));
-        nbsp(s);
+        word_nbsp(s, nmod.abis.to_str());
         match nmod.sort {
             ast::named => {
                 word_nbsp(s, ~"mod");
@@ -817,7 +820,7 @@ pub fn print_method(s: @ps, meth: @ast::method) {
     hardbreak_if_not_bol(s);
     maybe_print_comment(s, meth.span.lo);
     print_outer_attributes(s, meth.attrs);
-    print_fn(s, &meth.decl, meth.purity,
+    print_fn(s, &meth.decl, Some(meth.purity), AbiSet::Rust(),
              meth.ident, &meth.generics, Some(meth.self_ty.node),
              meth.vis);
     word(s.s, ~" ");
@@ -1650,13 +1653,14 @@ pub fn print_self_ty(s: @ps, self_ty: ast::self_ty_) -> bool {
 
 pub fn print_fn(s: @ps,
                 decl: &ast::fn_decl,
-                purity: ast::purity,
+                purity: Option<ast::purity>,
+                abis: AbiSet,
                 name: ast::ident,
                 generics: &ast::Generics,
                 opt_self_ty: Option<ast::self_ty_>,
                 vis: ast::visibility) {
     head(s, ~"");
-    print_fn_header_info(s, purity, ast::Many, None, vis);
+    print_fn_header_info(s, opt_self_ty, purity, abis, ast::Many, None, vis);
     nbsp(s);
     print_ident(s, name);
     print_generics(s, generics);
@@ -1905,7 +1909,7 @@ pub fn print_arg(s: @ps, input: ast::arg) {
 }
 
 pub fn print_ty_fn(s: @ps,
-                   opt_abi: Option<ast::Abi>,
+                   opt_abis: Option<AbiSet>,
                    opt_sigil: Option<ast::Sigil>,
                    opt_region: Option<@ast::Lifetime>,
                    purity: ast::purity,
@@ -1918,7 +1922,7 @@ pub fn print_ty_fn(s: @ps,
 
     // Duplicates the logic in `print_fn_header_info()`.  This is because that
     // function prints the sigil in the wrong place.  That should be fixed.
-    print_opt_abi(s, opt_abi);
+    print_extern_opt_abis(s, opt_abis);
     print_opt_sigil(s, opt_sigil);
     print_opt_lifetime(s, opt_region);
     print_purity(s, purity);
@@ -2146,9 +2150,22 @@ pub fn next_comment(s: @ps) -> Option<comments::cmnt> {
     }
 }
 
-pub fn print_opt_abi(s: @ps, opt_abi: Option<ast::Abi>) {
-    match opt_abi {
-        Some(ast::RustAbi) => { word_nbsp(s, ~"extern"); }
+pub fn print_opt_purity(s: @ps, opt_purity: Option<ast::purity>) {
+    match opt_purity {
+        Some(ast::impure_fn) => { }
+        Some(purity) => {
+            word_nbsp(s, purity_to_str(purity));
+        }
+        None => {}
+    }
+}
+
+pub fn print_extern_opt_abis(s: @ps, opt_abis: Option<AbiSet>) {
+    match opt_abis {
+        Some(abis) => {
+            word_nbsp(s, ~"extern");
+            word_nbsp(s, abis.to_str());
+        }
         None => {}
     };
 }
@@ -2163,12 +2180,25 @@ pub fn print_opt_sigil(s: @ps, opt_sigil: Option<ast::Sigil>) {
 }
 
 pub fn print_fn_header_info(s: @ps,
-                            purity: ast::purity,
+                            opt_sty: Option<ast::self_ty_>,
+                            opt_purity: Option<ast::purity>,
+                            abis: AbiSet,
                             onceness: ast::Onceness,
                             opt_sigil: Option<ast::Sigil>,
                             vis: ast::visibility) {
     word(s.s, visibility_qualified(vis, ~""));
-    print_purity(s, purity);
+
+    if abis != AbiSet::Rust() {
+        word_nbsp(s, ~"extern");
+        word_nbsp(s, abis.to_str());
+
+        if opt_purity != Some(ast::extern_fn) {
+            print_opt_purity(s, opt_purity);
+        }
+    } else {
+        print_opt_purity(s, opt_purity);
+    }
+
     print_onceness(s, onceness);
     word(s.s, ~"fn");
     print_opt_sigil(s, opt_sigil);
diff --git a/src/libsyntax/syntax.rc b/src/libsyntax/syntax.rc
index feff4b0616a..21c52f1bfcc 100644
--- a/src/libsyntax/syntax.rc
+++ b/src/libsyntax/syntax.rc
@@ -39,6 +39,7 @@ pub mod opt_vec;
 pub mod attr;
 pub mod diagnostic;
 pub mod codemap;
+pub mod abi;
 pub mod ast;
 pub mod ast_util;
 pub mod ast_map;
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index dd724948b32..a994f2b5b22 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -10,6 +10,7 @@
 
 use core::prelude::*;
 
+use abi::AbiSet;
 use ast::*;
 use ast;
 use ast_util;
@@ -29,10 +30,18 @@ use opt_vec::OptVec;
 pub enum vt<E> { mk_vt(visitor<E>), }
 
 pub enum fn_kind<'self> {
-    fk_item_fn(ident, &'self Generics, purity),   // fn foo()
-    fk_method(ident, &'self Generics, &'self method),   // fn foo(&self)
-    fk_anon(ast::Sigil),                    // fn@(x, y) { ... }
-    fk_fn_block,                            // |x, y| ...
+    // fn foo() or extern "Abi" fn foo()
+    fk_item_fn(ident, &'self Generics, purity, AbiSet),
+
+    // fn foo(&self)
+    fk_method(ident, &'self Generics, &'self method),
+
+    // fn@(x, y) { ... }
+    fk_anon(ast::Sigil),
+
+    // |x, y| ...
+    fk_fn_block,
+
     fk_dtor( // class destructor
         &'self Generics,
         &'self [attribute],
@@ -43,8 +52,8 @@ pub enum fn_kind<'self> {
 
 pub fn name_of_fn(fk: &fn_kind) -> ident {
     match *fk {
-      fk_item_fn(name, _, _) | fk_method(name, _, _) => {
-          /* FIXME (#2543) */ copy name
+      fk_item_fn(name, _, _, _) | fk_method(name, _, _) => {
+          name
       }
       fk_anon(*) | fk_fn_block(*) => parse::token::special_idents::anon,
       fk_dtor(*)                  => parse::token::special_idents::dtor
@@ -53,7 +62,7 @@ pub fn name_of_fn(fk: &fn_kind) -> ident {
 
 pub fn generics_of_fn(fk: &fn_kind) -> Generics {
     match *fk {
-        fk_item_fn(_, generics, _) |
+        fk_item_fn(_, generics, _, _) |
         fk_method(_, generics, _) |
         fk_dtor(generics, _, _, _) => {
             copy *generics
@@ -144,12 +153,13 @@ pub fn visit_item<E>(i: @item, e: E, v: vt<E>) {
             (v.visit_ty)(t, e, v);
             (v.visit_expr)(ex, e, v);
         }
-        item_fn(ref decl, purity, ref generics, ref body) => {
+        item_fn(ref decl, purity, abi, ref generics, ref body) => {
             (v.visit_fn)(
                 &fk_item_fn(
-                    /* FIXME (#2543) */ copy i.ident,
+                    i.ident,
                     generics,
-                    purity
+                    purity,
+                    abi
                 ),
                 decl,
                 body,