From 31f5ab3f0c59f65e6758fe5ffadb617a98dcd5b4 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Sun, 14 Dec 2014 15:42:41 +1300 Subject: [PATCH] Allow `Self` in impls. --- src/libsyntax/ext/base.rs | 2 +- src/libsyntax/ext/expand.rs | 51 +++++++++++++++++++++++++++++++--- src/test/run-pass/self-impl.rs | 42 ++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 src/test/run-pass/self-impl.rs diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 3947a602809..aefbb2a1fea 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -490,7 +490,7 @@ impl<'a> ExtCtxt<'a> { /// Returns a `Folder` for deeply expanding all macros in a AST node. pub fn expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> { - expand::MacroExpander { cx: self } + expand::MacroExpander::new(self) } pub fn new_parser_from_tts(&self, tts: &[ast::TokenTree]) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 20c8ff20b71..01d55eba316 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -15,6 +15,7 @@ use ast::{ItemMac, MacStmtWithSemicolon, Mrk, Stmt, StmtDecl, StmtMac}; use ast::{StmtExpr, StmtSemi}; use ast::TokenTree; use ast; +use ast_util::path_to_ident; use ext::mtwt; use ext::build::AstBuilder; use attr; @@ -37,6 +38,30 @@ enum Either { Right(R) } +pub fn expand_type(t: P, + fld: &mut MacroExpander, + impl_ty: Option>) + -> P { + debug!("expanding type {} with impl_ty {}", t, impl_ty); + let t = match (t.node.clone(), impl_ty) { + // Expand uses of `Self` in impls to the concrete type. + (ast::Ty_::TyPath(ref path, _), Some(ref impl_ty)) => { + let path_as_ident = path_to_ident(path); + // Note unhygenic comparison here. I think this is correct, since + // even though `Self` is almost just a type parameter, the treatment + // for this expansion is as if it were a keyword. + if path_as_ident.is_some() && + path_as_ident.unwrap().name == token::special_idents::type_self.name { + impl_ty.clone() + } else { + t + } + } + _ => t + }; + fold::noop_fold_ty(t, fld) +} + pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { e.and_then(|ast::Expr {id, node, span}| match node { // expr_mac should really be expr_ext or something; it's the @@ -1059,6 +1084,14 @@ fn expand_and_rename_fn_decl_and_block(fn_decl: P, block: P { pub cx: &'a mut ExtCtxt<'b>, + // The type of the impl currently being expanded. + current_impl_type: Option>, +} + +impl<'a, 'b> MacroExpander<'a, 'b> { + pub fn new(cx: &'a mut ExtCtxt<'b>) -> MacroExpander<'a, 'b> { + MacroExpander { cx: cx, current_impl_type: None } + } } impl<'a, 'b> Folder for MacroExpander<'a, 'b> { @@ -1071,7 +1104,14 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { } fn fold_item(&mut self, item: P) -> SmallVector> { - expand_item(item, self) + let prev_type = self.current_impl_type.clone(); + if let ast::Item_::ItemImpl(_, _, _, ref ty, _) = item.node { + self.current_impl_type = Some(ty.clone()); + } + + let result = expand_item(item, self); + self.current_impl_type = prev_type; + result } fn fold_item_underscore(&mut self, item: ast::Item_) -> ast::Item_ { @@ -1094,6 +1134,11 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { expand_method(method, self) } + fn fold_ty(&mut self, t: P) -> P { + let impl_type = self.current_impl_type.clone(); + expand_type(t, self, impl_type) + } + fn new_span(&mut self, span: Span) -> Span { new_span(self.cx, span) } @@ -1138,9 +1183,7 @@ pub fn expand_crate(parse_sess: &parse::ParseSess, user_exts: Vec, c: Crate) -> Crate { let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg); - let mut expander = MacroExpander { - cx: &mut cx, - }; + let mut expander = MacroExpander::new(&mut cx); for ExportedMacros { crate_name, macros } in imported_macros.into_iter() { let name = format!("<{} macros>", token::get_ident(crate_name)) diff --git a/src/test/run-pass/self-impl.rs b/src/test/run-pass/self-impl.rs new file mode 100644 index 00000000000..3ece042aef0 --- /dev/null +++ b/src/test/run-pass/self-impl.rs @@ -0,0 +1,42 @@ +// 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. + +// Test that we can use `Self` types in impls in the expected way. + +struct Foo; + +// Test uses on inherant impl. +impl Foo { + fn foo(_x: Self, _y: &Self, _z: Box) -> Self { + Foo + } +} + +// Test uses when implementing a trait and with a type parameter. +pub struct Baz { + pub f: X, +} + +trait Bar { + fn bar(x: Self, y: &Self, z: Box) -> Self; +} + +impl Bar for Box> { + fn bar(_x: Self, _y: &Self, _z: Box) -> Self { + box Baz { f: 42 } + } +} + +fn main() { + let _: Foo = Foo::foo(Foo, &Foo, box Foo); + let _: Box> = Bar::bar(box Baz { f: 42 }, + &box Baz { f: 42 }, + box box Baz { f: 42 }); +}