quote icon indicating copy to clipboard operation
quote copied to clipboard

Unit struct or const cannot be interpolated inside repetition

Open dtolnay opened this issue 3 months ago • 2 comments

use proc_macro2::{Ident, Span, TokenStream};
use quote::{ToTokens, quote};

struct Private;

impl ToTokens for Private {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        Ident::new("private", Span::call_site()).to_tokens(tokens);
    }
}

fn main() {
    let _ = quote! {
        // Okay
        ... #Private ...
    };

    let iter = vec!['0', '1', '2'];
    let _ = quote! {
        // Fail
        #(#Private #iter)*
    };
}
error[E0530]: let bindings cannot shadow unit structs
  --> src/main.rs:21:12
   |
 4 | struct Private;
   | --------------- the unit struct `Private` is defined here
...
21 |         #(#Private #iter)*
   |            ^^^^^^^ cannot be named the same as a unit struct

dtolnay avatar Sep 15 '25 03:09 dtolnay

The non-repetition expands to something like this, which is fine:

{
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_dot3(&mut _s);
    ::quote::ToTokens::to_tokens(&Private, &mut _s);
    ::quote::__private::push_dot3(&mut _s);
    _s
}

The repetition expands to:

{
    let mut _s = ::quote::__private::TokenStream::new();
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::ThereIsNoIteratorInRepetition;
        #[allow(unused_mut)]
        let (mut Private, i) = Private.quote_into_iter();
        let has_iter = has_iter | i;
        #[allow(unused_mut)]
        let (mut iter, i) = iter.quote_into_iter();
        let has_iter = has_iter | i;
        let _: ::quote::__private::HasIterator = has_iter;
        while true {
            let Private = match Private.next() {
                Some(_x) => ::quote::__private::RepInterp(_x),
                None => break,
            };
            let iter = match iter.next() {
                Some(_x) => ::quote::__private::RepInterp(_x),
                None => break,
            };
            ::quote::ToTokens::to_tokens(&Private, &mut _s);
            ::quote::ToTokens::to_tokens(&iter, &mut _s);
        }
    }
    _s
}

where let (mut Private, i) = Private.quote_into_iter() is the source of "let bindings cannot shadow unit structs".

dtolnay avatar Sep 15 '25 03:09 dtolnay

This correct expansion is probably achievable:

{
    let mut _s = ::quote::__private::TokenStream::new();
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::ThereIsNoIteratorInRepetition;
        let (var, i) = Private.quote_into_iter();
        let has_iter = has_iter | i;
        {
            fn Private() {}
            #[allow(unused_mut)]
            let mut Private = var;
            let (var, i) = iter.quote_into_iter();
            let has_iter = has_iter | i;
            {
                fn iter() {}
                #[allow(unused_mut)]
                let mut iter = var;
                let _: ::quote::__private::HasIterator = has_iter;
                while true {
                    let Private = match Private.next() {
                        Some(_x) => ::quote::__private::RepInterp(_x),
                        None => break,
                    };
                    let iter = match iter.next() {
                        Some(_x) => ::quote::__private::RepInterp(_x),
                        None => break,
                    };
                    ::quote::ToTokens::to_tokens(&Private, &mut _s);
                    ::quote::ToTokens::to_tokens(&iter, &mut _s);
                }
            }
        }
    }
    _s
}

dtolnay avatar Sep 15 '25 04:09 dtolnay