Skip to content

if constexpr code not ignored inside an immediately invoked lambda #125491

Open
@Toeger

Description

@Toeger

Minimal reproducible example

template <class... Ts>
struct Type_list {
	private:
	template <int index, class... Args>
	struct Get_at;
	template <class T, class... Args>
	struct Get_at<0, T, Args...> {
		using Type = T;
	};
	template <int index, class T, class... Args>
	struct Get_at<index, T, Args...> {
		using Type = typename Get_at<index - 1, Args...>::Type;
	};

	public:
	template <int index>
	using at = typename Get_at<index, Ts...>::Type;
	constexpr static auto size = sizeof...(Ts);
};

template <class F>
struct CI {};
template <class R, class... PArgs>
struct CI<R (*)(PArgs...)> {
	using Args = Type_list<PArgs...>;
};

template <class Function>
constexpr bool is_viable_source = []<class Properties_list>(Properties_list) {
	if constexpr (Properties_list::size != Properties_list::size) {
		static_assert(false);
		return sizeof(typename CI<Function>::Args::template at<0>) > 0;
	}
	return false;
}(Type_list<>{});

int main() {
	return is_viable_source<void (*)()>;
}

https://godbolt.org/z/o3co4j31e

Expected behavior

Successfully builds executable equivalent to int main() {}.

Actual behavior: Compilation error

<source>:17:2: error: implicit instantiation of undefined template 'Type_list<>::Get_at<0>'
   17 |         using at = typename Get_at<index, Ts...>::Type;
      |         ^
<source>:32:55: note: in instantiation of template type alias 'at' requested here
   32 |                 return sizeof(typename CI<Function>::Args::template at<0>) > 0;
      |                                                                     ^
<source>:29:78: note: while substituting into a lambda expression here
   29 | constexpr bool is_viable_source = []<class Properties_list>(Properties_list) {
      |                                                                              ^
<source>:38:9: note: in instantiation of variable template specialization 'is_viable_source<void (*)()>' requested here
   38 |         return is_viable_source<void (*)()>;
      |                ^
<source>:5:9: note: template is declared here
    5 |         struct Get_at;
      |                ^
1 error generated.

Affected versions

Clang 18, clang 19 and clang trunk
C++23 and above (it seems the explicit lambda template argument is required to reproduce, which is a C++23 feature)
Gcc compiles the code successfully.

Reasoning why clang is wrong

The error is caused by code inside an if constexpr that evaluates to false, thus clang should not have attempted to compile the code or ignored the error. Clang partially agrees that the if constexpr evaluates to false and that the code should not be compiled since it does not complain about the static_assert(false);.

Workaround

The code compiles after replacing the immediately invoked lambda with a helper function: https://godbolt.org/z/6P5qToWWT
This also applies to my ungolfed code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    clang:frontendLanguage frontend issues, e.g. anything involving "Sema"confirmedVerified by a second partyrejects-valid

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions