This is an archive of the discontinued LLVM Phabricator instance.

[Clang] Fix how we set the NumPositiveBits on an EnumDecl to cover the case of single enumerator with value zero or an empty enum
ClosedPublic

Authored by shafik on Jul 21 2022, 1:41 PM.

Details

Summary

Currently in Sema::ActOnEnumBody(...) when calculating NumPositiveBits we miss the case where there is only a single enumerator with value zero and the case of an empty enum. In both cases we end up with zero positive bits when in fact we need one bit to store the value zero.

This PR updates the calculation to account for these cases.

Relevant C++ standard quotes are [dcl.enum]p7:

.., If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0.

and [dcl.enum]p8:

... Otherwise, the values of the enumeration are the values representable by a hypothetical
integer type with minimal width M such that all enumerators can be represented. The width of the smallest
bit-field large enough to hold all the values of the enumeration type is M. ...

Diff Detail

Event Timeline

shafik created this revision.Jul 21 2022, 1:41 PM
Herald added a project: Restricted Project. · View Herald TranscriptJul 21 2022, 1:41 PM
shafik requested review of this revision.Jul 21 2022, 1:41 PM
shafik updated this revision to Diff 446702.Jul 21 2022, 9:21 PM

-Modified UBSan test to cover the empty enum case. I also refactored the test which was pretty clever but hard to work with as is.

erichkeane added inline comments.Jul 22 2022, 6:23 AM
clang/lib/Sema/SemaDecl.cpp
18907

What about:

std::max({NumPositiveBits, ActiveBits, 1}) (which uses the init-list version of std::max).

Also, not completely following this: BUT we don't need a positive bit IF we have a negative bit, right? So this is only necessary if BOTH NumPositiveBits and NumNegativeBits would have been 0?

18908

By coding standard, we need curleys around the 'else' as well if we have it on the 'if'.

compiler-rt/test/ubsan/TestCases/Misc/enum.cpp
10

can you do a test on:

enum ENeg { a = -1 }
?

That should ALSO have only a single bit.

shafik added inline comments.Jul 22 2022, 10:36 AM
clang/lib/Sema/SemaDecl.cpp
18907

Good call on the std::max alternative, I completely forgot about it.

If we look at the description of getNumPositiveBits() in Decl.h it says:

Returns the width in bits required to store all the non-negative enumerators of this enum.

So since 0 is non-negative we should update the positive bits. Make sense?

18908

Good catch.

My concern with the 'num positive bits' is really that enum EEmpty{};, enum EZero{ A =0 };, and enum ENeg {A = -1}; should all have 1 total bit of 'values'. So as long as THAT is true, I would expect this to be ok.

I fear that sticking to the 'non-negative' definition of NumPositiveBits might cause some oddball annoyances when trying to add this rule as a side-effect.

clang/lib/Sema/SemaDecl.cpp
18907

Hmm... ok then. As long as my test below ends up with the right values (that is, a value range of -1, 0 in the 1 case, and 0, 1 in the other).

shafik updated this revision to Diff 446997.Jul 22 2022, 3:46 PM
shafik marked 4 inline comments as done.
  • Addressed style comments
  • Changed to use initializer_list version of std::max
  • Added test to cover enum with -1 as sole enumerator value
compiler-rt/test/ubsan/TestCases/Misc/enum.cpp
10

Added this test and it indeed only has one bit.

erichkeane accepted this revision.Jul 25 2022, 6:40 AM

1 more question, otherwise this looks right to me.

compiler-rt/test/ubsan/TestCases/Misc/enum.cpp
27

What does THIS come from? What value is unknown? Shouldn't the -1 be fine?

This revision is now accepted and ready to land.Jul 25 2022, 6:40 AM
aaron.ballman requested changes to this revision.Jul 25 2022, 7:53 AM

Requesting changes until we have an answer for the testing situation.

clang/lib/Sema/SemaDecl.cpp
18904–18905

Add trailing punctuation, removes top-level const.

18913
compiler-rt/test/ubsan/TestCases/Misc/enum.cpp
27

+1, I'm surprised by the <unknown> there, but also... neither e1 nor e2 are of type enum EBool!

This revision now requires changes to proceed.Jul 25 2022, 7:53 AM

Also, this could use a release note. :-)

shafik added inline comments.Jul 25 2022, 12:08 PM
compiler-rt/test/ubsan/TestCases/Misc/enum.cpp
27

So it looks like clang knows that the only valid values for a bool enum is 0 or 1 and it will mask the value accordingly see godbolt for example using argc : https://godbolt.org/z/ceb9hPno9

So that would explain why the original test used a unsigned char* in order to prompt the diagnostic.

Looking into the ubsan diagnostic it looks like it treats bool as an unknown value, separate from integer and float. It is not clear to me why it does this but fixing that feels outside the scope of this change since this was part of the original test.

aaron.ballman accepted this revision.Jul 25 2022, 12:55 PM

LGTM (aside from some small nits that you can fix when landing).

compiler-rt/test/ubsan/TestCases/Misc/enum.cpp
27

Thanks for looking into it! I agree that fixing the ubsan diagnostic behavior is out of scope. That said, can you move the CHECK lines so that they come *after* the line of code being checked? I think that's what threw me for a loop regarding the EBool confusion I had.

This revision is now accepted and ready to land.Jul 25 2022, 12:55 PM
shafik updated this revision to Diff 447500.Jul 25 2022, 3:44 PM
shafik marked 5 inline comments as done.
  • Adding release note
  • Fixing comments
  • Removing top level const on local variable
  • Adjusting test so checks go after the line they are checking
shafik retitled this revision from [Clang] Fix how we set the NumPositiveBits on an E numDecl to cover the case of single enumerator with value zero or an empty enum to [Clang] Fix how we set the NumPositiveBits on an EnumDecl to cover the case of single enumerator with value zero or an empty enum.Jul 25 2022, 3:45 PM
shafik added inline comments.
compiler-rt/test/ubsan/TestCases/Misc/enum.cpp
27

Fixed, I did it like that to keep with the style of the original test but I see how it can be confusing.

This revision was landed with ongoing or failed builds.Jul 25 2022, 4:01 PM
This revision was automatically updated to reflect the committed changes.
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptJul 25 2022, 4:01 PM
Herald added a subscriber: Restricted Project. · View Herald Transcript