This is an archive of the discontinued LLVM Phabricator instance.

[libc++] `bitset::operator[] const` should return bool
ClosedPublic

Authored by philnik on Mar 19 2022, 7:13 PM.

Diff Detail

Event Timeline

philnik created this revision.Mar 19 2022, 7:13 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 19 2022, 7:13 PM
Herald added a subscriber: arphaman. · View Herald Transcript
philnik requested review of this revision.Mar 19 2022, 7:13 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 19 2022, 7:13 PM
Herald added a reviewer: Restricted Project. · View Herald Transcript
Mordante added inline comments.Mar 20 2022, 6:06 AM
libcxx/include/bitset
718–723

Changing the return type is an ABI break. So we can only make this change in the unstable ABI.

libcxx/test/std/utilities/template.bitset/bitset.members/index_const.pass.cpp
28

FYI you can use ASSERT_SAME_TYPE.

28

Since the operator returns different types, maybe it's good to add an explicit type test in
libcxx/test/std/utilities/template.bitset/bitset.members/index.pass.cpp too.

philnik updated this revision to Diff 416779.Mar 20 2022, 6:44 AM
philnik marked 3 inline comments as done.
  • Address comments

Thanks for the update, only a few small issues remain.

libcxx/include/__config
118

Let's make clear it's about the const overload.

libcxx/test/std/utilities/template.bitset/bitset.members/index_const.pass.cpp
29

Typically we just use the ABI name in our tests, for example in test/std/re/re.const/re.synopt/syntax_option_type.pass.cpp

#ifdef _LIBCPP_ABI_REGEX_CONSTANTS_NONZERO  // https://llvm.org/PR35967
    assert(std::regex_constants::ECMAScript != 0);
#else
    assert(std::regex_constants::ECMAScript == 0);
#endif

(In the WIP script to test against internal macro's I've whitelisted _LIBCPP_ABI_*.)

I actually think the test should be slightly different

#if !defined(_LIBCPP_VERSION) || defined(_LIBCPP_ABI_REGEX_CONSTANTS_NONZERO)  // https://llvm.org/PR35967
    assert(std::regex_constants::ECMAScript != 0);
#else
    assert(std::regex_constants::ECMAScript == 0);
#endif
philnik updated this revision to Diff 416784.Mar 20 2022, 7:44 AM
philnik marked 2 inline comments as done.
  • Address comments
jloser added a subscriber: jloser.Mar 20 2022, 2:02 PM
jloser added inline comments.
libcxx/test/std/utilities/template.bitset/bitset.members/index_const.pass.cpp
15

I don't think this include is needed since we're using ASSERT_SAME_TYPE macro now.

What an annoying "extension". The intent is good, however it results in our implementation being clearly non-conforming. I'm in favour of this, and it's a shame that it has to be guarded behind an ABI check.

Can you please add a test that checks for:

typename Container::const_reference cr0 = c[0];
c[0] = 1;
assert(cr0 == 1);

at runtime (and a corresponding test case for when const_reference is bool)?

philnik updated this revision to Diff 418793.Mar 28 2022, 11:03 PM
philnik marked an inline comment as done.
  • Address comments
philnik updated this revision to Diff 418810.Mar 29 2022, 12:38 AM
  • Try to fix CI
ldionne accepted this revision.Mar 29 2022, 8:24 AM

LGTM with changes, thanks!

libcxx/include/__config
117
libcxx/test/std/utilities/template.bitset/bitset.members/index_const.pass.cpp
46–55

