-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Accessing private fields from the def site of a proc macro #46635
Comments
@alexcrichton Since it's using |
This is likely related to the span that's attached to the field as that affects hygiene/resolution. I believe @dtolnay has been working with that more recently than I and may be able to help you out as well |
Yes this is a span difference between extern crate proc_macro2;
use proc_macro2::Span;
let span = Span::call_site();
let my_field = quote_spanned! {span=>
my_field: 0
};
let result = quote! {
#path { #my_field }
}; |
@jseyfried this is pretty unfortunate. Is it reasonable to make everything accessible in terms of visibility to the call site also accessible to the def site? Not bringing things in scope, but just some way to relax the visibility logic in this case. struct MyStruct {
my_field: i32,
}
fn main() {
// Expands to quote!($input { my_field: 0 }).
// Currently requires `my_field: 0` to be spanned call site.
// Should work without that.
pp!(MyStruct);
} |
Sorry for the delay getting to this. It's only supposed to require I believe the current behavior is hygienic. That is, if Otherwise, whether or not Do you (plural) think we could instead make it easier to do things at the call site? #[proc_macro]
#[uses(my_field)] // `quote!` will now expand `my_field` at the call site
fn example(_: TokenStream) -> TokenStream {
quote! { #path { my_field: 0 } } // this just works now
} You would be able to opt out of hygiene entirely with That being said, if this case (successful name resolution but privacy violation) becomes a pain point even with better hygiene opt-out ergonomics, I suppose I wouldn't be too opposed to relaxing the rules. |
I would prefer to relax the rules even if it means the same macro invocation resolves differently depending on whether the struct fields are visible to the call site. In the case of braced struct initializer expressions there isn't a risk of resolving to "the wrong" struct field with a particular name, so making it more likely to just do the right thing seems like the right tradeoff. // `pp!` expands to `quote!($input { my_field: 0 })` without a `#[uses(my_field)]`
mod m {
pub struct MyStruct {
my_field: i32,
}
const INNER: MyStruct = pp!(MyStruct); // ok, field is visible
}
const OUTER: m::MyStruct = pp!(m::MyStruct); // fails to compile |
I would expect that the macro def would have to do some hygiene manipulation in this case, i.e., it would have to explicitly take the hygiene context from the supplied identifier (the struct name) and apply it to the generated identifier (the field name) |
What do you see as the advantages of requiring the macro to perform the hygiene manipulation? |
For declarative macros, I think a macro definition accessing a field it can't see is bad for the same reason that a function accessing a field it can't see would be bad -- it requires you to read the macro definitions for all the macros in the containing module to know where the field is getting used. If a declarative macro opts out of this, I see it as part of the macro's API. For procedural macros, I think there's a better argument for relaxing the rules here. I believe the logical conclusion of this would be is to fall back to the resolution at the call site if the resolution of a name at a procedural macro def site fails. This would allow the macro to use names at the call site without opt-out while also allowing the macro to use names at the definition site reliably (regardless of where the macro is invoked) where it wants to. I believe the above together with |
That's interesting. My intuition would be exactly the opposite. In part because inspecting declarative macros just feels easier than inspecting procedural macros (e.g., a regular
So overall I guess there are two cases to distinguish (at least, are there more?):
The latter is what is happening here, right? And, per our conversation the other day, it seems like the same logic would apply to any "scoped" name resolution, e.g. a path like That last case is interesting because of privacy: pub mod some_module:: {
pub macro a() {
::some_module::private_fn();
}
pub macro b($n:ident) {
::some_module::$n();
}
pub macro c($n:path) {
$n();
}
pub macro d($n:expr) {
$n;
}
fn private_fn() { }
}
pub fn main() {
some_module::a!();
some_module::b!(private_fn);
some_module::c!(::some_module::private_fn);
some_module::d!(::some_module::private_fn());
} Presumably we want case In any case, the fact that |
Well, not for declarative macros from external crates, but I see your point. Couldn't you make this same argument to allow functions to access private fields as well though? I guess my argument is that while it would be nice to require a procedural macro to declare how it is unhygienic, this isn't feasible since it has so much power to subvert hygiene, e.g. it can rename a token from the macro input to generate a name that resolves at the call site.
Yeah, the latter is the issue we're discussing. The former isn't an issue unless, for example, the macro creates a struct with a field at the def site, and then generates an access to that field at the call site or expects the field to be in scope / visible at the call site.
Agreed. I think it applies to any resolution where privacy can be an issue; that is, everything but lexical resolutions.
I would prefer instead to fall back to resolving at the call site if the resolution at the def site fails as I proposed in the earlier comment. The rationale is that this still allows you to reliably use items at the macro definition regardless of where the macro is invoked, which is one of the two main purposes of hygiene. If we try both and detect ambiguity, then a resolution at the def site would be ambiguous if something with that name "happened" to be in scope at the call site. This sort of thing is what hygiene is supposed to avoid.
It's not a problem in a procedural macro, since the procedural macro definition is fully resolved and compiled before we start resolving the call site. That is, resolving at the definition site will never be blocked on a resolution at the call site, so there can't be a cycle/deadlock. This could be more of a problem for declarative macros, but I expect we could come up with reasonable shadowing restrictions to make it work if we wanted to. |
Hi.
I'm not sure whether it is a bug or not, that may be an hygiene issue.
But, in the following repository, if you run it with
cargo build
, you get the following error:However, that does not make sense, because the field is declared in the same module:
This only happens when this feature is enabled.
What makes me think this might be an hygiene issue is that I hard-coded the field name here.
So, if it's a bug, please fix this issue.
Thank you.
The text was updated successfully, but these errors were encountered: