From 2a2c9d38c78ece0a6c5de80e382a136173e64b14 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 26 Aug 2016 19:23:42 +0300 Subject: [PATCH 01/29] Improve shallow `Clone` deriving --- src/libcore/clone.rs | 19 +- src/libsyntax_ext/deriving/clone.rs | 179 +++++++++++------- src/libsyntax_ext/deriving/generic/mod.rs | 27 ++- .../borrowck/borrowck-union-borrow-nested.rs | 6 +- .../borrowck/borrowck-union-borrow.rs | 6 +- src/test/compile-fail/union/union-copy.rs | 4 +- .../compile-fail/union/union-derive-clone.rs | 41 ++++ src/test/compile-fail/union/union-derive.rs | 1 - src/test/run-pass/union/union-c-interop.rs | 7 +- src/test/run-pass/union/union-derive.rs | 9 +- 10 files changed, 199 insertions(+), 100 deletions(-) create mode 100644 src/test/compile-fail/union/union-derive-clone.rs diff --git a/src/libcore/clone.rs b/src/libcore/clone.rs index 748bb62a1f3eb..3333cbfc1fc4e 100644 --- a/src/libcore/clone.rs +++ b/src/libcore/clone.rs @@ -106,10 +106,23 @@ pub trait Clone : Sized { } } -// FIXME(aburka): this method is used solely by #[derive] to -// assert that every component of a type implements Clone. +// FIXME(aburka): these structs are used solely by #[derive] to +// assert that every component of a type implements Clone or Copy. // -// This should never be called by user code. +// These structs should never appear in user code. +#[doc(hidden)] +#[allow(missing_debug_implementations)] +#[unstable(feature = "derive_clone_copy", + reason = "deriving hack, should not be public", + issue = "0")] +pub struct AssertParamIsClone { _field: ::marker::PhantomData } +#[doc(hidden)] +#[allow(missing_debug_implementations)] +#[unstable(feature = "derive_clone_copy", + reason = "deriving hack, should not be public", + issue = "0")] +pub struct AssertParamIsCopy { _field: ::marker::PhantomData } +#[cfg(stage0)] #[doc(hidden)] #[inline(always)] #[unstable(feature = "derive_clone_copy", diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs index c7afaaf4796a4..aa7c2c301dd7a 100644 --- a/src/libsyntax_ext/deriving/clone.rs +++ b/src/libsyntax_ext/deriving/clone.rs @@ -11,20 +11,14 @@ use deriving::generic::*; use deriving::generic::ty::*; -use syntax::ast::{Expr, Generics, ItemKind, MetaItem, VariantData}; +use syntax::ast::{self, Expr, Generics, ItemKind, MetaItem, VariantData}; use syntax::attr; use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; -use syntax::parse::token::InternedString; +use syntax::parse::token::{keywords, InternedString}; use syntax::ptr::P; use syntax_pos::Span; -#[derive(PartialEq)] -enum Mode { - Deep, - Shallow, -} - pub fn expand_deriving_clone(cx: &mut ExtCtxt, span: Span, mitem: &MetaItem, @@ -40,29 +34,38 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, // if we used the short form with generics, we'd have to bound the generics with // Clone + Copy, and then there'd be no Clone impl at all if the user fills in something // that is Clone but not Copy. and until specialization we can't write both impls. + // - the item is a union with Copy fields + // Unions with generic parameters still can derive Clone because they require Copy + // for deriving, Clone alone is not enough. + // Whever Clone is implemented for fields is irrelevant so we don't assert it. let bounds; - let unify_fieldless_variants; let substructure; + let is_shallow; match *item { Annotatable::Item(ref annitem) => { match annitem.node { ItemKind::Struct(_, Generics { ref ty_params, .. }) | ItemKind::Enum(_, Generics { ref ty_params, .. }) - if ty_params.is_empty() && - attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") => { - + if attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") && + ty_params.is_empty() => { + bounds = vec![]; + is_shallow = true; + substructure = combine_substructure(Box::new(|c, s, sub| { + cs_clone_shallow("Clone", c, s, sub, false) + })); + } + ItemKind::Union(..) => { bounds = vec![Literal(path_std!(cx, core::marker::Copy))]; - unify_fieldless_variants = true; + is_shallow = true; substructure = combine_substructure(Box::new(|c, s, sub| { - cs_clone("Clone", c, s, sub, Mode::Shallow) + cs_clone_shallow("Clone", c, s, sub, true) })); } - _ => { bounds = vec![]; - unify_fieldless_variants = false; + is_shallow = false; substructure = combine_substructure(Box::new(|c, s, sub| { - cs_clone("Clone", c, s, sub, Mode::Deep) + cs_clone("Clone", c, s, sub) })); } } @@ -80,7 +83,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, additional_bounds: bounds, generics: LifetimeBounds::empty(), is_unsafe: false, - supports_unions: false, + supports_unions: true, methods: vec![MethodDef { name: "clone", generics: LifetimeBounds::empty(), @@ -89,37 +92,85 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, ret_ty: Self_, attributes: attrs, is_unsafe: false, - unify_fieldless_variants: unify_fieldless_variants, + unify_fieldless_variants: false, combine_substructure: substructure, }], associated_types: Vec::new(), }; - trait_def.expand(cx, mitem, item, push) + trait_def.expand_ext(cx, mitem, item, push, is_shallow) +} + +fn cs_clone_shallow(name: &str, + cx: &mut ExtCtxt, + trait_span: Span, + substr: &Substructure, + is_union: bool) + -> P { + fn assert_ty_bounds(cx: &mut ExtCtxt, stmts: &mut Vec, + ty: P, span: Span, helper_name: &str) { + // Generate statement `let _: helper_name;`, + // set the expn ID so we can use the unstable struct. + let span = super::allow_unstable(cx, span, "derive(Clone)"); + let assert_path = cx.path_all(span, true, + cx.std_path(&["clone", helper_name]), + vec![], vec![ty], vec![]); + let local = P(ast::Local { + pat: cx.pat_wild(span), + ty: Some(cx.ty_path(assert_path)), + init: None, + id: ast::DUMMY_NODE_ID, + span: span, + attrs: ast::ThinVec::new(), + }); + let stmt = ast::Stmt { + id: ast::DUMMY_NODE_ID, + node: ast::StmtKind::Local(local), + span: span, + }; + stmts.push(stmt); + } + fn process_variant(cx: &mut ExtCtxt, stmts: &mut Vec, variant: &VariantData) { + for field in variant.fields() { + // let _: AssertParamIsClone; + assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone"); + } + } + + let mut stmts = Vec::new(); + if is_union { + // let _: AssertParamIsCopy; + let self_ty = cx.ty_path(cx.path_ident(trait_span, keywords::SelfType.ident())); + assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, "AssertParamIsCopy"); + } else { + match *substr.fields { + StaticStruct(vdata, ..) => { + process_variant(cx, &mut stmts, vdata); + } + StaticEnum(enum_def, ..) => { + for variant in &enum_def.variants { + process_variant(cx, &mut stmts, &variant.node.data); + } + } + _ => cx.span_bug(trait_span, &format!("unexpected substructure in \ + shallow `derive({})`", name)) + } + } + stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span)))); + cx.expr_block(cx.block(trait_span, stmts)) } fn cs_clone(name: &str, cx: &mut ExtCtxt, trait_span: Span, - substr: &Substructure, - mode: Mode) + substr: &Substructure) -> P { let ctor_path; let all_fields; - let fn_path = match mode { - Mode::Shallow => cx.std_path(&["clone", "assert_receiver_is_clone"]), - Mode::Deep => cx.std_path(&["clone", "Clone", "clone"]), - }; + let fn_path = cx.std_path(&["clone", "Clone", "clone"]); let subcall = |cx: &mut ExtCtxt, field: &FieldInfo| { let args = vec![cx.expr_addr_of(field.span, field.self_.clone())]; - - let span = if mode == Mode::Shallow { - // set the expn ID so we can call the unstable method - super::allow_unstable(cx, field.span, "derive(Clone)") - } else { - field.span - }; - cx.expr_call_global(span, fn_path.clone(), args) + cx.expr_call_global(field.span, fn_path.clone(), args) }; let vdata; @@ -145,43 +196,31 @@ fn cs_clone(name: &str, } } - match mode { - Mode::Shallow => { - let mut stmts = all_fields.iter().map(|f| { - let call = subcall(cx, f); - cx.stmt_expr(call) - }).collect::>(); - stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span)))); - cx.expr_block(cx.block(trait_span, stmts)) - } - Mode::Deep => { - match *vdata { - VariantData::Struct(..) => { - let fields = all_fields.iter() - .map(|field| { - let ident = match field.name { - Some(i) => i, - None => { - cx.span_bug(trait_span, - &format!("unnamed field in normal struct in \ - `derive({})`", - name)) - } - }; - let call = subcall(cx, field); - cx.field_imm(field.span, ident, call) - }) - .collect::>(); + match *vdata { + VariantData::Struct(..) => { + let fields = all_fields.iter() + .map(|field| { + let ident = match field.name { + Some(i) => i, + None => { + cx.span_bug(trait_span, + &format!("unnamed field in normal struct in \ + `derive({})`", + name)) + } + }; + let call = subcall(cx, field); + cx.field_imm(field.span, ident, call) + }) + .collect::>(); - cx.expr_struct(trait_span, ctor_path, fields) - } - VariantData::Tuple(..) => { - let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect(); - let path = cx.expr_path(ctor_path); - cx.expr_call(trait_span, path, subcalls) - } - VariantData::Unit(..) => cx.expr_path(ctor_path), - } + cx.expr_struct(trait_span, ctor_path, fields) + } + VariantData::Tuple(..) => { + let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect(); + let path = cx.expr_path(ctor_path); + cx.expr_call(trait_span, path, subcalls) } + VariantData::Unit(..) => cx.expr_path(ctor_path), } } diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index 600f5d335c472..339a6c477ccd5 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -401,18 +401,29 @@ impl<'a> TraitDef<'a> { mitem: &ast::MetaItem, item: &'a Annotatable, push: &mut FnMut(Annotatable)) { + self.expand_ext(cx, mitem, item, push, false); + } + + pub fn expand_ext(&self, + cx: &mut ExtCtxt, + mitem: &ast::MetaItem, + item: &'a Annotatable, + push: &mut FnMut(Annotatable), + from_scratch: bool) { match *item { Annotatable::Item(ref item) => { let newitem = match item.node { ast::ItemKind::Struct(ref struct_def, ref generics) => { - self.expand_struct_def(cx, &struct_def, item.ident, generics) + self.expand_struct_def(cx, &struct_def, item.ident, generics, from_scratch) } ast::ItemKind::Enum(ref enum_def, ref generics) => { - self.expand_enum_def(cx, enum_def, &item.attrs, item.ident, generics) + self.expand_enum_def(cx, enum_def, &item.attrs, + item.ident, generics, from_scratch) } ast::ItemKind::Union(ref struct_def, ref generics) => { if self.supports_unions { - self.expand_struct_def(cx, &struct_def, item.ident, generics) + self.expand_struct_def(cx, &struct_def, item.ident, + generics, from_scratch) } else { cx.span_err(mitem.span, "this trait cannot be derived for unions"); @@ -661,7 +672,8 @@ impl<'a> TraitDef<'a> { cx: &mut ExtCtxt, struct_def: &'a VariantData, type_ident: Ident, - generics: &Generics) + generics: &Generics, + from_scratch: bool) -> P { let field_tys: Vec> = struct_def.fields() .iter() @@ -674,7 +686,7 @@ impl<'a> TraitDef<'a> { let (explicit_self, self_args, nonself_args, tys) = method_def.split_self_nonself_args(cx, self, type_ident, generics); - let body = if method_def.is_static() { + let body = if from_scratch || method_def.is_static() { method_def.expand_static_struct_method_body(cx, self, struct_def, @@ -709,7 +721,8 @@ impl<'a> TraitDef<'a> { enum_def: &'a EnumDef, type_attrs: &[ast::Attribute], type_ident: Ident, - generics: &Generics) + generics: &Generics, + from_scratch: bool) -> P { let mut field_tys = Vec::new(); @@ -727,7 +740,7 @@ impl<'a> TraitDef<'a> { let (explicit_self, self_args, nonself_args, tys) = method_def.split_self_nonself_args(cx, self, type_ident, generics); - let body = if method_def.is_static() { + let body = if from_scratch || method_def.is_static() { method_def.expand_static_enum_method_body(cx, self, enum_def, diff --git a/src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs b/src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs index 19975d79b60be..8b6b8d9ecb08e 100644 --- a/src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs +++ b/src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs @@ -18,16 +18,12 @@ struct S { b: u16, } +#[derive(Clone, Copy)] union U { s: S, c: u32, } -impl Clone for U { - fn clone(&self) -> Self { *self } -} -impl Copy for U {} - fn main() { unsafe { { diff --git a/src/test/compile-fail/borrowck/borrowck-union-borrow.rs b/src/test/compile-fail/borrowck/borrowck-union-borrow.rs index e8989a3c2d499..ecc698acc317f 100644 --- a/src/test/compile-fail/borrowck/borrowck-union-borrow.rs +++ b/src/test/compile-fail/borrowck/borrowck-union-borrow.rs @@ -12,16 +12,12 @@ #![feature(untagged_unions)] +#[derive(Clone, Copy)] union U { a: u8, b: u64, } -impl Clone for U { - fn clone(&self) -> Self { *self } -} -impl Copy for U {} - fn main() { unsafe { let mut u = U { b: 0 }; diff --git a/src/test/compile-fail/union/union-copy.rs b/src/test/compile-fail/union/union-copy.rs index 6e08ae0074d48..9014b3f2956b7 100644 --- a/src/test/compile-fail/union/union-copy.rs +++ b/src/test/compile-fail/union/union-copy.rs @@ -10,16 +10,16 @@ #![feature(untagged_unions)] +#[derive(Clone)] union U { a: u8 } +#[derive(Clone)] union W { a: String } -impl Clone for U { fn clone(&self) { panic!(); } } -impl Clone for W { fn clone(&self) { panic!(); } } impl Copy for U {} // OK impl Copy for W {} //~ ERROR the trait `Copy` may not be implemented for this type diff --git a/src/test/compile-fail/union/union-derive-clone.rs b/src/test/compile-fail/union/union-derive-clone.rs new file mode 100644 index 0000000000000..6e226d7d79f9f --- /dev/null +++ b/src/test/compile-fail/union/union-derive-clone.rs @@ -0,0 +1,41 @@ +// Copyright 2016 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(untagged_unions)] + +#[derive(Clone)] //~ ERROR the trait bound `U1: std::marker::Copy` is not satisfied +union U1 { + a: u8, +} + +#[derive(Clone)] +union U2 { + a: u8, // OK +} + +impl Copy for U2 {} + +#[derive(Clone, Copy)] +union U3 { + a: u8, // OK +} + +#[derive(Clone, Copy)] +union U4 { + a: T, // OK +} + +#[derive(Clone)] +struct CloneNoCopy; + +fn main() { + let u = U4 { a: CloneNoCopy }; + let w = u.clone(); //~ ERROR no method named `clone` found for type `U4` +} diff --git a/src/test/compile-fail/union/union-derive.rs b/src/test/compile-fail/union/union-derive.rs index 0f78e96f640c7..01ce9696284df 100644 --- a/src/test/compile-fail/union/union-derive.rs +++ b/src/test/compile-fail/union/union-derive.rs @@ -13,7 +13,6 @@ #![feature(untagged_unions)] #[derive( - Clone, //~ ERROR this trait cannot be derived for unions PartialEq, //~ ERROR this trait cannot be derived for unions Eq, //~ ERROR this trait cannot be derived for unions PartialOrd, //~ ERROR this trait cannot be derived for unions diff --git a/src/test/run-pass/union/union-c-interop.rs b/src/test/run-pass/union/union-c-interop.rs index a9f97620ebd46..bea4d5f923e21 100644 --- a/src/test/run-pass/union/union-c-interop.rs +++ b/src/test/run-pass/union/union-c-interop.rs @@ -10,14 +10,14 @@ #![feature(untagged_unions)] -#[derive(Copy)] +#[derive(Clone, Copy)] #[repr(C)] struct LARGE_INTEGER_U { LowPart: u32, HighPart: u32, } -#[derive(Copy)] +#[derive(Clone, Copy)] #[repr(C)] union LARGE_INTEGER { __unnamed__: LARGE_INTEGER_U, @@ -25,9 +25,6 @@ union LARGE_INTEGER { QuadPart: u64, } -impl Clone for LARGE_INTEGER_U { fn clone(&self) -> Self { *self } } -impl Clone for LARGE_INTEGER { fn clone(&self) -> Self { *self } } - #[link(name = "rust_test_helpers")] extern "C" { fn increment_all_parts(_: LARGE_INTEGER) -> LARGE_INTEGER; diff --git a/src/test/run-pass/union/union-derive.rs b/src/test/run-pass/union/union-derive.rs index b71c23990a474..8314416e2b10a 100644 --- a/src/test/run-pass/union/union-derive.rs +++ b/src/test/run-pass/union/union-derive.rs @@ -14,18 +14,23 @@ #[derive( Copy, + Clone, )] union U { a: u8, b: u16, } -impl Clone for U { - fn clone(&self) -> Self { *self } +#[derive(Clone, Copy)] +union W { + a: T, } fn main() { let u = U { b: 0 }; let u1 = u; let u2 = u.clone(); + + let w = W { a: 0 }; + let w1 = w.clone(); } From 62cb7510ac6285c93ec691198a92f910582d31a2 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 26 Aug 2016 19:23:42 +0300 Subject: [PATCH 02/29] Improve `Eq` deriving --- src/libcore/cmp.rs | 13 ++++- src/libsyntax/ext/build.rs | 18 ++++++ src/libsyntax_ext/deriving/clone.rs | 15 +---- src/libsyntax_ext/deriving/cmp/eq.rs | 55 ++++++++++++------- .../compile-fail/union/union-derive-eq.rs | 30 ++++++++++ src/test/compile-fail/union/union-derive.rs | 1 - src/test/run-pass/union/union-derive.rs | 13 ++++- 7 files changed, 109 insertions(+), 36 deletions(-) create mode 100644 src/test/compile-fail/union/union-derive-eq.rs diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index 670978a2d49af..f990a27e52b31 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -129,7 +129,7 @@ pub trait PartialEq { /// This trait can be used with `#[derive]`. When `derive`d, because `Eq` has /// no extra methods, it is only informing the compiler that this is an /// equivalence relation rather than a partial equivalence relation. Note that -/// the `derive` strategy requires all fields are `PartialEq`, which isn't +/// the `derive` strategy requires all fields are `Eq`, which isn't /// always desired. /// /// ## How can I implement `Eq`? @@ -165,6 +165,17 @@ pub trait Eq: PartialEq { fn assert_receiver_is_total_eq(&self) {} } +// FIXME: this struct is used solely by #[derive] to +// assert that every component of a type implements Eq. +// +// This struct should never appear in user code. +#[doc(hidden)] +#[allow(missing_debug_implementations)] +#[unstable(feature = "derive_eq", + reason = "deriving hack, should not be public", + issue = "0")] +pub struct AssertParamIsEq { _field: ::marker::PhantomData } + /// An `Ordering` is the result of a comparison between two values. /// /// # Examples diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 3dcdbc8909627..b81d95a6998c3 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -97,6 +97,7 @@ pub trait AstBuilder { typ: P, ex: P) -> P; + fn stmt_let_type_only(&self, span: Span, ty: P) -> ast::Stmt; fn stmt_item(&self, sp: Span, item: P) -> ast::Stmt; // blocks @@ -577,6 +578,23 @@ impl<'a> AstBuilder for ExtCtxt<'a> { }) } + // Generate `let _: Type;`, usually used for type assertions. + fn stmt_let_type_only(&self, span: Span, ty: P) -> ast::Stmt { + let local = P(ast::Local { + pat: self.pat_wild(span), + ty: Some(ty), + init: None, + id: ast::DUMMY_NODE_ID, + span: span, + attrs: ast::ThinVec::new(), + }); + ast::Stmt { + id: ast::DUMMY_NODE_ID, + node: ast::StmtKind::Local(local), + span: span, + } + } + fn stmt_item(&self, sp: Span, item: P) -> ast::Stmt { ast::Stmt { id: ast::DUMMY_NODE_ID, diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs index aa7c2c301dd7a..d7bc2a6faeeb9 100644 --- a/src/libsyntax_ext/deriving/clone.rs +++ b/src/libsyntax_ext/deriving/clone.rs @@ -115,20 +115,7 @@ fn cs_clone_shallow(name: &str, let assert_path = cx.path_all(span, true, cx.std_path(&["clone", helper_name]), vec![], vec![ty], vec![]); - let local = P(ast::Local { - pat: cx.pat_wild(span), - ty: Some(cx.ty_path(assert_path)), - init: None, - id: ast::DUMMY_NODE_ID, - span: span, - attrs: ast::ThinVec::new(), - }); - let stmt = ast::Stmt { - id: ast::DUMMY_NODE_ID, - node: ast::StmtKind::Local(local), - span: span, - }; - stmts.push(stmt); + stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path))); } fn process_variant(cx: &mut ExtCtxt, stmts: &mut Vec, variant: &VariantData) { for field in variant.fields() { diff --git a/src/libsyntax_ext/deriving/cmp/eq.rs b/src/libsyntax_ext/deriving/cmp/eq.rs index 425a47a991bc4..fa0fb2492c551 100644 --- a/src/libsyntax_ext/deriving/cmp/eq.rs +++ b/src/libsyntax_ext/deriving/cmp/eq.rs @@ -11,7 +11,7 @@ use deriving::generic::*; use deriving::generic::ty::*; -use syntax::ast::{Expr, MetaItem}; +use syntax::ast::{self, Expr, MetaItem}; use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; use syntax::parse::token::InternedString; @@ -23,22 +23,6 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt, mitem: &MetaItem, item: &Annotatable, push: &mut FnMut(Annotatable)) { - fn cs_total_eq_assert(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P { - cs_same_method(|cx, span, exprs| { - // create `a.(); b.(); c.(); ...` - // (where method is `assert_receiver_is_total_eq`) - let stmts = exprs.into_iter().map(|e| cx.stmt_expr(e)).collect(); - let block = cx.block(span, stmts); - cx.expr_block(block) - }, - Box::new(|cx, sp, _, _| { - cx.span_bug(sp, "non matching enums in derive(Eq)?") - }), - cx, - span, - substr) - } - let inline = cx.meta_word(span, InternedString::new("inline")); let hidden = cx.meta_list_item_word(span, InternedString::new("hidden")); let doc = cx.meta_list(span, InternedString::new("doc"), vec![hidden]); @@ -50,7 +34,7 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt, additional_bounds: Vec::new(), generics: LifetimeBounds::empty(), is_unsafe: false, - supports_unions: false, + supports_unions: true, methods: vec![MethodDef { name: "assert_receiver_is_total_eq", generics: LifetimeBounds::empty(), @@ -66,5 +50,38 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt, }], associated_types: Vec::new(), }; - trait_def.expand(cx, mitem, item, push) + trait_def.expand_ext(cx, mitem, item, push, true) +} + +fn cs_total_eq_assert(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P { + fn assert_ty_bounds(cx: &mut ExtCtxt, stmts: &mut Vec, + ty: P, span: Span, helper_name: &str) { + // Generate statement `let _: helper_name;`, + // set the expn ID so we can use the unstable struct. + let span = super::allow_unstable(cx, span, "derive(Eq)"); + let assert_path = cx.path_all(span, true, + cx.std_path(&["cmp", helper_name]), + vec![], vec![ty], vec![]); + stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path))); + } + fn process_variant(cx: &mut ExtCtxt, stmts: &mut Vec, variant: &ast::VariantData) { + for field in variant.fields() { + // let _: AssertParamIsEq; + assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsEq"); + } + } + + let mut stmts = Vec::new(); + match *substr.fields { + StaticStruct(vdata, ..) => { + process_variant(cx, &mut stmts, vdata); + } + StaticEnum(enum_def, ..) => { + for variant in &enum_def.variants { + process_variant(cx, &mut stmts, &variant.node.data); + } + } + _ => cx.span_bug(trait_span, "unexpected substructure in `derive(Eq)`") + } + cx.expr_block(cx.block(trait_span, stmts)) } diff --git a/src/test/compile-fail/union/union-derive-eq.rs b/src/test/compile-fail/union/union-derive-eq.rs new file mode 100644 index 0000000000000..9dfec288c1572 --- /dev/null +++ b/src/test/compile-fail/union/union-derive-eq.rs @@ -0,0 +1,30 @@ +// Copyright 2016 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(untagged_unions)] + +#[derive(Eq)] // OK +union U1 { + a: u8, +} + +impl PartialEq for U1 { fn eq(&self, rhs: &Self) -> bool { true } } + +#[derive(PartialEq)] +struct PartialEqNotEq; + +#[derive(Eq)] +union U2 { + a: PartialEqNotEq, //~ ERROR the trait bound `PartialEqNotEq: std::cmp::Eq` is not satisfied +} + +impl PartialEq for U2 { fn eq(&self, rhs: &Self) -> bool { true } } + +fn main() {} diff --git a/src/test/compile-fail/union/union-derive.rs b/src/test/compile-fail/union/union-derive.rs index 01ce9696284df..26dbdfd0b4118 100644 --- a/src/test/compile-fail/union/union-derive.rs +++ b/src/test/compile-fail/union/union-derive.rs @@ -14,7 +14,6 @@ #[derive( PartialEq, //~ ERROR this trait cannot be derived for unions - Eq, //~ ERROR this trait cannot be derived for unions PartialOrd, //~ ERROR this trait cannot be derived for unions Ord, //~ ERROR this trait cannot be derived for unions Hash, //~ ERROR this trait cannot be derived for unions diff --git a/src/test/run-pass/union/union-derive.rs b/src/test/run-pass/union/union-derive.rs index 8314416e2b10a..8ff6f17394f9e 100644 --- a/src/test/run-pass/union/union-derive.rs +++ b/src/test/run-pass/union/union-derive.rs @@ -15,22 +15,33 @@ #[derive( Copy, Clone, + Eq, )] union U { a: u8, b: u16, } -#[derive(Clone, Copy)] +impl PartialEq for U { fn eq(&self, rhs: &Self) -> bool { true } } + +#[derive( + Clone, + Copy, + Eq +)] union W { a: T, } +impl PartialEq for W { fn eq(&self, rhs: &Self) -> bool { true } } + fn main() { let u = U { b: 0 }; let u1 = u; let u2 = u.clone(); + assert!(u1 == u2); let w = W { a: 0 }; let w1 = w.clone(); + assert!(w == w1); } From 6b99e011626a6f73747307907cdadbc8ab9ebffd Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 11 Sep 2016 16:45:49 -0600 Subject: [PATCH 03/29] Delete stray ` character in error message. --- src/librustc_lint/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index a6049acdb10d4..e8d9e90456efc 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -569,7 +569,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::TyTuple(_) => { FfiUnsafe("found Rust tuple type in foreign module; \ - consider using a struct instead`") + consider using a struct instead") } ty::TyRawPtr(ref m) | ty::TyRef(_, ref m) => { From 0a62676c73ce8050941d571427dfb621b004a3b8 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Mon, 12 Sep 2016 17:47:59 -0400 Subject: [PATCH 04/29] fix "X is not a member of trait Y" span labels The span labels for associated types and consts were hardcoded to `Foo` rather than substituting the name of the trait. This also normalizes the wording for associated methods', traits', and consts' span labels. Fixes #36428. --- src/librustc_resolve/lib.rs | 6 +++--- src/test/compile-fail/E0407.rs | 2 +- src/test/compile-fail/E0438.rs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index c5b505fba38e9..a11ef6e221dc2 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -247,7 +247,7 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>, "method `{}` is not a member of trait `{}`", method, trait_); - err.span_label(span, &format!("not a member of `{}`", trait_)); + err.span_label(span, &format!("not a member of trait `{}`", trait_)); err } ResolutionError::TypeNotMemberOfTrait(type_, trait_) => { @@ -257,7 +257,7 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>, "type `{}` is not a member of trait `{}`", type_, trait_); - err.span_label(span, &format!("not a member of trait `Foo`")); + err.span_label(span, &format!("not a member of trait `{}`", trait_)); err } ResolutionError::ConstNotMemberOfTrait(const_, trait_) => { @@ -267,7 +267,7 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>, "const `{}` is not a member of trait `{}`", const_, trait_); - err.span_label(span, &format!("not a member of trait `Foo`")); + err.span_label(span, &format!("not a member of trait `{}`", trait_)); err } ResolutionError::VariableNotBoundInPattern(variable_name, from, to) => { diff --git a/src/test/compile-fail/E0407.rs b/src/test/compile-fail/E0407.rs index 2a150b7451210..c207dbfca5565 100644 --- a/src/test/compile-fail/E0407.rs +++ b/src/test/compile-fail/E0407.rs @@ -18,7 +18,7 @@ impl Foo for Bar { fn a() {} fn b() {} //~^ ERROR E0407 - //~| NOTE not a member of `Foo` + //~| NOTE not a member of trait `Foo` } fn main() { diff --git a/src/test/compile-fail/E0438.rs b/src/test/compile-fail/E0438.rs index f549d62aebfea..2e2df4bee5a35 100644 --- a/src/test/compile-fail/E0438.rs +++ b/src/test/compile-fail/E0438.rs @@ -10,11 +10,11 @@ #![feature(associated_consts)] -trait Foo {} +trait Bar {} -impl Foo for i32 { +impl Bar for i32 { const BAR: bool = true; //~ ERROR E0438 - //~| NOTE not a member of trait `Foo` + //~| NOTE not a member of trait `Bar` } fn main () { From 50f94f6c95c944f08c4af264f48260e42efefd47 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Fri, 2 Sep 2016 22:01:35 +0000 Subject: [PATCH 05/29] Avoid needless reexpansions. --- src/libsyntax/ext/base.rs | 15 ++++++++++----- src/libsyntax_ext/format.rs | 19 ++++++++----------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index edd38ea23e2fd..f6eb6f1da4f19 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -13,7 +13,7 @@ pub use self::SyntaxExtension::*; use ast; use ast::{Name, PatKind}; use attr::HasAttrs; -use codemap::{self, CodeMap, ExpnInfo}; +use codemap::{self, CodeMap, ExpnInfo, Spanned, respan}; use syntax_pos::{Span, ExpnId, NO_EXPANSION}; use errors::DiagnosticBuilder; use ext; @@ -805,8 +805,8 @@ impl<'a> ExtCtxt<'a> { /// Extract a string literal from the macro expanded version of `expr`, /// emitting `err_msg` if `expr` is not a string literal. This does not stop /// compilation on error, merely emits a non-fatal error and returns None. -pub fn expr_to_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) - -> Option<(InternedString, ast::StrStyle)> { +pub fn expr_to_spanned_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) + -> Option> { // Update `expr.span`'s expn_id now in case expr is an `include!` macro invocation. let expr = expr.map(|mut expr| { expr.span.expn_id = cx.backtrace; @@ -817,7 +817,7 @@ pub fn expr_to_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) let expr = cx.expander().fold_expr(expr); match expr.node { ast::ExprKind::Lit(ref l) => match l.node { - ast::LitKind::Str(ref s, style) => return Some(((*s).clone(), style)), + ast::LitKind::Str(ref s, style) => return Some(respan(expr.span, (s.clone(), style))), _ => cx.span_err(l.span, err_msg) }, _ => cx.span_err(expr.span, err_msg) @@ -825,6 +825,11 @@ pub fn expr_to_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) None } +pub fn expr_to_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) + -> Option<(InternedString, ast::StrStyle)> { + expr_to_spanned_string(cx, expr, err_msg).map(|s| s.node) +} + /// Non-fatally assert that `tts` is empty. Note that this function /// returns even when `tts` is non-empty, macros that *need* to stop /// compilation should call @@ -851,7 +856,7 @@ pub fn get_single_str_from_tts(cx: &mut ExtCtxt, cx.span_err(sp, &format!("{} takes 1 argument", name)); return None } - let ret = cx.expander().fold_expr(panictry!(p.parse_expr())); + let ret = panictry!(p.parse_expr()); if p.token != token::Eof { cx.span_err(sp, &format!("{} takes 1 argument", name)); } diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index 06b16095d1963..892ebcfa76129 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -17,7 +17,6 @@ use syntax::ast; use syntax::ext::base::*; use syntax::ext::base; use syntax::ext::build::AstBuilder; -use syntax::fold::Folder; use syntax::parse::token::{self, keywords}; use syntax::ptr::P; use syntax_pos::{Span, DUMMY_SP}; @@ -702,10 +701,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, let arg_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect(); let arg_unique_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect(); let macsp = ecx.call_site(); - // Expand the format literal so that efmt.span will have a backtrace. This - // is essential for locating a bug when the format literal is generated in - // a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")). - let efmt = ecx.expander().fold_expr(efmt); + let msg = "format argument must be a string literal."; + let fmt = match expr_to_spanned_string(ecx, efmt, msg) { + Some(fmt) => fmt, + None => return DummyResult::raw_expr(sp), + }; + let mut cx = Context { ecx: ecx, args: args, @@ -723,14 +724,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, str_pieces: Vec::new(), all_pieces_simple: true, macsp: macsp, - fmtsp: efmt.span, - }; - let fmt = match expr_to_string(cx.ecx, efmt, "format argument must be a string literal.") { - Some((fmt, _)) => fmt, - None => return DummyResult::raw_expr(sp), + fmtsp: fmt.span, }; - let mut parser = parse::Parser::new(&fmt); + let mut parser = parse::Parser::new(&fmt.node.0); let mut pieces = vec![]; loop { From 60440b226d2f70bdae803443ff7ad2e2af2c9b10 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sun, 4 Sep 2016 22:49:45 +0000 Subject: [PATCH 06/29] Refactor `noop_fold_stmt_kind` out of `noop_fold_stmt`. --- src/libsyntax/fold.rs | 50 +++++++++++-------------------------------- 1 file changed, 13 insertions(+), 37 deletions(-) diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 7500bfe9caa80..9fb4d0203f41e 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1320,51 +1320,27 @@ pub fn noop_fold_exprs(es: Vec>, folder: &mut T) -> Vec(Stmt {node, span, id}: Stmt, folder: &mut T) - -> SmallVector { +pub fn noop_fold_stmt(Stmt {node, span, id}: Stmt, folder: &mut T) -> SmallVector { let id = folder.new_id(id); let span = folder.new_span(span); + noop_fold_stmt_kind(node, folder).into_iter().map(|node| { + Stmt { id: id, node: node, span: span } + }).collect() +} +pub fn noop_fold_stmt_kind(node: StmtKind, folder: &mut T) -> SmallVector { match node { - StmtKind::Local(local) => SmallVector::one(Stmt { - id: id, - node: StmtKind::Local(folder.fold_local(local)), - span: span, - }), - StmtKind::Item(item) => folder.fold_item(item).into_iter().map(|item| Stmt { - id: id, - node: StmtKind::Item(item), - span: span, - }).collect(), + StmtKind::Local(local) => SmallVector::one(StmtKind::Local(folder.fold_local(local))), + StmtKind::Item(item) => folder.fold_item(item).into_iter().map(StmtKind::Item).collect(), StmtKind::Expr(expr) => { - if let Some(expr) = folder.fold_opt_expr(expr) { - SmallVector::one(Stmt { - id: id, - node: StmtKind::Expr(expr), - span: span, - }) - } else { - SmallVector::zero() - } + folder.fold_opt_expr(expr).into_iter().map(StmtKind::Expr).collect() } StmtKind::Semi(expr) => { - if let Some(expr) = folder.fold_opt_expr(expr) { - SmallVector::one(Stmt { - id: id, - node: StmtKind::Semi(expr), - span: span, - }) - } else { - SmallVector::zero() - } + folder.fold_opt_expr(expr).into_iter().map(StmtKind::Semi).collect() } - StmtKind::Mac(mac) => SmallVector::one(Stmt { - id: id, - node: StmtKind::Mac(mac.map(|(mac, semi, attrs)| { - (folder.fold_mac(mac), semi, fold_attrs(attrs.into(), folder).into()) - })), - span: span, - }) + StmtKind::Mac(mac) => SmallVector::one(StmtKind::Mac(mac.map(|(mac, semi, attrs)| { + (folder.fold_mac(mac), semi, fold_attrs(attrs.into(), folder).into()) + }))), } } From a9821e1658240bb2c056f260a4b6bc9789301fae Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Mon, 5 Sep 2016 03:46:05 +0000 Subject: [PATCH 07/29] Refactor `ExtCtxt` to use a `Resolver` instead of a `MacroLoader`. --- src/librustc/middle/cstore.rs | 4 ++++ src/librustc_driver/driver.rs | 15 +++++++-------- src/librustc_metadata/macro_import.rs | 4 ++-- src/librustc_resolve/lib.rs | 18 +++++++++++++++++- src/libsyntax/ext/base.rs | 12 ++++++------ src/libsyntax/ext/expand.rs | 12 ++++++------ src/libsyntax/test.rs | 6 +++--- src/libsyntax_ext/rustc_macro_registrar.rs | 4 ++-- src/test/compile-fail-fulldeps/qquote.rs | 4 ++-- src/test/run-fail-fulldeps/qquote.rs | 4 ++-- src/test/run-pass-fulldeps/qquote.rs | 4 ++-- 11 files changed, 53 insertions(+), 34 deletions(-) diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index b33bc520fe216..5bbc0cd9d9071 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -39,6 +39,7 @@ use std::rc::Rc; use std::path::PathBuf; use syntax::ast; use syntax::attr; +use syntax::ext::base::LoadedMacro; use syntax::ptr::P; use syntax::parse::token::InternedString; use syntax_pos::Span; @@ -488,6 +489,9 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { fn metadata_encoding_version(&self) -> &[u8] { bug!("metadata_encoding_version") } } +pub trait MacroLoader { + fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec; +} /// Metadata encoding and decoding can make use of thread-local encoding and /// decoding contexts. These allow implementers of serialize::Encodable and diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index ba6c4b9b84c37..0d9bf14f12fb2 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -638,6 +638,12 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, } sess.track_errors(|| sess.lint_store.borrow_mut().process_command_line(sess))?; + let mut macro_loader = + macro_import::MacroLoader::new(sess, &cstore, crate_name, krate.config.clone()); + + let resolver_arenas = Resolver::arenas(); + let mut resolver = Resolver::new(sess, make_glob_map, &mut macro_loader, &resolver_arenas); + krate = time(time_passes, "expansion", || { // Windows dlls do not have rpaths, so they don't know how to find their // dependencies. It's up to us to tell the system where to find all the @@ -672,14 +678,10 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, trace_mac: sess.opts.debugging_opts.trace_macros, should_test: sess.opts.test, }; - let mut loader = macro_import::MacroLoader::new(sess, - &cstore, - crate_name, - krate.config.clone()); let mut ecx = syntax::ext::base::ExtCtxt::new(&sess.parse_sess, krate.config.clone(), cfg, - &mut loader); + &mut resolver); syntax_ext::register_builtins(&mut ecx.syntax_env); let ret = syntax::ext::expand::expand_crate(&mut ecx, syntax_exts, krate); if cfg!(windows) { @@ -708,9 +710,6 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, &sess.features.borrow()) }); - let resolver_arenas = Resolver::arenas(); - let mut resolver = Resolver::new(sess, make_glob_map, &resolver_arenas); - let krate = time(sess.time_passes(), "assigning node ids", || resolver.assign_node_ids(krate)); if sess.opts.debugging_opts.input_stats { diff --git a/src/librustc_metadata/macro_import.rs b/src/librustc_metadata/macro_import.rs index 22691975050e5..e41f076d64a80 100644 --- a/src/librustc_metadata/macro_import.rs +++ b/src/librustc_metadata/macro_import.rs @@ -18,6 +18,7 @@ use creader::{CrateReader, Macros}; use cstore::CStore; use rustc::hir::def_id::DefIndex; +use rustc::middle; use rustc::session::Session; use rustc::util::nodemap::FnvHashMap; use rustc_back::dynamic_lib::DynamicLibrary; @@ -26,7 +27,6 @@ use rustc_macro::__internal::Registry; use syntax::ast; use syntax::attr; use syntax::ext::base::LoadedMacro; -use syntax::ext; use syntax::parse::token; use syntax_ext::deriving::custom::CustomDerive; use syntax_pos::Span; @@ -55,7 +55,7 @@ pub fn call_bad_macro_reexport(a: &Session, b: Span) { pub type MacroSelection = FnvHashMap; -impl<'a> ext::base::MacroLoader for MacroLoader<'a> { +impl<'a> middle::cstore::MacroLoader for MacroLoader<'a> { fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index c5b505fba38e9..c1e6d93a970e5 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -45,6 +45,7 @@ use self::ParentLink::*; use rustc::hir::map::Definitions; use rustc::hir::{self, PrimTy, TyBool, TyChar, TyFloat, TyInt, TyUint, TyStr}; +use rustc::middle::cstore::MacroLoader; use rustc::session::Session; use rustc::lint; use rustc::hir::def::*; @@ -53,6 +54,8 @@ use rustc::ty; use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap}; use rustc::util::nodemap::{NodeMap, NodeSet, FnvHashMap, FnvHashSet}; +use syntax::ext; +use syntax::ext::base::LoadedMacro; use syntax::ext::hygiene::Mark; use syntax::ast::{self, FloatTy}; use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, IntTy, UintTy}; @@ -1068,6 +1071,8 @@ pub struct Resolver<'a> { arenas: &'a ResolverArenas<'a>, dummy_binding: &'a NameBinding<'a>, new_import_semantics: bool, // true if `#![feature(item_like_imports)]` + + macro_loader: &'a mut MacroLoader, } pub struct ResolverArenas<'a> { @@ -1149,6 +1154,12 @@ impl<'a> hir::lowering::Resolver for Resolver<'a> { } } +impl<'a> ext::base::Resolver for Resolver<'a> { + fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec { + self.macro_loader.load_crate(extern_crate, allows_macros) + } +} + trait Named { fn name(&self) -> Name; } @@ -1166,7 +1177,10 @@ impl Named for hir::PathSegment { } impl<'a> Resolver<'a> { - pub fn new(session: &'a Session, make_glob_map: MakeGlobMap, arenas: &'a ResolverArenas<'a>) + pub fn new(session: &'a Session, + make_glob_map: MakeGlobMap, + macro_loader: &'a mut MacroLoader, + arenas: &'a ResolverArenas<'a>) -> Resolver<'a> { let root_def_id = DefId::local(CRATE_DEF_INDEX); let graph_root = @@ -1227,6 +1241,8 @@ impl<'a> Resolver<'a> { vis: ty::Visibility::Public, }), new_import_semantics: session.features.borrow().item_like_imports, + + macro_loader: macro_loader, } } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index f6eb6f1da4f19..d0e11643c64c1 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -546,7 +546,7 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>) syntax_expanders } -pub trait MacroLoader { +pub trait Resolver { fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec; } @@ -556,8 +556,8 @@ pub enum LoadedMacro { CustomDerive(String, Box), } -pub struct DummyMacroLoader; -impl MacroLoader for DummyMacroLoader { +pub struct DummyResolver; +impl Resolver for DummyResolver { fn load_crate(&mut self, _: &ast::Item, _: bool) -> Vec { Vec::new() } @@ -572,7 +572,7 @@ pub struct ExtCtxt<'a> { pub backtrace: ExpnId, pub ecfg: expand::ExpansionConfig<'a>, pub crate_root: Option<&'static str>, - pub loader: &'a mut MacroLoader, + pub resolver: &'a mut Resolver, pub exported_macros: Vec, @@ -584,7 +584,7 @@ pub struct ExtCtxt<'a> { impl<'a> ExtCtxt<'a> { pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig, ecfg: expand::ExpansionConfig<'a>, - loader: &'a mut MacroLoader) + resolver: &'a mut Resolver) -> ExtCtxt<'a> { ExtCtxt { syntax_env: initial_syntax_expander_table(&ecfg), @@ -594,7 +594,7 @@ impl<'a> ExtCtxt<'a> { ecfg: ecfg, crate_root: None, exported_macros: Vec::new(), - loader: loader, + resolver: resolver, derive_modes: HashMap::new(), recursion_count: 0, } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 4715eda837490..44db1dd17ae6a 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -644,7 +644,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { // We need to error on `#[macro_use] extern crate` when it isn't at the // crate root, because `$crate` won't work properly. let is_crate_root = self.cx.syntax_env.is_crate_root(); - for def in self.cx.loader.load_crate(&*item, is_crate_root) { + for def in self.cx.resolver.load_crate(&*item, is_crate_root) { match def { LoadedMacro::Def(def) => self.cx.insert_macro(def), LoadedMacro::CustomDerive(name, ext) => { @@ -809,7 +809,7 @@ fn mark_tts(tts: &[TokenTree], m: Mark) -> Vec { mod tests { use super::{expand_crate, ExpansionConfig}; use ast; - use ext::base::{ExtCtxt, DummyMacroLoader}; + use ext::base::{ExtCtxt, DummyResolver}; use parse; use util::parser_testing::{string_to_parser}; use visit; @@ -850,7 +850,7 @@ mod tests { src, Vec::new(), &sess).unwrap(); // should fail: - let mut loader = DummyMacroLoader; + let mut loader = DummyResolver; let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader); expand_crate(&mut ecx, vec![], crate_ast); } @@ -865,7 +865,7 @@ mod tests { "".to_string(), src, Vec::new(), &sess).unwrap(); - let mut loader = DummyMacroLoader; + let mut loader = DummyResolver; let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader); expand_crate(&mut ecx, vec![], crate_ast); } @@ -879,7 +879,7 @@ mod tests { "".to_string(), src, Vec::new(), &sess).unwrap(); - let mut loader = DummyMacroLoader; + let mut loader = DummyResolver; let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader); expand_crate(&mut ecx, vec![], crate_ast); } @@ -888,7 +888,7 @@ mod tests { let ps = parse::ParseSess::new(); let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod()); // the cfg argument actually does matter, here... - let mut loader = DummyMacroLoader; + let mut loader = DummyResolver; let mut ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut loader); expand_crate(&mut ecx, vec![], crate_ast) } diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 3108296e778a2..dde8a8d271f62 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -28,7 +28,7 @@ use errors; use errors::snippet::{SnippetData}; use config; use entry::{self, EntryPointType}; -use ext::base::{ExtCtxt, DummyMacroLoader}; +use ext::base::{ExtCtxt, DummyResolver}; use ext::build::AstBuilder; use ext::expand::ExpansionConfig; use fold::Folder; @@ -276,13 +276,13 @@ fn generate_test_harness(sess: &ParseSess, let mut cleaner = EntryPointCleaner { depth: 0 }; let krate = cleaner.fold_crate(krate); - let mut loader = DummyMacroLoader; + let mut resolver = DummyResolver; let mut cx: TestCtxt = TestCtxt { sess: sess, span_diagnostic: sd, ext_cx: ExtCtxt::new(sess, vec![], ExpansionConfig::default("test".to_string()), - &mut loader), + &mut resolver), path: Vec::new(), testfns: Vec::new(), reexport_test_harness_main: reexport_test_harness_main, diff --git a/src/libsyntax_ext/rustc_macro_registrar.rs b/src/libsyntax_ext/rustc_macro_registrar.rs index 7693e2416f4b0..78fed9d33dd82 100644 --- a/src/libsyntax_ext/rustc_macro_registrar.rs +++ b/src/libsyntax_ext/rustc_macro_registrar.rs @@ -13,7 +13,7 @@ use std::mem; use errors; use syntax::ast::{self, Ident, NodeId}; use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute}; -use syntax::ext::base::{ExtCtxt, DummyMacroLoader}; +use syntax::ext::base::{ExtCtxt, DummyResolver}; use syntax::ext::build::AstBuilder; use syntax::ext::expand::ExpansionConfig; use syntax::parse::ParseSess; @@ -44,7 +44,7 @@ pub fn modify(sess: &ParseSess, num_crate_types: usize, handler: &errors::Handler, features: &Features) -> ast::Crate { - let mut loader = DummyMacroLoader; + let mut loader = DummyResolver; let mut cx = ExtCtxt::new(sess, Vec::new(), ExpansionConfig::default("rustc_macro".to_string()), diff --git a/src/test/compile-fail-fulldeps/qquote.rs b/src/test/compile-fail-fulldeps/qquote.rs index e29ded8a052c6..3e5d17e2ffb17 100644 --- a/src/test/compile-fail-fulldeps/qquote.rs +++ b/src/test/compile-fail-fulldeps/qquote.rs @@ -22,11 +22,11 @@ use syntax_pos::DUMMY_SP; fn main() { let ps = syntax::parse::ParseSess::new(); - let mut loader = syntax::ext::base::DummyMacroLoader; + let mut resolver = syntax::ext::base::DummyResolver; let mut cx = syntax::ext::base::ExtCtxt::new( &ps, vec![], syntax::ext::expand::ExpansionConfig::default("qquote".to_string()), - &mut loader); + &mut resolver); cx.bt_push(syntax::codemap::ExpnInfo { call_site: DUMMY_SP, callee: syntax::codemap::NameAndSpan { diff --git a/src/test/run-fail-fulldeps/qquote.rs b/src/test/run-fail-fulldeps/qquote.rs index 47e97abbbaa47..1458583ff5830 100644 --- a/src/test/run-fail-fulldeps/qquote.rs +++ b/src/test/run-fail-fulldeps/qquote.rs @@ -25,11 +25,11 @@ use syntax_pos::DUMMY_SP; fn main() { let ps = syntax::parse::ParseSess::new(); - let mut loader = syntax::ext::base::DummyMacroLoader; + let mut resolver = syntax::ext::base::DummyResolver; let mut cx = syntax::ext::base::ExtCtxt::new( &ps, vec![], syntax::ext::expand::ExpansionConfig::default("qquote".to_string()), - &mut loader); + &mut resolver); cx.bt_push(syntax::codemap::ExpnInfo { call_site: DUMMY_SP, callee: syntax::codemap::NameAndSpan { diff --git a/src/test/run-pass-fulldeps/qquote.rs b/src/test/run-pass-fulldeps/qquote.rs index a4f0e35cc5ac7..2a53a62a5ab60 100644 --- a/src/test/run-pass-fulldeps/qquote.rs +++ b/src/test/run-pass-fulldeps/qquote.rs @@ -21,11 +21,11 @@ use syntax_pos::DUMMY_SP; fn main() { let ps = syntax::parse::ParseSess::new(); - let mut loader = syntax::ext::base::DummyMacroLoader; + let mut resolver = syntax::ext::base::DummyResolver; let mut cx = syntax::ext::base::ExtCtxt::new( &ps, vec![], syntax::ext::expand::ExpansionConfig::default("qquote".to_string()), - &mut loader); + &mut resolver); cx.bt_push(syntax::codemap::ExpnInfo { call_site: DUMMY_SP, callee: syntax::codemap::NameAndSpan { From 20b43b23230ce063ccf99a4841d85790ad311bdf Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 8 Sep 2016 00:04:43 +0000 Subject: [PATCH 08/29] Rewrite the unit tests in `ext/expand.rs` as a `compile-fail` test. --- src/libsyntax/ext/expand.rs | 107 ------------------ .../compile-fail/macro-expansion-tests.rs | 46 ++++++++ 2 files changed, 46 insertions(+), 107 deletions(-) create mode 100644 src/test/compile-fail/macro-expansion-tests.rs diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 44db1dd17ae6a..d8365391153bc 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -803,110 +803,3 @@ impl Folder for Marker { fn mark_tts(tts: &[TokenTree], m: Mark) -> Vec { noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None}) } - - -#[cfg(test)] -mod tests { - use super::{expand_crate, ExpansionConfig}; - use ast; - use ext::base::{ExtCtxt, DummyResolver}; - use parse; - use util::parser_testing::{string_to_parser}; - use visit; - use visit::Visitor; - - // a visitor that extracts the paths - // from a given thingy and puts them in a mutable - // array (passed in to the traversal) - #[derive(Clone)] - struct PathExprFinderContext { - path_accumulator: Vec , - } - - impl Visitor for PathExprFinderContext { - fn visit_expr(&mut self, expr: &ast::Expr) { - if let ast::ExprKind::Path(None, ref p) = expr.node { - self.path_accumulator.push(p.clone()); - } - visit::walk_expr(self, expr); - } - } - - // these following tests are quite fragile, in that they don't test what - // *kind* of failure occurs. - - fn test_ecfg() -> ExpansionConfig<'static> { - ExpansionConfig::default("test".to_string()) - } - - // make sure that macros can't escape fns - #[should_panic] - #[test] fn macros_cant_escape_fns_test () { - let src = "fn bogus() {macro_rules! z (() => (3+4));}\ - fn inty() -> i32 { z!() }".to_string(); - let sess = parse::ParseSess::new(); - let crate_ast = parse::parse_crate_from_source_str( - "".to_string(), - src, - Vec::new(), &sess).unwrap(); - // should fail: - let mut loader = DummyResolver; - let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader); - expand_crate(&mut ecx, vec![], crate_ast); - } - - // make sure that macros can't escape modules - #[should_panic] - #[test] fn macros_cant_escape_mods_test () { - let src = "mod foo {macro_rules! z (() => (3+4));}\ - fn inty() -> i32 { z!() }".to_string(); - let sess = parse::ParseSess::new(); - let crate_ast = parse::parse_crate_from_source_str( - "".to_string(), - src, - Vec::new(), &sess).unwrap(); - let mut loader = DummyResolver; - let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader); - expand_crate(&mut ecx, vec![], crate_ast); - } - - // macro_use modules should allow macros to escape - #[test] fn macros_can_escape_flattened_mods_test () { - let src = "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\ - fn inty() -> i32 { z!() }".to_string(); - let sess = parse::ParseSess::new(); - let crate_ast = parse::parse_crate_from_source_str( - "".to_string(), - src, - Vec::new(), &sess).unwrap(); - let mut loader = DummyResolver; - let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader); - expand_crate(&mut ecx, vec![], crate_ast); - } - - fn expand_crate_str(crate_str: String) -> ast::Crate { - let ps = parse::ParseSess::new(); - let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod()); - // the cfg argument actually does matter, here... - let mut loader = DummyResolver; - let mut ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut loader); - expand_crate(&mut ecx, vec![], crate_ast) - } - - #[test] fn macro_tokens_should_match(){ - expand_crate_str( - "macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string()); - } - - // should be able to use a bound identifier as a literal in a macro definition: - #[test] fn self_macro_parsing(){ - expand_crate_str( - "macro_rules! foo ((zz) => (287;)); - fn f(zz: i32) {foo!(zz);}".to_string() - ); - } - - // create a really evil test case where a $x appears inside a binding of $x - // but *shouldn't* bind because it was inserted by a different macro.... - // can't write this test case until we have macro-generating macros. -} diff --git a/src/test/compile-fail/macro-expansion-tests.rs b/src/test/compile-fail/macro-expansion-tests.rs new file mode 100644 index 0000000000000..a064e69bc6d59 --- /dev/null +++ b/src/test/compile-fail/macro-expansion-tests.rs @@ -0,0 +1,46 @@ +// Copyright 2016 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. + +mod macros_cant_escape_fns { + fn f() { + macro_rules! m { () => { 3 + 4 } } + } + fn g() -> i32 { m!() } //~ ERROR macro undefined +} + +mod macros_cant_escape_mods { + mod f { + macro_rules! m { () => { 3 + 4 } } + } + fn g() -> i32 { m!() } //~ ERROR macro undefined +} + +mod macros_can_escape_flattened_mods_test { + #[macro_use] + mod f { + macro_rules! m { () => { 3 + 4 } } + } + fn g() -> i32 { m!() } +} + +fn macro_tokens_should_match() { + macro_rules! m { (a) => { 13 } } + m!(a); +} + +// should be able to use a bound identifier as a literal in a macro definition: +fn self_macro_parsing() { + macro_rules! foo { (zz) => { 287; } } + fn f(zz: i32) { + foo!(zz); + } +} + +fn main() {} From 72a636975fc5d0bb4af45af7bdd97987cc722a6a Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 7 Sep 2016 23:21:59 +0000 Subject: [PATCH 09/29] Move macro resolution into `librustc_resolve`. --- src/librustc/session/mod.rs | 9 +- src/librustc_driver/driver.rs | 9 +- src/librustc_resolve/lib.rs | 18 +- src/librustc_resolve/macros.rs | 213 ++++++++++++++++++++ src/libsyntax/ext/base.rs | 298 ++++++---------------------- src/libsyntax/ext/expand.rs | 182 +++++++---------- src/libsyntax/ext/hygiene.rs | 11 +- src/libsyntax/ext/source_util.rs | 4 +- src/libsyntax/ext/tt/macro_rules.rs | 4 +- src/libsyntax_ext/deriving/mod.rs | 9 +- src/libsyntax_ext/lib.rs | 88 +++++--- 11 files changed, 439 insertions(+), 406 deletions(-) create mode 100644 src/librustc_resolve/macros.rs diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 3477ec6f99af1..49686d63ee43b 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -21,7 +21,7 @@ use util::nodemap::{NodeMap, FnvHashMap}; use util::common::duration_to_secs_str; use mir::transform as mir_pass; -use syntax::ast::{NodeId, Name}; +use syntax::ast::NodeId; use errors::{self, DiagnosticBuilder}; use errors::emitter::{Emitter, EmitterWriter}; use syntax::json::JsonEmitter; @@ -39,7 +39,7 @@ use llvm; use std::path::{Path, PathBuf}; use std::cell::{self, Cell, RefCell}; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::env; use std::ffi::CString; use std::rc::Rc; @@ -96,10 +96,6 @@ pub struct Session { pub injected_allocator: Cell>, pub injected_panic_runtime: Cell>, - /// Names of all bang-style macros and syntax extensions - /// available in this crate - pub available_macros: RefCell>, - /// Map from imported macro spans (which consist of /// the localized span for the macro body) to the /// macro name and defintion span in the source crate. @@ -552,7 +548,6 @@ pub fn build_session_(sopts: config::Options, next_node_id: Cell::new(1), injected_allocator: Cell::new(None), injected_panic_runtime: Cell::new(None), - available_macros: RefCell::new(HashSet::new()), imported_macro_spans: RefCell::new(HashMap::new()), incr_comp_session: RefCell::new(IncrCompSession::NotInitialized), perf_stats: PerfStats { diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 0d9bf14f12fb2..bf50c96034dd0 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -50,6 +50,7 @@ use std::io::{self, Write}; use std::path::{Path, PathBuf}; use syntax::{ast, diagnostics, visit}; use syntax::attr; +use syntax::ext::base::ExtCtxt; use syntax::parse::{self, PResult, token}; use syntax::util::node_count::NodeCounter; use syntax; @@ -643,6 +644,7 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, let resolver_arenas = Resolver::arenas(); let mut resolver = Resolver::new(sess, make_glob_map, &mut macro_loader, &resolver_arenas); + syntax_ext::register_builtins(&mut resolver, sess.features.borrow().quote); krate = time(time_passes, "expansion", || { // Windows dlls do not have rpaths, so they don't know how to find their @@ -678,16 +680,11 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, trace_mac: sess.opts.debugging_opts.trace_macros, should_test: sess.opts.test, }; - let mut ecx = syntax::ext::base::ExtCtxt::new(&sess.parse_sess, - krate.config.clone(), - cfg, - &mut resolver); - syntax_ext::register_builtins(&mut ecx.syntax_env); + let mut ecx = ExtCtxt::new(&sess.parse_sess, krate.config.clone(), cfg, &mut resolver); let ret = syntax::ext::expand::expand_crate(&mut ecx, syntax_exts, krate); if cfg!(windows) { env::set_var("PATH", &old_path); } - *sess.available_macros.borrow_mut() = ecx.syntax_env.names; ret }); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index c1e6d93a970e5..6bf45ab8f6fa0 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -54,8 +54,6 @@ use rustc::ty; use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap}; use rustc::util::nodemap::{NodeMap, NodeSet, FnvHashMap, FnvHashSet}; -use syntax::ext; -use syntax::ext::base::LoadedMacro; use syntax::ext::hygiene::Mark; use syntax::ast::{self, FloatTy}; use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, IntTy, UintTy}; @@ -82,6 +80,7 @@ use resolve_imports::{ImportDirective, NameResolution}; // registered before they are used. mod diagnostics; +mod macros; mod check_unused; mod build_reduced_graph; mod resolve_imports; @@ -1073,6 +1072,10 @@ pub struct Resolver<'a> { new_import_semantics: bool, // true if `#![feature(item_like_imports)]` macro_loader: &'a mut MacroLoader, + macro_names: FnvHashSet, + + // Maps the `Mark` of an expansion to its containing module or block. + expansion_data: Vec, } pub struct ResolverArenas<'a> { @@ -1154,12 +1157,6 @@ impl<'a> hir::lowering::Resolver for Resolver<'a> { } } -impl<'a> ext::base::Resolver for Resolver<'a> { - fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec { - self.macro_loader.load_crate(extern_crate, allows_macros) - } -} - trait Named { fn name(&self) -> Name; } @@ -1243,6 +1240,8 @@ impl<'a> Resolver<'a> { new_import_semantics: session.features.borrow().item_like_imports, macro_loader: macro_loader, + macro_names: FnvHashSet(), + expansion_data: vec![macros::ExpansionData::default()], } } @@ -2784,8 +2783,7 @@ impl<'a> Resolver<'a> { } fn find_best_match(&mut self, name: &str) -> SuggestionType { - if let Some(macro_name) = self.session.available_macros - .borrow().iter().find(|n| n.as_str() == name) { + if let Some(macro_name) = self.macro_names.iter().find(|n| n.as_str() == name) { return SuggestionType::Macro(format!("{}!", macro_name)); } diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs new file mode 100644 index 0000000000000..36f501a54d261 --- /dev/null +++ b/src/librustc_resolve/macros.rs @@ -0,0 +1,213 @@ +// Copyright 2016 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. + +use Resolver; +use rustc::util::nodemap::FnvHashMap; +use std::cell::RefCell; +use std::mem; +use std::rc::Rc; +use syntax::ast::{self, Name}; +use syntax::errors::DiagnosticBuilder; +use syntax::ext::base::{self, LoadedMacro, MultiModifier, MultiDecorator}; +use syntax::ext::base::{NormalTT, SyntaxExtension}; +use syntax::ext::expand::{Expansion, Invocation, InvocationKind}; +use syntax::ext::hygiene::Mark; +use syntax::parse::token::intern; +use syntax::util::lev_distance::find_best_match_for_name; +use syntax::visit::{self, Visitor}; + +#[derive(Clone, Default)] +pub struct ExpansionData { + module: Rc, +} + +// FIXME(jseyfried): merge with `::ModuleS`. +#[derive(Default)] +struct ModuleData { + parent: Option>, + macros: RefCell>>, + macros_escape: bool, +} + +impl<'a> base::Resolver for Resolver<'a> { + fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec { + self.macro_loader.load_crate(extern_crate, allows_macros) + } + + fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion) { + expansion.visit_with(&mut ExpansionVisitor { + current_module: self.expansion_data[mark.as_u32() as usize].module.clone(), + resolver: self, + }); + } + + fn add_macro(&mut self, scope: Mark, ident: ast::Ident, ext: Rc) { + if let NormalTT(..) = *ext { + self.macro_names.insert(ident.name); + } + + let mut module = self.expansion_data[scope.as_u32() as usize].module.clone(); + while module.macros_escape { + module = module.parent.clone().unwrap(); + } + module.macros.borrow_mut().insert(ident.name, ext); + } + + fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec) { + self.macros_at_scope.insert(id, macros); + } + + fn find_attr_invoc(&mut self, attrs: &mut Vec) -> Option { + for i in 0..attrs.len() { + let name = intern(&attrs[i].name()); + match self.expansion_data[0].module.macros.borrow().get(&name) { + Some(ext) => match **ext { + MultiModifier(..) | MultiDecorator(..) => return Some(attrs.remove(i)), + _ => {} + }, + None => {} + } + } + None + } + + fn resolve_invoc(&mut self, invoc: &Invocation) -> Option> { + let (name, span) = match invoc.kind { + InvocationKind::Bang { ref mac, .. } => { + let path = &mac.node.path; + if path.segments.len() > 1 || path.global || + !path.segments[0].parameters.is_empty() { + self.session.span_err(path.span, + "expected macro name without module separators"); + return None; + } + (path.segments[0].identifier.name, path.span) + } + InvocationKind::Attr { ref attr, .. } => (intern(&*attr.name()), attr.span), + }; + + let mut module = self.expansion_data[invoc.mark().as_u32() as usize].module.clone(); + loop { + if let Some(ext) = module.macros.borrow().get(&name) { + return Some(ext.clone()); + } + match module.parent.clone() { + Some(parent) => module = parent, + None => break, + } + } + + let mut err = + self.session.struct_span_err(span, &format!("macro undefined: '{}!'", name)); + self.suggest_macro_name(&name.as_str(), &mut err); + err.emit(); + None + } +} + +impl<'a> Resolver<'a> { + fn suggest_macro_name(&mut self, name: &str, err: &mut DiagnosticBuilder<'a>) { + if let Some(suggestion) = find_best_match_for_name(self.macro_names.iter(), name, None) { + if suggestion != name { + err.help(&format!("did you mean `{}!`?", suggestion)); + } else { + err.help(&format!("have you added the `#[macro_use]` on the module/import?")); + } + } + } +} + +struct ExpansionVisitor<'b, 'a: 'b> { + resolver: &'b mut Resolver<'a>, + current_module: Rc, +} + +impl<'a, 'b> ExpansionVisitor<'a, 'b> { + fn visit_invoc(&mut self, id: ast::NodeId) { + assert_eq!(id, self.resolver.expansion_data.len() as u32); + self.resolver.expansion_data.push(ExpansionData { + module: self.current_module.clone(), + }); + } + + // does this attribute list contain "macro_use"? + fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool { + for attr in attrs { + if attr.check_name("macro_escape") { + let msg = "macro_escape is a deprecated synonym for macro_use"; + let mut err = self.resolver.session.struct_span_warn(attr.span, msg); + if let ast::AttrStyle::Inner = attr.node.style { + err.help("consider an outer attribute, #[macro_use] mod ...").emit(); + } else { + err.emit(); + } + } else if !attr.check_name("macro_use") { + continue; + } + + if !attr.is_word() { + self.resolver.session.span_err(attr.span, + "arguments to macro_use are not allowed here"); + } + return true; + } + + false + } +} + +macro_rules! method { + ($visit:ident: $ty:ty, $invoc:path, $walk:ident) => { + fn $visit(&mut self, node: &$ty) { + match node.node { + $invoc(..) => self.visit_invoc(node.id), + _ => visit::$walk(self, node), + } + } + } +} + +impl<'a, 'b> Visitor for ExpansionVisitor<'a, 'b> { + method!(visit_trait_item: ast::TraitItem, ast::TraitItemKind::Macro, walk_trait_item); + method!(visit_impl_item: ast::ImplItem, ast::ImplItemKind::Macro, walk_impl_item); + method!(visit_stmt: ast::Stmt, ast::StmtKind::Mac, walk_stmt); + method!(visit_expr: ast::Expr, ast::ExprKind::Mac, walk_expr); + method!(visit_pat: ast::Pat, ast::PatKind::Mac, walk_pat); + method!(visit_ty: ast::Ty, ast::TyKind::Mac, walk_ty); + + fn visit_item(&mut self, item: &ast::Item) { + match item.node { + ast::ItemKind::Mac(..) if item.id == ast::DUMMY_NODE_ID => {} // Scope placeholder + ast::ItemKind::Mac(..) => self.visit_invoc(item.id), + ast::ItemKind::Mod(..) => { + let module_data = ModuleData { + parent: Some(self.current_module.clone()), + macros: RefCell::new(FnvHashMap()), + macros_escape: self.contains_macro_use(&item.attrs), + }; + let orig_module = mem::replace(&mut self.current_module, Rc::new(module_data)); + visit::walk_item(self, item); + self.current_module = orig_module; + } + _ => visit::walk_item(self, item), + } + } + + fn visit_block(&mut self, block: &ast::Block) { + let module_data = ModuleData { + parent: Some(self.current_module.clone()), + macros: RefCell::new(FnvHashMap()), + macros_escape: false, + }; + let orig_module = mem::replace(&mut self.current_module, Rc::new(module_data)); + visit::walk_block(self, block); + self.current_module = orig_module; + } +} diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index d0e11643c64c1..d46e2a9872e8d 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -10,27 +10,25 @@ pub use self::SyntaxExtension::*; -use ast; -use ast::{Name, PatKind}; +use ast::{self, Attribute, Name, PatKind}; use attr::HasAttrs; use codemap::{self, CodeMap, ExpnInfo, Spanned, respan}; use syntax_pos::{Span, ExpnId, NO_EXPANSION}; use errors::DiagnosticBuilder; -use ext; -use ext::expand; +use ext::expand::{self, Invocation, Expansion}; +use ext::hygiene::Mark; use ext::tt::macro_rules; use parse; use parse::parser; use parse::token; -use parse::token::{InternedString, intern, str_to_ident}; +use parse::token::{InternedString, str_to_ident}; use ptr::P; use std_inject; use util::small_vector::SmallVector; -use util::lev_distance::find_best_match_for_name; use fold::Folder; use feature_gate; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::path::PathBuf; use std::rc::Rc; use tokenstream; @@ -44,7 +42,7 @@ pub enum Annotatable { } impl HasAttrs for Annotatable { - fn attrs(&self) -> &[ast::Attribute] { + fn attrs(&self) -> &[Attribute] { match *self { Annotatable::Item(ref item) => &item.attrs, Annotatable::TraitItem(ref trait_item) => &trait_item.attrs, @@ -52,7 +50,7 @@ impl HasAttrs for Annotatable { } } - fn map_attrs) -> Vec>(self, f: F) -> Self { + fn map_attrs) -> Vec>(self, f: F) -> Self { match self { Annotatable::Item(item) => Annotatable::Item(item.map_attrs(f)), Annotatable::TraitItem(trait_item) => Annotatable::TraitItem(trait_item.map_attrs(f)), @@ -464,91 +462,15 @@ pub enum SyntaxExtension { pub type NamedSyntaxExtension = (Name, SyntaxExtension); -/// The base map of methods for expanding syntax extension -/// AST nodes into full ASTs -fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>) - -> SyntaxEnv { - // utility function to simplify creating NormalTT syntax extensions - fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension { - NormalTT(Box::new(f), None, false) - } - - let mut syntax_expanders = SyntaxEnv::new(); - syntax_expanders.insert(intern("macro_rules"), MacroRulesTT); - - if ecfg.enable_quotes() { - // Quasi-quoting expanders - syntax_expanders.insert(intern("quote_tokens"), - builtin_normal_expander( - ext::quote::expand_quote_tokens)); - syntax_expanders.insert(intern("quote_expr"), - builtin_normal_expander( - ext::quote::expand_quote_expr)); - syntax_expanders.insert(intern("quote_ty"), - builtin_normal_expander( - ext::quote::expand_quote_ty)); - syntax_expanders.insert(intern("quote_item"), - builtin_normal_expander( - ext::quote::expand_quote_item)); - syntax_expanders.insert(intern("quote_pat"), - builtin_normal_expander( - ext::quote::expand_quote_pat)); - syntax_expanders.insert(intern("quote_arm"), - builtin_normal_expander( - ext::quote::expand_quote_arm)); - syntax_expanders.insert(intern("quote_stmt"), - builtin_normal_expander( - ext::quote::expand_quote_stmt)); - syntax_expanders.insert(intern("quote_matcher"), - builtin_normal_expander( - ext::quote::expand_quote_matcher)); - syntax_expanders.insert(intern("quote_attr"), - builtin_normal_expander( - ext::quote::expand_quote_attr)); - syntax_expanders.insert(intern("quote_arg"), - builtin_normal_expander( - ext::quote::expand_quote_arg)); - syntax_expanders.insert(intern("quote_block"), - builtin_normal_expander( - ext::quote::expand_quote_block)); - syntax_expanders.insert(intern("quote_meta_item"), - builtin_normal_expander( - ext::quote::expand_quote_meta_item)); - syntax_expanders.insert(intern("quote_path"), - builtin_normal_expander( - ext::quote::expand_quote_path)); - } - - syntax_expanders.insert(intern("line"), - builtin_normal_expander( - ext::source_util::expand_line)); - syntax_expanders.insert(intern("column"), - builtin_normal_expander( - ext::source_util::expand_column)); - syntax_expanders.insert(intern("file"), - builtin_normal_expander( - ext::source_util::expand_file)); - syntax_expanders.insert(intern("stringify"), - builtin_normal_expander( - ext::source_util::expand_stringify)); - syntax_expanders.insert(intern("include"), - builtin_normal_expander( - ext::source_util::expand_include)); - syntax_expanders.insert(intern("include_str"), - builtin_normal_expander( - ext::source_util::expand_include_str)); - syntax_expanders.insert(intern("include_bytes"), - builtin_normal_expander( - ext::source_util::expand_include_bytes)); - syntax_expanders.insert(intern("module_path"), - builtin_normal_expander( - ext::source_util::expand_mod)); - syntax_expanders -} - pub trait Resolver { - fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) - -> Vec; + fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec; + + fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion); + fn add_macro(&mut self, scope: Mark, ident: ast::Ident, ext: Rc); + fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec); + + fn find_attr_invoc(&mut self, attrs: &mut Vec) -> Option; + fn resolve_invoc(&mut self, invoc: &Invocation) -> Option>; } pub enum LoadedMacro { @@ -558,9 +480,31 @@ pub enum LoadedMacro { pub struct DummyResolver; impl Resolver for DummyResolver { - fn load_crate(&mut self, _: &ast::Item, _: bool) -> Vec { + fn load_crate(&mut self, _extern_crate: &ast::Item, _allows_macros: bool) -> Vec { Vec::new() } + + fn visit_expansion(&mut self, _invoc: Mark, _expansion: &Expansion) {} + fn add_macro(&mut self, _scope: Mark, _ident: ast::Ident, _ext: Rc) {} + fn add_expansions_at_stmt(&mut self, _id: ast::NodeId, _macros: Vec) {} + + fn find_attr_invoc(&mut self, _attrs: &mut Vec) -> Option { None } + fn resolve_invoc(&mut self, _invoc: &Invocation) -> Option> { None } +} + +#[derive(Clone)] +pub struct ModuleData { + pub mod_path: Vec, + pub directory: PathBuf, +} + +#[derive(Clone)] +pub struct ExpansionData { + pub mark: Mark, + pub depth: usize, + pub backtrace: ExpnId, + pub module: Rc, + pub in_block: bool, } /// One of these is made during expansion and incrementally updated as we go; @@ -569,16 +513,12 @@ impl Resolver for DummyResolver { pub struct ExtCtxt<'a> { pub parse_sess: &'a parse::ParseSess, pub cfg: ast::CrateConfig, - pub backtrace: ExpnId, pub ecfg: expand::ExpansionConfig<'a>, pub crate_root: Option<&'static str>, pub resolver: &'a mut Resolver, - pub exported_macros: Vec, - - pub syntax_env: SyntaxEnv, pub derive_modes: HashMap>, - pub recursion_count: usize, + pub current_expansion: ExpansionData, } impl<'a> ExtCtxt<'a> { @@ -587,16 +527,20 @@ impl<'a> ExtCtxt<'a> { resolver: &'a mut Resolver) -> ExtCtxt<'a> { ExtCtxt { - syntax_env: initial_syntax_expander_table(&ecfg), parse_sess: parse_sess, cfg: cfg, - backtrace: NO_EXPANSION, ecfg: ecfg, crate_root: None, exported_macros: Vec::new(), resolver: resolver, derive_modes: HashMap::new(), - recursion_count: 0, + current_expansion: ExpansionData { + mark: Mark::root(), + depth: 0, + backtrace: NO_EXPANSION, + module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }), + in_block: false, + }, } } @@ -609,23 +553,22 @@ impl<'a> ExtCtxt<'a> { -> parser::Parser<'a> { parse::tts_to_parser(self.parse_sess, tts.to_vec(), self.cfg()) } - pub fn codemap(&self) -> &'a CodeMap { self.parse_sess.codemap() } pub fn parse_sess(&self) -> &'a parse::ParseSess { self.parse_sess } pub fn cfg(&self) -> ast::CrateConfig { self.cfg.clone() } pub fn call_site(&self) -> Span { - self.codemap().with_expn_info(self.backtrace, |ei| match ei { + self.codemap().with_expn_info(self.backtrace(), |ei| match ei { Some(expn_info) => expn_info.call_site, None => self.bug("missing top span") }) } - pub fn backtrace(&self) -> ExpnId { self.backtrace } + pub fn backtrace(&self) -> ExpnId { self.current_expansion.backtrace } /// Returns span for the macro which originally caused the current expansion to happen. /// /// Stops backtracing at include! boundary. pub fn expansion_cause(&self) -> Span { - let mut expn_id = self.backtrace; + let mut expn_id = self.backtrace(); let mut last_macro = None; loop { if self.codemap().with_expn_info(expn_id, |info| { @@ -646,15 +589,15 @@ impl<'a> ExtCtxt<'a> { } pub fn bt_push(&mut self, ei: ExpnInfo) { - if self.recursion_count > self.ecfg.recursion_limit { + if self.current_expansion.depth > self.ecfg.recursion_limit { self.span_fatal(ei.call_site, &format!("recursion limit reached while expanding the macro `{}`", ei.callee.name())); } let mut call_site = ei.call_site; - call_site.expn_id = self.backtrace; - self.backtrace = self.codemap().record_expansion(ExpnInfo { + call_site.expn_id = self.backtrace(); + self.current_expansion.backtrace = self.codemap().record_expansion(ExpnInfo { call_site: call_site, callee: ei.callee }); @@ -667,14 +610,11 @@ impl<'a> ExtCtxt<'a> { } if def.use_locally { let ext = macro_rules::compile(self, &def); - self.syntax_env.insert(def.ident.name, ext); + self.resolver.add_macro(self.current_expansion.mark, def.ident, Rc::new(ext)); } } - pub fn insert_custom_derive(&mut self, - name: &str, - ext: Box, - sp: Span) { + pub fn insert_custom_derive(&mut self, name: &str, ext: Box, sp: Span) { if !self.ecfg.enable_rustc_macro() { feature_gate::emit_feature_err(&self.parse_sess.span_diagnostic, "rustc_macro", @@ -685,8 +625,7 @@ impl<'a> ExtCtxt<'a> { } let name = token::intern_and_get_ident(name); if self.derive_modes.insert(name.clone(), ext).is_some() { - self.span_err(sp, &format!("cannot shadow existing derive mode `{}`", - name)); + self.span_err(sp, &format!("cannot shadow existing derive mode `{}`", name)); } } @@ -765,20 +704,6 @@ impl<'a> ExtCtxt<'a> { token::intern(st) } - pub fn suggest_macro_name(&mut self, - name: &str, - err: &mut DiagnosticBuilder<'a>) { - let names = &self.syntax_env.names; - if let Some(suggestion) = find_best_match_for_name(names.iter(), name, None) { - if suggestion != name { - err.help(&format!("did you mean `{}!`?", suggestion)); - } else { - err.help(&format!("have you added the `#[macro_use]` on the \ - module/import?")); - } - } - } - pub fn initialize(&mut self, user_exts: Vec, krate: &ast::Crate) { if std_inject::no_core(&krate) { self.crate_root = None; @@ -789,16 +714,16 @@ impl<'a> ExtCtxt<'a> { } for (name, extension) in user_exts { - self.syntax_env.insert(name, extension); + let ident = ast::Ident::with_empty_ctxt(name); + self.resolver.add_macro(Mark::root(), ident, Rc::new(extension)); } - self.syntax_env.current_module = Module(0); - let mut paths = ModulePaths { + let mut module = ModuleData { mod_path: vec![token::str_to_ident(&self.ecfg.crate_name)], directory: PathBuf::from(self.parse_sess.codemap().span_to_filename(krate.span)), }; - paths.directory.pop(); - self.syntax_env.module_data[0].paths = Rc::new(paths); + module.directory.pop(); + self.current_expansion.module = Rc::new(module); } } @@ -809,7 +734,7 @@ pub fn expr_to_spanned_string(cx: &mut ExtCtxt, expr: P, err_msg: &st -> Option> { // Update `expr.span`'s expn_id now in case expr is an `include!` macro invocation. let expr = expr.map(|mut expr| { - expr.span.expn_id = cx.backtrace; + expr.span.expn_id = cx.backtrace(); expr }); @@ -884,104 +809,3 @@ pub fn get_exprs_from_tts(cx: &mut ExtCtxt, } Some(es) } - -/// In order to have some notion of scoping for macros, -/// we want to implement the notion of a transformation -/// environment. -/// -/// This environment maps Names to SyntaxExtensions. -pub struct SyntaxEnv { - module_data: Vec, - pub current_module: Module, - - /// All bang-style macro/extension names - /// encountered so far; to be used for diagnostics in resolve - pub names: HashSet, -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct Module(u32); - -struct ModuleData { - parent: Module, - paths: Rc, - macros: HashMap>, - macros_escape: bool, - in_block: bool, -} - -#[derive(Clone)] -pub struct ModulePaths { - pub mod_path: Vec, - pub directory: PathBuf, -} - -impl SyntaxEnv { - fn new() -> SyntaxEnv { - let mut env = SyntaxEnv { - current_module: Module(0), - module_data: Vec::new(), - names: HashSet::new(), - }; - let paths = Rc::new(ModulePaths { mod_path: Vec::new(), directory: PathBuf::new() }); - env.add_module(false, false, paths); - env - } - - fn data(&self, module: Module) -> &ModuleData { - &self.module_data[module.0 as usize] - } - - pub fn paths(&self) -> Rc { - self.data(self.current_module).paths.clone() - } - - pub fn in_block(&self) -> bool { - self.data(self.current_module).in_block - } - - pub fn add_module(&mut self, macros_escape: bool, in_block: bool, paths: Rc) - -> Module { - let data = ModuleData { - parent: self.current_module, - paths: paths, - macros: HashMap::new(), - macros_escape: macros_escape, - in_block: in_block, - }; - - self.module_data.push(data); - Module(self.module_data.len() as u32 - 1) - } - - pub fn find(&self, name: Name) -> Option> { - let mut module = self.current_module; - let mut module_data; - loop { - module_data = self.data(module); - if let Some(ext) = module_data.macros.get(&name) { - return Some(ext.clone()); - } - if module == module_data.parent { - return None; - } - module = module_data.parent; - } - } - - pub fn insert(&mut self, name: Name, ext: SyntaxExtension) { - if let NormalTT(..) = ext { - self.names.insert(name); - } - - let mut module = self.current_module; - while self.data(module).macros_escape { - module = self.data(module).parent; - } - self.module_data[module.0 as usize].macros.insert(name, Rc::new(ext)); - } - - pub fn is_crate_root(&mut self) -> bool { - self.current_module == Module(0) - } -} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index d8365391153bc..beb1687dfdc72 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -25,6 +25,7 @@ use parse::token::{intern, keywords}; use ptr::P; use tokenstream::TokenTree; use util::small_vector::SmallVector; +use visit::Visitor; use std::mem; use std::path::PathBuf; @@ -32,7 +33,8 @@ use std::rc::Rc; macro_rules! expansions { ($($kind:ident: $ty:ty [$($vec:ident, $ty_elt:ty)*], $kind_name:expr, .$make:ident, - $(.$fold:ident)* $(lift .$fold_elt:ident)*;)*) => { + $(.$fold:ident)* $(lift .$fold_elt:ident)*, + $(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => { #[derive(Copy, Clone)] pub enum ExpansionKind { OptExpr, $( $kind, )* } pub enum Expansion { OptExpr(Option>), $( $kind($ty), )* } @@ -77,6 +79,17 @@ macro_rules! expansions { }, )*)* } } + + pub fn visit_with(&self, visitor: &mut V) { + match *self { + Expansion::OptExpr(Some(ref expr)) => visitor.visit_expr(expr), + Expansion::OptExpr(None) => {} + $($( Expansion::$kind(ref ast) => visitor.$visit(ast), )*)* + $($( Expansion::$kind(ref ast) => for ast in ast.as_slice() { + visitor.$visit_elt(ast); + }, )*)* + } + } } impl<'a, 'b> Folder for MacroExpander<'a, 'b> { @@ -94,17 +107,17 @@ macro_rules! expansions { } expansions! { - Expr: P [], "expression", .make_expr, .fold_expr; - Pat: P [], "pattern", .make_pat, .fold_pat; - Ty: P [], "type", .make_ty, .fold_ty; + Expr: P [], "expression", .make_expr, .fold_expr, .visit_expr; + Pat: P [], "pattern", .make_pat, .fold_pat, .visit_pat; + Ty: P [], "type", .make_ty, .fold_ty, .visit_ty; Stmts: SmallVector [SmallVector, ast::Stmt], - "statement", .make_stmts, lift .fold_stmt; + "statement", .make_stmts, lift .fold_stmt, lift .visit_stmt; Items: SmallVector> [SmallVector, P], - "item", .make_items, lift .fold_item; + "item", .make_items, lift .fold_item, lift .visit_item; TraitItems: SmallVector [SmallVector, ast::TraitItem], - "trait item", .make_trait_items, lift .fold_trait_item; + "trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item; ImplItems: SmallVector [SmallVector, ast::ImplItem], - "impl item", .make_impl_items, lift .fold_impl_item; + "impl item", .make_impl_items, lift .fold_impl_item, lift .visit_impl_item; } impl ExpansionKind { @@ -127,15 +140,12 @@ impl ExpansionKind { } pub struct Invocation { - kind: InvocationKind, + pub kind: InvocationKind, expansion_kind: ExpansionKind, - mark: Mark, - module: Module, - backtrace: ExpnId, - depth: usize, + expansion_data: ExpansionData, } -enum InvocationKind { +pub enum InvocationKind { Bang { attrs: Vec, mac: ast::Mac, @@ -148,6 +158,19 @@ enum InvocationKind { }, } +impl Invocation { + fn span(&self) -> Span { + match self.kind { + InvocationKind::Bang { span, .. } => span, + InvocationKind::Attr { ref attr, .. } => attr.span, + } + } + + pub fn mark(&self) -> Mark { + self.expansion_data.mark + } +} + pub struct MacroExpander<'a, 'b:'a> { pub cx: &'a mut ExtCtxt<'b>, pub single_step: bool, @@ -170,7 +193,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let items = Expansion::Items(SmallVector::many(krate.module.items)); krate.module.items = self.expand(items).make_items().into(); - krate.exported_macros = self.cx.exported_macros.clone(); + krate.exported_macros = mem::replace(&mut self.cx.exported_macros, Vec::new()); if self.cx.parse_sess.span_diagnostic.err_count() > err_count { self.cx.parse_sess.span_diagnostic.abort_if_errors(); @@ -181,21 +204,23 @@ impl<'a, 'b> MacroExpander<'a, 'b> { // Fully expand all the invocations in `expansion`. fn expand(&mut self, expansion: Expansion) -> Expansion { - self.cx.recursion_count = 0; + let orig_expansion_data = self.cx.current_expansion.clone(); + self.cx.current_expansion.depth = 0; + let (expansion, mut invocations) = self.collect_invocations(expansion); invocations.reverse(); let mut expansions = vec![vec![(0, expansion)]]; while let Some(invoc) = invocations.pop() { - let Invocation { mark, module, depth, backtrace, .. } = invoc; - self.cx.syntax_env.current_module = module; - self.cx.recursion_count = depth; - self.cx.backtrace = backtrace; + let ExpansionData { depth, mark, .. } = invoc.expansion_data; + self.cx.current_expansion = invoc.expansion_data.clone(); - let expansion = self.expand_invoc(invoc); + let expansion = match self.cx.resolver.resolve_invoc(&invoc) { + Some(ext) => self.expand_invoc(invoc, ext), + None => invoc.expansion_kind.dummy(invoc.span()), + }; - self.cx.syntax_env.current_module = module; - self.cx.recursion_count = depth + 1; + self.cx.current_expansion.depth = depth + 1; let (expansion, new_invocations) = self.collect_invocations(expansion); if expansions.len() == depth { @@ -207,6 +232,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } } + self.cx.current_expansion = orig_expansion_data; + let mut placeholder_expander = PlaceholderExpander::new(); while let Some(expansions) = expansions.pop() { for (mark, expansion) in expansions.into_iter().rev() { @@ -233,30 +260,27 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }; (expansion.fold_with(&mut collector), collector.invocations) }; - self.cx.cfg = crate_config; + + let mark = self.cx.current_expansion.mark; + self.cx.resolver.visit_expansion(mark, &result.0); result } - fn expand_invoc(&mut self, invoc: Invocation) -> Expansion { + fn expand_invoc(&mut self, invoc: Invocation, ext: Rc) -> Expansion { match invoc.kind { - InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc), - InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc), + InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc, ext), + InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc, ext), } } - fn expand_attr_invoc(&mut self, invoc: Invocation) -> Expansion { + fn expand_attr_invoc(&mut self, invoc: Invocation, ext: Rc) -> Expansion { let Invocation { expansion_kind: kind, .. } = invoc; let (attr, item) = match invoc.kind { InvocationKind::Attr { attr, item } => (attr, item), _ => unreachable!(), }; - let extension = match self.cx.syntax_env.find(intern(&attr.name())) { - Some(extension) => extension, - None => unreachable!(), - }; - attr::mark_used(&attr); self.cx.bt_push(ExpnInfo { call_site: attr.span, @@ -267,7 +291,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } }); - match *extension { + match *ext { MultiModifier(ref mac) => { let item = mac.expand(self.cx, attr.span, &attr.node.value, item); kind.expect_from_annotatables(item) @@ -284,8 +308,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } /// Expand a macro invocation. Returns the result of expansion. - fn expand_bang_invoc(&mut self, invoc: Invocation) -> Expansion { - let Invocation { mark, expansion_kind: kind, .. } = invoc; + fn expand_bang_invoc(&mut self, invoc: Invocation, ext: Rc) -> Expansion { + let (mark, kind) = (invoc.mark(), invoc.expansion_kind); let (attrs, mac, ident, span) = match invoc.kind { InvocationKind::Bang { attrs, mac, ident, span } => (attrs, mac, ident, span), _ => unreachable!(), @@ -306,19 +330,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } let extname = path.segments[0].identifier.name; - let extension = if let Some(extension) = self.cx.syntax_env.find(extname) { - extension - } else { - let mut err = - self.cx.struct_span_err(path.span, &format!("macro undefined: '{}!'", &extname)); - self.cx.suggest_macro_name(&extname.as_str(), &mut err); - err.emit(); - return kind.dummy(span); - }; - let ident = ident.unwrap_or(keywords::Invalid.ident()); let marked_tts = mark_tts(&tts, mark); - let opt_expanded = match *extension { + let opt_expanded = match *ext { NormalTT(ref expandfun, exp_span, allow_internal_unstable) => { if ident.name != keywords::Invalid.name() { let msg = @@ -442,10 +456,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.invocations.push(Invocation { kind: kind, expansion_kind: expansion_kind, - mark: mark, - module: self.cx.syntax_env.current_module, - backtrace: self.cx.backtrace, - depth: self.cx.recursion_count, + expansion_data: ExpansionData { mark: mark, ..self.cx.current_expansion.clone() }, }); placeholder(expansion_kind, mark.as_u32()) } @@ -462,50 +473,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } // If `item` is an attr invocation, remove and return the macro attribute. - fn classify_item(&self, mut item: T) -> (T, Option) { + fn classify_item(&mut self, mut item: T) -> (T, Option) { let mut attr = None; item = item.map_attrs(|mut attrs| { - for i in 0..attrs.len() { - if let Some(extension) = self.cx.syntax_env.find(intern(&attrs[i].name())) { - match *extension { - MultiModifier(..) | MultiDecorator(..) => { - attr = Some(attrs.remove(i)); - break; - } - _ => {} - } - } - } + attr = self.cx.resolver.find_attr_invoc(&mut attrs); attrs }); (item, attr) } - // does this attribute list contain "macro_use" ? - fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool { - for attr in attrs { - let mut is_use = attr.check_name("macro_use"); - if attr.check_name("macro_escape") { - let msg = "macro_escape is a deprecated synonym for macro_use"; - let mut err = self.cx.struct_span_warn(attr.span, msg); - is_use = true; - if let ast::AttrStyle::Inner = attr.node.style { - err.help("consider an outer attribute, #[macro_use] mod ...").emit(); - } else { - err.emit(); - } - }; - - if is_use { - if !attr.is_word() { - self.cx.span_err(attr.span, "arguments to macro_use are not allowed here"); - } - return true; - } - } - false - } - fn configure(&mut self, node: T) -> Option { self.cfg.configure(node) } @@ -574,11 +550,9 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { } fn fold_block(&mut self, block: P) -> P { - let paths = self.cx.syntax_env.paths(); - let module = self.cx.syntax_env.add_module(false, true, paths); - let orig_module = mem::replace(&mut self.cx.syntax_env.current_module, module); + let orig_in_block = mem::replace(&mut self.cx.current_expansion.in_block, true); let result = noop_fold_block(block, self); - self.cx.syntax_env.current_module = orig_module; + self.cx.current_expansion.in_block = orig_in_block; result } @@ -613,8 +587,8 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { }) } ast::ItemKind::Mod(ast::Mod { inner, .. }) => { - let mut paths = (*self.cx.syntax_env.paths()).clone(); - paths.mod_path.push(item.ident); + let mut module = (*self.cx.current_expansion.module).clone(); + module.mod_path.push(item.ident); // Detect if this is an inline module (`mod m { ... }` as opposed to `mod m;`). // In the non-inline case, `inner` is never the dummy span (c.f. `parse_item_mod`). @@ -622,28 +596,26 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let inline_module = item.span.contains(inner) || inner == syntax_pos::DUMMY_SP; if inline_module { - paths.directory.push(&*{ + module.directory.push(&*{ ::attr::first_attr_value_str_by_name(&item.attrs, "path") .unwrap_or(item.ident.name.as_str()) }); } else { - paths.directory = + module.directory = PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(inner)); - paths.directory.pop(); + module.directory.pop(); } - let macro_use = self.contains_macro_use(&item.attrs); - let in_block = self.cx.syntax_env.in_block(); - let module = self.cx.syntax_env.add_module(macro_use, in_block, Rc::new(paths)); - let module = mem::replace(&mut self.cx.syntax_env.current_module, module); + let orig_module = + mem::replace(&mut self.cx.current_expansion.module, Rc::new(module)); let result = noop_fold_item(item, self); - self.cx.syntax_env.current_module = module; - result - }, + self.cx.current_expansion.module = orig_module; + return result; + } ast::ItemKind::ExternCrate(..) => { // We need to error on `#[macro_use] extern crate` when it isn't at the // crate root, because `$crate` won't work properly. - let is_crate_root = self.cx.syntax_env.is_crate_root(); + let is_crate_root = self.cx.current_expansion.module.mod_path.len() == 1; for def in self.cx.resolver.load_crate(&*item, is_crate_root) { match def { LoadedMacro::Def(def) => self.cx.insert_macro(def), diff --git a/src/libsyntax/ext/hygiene.rs b/src/libsyntax/ext/hygiene.rs index 27e8eab62e114..34126fac4ac78 100644 --- a/src/libsyntax/ext/hygiene.rs +++ b/src/libsyntax/ext/hygiene.rs @@ -29,7 +29,7 @@ pub struct SyntaxContextData { pub prev_ctxt: SyntaxContext, } -/// A mark represents a unique id associated with a macro expansion. +/// A mark is a unique id associated with a macro expansion. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] pub struct Mark(u32); @@ -41,6 +41,11 @@ impl Mark { }) } + /// The mark of the theoretical expansion that generates freshly parsed, unexpanded AST. + pub fn root() -> Self { + Mark(0) + } + pub fn as_u32(&self) -> u32 { self.0 } @@ -56,8 +61,8 @@ impl HygieneData { fn new() -> Self { HygieneData { syntax_contexts: vec![SyntaxContextData { - outer_mark: Mark(0), // the null mark - prev_ctxt: SyntaxContext(0), // the empty context + outer_mark: Mark::root(), + prev_ctxt: SyntaxContext::empty(), }], markings: HashMap::new(), next_mark: Mark(1), diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index 105b226111738..e75e41d0c2d4b 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -74,8 +74,8 @@ pub fn expand_stringify(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTre pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree]) -> Box { base::check_zero_tts(cx, sp, tts, "module_path!"); - let paths = cx.syntax_env.paths(); - let string = paths.mod_path.iter().map(|x| x.to_string()).collect::>().join("::"); + let mod_path = &cx.current_expansion.module.mod_path; + let string = mod_path.iter().map(|x| x.to_string()).collect::>().join("::"); base::MacEager::expr(cx.expr_str( sp, diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index ed80ec9cbc49e..51ef45b97be6f 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -211,8 +211,8 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, imported_from, rhs); let mut p = Parser::new(cx.parse_sess(), cx.cfg(), Box::new(trncbr)); - p.directory = cx.syntax_env.paths().directory.clone(); - p.restrictions = match cx.syntax_env.in_block() { + p.directory = cx.current_expansion.module.directory.clone(); + p.restrictions = match cx.current_expansion.in_block { true => Restrictions::NO_NONINLINE_MOD, false => Restrictions::empty(), }; diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index fcbce36389082..6162beb80eccc 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -11,8 +11,7 @@ //! The compiler code necessary to implement the `#[derive]` extensions. use syntax::ast::{self, MetaItem}; -use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxEnv}; -use syntax::ext::base::MultiModifier; +use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; use syntax::feature_gate; use syntax::codemap; @@ -89,7 +88,7 @@ fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span { } } -fn expand_derive(cx: &mut ExtCtxt, +pub fn expand_derive(cx: &mut ExtCtxt, span: Span, mitem: &MetaItem, annotatable: Annotatable) @@ -243,10 +242,6 @@ fn expand_derive(cx: &mut ExtCtxt, macro_rules! derive_traits { ($( $name:expr => $func:path, )+) => { - pub fn register_all(env: &mut SyntaxEnv) { - env.insert(intern("derive"), MultiModifier(Box::new(expand_derive))); - } - pub fn is_builtin_trait(name: &str) -> bool { match name { $( $name )|+ => true, diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 2065d92fd6ed7..3a6212e5445ce 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -34,11 +34,6 @@ extern crate syntax_pos; extern crate rustc_macro; extern crate rustc_errors as errors; -use syntax::ext::base::{MacroExpanderFn, NormalTT}; -use syntax::ext::base::{SyntaxEnv, SyntaxExtension}; -use syntax::parse::token::intern; - - mod asm; mod cfg; mod concat; @@ -53,28 +48,67 @@ pub mod rustc_macro_registrar; // for custom_derive pub mod deriving; -pub fn register_builtins(env: &mut SyntaxEnv) { - // utility function to simplify creating NormalTT syntax extensions - fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension { - NormalTT(Box::new(f), None, false) +use std::rc::Rc; +use syntax::ast; +use syntax::ext::base::{MacroExpanderFn, MacroRulesTT, NormalTT, MultiModifier}; +use syntax::ext::hygiene::Mark; +use syntax::parse::token::intern; + +pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, enable_quotes: bool) { + let mut register = |name, ext| { + resolver.add_macro(Mark::root(), ast::Ident::with_empty_ctxt(intern(name)), Rc::new(ext)); + }; + + register("macro_rules", MacroRulesTT); + + macro_rules! register { + ($( $name:ident: $f:expr, )*) => { $( + register(stringify!($name), NormalTT(Box::new($f as MacroExpanderFn), None, false)); + )* } } - env.insert(intern("asm"), builtin_normal_expander(asm::expand_asm)); - env.insert(intern("cfg"), builtin_normal_expander(cfg::expand_cfg)); - env.insert(intern("concat"), - builtin_normal_expander(concat::expand_syntax_ext)); - env.insert(intern("concat_idents"), - builtin_normal_expander(concat_idents::expand_syntax_ext)); - env.insert(intern("env"), builtin_normal_expander(env::expand_env)); - env.insert(intern("option_env"), - builtin_normal_expander(env::expand_option_env)); - env.insert(intern("format_args"), - // format_args uses `unstable` things internally. - NormalTT(Box::new(format::expand_format_args), None, true)); - env.insert(intern("log_syntax"), - builtin_normal_expander(log_syntax::expand_syntax_ext)); - env.insert(intern("trace_macros"), - builtin_normal_expander(trace_macros::expand_trace_macros)); - - deriving::register_all(env); + if enable_quotes { + use syntax::ext::quote::*; + register! { + quote_tokens: expand_quote_tokens, + quote_expr: expand_quote_expr, + quote_ty: expand_quote_ty, + quote_item: expand_quote_item, + quote_pat: expand_quote_pat, + quote_arm: expand_quote_arm, + quote_stmt: expand_quote_stmt, + quote_matcher: expand_quote_matcher, + quote_attr: expand_quote_attr, + quote_arg: expand_quote_arg, + quote_block: expand_quote_block, + quote_meta_item: expand_quote_meta_item, + quote_path: expand_quote_path, + } + } + + use syntax::ext::source_util::*; + register! { + line: expand_line, + column: expand_column, + file: expand_file, + stringify: expand_stringify, + include: expand_include, + include_str: expand_include_str, + include_bytes: expand_include_bytes, + module_path: expand_mod, + + asm: asm::expand_asm, + cfg: cfg::expand_cfg, + concat: concat::expand_syntax_ext, + concat_idents: concat_idents::expand_syntax_ext, + env: env::expand_env, + option_env: env::expand_option_env, + log_syntax: log_syntax::expand_syntax_ext, + trace_macros: trace_macros::expand_trace_macros, + } + + // format_args uses `unstable` things internally. + register("format_args", NormalTT(Box::new(format::expand_format_args), None, true)); + + register("derive", MultiModifier(Box::new(deriving::expand_derive))); } From c86c8d41a26b2037e80c9fd028a59313a78b3a66 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Mon, 5 Sep 2016 00:10:27 +0000 Subject: [PATCH 10/29] Perform node id assignment and `macros_at_scope` construction during the `InvocationCollector` and `PlaceholderExpander` folds. --- src/librustc_driver/driver.rs | 2 - src/librustc_resolve/assign_ids.rs | 92 ------------------------------ src/librustc_resolve/lib.rs | 1 - src/librustc_resolve/macros.rs | 4 ++ src/libsyntax/ext/base.rs | 3 + src/libsyntax/ext/expand.rs | 24 ++++++-- src/libsyntax/ext/placeholders.rs | 61 ++++++++++++++++++-- 7 files changed, 83 insertions(+), 104 deletions(-) delete mode 100644 src/librustc_resolve/assign_ids.rs diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index bf50c96034dd0..007222b2edf6d 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -707,8 +707,6 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, &sess.features.borrow()) }); - let krate = time(sess.time_passes(), "assigning node ids", || resolver.assign_node_ids(krate)); - if sess.opts.debugging_opts.input_stats { println!("Post-expansion node count: {}", count_nodes(&krate)); } diff --git a/src/librustc_resolve/assign_ids.rs b/src/librustc_resolve/assign_ids.rs deleted file mode 100644 index a9e3c6ffe9ed8..0000000000000 --- a/src/librustc_resolve/assign_ids.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2016 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. - -use Resolver; -use rustc::session::Session; -use rustc::util::nodemap::FnvHashMap; -use syntax::ast; -use syntax::ext::hygiene::Mark; -use syntax::fold::{self, Folder}; -use syntax::ptr::P; -use syntax::util::move_map::MoveMap; -use syntax::util::small_vector::SmallVector; - -use std::mem; - -impl<'a> Resolver<'a> { - pub fn assign_node_ids(&mut self, krate: ast::Crate) -> ast::Crate { - NodeIdAssigner { - sess: self.session, - macros_at_scope: &mut self.macros_at_scope, - }.fold_crate(krate) - } -} - -struct NodeIdAssigner<'a> { - sess: &'a Session, - macros_at_scope: &'a mut FnvHashMap>, -} - -impl<'a> Folder for NodeIdAssigner<'a> { - fn new_id(&mut self, old_id: ast::NodeId) -> ast::NodeId { - assert_eq!(old_id, ast::DUMMY_NODE_ID); - self.sess.next_node_id() - } - - fn fold_block(&mut self, block: P) -> P { - block.map(|mut block| { - block.id = self.new_id(block.id); - - let stmt = block.stmts.pop(); - let mut macros = Vec::new(); - block.stmts = block.stmts.move_flat_map(|stmt| { - if let ast::StmtKind::Item(ref item) = stmt.node { - if let ast::ItemKind::Mac(..) = item.node { - macros.push(item.ident.ctxt.data().outer_mark); - return None; - } - } - - let stmt = self.fold_stmt(stmt).pop().unwrap(); - if !macros.is_empty() { - self.macros_at_scope.insert(stmt.id, mem::replace(&mut macros, Vec::new())); - } - Some(stmt) - }); - - stmt.and_then(|mut stmt| { - // Avoid wasting a node id on a trailing expression statement, - // which shares a HIR node with the expression itself. - if let ast::StmtKind::Expr(expr) = stmt.node { - let expr = self.fold_expr(expr); - stmt.id = expr.id; - stmt.node = ast::StmtKind::Expr(expr); - Some(stmt) - } else { - self.fold_stmt(stmt).pop() - } - }).map(|stmt| { - if !macros.is_empty() { - self.macros_at_scope.insert(stmt.id, mem::replace(&mut macros, Vec::new())); - } - block.stmts.push(stmt); - }); - - block - }) - } - - fn fold_item(&mut self, item: P) -> SmallVector> { - match item.node { - ast::ItemKind::Mac(..) => SmallVector::zero(), - _ => fold::noop_fold_item(item, self), - } - } -} diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 6bf45ab8f6fa0..ad0507c9fb7e1 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -84,7 +84,6 @@ mod macros; mod check_unused; mod build_reduced_graph; mod resolve_imports; -mod assign_ids; enum SuggestionType { Macro(String), diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 36f501a54d261..67ee4c307d3c3 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -41,6 +41,10 @@ impl<'a> base::Resolver for Resolver<'a> { self.macro_loader.load_crate(extern_crate, allows_macros) } + fn next_node_id(&mut self) -> ast::NodeId { + self.session.next_node_id() + } + fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion) { expansion.visit_with(&mut ExpansionVisitor { current_module: self.expansion_data[mark.as_u32() as usize].module.clone(), diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index d46e2a9872e8d..1e8f8ef9ddd6c 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -464,6 +464,7 @@ pub type NamedSyntaxExtension = (Name, SyntaxExtension); pub trait Resolver { fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec; + fn next_node_id(&mut self) -> ast::NodeId; fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion); fn add_macro(&mut self, scope: Mark, ident: ast::Ident, ext: Rc); @@ -479,10 +480,12 @@ pub enum LoadedMacro { } pub struct DummyResolver; + impl Resolver for DummyResolver { fn load_crate(&mut self, _extern_crate: &ast::Item, _allows_macros: bool) -> Vec { Vec::new() } + fn next_node_id(&mut self) -> ast::NodeId { ast::DUMMY_NODE_ID } fn visit_expansion(&mut self, _invoc: Mark, _expansion: &Expansion) {} fn add_macro(&mut self, _scope: Mark, _ident: ast::Ident, _ext: Rc) {} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index beb1687dfdc72..eef38ea28e0f4 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -195,6 +195,10 @@ impl<'a, 'b> MacroExpander<'a, 'b> { krate.module.items = self.expand(items).make_items().into(); krate.exported_macros = mem::replace(&mut self.cx.exported_macros, Vec::new()); + for def in &mut krate.exported_macros { + def.id = self.cx.resolver.next_node_id() + } + if self.cx.parse_sess.span_diagnostic.err_count() > err_count { self.cx.parse_sess.span_diagnostic.abort_if_errors(); } @@ -234,7 +238,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.current_expansion = orig_expansion_data; - let mut placeholder_expander = PlaceholderExpander::new(); + let mut placeholder_expander = PlaceholderExpander::new(self.cx); while let Some(expansions) = expansions.pop() { for (mark, expansion) in expansions.into_iter().rev() { let expansion = expansion.fold_with(&mut placeholder_expander); @@ -530,9 +534,14 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { None => return SmallVector::zero(), }; - let (mac, style, attrs) = match stmt.node { - StmtKind::Mac(mac) => mac.unwrap(), - _ => return noop_fold_stmt(stmt, self), + let (mac, style, attrs) = if let StmtKind::Mac(mac) = stmt.node { + mac.unwrap() + } else { + // The placeholder expander gives ids to statements, so we avoid folding the id here. + let ast::Stmt { id, node, span } = stmt; + return noop_fold_stmt_kind(node, self).into_iter().map(|node| { + ast::Stmt { id: id, node: node, span: span } + }).collect() }; let mut placeholder = @@ -624,7 +633,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { } } } - SmallVector::one(item) + noop_fold_item(item, self) }, _ => noop_fold_item(item, self), } @@ -687,6 +696,11 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { noop_fold_item_kind(self.cfg.configure_item_kind(item), self) } + + fn new_id(&mut self, id: ast::NodeId) -> ast::NodeId { + assert_eq!(id, ast::DUMMY_NODE_ID); + self.cx.resolver.next_node_id() + } } pub struct ExpansionConfig<'feat> { diff --git a/src/libsyntax/ext/placeholders.rs b/src/libsyntax/ext/placeholders.rs index abadcf867b146..7635705daa052 100644 --- a/src/libsyntax/ext/placeholders.rs +++ b/src/libsyntax/ext/placeholders.rs @@ -10,13 +10,16 @@ use ast; use codemap::{DUMMY_SP, dummy_spanned}; +use ext::base::ExtCtxt; use ext::expand::{Expansion, ExpansionKind}; use fold::*; use parse::token::keywords; use ptr::P; +use util::move_map::MoveMap; use util::small_vector::SmallVector; use std::collections::HashMap; +use std::mem; pub fn placeholder(kind: ExpansionKind, id: ast::NodeId) -> Expansion { fn mac_placeholder() -> ast::Mac { @@ -69,13 +72,15 @@ pub fn macro_scope_placeholder() -> Expansion { placeholder(ExpansionKind::Items, ast::DUMMY_NODE_ID) } -pub struct PlaceholderExpander { +pub struct PlaceholderExpander<'a, 'b: 'a> { expansions: HashMap, + cx: &'a mut ExtCtxt<'b>, } -impl PlaceholderExpander { - pub fn new() -> Self { +impl<'a, 'b> PlaceholderExpander<'a, 'b> { + pub fn new(cx: &'a mut ExtCtxt<'b>) -> Self { PlaceholderExpander { + cx: cx, expansions: HashMap::new(), } } @@ -89,7 +94,7 @@ impl PlaceholderExpander { } } -impl Folder for PlaceholderExpander { +impl<'a, 'b> Folder for PlaceholderExpander<'a, 'b> { fn fold_item(&mut self, item: P) -> SmallVector> { match item.node { // Scope placeholder @@ -155,6 +160,54 @@ impl Folder for PlaceholderExpander { _ => noop_fold_ty(ty, self), } } + + fn fold_block(&mut self, block: P) -> P { + noop_fold_block(block, self).map(|mut block| { + let mut macros = Vec::new(); + let mut remaining_stmts = block.stmts.len(); + + block.stmts = block.stmts.move_flat_map(|mut stmt| { + remaining_stmts -= 1; + + // Scope placeholder + if let ast::StmtKind::Item(ref item) = stmt.node { + if let ast::ItemKind::Mac(..) = item.node { + macros.push(item.ident.ctxt.data().outer_mark); + return None; + } + } + + match stmt.node { + // Avoid wasting a node id on a trailing expression statement, + // which shares a HIR node with the expression itself. + ast::StmtKind::Expr(ref expr) if remaining_stmts == 0 => stmt.id = expr.id, + + _ => { + assert_eq!(stmt.id, ast::DUMMY_NODE_ID); + stmt.id = self.cx.resolver.next_node_id(); + } + } + + if !macros.is_empty() { + let macros = mem::replace(&mut macros, Vec::new()); + self.cx.resolver.add_expansions_at_stmt(stmt.id, macros); + } + + Some(stmt) + }); + + block + }) + } + + fn fold_mod(&mut self, module: ast::Mod) -> ast::Mod { + let mut module = noop_fold_mod(module, self); + module.items = module.items.move_flat_map(|item| match item.node { + ast::ItemKind::Mac(_) => None, // remove scope placeholders from modules + _ => Some(item), + }); + module + } } pub fn reconstructed_macro_rules(def: &ast::MacroDef, path: &ast::Path) -> Expansion { From f3c2dca3539e6edc745f9c91898cb97d281865c1 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 6 Sep 2016 01:45:23 +0000 Subject: [PATCH 11/29] Remove scope placeholders from the crate root. --- src/libsyntax/ext/expand.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index eef38ea28e0f4..0eb9d4bc0c2f7 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -191,8 +191,15 @@ impl<'a, 'b> MacroExpander<'a, 'b> { fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate { let err_count = self.cx.parse_sess.span_diagnostic.err_count(); - let items = Expansion::Items(SmallVector::many(krate.module.items)); - krate.module.items = self.expand(items).make_items().into(); + let mut krate_item = placeholder(ExpansionKind::Items, ast::DUMMY_NODE_ID) + .make_items().pop().unwrap().unwrap(); + krate_item.node = ast::ItemKind::Mod(krate.module); + let krate_item = Expansion::Items(SmallVector::one(P(krate_item))); + + krate.module = match self.expand(krate_item).make_items().pop().unwrap().unwrap().node { + ast::ItemKind::Mod(module) => module, + _ => unreachable!(), + }; krate.exported_macros = mem::replace(&mut self.cx.exported_macros, Vec::new()); for def in &mut krate.exported_macros { @@ -596,6 +603,10 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { }) } ast::ItemKind::Mod(ast::Mod { inner, .. }) => { + if item.ident == keywords::Invalid.ident() { + return noop_fold_item(item, self); + } + let mut module = (*self.cx.current_expansion.module).clone(); module.mod_path.push(item.ident); From 78c00398780db6f59ebf43e765fa9368dad436d2 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 6 Sep 2016 07:52:09 +0000 Subject: [PATCH 12/29] Expand generated test harnesses and macro registries. --- src/librustc_driver/driver.rs | 2 ++ src/libsyntax/test.rs | 30 +++++++++++----------- src/libsyntax_ext/rustc_macro_registrar.rs | 23 ++++++++--------- 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 007222b2edf6d..36e9fccdf5fd8 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -690,6 +690,7 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, krate = time(time_passes, "maybe building test harness", || { syntax::test::modify_for_testing(&sess.parse_sess, + &mut resolver, sess.opts.test, krate, sess.diagnostic()) @@ -700,6 +701,7 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, let is_rustc_macro_crate = crate_types.contains(&config::CrateTypeRustcMacro); let num_crate_types = crate_types.len(); syntax_ext::rustc_macro_registrar::modify(&sess.parse_sess, + &mut resolver, krate, is_rustc_macro_crate, num_crate_types, diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index dde8a8d271f62..46c9a4606ccef 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -28,7 +28,7 @@ use errors; use errors::snippet::{SnippetData}; use config; use entry::{self, EntryPointType}; -use ext::base::{ExtCtxt, DummyResolver}; +use ext::base::{ExtCtxt, Resolver}; use ext::build::AstBuilder; use ext::expand::ExpansionConfig; use fold::Folder; @@ -70,6 +70,7 @@ struct TestCtxt<'a> { // Traverse the crate, collecting all the test functions, eliding any // existing main functions, and synthesizing a main test harness pub fn modify_for_testing(sess: &ParseSess, + resolver: &mut Resolver, should_test: bool, krate: ast::Crate, span_diagnostic: &errors::Handler) -> ast::Crate { @@ -82,7 +83,7 @@ pub fn modify_for_testing(sess: &ParseSess, "reexport_test_harness_main"); if should_test { - generate_test_harness(sess, reexport_test_harness_main, krate, span_diagnostic) + generate_test_harness(sess, resolver, reexport_test_harness_main, krate, span_diagnostic) } else { krate } @@ -248,27 +249,28 @@ fn mk_reexport_mod(cx: &mut TestCtxt, tests: Vec, }).chain(tested_submods.into_iter().map(|(r, sym)| { let path = cx.ext_cx.path(DUMMY_SP, vec![super_, r, sym]); cx.ext_cx.item_use_simple_(DUMMY_SP, ast::Visibility::Public, r, path) - })); + })).collect(); let reexport_mod = ast::Mod { inner: DUMMY_SP, - items: items.collect(), + items: items, }; let sym = token::gensym_ident("__test_reexports"); - let it = P(ast::Item { + let it = cx.ext_cx.expander().fold_item(P(ast::Item { ident: sym.clone(), attrs: Vec::new(), id: ast::DUMMY_NODE_ID, node: ast::ItemKind::Mod(reexport_mod), vis: ast::Visibility::Public, span: DUMMY_SP, - }); + })).pop().unwrap(); (it, sym) } fn generate_test_harness(sess: &ParseSess, + resolver: &mut Resolver, reexport_test_harness_main: Option, krate: ast::Crate, sd: &errors::Handler) -> ast::Crate { @@ -276,13 +278,10 @@ fn generate_test_harness(sess: &ParseSess, let mut cleaner = EntryPointCleaner { depth: 0 }; let krate = cleaner.fold_crate(krate); - let mut resolver = DummyResolver; let mut cx: TestCtxt = TestCtxt { sess: sess, span_diagnostic: sd, - ext_cx: ExtCtxt::new(sess, vec![], - ExpansionConfig::default("test".to_string()), - &mut resolver), + ext_cx: ExtCtxt::new(sess, vec![], ExpansionConfig::default("test".to_string()), resolver), path: Vec::new(), testfns: Vec::new(), reexport_test_harness_main: reexport_test_harness_main, @@ -511,16 +510,17 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P, Option>) { items: vec![import, mainfn, tests], }; let item_ = ast::ItemKind::Mod(testmod); - let mod_ident = token::gensym_ident("__test"); - let item = P(ast::Item { + + let mut expander = cx.ext_cx.expander(); + let item = expander.fold_item(P(ast::Item { id: ast::DUMMY_NODE_ID, ident: mod_ident, attrs: vec![], node: item_, vis: ast::Visibility::Public, span: DUMMY_SP, - }); + })).pop().unwrap(); let reexport = cx.reexport_test_harness_main.as_ref().map(|s| { // building `use = __test::main` let reexport_ident = token::str_to_ident(&s); @@ -529,14 +529,14 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P, Option>) { nospan(ast::ViewPathSimple(reexport_ident, path_node(vec![mod_ident, token::str_to_ident("main")]))); - P(ast::Item { + expander.fold_item(P(ast::Item { id: ast::DUMMY_NODE_ID, ident: keywords::Invalid.ident(), attrs: vec![], node: ast::ItemKind::Use(P(use_path)), vis: ast::Visibility::Inherited, span: DUMMY_SP - }) + })).pop().unwrap() }); debug!("Synthetic test module:\n{}\n", pprust::item_to_string(&item)); diff --git a/src/libsyntax_ext/rustc_macro_registrar.rs b/src/libsyntax_ext/rustc_macro_registrar.rs index 78fed9d33dd82..c07e79179398e 100644 --- a/src/libsyntax_ext/rustc_macro_registrar.rs +++ b/src/libsyntax_ext/rustc_macro_registrar.rs @@ -13,12 +13,13 @@ use std::mem; use errors; use syntax::ast::{self, Ident, NodeId}; use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute}; -use syntax::ext::base::{ExtCtxt, DummyResolver}; +use syntax::ext::base::ExtCtxt; use syntax::ext::build::AstBuilder; use syntax::ext::expand::ExpansionConfig; use syntax::parse::ParseSess; use syntax::parse::token::{self, InternedString}; use syntax::feature_gate::Features; +use syntax::fold::Folder; use syntax::ptr::P; use syntax_pos::{Span, DUMMY_SP}; use syntax::visit::{self, Visitor}; @@ -39,16 +40,14 @@ struct CollectCustomDerives<'a> { } pub fn modify(sess: &ParseSess, + resolver: &mut ::syntax::ext::base::Resolver, mut krate: ast::Crate, is_rustc_macro_crate: bool, num_crate_types: usize, handler: &errors::Handler, features: &Features) -> ast::Crate { - let mut loader = DummyResolver; - let mut cx = ExtCtxt::new(sess, - Vec::new(), - ExpansionConfig::default("rustc_macro".to_string()), - &mut loader); + let ecfg = ExpansionConfig::default("rustc_macro".to_string()); + let mut cx = ExtCtxt::new(sess, Vec::new(), ecfg, resolver); let mut collect = CollectCustomDerives { derives: Vec::new(), @@ -268,13 +267,11 @@ fn mk_registrar(cx: &mut ExtCtxt, i.vis = ast::Visibility::Public; i }); - let module = cx.item_mod(span, - span, - ast::Ident::with_empty_ctxt(token::gensym("registrar")), - Vec::new(), - vec![krate, func]); - module.map(|mut i| { + let ident = ast::Ident::with_empty_ctxt(token::gensym("registrar")); + let module = cx.item_mod(span, span, ident, Vec::new(), vec![krate, func]).map(|mut i| { i.vis = ast::Visibility::Public; i - }) + }); + + cx.expander().fold_item(module).pop().unwrap() } From b54e1e399741579612f13e2df98a25ea9447989d Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 6 Sep 2016 05:42:45 +0000 Subject: [PATCH 13/29] Differentiate between monotonic and non-monotonic expansion and only assign node ids during monotonic expansion. --- src/libsyntax/ext/base.rs | 8 +++++++- src/libsyntax/ext/expand.rs | 24 ++++++++++++++-------- src/libsyntax/ext/placeholders.rs | 10 ++++++--- src/libsyntax/test.rs | 4 ++-- src/libsyntax_ext/rustc_macro_registrar.rs | 2 +- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 1e8f8ef9ddd6c..fb4816d3847ed 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -549,7 +549,13 @@ impl<'a> ExtCtxt<'a> { /// Returns a `Folder` for deeply expanding all macros in an AST node. pub fn expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> { - expand::MacroExpander::new(self, false, false) + expand::MacroExpander::new(self, false) + } + + /// Returns a `Folder` that deeply expands all macros and assigns all node ids in an AST node. + /// Once node ids are assigned, the node may not be expanded, removed, or otherwise modified. + pub fn monotonic_expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> { + expand::MacroExpander::new(self, true) } pub fn new_parser_from_tts(&self, tts: &[tokenstream::TokenTree]) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 0eb9d4bc0c2f7..62e299684b760 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -175,16 +175,16 @@ pub struct MacroExpander<'a, 'b:'a> { pub cx: &'a mut ExtCtxt<'b>, pub single_step: bool, pub keep_macs: bool, + monotonic: bool, // c.f. `cx.monotonic_expander()` } impl<'a, 'b> MacroExpander<'a, 'b> { - pub fn new(cx: &'a mut ExtCtxt<'b>, - single_step: bool, - keep_macs: bool) -> MacroExpander<'a, 'b> { + pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self { MacroExpander { cx: cx, - single_step: single_step, - keep_macs: keep_macs + monotonic: monotonic, + single_step: false, + keep_macs: false, } } @@ -245,7 +245,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.current_expansion = orig_expansion_data; - let mut placeholder_expander = PlaceholderExpander::new(self.cx); + let mut placeholder_expander = PlaceholderExpander::new(self.cx, self.monotonic); while let Some(expansions) = expansions.pop() { for (mark, expansion) in expansions.into_iter().rev() { let expansion = expansion.fold_with(&mut placeholder_expander); @@ -268,6 +268,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }, cx: self.cx, invocations: Vec::new(), + monotonic: self.monotonic, }; (expansion.fold_with(&mut collector), collector.invocations) }; @@ -450,6 +451,7 @@ struct InvocationCollector<'a, 'b: 'a> { cx: &'a mut ExtCtxt<'b>, cfg: StripUnconfigured<'a>, invocations: Vec, + monotonic: bool, } macro_rules! fully_configure { @@ -709,8 +711,12 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { } fn new_id(&mut self, id: ast::NodeId) -> ast::NodeId { - assert_eq!(id, ast::DUMMY_NODE_ID); - self.cx.resolver.next_node_id() + if self.monotonic { + assert_eq!(id, ast::DUMMY_NODE_ID); + self.cx.resolver.next_node_id() + } else { + id + } } } @@ -763,7 +769,7 @@ pub fn expand_crate(cx: &mut ExtCtxt, user_exts: Vec, c: Crate) -> Crate { cx.initialize(user_exts, &c); - cx.expander().expand_crate(c) + cx.monotonic_expander().expand_crate(c) } // Expands crate using supplied MacroExpander - allows for diff --git a/src/libsyntax/ext/placeholders.rs b/src/libsyntax/ext/placeholders.rs index 7635705daa052..47f366a88768e 100644 --- a/src/libsyntax/ext/placeholders.rs +++ b/src/libsyntax/ext/placeholders.rs @@ -75,13 +75,15 @@ pub fn macro_scope_placeholder() -> Expansion { pub struct PlaceholderExpander<'a, 'b: 'a> { expansions: HashMap, cx: &'a mut ExtCtxt<'b>, + monotonic: bool, } impl<'a, 'b> PlaceholderExpander<'a, 'b> { - pub fn new(cx: &'a mut ExtCtxt<'b>) -> Self { + pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self { PlaceholderExpander { cx: cx, expansions: HashMap::new(), + monotonic: monotonic, } } @@ -182,13 +184,15 @@ impl<'a, 'b> Folder for PlaceholderExpander<'a, 'b> { // which shares a HIR node with the expression itself. ast::StmtKind::Expr(ref expr) if remaining_stmts == 0 => stmt.id = expr.id, - _ => { + _ if self.monotonic => { assert_eq!(stmt.id, ast::DUMMY_NODE_ID); stmt.id = self.cx.resolver.next_node_id(); } + + _ => {} } - if !macros.is_empty() { + if self.monotonic && !macros.is_empty() { let macros = mem::replace(&mut macros, Vec::new()); self.cx.resolver.add_expansions_at_stmt(stmt.id, macros); } diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 46c9a4606ccef..6327e8f71bcd5 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -257,7 +257,7 @@ fn mk_reexport_mod(cx: &mut TestCtxt, tests: Vec, }; let sym = token::gensym_ident("__test_reexports"); - let it = cx.ext_cx.expander().fold_item(P(ast::Item { + let it = cx.ext_cx.monotonic_expander().fold_item(P(ast::Item { ident: sym.clone(), attrs: Vec::new(), id: ast::DUMMY_NODE_ID, @@ -512,7 +512,7 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P, Option>) { let item_ = ast::ItemKind::Mod(testmod); let mod_ident = token::gensym_ident("__test"); - let mut expander = cx.ext_cx.expander(); + let mut expander = cx.ext_cx.monotonic_expander(); let item = expander.fold_item(P(ast::Item { id: ast::DUMMY_NODE_ID, ident: mod_ident, diff --git a/src/libsyntax_ext/rustc_macro_registrar.rs b/src/libsyntax_ext/rustc_macro_registrar.rs index c07e79179398e..ce3e53cdf97f4 100644 --- a/src/libsyntax_ext/rustc_macro_registrar.rs +++ b/src/libsyntax_ext/rustc_macro_registrar.rs @@ -273,5 +273,5 @@ fn mk_registrar(cx: &mut ExtCtxt, i }); - cx.expander().fold_item(module).pop().unwrap() + cx.monotonic_expander().fold_item(module).pop().unwrap() } From 5a881e920ec39c80310ff832867f2ef51168a1de Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 12 Sep 2016 18:09:49 -0400 Subject: [PATCH 14/29] Make sure that projection bounds in ty::TraitObject are sorted in a way that is stable across compilation sessions and crate boundaries. --- src/librustc/ty/context.rs | 2 +- src/librustc/ty/mod.rs | 4 ---- src/librustc/ty/sty.rs | 21 ++++++++++++--------- src/librustc/ty/trait_def.rs | 10 ++++++++-- src/librustc_metadata/decoder.rs | 4 +++- src/librustc_typeck/collect.rs | 5 ++++- 6 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 20601493d68f3..6d7a2d6cba1c7 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1303,7 +1303,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } pub fn mk_trait(self, mut obj: TraitObject<'tcx>) -> Ty<'tcx> { - obj.projection_bounds.sort_by(|a, b| a.sort_key().cmp(&b.sort_key())); + obj.projection_bounds.sort_by_key(|b| b.sort_key(self)); self.mk_ty(TyTrait(box obj)) } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index f634c8e37d7bd..14eb2fb7914c3 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1018,10 +1018,6 @@ impl<'tcx> PolyProjectionPredicate<'tcx> { pub fn item_name(&self) -> Name { self.0.projection_ty.item_name // safe to skip the binder to access a name } - - pub fn sort_key(&self) -> (DefId, Name) { - self.0.projection_ty.sort_key() - } } pub trait ToPolyTraitRef<'tcx> { diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index a755dd056cd84..5fdc7abc0af5b 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -23,7 +23,7 @@ use std::mem; use std::ops; use syntax::abi; use syntax::ast::{self, Name}; -use syntax::parse::token::keywords; +use syntax::parse::token::{keywords, InternedString}; use serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -440,12 +440,6 @@ pub struct ProjectionTy<'tcx> { pub item_name: Name, } -impl<'tcx> ProjectionTy<'tcx> { - pub fn sort_key(&self) -> (DefId, Name) { - (self.trait_ref.def_id, self.item_name) - } -} - #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct BareFnTy<'tcx> { pub unsafety: hir::Unsafety, @@ -738,8 +732,17 @@ impl<'a, 'tcx, 'gcx> PolyExistentialProjection<'tcx> { self.0.item_name // safe to skip the binder to access a name } - pub fn sort_key(&self) -> (DefId, Name) { - (self.0.trait_ref.def_id, self.0.item_name) + pub fn sort_key(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> (u64, InternedString) { + // We want something here that is stable across crate boundaries. + // The DefId isn't but the `deterministic_hash` of the corresponding + // DefPath is. + let trait_def = tcx.lookup_trait_def(self.0.trait_ref.def_id); + let def_path_hash = trait_def.def_path_hash; + + // An `ast::Name` is also not stable (it's just an index into an + // interning table), so map to the corresponding `InternedString`. + let item_name = self.0.item_name.as_str(); + (def_path_hash, item_name) } pub fn with_self_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, diff --git a/src/librustc/ty/trait_def.rs b/src/librustc/ty/trait_def.rs index 61285e8f8b0a5..268b2fcaa4adb 100644 --- a/src/librustc/ty/trait_def.rs +++ b/src/librustc/ty/trait_def.rs @@ -70,7 +70,11 @@ pub struct TraitDef<'tcx> { pub specialization_graph: RefCell, /// Various flags - pub flags: Cell + pub flags: Cell, + + /// The ICH of this trait's DefPath, cached here so it doesn't have to be + /// recomputed all the time. + pub def_path_hash: u64, } impl<'a, 'gcx, 'tcx> TraitDef<'tcx> { @@ -78,7 +82,8 @@ impl<'a, 'gcx, 'tcx> TraitDef<'tcx> { paren_sugar: bool, generics: &'tcx ty::Generics<'tcx>, trait_ref: ty::TraitRef<'tcx>, - associated_type_names: Vec) + associated_type_names: Vec, + def_path_hash: u64) -> TraitDef<'tcx> { TraitDef { paren_sugar: paren_sugar, @@ -90,6 +95,7 @@ impl<'a, 'gcx, 'tcx> TraitDef<'tcx> { blanket_impls: RefCell::new(vec![]), flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS), specialization_graph: RefCell::new(traits::specialization_graph::Graph::new()), + def_path_hash: def_path_hash, } } diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index d2840fbe4fe46..824e892072f57 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -385,12 +385,14 @@ pub fn get_trait_def<'a, 'tcx>(cdata: Cmd, let unsafety = parse_unsafety(item_doc); let associated_type_names = parse_associated_type_names(item_doc); let paren_sugar = parse_paren_sugar(item_doc); + let def_path = def_path(cdata, item_id); ty::TraitDef::new(unsafety, paren_sugar, generics, item_trait_ref(item_doc, tcx, cdata), - associated_type_names) + associated_type_names, + def_path.deterministic_hash(tcx)) } pub fn get_adt_def<'a, 'tcx>(cdata: Cmd, diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 7d111cdc4156f..04aca8c0947ca 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1290,12 +1290,15 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, } }).collect(); + let def_path_hash = tcx.def_path(def_id).deterministic_hash(tcx); + let trait_ref = ty::TraitRef::new(def_id, substs); let trait_def = ty::TraitDef::new(unsafety, paren_sugar, ty_generics, trait_ref, - associated_type_names); + associated_type_names, + def_path_hash); tcx.intern_trait_def(trait_def) } From 5c923f0159105bf9491a86cd5bd7eb20807387b6 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 12 Sep 2016 18:24:45 -0400 Subject: [PATCH 15/29] Remove redundant sorting of projection bounds in tyencode. --- src/librustc_metadata/tyencode.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/librustc_metadata/tyencode.rs b/src/librustc_metadata/tyencode.rs index 8030abf6330e7..dbefd3eacc24a 100644 --- a/src/librustc_metadata/tyencode.rs +++ b/src/librustc_metadata/tyencode.rs @@ -104,14 +104,7 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx enc_region(w, cx, obj.region_bound); - // Encode projection_bounds in a stable order - let mut projection_bounds: Vec<_> = obj.projection_bounds - .iter() - .map(|b| (b.item_name().as_str(), b)) - .collect(); - projection_bounds.sort_by_key(|&(ref name, _)| name.clone()); - - for tp in projection_bounds.iter().map(|&(_, tp)| tp) { + for tp in &obj.projection_bounds { write!(w, "P"); enc_existential_projection(w, cx, &tp.0); } From 94d75013bcf6fc8e79cd7c0c76c37e68c458db6f Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 13 Sep 2016 08:40:14 -0400 Subject: [PATCH 16/29] Remove redundant sorting of projections in TypeIdHasher. --- src/librustc/ty/util.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 6b3ebaa895fa3..c6020838b5320 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -458,15 +458,11 @@ impl<'a, 'gcx, 'tcx> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx> { data.region_bound.visit_with(self); self.hash(data.builtin_bounds); - // Only projection bounds are left, sort and hash them. - let mut projection_bounds: Vec<_> = data.projection_bounds - .iter() - .map(|b| (b.item_name().as_str(), b)) - .collect(); - projection_bounds.sort_by_key(|&(ref name, _)| name.clone()); - for (name, bound) in projection_bounds { + // Only projection bounds are left, hash them. + self.hash(data.projection_bounds.len()); + for bound in &data.projection_bounds { self.def_id(bound.0.trait_ref.def_id); - self.hash(name); + self.hash(bound.0.item_name); bound.visit_with(self); } From 75a0dd0fca4e3e3b5eade66fb3dddc2b9868dc52 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 13 Sep 2016 08:45:34 -0400 Subject: [PATCH 17/29] Make TypeIdHasher use DefPath::deterministic_hash() for stability. --- src/librustc/ty/util.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index c6020838b5320..a8287ecc046cf 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -411,15 +411,11 @@ impl<'a, 'gcx, 'tcx> TypeIdHasher<'a, 'gcx, 'tcx> { } fn def_id(&mut self, did: DefId) { - // Hash the crate identification information. - let name = self.tcx.crate_name(did.krate); - let disambiguator = self.tcx.crate_disambiguator(did.krate); - self.hash((name, disambiguator)); - - // Hash the item path within that crate. - // FIXME(#35379) This should use a deterministic - // DefPath hashing mechanism, not the DefIndex. - self.hash(did.index); + // Hash the DefPath corresponding to the DefId, which is independent + // of compiler internal state. + let tcx = self.tcx; + let def_path = tcx.def_path(did); + def_path.deterministic_hash_to(tcx, &mut self.state); } } From 869d14447af84bfb506e19abf2ca97810845c675 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 13 Sep 2016 15:02:17 -0400 Subject: [PATCH 18/29] TypeIdHasher: Let projections be hashed implicitly by the visitor. --- src/librustc/ty/util.rs | 11 ----------- src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs | 5 +++++ src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs | 5 +++++ src/test/run-pass/typeid-intrinsic.rs | 9 +++++++++ 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index a8287ecc046cf..344f0e57d6423 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -453,17 +453,6 @@ impl<'a, 'gcx, 'tcx> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx> { // Hash region and builtin bounds. data.region_bound.visit_with(self); self.hash(data.builtin_bounds); - - // Only projection bounds are left, hash them. - self.hash(data.projection_bounds.len()); - for bound in &data.projection_bounds { - self.def_id(bound.0.trait_ref.def_id); - self.hash(bound.0.item_name); - bound.visit_with(self); - } - - // Bypass super_visit_with, we've visited everything. - return false; } TyTuple(tys) => { self.hash(tys.len()); diff --git a/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs b/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs index 42c0da6286bdc..10e315f269f97 100644 --- a/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs +++ b/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs @@ -22,6 +22,8 @@ pub type F = Option; pub type G = usize; pub type H = &'static str; pub type I = Box; +pub type I32Iterator = Iterator; +pub type U32Iterator = Iterator; pub fn id_A() -> TypeId { TypeId::of::() } pub fn id_B() -> TypeId { TypeId::of::() } @@ -34,3 +36,6 @@ pub fn id_H() -> TypeId { TypeId::of::() } pub fn id_I() -> TypeId { TypeId::of::() } pub fn foo() -> TypeId { TypeId::of::() } + +pub fn id_i32_iterator() -> TypeId { TypeId::of::() } +pub fn id_u32_iterator() -> TypeId { TypeId::of::() } diff --git a/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs b/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs index 42c0da6286bdc..10e315f269f97 100644 --- a/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs +++ b/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs @@ -22,6 +22,8 @@ pub type F = Option; pub type G = usize; pub type H = &'static str; pub type I = Box; +pub type I32Iterator = Iterator; +pub type U32Iterator = Iterator; pub fn id_A() -> TypeId { TypeId::of::() } pub fn id_B() -> TypeId { TypeId::of::() } @@ -34,3 +36,6 @@ pub fn id_H() -> TypeId { TypeId::of::() } pub fn id_I() -> TypeId { TypeId::of::() } pub fn foo() -> TypeId { TypeId::of::() } + +pub fn id_i32_iterator() -> TypeId { TypeId::of::() } +pub fn id_u32_iterator() -> TypeId { TypeId::of::() } diff --git a/src/test/run-pass/typeid-intrinsic.rs b/src/test/run-pass/typeid-intrinsic.rs index e99a5f69af40f..36650368d57be 100644 --- a/src/test/run-pass/typeid-intrinsic.rs +++ b/src/test/run-pass/typeid-intrinsic.rs @@ -78,4 +78,13 @@ pub fn main() { b.hash(&mut s2); assert_eq!(s1.finish(), s2.finish()); + + // Check projections + + assert_eq!(TypeId::of::(), other1::id_i32_iterator()); + assert_eq!(TypeId::of::(), other1::id_u32_iterator()); + assert_eq!(other1::id_i32_iterator(), other2::id_i32_iterator()); + assert_eq!(other1::id_u32_iterator(), other2::id_u32_iterator()); + assert!(other1::id_i32_iterator() != other1::id_u32_iterator()); + assert!(TypeId::of::() != TypeId::of::()); } From 377c3e1123dd63689ccfddb5d37699f31846caf5 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 13 Sep 2016 15:46:21 -0400 Subject: [PATCH 19/29] Fix rebasing fallout. --- src/librustc_metadata/decoder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 824e892072f57..624bffb7e0369 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -385,7 +385,7 @@ pub fn get_trait_def<'a, 'tcx>(cdata: Cmd, let unsafety = parse_unsafety(item_doc); let associated_type_names = parse_associated_type_names(item_doc); let paren_sugar = parse_paren_sugar(item_doc); - let def_path = def_path(cdata, item_id); + let def_path = def_path(cdata, item_id).unwrap(); ty::TraitDef::new(unsafety, paren_sugar, From 7ec9b81326121ae07feb0a19f76b3f16b98d0c43 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 13 Sep 2016 16:01:39 -0400 Subject: [PATCH 20/29] TypeIdHasher: Remove more redundant explicit visit calls. --- src/librustc/ty/util.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 344f0e57d6423..d34fdaa7d71cd 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -441,17 +441,7 @@ impl<'a, 'gcx, 'tcx> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx> { self.hash(f.sig.variadic()); } TyTrait(ref data) => { - // Trait objects have a list of projection bounds - // that are not guaranteed to be sorted in an order - // that gets preserved across crates, so we need - // to sort them again by the name, in string form. - - // Hash the whole principal trait ref. self.def_id(data.principal.def_id()); - data.principal.visit_with(self); - - // Hash region and builtin bounds. - data.region_bound.visit_with(self); self.hash(data.builtin_bounds); } TyTuple(tys) => { From b49a26ec6fe2faf06360139b4c6ed59684b083b4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 13 Sep 2016 16:04:27 -0400 Subject: [PATCH 21/29] invoke drop glue with a ptr to (data, meta) This is done by creating a little space on the stack. Hokey, but it's the simplest fix I can see. --- src/librustc_trans/glue.rs | 9 ++++++++- src/librustc_trans/intrinsic.rs | 1 + src/librustc_trans/mir/block.rs | 24 +++++++++++++++++++++--- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 6b48c6ae26dac..3073b1dbfaeeb 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -296,6 +296,7 @@ fn trans_custom_dtor<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, sized_args = [v0]; &sized_args } else { + // FIXME(#36457) -- we should pass unsized values to drop glue as two arguments unsized_args = [ Load(bcx, get_dataptr(bcx, v0)), Load(bcx, get_meta(bcx, v0)) @@ -440,7 +441,9 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, } } -fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueKind<'tcx>) +fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + v0: ValueRef, + g: DropGlueKind<'tcx>) -> Block<'blk, 'tcx> { let t = g.ty(); @@ -463,6 +466,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK let llval = get_dataptr(bcx, v0); let llbox = Load(bcx, llval); let bcx = drop_ty(bcx, v0, content_ty, DebugLoc::None); + // FIXME(#36457) -- we should pass unsized values to drop glue as two arguments let info = get_meta(bcx, v0); let info = Load(bcx, info); let (llsize, llalign) = @@ -488,6 +492,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK // No support in vtable for distinguishing destroying with // versus without calling Drop::drop. Assert caller is // okay with always calling the Drop impl, if any. + // FIXME(#36457) -- we should pass unsized values to drop glue as two arguments assert!(!skip_dtor); let data_ptr = get_dataptr(bcx, v0); let vtable_ptr = Load(bcx, get_meta(bcx, v0)); @@ -543,6 +548,7 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, let value = if type_is_sized(cx.tcx(), t) { adt::MaybeSizedValue::sized(av) } else { + // FIXME(#36457) -- we should pass unsized values as two arguments let data = Load(cx, get_dataptr(cx, av)); let info = Load(cx, get_meta(cx, av)); adt::MaybeSizedValue::unsized_(data, info) @@ -586,6 +592,7 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, let val = if type_is_sized(cx.tcx(), field_ty) { llfld_a } else { + // FIXME(#36457) -- we should pass unsized values as two arguments let scratch = alloc_ty(cx, field_ty, "__fat_ptr_iter"); Store(cx, llfld_a, get_dataptr(cx, scratch)); Store(cx, value.meta, get_meta(cx, scratch)); diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 2049696ee4f71..fbade107ecfda 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -186,6 +186,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let ptr = if is_sized { llargs[0] } else { + // FIXME(#36457) -- we should pass unsized values as two arguments let scratch = alloc_ty(bcx, tp_ty, "drop"); call_lifetime_start(bcx, scratch); Store(bcx, llargs[0], get_dataptr(bcx, scratch)); diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index fbd04d7b38029..baeafbe3e346f 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -242,10 +242,28 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let lvalue = self.trans_lvalue(&bcx, location); let drop_fn = glue::get_drop_glue(bcx.ccx(), ty); let drop_ty = glue::get_drop_glue_type(bcx.tcx(), ty); - let llvalue = if drop_ty != ty { - bcx.pointercast(lvalue.llval, type_of::type_of(bcx.ccx(), drop_ty).ptr_to()) + let is_sized = common::type_is_sized(bcx.tcx(), ty); + let llvalue = if is_sized { + if drop_ty != ty { + bcx.pointercast(lvalue.llval, type_of::type_of(bcx.ccx(), drop_ty).ptr_to()) + } else { + lvalue.llval + } } else { - lvalue.llval + // FIXME(#36457) Currently drop glue takes sized + // values as a `*(data, meta)`, but elsewhere in + // MIR we pass `(data, meta)` as two separate + // arguments. It would be better to fix drop glue, + // but I am shooting for a quick fix to #35546 + // here that can be cleanly backported to beta, so + // I want to avoid touching all of trans. + bcx.with_block(|bcx| { + let scratch = base::alloc_ty(bcx, ty, "drop"); + base::call_lifetime_start(bcx, scratch); + build::Store(bcx, lvalue.llval, base::get_dataptr(bcx, scratch)); + build::Store(bcx, lvalue.llextra, base::get_meta(bcx, scratch)); + scratch + }) }; if let Some(unwind) = unwind { bcx.invoke(drop_fn, From 693676da4f65a14fbae2c44cd4e2a94ba0ccf6d5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 13 Sep 2016 18:33:35 -0400 Subject: [PATCH 22/29] add missing test --- src/test/run-pass/issue-35546.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/test/run-pass/issue-35546.rs diff --git a/src/test/run-pass/issue-35546.rs b/src/test/run-pass/issue-35546.rs new file mode 100644 index 0000000000000..e8d14f1d42146 --- /dev/null +++ b/src/test/run-pass/issue-35546.rs @@ -0,0 +1,28 @@ +// Copyright 2016 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. + +// Regression test for #35546. Check that we are able to codegen +// this. Before we had problems because of the drop glue signature +// around dropping a trait object (specifically, when dropping the +// `value` field of `Node`). + +struct Node { + next: Option>>, + value: T, +} + +fn clear(head: &mut Option>>) { + match head.take() { + Some(node) => *head = node.next, + None => (), + } +} + +fn main() {} From 606cdede0deaa6678fe7db3cc12b1a1e063012ee Mon Sep 17 00:00:00 2001 From: Eugene Bulkin Date: Tue, 13 Sep 2016 17:21:54 -0700 Subject: [PATCH 23/29] Add checked operation methods to Duration --- src/libstd/time/duration.rs | 162 ++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 79bbe5e7daa45..12e580fe80184 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -97,6 +97,130 @@ impl Duration { #[stable(feature = "duration", since = "1.3.0")] #[inline] pub fn subsec_nanos(&self) -> u32 { self.nanos } + + /// Checked duration addition. Computes `self + other`, returning `None` + /// if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)), Some(Duration::new(0, 1))); + /// assert_eq!(Duration::new(1, 0).checked_add(Duration::new(::u64::MAX, 0)), None); + /// ``` + #[unstable(feature = "duration_checked_ops", issue = "35774")] + #[inline] + pub fn checked_add(self, rhs: Duration) -> Option { + if let Some(mut secs) = self.secs.checked_add(rhs.secs) { + let mut nanos = self.nanos + rhs.nanos; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + if let Some(new_secs) = secs.checked_add(1) { + secs = new_secs; + } else { + return None; + } + } + debug_assert!(nanos < NANOS_PER_SEC); + Some(Duration { + secs: secs, + nanos: nanos, + }) + } else { + None + } + } + + /// Checked duration subtraction. Computes `self + other`, returning `None` + /// if the result would be negative or if underflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// assert_eq!(Duration::new(0, 1).checked_sub(Duration::new(0, 0)), Some(Duration::new(0, 1))); + /// assert_eq!(Duration::new(0, 0).checked_sub(Duration::new(0, 1)), None); + /// ``` + #[unstable(feature = "duration_checked_ops", issue = "35774")] + #[inline] + pub fn checked_sub(self, rhs: Duration) -> Option { + if let Some(mut secs) = self.secs.checked_sub(rhs.secs) { + let nanos = if self.nanos >= rhs.nanos { + self.nanos - rhs.nanos + } else { + if let Some(sub_secs) = secs.checked_sub(1) { + secs = sub_secs; + self.nanos + NANOS_PER_SEC - rhs.nanos + } else { + return None; + } + }; + debug_assert!(nanos < NANOS_PER_SEC); + Some(Duration { secs: secs, nanos: nanos }) + } else { + None + } + } + + /// Checked integer multiplication. Computes `self * other`, returning + /// `None` if underflow or overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// assert_eq!(Duration::new(0, 500_000_001).checked_mul(2), Some(Duration::new(1, 2))); + /// assert_eq!(Duration::new(::u64::MAX - 1, 0).checked_mul(2), None); + /// ``` + #[unstable(feature = "duration_checked_ops", issue = "35774")] + #[inline] + pub fn checked_mul(self, rhs: u32) -> Option { + // Multiply nanoseconds as u64, because it cannot overflow that way. + let total_nanos = self.nanos as u64 * rhs as u64; + let extra_secs = total_nanos / (NANOS_PER_SEC as u64); + let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32; + if let Some(secs) = self.secs + .checked_mul(rhs as u64) + .and_then(|s| s.checked_add(extra_secs)) { + debug_assert!(nanos < NANOS_PER_SEC); + Some(Duration { + secs: secs, + nanos: nanos, + }) + } else { + None + } + } + + /// Checked duration division. Computes `self / other`, returning `None` + /// if `other == 0` or the operation results in underflow or overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0))); + /// assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000))); + /// assert_eq!(Duration::new(2, 0).checked_div(0), None); + /// ``` + #[unstable(feature = "duration_checked_ops", issue = "35774")] + #[inline] + pub fn checked_div(self, rhs: u32) -> Option { + if rhs != 0 { + let secs = self.secs / (rhs as u64); + let carry = self.secs - secs * (rhs as u64); + let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64); + let nanos = self.nanos / rhs + (extra_nanos as u32); + debug_assert!(nanos < NANOS_PER_SEC); + Some(Duration { secs: secs, nanos: nanos }) + } else { + None + } + } } #[stable(feature = "duration", since = "1.3.0")] @@ -234,6 +358,15 @@ mod tests { Duration::new(1, 1)); } + #[test] + fn checked_add() { + assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)), + Some(Duration::new(0, 1))); + assert_eq!(Duration::new(0, 500_000_000).checked_add(Duration::new(0, 500_000_001)), + Some(Duration::new(1, 1))); + assert_eq!(Duration::new(1, 0).checked_add(Duration::new(::u64::MAX, 0)), None); + } + #[test] fn sub() { assert_eq!(Duration::new(0, 1) - Duration::new(0, 0), @@ -244,6 +377,18 @@ mod tests { Duration::new(0, 999_999_999)); } + #[test] + fn checked_sub() { + let zero = Duration::new(0, 0); + let one_nano = Duration::new(0, 1); + let one_sec = Duration::new(1, 0); + assert_eq!(one_nano.checked_sub(zero), Some(Duration::new(0, 1))); + assert_eq!(one_sec.checked_sub(one_nano), + Some(Duration::new(0, 999_999_999))); + assert_eq!(zero.checked_sub(one_nano), None); + assert_eq!(zero.checked_sub(one_sec), None); + } + #[test] #[should_panic] fn sub_bad1() { Duration::new(0, 0) - Duration::new(0, 1); @@ -263,6 +408,16 @@ mod tests { Duration::new(2000, 4000)); } + #[test] + fn checked_mul() { + assert_eq!(Duration::new(0, 1).checked_mul(2), Some(Duration::new(0, 2))); + assert_eq!(Duration::new(1, 1).checked_mul(3), Some(Duration::new(3, 3))); + assert_eq!(Duration::new(0, 500_000_001).checked_mul(4), Some(Duration::new(2, 4))); + assert_eq!(Duration::new(0, 500_000_001).checked_mul(4000), + Some(Duration::new(2000, 4000))); + assert_eq!(Duration::new(::u64::MAX - 1, 0).checked_mul(2), None); + } + #[test] fn div() { assert_eq!(Duration::new(0, 1) / 2, Duration::new(0, 0)); @@ -270,4 +425,11 @@ mod tests { assert_eq!(Duration::new(99, 999_999_000) / 100, Duration::new(0, 999_999_990)); } + + #[test] + fn checked_div() { + assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0))); + assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000))); + assert_eq!(Duration::new(2, 0).checked_div(0), None); + } } From 07b41b5555f2582ce741569ce44116451105742c Mon Sep 17 00:00:00 2001 From: Eugene Bulkin Date: Tue, 13 Sep 2016 17:32:24 -0700 Subject: [PATCH 24/29] Fix Duration::checked_mul documentation --- src/libstd/time/duration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 12e580fe80184..3024a44a208ab 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -164,7 +164,7 @@ impl Duration { } } - /// Checked integer multiplication. Computes `self * other`, returning + /// Checked duration multiplication. Computes `self * other`, returning /// `None` if underflow or overflow occurred. /// /// # Examples From b1bcd185b01e1aee3a6c2e976e915b244626e129 Mon Sep 17 00:00:00 2001 From: Eugene Bulkin Date: Tue, 13 Sep 2016 17:58:45 -0700 Subject: [PATCH 25/29] Implement add, sub, mul and div methods using checked methods for Duration --- src/libstd/time/duration.rs | 39 ++++--------------------------------- 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 3024a44a208ab..a3493f0593c8c 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -228,15 +228,7 @@ impl Add for Duration { type Output = Duration; fn add(self, rhs: Duration) -> Duration { - let mut secs = self.secs.checked_add(rhs.secs) - .expect("overflow when adding durations"); - let mut nanos = self.nanos + rhs.nanos; - if nanos >= NANOS_PER_SEC { - nanos -= NANOS_PER_SEC; - secs = secs.checked_add(1).expect("overflow when adding durations"); - } - debug_assert!(nanos < NANOS_PER_SEC); - Duration { secs: secs, nanos: nanos } + self.checked_add(rhs).expect("overflow when adding durations") } } @@ -252,17 +244,7 @@ impl Sub for Duration { type Output = Duration; fn sub(self, rhs: Duration) -> Duration { - let mut secs = self.secs.checked_sub(rhs.secs) - .expect("overflow when subtracting durations"); - let nanos = if self.nanos >= rhs.nanos { - self.nanos - rhs.nanos - } else { - secs = secs.checked_sub(1) - .expect("overflow when subtracting durations"); - self.nanos + NANOS_PER_SEC - rhs.nanos - }; - debug_assert!(nanos < NANOS_PER_SEC); - Duration { secs: secs, nanos: nanos } + self.checked_sub(rhs).expect("overflow when subtracting durations") } } @@ -278,15 +260,7 @@ impl Mul for Duration { type Output = Duration; fn mul(self, rhs: u32) -> Duration { - // Multiply nanoseconds as u64, because it cannot overflow that way. - let total_nanos = self.nanos as u64 * rhs as u64; - let extra_secs = total_nanos / (NANOS_PER_SEC as u64); - let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32; - let secs = self.secs.checked_mul(rhs as u64) - .and_then(|s| s.checked_add(extra_secs)) - .expect("overflow when multiplying duration"); - debug_assert!(nanos < NANOS_PER_SEC); - Duration { secs: secs, nanos: nanos } + self.checked_mul(rhs).expect("overflow when multiplying duration by scalar") } } @@ -302,12 +276,7 @@ impl Div for Duration { type Output = Duration; fn div(self, rhs: u32) -> Duration { - let secs = self.secs / (rhs as u64); - let carry = self.secs - secs * (rhs as u64); - let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64); - let nanos = self.nanos / rhs + (extra_nanos as u32); - debug_assert!(nanos < NANOS_PER_SEC); - Duration { secs: secs, nanos: nanos } + self.checked_div(rhs).expect("divide by zero error when dividing duration by scalar") } } From 6353e30bb2d7968332217c04ac25e0800500fb33 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 13 Sep 2016 18:31:26 -0400 Subject: [PATCH 26/29] clear obligations-added flag with nested fulfillcx This flag is a debugging measure designed to detect cases where we start a snapshot, create type variables, register obligations involving those type variables in the fulfillment cx, and then have to unroll the snapshot, leaving "dangling type variables" behind. HOWEVER, in some cases the flag is wrong. In particular, we sometimes create a "mini-fulfilment-cx" in which we enroll obligations. As long as this fulfillment cx is fully drained before we return, this is not a problem, as there won't be any escaping obligations in the main cx. So we add a fn to save/restore the flag. --- src/librustc/infer/mod.rs | 27 +++++++++++++++ src/librustc/traits/specialize/mod.rs | 46 ++++++++++++++------------ src/test/compile-fail/issue-36053-2.rs | 21 ++++++++++++ src/test/run-pass/issue-36053.rs | 32 ++++++++++++++++++ 4 files changed, 104 insertions(+), 22 deletions(-) create mode 100644 src/test/compile-fail/issue-36053-2.rs create mode 100644 src/test/run-pass/issue-36053.rs diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 59431f3f02dce..39fc50666a8ce 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -830,6 +830,33 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { result.map(move |t| InferOk { value: t, obligations: fields.obligations }) } + // Clear the "obligations in snapshot" flag, invoke the closure, + // then restore the flag to its original value. This flag is a + // debugging measure designed to detect cases where we start a + // snapshot, create type variables, register obligations involving + // those type variables in the fulfillment cx, and then have to + // unroll the snapshot, leaving "dangling type variables" behind. + // In such cases, the flag will be set by the fulfillment cx, and + // an assertion will fail when rolling the snapshot back. Very + // useful, much better than grovelling through megabytes of + // RUST_LOG output. + // + // HOWEVER, in some cases the flag is wrong. In particular, we + // sometimes create a "mini-fulfilment-cx" in which we enroll + // obligations. As long as this fulfillment cx is fully drained + // before we return, this is not a problem, as there won't be any + // escaping obligations in the main cx. In those cases, you can + // use this function. + pub fn save_and_restore_obligations_in_snapshot_flag(&self, func: F) -> R + where F: FnOnce(&Self) -> R + { + let flag = self.obligations_in_snapshot.get(); + self.obligations_in_snapshot.set(false); + let result = func(self); + self.obligations_in_snapshot.set(flag); + result + } + fn start_snapshot(&self) -> CombinedSnapshot { debug!("start_snapshot()"); diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index f3ba4d16eb0b2..2f63526bf6c27 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -203,32 +203,34 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, // attempt to prove all of the predicates for impl2 given those for impl1 // (which are packed up in penv) - let mut fulfill_cx = FulfillmentContext::new(); - for oblig in obligations.into_iter() { - fulfill_cx.register_predicate_obligation(&infcx, oblig); - } - match fulfill_cx.select_all_or_error(infcx) { - Err(errors) => { - // no dice! - debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \ - {:?}", - source_trait_ref, - target_trait_ref, - errors, - infcx.parameter_environment.caller_bounds); - Err(()) + infcx.save_and_restore_obligations_in_snapshot_flag(|infcx| { + let mut fulfill_cx = FulfillmentContext::new(); + for oblig in obligations.into_iter() { + fulfill_cx.register_predicate_obligation(&infcx, oblig); } + match fulfill_cx.select_all_or_error(infcx) { + Err(errors) => { + // no dice! + debug!("fulfill_implication: for impls on {:?} and {:?}, \ + could not fulfill: {:?} given {:?}", + source_trait_ref, + target_trait_ref, + errors, + infcx.parameter_environment.caller_bounds); + Err(()) + } - Ok(()) => { - debug!("fulfill_implication: an impl for {:?} specializes {:?}", - source_trait_ref, - target_trait_ref); + Ok(()) => { + debug!("fulfill_implication: an impl for {:?} specializes {:?}", + source_trait_ref, + target_trait_ref); - // Now resolve the *substitution* we built for the target earlier, replacing - // the inference variables inside with whatever we got from fulfillment. - Ok(infcx.resolve_type_vars_if_possible(&target_substs)) + // Now resolve the *substitution* we built for the target earlier, replacing + // the inference variables inside with whatever we got from fulfillment. + Ok(infcx.resolve_type_vars_if_possible(&target_substs)) + } } - } + }) } pub struct SpecializesCache { diff --git a/src/test/compile-fail/issue-36053-2.rs b/src/test/compile-fail/issue-36053-2.rs new file mode 100644 index 0000000000000..7da529487aa86 --- /dev/null +++ b/src/test/compile-fail/issue-36053-2.rs @@ -0,0 +1,21 @@ +// Copyright 2016 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. + +// Regression test for #36053. ICE was caused due to obligations +// being added to a special, dedicated fulfillment cx during +// a probe. + +use std::iter::once; +fn main() { + once::<&str>("str").fuse().filter(|a: &str| true).count(); + //~^ ERROR no method named `count` + //~| ERROR E0281 + //~| ERROR E0281 +} diff --git a/src/test/run-pass/issue-36053.rs b/src/test/run-pass/issue-36053.rs new file mode 100644 index 0000000000000..2411996cf054b --- /dev/null +++ b/src/test/run-pass/issue-36053.rs @@ -0,0 +1,32 @@ +// Copyright 2016 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. + +// Regression test for #36053. ICE was caused due to obligations being +// added to a special, dedicated fulfillment cx during a +// probe. Problem seems to be related to the particular definition of +// `FusedIterator` in std but I was not able to isolate that into an +// external crate. + +#![feature(fused)] +use std::iter::FusedIterator; + +struct Thing<'a>(&'a str); +impl<'a> Iterator for Thing<'a> { + type Item = &'a str; + fn next(&mut self) -> Option<&'a str> { + None + } +} + +impl<'a> FusedIterator for Thing<'a> {} + +fn main() { + Thing("test").fuse().filter(|_| true).count(); +} From a4ee9c6e96025fa2b5eb254e4ccdd4c6910f5f60 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 13 Sep 2016 20:51:39 +0200 Subject: [PATCH 27/29] core: Use primitive indexing in slice's Index/IndexMut [T]'s Index implementation is normally not used for indexing, instead the compiler supplied indexing is used. Use the compiler supplied version in Index/IndexMut. This removes an inconsistency: Compiler supplied bound check failures look like this: thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 4' If you convince Rust to use the Index impl for slices, bounds check failure looks like this instead: thread 'main' panicked at 'assertion failed: index < self.len()' The latter is used if you for example use Index generically:: use std::ops::Index; fn foo(x: &T) where T: Index { &x[4]; } foo(&[1, 2, 3][..]) --- src/libcore/slice.rs | 8 ++++---- src/test/run-fail/bounds-check-no-overflow.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index b22bdb43414fd..9755623365961 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -520,8 +520,8 @@ impl ops::Index for [T] { type Output = T; fn index(&self, index: usize) -> &T { - assert!(index < self.len()); - unsafe { self.get_unchecked(index) } + // NB built-in indexing + &(*self)[index] } } @@ -530,8 +530,8 @@ impl ops::Index for [T] { impl ops::IndexMut for [T] { #[inline] fn index_mut(&mut self, index: usize) -> &mut T { - assert!(index < self.len()); - unsafe { self.get_unchecked_mut(index) } + // NB built-in indexing + &mut (*self)[index] } } diff --git a/src/test/run-fail/bounds-check-no-overflow.rs b/src/test/run-fail/bounds-check-no-overflow.rs index 4d502cb2106b1..3d1cbb446e848 100644 --- a/src/test/run-fail/bounds-check-no-overflow.rs +++ b/src/test/run-fail/bounds-check-no-overflow.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:assertion failed: index < self.len() +// error-pattern:index out of bounds use std::usize; use std::mem::size_of; From f2eb4f11d0c1e5667678def88235309bd083e7fe Mon Sep 17 00:00:00 2001 From: Eugene Bulkin Date: Wed, 14 Sep 2016 15:41:19 -0700 Subject: [PATCH 28/29] Fix doc-tests for Duration --- src/libstd/time/duration.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index a3493f0593c8c..6931c8d631527 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -106,8 +106,10 @@ impl Duration { /// Basic usage: /// /// ``` + /// use std::time::Duration; + /// /// assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)), Some(Duration::new(0, 1))); - /// assert_eq!(Duration::new(1, 0).checked_add(Duration::new(::u64::MAX, 0)), None); + /// assert_eq!(Duration::new(1, 0).checked_add(Duration::new(std::u64::MAX, 0)), None); /// ``` #[unstable(feature = "duration_checked_ops", issue = "35774")] #[inline] @@ -140,6 +142,8 @@ impl Duration { /// Basic usage: /// /// ``` + /// use std::time::Duration; + /// /// assert_eq!(Duration::new(0, 1).checked_sub(Duration::new(0, 0)), Some(Duration::new(0, 1))); /// assert_eq!(Duration::new(0, 0).checked_sub(Duration::new(0, 1)), None); /// ``` @@ -172,8 +176,10 @@ impl Duration { /// Basic usage: /// /// ``` + /// use std::time::Duration; + /// /// assert_eq!(Duration::new(0, 500_000_001).checked_mul(2), Some(Duration::new(1, 2))); - /// assert_eq!(Duration::new(::u64::MAX - 1, 0).checked_mul(2), None); + /// assert_eq!(Duration::new(std::u64::MAX - 1, 0).checked_mul(2), None); /// ``` #[unstable(feature = "duration_checked_ops", issue = "35774")] #[inline] @@ -203,6 +209,8 @@ impl Duration { /// Basic usage: /// /// ``` + /// use std::time::Duration; + /// /// assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0))); /// assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000))); /// assert_eq!(Duration::new(2, 0).checked_div(0), None); From b6321bd13362d69dc0bc4a1ca8416d58b0ff63d2 Mon Sep 17 00:00:00 2001 From: Eugene Bulkin Date: Wed, 14 Sep 2016 17:13:06 -0700 Subject: [PATCH 29/29] Add feature crate attribute for duration_checked_ops to docs --- src/libstd/time/duration.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 6931c8d631527..246c57ab23871 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -106,6 +106,8 @@ impl Duration { /// Basic usage: /// /// ``` + /// #![feature(duration_checked_ops)] + /// /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)), Some(Duration::new(0, 1))); @@ -142,6 +144,8 @@ impl Duration { /// Basic usage: /// /// ``` + /// #![feature(duration_checked_ops)] + /// /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 1).checked_sub(Duration::new(0, 0)), Some(Duration::new(0, 1))); @@ -176,6 +180,8 @@ impl Duration { /// Basic usage: /// /// ``` + /// #![feature(duration_checked_ops)] + /// /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 500_000_001).checked_mul(2), Some(Duration::new(1, 2))); @@ -209,6 +215,8 @@ impl Duration { /// Basic usage: /// /// ``` + /// #![feature(duration_checked_ops)] + /// /// use std::time::Duration; /// /// assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0)));