Rollup merge of #63867 - petrochenkov:dhelpers, r=matthewjasper

resolve: Block expansion of a derive container until all its derives are resolved

So, it turns out there's one more reason to block expansion of a `#[derive]` container until all the derives inside it are resolved, beside `Copy` (https://github.com/rust-lang/rust/pull/63248).

The set of derive helper attributes registered by derives in the container also has to be known before the derives themselves are expanded, otherwise it may be too late (see https://github.com/rust-lang/rust/pull/63468#issuecomment-524550872 and the `#[stable_hasher]`-related test failures in https://github.com/rust-lang/rust/pull/63468).

So, we stop our attempts to unblock the container earlier, as soon as the `Copy` status is known, and just block until all its derives are resolved.
After all the derives are resolved we immediately go and process their helper attributes in the item, without delaying it until expansion of the individual derives.

Unblocks https://github.com/rust-lang/rust/pull/63468
r? @matthewjasper (as a reviewer of https://github.com/rust-lang/rust/pull/63248)
cc @c410-f3r
This commit is contained in:
Mazdak Farrokhzad 2019-08-29 13:17:52 +02:00 committed by GitHub
commit e4e6b01ca1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 206 additions and 164 deletions

View File

@ -527,9 +527,7 @@ pub fn get_span(&self, index: DefIndex, sess: &Session) -> Span {
attributes.iter().cloned().map(Symbol::intern).collect::<Vec<_>>();
(
trait_name,
SyntaxExtensionKind::Derive(Box::new(ProcMacroDerive {
client, attrs: helper_attrs.clone()
})),
SyntaxExtensionKind::Derive(Box::new(ProcMacroDerive { client })),
helper_attrs,
)
}

View File

@ -13,7 +13,7 @@
use syntax::ast::{self, NodeId, Ident};
use syntax::attr::StabilityLevel;
use syntax::edition::Edition;
use syntax::ext::base::{self, Indeterminate, SpecialDerives};
use syntax::ext::base::{self, InvocationRes, Indeterminate, SpecialDerives};
use syntax::ext::base::{MacroKind, SyntaxExtension};
use syntax::ext::expand::{AstFragment, Invocation, InvocationKind};
use syntax::ext::hygiene::{self, ExpnId, ExpnData, ExpnKind};
@ -142,7 +142,7 @@ fn resolve_imports(&mut self) {
fn resolve_macro_invocation(
&mut self, invoc: &Invocation, eager_expansion_root: ExpnId, force: bool
) -> Result<Option<Lrc<SyntaxExtension>>, Indeterminate> {
) -> Result<InvocationRes, Indeterminate> {
let invoc_id = invoc.expansion_data.id;
let parent_scope = match self.invocation_parent_scopes.get(&invoc_id) {
Some(parent_scope) => *parent_scope,
@ -165,25 +165,24 @@ fn resolve_macro_invocation(
InvocationKind::Derive { ref path, .. } =>
(path, MacroKind::Derive, &[][..], false),
InvocationKind::DeriveContainer { ref derives, .. } => {
// Block expansion of derives in the container until we know whether one of them
// is a built-in `Copy`. Skip the resolution if there's only one derive - either
// it's not a `Copy` and we don't need to do anything, or it's a `Copy` and it
// will automatically knows about itself.
let mut result = Ok(None);
if derives.len() > 1 {
for path in derives {
match self.resolve_macro_path(path, Some(MacroKind::Derive),
&parent_scope, true, force) {
Ok((Some(ref ext), _)) if ext.is_derive_copy => {
self.add_derives(invoc_id, SpecialDerives::COPY);
return Ok(None);
}
Err(Determinacy::Undetermined) => result = Err(Indeterminate),
_ => {}
}
}
// Block expansion of the container until we resolve all derives in it.
// This is required for two reasons:
// - Derive helper attributes are in scope for the item to which the `#[derive]`
// is applied, so they have to be produced by the container's expansion rather
// than by individual derives.
// - Derives in the container need to know whether one of them is a built-in `Copy`.
// FIXME: Try to avoid repeated resolutions for derives here and in expansion.
let mut exts = Vec::new();
for path in derives {
exts.push(match self.resolve_macro_path(
path, Some(MacroKind::Derive), &parent_scope, true, force
) {
Ok((Some(ext), _)) => ext,
Ok(_) | Err(Determinacy::Determined) => self.dummy_ext(MacroKind::Derive),
Err(Determinacy::Undetermined) => return Err(Indeterminate),
})
}
return result;
return Ok(InvocationRes::DeriveContainer(exts));
}
};
@ -203,7 +202,7 @@ fn resolve_macro_invocation(
self.definitions.add_parent_module_of_macro_def(invoc_id, normal_module_def_id);
}
Ok(Some(ext))
Ok(InvocationRes::Single(ext))
}
fn check_unused_macros(&self) {

View File

@ -11,6 +11,7 @@
use crate::symbol::{kw, sym, Ident, Symbol};
use crate::{ThinVec, MACRO_ARGUMENTS};
use crate::tokenstream::{self, TokenStream, TokenTree};
use crate::visit::Visitor;
use errors::{DiagnosticBuilder, DiagnosticId};
use smallvec::{smallvec, SmallVec};
@ -72,6 +73,17 @@ pub fn span(&self) -> Span {
}
}
pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) {
match self {
Annotatable::Item(item) => visitor.visit_item(item),
Annotatable::TraitItem(trait_item) => visitor.visit_trait_item(trait_item),
Annotatable::ImplItem(impl_item) => visitor.visit_impl_item(impl_item),
Annotatable::ForeignItem(foreign_item) => visitor.visit_foreign_item(foreign_item),
Annotatable::Stmt(stmt) => visitor.visit_stmt(stmt),
Annotatable::Expr(expr) => visitor.visit_expr(expr),
}
}
pub fn expect_item(self) -> P<ast::Item> {
match self {
Annotatable::Item(i) => i,
@ -700,6 +712,12 @@ pub fn expn_data(&self, parent: ExpnId, call_site: Span, descr: Symbol) -> ExpnD
pub type NamedSyntaxExtension = (Name, SyntaxExtension);
/// Result of resolving a macro invocation.
pub enum InvocationRes {
Single(Lrc<SyntaxExtension>),
DeriveContainer(Vec<Lrc<SyntaxExtension>>),
}
/// Error type that denotes indeterminacy.
pub struct Indeterminate;
@ -727,7 +745,7 @@ fn visit_ast_fragment_with_placeholders(&mut self, expn_id: ExpnId, fragment: &A
fn resolve_macro_invocation(
&mut self, invoc: &Invocation, eager_expansion_root: ExpnId, force: bool
) -> Result<Option<Lrc<SyntaxExtension>>, Indeterminate>;
) -> Result<InvocationRes, Indeterminate>;
fn check_unused_macros(&self);

View File

@ -4,7 +4,7 @@
use crate::source_map::respan;
use crate::config::StripUnconfigured;
use crate::ext::base::*;
use crate::ext::proc_macro::collect_derives;
use crate::ext::proc_macro::{collect_derives, MarkAttrs};
use crate::ext::hygiene::{ExpnId, SyntaxContext, ExpnData, ExpnKind};
use crate::ext::tt::macro_rules::annotate_err_with_kind;
use crate::ext::placeholders::{placeholder, PlaceholderExpander};
@ -307,10 +307,10 @@ pub fn fully_expand_fragment(&mut self, input_fragment: AstFragment) -> AstFragm
let eager_expansion_root =
if self.monotonic { invoc.expansion_data.id } else { orig_expansion_data.id };
let ext = match self.cx.resolver.resolve_macro_invocation(
let res = match self.cx.resolver.resolve_macro_invocation(
&invoc, eager_expansion_root, force
) {
Ok(ext) => ext,
Ok(res) => res,
Err(Indeterminate) => {
undetermined_invocations.push(invoc);
continue
@ -322,54 +322,72 @@ pub fn fully_expand_fragment(&mut self, input_fragment: AstFragment) -> AstFragm
self.cx.current_expansion = invoc.expansion_data.clone();
// FIXME(jseyfried): Refactor out the following logic
let (expanded_fragment, new_invocations) = if let Some(ext) = ext {
let fragment = self.expand_invoc(invoc, &ext.kind);
self.collect_invocations(fragment, &[])
} else if let InvocationKind::DeriveContainer { derives: traits, item } = invoc.kind {
if !item.derive_allowed() {
let attr = attr::find_by_name(item.attrs(), sym::derive)
.expect("`derive` attribute should exist");
let span = attr.span;
let mut err = self.cx.mut_span_err(span,
"`derive` may only be applied to \
structs, enums and unions");
if let ast::AttrStyle::Inner = attr.style {
let trait_list = traits.iter()
.map(|t| t.to_string()).collect::<Vec<_>>();
let suggestion = format!("#[derive({})]", trait_list.join(", "));
err.span_suggestion(
span, "try an outer attribute", suggestion,
// We don't 𝑘𝑛𝑜𝑤 that the following item is an ADT
Applicability::MaybeIncorrect
);
let (expanded_fragment, new_invocations) = match res {
InvocationRes::Single(ext) => {
let fragment = self.expand_invoc(invoc, &ext.kind);
self.collect_invocations(fragment, &[])
}
InvocationRes::DeriveContainer(exts) => {
let (derives, item) = match invoc.kind {
InvocationKind::DeriveContainer { derives, item } => (derives, item),
_ => unreachable!(),
};
if !item.derive_allowed() {
let attr = attr::find_by_name(item.attrs(), sym::derive)
.expect("`derive` attribute should exist");
let span = attr.span;
let mut err = self.cx.mut_span_err(span,
"`derive` may only be applied to structs, enums and unions");
if let ast::AttrStyle::Inner = attr.style {
let trait_list = derives.iter()
.map(|t| t.to_string()).collect::<Vec<_>>();
let suggestion = format!("#[derive({})]", trait_list.join(", "));
err.span_suggestion(
span, "try an outer attribute", suggestion,
// We don't 𝑘𝑛𝑜𝑤 that the following item is an ADT
Applicability::MaybeIncorrect
);
}
err.emit();
}
err.emit();
}
let mut item = self.fully_configure(item);
item.visit_attrs(|attrs| attrs.retain(|a| a.path != sym::derive));
let derive_placeholders =
all_derive_placeholders.entry(invoc.expansion_data.id).or_default();
let mut item = self.fully_configure(item);
item.visit_attrs(|attrs| attrs.retain(|a| a.path != sym::derive));
let mut helper_attrs = Vec::new();
let mut has_copy = false;
for ext in exts {
helper_attrs.extend(&ext.helper_attrs);
has_copy |= ext.is_derive_copy;
}
// Mark derive helpers inside this item as known and used.
// FIXME: This is a hack, derive helpers should be integrated with regular name
// resolution instead. For example, helpers introduced by a derive container
// can be in scope for all code produced by that container's expansion.
item.visit_with(&mut MarkAttrs(&helper_attrs));
if has_copy {
self.cx.resolver.add_derives(invoc.expansion_data.id, SpecialDerives::COPY);
}
derive_placeholders.reserve(traits.len());
invocations.reserve(traits.len());
for path in traits {
let expn_id = ExpnId::fresh(None);
derive_placeholders.push(NodeId::placeholder_from_expn_id(expn_id));
invocations.push(Invocation {
kind: InvocationKind::Derive { path, item: item.clone() },
fragment_kind: invoc.fragment_kind,
expansion_data: ExpansionData {
id: expn_id,
..invoc.expansion_data.clone()
},
});
let derive_placeholders =
all_derive_placeholders.entry(invoc.expansion_data.id).or_default();
derive_placeholders.reserve(derives.len());
invocations.reserve(derives.len());
for path in derives {
let expn_id = ExpnId::fresh(None);
derive_placeholders.push(NodeId::placeholder_from_expn_id(expn_id));
invocations.push(Invocation {
kind: InvocationKind::Derive { path, item: item.clone() },
fragment_kind: invoc.fragment_kind,
expansion_data: ExpansionData {
id: expn_id,
..invoc.expansion_data.clone()
},
});
}
let fragment = invoc.fragment_kind
.expect_from_annotatables(::std::iter::once(item));
self.collect_invocations(fragment, derive_placeholders)
}
let fragment = invoc.fragment_kind
.expect_from_annotatables(::std::iter::once(item));
self.collect_invocations(fragment, derive_placeholders)
} else {
unreachable!()
};
if expanded_fragments.len() < depth {

View File

@ -78,7 +78,6 @@ pub struct ProcMacroDerive {
pub client: proc_macro::bridge::client::Client<
fn(proc_macro::TokenStream) -> proc_macro::TokenStream,
>,
pub attrs: Vec<ast::Name>,
}
impl MultiItemModifier for ProcMacroDerive {
@ -111,9 +110,6 @@ fn expand(&self,
}
}
// Mark attributes as known, and used.
MarkAttrs(&self.attrs).visit_item(&item);
let token = token::Interpolated(Lrc::new(token::NtItem(item)));
let input = tokenstream::TokenTree::token(token, DUMMY_SP).into();
@ -164,7 +160,7 @@ fn expand(&self,
}
}
struct MarkAttrs<'a>(&'a [ast::Name]);
crate struct MarkAttrs<'a>(crate &'a [ast::Name]);
impl<'a> Visitor<'a> for MarkAttrs<'a> {
fn visit_attribute(&mut self, attr: &Attribute) {

View File

@ -1,15 +1,3 @@
error: cannot find derive macro `Send` in this scope
--> $DIR/deriving-bounds.rs:1:10
|
LL | #[derive(Send)]
| ^^^^
|
note: unsafe traits like `Send` should be implemented explicitly
--> $DIR/deriving-bounds.rs:1:10
|
LL | #[derive(Send)]
| ^^^^
error: cannot find derive macro `Sync` in this scope
--> $DIR/deriving-bounds.rs:5:10
|
@ -22,5 +10,17 @@ note: unsafe traits like `Sync` should be implemented explicitly
LL | #[derive(Sync)]
| ^^^^
error: cannot find derive macro `Send` in this scope
--> $DIR/deriving-bounds.rs:1:10
|
LL | #[derive(Send)]
| ^^^^
|
note: unsafe traits like `Send` should be implemented explicitly
--> $DIR/deriving-bounds.rs:1:10
|
LL | #[derive(Send)]
| ^^^^
error: aborting due to 2 previous errors

View File

@ -1,5 +1,5 @@
error: cannot find derive macro `x3300` in this scope
--> $DIR/issue-43106-gating-of-derive-2.rs:4:14
--> $DIR/issue-43106-gating-of-derive-2.rs:12:14
|
LL | #[derive(x3300)]
| ^^^^^
@ -11,7 +11,7 @@ LL | #[derive(x3300)]
| ^^^^^
error: cannot find derive macro `x3300` in this scope
--> $DIR/issue-43106-gating-of-derive-2.rs:12:14
--> $DIR/issue-43106-gating-of-derive-2.rs:4:14
|
LL | #[derive(x3300)]
| ^^^^^

View File

@ -1,9 +1,6 @@
// `#![derive]` raises errors when it occurs at contexts other than ADT
// definitions.
#![derive(Debug)]
//~^ ERROR `derive` may only be applied to structs, enums and unions
#[derive(Debug)]
//~^ ERROR `derive` may only be applied to structs, enums and unions
mod derive {

View File

@ -1,38 +1,32 @@
error: `derive` may only be applied to structs, enums and unions
--> $DIR/issue-43106-gating-of-derive.rs:4:1
|
LL | #![derive(Debug)]
| ^^^^^^^^^^^^^^^^^ help: try an outer attribute: `#[derive(Debug)]`
error: `derive` may only be applied to structs, enums and unions
--> $DIR/issue-43106-gating-of-derive.rs:7:1
|
LL | #[derive(Debug)]
| ^^^^^^^^^^^^^^^^
error: `derive` may only be applied to structs, enums and unions
--> $DIR/issue-43106-gating-of-derive.rs:10:17
--> $DIR/issue-43106-gating-of-derive.rs:7:17
|
LL | mod inner { #![derive(Debug)] }
| ^^^^^^^^^^^^^^^^^ help: try an outer attribute: `#[derive(Debug)]`
error: `derive` may only be applied to structs, enums and unions
--> $DIR/issue-43106-gating-of-derive.rs:13:5
--> $DIR/issue-43106-gating-of-derive.rs:10:5
|
LL | #[derive(Debug)]
| ^^^^^^^^^^^^^^^^
error: `derive` may only be applied to structs, enums and unions
--> $DIR/issue-43106-gating-of-derive.rs:26:5
--> $DIR/issue-43106-gating-of-derive.rs:23:5
|
LL | #[derive(Debug)]
| ^^^^^^^^^^^^^^^^
error: `derive` may only be applied to structs, enums and unions
--> $DIR/issue-43106-gating-of-derive.rs:30:5
--> $DIR/issue-43106-gating-of-derive.rs:27:5
|
LL | #[derive(Debug)]
| ^^^^^^^^^^^^^^^^
error: aborting due to 6 previous errors
error: aborting due to 5 previous errors

View File

@ -1,3 +1,4 @@
#![derive(Copy)] //~ ERROR `derive` may only be applied to structs, enums and unions
//~| ERROR cannot determine resolution for the derive macro `Copy`
fn main() {}

View File

@ -4,5 +4,13 @@ error: `derive` may only be applied to structs, enums and unions
LL | #![derive(Copy)]
| ^^^^^^^^^^^^^^^^ help: try an outer attribute: `#[derive(Copy)]`
error: aborting due to previous error
error: cannot determine resolution for the derive macro `Copy`
--> $DIR/issue-36617.rs:1:11
|
LL | #![derive(Copy)]
| ^^^^
|
= note: import resolution is stuck, try simplifying macro imports
error: aborting due to 2 previous errors

View File

@ -0,0 +1,18 @@
// Derive helpers are resolved successfully inside `cfg_attr`.
// check-pass
// compile-flats:--cfg TRUE
// aux-build:test-macros.rs
#[macro_use]
extern crate test_macros;
#[cfg_attr(TRUE, empty_helper)]
#[derive(Empty)]
#[cfg_attr(TRUE, empty_helper)]
struct S {
#[cfg_attr(TRUE, empty_helper)]
field: u8,
}
fn main() {}

View File

@ -19,7 +19,8 @@ struct S {
struct U;
mod inner {
#[empty_helper] //~ ERROR cannot find attribute macro `empty_helper` in this scope
// FIXME No ambiguity, attributes in non-macro positions are not resolved properly
#[empty_helper]
struct V;
}

View File

@ -1,9 +1,3 @@
error: cannot find attribute macro `empty_helper` in this scope
--> $DIR/derive-helper-shadowing.rs:22:15
|
LL | #[empty_helper]
| ^^^^^^^^^^^^
error[E0659]: `empty_helper` is ambiguous (derive helper attribute vs any other name)
--> $DIR/derive-helper-shadowing.rs:8:3
|
@ -22,6 +16,6 @@ LL | use test_macros::empty_attr as empty_helper;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: use `crate::empty_helper` to refer to this attribute macro unambiguously
error: aborting due to 2 previous errors
error: aborting due to previous error
For more information about this error, try `rustc --explain E0659`.

View File

@ -1,50 +1,8 @@
error: cannot find derive macro `FooWithLongNan` in this scope
--> $DIR/resolve-error.rs:22:10
error: cannot find macro `bang_proc_macrp!` in this scope
--> $DIR/resolve-error.rs:56:5
|
LL | #[derive(FooWithLongNan)]
| ^^^^^^^^^^^^^^ help: a derive macro with a similar name exists: `FooWithLongName`
error: cannot find attribute macro `attr_proc_macra` in this scope
--> $DIR/resolve-error.rs:27:3
|
LL | #[attr_proc_macra]
| ^^^^^^^^^^^^^^^ help: an attribute macro with a similar name exists: `attr_proc_macro`
error: cannot find attribute macro `FooWithLongNan` in this scope
--> $DIR/resolve-error.rs:31:3
|
LL | #[FooWithLongNan]
| ^^^^^^^^^^^^^^
error: cannot find derive macro `Dlone` in this scope
--> $DIR/resolve-error.rs:34:10
|
LL | #[derive(Dlone)]
| ^^^^^ help: a derive macro with a similar name exists: `Clone`
error: cannot find derive macro `Dlona` in this scope
--> $DIR/resolve-error.rs:38:10
|
LL | #[derive(Dlona)]
| ^^^^^ help: a derive macro with a similar name exists: `Clona`
error: cannot find derive macro `attr_proc_macra` in this scope
--> $DIR/resolve-error.rs:42:10
|
LL | #[derive(attr_proc_macra)]
| ^^^^^^^^^^^^^^^
error: cannot find macro `FooWithLongNama!` in this scope
--> $DIR/resolve-error.rs:47:5
|
LL | FooWithLongNama!();
| ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `FooWithLongNam`
error: cannot find macro `attr_proc_macra!` in this scope
--> $DIR/resolve-error.rs:50:5
|
LL | attr_proc_macra!();
| ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `attr_proc_mac`
LL | bang_proc_macrp!();
| ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `bang_proc_macro`
error: cannot find macro `Dlona!` in this scope
--> $DIR/resolve-error.rs:53:5
@ -52,11 +10,53 @@ error: cannot find macro `Dlona!` in this scope
LL | Dlona!();
| ^^^^^
error: cannot find macro `bang_proc_macrp!` in this scope
--> $DIR/resolve-error.rs:56:5
error: cannot find macro `attr_proc_macra!` in this scope
--> $DIR/resolve-error.rs:50:5
|
LL | bang_proc_macrp!();
| ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `bang_proc_macro`
LL | attr_proc_macra!();
| ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `attr_proc_mac`
error: cannot find macro `FooWithLongNama!` in this scope
--> $DIR/resolve-error.rs:47:5
|
LL | FooWithLongNama!();
| ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `FooWithLongNam`
error: cannot find derive macro `attr_proc_macra` in this scope
--> $DIR/resolve-error.rs:42:10
|
LL | #[derive(attr_proc_macra)]
| ^^^^^^^^^^^^^^^
error: cannot find derive macro `Dlona` in this scope
--> $DIR/resolve-error.rs:38:10
|
LL | #[derive(Dlona)]
| ^^^^^ help: a derive macro with a similar name exists: `Clona`
error: cannot find derive macro `Dlone` in this scope
--> $DIR/resolve-error.rs:34:10
|
LL | #[derive(Dlone)]
| ^^^^^ help: a derive macro with a similar name exists: `Clone`
error: cannot find attribute macro `FooWithLongNan` in this scope
--> $DIR/resolve-error.rs:31:3
|
LL | #[FooWithLongNan]
| ^^^^^^^^^^^^^^
error: cannot find attribute macro `attr_proc_macra` in this scope
--> $DIR/resolve-error.rs:27:3
|
LL | #[attr_proc_macra]
| ^^^^^^^^^^^^^^^ help: an attribute macro with a similar name exists: `attr_proc_macro`
error: cannot find derive macro `FooWithLongNan` in this scope
--> $DIR/resolve-error.rs:22:10
|
LL | #[derive(FooWithLongNan)]
| ^^^^^^^^^^^^^^ help: a derive macro with a similar name exists: `FooWithLongName`
error: aborting due to 10 previous errors