This is an archive of the discontinued LLVM Phabricator instance.

[Clang] Implement CWG2518 - static_assert(false)
ClosedPublic

Authored by cor3ntin on Feb 17 2023, 10:49 AM.

Details

Summary

This allows static_assert(false) to not be ill-formed
in template definitions.

This change is applied as a DR in all C++ modes.

Of notes, a couple of tests were relying of the eager nature
of static_assert

  • test/SemaTemplate/instantiation-dependence.cpp
  • test/SemaTemplate/instantiate-var-template.cpp

I don't know if the changes to static_assert
still allow that sort of tests to be expressed.

Diff Detail

Event Timeline

cor3ntin created this revision.Feb 17 2023, 10:49 AM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 17 2023, 10:49 AM
cor3ntin requested review of this revision.Feb 17 2023, 10:49 AM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 17 2023, 10:49 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
cor3ntin added a reviewer: Restricted Project.Feb 17 2023, 10:50 AM
cor3ntin updated this revision to Diff 498448.Feb 17 2023, 10:51 AM

Forgot to run clang-format

erichkeane added inline comments.
clang/lib/Sema/SemaDeclCXX.cpp
16790

Hmm... interesting way to calculate template depth, I wasn't aware of that one. Does this cause problems in 'a template caused another template to instantiate' sorta thing?

Also, is the "CPlusPlus" test here necessary?

clang/test/SemaTemplate/instantiate-var-template.cpp
34

You should be able to instantiate this template later, and probably what we now have to do. Also, 'dependent' is the spelling in this case, 'dependant' is something different :)

clang/test/SemaTemplate/instantiation-dependence.cpp
40–41

This one is probably a bigger pain to test, and I don't have a good idea.

Endill added a subscriber: Endill.Feb 17 2023, 10:59 AM

Thank you for the patch.
Any plans to backport this to 16.x branch?

cor3ntin added inline comments.Feb 17 2023, 11:05 AM
clang/lib/Sema/SemaDeclCXX.cpp
16790

It's what I came up with. Do you think there is a better way?
If you have suggestions for additional tests, I'm all ears!

The CplusPlus tests is just there to avoid unnecessary cycles in C mode. It probably doesn't do much of a difference.

clang/test/SemaTemplate/instantiate-var-template.cpp
34

I'm afraid doing though would defeat the intent of the test - it is after all named "InstantiationDependent"

Thank you for the patch.
Any plans to backport this to 16.x branch?

I think that's something we'd need to discuss with more folks but maybe a bit of time to see how this behaves on real code wouldn't hurt - even if I understand people have a keen interest in getting access to that sooner rather than later.

Thank you for the patch.
Any plans to backport this to 16.x branch?

I would not really want us to do that, the breaking change here is concerning, and I'd like this to spend time in trunk 'baking' a while.

clang/lib/Sema/SemaDeclCXX.cpp
16790

For "Zero" I usually would just check if the current decl-context is dependent. I'm not positive if that is 'correct' without more thought, though I would expect it to be? I think we make sure that is set up correctly everywhere. it would at least be a good check to see where we mess that up :)

clang/test/SemaTemplate/instantiate-var-template.cpp
34

Ah, yeah, you're right, it isn't clear what this is trying to test to me unfortunately. It might require some history digging. Same comment as below on the spelling.

cor3ntin updated this revision to Diff 498455.Feb 17 2023, 11:15 AM
  • Simplify how we check we are in a dependent context
  • Fix typos
cor3ntin marked 2 inline comments as done.Feb 17 2023, 11:17 AM
cor3ntin added inline comments.
clang/lib/Sema/SemaDeclCXX.cpp
16790

Yes, i feel stupid now, that was the obvious solution! Thanks

erichkeane accepted this revision.Feb 17 2023, 11:41 AM
erichkeane added inline comments.
clang/lib/Sema/SemaDeclCXX.cpp
16791

CplusPlus check is now not really beneficial? I'm not sure how much it matters now though that these are both just bit-loads.

This revision is now accepted and ready to land.Feb 17 2023, 11:41 AM
cor3ntin added inline comments.Feb 17 2023, 11:53 AM
clang/lib/Sema/SemaDeclCXX.cpp
16791

isDependentContext still does a bunch of work, recursively. I think we should keep it!

erichkeane added inline comments.Feb 17 2023, 12:01 PM
clang/lib/Sema/SemaDeclCXX.cpp
16791

Ah, Oh! You're right! It is the Expr class where this is free/just checking a bit. Disregard.

shafik added a subscriber: shafik.Feb 17 2023, 1:48 PM
shafik added inline comments.
clang/docs/ReleaseNotes.rst
76

