GithubHelp home page GithubHelp logo

gsl::not_null at compile time about gsl HOT 13 CLOSED

jonnygrant avatar jonnygrant commented on June 8, 2024 1
gsl::not_null at compile time

from gsl.

Comments (13)

dmitrykobets-msft avatar dmitrykobets-msft commented on June 8, 2024 1

Hi @jonnygrant,

There are some compile-time checks that are offered by GSL not_null, such as disallowing construction directly from the nullptr (i.e., f(nullptr);), but these are implemented via C++ language features (such as deleting the constructor taking std::nullptr_t). Otherwise, all checks intentionally occur at runtime.

More comprehensive compile-time checks would be best achieved via a static analysis tool that understands gsl::not_null. For example MSVC currently has some experimental gsl::not_null support that would raise a compile-time diagnostic on your code example.

from gsl.

dmitrykobets-msft avatar dmitrykobets-msft commented on June 8, 2024 1

@jonnygrant thanks for continuing the investigation and iterating on the design.

With your latest changes, this example fails to compile: https://godbolt.org/z/o6oj63Pd1, whereas in the existing GSL implementation it will compile and run fine if the pointer is not null, and runtime error otherwise.

int main()
{
    gsl::not_null<int*> test = new (std::nothrow) int();
    int a = *test;
    (void)a; // suppress unused var warning
}

In fact, even the following code will fail the compile-time check:
https://godbolt.org/z/4TG1aP3os

void foo(int* ptr)
{
   gsl::not_null<int*> nn  = ptr; // compiler can't assert that ptr != nullptr, compile_error_null_check
   (void)nn; // suppress unused var warning
}
int main() {}

So the programming model effectively proposed with these changes requires the users of not_null to manually insert nullchecks:

void foo(int* ptr)
{
   if (ptr == nullptr) return;
   gsl::not_null<int*> nn  = ptr; // compiles fine
   (void)nn; // suppress unused var warning
}
int main() {}

If this is intended, then you will probably want to first open a CoreGuidelines issue to discuss this proposal, since it is a non-trivial deviation from the current programming model suggested by not_null.

from gsl.

jonnygrant avatar jonnygrant commented on June 8, 2024 1

@dmitrykobets-msft Many thanks for your reply and the suggestion, I've filed on CppCoreGuidelines now. Let's see what they say.

You're right nullptr checks would need to be inserted in some application code.
My idea gives the programmer chance to evaluate where the nullptr came from at build time, and what logic caused it.

from gsl.

jonnygrant avatar jonnygrant commented on June 8, 2024

I tried
strict_not_null

but it also didn't give what I need.
https://godbolt.org/z/nET6nse31

from gsl.

beinhaerter avatar beinhaerter commented on June 8, 2024

I tried strict_not_null

but it also didn't give what I need. https://godbolt.org/z/nET6nse31

That is expected. The difference between strict_not_null and not_null is that strict_not_null has an explicit constructor whereas not_null has a constructor that can be called implicitely. And that difference is not relevant for what you want to achieve.

from gsl.

jonnygrant avatar jonnygrant commented on June 8, 2024

Hi @dmitrykobets-msft Many thanks for your reply.

Thank you for the static analysis tool suggestion. It's a good idea. I'm only on GNU/Linux gcc, so I don' think I could compile that unfortunately.

I did patch not_null locally, and now the build stops in an optimized build when it sees my int* is NULL

Example uses the following. It adds a dummy method, just to trigger the warning -Wnotnull
__attribute__ ((nonnull));

https://godbolt.org/z/xd1YeE5cs

Would you consider integrating something similar into the official GSL not_null class?

from gsl.

jonnygrant avatar jonnygrant commented on June 8, 2024

This might be better using

__attribute__((error("not_null found nullptr")));

https://godbolt.org/z/cMvjqb93T

Could you consider adding this. It works with GCC and Clang.

from gsl.

beinhaerter avatar beinhaerter commented on June 8, 2024

In your godbolt with error I changed the definition of test in main to int * test = new (std::nothrow) int();. That code still does not compile because the pointer might be null and the compiler might need to call compile_error_null_check. So with this proposed solution legitimate code does not compile.

from gsl.

jonnygrant avatar jonnygrant commented on June 8, 2024

@beinhaerter

I tried your suggestion with GCC and found the same, however with clang, it did compile
https://godbolt.org/z/63jYWW794

Same with malloc(8), clang compiled but not gcc.
https://godbolt.org/z/hve7hha9x

Maybe the
__attribute__ ((nonnull));

example is better I provided above.

from gsl.

beinhaerter avatar beinhaerter commented on June 8, 2024

Your __attribute__((nonnull)) change also fails with GCC and int * test = new (std::nothrow) int();.

from gsl.

jonnygrant avatar jonnygrant commented on June 8, 2024

Your __attribute__((nonnull)) change also fails with GCC and int * test = new (std::nothrow) int();.

May I ask, my idea is to get a not_null build error when a pointer is passed that might be nullptr. So it seems to be working as expected?

You get a nice compile time error when accidentally passing a value that might be a nullptr as a not_null<int*>

In constructor 'constexpr gsl::not_null<T>::not_null(T) [with <template-parameter-2-1> = void; T = int*]',
    inlined from 'int main()' at <source>:475:6:
<source>:236:53: error: argument 1 null where non-null expected [-Werror=nonnull]

For such a fatal memory allocation failure by new, I'd put some handler in to log it and abort()

if(nullptr == test)
{
    log_error("FATAL new (std::nothrow) int(); returned nullptr\n");
    abort();
}

Oddly this attribute nonnull example doesn't work in the else {}, even after checking the pointer is valid. Sharing the link
https://godbolt.org/z/7661ffdxK

from gsl.

jonnygrant avatar jonnygrant commented on June 8, 2024

In your godbolt with error I changed the definition of test in main to int * test = new (std::nothrow) int();. That code still does not compile because the pointer might be null and the compiler might need to call compile_error_null_check. So with this proposed solution legitimate code does not compile.

I updated this error example after getting the build error for the possible nullptr returned by operator new, so this works well

https://godbolt.org/z/Kh6Wa3oTM

from gsl.

hsutter avatar hsutter commented on June 8, 2024

Maintainers call: Closed per isocpp/CppCoreGuidelines#2071

from gsl.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.