Title
The simd::basic_mask(bool) overload needs to be more constrained
Status
new
Section
[simd.mask.ctor]
Submitter
Matthias Kretz

Created on 2025-09-24.00:00:00 last changed 2 days ago

Messages

Date: 2025-10-10.15:45:30

Proposed resolution:

This wording is relative to N5014.

  1. Modify [simd.mask.overview], class template basic_mask synopsis, as indicated:

    namespace std::simd {
      template<size_t Bytes, class Abi> class basic_mask {
      public:
        […]
        
        constexpr basic_mask() noexcept = default;
        
        // [simd.mask.ctor], basic_mask constructors
        constexpr explicit basic_mask(same_as<value_type> auto) noexcept;
        template<size_t UBytes, class UAbi>
          constexpr explicit basic_mask(const basic_mask<UBytes, UAbi>&) noexcept;
        template<class G>
          constexpr explicit basic_mask(G&& gen) noexcept;
        template<same_as<bitset<size()>> T>
          constexpr basic_mask(const Tbitset<size()>& b) noexcept;
        template<unsigned_integral T> requires (!same_as<T, value_type>)
          constexpr explicit basic_mask(Tunsigned_integral auto val) noexcept;
        
        […]
      };
    }
    
  2. Modify [simd.mask.ctor] as indicated:

    constexpr explicit basic_mask(same_as<value_type> auto x) noexcept;
    

    -1- Effects: Initializes each element with `x`.

    […]
    template<same_as<bitset<size()>> T>
      constexpr basic_mask(const Tbitset<size()>& b) noexcept;
    

    -7- Effects: Initializes the ith element with b[i] for all i in the range `[0, size())`.

    template<unsigned_integral T> requires (!same_as<T, value_type>)
      constexpr explicit basic_mask(Tunsigned_integral auto val) noexcept;
    

    -8- Effects: Initializes the first M elements to the corresponding bit values in `val`, […]

Date: 2025-10-15.00:00:00

[ 2025-10-06; Matthias Kretz improves wording after reflector discussion ]

Date: 2025-10-10.15:45:30

[simd.mask.ctor] defines the overloads `basic_mask(bool)` and `basic_mask(unsigned_integral auto)`. This leads to the following pitfall:

auto g0() {
  unsigned short k = 0xf;
  return simd::mask<float, 8>(k); // mov eax, 15
}

auto g1() {
  unsigned short k = 0xf;
  return simd::mask<float, 8>(k >> 1); // mov eax, -1 ⚠️
}

auto g2() {
  unsigned int k = 0xf;
  return simd::mask<float, 8>(k >> 1); // mov eax, 7
}

In `g1`, `k` is promoted to `int`, shifted and then passed to the mask constructor. Instead of failing, `int(0x7)` is converted to `bool` and the mask thus initialized to all `true`.

Also consider:

  1. simd::mask<float>(true_type());

  2. unsigned_integral<bool> is `true` => same_as<bool> auto instead of 'bool' makes the overload set ambiguous

  3. `float` is convertible to `bool`, thus simd::mask<float>(1.f) continues to compile

This wording is relative to N5014.

  1. Modify [simd.mask.overview], class template basic_mask synopsis, as indicated:

    namespace std::simd {
      template<size_t Bytes, class Abi> class basic_mask {
      public:
        […]
        
        constexpr basic_mask() noexcept = default;
        
        // [simd.mask.ctor], basic_mask constructors
        constexpr explicit basic_mask(value_type) noexcept;
        template<size_t UBytes, class UAbi>
          constexpr explicit basic_mask(const basic_mask<UBytes, UAbi>&) noexcept;
        template<class G>
          constexpr explicit basic_mask(G&& gen) noexcept;
        constexpr basic_mask(const bitset<size()>& b) noexcept;
        constexpr explicit basic_mask(unsigned_integral auto val) noexcept;
        basic_mask(signed_integral auto) = delete;
        
        […]
      };
    }
    
History
Date User Action Args
2025-10-10 15:45:30adminsetmessages: + msg15137
2025-09-26 16:59:48adminsetmessages: + msg15082
2025-09-24 00:00:00admincreate