// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #include "Luau/NotNull.h" #include "doctest.h" #include #include #include using Luau::NotNull; static_assert(!std::is_convertible, bool>::value, "NotNull ought not to be convertible into bool"); namespace { struct Test { int x; float y; static int count; Test() { ++count; } ~Test() { --count; } }; int Test::count = 0; } // namespace int foo(NotNull p) { return *p; } void bar(int* q) {} TEST_SUITE_BEGIN("NotNull"); TEST_CASE("basic_stuff") { NotNull a = NotNull{new int(55)}; // Does runtime test NotNull b{new int(55)}; // As above // NotNull c = new int(55); // Nope. Mildly regrettable, but implicit conversion from T* to NotNull in the general case is not // good. // a = nullptr; // nope NotNull d = a; // No runtime test. a is known not to be null. int e = *d; *d = 1; CHECK(e == 55); const NotNull f = d; *f = 5; // valid: there is a difference between const NotNull and NotNull // f = a; // nope CHECK_EQ(a, d); CHECK(a != b); NotNull g(a); CHECK(g == a); // *g = 123; // nope (void)f; NotNull t{new Test}; t->x = 5; t->y = 3.14f; const NotNull u = t; u->x = 44; int v = u->x; CHECK(v == 44); bar(a); // a++; // nope // a[41]; // nope // a + 41; // nope // a - 41; // nope delete a; delete b; delete t; CHECK_EQ(0, Test::count); } TEST_CASE("hashable") { std::unordered_map, const char*> map; int a_ = 8; int b_ = 10; NotNull a{&a_}; NotNull b{&b_}; std::string hello = "hello"; std::string world = "world"; map[a] = hello.c_str(); map[b] = world.c_str(); CHECK_EQ(2, map.size()); CHECK_EQ(hello.c_str(), map[a]); CHECK_EQ(world.c_str(), map[b]); } TEST_CASE("const") { int p = 0; int q = 0; NotNull n{&p}; *n = 123; NotNull m = n; // Conversion from NotNull to NotNull is allowed CHECK(123 == *m); // readonly access of m is ok // *m = 321; // nope. m points at const data. // NotNull o = m; // nope. Conversion from NotNull to NotNull is forbidden NotNull n2{&q}; m = n2; // ok. m points to const data, but is not itself const const NotNull m2 = n; // m2 = n2; // nope. m2 is const. *m2 = 321; // ok. m2 is const, but points to mutable data CHECK(321 == *n); } TEST_CASE("const_compatibility") { int* raw = new int(8); NotNull a(raw); NotNull b(raw); NotNull c = a; // NotNull d = c; // nope - no conversion from const to non-const CHECK_EQ(*c, 8); delete raw; } TEST_SUITE_END();