// 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 <stdio.h> #include <unordered_map> #include <string> using Luau::NotNull; static_assert(!std::is_convertible<NotNull<int>, bool>::value, "NotNull<T> 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<int> p) { return *p; } void bar(int* q) {} TEST_SUITE_BEGIN("NotNull"); TEST_CASE("basic_stuff") { NotNull<int> a = NotNull{new int(55)}; // Does runtime test NotNull<int> b{new int(55)}; // As above // NotNull<int> c = new int(55); // Nope. Mildly regrettable, but implicit conversion from T* to NotNull<T> in the general case is not // good. // a = nullptr; // nope NotNull<int> d = a; // No runtime test. a is known not to be null. int e = *d; *d = 1; CHECK(e == 55); const NotNull<int> f = d; *f = 5; // valid: there is a difference between const NotNull<T> and NotNull<const T> // f = a; // nope CHECK_EQ(a, d); CHECK(a != b); NotNull<const int> g(a); CHECK(g == a); // *g = 123; // nope (void)f; NotNull<Test> t{new Test}; t->x = 5; t->y = 3.14f; const NotNull<Test> 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<NotNull<int>, const char*> map; int a_ = 8; int b_ = 10; NotNull<int> a{&a_}; NotNull<int> 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<int> n{&p}; *n = 123; NotNull<const int> m = n; // Conversion from NotNull<T> to NotNull<const T> is allowed CHECK(123 == *m); // readonly access of m is ok // *m = 321; // nope. m points at const data. // NotNull<int> o = m; // nope. Conversion from NotNull<const T> to NotNull<T> is forbidden NotNull<int> n2{&q}; m = n2; // ok. m points to const data, but is not itself const const NotNull<int> 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<int> a(raw); NotNull<const int> b(raw); NotNull<const int> c = a; // NotNull<int> d = c; // nope - no conversion from const to non-const CHECK_EQ(*c, 8); delete raw; } TEST_SUITE_END();