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

Reply via email to