Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect Clang compilation error in non-taken if-constexpr branch within template lambda #129912

Closed
mikebentley15 opened this issue Mar 5, 2025 · 2 comments
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" duplicate Resolved as duplicate lambda C++11 lambda expressions

Comments

@mikebentley15
Copy link

Godbolt: https://godbolt.org/z/WGWWozG1q

The scenario is fairly strange to reproduce. It's code to conditionally wrap a callable with a mutable or a const lambda. The if constexpr branch that isn't taken causes the compilation to fail.

Simplified code:

#include <cstdio>
#include <iostream>
#include <type_traits>
#include <utility>

struct ConstWrap
{};
struct MutableWrap
{};

auto lambdaWrap(auto callable) -> decltype(auto)
{
    const auto decorator = [&callable]<typename WrapType>() {
        if constexpr (std::is_same_v<ConstWrap, WrapType>) {
            return [capturedCallable = std::move(callable)]() {
                std::puts("ConstWrap");
                capturedCallable();
            };
        } else {
            return [capturedCallable = std::move(callable)]() mutable {
                std::puts("MutableWrap");
                capturedCallable();
            };
        }
    };
    return decorator.template operator()<MutableWrap>();
}

int main()
{
    auto decorated =
        lambdaWrap([counter = 0]() mutable { std::cout << "Counter: " << ++counter << "\n"; });
    decorated();
    return 0;
}

This fails with

<source>:17:17: error: no matching function for call to object of type 'const typename std::remove_reference<(lambda at <source>:32:20) &>::type' (aka 'const (lambda at <source>:32:20)')
   17 |                 capturedCallable();
      |                 ^~~~~~~~~~~~~~~~
<source>:15:63: note: while substituting into a lambda expression here
   15 |             return [capturedCallable = std::move(callable)]() {
      |                                                               ^
<source>:13:61: note: while substituting into a lambda expression here
   13 |     const auto decorator = [&callable]<typename WrapType>() {
      |                                                             ^
<source>:32:9: note: in instantiation of function template specialization 'lambdaWrap<(lambda at <source>:32:20)>' requested here
   32 |         lambdaWrap([counter = 0]() mutable { std::cout << "Counter: " << ++counter << "\n"; });
      |         ^
<source>:32:20: note: candidate function not viable: 'this' argument has type 'const typename std::remove_reference<(lambda at <source>:32:20) &>::type' (aka 'const (lambda at <source>:32:20)'), but method is not marked const
   32 |         lambdaWrap([counter = 0]() mutable { std::cout << "Counter: " << ++counter << "\n"; });
      |                    ^
1 error generated.
Compiler returned: 1

on Clang 18.1.0 and on trunk (same error). This works if the decorator lambda is not within a templated function (e.g., move it into main() and it works). My current workaround is to move the if constexpr outside of the decorator.

GCC 10.4 is capable of compiling this example.

Perhaps this is related to #113792.

@llvmbot llvmbot added the clang Clang issues not falling into any other category label Mar 5, 2025
@EugeneZelenko EugeneZelenko added clang:frontend Language frontend issues, e.g. anything involving "Sema" lambda C++11 lambda expressions constexpr Anything related to constant evaluation and removed clang Clang issues not falling into any other category labels Mar 5, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 5, 2025

@llvm/issue-subscribers-clang-frontend

Author: Michael Bentley, PhD (mikebentley15)

Godbolt: https://godbolt.org/z/WGWWozG1q

The scenario is fairly strange to reproduce. It's code to conditionally wrap a callable with a mutable or a const lambda. The if constexpr branch that isn't taken causes the compilation to fail.

Simplified code:

#include &lt;cstdio&gt;
#include &lt;iostream&gt;
#include &lt;type_traits&gt;
#include &lt;utility&gt;

struct ConstWrap
{};
struct MutableWrap
{};

auto lambdaWrap(auto callable) -&gt; decltype(auto)
{
    const auto decorator = [&amp;callable]&lt;typename WrapType&gt;() {
        if constexpr (std::is_same_v&lt;ConstWrap, WrapType&gt;) {
            return [capturedCallable = std::move(callable)]() {
                std::puts("ConstWrap");
                capturedCallable();
            };
        } else {
            return [capturedCallable = std::move(callable)]() mutable {
                std::puts("MutableWrap");
                capturedCallable();
            };
        }
    };
    return decorator.template operator()&lt;MutableWrap&gt;();
}

int main()
{
    auto decorated =
        lambdaWrap([counter = 0]() mutable { std::cout &lt;&lt; "Counter: " &lt;&lt; ++counter &lt;&lt; "\n"; });
    decorated();
    return 0;
}

This fails with

&lt;source&gt;:17:17: error: no matching function for call to object of type 'const typename std::remove_reference&lt;(lambda at &lt;source&gt;:32:20) &amp;&gt;::type' (aka 'const (lambda at &lt;source&gt;:32:20)')
   17 |                 capturedCallable();
      |                 ^~~~~~~~~~~~~~~~
&lt;source&gt;:15:63: note: while substituting into a lambda expression here
   15 |             return [capturedCallable = std::move(callable)]() {
      |                                                               ^
&lt;source&gt;:13:61: note: while substituting into a lambda expression here
   13 |     const auto decorator = [&amp;callable]&lt;typename WrapType&gt;() {
      |                                                             ^
&lt;source&gt;:32:9: note: in instantiation of function template specialization 'lambdaWrap&lt;(lambda at &lt;source&gt;:32:20)&gt;' requested here
   32 |         lambdaWrap([counter = 0]() mutable { std::cout &lt;&lt; "Counter: " &lt;&lt; ++counter &lt;&lt; "\n"; });
      |         ^
&lt;source&gt;:32:20: note: candidate function not viable: 'this' argument has type 'const typename std::remove_reference&lt;(lambda at &lt;source&gt;:32:20) &amp;&gt;::type' (aka 'const (lambda at &lt;source&gt;:32:20)'), but method is not marked const
   32 |         lambdaWrap([counter = 0]() mutable { std::cout &lt;&lt; "Counter: " &lt;&lt; ++counter &lt;&lt; "\n"; });
      |                    ^
1 error generated.
Compiler returned: 1

on Clang 18.1.0 and on trunk (same error). This works if the decorator lambda is not within a templated function (e.g., move it into main() and it works). My current workaround is to move the if constexpr outside of the decorator.

GCC 10.4 is capable of compiling this example.

Perhaps this is related to #113792.

@frederick-vs-ja
Copy link
Contributor

Closing as duplicate of #58872 (as many discussions are there). See also P0588R1 and #61426.

(Removing the constexpr Anything related to constant evaluation label as this isn't quite related to constant evaluation.)

@frederick-vs-ja frederick-vs-ja added duplicate Resolved as duplicate and removed constexpr Anything related to constant evaluation labels Mar 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" duplicate Resolved as duplicate lambda C++11 lambda expressions
Projects
None yet
Development

No branches or pull requests

4 participants