rust-lang / rust-lang/rust

Unexpected "macro is ambiguous" error when macro imports are generated by a macro

Open
#153,478 7 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-macros A-resolve C-bug T-compiler T-lang
Dominant language
Rust
Stars
119k
Forks
16.2k
PR merge metrics
PR metrics pending

Description

This code compiles just fine, as one would expect:

macro_rules! assert_biteq_rt {
    ($left:expr, $right:expr $(,)?) => {};
}
macro_rules! assert_biteq_const {
    ($left:expr, $right:expr $(,)?) => {};
}

// Use the runtime version by default.
// This way, they can be shadowed by the const versions.
pub(crate) use assert_biteq_rt as assert_biteq;

// Also make the const version available for re-exports.
pub(crate) use assert_biteq_const;

mod something_nomacro {
    use super::*;

    mod const_ {
        use super::*;
        use crate::assert_biteq_const as assert_biteq;
        
        #[test]
        pub fn test() {
            const { assert_biteq!(0, 0); }
        }
    }
}

However, if I write a macro to generate the actual module at the end that contains the test...

macro_rules! float_test {
    (
        name: $name:ident,
        test $test:block
    ) => {
        mod $name {
            use super::*;
            
            mod const_ {
                use super::*;
                use crate::assert_biteq_const as assert_biteq;
                
                #[test]
                fn test() {
                    const { $test }
                }
            }
        }
    }
}

float_test! {
    name: something_macro,
    test {
        assert_biteq!(0, 0);
    }
}

... then it stops compiling:

error[E0659]: `assert_biteq` is ambiguous
  --> src/lib.rs:53:9
   |
53 |         assert_biteq!(0, 0);
   |         ^^^^^^^^^^^^ ambiguous name
   |
   = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution
note: `assert_biteq` could refer to the macro imported here
  --> src/lib.rs:39:21
   |
39 |                   use crate::assert_biteq_const as assert_biteq;
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
50 | / float_test! {
51 | |     name: something_macro,
52 | |     test {
53 | |         assert_biteq!(0, 0);
54 | |     }
55 | | }
   | |_- in this macro invocation
   = help: use `self::assert_biteq` to refer to this macro unambiguously
note: `assert_biteq` could also refer to the macro imported here
  --> src/lib.rs:38:21
   |
38 |                   use super::*;
   |                       ^^^^^^^^
...
50 | / float_test! {
51 | |     name: something_macro,
52 | |     test {
53 | |         assert_biteq!(0, 0);
54 | |     }
55 | | }
   | |_- in this macro invocation
   = help: consider adding an explicit import of `assert_biteq` to disambiguate

The error helpfully tells me to add an explicit import, but I already did that.

This is a minimized version of real code from the libcore test suite. The only way I found so far to avoid this problem is to avoid use super::*; inside mod const_ and instead manually re-import everything we need. That's quite annoying as it means every time we add a new top-level import, we have to remember to also import it inside mod const_ or else the macro-generated tests will start failing.

Cc @petrochenkov in the hopes that you have an idea of what is going on. It is quite puzzling to me that manually expanding the float_test! macro makes the error go away...

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the minimized Rust example in the issue, then compare it with library/coretests/tests/floats/mod.rs. Reproduce the ambiguity and compare manual expansion with float_test! expansion, focusing on the interaction between use super::* and the explicit macro import. Done means the generated tests compile without manually re-importing every top-level name, with a regression test covering the behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.