Issue |
127086
|
Summary |
Various issues with using uncaptured constexpr variable in lambda
|
Labels |
|
Assignees |
|
Reporter |
davidstone
|
I cannot figure out what causes clang to decide that I odr-used a variable (thus requiring a capture), and failing to capture a variable clang has decided I odr-used causes error messages in unrelated sections of code.
First, clang rejects the following:
```c++
void f() {
constexpr bool b = true;
[] { b; };
}
```
with
```console
<source>:3:7: error: variable 'b' cannot be implicitly captured in a lambda with no capture-default specified
3 | [] { b; };
| ^
<source>:2:17: note: 'b' declared here
2 | constexpr bool b = true;
| ^
<source>:3:2: note: lambda _expression_ begins here
3 | [] { b; };
| ^
<source>:3:3: note: capture 'b' by value
3 | [] { b; };
| ^
| b
<source>:3:3: note: capture 'b' by reference
3 | [] { b; };
| ^
| &b
<source>:3:3: note: default capture by value
3 | [] { b; };
| ^
| =
<source>:3:3: note: default capture by reference
3 | [] { b; };
| ^
| &
<source>:3:7: warning: _expression_ result unused [-Wunused-value]
3 | [] { b; };
| ^
<source>:3:2: warning: _expression_ result unused [-Wunused-value]
3 | [] { b; };
| ^~~~~~~~~
2 warnings and 1 error generated.
Compiler returned: 1
```
https://godbolt.org/z/Mva5ffYoe
but accepts
```c++
void f() {
constexpr bool b = true;
[] { +b; };
}
```
It also rejects
```c++
void f() {
constexpr bool b = true;
[] { static_cast<void>(b); };
}
```
but accepts
```c++
void f() {
constexpr bool b = true;
[] { static_cast<bool>(b); };
}
```
It accepts
```c++
template<typename T>
void f() {
constexpr bool b = T::b;
[] { b; };
}
```
until we try to actually instantiate the function:
```c++
template<typename T>
void f() {
constexpr bool b = T::b;
[] { b; };
}
struct s {
static constexpr bool b = true;
};
void g() {
f<s>();
}
```
even though whether `b` is odr-used is not a dependent property.
When `b` is used as part of an _expression_ that instantiates a template inside of a template, the error message is wrong (and has gotten more wrong with clang trunk). First, when using a regular function, clang trunk and clang 19.1.0 agree on the wrong error message:
```c++
template<int>
void templ() {
}
template<typename>
void f() {
constexpr bool b = true;
[] {
b, templ<0>();
};
}
```
causes clang to report
```console
<source>:10:6: error: no matching function for call to 'templ'
10 | b, templ<0>();
| ^~~~~~~~
<source>:2:6: note: candidate template ignored: invalid explicitly-specified argument for 1st template parameter
2 | void templ() {
| ^
<source>:10:3: warning: left operand of comma operator has no effect [-Wunused-value]
10 | b, templ<0>();
| ^
1 warning and 1 error generated.
Compiler returned: 1
```
https://godbolt.org/z/M4vE39K5q
If the template is a user-defined literal, clang trunk and clang 19.1.0 give different error messages:
```c++
template<char...>
int operator""_literal() {
return 0;
}
template<typename>
void f() {
constexpr bool b = true;
[] {
b, 0_literal;
};
}
```
trunk:
```console
<source>:10:7: error: no matching function for call to 'operator""_literal'
10 | b, 0_literal;
| ^
<source>:2:5: note: candidate template ignored: invalid explicitly-specified argument for 1st template parameter
2 | int operator""_literal() {
| ^
1 error generated.
Compiler returned: 1
```
19.1.0:
```console
<source>:10:3: error: variable 'b' cannot be implicitly captured in a lambda with no capture-default specified
10 | b, 0_literal;
| ^
<source>:8:17: note: 'b' declared here
8 | constexpr bool b = true;
| ^
<source>:9:2: note: lambda _expression_ begins here
9 | [] {
| ^
<source>:9:3: note: capture 'b' by value
9 | [] {
| ^
| b
<source>:9:3: note: capture 'b' by reference
9 | [] {
| ^
| &b
<source>:9:3: note: default capture by value
9 | [] {
| ^
| =
<source>:9:3: note: default capture by reference
9 | [] {
| ^
| &
<source>:10:3: warning: left operand of comma operator has no effect [-Wunused-value]
10 | b, 0_literal;
| ^
1 warning and 1 error generated.
Compiler returned: 1
```
https://godbolt.org/z/eTfx6evnz
And if the template is a user-defined literal and constexpr variable's value is dependent on a template parameter then clang 19.1.0 accepts the code and clang trunk rejects (until you instantiate the template):
```c++
template<char...>
int operator""_literal() {
return 0;
}
template<typename T>
void f() {
constexpr bool b = T::b;
[] {
b, 0_literal;
};
}
```
```console
<source>:10:7: error: no matching function for call to 'operator""_literal'
10 | b, 0_literal;
| ^
<source>:2:5: note: candidate template ignored: invalid explicitly-specified argument for 1st template parameter
2 | int operator""_literal() {
| ^
1 error generated.
Compiler returned: 1
```
https://godbolt.org/z/cdo3jzWdq
And if you make the _expression_ you use dependent in other ways, then clang 19.1.0 actually accepts the code even on instantiation:
```c++
template<char...>
int operator""_literal() {
return 0;
}
template<typename T>
void f() {
constexpr bool b = T::b;
[] {
b ? 0_literal : 0;
};
}
struct s {
static constexpr bool b = true;
};
void g() {
f<s>();
}
```
clang trunk's error:
```console
<source>:10:8: error: no matching function for call to 'operator""_literal'
10 | b ? 0_literal : 0;
| ^
<source>:2:5: note: candidate template ignored: invalid explicitly-specified argument for 1st template parameter
2 | int operator""_literal() {
| ^
<source>:9:2: warning: _expression_ result unused [-Wunused-value]
9 | [] {
| ^~~~
10 | b ? 0_literal : 0;
| ~~~~~~~~~~~~~~~~~~
11 | };
| ~
<source>:19:2: note: in instantiation of function template specialization 'f<s>' requested here
19 | f<s>();
| ^
1 warning and 1 error generated.
Compiler returned: 1
```
https://godbolt.org/z/qd5Mf3oP3
This is actually the version of the bug I ran into in the wild. I had apparently working code rejected by a recent build of clang because I had code like the last pattern I showed: I set a constexpr local variable to the result of a function dependent on a template parameter, then used its value as the first argument of the conditional operator that used a user-defined literal in one of the other arguments.
Note that gcc accepts all of my examples, and I believe it is correct to do so. Lambda bodies that do odr-use the constexpr variable (such as `&b`) are correctly rejected by all compilers.
_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs