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

Safe numerics #125

Merged
merged 22 commits into from
Aug 30, 2024
Merged

Safe numerics #125

merged 22 commits into from
Aug 30, 2024

Commits on Aug 23, 2023

  1. Move checked_cast to flux::num namespace

    ...in the <flux/core/numeric.hpp> header
    tcbrindle committed Aug 23, 2023
    Configuration menu
    Copy the full SHA
    a40e4fd View commit details
    Browse the repository at this point in the history

Commits on Aug 30, 2023

  1. Vastly expand our suite of numeric functions

    This PR does a whole lot of things.
    
    Firstly, we define some concepts for integers. Specifically, the `flux::num::integral` concept is satisfied by any `std::integral` type *except* `bool`, `char`, `wchar_t` and the various `charN_t`s. We also have corresponding `signed_integral` and `unsigned_integral` concepts.
    
    Next, we define some functions which perform *unchecked* integer operations, namely:
    
     * `unchecked_add`
     * `unchecked_sub`
     * `unchecked_mul`
     * `unchecked_div`
     * `unchecked_mod`
    
    These work call the built-in operators (and so can cause UB for signed types), but require both arguments to be the same type, and cast the result back to their argument type -- that is, there is no promotion, so `unchecked_add(short, short)` returns a `short`, not an `int`. The intention is that these can be used in places where signed UB can allow extra optimisations, and explicitly acknowledge that you're doing something dangerous.
    
    Next is a set of wrapping functions:
    
    * `wrapping_add`
    * `wrapping_sub`
    * `wrapping_mul`
    
    These work by casting their arguments to an unsigned type, performing the operation, and casting back to the starting type. They never cause UB, and can be used to specifically document that you want wrapping semantics.
    
    Next is a set of functions which check whether overflow occurred:
    
     * `overflowing_add`
     * `overflowing_sub`
     * `overflowing_mul`
    
    These return a `(T, bool)` pair which safely performs the operation (as if by wrapping) and reports whether overflow occurred. They use compiler builtins in GCC and Clang. These work for unsigned types as well as signed types, so you can test whether your size_t overflowed.
    
    Then we have a set of checked arithmetic functions:
    
    * `checked_add`
    * `checked_sub`
    * `checked_mul`
    * `checked_div`
    * `checked_mod`
    
    These raise a `flux::runtime_error` if overflow occurs (or division by zero for the last two functions), regardless of the compiled checking policy.
    
    Finally, we have
    
    * `add`
    * `sub`
    * `mul`
    * `div`
    * `mod`
    
    These perform overflow/divide-by-zero checks according to the configured policies. By default, they trap on overflow in debug mode or wrap in release mode.
    
    Phew!
    tcbrindle committed Aug 30, 2023
    Configuration menu
    Copy the full SHA
    d7d43e6 View commit details
    Browse the repository at this point in the history

Commits on Aug 31, 2023

  1. Add [un]checked shift functions

    This adds
     * num::unchecked_shl
     * num::unchecked_shr
     * num::checked_shl
     * num::checked_shr
     * num::shl
     * num::shr
    
    Which perform left and right bitshift operations either explicitly without undefined behaviour checking, or which check whether the shift amount is out of bounds
    tcbrindle committed Aug 31, 2023
    Configuration menu
    Copy the full SHA
    3a13521 View commit details
    Browse the repository at this point in the history
  2. Switch the names of two test files

    I got them the wrong way round
    tcbrindle committed Aug 31, 2023
    Configuration menu
    Copy the full SHA
    fc83c85 View commit details
    Browse the repository at this point in the history
  3. Configuration menu
    Copy the full SHA
    bcae928 View commit details
    Browse the repository at this point in the history

Commits on Aug 21, 2024

  1. Configuration menu
    Copy the full SHA
    bad1add View commit details
    Browse the repository at this point in the history
  2. Don't try testing extended integer types

    Results seem to be too inconsistent on CI
    tcbrindle committed Aug 21, 2024
    Configuration menu
    Copy the full SHA
    8d9f186 View commit details
    Browse the repository at this point in the history
  3. Configuration menu
    Copy the full SHA
    72368c5 View commit details
    Browse the repository at this point in the history

