Skip to content

Reducing Warnings - Safe Integral Comparisons #960

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

Merged
merged 4 commits into from
Apr 7, 2022

Conversation

eramongodb
Copy link
Contributor

This is part of a series of PRs intended to reduce the set of warnings currently being observed when building the C driver.

This PR introduces a set of utilities modeled after the Safe Integral Comparisons proposal for the C++ standard library, which was accepted and merged into the C++20 standard. These utilities are extremely useful in addressing warnings involving comparisons or conversions between integers with different signedness and sizes.

The interface proposed in the original paper relies on type deduction to select appropriate behavior according to the signedness and size of the given arguments:

A a = ...
B b = ...
// no need for any cast since std::cmp_less is taking care of everything
if(std::cmp_less(a,b)){
  // do X
} else {
  // do Y
}

As C does not have type deduction required to support this interface, two major compromises are made instead:

  1. All functions must explicitly specify the signedness of the provided argument(s).
  2. All arguments are promoted to either int64_t or uint64_t as specified by (1) before further analysis.

Therefore, the equivalent code to the above example given A = int16_t and B = uint32_t looks like the following:

int16_t a = ...
uint32_t b = ...

if (bson_cmp_less_su (a, b)) {
  /* ... */
} else {
  /* ... */
}

where the _su suffix indicates a is signed and b is unsigned.

The set of supported types is also limited to the following:

  • signed and unsigned char, short, int, long, and long long (but not plain char).
  • signed and unsigned fixed width integers of size 8, 16, 32, and 64.
  • ssize_t and size_t.

More types may be added if needed, so long as for the given type T, sizeof (T) <= sizeof (uint64_t) is true and its representable range is known and defined with corresponding *_MIN and *_MAX macros.

To support the std::in_range set of functions even on C90 (where the necessary <limits.h> and <stdint.h> macros may not be defined), the corresponding set of numeric limit macros have been added to bson-compat.h, including those for ssize_t (note: SSIZE_MIN is normally not defined even by the POSIX standard). The preprocessor block conditioned on !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L may be removed once the minimum C standard for the C driver is bumped to C99 or newer.

@eramongodb eramongodb self-assigned this Mar 24, 2022
Copy link
Contributor

@rcsanchez97 rcsanchez97 left a comment

Choose a reason for hiding this comment

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

I have made some suggested changes based on what appear to be missing unsigned specifiers. Though, I may be mistaken so I am going to go ahead and approve the PR and leave it up to you to decide whether my suggestions are appropriate.

BSON_ASSERT (!bson_cmp_less_ss (1, -1));
BSON_ASSERT (!bson_cmp_less_ss (1, 1));

BSON_ASSERT (!bson_cmp_less_uu (0u, 0));
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
BSON_ASSERT (!bson_cmp_less_uu (0u, 0));
BSON_ASSERT (!bson_cmp_less_uu (0u, 0u));

BSON_ASSERT (bson_cmp_greater_ss (1, -1));
BSON_ASSERT (!bson_cmp_greater_ss (1, 1));

BSON_ASSERT (!bson_cmp_greater_uu (0u, 0));
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
BSON_ASSERT (!bson_cmp_greater_uu (0u, 0));
BSON_ASSERT (!bson_cmp_greater_uu (0u, 0u));

BSON_ASSERT (!bson_cmp_less_equal_ss (1, -1));
BSON_ASSERT (bson_cmp_less_equal_ss (1, 1));

BSON_ASSERT (bson_cmp_less_equal_uu (0u, 0));
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
BSON_ASSERT (bson_cmp_less_equal_uu (0u, 0));
BSON_ASSERT (bson_cmp_less_equal_uu (0u, 0u));

BSON_ASSERT (bson_cmp_greater_equal_ss (1, -1));
BSON_ASSERT (bson_cmp_greater_equal_ss (1, 1));

BSON_ASSERT (bson_cmp_greater_equal_uu (0u, 0));
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
BSON_ASSERT (bson_cmp_greater_equal_uu (0u, 0));
BSON_ASSERT (bson_cmp_greater_equal_uu (0u, 0u));


BSON_ASSERT (bson_in_range_unsigned (int32_t, 0u));
BSON_ASSERT (bson_in_range_unsigned (int32_t, (uint64_t) int32_max));
BSON_ASSERT (!bson_in_range_unsigned (int32_t, (uint64_t) (int32_max + 1)));
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
BSON_ASSERT (!bson_in_range_unsigned (int32_t, (uint64_t) (int32_max + 1)));
BSON_ASSERT (!bson_in_range_unsigned (int32_t, (uint64_t) (int32_max + 1u)));

Copy link
Contributor Author

Choose a reason for hiding this comment

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

int32_max is an int64_t (signed), so a signed integer literal is appropriate here.

Copy link
Contributor Author

@eramongodb eramongodb left a comment

Choose a reason for hiding this comment

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

Passing 0 to unsigned parameter is benign since 0 is guaranteed to be representable regardless of signedness, but for consistency, I agree it would be good to update the literals with unsigned suffixes. (Hurray for copy-paste errors...)


BSON_ASSERT (bson_in_range_unsigned (int32_t, 0u));
BSON_ASSERT (bson_in_range_unsigned (int32_t, (uint64_t) int32_max));
BSON_ASSERT (!bson_in_range_unsigned (int32_t, (uint64_t) (int32_max + 1)));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

int32_max is an int64_t (signed), so a signed integer literal is appropriate here.

Copy link
Collaborator

@kevinAlbs kevinAlbs left a comment

Choose a reason for hiding this comment

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

LGTM. Nicely done. I think bson-cmp.h is going to be useful. I have two comments, but nothing to block merge.

Comment on lines 149 to 150
return bson_cmp_greater_equal_uu (value, 0u) && \
bson_cmp_less_equal_uu (value, max); \
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return bson_cmp_greater_equal_uu (value, 0u) && \
bson_cmp_less_equal_uu (value, max); \
return bson_cmp_less_equal_uu (value, max); \

Copy link
Contributor Author

Choose a reason for hiding this comment

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

SGTM.

BSON_ASSERT (!bson_in_range_signed (int8_t, int8_max + 1));

BSON_ASSERT (bson_in_range_unsigned (int8_t, 0u));
BSON_ASSERT (bson_in_range_unsigned (int8_t, (uint64_t) int8_max));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggest removing unnecessary cast to uint64_t for consistency with other calls. Here and below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These casts are necesary, as the intN_min and intN_max variables are signed integers. Without the casts, the arguments will produce implicit sign conversion warnings from int64_t to uint64_t.

@eramongodb eramongodb merged commit 3032eeb into mongodb:master Apr 7, 2022
@eramongodb eramongodb deleted the cdriver-warnings branch April 7, 2022 18:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants