Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`if constexpr`, inside lambda, inside pack expansion -- compiler bug?

clang version 5.0.0 (trunk 305664)
Target: x86_64-unknown-linux-gnu

The following code compiles successfully:

template <int... A>
void f() {
    ([](auto) {
        if constexpr (A == 0)
            return 42;
        else
            return 3.14;
    }(0), ...);
}

int main() {
    f<0, 1>();
}

... but this one doesn't:

template <int... A>
void f() {
    ([](auto...) {            // Variadic lambda
        if constexpr (A == 0)
            return 42;
        else
            return 3.14;
    }(), ...);                // No argument
}

int main() {
    f<0, 1>();
}

... yielding:

<source>:7:13: error: 'auto' in return type deduced as 'double' here but deduced as 'int' in earlier return statement
            return 3.14;
            ^
<source>:3:6: note: in instantiation of function template specialization 'f()::(anonymous class)::operator()<>' requested here
    ([](auto...) {            // Variadic lambda
     ^
<source>:12:5: note: in instantiation of function template specialization 'f<0, 1>' requested here
    f<0, 1>();
    ^

I wouldn't expect different behaviours between an empty argument pack and a dummy argument.

Is there a reason to this discrepancy, or is this a compiler bug?

like image 740
Quentin Avatar asked Jun 19 '17 23:06

Quentin


1 Answers

I believe this is a clang bug.

The rules in [dcl.spec.auto] are, emphasis mine:

If the declared return type of the function contains a placeholder type, the return type of the function is deduced from non-discarded return statements, if any, in the body of the function ([stmt.if]).

[...]

If a function with a declared return type that contains a placeholder type has multiple non-discarded return statements, the return type is deduced for each such return statement. If the type deduced is not the same in each deduction, the program is ill-formed.

One or the other return statement in the lambda is discarded (the non-taken branch in if constexpr is called a discarded statement), which leaves only one non-discarded return statement, so the return type of the lambda should be simply deduced from that one left over.

Moreover, clang is okay with just this:

template <int A>
void f() {
    [](auto...) {
        if constexpr (A == 0)
            return 42;
        else
            return 3.14;
    }();
}

int main() {  
    f<0>();
    f<1>();
}

So it's probably some bad interaction with how lambdas work in pack expressions.

like image 154
Barry Avatar answered Nov 14 '22 01:11

Barry