This is an archive of the discontinued LLVM Phabricator instance.

[Clang] Add a compatibiliy warning for non-literals in constexpr.
ClosedPublic

Authored by cor3ntin on Mar 22 2022, 11:53 AM.

Diff Detail

Event Timeline

cor3ntin created this revision.Mar 22 2022, 11:53 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 22 2022, 11:53 AM
cor3ntin requested review of this revision.Mar 22 2022, 11:53 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 22 2022, 11:53 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
aaron.ballman added inline comments.Mar 22 2022, 12:30 PM
clang/include/clang/Basic/DiagnosticSemaKinds.td
2724

You should re-flow the whole diagnostic to the usual 80-col limit.

clang/lib/Sema/SemaDeclCXX.cpp
1903–1912

Formatting looks off here (I was expecting the else if on the same line as the curly brace, but maybe clang-format is being weird?)

1907
clang/test/SemaCXX/constant-expression-cxx2b.cpp
100

Shouldn't this be a cxx2b-warning instead?

cor3ntin updated this revision to Diff 417376.Mar 22 2022, 1:05 PM
  • fix formatting
  • use cxx2b in the test as per aaron request. This doesn't really matter as this part

of the test file is only executed in c++2b mode though.

clang/lib/Sema/SemaDeclCXX.cpp
1905

This seems to trigger even when the type is dependent:

<stdin>:1:36: warning: definition of a variable of non-literal type in a constexpr function is incompatible with C++ standards before C++2b [-Wpre-c++2b-compat]
auto qq = [](auto x) { decltype(x) n; };
                                   ^
1 warning generated.

This also seems to emit even when Kind is not Sema::CheckConstexprKind::Diagnose (unlike the static/thread_local case above). Is the CheckLiteralType logic not reusable for this?

clang/test/SemaCXX/constant-expression-cxx2b.cpp
220

Not sure how much we want the message in this case. The lambda is not marked constexpr (although it is implicitly constexpr in C++2b).

Note that we don't get a message for this:

auto qq = [] { return 0; static int x = 42; };
constexpr int qx = qq();

I am not sure how difficult it would be to come at this from the used-in-constant-evaluation side, but there is probably a larger class of messages in the same situation (so it would probably be a separate endeavour).

cor3ntin updated this revision to Diff 417534.Mar 23 2022, 1:59 AM
  • Check That the type is dependant
  • Only emit the warning when Kind == Sema::CheckConstexprKind::Diagnose
cor3ntin updated this revision to Diff 417535.Mar 23 2022, 1:59 AM

Formatting

cor3ntin added inline comments.Mar 23 2022, 2:03 AM
clang/lib/Sema/SemaDeclCXX.cpp
1905

You are right, thanks for noticing that, it was rather bogus.
The reason I'm not using CheckLiteralType is to avoid duplicating a diagnostics message, as CheckLiteralType doesn't allow us to pass parameter to the diagnostic message.

It leaves us with an uncovered scenario though: We do not emit the warning on template instantiation, and I don't think there is an easy way to do that.

I'm seeing precommit CI failures:

Failed Tests (1):

Clang :: SemaCXX/constant-expression-cxx2b.cpp
clang/lib/Sema/SemaDeclCXX.cpp
1905

The reason I'm not using CheckLiteralType is to avoid duplicating a diagnostics message, as CheckLiteralType doesn't allow us to pass parameter to the diagnostic message.

Huh?

static bool CheckLiteralType(Sema &SemaRef, Sema::CheckConstexprKind Kind,
                             SourceLocation Loc, QualType T, unsigned DiagID,
                             Ts &&...DiagArgs) {
  ...
}

I would hope DiagArgs should do exactly that? :-)

cor3ntin updated this revision to Diff 417586.Mar 23 2022, 6:18 AM
  • Fix test
  • Add a comment explaining why we do not use CheckLiteralType

@aaron,ballman I completely missed that...!

However, CheckLiteralType does a bunch of tests that we do not need
to diagnose *why* a type is not literal, which we do not care about here.

I tried to play around with the code and trying to use CheckLiteralType, or to modify
it for this scenario does not appear to me an improvenent, so I rather keep the change as is.

I also fixed the fail test, i think i accidentally removed a line from the test file...

  • Fix test
  • Add a comment explaining why we do not use CheckLiteralType

@aaron,ballman I completely missed that...!

No worries!

However, CheckLiteralType does a bunch of tests that we do not need
to diagnose *why* a type is not literal, which we do not care about here.

