Hello
I have this code: https://gitlab.labs.nic.cz/turris/pakon-aggregator (unfortunately, I don't have a minimal code example yet). It fails with rustc 1.20.0-nightly (067971139 2017-07-02) (for some reason this is the version I get if I run rustup default nightly-2017-07-03, it seems to be one day off) and newer, but passes with older versions of nighly as well as stable and beta.
The error I get is:
error: unexpected end of macro invocation
--> src/keeper/column.rs:269:1
|
269 | / column! {
270 | | /// Identifier of an address inside an endpoint.
271 | | ///
272 | | /// This is usually used to specify aggregation by a value in queries.
... |
357 | | name Port u16;
358 | | }
| |_^ in this macro invocation
|
= note: this error originates in a macro outside of the current crate
This is actually my own macro, so the „originates in a macro outside of the current crate“ part is wrong. Also, renaming the macro makes it compile again. This leads me to think something (the compiler) brought in a colliding macro with the same name.
Is my theory about the colliding macro true? Can this killing of my poor innocent macro be prevented somehow? Should local macros have precedence? Or, at least, should I get a better error message, like complaining when I define the macro, not when I use it? This makes me think that if I had the bad luck of having a same-name macro with the same invocation syntax, it would silently expand the wrong one.
$ rustc --version --verbose
rustc 1.20.0-nightly (067971139 2017-07-02)
binary: rustc
commit-hash: 0679711398bef656699e1ff6b004ecccbdb67284
commit-date: 2017-07-02
host: x86_64-unknown-linux-gnu
release: 1.20.0-nightly
LLVM version: 4.0
Not sure if it is relevant, but there is already a column! macro in the standard library.
Minimized, side effect of #42938:
macro_rules! column { (foo) => { panic!() } }
fn main() {
column!(foo);
}
Or alternatively with a singularly unhelpful error message (column! isn't even used here, just defined):
macro_rules! column { (foo) => {} }
fn main() { panic!(); }
error: unexpected end of macro invocation
--> <anon>:4:5
|
4 | panic!();
| ^^^^^^^^^
|
= note: this error originates in a macro outside of the current crate
Same thing happens with a file or line macro. #42938 just added column to the list. Should macros with those names be illegal, or should we work around them?
panic!() should use core::{file,line,column}!(). IIRC rust already has the qualified macro feature.
So I guess there are three "issues" here:
std which depend on other macros from std being available as defined in std. This is bad in my opinion and should maybe be fixed (one idea would be to to make macros like column! available as _rust_std_column! as well, for use in std. This would at least reduce the likelihood of them being redefined column!).column! inside of panic! which causes the regression here.Edit: Tagging as T-libs since the definition of the panic! macro is their responsibility although I guess compiler/lang may be applicable as well.
@kennytm: Sadly, that doesn't work: file!, line! and column! are compiler-builtin macros that don't exist in any crate.
An empty implementation, however, does exist at the std/core root (presumably for documentation reasons). I don't know much about the compiler internals, but would it be possible to "fill in" that implementation, instead of creating a "new" builtin macro?
I see, so there was the column! macro in the library to start with and I should rename it.
Still, I think the error message could be a bit more helpful.
@vorner Your macro definition shouldn't interfere with the standard library, but yes, that's what's currently happening.
I realized that my proposed "filling in" solution also wouldn't work with #![no_core]. I'm not sure how to approach this.
Since #![no_core] is unstable I think telling the developers to use __rust_std_column__!() instead of column!() is permitted.
There should be a lint which warns about shadowing any of the built-in macros.
(Alternatively, would it be possible to rewrite file!, line!, column! as procedural macros (instead of libsyntax built-in plugins) that respects #35896 (macro modularization)? #![no_core] developers would still need to #[macro_use] extern crate core_macros; to use them.)
Needs to be prioritized. There's probably something we can do better here.
cc @jseyfried -- this is due to an issue with macros 1.0 hygiene, maybe macros 2.0 can be made to prevent situations like this?
@est31 Yeah, macros 2.0 already prevents situations like this.
Example:
// crate A
pub macro n() { println!("Hello world!") }
pub macro m() { n!() }
// crate B
extern crate A;
use A::m;
macro n() {}
macro println() {}
//^ These don't interfere with the expansion of `m!()` below.
fn main() {
m!(); // prints "Hello world!"
}
One way to fix this issue would be to migrate the std macros to macros 2.0. However, this would be a breaking change (e.g. people might be intentionally "overriding" dependencies of the std macros). Also, IIRC I tried something like this a while ago and found some breakage due to overriding in practice.
That being said, it might be worth assessing migrating with a warning cycle or some other partial mitigation.
cc @nrc
@est31 Caveat: while macros 2.0 prevents this situation, it doesn't help when using macros 1.0 from std. More specifically, a macros 2.0 can still "override" a dependency of an unhygienic macros 1.0.
It might be possible to special-case certain situation so that macros 2.0 do not "override" unhygienic macros 1.0 dependencies. For example, we could say:
However, this would add complexity and probably has other downsides (e.g. breaking invariants preserved by macro expansion).
Out of curiosity, could we define unstable macros in the compiler like __column and use those in the panic! macro here? That'd fix the regression at least, right?
Othrewise, is there perhaps another sort of small patch we could apply to avoid the regression here? Beta's going out in just over a month!
I see the nominated label, would be great to have a team talk about this. There needs to be a decision on whether this change is expected breakage or a bug? Or, to formulate the question more generally: is it forbidden for any macro of the standard library to change which macros it calls, or is such change allowed?
Also similar question: is it allowed for a macro in the standard library to become a wrapper of another macro? If it becomes, it adds 1 to the depth and could cause to code (which uses the recursion depth up to the limit, but doesn't attempt to surpass the limit) to be rejected that was previously accepted.
RFC 1105 has no section on macros...
is it forbidden for any macro of the standard library to change which macros it calls, or is such change allowed?
I think it should be OK to move away from unhygienic "dependency overrides" (e.g. with @alexcrichton's idea) if it doesn't cause breakage in practice.
is it allowed for a macro in the standard library to become a wrapper of another macro? If it becomes, it adds 1 to the depth and could cause to code ... to be rejected
I doubt this would be an issue in practice, but if it is we could always add a small offset to the global recursion limit when computing the recursion limit for macros (macro expansion is stackless, so this is OK).
@jseyfried just to confirm, but the "workaround" here would be to add a __column macro to the compiler versus to the standard library, because if the standard library had a __column macro that expanded to column!() that wouldn't work, right?
@alexcrichton Right.
We could instead define a declarative macros 2.0 __column in the standard library that expanded to column!(), and that would work due to hygiene; however, I think it would be cleaner to just add __column to the compiler.
Why is adding __column to the compiler better?
column is already in the compiler, so adding __column would be a single-line change. Also, declarative macros 2.0 is unstable enough that I'm not sure we want to use for stable APIs yet.
@est31 out of curiosity, would you be willing to help implement the workaround here?
@alexcrichton depends on whether such a workaround would actually be helping people. @vorner would it help you? @alexcrichton are there any results of a crater run on the beta available?
I already worked around the problem by renaming the macro (it is a private one to generate some code, so I don't care about its name). It was just the surprise the code broke.
But if you ask if it would have helped me if it was there to begin with, then I think yes ‒ I didn't have a clue what was wrong and only reporting the issue helped shed some light.
Ah unfortunately we don't have a crater run yet to see the impact of this.
Discussed in compiler meeting. Awaiting some sort of crater run before assessing priority.
Libss concluded the same!
I've looked at the root regressions from a crater run between stable and beta (link), and couldn't find any regression introduced by this issue. They would have shown up there because this PR made it into beta but is not in stable yet.
@est31
There's #43672, which is an old version of Diesel.
@arielb1 oh, right. Missed it :)
So dunno then... PR to https://github.com/stefanoc/magnet ? Or should this be fixed in the compiler because it affected diesel?
I think this should be fixed in the standard library by making panic a macro 2.0.
@arielb1 above @jseyfried [mentioned]:
Also, declarative macros 2.0 is unstable enough that I'm not sure we want to use for stable APIs yet.
In that sense, would you be opposed to a compiler-defined __column macro (that's unstable) that libstd calls?
It does indeed seem like we should implement this!
@alexcrichton
I don't care what fix happens, only that it does.
@est31 out of curiosity, would you be willing to help implement this?
@alexcrichton sure!
IMO its not really needed, but you seem to insist...
Most helpful comment
Out of curiosity, could we define unstable macros in the compiler like
__columnand use those in thepanic!macro here? That'd fix the regression at least, right?Othrewise, is there perhaps another sort of small patch we could apply to avoid the regression here? Beta's going out in just over a month!