diff options
| author | Niko Matsakis <niko@alum.mit.edu> | 2013-03-13 22:25:28 -0400 |
|---|---|---|
| committer | Brian Anderson <banderson@mozilla.com> | 2013-03-29 18:36:20 -0700 |
| commit | 6965fe4bceea836586bd8e7aa01a92a35b467f78 (patch) | |
| tree | d95089f050cd67db2a5171a799763faa09f5b0a8 /src/libsyntax | |
| parent | f864934f548be9f03d2c0512de8d7e908469e2ae (diff) | |
| download | rust-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.rs | 427 | ||||
| -rw-r--r-- | src/libsyntax/ast.rs | 41 | ||||
| -rw-r--r-- | src/libsyntax/ast_map.rs | 11 | ||||
| -rw-r--r-- | src/libsyntax/ast_util.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/attr.rs | 21 | ||||
| -rw-r--r-- | src/libsyntax/ext/pipes/ast_builder.rs | 4 | ||||
| -rw-r--r-- | src/libsyntax/fold.rs | 7 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 113 | ||||
| -rw-r--r-- | src/libsyntax/print/pprust.rs | 64 | ||||
| -rw-r--r-- | src/libsyntax/syntax.rc | 1 | ||||
| -rw-r--r-- | src/libsyntax/visit.rs | 30 |
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, |