I think those kinds of diagnostics could be useful to a developer who has enabled this compat warning. It may not be immediately clear why the type is not a literal, and those notes can help some folks get to that answer much more quickly. Do you have some examples of whether the diagnostics clearly are distracting or useless?

clang/lib/Sema/SemaDeclCXX.cpp
1905

It leaves us with an uncovered scenario though: We do not emit the warning on template instantiation, and I don't think there is an easy way to do that.

I believe the code paths that lead us here all come from Sema::CheckConstexprFunctionDefinition() which is called from Sema::ActOnFinishFunctionBody() which seems to be called when instantiating templates in Sema::InstantiateFunctionDefinition(), so perhaps some more investigation is needed as to why we're not reaching this for template instantiations.

cor3ntin added inline comments.Mar 23 2022, 8:03 AM
clang/lib/Sema/SemaDeclCXX.cpp
1905

We could add something in addition of Sema::CheckConstexprKind::CheckValid and Sema::CheckConstexprKind::Diagnose, but

  • not for implicit lambdas, because we should not warn on lambdas that won't be constexpr
  • for explicit constexpr lambdas / functions, it would force us to call CheckConstexprFunctionDefinition on instanciation, which we currently don't do, and is not free for that one warning - and we would have to not-reemit the other warnings. It seems like quite a fair amount of work for a diagnostic not enabled by default.
cor3ntin updated this revision to Diff 417640.Mar 23 2022, 8:42 AM

After talks with Aaron, I'm using CheckLiteralType
in all cases, which adds notes about why a type
is not literal.

We still need to branch on CPlusPlus2b because the
diagnostics take a variable number of arguments,
and I'd rather not have more diagnostics.

clang/lib/Sema/SemaDeclCXX.cpp
1905–1907

Apply Aaron's suggestion (https://reviews.llvm.org/D122249?id=417358#inline-1168823) to the updated code.

cor3ntin updated this revision to Diff 419120.Mar 30 2022, 5:32 AM
  • Add a commebt to clarify which diagnostic message is being printed,

as per Aaron suggestion.

This LGTM (with minor comment). Please wait for Aaron to respond re: the handling of template instantiations.

clang/lib/Sema/SemaDeclCXX.cpp
1905

so perhaps some more investigation is needed as to why we're not reaching this for template instantiations.

@aaron.ballman, do you have any position on whether we want this investigation before moving forward with this patch?

1912–1913

Minor nit: The coding standards were updated (some time ago now) to recommend keeping if-else chains consistently braced or not-braced.

This revision is now accepted and ready to land.Mar 30 2022, 7:10 AM
cor3ntin updated this revision to Diff 419158.Mar 30 2022, 8:30 AM

Add braces

aaron.ballman accepted this revision.Mar 30 2022, 10:10 AM

LGTM

clang/lib/Sema/SemaDeclCXX.cpp
1905

so perhaps some more investigation is needed as to why we're not reaching this for template instantiations.

@aaron.ballman, do you have any position on whether we want this investigation before moving forward with this patch?

@hubert.reinterpretcast -- I think @cor3ntin did that investigation and found that we don't make it to this code path because of https://github.com/llvm/llvm-project/blob/main/clang/lib/Sema/SemaDecl.cpp#L14961.

I think this is incremental progress, and we can handle the template instantiation cases in a follow-up.

cor3ntin added a comment.EditedMar 30 2022, 10:18 AM

I thing to support the template case, we would have to modify TemplateDeclInstantiator::VisitVarDecl - which seems the best way to have a diagnostic without running through the whole function AST twice.
However, there is currently no check of any sort there so it would be a bit novel and odd!

Thank you both for the review

Edit: Longer comment

As per http://eel.is/c++draft/dcl.constexpr#7, the standard doesn't require to check the whole function on instantiation.
And there would be issue in doing that: all the warnings, emitted in other cases, would have to not be re-emitted

consider

constexpr void f(auto) {
if(false)    goto a;
a:;
}

void g() {
    f(0);
}

We do not want to warn twice here.
So we can't reuse the existing infrastructure.
The functions checking for constant-evaluations are not great either because they may ignore branches etc.
So I think the two options are

  • During instantiation warn fir non-literal var decls if they are in the context of a constexpr function
  • Make a new function like CheckConstexprFunctionBody for only that warning and go through the whole tree twice
  • Modify CheckConstexprFunctionBody with a new Sema::CheckConstexprKind thing for instantiation, which also goes through the whole tree twice.

None of these options seem great.

This revision was landed with ongoing or failed builds.Mar 30 2022, 10:19 AM
This revision was automatically updated to reflect the committed changes.