Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve signed-unsigned comparison issues in TypeSafeIndex #13486

Conversation

SeanCurtis-TRI
Copy link
Contributor

@SeanCurtis-TRI SeanCurtis-TRI commented Jun 2, 2020

When comparing an unsigned integral value with a TypeSafeIndex, we needed to account for the possibility that the unsigned integral value could contain larger values than the TypeSafeIndex. This was done by comparing the unsigned integer with the maximum int (the largest type safe
index value). This is intrinsically a signed-unsigned comparision which is problematic.

Instead, we cast the maximum int to the unsigned integral type. If the unsigned type is larger (more bits), the relationship will be fully preserved and unsigned values that are larger than can be represented will be detected.

If the unsigned type has fewer bits, the maximum int (01111.1111) will be truncated to a number consisting of all 1s, the maximum unsigned value of that number of bits. We are guaranteed that the unsigned value must be less than or equal to that truncated value which means its less than or
equal to the full maximum index.

In other words:

value <= static_cast(kMaxIndex) is always true if
sizeof(U) < sizeof(int).

resolves #13482


This change is Reviewable

@SeanCurtis-TRI
Copy link
Contributor Author

@drake-jenkins-bot linux-focal-unprovisioned-gcc-bazel-experimental-debug please

@jamiesnape
Copy link
Contributor

Lint is unhappy, but looks like the Focal build is going to be good otherwise.

Copy link
Contributor Author

@SeanCurtis-TRI SeanCurtis-TRI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+@jamiesnape for feature review.

I've fixed the lint. I"ll defer to you if you want to see the focal CI run again.

Reviewable status: LGTM missing from assignee jamiesnape, needs platform reviewer assigned, needs at least two assigned reviewers (waiting on @jamiesnape)

@SeanCurtis-TRI SeanCurtis-TRI force-pushed the PR_patch_type_safe_index_focal branch from 92e45ee to 38e712a Compare June 2, 2020 22:47
@SeanCurtis-TRI SeanCurtis-TRI changed the title Resolve signed-unsigned comparison issues Resolve signed-unsigned comparison issues in TypeSafeIndex Jun 2, 2020
Copy link
Contributor

@jamiesnape jamiesnape left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

I don't think we need to re-run Focal CI.

Reviewed 2 of 2 files at r1.
Reviewable status: needs platform reviewer assigned, needs at least two assigned reviewers

Copy link
Contributor Author

@SeanCurtis-TRI SeanCurtis-TRI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+@jwnimmer-tri for platform review, please.

Reviewable status: LGTM missing from assignee jwnimmer-tri(platform) (waiting on @jwnimmer-tri)

Copy link
Collaborator

@jwnimmer-tri jwnimmer-tri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

Reviewed 2 of 2 files at r1.
Reviewable status: 4 unresolved discussions (waiting on @SeanCurtis-TRI)


common/type_safe_index.h, line 486 at r1 (raw file):

  // Invocations provide a string explaining the origin of the bad value.
  static void AssertValid(int64_t index, const char* source) {
    if (index < 0 || index > std::numeric_limits<int>::max()) {

nit kMaxIndex?


common/type_safe_index.h, line 497 at r1 (raw file):

  // the current index value.
  void AssertNoOverflow(int delta, const char* source) const {
    if (delta > 0 && index_ > std::numeric_limits<int>::max() - delta) {

nit kMaxIndex?


common/type_safe_index.h, line 513 at r1 (raw file):

  int index_{kDefaultInvalid};

  // The largest representable index.

The casts of this constant to a smaller unsigned type (like uint16_t) earlier in this file are only valid if the maximum here is filled with 1's bits. So if someone changed this to be 1 billion as a round number, instead of 2^32-1, it would be very bad. How about a cautionary or explanatory comment to that effect here?


common/test/type_safe_index_test.cc, line 567 at r1 (raw file):

  EXPECT_FALSE(equal_index > big_unsigned);
  EXPECT_TRUE(equal_index >= big_unsigned);
}

nit It hurt my brain that the first new test case had "equal" stanza in the middle, but this case had "equal" stanza last.

@SeanCurtis-TRI SeanCurtis-TRI force-pushed the PR_patch_type_safe_index_focal branch 2 times, most recently from db372d8 to 52af7da Compare June 3, 2020 22:48
When comparing an unsigned integral value with a TypeSafeIndex, we
needed to account for the possibility that the unsigned integral value
could contain larger values than the TypeSafeIndex. This was done by
comparing the unsigned integer with the maximum int (the largest type safe
index value). This is intrinsically a signed-unsigned comparision which is
problematic.

Instead, we cast the maximum int to the unsigned integral type. If the
unsigned type is larger (more bits), the relationship will be fully
preserved and unsigned values that are larger than can be represented will
be detected.

If the unsigned type has fewer bits, the maximum int (01111.1111) will be
truncated to a number consisting of all 1s, the maximum unsigned value of
that number of bits. We are guaranteed that the unsigned _value_ must
be less than or equal to that truncated value which means its less than or
equal to the full maximum index.

In other words:

value <= static_cast<U>(kMaxIndex) is always *true* if
    sizeof(U) < sizeof(int).
@SeanCurtis-TRI SeanCurtis-TRI force-pushed the PR_patch_type_safe_index_focal branch from 52af7da to 9e83247 Compare June 3, 2020 22:49
Copy link
Contributor Author

@SeanCurtis-TRI SeanCurtis-TRI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments addressed.

Reviewable status: 1 unresolved discussion (waiting on @jamiesnape and @jwnimmer-tri)


common/type_safe_index.h, line 486 at r1 (raw file):

Previously, jwnimmer-tri (Jeremy Nimmer) wrote…

nit kMaxIndex?

Done


common/type_safe_index.h, line 497 at r1 (raw file):

Previously, jwnimmer-tri (Jeremy Nimmer) wrote…

nit kMaxIndex?

Done


common/type_safe_index.h, line 513 at r1 (raw file):

Previously, jwnimmer-tri (Jeremy Nimmer) wrote…

The casts of this constant to a smaller unsigned type (like uint16_t) earlier in this file are only valid if the maximum here is filled with 1's bits. So if someone changed this to be 1 billion as a round number, instead of 2^32-1, it would be very bad. How about a cautionary or explanatory comment to that effect here?

Done


common/test/type_safe_index_test.cc, line 567 at r1 (raw file):

Previously, jwnimmer-tri (Jeremy Nimmer) wrote…

nit It hurt my brain that the first new test case had "equal" stanza in the middle, but this case had "equal" stanza last.

Done

Copy link
Collaborator

@jwnimmer-tri jwnimmer-tri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 2 of 2 files at r2.
Reviewable status: :shipit: complete! all discussions resolved, LGTM from assignees jwnimmer-tri(platform),jamiesnape

@jwnimmer-tri jwnimmer-tri merged commit 73ebc52 into RobotLocomotion:master Jun 4, 2020
@SeanCurtis-TRI SeanCurtis-TRI deleted the PR_patch_type_safe_index_focal branch June 4, 2020 15:47
@jamiesnape jamiesnape removed their assignment Jun 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Comparison operators in TypeSafeIndex trigger GCC 9 sign-compare warning
3 participants