1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// 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();