<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">