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

<iostream>: Bitmasks should tolerate unconstrained operator overloads #292

Open
StephanTLavavej opened this issue Nov 14, 2019 · 1 comment
Labels
bug Something isn't working

Comments

@StephanTLavavej
Copy link
Member

StephanTLavavej commented Nov 14, 2019

This should compile, but doesn't:

C:\Temp>type meow.cpp
template <typename T, typename U> void operator&(T&&, U&&) = delete;
template <typename T, typename U> void operator|(T&&, U&&) = delete;
template <typename T, typename U> void operator^(T&&, U&&) = delete;
template <typename T, typename U> void operator&=(T&&, U&&) = delete;
template <typename T, typename U> void operator|=(T&&, U&&) = delete;
template <typename T, typename U> void operator^=(T&&, U&&) = delete;
template <typename T> void operator~(T&&) = delete;

#include <iostream>

int main() {
    std::cout << 1729 << "\n";
}

C:\Temp>cl /EHsc /nologo /W4 meow.cpp
meow.cpp
S:\msvc\binaries\amd64chk\inc\xlocale(1059): error C2280: 'void operator &<const std::_Codecvt_mode&,std::_Codecvt_mode>(T,U &&)': attempting to reference a deleted function
        with
        [
            T=const std::_Codecvt_mode &,
            U=std::_Codecvt_mode
        ]
meow.cpp(1): note: see declaration of 'operator &'
S:\msvc\binaries\amd64chk\inc\xlocale(1125): error C2280: 'void operator &<const std::_Codecvt_mode&,std::_Codecvt_mode>(T,U &&)': attempting to reference a deleted function
        with
        [
            T=const std::_Codecvt_mode &,
            U=std::_Codecvt_mode
        ]
meow.cpp(1): note: see declaration of 'operator &'
[...19 more errors...]

This happens when the STL uses an enum as a bitmask:

enum _Codecvt_mode { _Consume_header = 4, _Generate_header = 2 };

if ((_Mode & _Consume_header) != 0 && _Ch == 0xfeffu) { // drop header and retry

without defining dedicated bitmask operators:

STL/stl/inc/type_traits

Lines 2048 to 2089 in 58bb49d

#define _BITMASK_OPS(_BITMASK) \
_NODISCARD constexpr _BITMASK operator&(_BITMASK _Left, _BITMASK _Right) noexcept { /* return _Left & _Right */ \
using _IntTy = _STD underlying_type_t<_BITMASK>; \
return static_cast<_BITMASK>(static_cast<_IntTy>(_Left) & static_cast<_IntTy>(_Right)); \
} \
\
_NODISCARD constexpr _BITMASK operator|(_BITMASK _Left, _BITMASK _Right) noexcept { /* return _Left | _Right */ \
using _IntTy = _STD underlying_type_t<_BITMASK>; \
return static_cast<_BITMASK>(static_cast<_IntTy>(_Left) | static_cast<_IntTy>(_Right)); \
} \
\
_NODISCARD constexpr _BITMASK operator^(_BITMASK _Left, _BITMASK _Right) noexcept { /* return _Left ^ _Right */ \
using _IntTy = _STD underlying_type_t<_BITMASK>; \
return static_cast<_BITMASK>(static_cast<_IntTy>(_Left) ^ static_cast<_IntTy>(_Right)); \
} \
\
constexpr _BITMASK& operator&=(_BITMASK& _Left, _BITMASK _Right) noexcept { /* return _Left &= _Right */ \
return _Left = _Left & _Right; \
} \
\
constexpr _BITMASK& operator|=(_BITMASK& _Left, _BITMASK _Right) noexcept { /* return _Left |= _Right */ \
return _Left = _Left | _Right; \
} \
\
constexpr _BITMASK& operator^=(_BITMASK& _Left, _BITMASK _Right) noexcept { /* return _Left ^= _Right */ \
return _Left = _Left ^ _Right; \
} \
\
_NODISCARD constexpr _BITMASK operator~(_BITMASK _Left) noexcept { /* return ~_Left */ \
using _IntTy = _STD underlying_type_t<_BITMASK>; \
return static_cast<_BITMASK>(~static_cast<_IntTy>(_Left)); \
} \
\
_NODISCARD constexpr bool _Bitmask_includes( \
_BITMASK _Left, _BITMASK _Elements) noexcept { /* return (_Left & _Elements) != _BITMASK{} */ \
return (_Left & _Elements) != _BITMASK{}; \
} \
\
_NODISCARD constexpr bool _Bitmask_includes_all( \
_BITMASK _Left, _BITMASK _Elements) noexcept { /* return (_Left & _Elements) == _Elements */ \
return (_Left & _Elements) == _Elements; \
}

The test case has pathological overloads, but users can and have encountered this with reasonable code (typically, when they define templated operators to handle their own enums).

Curiously, this doesn't happen in /std:c++17 or /std:c++latest mode. I haven't analyzed why, but this is definitely a problem in /std:c++14 mode.

We should audit the entire STL for this problem.

Also tracked by Microsoft-internal VSO-115352 / AB#115352.

@StephanTLavavej StephanTLavavej added the bug Something isn't working label Nov 14, 2019
@AlexGuteniev
Copy link
Contributor

The problem appears to be wider, not only about bitmasks.

Consider the following part:

enum { _Nlow = 0x25, _Nbytes = 1 };

And the following example:

template <typename T, typename U> int operator<(T&&, U&&) = delete;

struct Table {
    enum { Nlow = 4 };
};

int main(int argc, const char* argv[])
{
    int Bytecode = 0;
    if (Bytecode < Table::Nlow) {
        return 1;
    }
    return 0;
}

if you really want to avoid this, should deprecate anonymous enums in favor of static constexpr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants