llvm / llvm/llvm-project

`if constexpr (requires{...})` selects the wrong branch inside a generic lambda invoked via `.template operator()<...>()`

Open
#210,520 3 comments 0 reactions 0 assignees View on GitHub
clang:frontend constexpr
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

`if constexpr` picks the `else` branch of a `requires`-expression condition that is actually true, when that condition is checked inside a generic lambda which is itself invoked with an explicit template argument (`.template operator()()`) from inside a function template. GCC accepts the code below; Clang rejects it with a diagnostic on the (genuinely ill-formed, and never supposed to be reached) `else` branch.

### Minimal reproducer

Two functions with the exact same `if constexpr (requires{...}) ... else ...` logic on the same callable — the only difference is whether the check happens directly in the function template's body (`compiles`) or one level deeper, inside a generic lambda invoked via `.template operator()<0>()` (`clang_crashes`):

```cpp
template
void compiles(Func&& func)
{
constexpr int i = 0;

if constexpr (requires { func.template operator()(); })
func.template operator()();
else
func(); // would be ill-formed without explicit <>
}

template
void clang_crashes(Func&& func)
{
[&]()
{
if constexpr (requires { func.template operator()(); })
func.template operator()();
else
func(); // ill-formed without explicit <>
}.template operator()<0>();
}

int main()
{
// invokes the lambda with a == 0 after testing if it has a template parameter
compiles([]() {});
clang_crashes([]() {});
}
```

### Actual behavior (clang 18.1.3 and clang 20.1.2, Ubuntu, `clang++ -std=c++20 -fsyntax-only repro.cpp`)

`compiles` compiles fine. `clang_crashes` does not, identically on both versions:

```
repro.cpp:20:13: error: no matching function for call to object of type '(lambda at repro.cpp:28:19)'
func(); // ill-formed without explicit <>
^~~~
repro.cpp:16:5: note: while substituting into a lambda expression here
repro.cpp:28:5: note: in instantiation of function template specialization 'clang_crashes<(lambda at repro.cpp:28:19)>' requested here
clang_crashes([]() {});
^
repro.cpp:28:19: note: candidate template ignored: couldn't infer template argument 'a'
```

### Expected behavior

Both functions should compile cleanly, as they do with GCC 13.3.0 (`-std=c++20`).

### Related issues

- [#123306](https://github.com/llvm/llvm-project/issues/123306) (open) — closest match, possibly the same root cause: delayed template instantiation across a templated generic lambda boundary not working correctly (cites `[temp.point]p1`), GCC/MSVC accept, Clang rejects. Different surface syntax though: their trigger is a `requires`-clause-constrained member function called from a templated lambda capturing a class instance, not an `if constexpr` branching on a `requires` *expression*. This reproducer is more minimal and isolates the `if constexpr` branch-selection failure specifically, but the two may collapse to the same underlying bug.
- [#73418](https://github.com/llvm/llvm-project/issues/73418) (open) — also a `requires`-expression inside a nested generic lambda, but the symptom is an internal compiler error (via pack expansion in the `requires`-clause), not a wrong branch silently selected.
- [#61387](https://github.com/llvm/llvm-project/issues/61387) (open) — lambda with explicit template parameters and a `requires`-clause using a dependent variable template; different trigger shape.
- [#198987](https://github.com/llvm/llvm-project/issues/198987) / [#198984](https://github.com/llvm/llvm-project/issues/198984) (closed) — same symptom category ("`if constexpr` instantiates the discarded branch") but only reproduces with C++20 modules, unrelated to generic lambdas.

### Versions tested

- Clang: `Ubuntu clang version 18.1.3 (1ubuntu1)`, target `x86_64-pc-linux-gnu` — `clang_crashes` rejected
- Clang: `Ubuntu clang version 20.1.2 (0ubuntu1~24.04.3)`, target `x86_64-pc-linux-gnu` — `clang_crashes` rejected, identical diagnostic
- GCC: `g++ (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0`, `-std=c++20` — both accepted

Contributor guide

Open the contributing guide

Research direction

Start by running the supplied repro.cpp with Clang using `-std=c++20 -fsyntax-only`, then compare the result with GCC. Trace the `if constexpr` and `requires` handling across the explicitly invoked generic lambda; done means Clang accepts both functions without instantiating the discarded branch, with a regression test covering this reproducer.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.