<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/127086>127086</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Various issues with using uncaptured constexpr variable in lambda
</td>
</tr>
<tr>
<th>Labels</th>
<td>
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
davidstone
</td>
</tr>
</table>
<pre>
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.
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzsWktv4zgS_jXMpRBDFmM7PvjgJBMg2J3FHhrb2NOAIksSp2lSTVJ2uw_z2xcUJdvyI7GTdLp3sUKAJHqwPtbzq5KYc7LQiDMyuiOjhytW-9LYmWBLKZw3Gq8yI9azJ-BMa-Mhl0VtEUztYVUyD5zVDh1wxXQB3oBALgWCD9eewAh7XTsUwGDJrGSZQiDprS9rBxa_1tJKXQADzipfWyTplKT3wLSAnEkl45Ltxd01oriSuVae2JXVQkJrjYUFOscKdCA11NqiYh4FOOReGu3A5MCNwAFJ5iSZP0rrfEAQ17f4J3LvwJcIuVHKrKQuCJ3Hm8k4iT-cpHfhJ5kvjRSQk_SWpFMgk7vmxik32nn8VlnIjFGQAaEP4G2NhLZ3RN2HJyAj9A7I5KG9NnnYlRQFr6Qv9yEY7YzCcIreO1NbjoT-RuicEjqfEDqP2gh_7NhhkpF00hk2Q5CLSkkuvVp3OhdBbQwUW2SCQRAMemOQa4E5q5UHVyGXuURBkjkAAAUyuYeTm4LmCLf0DjL67RB_Suh82GxAG4_hdwdbIFcsICzRYrtq2lv1Bb1vYRyVTFvpG8GtEsKC6Jw0GjIspHa7AGgPwAUaOAmB7kLoIqHTQbaGJVM1vlrvUeyRC9nr0FjM0aLmb9ZHh6x_Lh2fg6tzyw7fj9MSoQ-vw_NmPZ2GlI6fyQIrZnXMYbt-bNEFfLVu0icZ3V1_jn9fR72NHt4G81RkvQuay4Lsr_YgyTzt5Lum4AzbglGgRhuKRKgJ92ZRSYUWLPraahQB6_AwJZfeVy5UhvSRpI-FEZlRfmBsQdLH7yR9_H3JRnn-b4Px9qz2wDjHyrsfWUtIendGOXnywJQzXbX7kYCcZ17yPzhzntD7sGDwiPQ2C4X_RaAfpLU-yPDAJSCfXsbocVEFGkLovV9XqNkC4VOQcRH8T8Hd6Dx7JYmotZcKVgjergPJYtzXTKk1SO08014yj5H41LrhSs_xnp-0o2TuvK25B7ddOVrvpdq_XalFWOwjzEPSioaPdj-lSFyiBl-auihhVaIv0QIZJxkZJyDdlpVKFwoCMBBYoRaoPVTWVGj9umWfn0vUu49G4uygYtYHosr0bppsGPaOsRww6OwQzgcWHh7anAy8Nhi0x4uDnJU1ugi0PKTBwKkL4z1qWBiL7cWG-bUc39b6C0mnA9iw5VUAXrtI5S0WtWJ26zcbOt082eTa-P9wOhgOEmCFRYRmR524HsQzHU9qv-NxzYW-TTufOe6vF3vrifTR_p9Ms0bfDQx6n-x7UjJ9Pjz32yqLlbH-XNY_TAidj3u0XxtYMM_LYKTONJAbC5wpFSSQdNIqbRKL5zA55OgNM3xmX73Kvy21R0j9uM8ktZCiyTgbDy60sbHeSr1kSorg_G17cr3pOIDZol6EYAp7GTq_XaFili3Qo-01Bye8Y48sHOUtjVbpHnFRmHsIYRz8uukmFwsWT3hjm3DSBjDPkftnyMzblL1_RPjDDucH8Jub5W90-rfR17YI5k0wb40ZklPt0IaWUWoUoKRHy9SLyaGQSwQh84Yu-72e_szMwEtmB4NBjHCp_cY6JE1Jmv6xwdKP-qgSSPZKzofljmQD7LyUERPzoUqeTRKTi5PEKeX1s8axHZz215MhFzLF6CdlijM9pb-rLvLeEGvR8y81JP3gIc-r7HzUxrcXjXhu33HEM33NiGe613WeP86ZXjLOmZ5otd8-xzkHxv584m1ojs9tDnC8MLd5J40cndmcg-XddfLrMIyTAfzhRAI_5d_GuNTf4-1zLUCeSyYie9hkhC4HknTioiOFR7fdl9G7HdOmBgRheq9DiQ19A4MbgTs0JdKW7kUBSW9jX7029UEjve3Dpr8KcXnfJv1t9OUnE5bL4uLY8X8Cc2aMc2Hon98_i6-9GA8hs2BfYqzsVOBwvna4E7hSg2nGLCu2du1U4yBi22nWQeg24xqjd6Lz_NnWf208AqGPW2eGYLfkZFj-ElO13qxp4jYRf1GuuP1Rzc1pfT6fIv4HcsTzRPqNb3TO5dZxqvRM8n6Ngf46OFoZw774w_dLR0Zcw4PeQu7lnEDlNm64sVJjSabk93gPSSfbkJk0n0yg872OaNjnocci7Cfyua9i9HtOzT9pvP1TKV1D4Lr8HPLyEq1rFRL-zeoCnsCyoC9vgt6awbBUYgBPUDIBrKqYRR1a2JWxX8JGmtweaRiKwNYZWOQhELJaqsibm5SSYTNabVdqHlOyrTqKOQ8V8x6thidwpVnFjT-BQw9sJ_8pw5nattneNAu0jt6M3Te2fZFxbkpYfE_gO7bK2i9PpHV-G9itlrjRQob1mdr2As07gfZ7m6MMOZROjd0asYp2K7v2NcQ_jG-_3yk43xTQkCNNDos14De2qBS67kudJ8hQSVwiSB9sy421oRXxBoQBZwbw99hWZ0ZIdHFpYbo3I91u9lh74NKu5mXQQvC20EKGX1NgFjsZat23uVKhO2oc1w2uxIyKKZ2yK5wNJ3R6M04n05urcoY5FZNhMuRsnN2kGaUsp5PxKOc3KReCjq7kLE3SUZIO6XCcjCkdJKNRziY3CZ9yllLMyE2CCybVQKnlInj7lXSuxtkwnSS34yvFMlSu-6LKzsJd11ldOHKTKOm82z7npVc4-xez0tQhMlyNLo5g4vuUWm-GNEd0JHU7sriqrZrtxaL0ZZ0NuFmQ9DHIa39dV9YElZH0MYoj6WOLezlL_xMAAP__PwIAlA">