Can you add the same test to the non-const operator[] (but you'll only have one assert)?

Also, I'd recommend against using #endif // comment since this block is so short -- in this case it's even wrong.

This revision is now accepted and ready to land.Mar 29 2022, 8:24 AM
ldionne requested changes to this revision.Mar 29 2022, 10:58 AM

It looks like we have the same issue in vector<bool> -- can we also fix it there? I think we only need one macro for both, so I'd make that change in this patch. Sorry for the churn.

This revision now requires changes to proceed.Mar 29 2022, 10:58 AM

It looks like we have the same issue in vector<bool> -- can we also fix it there? I think we only need one macro for both, so I'd make that change in this patch. Sorry for the churn.

vector<bool> returns a reference` or a const_reference as expected (https://en.cppreference.com/w/cpp/container/vector/operator_at)
Or am I missing something?

philnik updated this revision to Diff 419671.Apr 1 2022, 1:53 AM
  • Address comments

@Mordante I didn't see it either. const_reference should be defined as bool.

philnik updated this revision to Diff 419682.Apr 1 2022, 2:24 AM
philnik marked 2 inline comments as done.
  • Address more comments
var-const accepted this revision as: var-const.Apr 1 2022, 9:37 PM
var-const added inline comments.
libcxx/test/std/containers/sequences/vector.bool/const_reference.pass.cpp
17

Optional nit: since this test isn't constexpr, it doesn't need to return anything -- unless you expect it to become constexpr at some future point?

libcxx/test/std/utilities/template.bitset/bitset.members/index_const.pass.cpp
46–55

This comment doesn't seem to be addressed.

philnik updated this revision to Diff 419935.Apr 1 2022, 10:24 PM
philnik marked 2 inline comments as done.
  • Address comments
libcxx/test/std/containers/sequences/vector.bool/const_reference.pass.cpp
17

This test should become constexpr once we implement P1004R2.

libcxx/test/std/utilities/template.bitset/bitset.members/index_const.pass.cpp
46–55

Thanks! I completely forgot to remove the comment.

var-const added inline comments.Apr 1 2022, 10:51 PM
libcxx/test/std/containers/sequences/vector.bool/const_reference.pass.cpp
17

In that case, can you please add a TODO?

philnik added inline comments.Apr 1 2022, 11:10 PM
libcxx/test/std/containers/sequences/vector.bool/const_reference.pass.cpp
17

I don't think adding a TODO makes a lot of sense here. When implementing P1004R2 almost every single file inside vector.bool and vector have to be changed anyways.

ldionne accepted this revision.Apr 6 2022, 11:03 AM

LGTM once CI is passing. Please ping me again if it ends up being more complicated than just removing the now redundant insert() method. Thanks for the cleanup!

libcxx/include/vector
2088

To fix the CI issue, I think you'll need to remove the additional insert(...) method we have that takes a const_reference. It's not in the spec, and it was probably just provided to make it work with our non-conforming const_reference type.

You should also survey for other places where we take a const_reference and make sure that those methods are all part of the spec -- it's possible that we have other cases like this, and we might not catch them (I don't know how good our coverage is for std::vector<bool>).

libcxx/test/std/utilities/template.bitset/bitset.members/index.pass.cpp
61–64
This revision is now accepted and ready to land.Apr 6 2022, 11:03 AM
philnik updated this revision to Diff 421150.Apr 7 2022, 3:40 AM
  • Remove insert overload

@ldionne I just removed the insert overload, but it seems it was never defined, so I remove it in ABIv1 too.

philnik updated this revision to Diff 421701.Apr 9 2022, 12:48 AM
  • Try to fix CI
ldionne accepted this revision.Apr 13 2022, 7:21 AM

LGTM, but I would really like to investigate removing const_reference from bitset altogether, since there isn't one in the Standard. It's a rather bad API to provide a typedef called const_reference that isn't a reference, and in fact it leads to the very confusion we are removing an extension for. I'd rather break people's source (considering there probably isn't too many users of bitset::const_reference out there) than give them an API that is both non-standard and error-prone.

This revision was landed with ongoing or failed builds.Apr 13 2022, 1:55 PM
This revision was automatically updated to reflect the committed changes.
rsmith added a subscriber: rsmith.Apr 14 2022, 11:01 AM

Is it intentional that this leaves std::vector<bool>::const_iterator::reference as the class type? We're seeing some breakage in absl due to that.

We're also seeing some breakage due to the result of std::bitset::operator[]() const no longer being convertible to std::bitset::const_reference, which would at least have been easier to understand if the const_reference typedef were removed entirely.

Is it intentional that this leaves std::vector<bool>::const_iterator::reference as the class type? We're seeing some breakage in absl due to that.

FWIW, the broken code looks roughly like this:

struct NumberLike {
  // ...
  template<typename T> NumberLike(T) requires std::is_arithmetic_v<T>;
  NumberLike(std::vector<bool>::reference);
  NumberLike(std::vector<bool>::const_reference);
}
void Consume(NumberLike);
template<typename Container>
void ConsumeAll(const Container &c) {
  for (const auto &v : c) Consume(v);
}

This works if vector<bool>::const_reference and vector<bool>::const_iterator::reference are the same type, but if the iterator produces the class type but vector<bool>::const_reference is simply bool then the Consume call fails.

Is it intentional that this leaves std::vector<bool>::const_iterator::reference as the class type? We're seeing some breakage in absl due to that.

We're also seeing some breakage due to the result of std::bitset::operator[]() const no longer being convertible to std::bitset::const_reference, which would at least have been easier to understand if the const_reference typedef were removed entirely.

I just uploaded D123851 for the vector part. I'm planning to look into removing bitset::const_reference, but since it shouldn't exist, nobody should use it anyways.