Skip to content

Regression in macro hygiene for recursive macros #29746

Closed
@ExpHP

Description

@ExpHP

Possible regression in nightly from stable 1.4 with respect to the use of macro hygiene to recursively construct patterns and expressions.

Code:

// zip!(a1,a2,a3,a4) is equivalent to:
//  a1.zip(a2).zip(a3).zip(a4).map(|(((x1,x2),x3),x4)| (x1,x2,x3,x4))
macro_rules! zip {
    // Entry point
    ([$a:expr, $b:expr, $($rest:expr),*]) => {
        zip!([$($rest),*], $a.zip($b), (x,y), [x,y])
    };

    // Intermediate steps to build the zipped expression, the match pattern, and
    //  and the output tuple of the closure, using macro hygene to repeatedly
    //  introduce new variables named 'x'.
    ([$a:expr, $($rest:expr),*], $zip:expr, $pat:pat, [$($flat:expr),*]) => {
        zip!([$($rest),*], $zip.zip($a), ($pat,x), [$($flat),*, x])
    };

    // Final step
    ([], $zip:expr, $pat:pat, [$($flat:expr),+]) => {
        $zip.map(|$pat| ($($flat),+))
    };

    // Comma
    ([$a:expr], $zip:expr, $pat:pat, [$($flat:expr),*]) => {
        zip!([$a,], $zip, $pat, [$($flat),*])
    };
}

fn main() {
    let p1 = vec![1i32,    2].into_iter();
    let p2 = vec!["10",    "20"].into_iter();
    let p3 = vec![100u16,  200].into_iter();
    let p4 = vec![1000i64, 2000].into_iter();

    let e = zip!([p1,p2,p3,p4]).collect::<Vec<_>>();
    assert_eq!(e[0], (1i32,"10",100u16,1000i64));
}

On stable:

Successful build and run

On nightly:

<anon>:14:42: 14:43 error: mismatched types:
 expected `i32`,
    found `u16`
(expected i32,
    found u16) [E0308]
<anon>:14       zip!([$($rest),*], $zip.zip($a), ($pat,x), [$($flat),*, x])
                                                       ^
<anon>:14:3: 14:62 note: in this expansion of zip! (defined in <anon>)
<anon>:24:3: 24:40 note: in this expansion of zip! (defined in <anon>)
<anon>:14:3: 14:62 note: in this expansion of zip! (defined in <anon>)
<anon>:7:3: 7:47 note: in this expansion of zip! (defined in <anon>)
<anon>:34:10: 34:29 note: in this expansion of zip! (defined in <anon>)
<anon>:14:42: 14:43 help: see the detailed explanation for E0308
<anon>:14:42: 14:43 error: mismatched types:
 expected `i64`,
    found `i32`
(expected i64,
    found i32) [E0308]
<anon>:14       zip!([$($rest),*], $zip.zip($a), ($pat,x), [$($flat),*, x])
                                                   ^
<anon>:14:3: 14:62 note: in this expansion of zip! (defined in <anon>)
<anon>:24:3: 24:40 note: in this expansion of zip! (defined in <anon>)
<anon>:14:3: 14:62 note: in this expansion of zip! (defined in <anon>)
<anon>:7:3: 7:47 note: in this expansion of zip! (defined in <anon>)
<anon>:34:10: 34:29 note: in this expansion of zip! (defined in <anon>)
<anon>:14:42: 14:43 help: see the detailed explanation for E0308
<std macros>:5:22: 5:33 error: mismatched types:
 expected `(i32, &str, u16, i32)`,
    found `(i32, &str, u16, i64)`
(expected i32,
    found i64) [E0308]
<std macros>:5 if ! ( * left_val == * right_val ) {
                                    ^~~~~~~~~~~
<anon>:35:2: 35:47 note: in this expansion of assert_eq! (defined in <std macros>)
<std macros>:5:22: 5:33 help: see the detailed explanation for E0308
error: aborting due to 3 previous errors
playpen: application terminated with error code 101

Note: When all vecs in the example are the same type, the error does not occur.

A simpler (but far more contrived) example can be found here: http://is.gd/EHUV1z

A git bisect identified this commit in PR#28642.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions