inconsistent compilation error inside constexpr if

Hello,

In the below code the compiler throws “undeclared identifier” when the commented line is uncommented. Whereas the line just before compiles fine.

Regards,
Manu

typedef bool (* DummyFunc) ();

bool ExecDummy (DummyFunc fptr) {

if (fptr)
return fptr ();

return false;
}

constexpr unsigned int IsMyAppClient = 0;

constexpr bool CheckForTypeClient (unsigned int pAppType)
{
return ((pAppType & IsMyAppClient) != 0);
}

class MyAppTemplate
{
public:
template
static bool MyAppInit ();
};

template
bool
MyAppTemplate::MyAppInit ()
{
if constexpr (CheckForTypeClient(T)) {

return ClientMain (); // no error
//return ExecDummy(ClientMain); // error: use of undeclared identifier 'ClientMain’
}

return false;
}

int __cdecl
main (int pArgc, char* pArgv[])
{
constexpr int TVal = 3;

MyAppTemplate::MyAppInit ();

return 0;
}

From what I could test on godbolt, using LLVM evrsions back to 5.0,

Clang does reject the "return ClientMain();" call you aren't seeing an
error on. So I'm not sure what compiler/version/situation you're
running, but at least at first blush it doesn't look like clang.

Hi David,

Thanks for the reply. I missed to mention the environment and what i am trying to do.

  1. Environment is ClangCL (version 10.0), Microsoft Visual Studio.
  2. I assumed that since the ‘constexpr if’ evaluates to false, the external function’s presence would not be checked. So, if my application is not of type ‘client’ ClientMain would not be called. My actual code inside the constexpr if is the commented line of code that does not compile. To experiment i simply invoked ClientMain directly which compiled fine.

Here is the assembly generated from the successful compilation (with the first line inside ‘constexpr if’ enabled).

“??$MyAppInit@$02@MyAppTemplate@@SQ_NXZ”: # @"??$MyAppInit@$02@MyAppTemplate@@SQ_NXZ"
.Lfunc_begin2:
.cv_func_id 2
.cv_loc 2 1 32 0 # Main.cpp:32:0

%bb.0:

.cv_loc 2 1 39 0 # Main.cpp:39:0
xor eax, eax

kill: def $al killed $al killed $eax

.Ltmp6:
ret
.Ltmp7:
.Lfunc_end2:

Here is how the compiler is invoked:

E:\LLVM\bin\clang-cl.exe /c /Z7 /nologo /W3 /WX- /diagnostics:column /Od /D _DEBUG /D _CONSOLE /D _UNICODE /D UNICODE /EHsc /MTd /GS /fp:precise /permissive- /std:c++17 /FA /Fa"x64\Debug\" /Fo"x64\Debug\" /Gv /TP -m64 Main.cpp
1> Done executing task “CL”.

Is my understanding of ‘constexpr if’ wrong ? won’t the body get discarded in case it gets evaluated to false?

Regards,
Manu

Hi David,

Thanks for the reply. I missed to mention the environment and what i am trying to do.

  1. Environment is ClangCL (version 10.0), Microsoft Visual Studio.
  2. I assumed that since the ‘constexpr if’ evaluates to false, the external function’s presence would not be checked. So, if my application is not of type ‘client’ ClientMain would not be called. My actual code inside the constexpr if is the commented line of code that does not compile. To experiment i simply invoked ClientMain directly which compiled fine.

Here is the assembly generated from the successful compilation (with the first line inside ‘constexpr if’ enabled).

“??$MyAppInit@$02@MyAppTemplate@@SQ_NXZ”: # @"??$MyAppInit@$02@MyAppTemplate@@SQ_NXZ"
.Lfunc_begin2:
.cv_func_id 2
.cv_loc 2 1 32 0 # Main.cpp:32:0

%bb.0:

.cv_loc 2 1 39 0 # Main.cpp:39:0
xor eax, eax

kill: def $al killed $al killed $eax

.Ltmp6:
ret
.Ltmp7:
.Lfunc_end2:

Here is how the compiler is invoked:

E:\LLVM\bin\clang-cl.exe /c /Z7 /nologo /W3 /WX- /diagnostics:column /Od /D _DEBUG /D _CONSOLE /D _UNICODE /D UNICODE /EHsc /MTd /GS /fp:precise /permissive- /std:c++17 /FA /Fa"x64\Debug\" /Fo"x64\Debug\" /Gv /TP -m64 Main.cpp
1> Done executing task “CL”.

Is my understanding of ‘constexpr if’ wrong ? won’t the body get discarded in case it gets evaluated to false?

Yeah, that’s not quite how constexpr works - I think you may’ve gained that model partly by the combination of MSVC-compatible lazy-parsed templates and constexpr.

The spec basically says something /very roughly/ like “dependent things in a non-taken constexpr if branch remain dependent even after template instantiation”. But that means code that’s invalid no matter what (like “static_assert(false)”) still trigger errors. See the latter parts of the constexpr if section here: https://en.cppreference.com/w/cpp/language/if

Hello,

In the below code the compiler throws “undeclared identifier” when the commented line is uncommented. Whereas the line just before compiles fine.

Regards,
Manu

typedef bool (* DummyFunc) ();

bool ExecDummy (DummyFunc fptr) {

if (fptr)
return fptr ();

return false;
}

constexpr unsigned int IsMyAppClient = 0;

constexpr bool CheckForTypeClient (unsigned int pAppType)
{
return ((pAppType & IsMyAppClient) != 0);
}

class MyAppTemplate
{
public:
template
static bool MyAppInit ();
};

template
bool
MyAppTemplate::MyAppInit ()
{
if constexpr (CheckForTypeClient(T)) {

return ClientMain (); // no error
//return ExecDummy(ClientMain); // error: use of undeclared identifier 'ClientMain’
}

This code is invalid, as David Blaikie explains. However, in MSVC-compatible mode (under -fms-compatibility, which is enabled by default for some Windows-targeting modes), Clang attempts to to accept certain invalid code that MSVC has historically accepted, including this case, where we allow the call to ClientMain() to find declarations of a ClientMain function that appear after the template definition. That recovery from invalid code is only done for certain syntactic patterns, such as unqualified function calls – so it applies to ClientMain() but not to (ClientMain).

Thanks Richard and David for your replies. That was helpful.

Regards,
Manu