Skip to content

Commit a029ea3

Browse files
authored
Auto merge of #35957 - alexcrichton:macros-1.1, r=nrc
rustc: Implement custom derive (macros 1.1) This commit is an implementation of [RFC 1681] which adds support to the compiler for first-class user-define custom `#[derive]` modes with a far more stable API than plugins have today. [RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md The main features added by this commit are: * A new `rustc-macro` crate-type. This crate type represents one which will provide custom `derive` implementations and perhaps eventually flower into the implementation of macros 2.0 as well. * A new `rustc_macro` crate in the standard distribution. This crate will provide the runtime interface between macro crates and the compiler. The API here is particularly conservative right now but has quite a bit of room to expand into any manner of APIs required by macro authors. * The ability to load new derive modes through the `#[macro_use]` annotations on other crates. All support added here is gated behind the `rustc_macro` feature gate, both for the library support (the `rustc_macro` crate) as well as the language features. There are a few minor differences from the implementation outlined in the RFC, such as the `rustc_macro` crate being available as a dylib and all symbols are `dlsym`'d directly instead of having a shim compiled. These should only affect the implementation, however, not the public interface. This commit also ended up touching a lot of code related to `#[derive]`, making a few notable changes: * Recognized derive attributes are no longer desugared to `derive_Foo`. Wasn't sure how to keep this behavior and *not* expose it to custom derive. * Derive attributes no longer have access to unstable features by default, they have to opt in on a granular level. * The `derive(Copy,Clone)` optimization is now done through another "obscure attribute" which is just intended to ferry along in the compiler that such an optimization is possible. The `derive(PartialEq,Eq)` optimization was also updated to do something similar. --- One part of this PR which needs to be improved before stabilizing are the errors and exact interfaces here. The error messages are relatively poor quality and there are surprising spects of this such as `#[derive(PartialEq, Eq, MyTrait)]` not working by default. The custom attributes added by the compiler end up becoming unstable again when going through a custom impl. Hopefully though this is enough to start allowing experimentation on crates.io!
2 parents d128e6b + ecc6c39 commit a029ea3

File tree

84 files changed

+2211
-277
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+2211
-277
lines changed

mk/crates.mk

+6-4
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ RUSTC_CRATES := rustc rustc_typeck rustc_mir rustc_borrowck rustc_resolve rustc_
5959
rustc_trans rustc_back rustc_llvm rustc_privacy rustc_lint \
6060
rustc_data_structures rustc_platform_intrinsics rustc_errors \
6161
rustc_plugin rustc_metadata rustc_passes rustc_save_analysis \
62-
rustc_const_eval rustc_const_math rustc_incremental
62+
rustc_const_eval rustc_const_math rustc_incremental rustc_macro
6363
HOST_CRATES := syntax syntax_ext proc_macro syntax_pos $(RUSTC_CRATES) rustdoc fmt_macros \
6464
flate arena graphviz rbml log serialize
6565
TOOLS := compiletest rustdoc rustc rustbook error_index_generator
@@ -99,7 +99,7 @@ DEPS_term := std
9999
DEPS_test := std getopts term native:rust_test_helpers
100100

101101
DEPS_syntax := std term serialize log arena libc rustc_bitflags rustc_unicode rustc_errors syntax_pos
102-
DEPS_syntax_ext := syntax syntax_pos rustc_errors fmt_macros
102+
DEPS_syntax_ext := syntax syntax_pos rustc_errors fmt_macros rustc_macro
103103
DEPS_proc_macro := syntax syntax_pos rustc_plugin log
104104
DEPS_syntax_pos := serialize
105105

@@ -118,11 +118,13 @@ DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_bo
118118
rustc_trans rustc_privacy rustc_lint rustc_plugin \
119119
rustc_metadata syntax_ext proc_macro \
120120
rustc_passes rustc_save_analysis rustc_const_eval \
121-
rustc_incremental syntax_pos rustc_errors
121+
rustc_incremental syntax_pos rustc_errors rustc_macro
122122
DEPS_rustc_errors := log libc serialize syntax_pos
123123
DEPS_rustc_lint := rustc log syntax syntax_pos rustc_const_eval
124124
DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags
125-
DEPS_rustc_metadata := rustc syntax syntax_pos rustc_errors rbml rustc_const_math
125+
DEPS_rustc_macro := std syntax
126+
DEPS_rustc_metadata := rustc syntax syntax_pos rustc_errors rbml rustc_const_math \
127+
rustc_macro syntax_ext
126128
DEPS_rustc_passes := syntax syntax_pos rustc core rustc_const_eval rustc_errors
127129
DEPS_rustc_mir := rustc syntax syntax_pos rustc_const_math rustc_const_eval rustc_bitflags
128130
DEPS_rustc_resolve := arena rustc log syntax syntax_pos rustc_errors

src/librustc/middle/dependency_format.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,13 @@ fn calculate_type(sess: &session::Session,
139139
}
140140
}
141141