I am assuming this will be updated eventually but that version is not the final one and the one that was approved can be found from: https://github.com/cplusplus/papers/issues/1251

and it here: https://cplusplus.github.io/CWG/issues/2518.html

I think a better wording would be would not be ill-formed when evaluated in the context of a template definition

clang/lib/Sema/SemaDeclCXX.cpp
16791

So isDependentContext() strictly means we are in a definition context? I think it makes sense after reading the implementation but not obvious at first. Maybe that would to document someplace?

erichkeane added inline comments.Feb 17 2023, 2:07 PM
clang/lib/Sema/SemaDeclCXX.cpp
16791

Shafik: (if you mean Template Definition Context), yes more or less. IIRC, that term was added to the standard after the clang Templates were implemented (I remember someone mentioning a big standards 'template' rewrite a few years back), so thats perhaps why it wasn't named that. You can get the same from an expression (isInstantiationDependent, isValueDependent, and isTypeDependent, the latter two having language equivalence, and the former just a catch-all).

Feel free to write up some documentation on this in the internals manual, as long as you properly explain the Template Definition Context/etc means.

rsmith added a subscriber: rsmith.Feb 17 2023, 3:05 PM
rsmith added inline comments.
clang/lib/Sema/SemaDeclCXX.cpp
16809–16827

I wonder if it's worth adding a custom diagnostic (eg, "this template cannot be instantiated: %0") for the case where we're in template instantiation and the expression is the bool literal false.

clang/test/SemaCXX/static-assert.cpp
53–59

I think we should also test the case where AlwaysFails is instantiated twice, and that we get one "static assertion failed" error per instantiation, rather than only one in total.

clang/test/SemaTemplate/instantiate-var-template.cpp
34

I think you can preserve the intent of this test by using the old array trick rather than a static assert:

int check[a<sizeof(sizeof(f(T())))> == 0 ? 1 : -1]; // expected-error {{array with a negative size}}
clang/test/SemaTemplate/instantiation-dependence.cpp
40–41

You can use the array trick here too.

cor3ntin updated this revision to Diff 498573.Feb 18 2023, 3:37 AM
cor3ntin marked 2 inline comments as done.
  • Reword Release note
  • Restore dependent instantiation tests using Richard's suggestion
  • Add additional test to check that diagnostics for static_assert are emitted once per instantiation.
cor3ntin added inline comments.Feb 21 2023, 7:14 AM
clang/docs/ReleaseNotes.rst
76

I'll tweak the comment a bit, thanks.
The link points to the yet-to-be-published core issue list so it will automatically point to the right thing eventually.

clang/lib/Sema/SemaDeclCXX.cpp
16809–16827

I'm not sure i see the motivation. Why would we want to special case false? The expression could also be an always false, never dependent expression

Thank you for the patch.
Any plans to backport this to 16.x branch?

I would not really want us to do that, the breaking change here is concerning, and I'd like this to spend time in trunk 'baking' a while.

+1, at this point the only things we should be backporting to 16.x are fixes for regressions or fixes for security concerns.

clang/lib/Sema/SemaDeclCXX.cpp
16809–16827

Richard may have different ideas in mind, but the motivation to me is code like:

template <typename Ty>
struct S {
  static_assert(false, "you have to use one of the valid specializations, not the primary template");
};

template <>
struct S<int> {
};

template <>
struct S<float> {    
};

int main() {
  S<int> s1;
  S<float> s2;
  S<double> s3;
}

Rather than telling the user the static_assert failed because false is not true, having a custom diagnostic might read better for users. GCC doesn't produce a custom diagnostic -- the behavior isn't terrible, but the "false evaluates to false" note is effectively just noise, too: https://godbolt.org/z/456bzWG7c

cor3ntin added inline comments.Feb 23 2023, 6:56 AM
clang/lib/Sema/SemaDeclCXX.cpp
16809–16827

OH. That makes sense now, thanks. I think I agree.
Interestingly, in gcc immediate calls are really immediate :) https://godbolt.org/z/b3vrzf4sj

cor3ntin updated this revision to Diff 499898.Feb 23 2023, 9:28 AM

I played with different diagnostics, we already specialize the
literal case, and I didn't find that not saying a static_assert
failed improved things.
Instead, printing, in addition of that diagnostic, an instantiation
stack provides additional useful information.
(The stack is only printed for the literal bool/integer case)

aaron.ballman added inline comments.Feb 27 2023, 11:12 AM
clang/lib/Sema/SemaDeclCXX.cpp
16809–16827

I think you should add this case as a test in dr25xx.cpp to show we get the behavior correct with specializations.

clang/test/CXX/drs/dr25xx.cpp
5–14

What do these tests have to do with this DR?

9