Commits on Aug 22, 2024

  1. Move checked_pow() to cartesian_base.hpp

    ...where it's used.
    
    One day we might make this public again, but for now let's just have it as an implementation detail.
    tcbrindle committed Aug 22, 2024
    Configuration menu
    Copy the full SHA
    93bdec9 View commit details
    Browse the repository at this point in the history
  2. Simplify numeric concepts

    Originally the idea was that I wanted to allow user-defined types, e.g `enum class my_int : int {}` to be declared as "extended integers", and be used with the Flux numeric functions.
    
    But YAGNI (at least for now) so let's keep things simple
    tcbrindle committed Aug 22, 2024
    Configuration menu
    Copy the full SHA
    c975597 View commit details
    Browse the repository at this point in the history
  3. Simplify overflowing_cast

    Use the new C++20 std::in_range(), which seems to generate better code with GCC (and exactly the same with Clang)
    tcbrindle committed Aug 22, 2024
    Configuration menu
    Copy the full SHA
    503dc4c View commit details
    Browse the repository at this point in the history
  4. Add missing header

    tcbrindle committed Aug 22, 2024
    Configuration menu
    Copy the full SHA
    7982c1e View commit details
    Browse the repository at this point in the history
  5. Configuration menu
    Copy the full SHA
    56d1bf4 View commit details
    Browse the repository at this point in the history
  6. Configuration menu
    Copy the full SHA
    28dba23 View commit details
    Browse the repository at this point in the history

Commits on Aug 23, 2024

  1. Add num::cast function and associated policy

    This function either calls `num::checked_cast` or `num::unchecked_cast` depending on the currently configured integer cast policy. In checked mode, lossy casts -- that is, those that result in a change in value -- will raise a runtime error, whereas in unchecked mode these will silently use the incorrect value.
    tcbrindle committed Aug 23, 2024
    Configuration menu
    Copy the full SHA
    caeaec7 View commit details
    Browse the repository at this point in the history
  2. Use num::cast where appropriate...

    ...rather than checked_cast.
    tcbrindle committed Aug 23, 2024
    Configuration menu
    Copy the full SHA
    1f3ce4b View commit details
    Browse the repository at this point in the history
  3. Use num::integral rather than std::integral...

    ...in function arguments, because I don't think flux::chunk(seq, 'a') is something we should allow.
    tcbrindle committed Aug 23, 2024
    Configuration menu
    Copy the full SHA
    5d2dce5 View commit details
    Browse the repository at this point in the history

Commits on Aug 28, 2024

  1. Used safe arithmetic in sum() and product()

    ...at least when using integral types.
    tcbrindle committed Aug 28, 2024
    Configuration menu
    Copy the full SHA
    5996620 View commit details
    Browse the repository at this point in the history
  2. Work around libc++18 std::invoke() bug

    It seems like std::invoke() in libc++18 (but not previous versions) doesn't like default function parameters in certain contexts.
    
    Reported as llvm/llvm-project#106428
    tcbrindle committed Aug 28, 2024
    Configuration menu
    Copy the full SHA
    9388588 View commit details
    Browse the repository at this point in the history

Commits on Aug 29, 2024

  1. Add various signed integer negation functions

    Specifically,
    
     * num::unchecked_neg
     * num::wrapping_neg
     * num::overflowing_neg
     * num::checked_neg
     * num::neg
    
    Each call to `xxx_neg(val)` is semantically the same as `xxx_sub(0, val)`, but is shorter to spell and may generate better code.
    tcbrindle committed Aug 29, 2024
    Configuration menu
    Copy the full SHA
    d5ec687 View commit details
    Browse the repository at this point in the history
  2. Fix constexpr checked_neg() with MSVC

    It seems like MSVC doesn't consider -INT_MIN to overflow even in constexpr mode.
    
    This version generates better code with Clang and GCC anyway, so that's good.
    tcbrindle committed Aug 29, 2024
    Configuration menu
    Copy the full SHA
    90da1b5 View commit details
    Browse the repository at this point in the history
  3. Configuration menu
    Copy the full SHA
    5ad5023 View commit details
    Browse the repository at this point in the history