Skip to content

[Clang] constexpr variables wrongfully treated as captures in certain lambdas #120503

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

Open
yuxuanchen1997 opened this issue Dec 19, 2024 · 6 comments
Labels
c++20 clang:frontend Language frontend issues, e.g. anything involving "Sema" constexpr Anything related to constant evaluation diverges-from:edg Does the clang frontend diverge from edg compiler diverges-from:gcc Does the clang frontend diverge from gcc on this issue diverges-from:msvc Does the clang frontend diverge from msvc on this issue lambda C++11 lambda expressions

Comments

@yuxuanchen1997
Copy link
Member

yuxuanchen1997 commented Dec 19, 2024

Clang currently rejects the following program by saying "error: variable 'XXXcap' cannot be implicitly captured in a lambda with no capture-default specified"

template <typename...>
struct tag_t {};

template <auto...>
struct vtag_t {};

template <auto... V>
inline constexpr vtag_t<V...> vtag{};

template <typename T>
constexpr bool alt = true;

template <typename = void>
auto vtag_safety_of_non_stored_args() {
  return []<typename T>(tag_t<T>) {
    return vtag<[]() {
      constexpr bool XXXcap = alt<T>;
      if constexpr (XXXcap) {
        return vtag<>;
      } else {
        return vtag<XXXcap>;
      }
    }>;
  }(tag_t<int>{});
}

void check() {
  vtag_safety_of_non_stored_args();
}

This seems wrong. As XXXcap here is not a capture. GCC and MSVC seem to have no problem with this.

See compiler explorer: https://godbolt.org/z/7rqcfGe7x

@yuxuanchen1997 yuxuanchen1997 changed the title [Clang] constexpr variables are treated as captures [Clang] constexpr variables wrongfully treated as captures Dec 19, 2024
@github-actions github-actions bot added the clang Clang issues not falling into any other category label Dec 19, 2024
@yuxuanchen1997 yuxuanchen1997 added c++20 and removed clang Clang issues not falling into any other category labels Dec 19, 2024
@yuxuanchen1997 yuxuanchen1997 changed the title [Clang] constexpr variables wrongfully treated as captures [Clang] constexpr variables wrongfully treated as captures in unevaluated lambdas Dec 19, 2024
@llvmbot
Copy link
Member

llvmbot commented Dec 19, 2024

@llvm/issue-subscribers-c-20

Author: Yuxuan Chen (yuxuanchen1997)

Clang currently rejects the following program by saying "error: variable 'XXXcap' cannot be implicitly captured in a lambda with no capture-default specified" ```cpp template <typename...> struct tag_t {};

template <auto...>
struct vtag_t {};

template <auto... V>
inline constexpr vtag_t<V...> vtag{};

template <typename T>
constexpr bool alt = true;

template <typename = void>
auto vtag_safety_of_non_stored_args() {
return []<typename T>(tag_t<T>) {
return vtag< {
constexpr bool XXXcap = alt<T>;
if constexpr (XXXcap) {
return vtag<>;
} else {
return vtag<XXXcap>;
}
}>;
}(tag_t<int>{});
}

void check() {
vtag_safety_of_non_stored_args();
}


This seems wrong. As `XXXcap` here is not a capture. GCC and MSVC seem to have no problem with this.
 
See compiler explorer: https://godbolt.org/z/7rqcfGe7x

</details>

@yuxuanchen1997 yuxuanchen1997 added diverges-from:gcc Does the clang frontend diverge from gcc on this issue diverges-from:msvc Does the clang frontend diverge from msvc on this issue diverges-from:edg Does the clang frontend diverge from edg compiler labels Dec 19, 2024
@EugeneZelenko EugeneZelenko added clang:frontend Language frontend issues, e.g. anything involving "Sema" constexpr Anything related to constant evaluation labels Dec 19, 2024
@llvmbot
Copy link
Member

llvmbot commented Dec 19, 2024

@llvm/issue-subscribers-clang-frontend

Author: Yuxuan Chen (yuxuanchen1997)

Clang currently rejects the following program by saying "error: variable 'XXXcap' cannot be implicitly captured in a lambda with no capture-default specified" ```cpp template <typename...> struct tag_t {};

