microsoft / GSL

Guidelines Support Library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Broken static_assert in gsl::not_null

Leedehai opened this issue · comments

static_assert(std::is_convertible<decltype(std::declval<T>() != nullptr), bool>::value,
  "T cannot be compared to nullptr.");

Please see https://stackoverflow.com/a/65867132/8385554.

Could you please provide an example that shows that the static_assert is broken?

Addressing the answer in the StackOverflow post:

#include <type_traits>

template<typename T>
void foo()
{
    static_assert(
              std::is_convertible<decltype(std::declval<T>() != nullptr), bool>::value,
              "T cannot be compared to nullptr.");
}

int main()
{
    foo<int>();
} 

gsl::not_null is designed to work on pointers. However, the example provided on StackOverflow has a function foo that takes an int as the template parameter rather than an int*, which fails the static_assert.

@JordanMaples As written in the answer:

This results in a compiler diagnostic, and not an assertion, using Microsoft's own compiler:

So the code is correct in the sense that it prevents gsl::not_null<int> to compile. But it is broken in the sense that not the static_assert triggers, but a compiler diagnostic is issued. We would get a better compiler warning with this code:

#include <type_traits>

template<typename T, typename = void>
struct is_nullptr_convertible : std::false_type {};

template <typename T>
struct is_nullptr_convertible<T, std::is_convertible<decltype(std::declval<T>() != nullptr), bool>> : std::true_type {};

template<typename T>
void foo()
{
    static_assert(
        is_nullptr_convertible<T>::value,
        "T cannot be compared to nullptr.");
}

int main()
{
    foo<int>();
}

So maybe we could adjust the static_assert!?

Ah, I understand now. I focused on the wrong part of the answer. I thought they were talking about consumption of the type rather than the source of the error. I'm going to blame a lack of coffee.

Would you mind drafting a PR so I can discuss it with the other maintainers this week?

@CaseyCarter or @StephanTLavavej, would one of you mind looking at this static_assert from gsl::not_null?

As it currently exists:

static_assert(std::is_convertible<decltype(std::declval<T>() != nullptr), bool>::value,
                  "T cannot be compared to nullptr.");

Do you have any suggestions as to how we can revise this so that the static_assert fires, rather than the compiler diagnostic?

You'll need to make a "comparable to nullptr" trait and static_assert that. Something like:

template <class T, class = void>
constexpr bool meow = false;

template <class T>
constexpr bool meow<T, std::void_t<decltype(std::declval<T>() != nullptr)>> = 
    std::is_convertible_v<decltype(std::declval<T>() != nullptr), bool>;

static_assert(meow<T>, "Bonehead user, T can't be compared to nullptr!");

@CaseyCarter That looks quite similar to what I tried and what worked on my local PC. Do you have an idea why it fails in the CI (see PR #975)?

@CaseyCarter That looks quite similar to what I tried and what worked on my local PC. Do you have an idea why it fails in the CI (see PR #975)?

I left a comment on the PR.