This is an archive of the discontinued LLVM Phabricator instance.

[Clang] Tighten restrictions on enum out of range diagnostic
ClosedPublic

Authored by shafik on Aug 11 2022, 11:11 AM.

Details

Summary

In D131528 using Info.EvalMode == EvalInfo::EM_ConstantExpression is not strict enough to restrict the diagnostic to only constant expression contexts. It is sometimes set in cases where we are still determining if we are in a constant expression context.

Using InConstantContext will tighten the restriction.

Diff Detail

Event Timeline

shafik created this revision.Aug 11 2022, 11:11 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 11 2022, 11:11 AM
aaron.ballman accepted this revision.Aug 11 2022, 12:42 PM

LGTM presuming precommit CI comes back clean.

This revision is now accepted and ready to land.Aug 11 2022, 12:42 PM
erichkeane accepted this revision.Aug 11 2022, 12:59 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 11 2022, 1:44 PM

Ok it looks like this is a bit more subtle, I broke the llvm-test-suite file paq8p.cpp again. We need both conditions to be true Info.EvalMode == EvalInfo::EM_ConstantExpression && Info.InConstantContext. We need to be in a context that requires a constant value but also in a constant expression context.

We are doing a little bit of an odd thing here and so this is a bit unique. I am doing a quick check-clang and will patch this up as long as that passes.

We're seeing this warning in code with global constants, e.g.

const Enum x = static_cast<Enum>(-1);

is this intended?

We're seeing this warning in code with global constants, e.g.

const Enum x = static_cast<Enum>(-1);

is this intended?

This is constant initialization, so this is indeed expected.

Depending on your situation you can fix this by using a fixed underlying type e.g. : enum A : int or by making it a scoped enum enum class A or by extending the enumerators themselves to include the value you are using.

If this not your code you can also turn the error into a warning using -Wno-error=enum-constexpr-conversion

This is constant initialization, so this is indeed expected.

Do we have a test for this in clang? It seems that the diag appeared for that case after the change that turned this diag into a disable-able warning. Given it's intentional, we should make sure we test that this diag is emitted, to make sure it stays around once the diag goes back to always-an-error.

We're seeing this warning in code with global constants, e.g.

const Enum x = static_cast<Enum>(-1);

is this intended?

This is constant initialization, so this is indeed expected.

Depending on your situation you can fix this by using a fixed underlying type e.g. : enum A : int or by making it a scoped enum enum class A or by extending the enumerators themselves to include the value you are using.

If this not your code you can also turn the error into a warning using -Wno-error=enum-constexpr-conversion

Another question about this. The diag name and all the wording so far was about "constexpr". The expression Amy pasted is constant initialization, but _not_ constexpr.

Is this really intentional? This is quite an increase in scope for the diag, if so. we did a full cleanup for the original diag, but after the recent tweaks it fires in many many more places.

Can we put this under a different warning flag for non-constexpr cases if this is really intentional? I'm assuming the non-constexpr case won't become an unconditional error later? It probably also shouldn't be mapped to an error?

This is constant initialization, so this is indeed expected.

Do we have a test for this in clang? It seems that the diag appeared for that case after the change that turned this diag into a disable-able warning. Given it's intentional, we should make sure we test that this diag is emitted, to make sure it stays around once the diag goes back to always-an-error.

After discussing this with @erichkeane we decided that a diagnostic for the constant initialization of a global was not desirable.

It took me a while to find a solution for this case. One might think that CCEDiag machinery would have the information we need to determine whether we are initializing a constexpr variable or not but actually the determination of whether to discard the diagnostic is done by the consumer further away. An example can be seen in Sema::CheckCompleteVariableDeclaration when it gets the diagnostic notes from var->checkForConstantInitialization(Notes). If the initialization is not constant then it must do various checks for example var->isConstexpr() to determine whether to discard the diagnostics or not.

Fortunately, we have access to the VarDecl through Info.EvaluatingDecl and we do similar checks in other instances. I am testing some approaches.