template <auto...>
struct vtag_t {};

template <auto... V>
inline constexpr vtag_t<V...> vtag{};

template <typename T>
constexpr bool alt = true;

template <typename = void>
auto vtag_safety_of_non_stored_args() {
return []<typename T>(tag_t<T>) {
return vtag< {
constexpr bool XXXcap = alt<T>;
if constexpr (XXXcap) {
return vtag<>;
} else {
return vtag<XXXcap>;
}
}>;
}(tag_t<int>{});
}

void check() {
vtag_safety_of_non_stored_args();
}


This seems wrong. As `XXXcap` here is not a capture. GCC and MSVC seem to have no problem with this.
 
See compiler explorer: https://godbolt.org/z/7rqcfGe7x

</details>

@snarkmaster
Copy link

snarkmaster commented Dec 19, 2024

I initially ran into this problem when my code had if constexpr (alt<T>) with true alt<T>, but the branch wasn't being taken. I then lifted alt<T> to XXXcap, and the above (incorrect) error appeared. @yuxuanchen1997 helped reduce the repro that you see here.

With Yuxuan's reduced code, I just tried to reproduce the initial "wrong branch being taken" behavior, and got a different manifestation of this bug:

#include <type_traits>

template <typename...>
struct tag_t {};

template <auto...>
struct vtag_t {};
template <auto... V>
inline constexpr vtag_t<V...> vtag{};

template <typename T>
constexpr bool alt = true;

template <typename = void>
auto branch_on_alt() {
  return []<typename T>(tag_t<T>) {
    return vtag<[]() {
      if constexpr (alt<T>) {
        return vtag<1>;
      } else {
        return vtag<0>;
      }
    }()>;
  }(tag_t<int>{});
}

static_assert(std::is_same_v<
  decltype(branch_on_alt()), vtag_t<vtag<1>>>);

Again, the error (see https://godbolt.org/z/e364M1v9T) does not reproduce on gcc or MSVC. But maybe the new message has some additional hint for why this is happening?

return type 'vtag_t<0>' must match previous return type 'vtag_t<1>' when lambda expression has unspecified explicit return type

So, rather than "the wrong branch is taken", it seems like clang is attempting to compile both if constexpr branches, which sounds incorrect to me.


Edit: Yes, lifting the inner lambda to a function works around the issue:

template <typename T>
constexpr auto lifted_lambda() {
  if constexpr (alt<T>) {
    return vtag<1>;
  } else {
    return vtag<0>;
  }
}

template <typename = void>
auto branch_on_alt() {
  return []<typename T>(tag_t<T>) {
    return vtag<lifted_lambda<T>()>;
  }(tag_t<int>{});
}

@yuxuanchen1997 yuxuanchen1997 changed the title [Clang] constexpr variables wrongfully treated as captures in unevaluated lambdas [Clang] constexpr variables wrongfully treated as captures in certain lambdas Dec 19, 2024
@yuxuanchen1997 yuxuanchen1997 added the lambda C++11 lambda expressions label Dec 19, 2024
@shafik
Copy link
Collaborator

shafik commented Dec 19, 2024

CC @cor3ntin

@efriedma-quic
Copy link
Collaborator

The common thread for the issues is that there's a lambda defined inside a template argument list, I think.

@cor3ntin
Copy link
Contributor

might be fixed by #107942 @mizvekov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++20 clang:frontend Language frontend issues, e.g. anything involving "Sema" constexpr Anything related to constant evaluation diverges-from:edg Does the clang frontend diverge from edg compiler diverges-from:gcc Does the clang frontend diverge from gcc on this issue diverges-from:msvc Does the clang frontend diverge from msvc on this issue lambda C++11 lambda expressions
Projects
None yet
Development

No branches or pull requests

7 participants