142-
// Everything else falls through below
143-
config::CrateTypeExecutable | config::CrateTypeDylib => {},
142+
// Everything else falls through below. This will happen either with the
143+
// `-C prefer-dynamic` or because we're a rustc-macro crate. Note that
144+
// rustc-macro crates are required to be dylibs, and they're currently
145+
// required to link to libsyntax as well.
146+
config::CrateTypeExecutable |
147+
config::CrateTypeDylib |
148+
config::CrateTypeRustcMacro => {},
144149
}
145150

146151
let mut formats = FnvHashMap();

src/librustc/middle/reachable.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
138138
// Creates a new reachability computation context.
139139
fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> ReachableContext<'a, 'tcx> {
140140
let any_library = tcx.sess.crate_types.borrow().iter().any(|ty| {
141-
*ty == config::CrateTypeRlib || *ty == config::CrateTypeDylib
141+
*ty == config::CrateTypeRlib || *ty == config::CrateTypeDylib ||
142+
*ty == config::CrateTypeRustcMacro
142143
});
143144
ReachableContext {
144145
tcx: tcx,

src/librustc/middle/weak_lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ fn verify(sess: &Session, items: &lang_items::LanguageItems) {
7070
let needs_check = sess.crate_types.borrow().iter().any(|kind| {
7171
match *kind {
7272
config::CrateTypeDylib |
73+
config::CrateTypeRustcMacro |
7374
config::CrateTypeCdylib |
7475
config::CrateTypeExecutable |
7576
config::CrateTypeStaticlib => true,

src/librustc/session/config.rs

+6
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@ pub enum CrateType {
475475
CrateTypeRlib,
476476
CrateTypeStaticlib,
477477
CrateTypeCdylib,
478+
CrateTypeRustcMacro,
478479
}
479480

480481
#[derive(Clone, Hash)]
@@ -962,6 +963,9 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig {
962963
if sess.opts.debug_assertions {
963964
ret.push(attr::mk_word_item(InternedString::new("debug_assertions")));
964965
}
966+
if sess.opts.crate_types.contains(&CrateTypeRustcMacro) {
967+
ret.push(attr::mk_word_item(InternedString::new("rustc_macro")));
968+
}
965969
return ret;
966970
}
967971

@@ -1547,6 +1551,7 @@ pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateTy
15471551
"dylib" => CrateTypeDylib,
15481552
"cdylib" => CrateTypeCdylib,
15491553
"bin" => CrateTypeExecutable,
1554+
"rustc-macro" => CrateTypeRustcMacro,
15501555
_ => {
15511556
return Err(format!("unknown crate type: `{}`",
15521557
part));
@@ -1635,6 +1640,7 @@ impl fmt::Display for CrateType {
16351640
CrateTypeRlib => "rlib".fmt(f),
16361641
CrateTypeStaticlib => "staticlib".fmt(f),
16371642
CrateTypeCdylib => "cdylib".fmt(f),
1643+
CrateTypeRustcMacro => "rustc-macro".fmt(f),
16381644
}
16391645
}
16401646
}

src/librustc/session/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ pub struct Session {
6262
pub entry_fn: RefCell<Option<(NodeId, Span)>>,
6363
pub entry_type: Cell<Option<config::EntryFnType>>,
6464
pub plugin_registrar_fn: Cell<Option<ast::NodeId>>,
65+
pub derive_registrar_fn: Cell<Option<ast::NodeId>>,
6566
pub default_sysroot: Option<PathBuf>,
6667
// The name of the root source file of the crate, in the local file system.
6768
// The path is always expected to be absolute. `None` means that there is no
@@ -314,6 +315,12 @@ impl Session {
314315
format!("__rustc_plugin_registrar__{}_{}", svh, index.as_usize())
315316
}
316317

318+
pub fn generate_derive_registrar_symbol(&self,
319+
svh: &Svh,
320+
index: DefIndex) -> String {
321+
format!("__rustc_derive_registrar__{}_{}", svh, index.as_usize())
322+
}
323+
317324
pub fn sysroot<'a>(&'a self) -> &'a Path {
318325
match self.opts.maybe_sysroot {
319326
Some (ref sysroot) => sysroot,
@@ -501,6 +508,7 @@ pub fn build_session_(sopts: config::Options,
501508
entry_fn: RefCell::new(None),
502509
entry_type: Cell::new(None),
503510
plugin_registrar_fn: Cell::new(None),
511+
derive_registrar_fn: Cell::new(None),
504512
default_sysroot: default_sysroot,
505513
local_crate_source_file: local_crate_source_file,
506514
working_dir: env::current_dir().unwrap(),

src/librustc/ty/context.rs

+5
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,10 @@ pub struct GlobalCtxt<'tcx> {
495495

496496
/// Cache for layouts computed from types.
497497
pub layout_cache: RefCell<FnvHashMap<Ty<'tcx>, &'tcx Layout>>,
498+
499+
/// Map from function to the `#[derive]` mode that it's defining. Only used
500+
/// by `rustc-macro` crates.
501+
pub derive_macros: RefCell<NodeMap<token::InternedString>>,
498502
}
499503

500504
impl<'tcx> GlobalCtxt<'tcx> {
@@ -756,6 +760,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
756760
crate_name: token::intern_and_get_ident(crate_name),
757761
data_layout: data_layout,
758762
layout_cache: RefCell::new(FnvHashMap()),
763+
derive_macros: RefCell::new(NodeMap()),
759764
}, f)
760765
}
761766
}
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use rustc::dep_graph::DepNode;
12+
use rustc::hir::intravisit::Visitor;
13+
use rustc::hir::map::Map;
14+
use rustc::hir;
15+
use syntax::ast;
16+
use syntax::attr;
17+
18+
pub fn find(hir_map: &Map) -> Option<ast::NodeId> {
19+
let _task = hir_map.dep_graph.in_task(DepNode::PluginRegistrar);
20+
let krate = hir_map.krate();
21+
22+
let mut finder = Finder { registrar: None };
23+
krate.visit_all_items(&mut finder);
24+
finder.registrar
25+
}
26+
27+
struct Finder {
28+
registrar: Option<ast::NodeId>,
29+
}
30+
31+
impl<'v> Visitor<'v> for Finder {
32+
fn visit_item(&mut self, item: &hir::Item) {
33+
if attr::contains_name(&item.attrs, "rustc_derive_registrar") {
34+
self.registrar = Some(item.id);
35+
}
36+
}
37+
}

src/librustc_driver/driver.rs

+18
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ use syntax::util::node_count::NodeCounter;
5555
use syntax;
5656
use syntax_ext;
5757

58+
use derive_registrar;
59+
5860
#[derive(Clone)]
5961
pub struct Resolutions {
6062
pub def_map: DefMap,
@@ -696,6 +698,18 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
696698
sess.diagnostic())
697699
});
698700

701+
krate = time(time_passes, "maybe creating a macro crate", || {
702+
let crate_types = sess.crate_types.borrow();
703+
let is_rustc_macro_crate = crate_types.contains(&config::CrateTypeRustcMacro);
704+
let num_crate_types = crate_types.len();
705+
syntax_ext::rustc_macro_registrar::modify(&sess.parse_sess,
706+
krate,
707+
is_rustc_macro_crate,
708+
num_crate_types,
709+
sess.diagnostic(),
710+
&sess.features.borrow())
711+
});
712+
699713
let resolver_arenas = Resolver::arenas();
700714
let mut resolver = Resolver::new(sess, make_glob_map, &resolver_arenas);
701715

@@ -838,6 +852,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
838852
sess.plugin_registrar_fn.set(time(time_passes, "looking for plugin registrar", || {
839853
plugin::build::find_plugin_registrar(sess.diagnostic(), &hir_map)
840854
}));
855+
sess.derive_registrar_fn.set(derive_registrar::find(&hir_map));
841856

842857
let region_map = time(time_passes,
843858
"region resolution",
@@ -1171,6 +1186,9 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<c
11711186
Some(ref n) if *n == "staticlib" => {
11721187
Some(config::CrateTypeStaticlib)
11731188
}
1189+
Some(ref n) if *n == "rustc-macro" => {
1190+
Some(config::CrateTypeRustcMacro)
1191+
}
11741192
Some(ref n) if *n == "bin" => Some(config::CrateTypeExecutable),
11751193
Some(_) => {
11761194
session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPES,

src/librustc_driver/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ pub mod test;
107107
pub mod driver;
108108
pub mod pretty;
109109
pub mod target_features;
110-
110+
mod derive_registrar;
111111

112112
const BUG_REPORT_URL: &'static str = "https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.\
113113
md#bug-reports";

src/librustc_macro/Cargo.toml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
authors = ["The Rust Project Developers"]
3+
name = "rustc_macro"
4+
version = "0.0.0"
5+
6+
[lib]
7+
name = "rustc_macro"
8+
path = "lib.rs"
9+
crate-type = ["dylib"]
10+
11+
[dependencies]
12+
syntax = { path = "../libsyntax" }

0 commit comments

Comments
 (0)