Why do we expect an error on this line in a #if 0 block??

clang/test/SemaCXX/static-assert.cpp
235–236
cor3ntin updated this revision to Diff 500866.Feb 27 2023, 11:59 AM
cor3ntin marked an inline comment as done.

Address Aaron's comments

cor3ntin added inline comments.Feb 27 2023, 12:02 PM
clang/test/CXX/drs/dr25xx.cpp
5–14

This dr is wild https://wiki.edg.com/pub/Wg21issaquah2023/StrawPolls/p2796r0.html
CWG merged the static_assert PR in the DR asserting that error should produce a diagnostics - note that there will probably be some follow ups
https://lists.isocpp.org/core/2023/02/13915.php

Here I'm testing a warning is emitted even if the build was already failed.

9

Oups, we don't

aaron.ballman added inline comments.Feb 27 2023, 2:17 PM
clang/test/CXX/drs/dr25xx.cpp
5–14

Ah, yeah, CWG2700 is sort of included in here.

I wonder if we should back out the #error/#warning tests and handle that explicitly as part of CWG2700 as that's going to supersede the changes from CWG2518.

cor3ntin updated this revision to Diff 500928.Feb 27 2023, 2:57 PM

Remove the #error / #warning tests

aaron.ballman accepted this revision.Feb 28 2023, 5:13 AM

LGTM!

clang/test/SemaTemplate/instantiate-var-template.cpp
36

:: gasps :: spurious whitespace change! :-D

This revision was landed with ongoing or failed builds.Feb 28 2023, 8:21 AM
This revision was automatically updated to reflect the committed changes.

Here's one change this patch causes on "real" code (invalid code, but something a user might try to compile): we see is a static_assert in gmock that now fails to report a useful error message: https://godbolt.org/z/sPr1PYT9d

Previously we saw error: static assertion failed due to requirement <...snip...>: This method does not take 2 arguments. Parenthesize all types with unprotected commas.. It still fails to compile, but without the nice error message.

My guess, although I haven't looked too closely, is that it's due to the static_assert(false) here: https://github.com/google/googletest/blob/2d4f208765af7fa376b878860a7677ecc0bc390a/googlemock/include/gmock/gmock-function-mocker.h#L149

@rupprecht

This expands to

template <typename T>
class MyMock {
  static_assert( static_assert( ::testing::tuple_size<typename ::testing::internal::Function< ::testing::internal::identity_t<int(double x, char c, bool cond)> >::ArgumentTuple>::value == 2, "This method does not take " "2" " arguments. Parenthesize all types with unprotected commas."); 
};

(some stuff omitted)

Not getting the error here is the expected outcome of this change.
The committee considered that these scenarios were far less likely than the scenario where people did not want a diagnostic.
Instantiating MyMock<int> T; produces the warning.

I think this code can be changed to something like that https://godbolt.org/z/6bcrYfdTf - to partially recover the previous diagnostics.
If however we find this change to disruptive, we should inform WG21.

If however we find this change to disruptive, we should inform WG21.

Thanks for the explanation, I think I understand the issue now. I got totally thrown off by the title -- it's not about literally writing static_assert(false), it's about deferring static_assert evaluation to template instantiations. Being able to write static_assert(false) (or any falsy constant expression) is just the common use case for this.

So possibly the most trivial example of something that used to break, but now builds:

template <typename>
void Fail() {
    static_assert(false, "my assertion failed");
}

... but will still fail as soon as you invoke Fail<any_type>() somewhere.

It doesn't look like there's a lot of impact from this, and the breakages are corner cases like this. It might be worth mentioning this one case to WG21 but I'm not sure what I would change about the wording.

clang/www/cxx_dr_status.html
14915

Is it possible to make this link point to https://cplusplus.github.io/CWG/issues/2518.html? This link is inaccessible to anyone not on ISO.

If however we find this change to disruptive, we should inform WG21.

Thanks for the explanation, I think I understand the issue now. I got totally thrown off by the title -- it's not about literally writing static_assert(false), it's about deferring static_assert evaluation to template instantiations. Being able to write static_assert(false) (or any falsy constant expression) is just the common use case for this.

So possibly the most trivial example of something that used to break, but now builds:

template <typename>
void Fail() {
    static_assert(false, "my assertion failed");
}

... but will still fail as soon as you invoke Fail<any_type>() somewhere.

It doesn't look like there's a lot of impact from this, and the breakages are corner cases like this. It might be worth mentioning this one case to WG21 but I'm not sure what I would change about the wording.

Will do !

clang/www/cxx_dr_status.html
14915

It will get updated whenever a new core issue list gets published, which will hopefully be soon-ish.
This file is generated automatically so changing the link might actually not be trivial