1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include <string.h>
8 #include "mozilla/Result.h"
9
10 using mozilla::Err;
11 using mozilla::GenericErrorResult;
12 using mozilla::Ok;
13 using mozilla::Result;
14
15 struct Failed
16 {
17 int x;
18 };
19
20 static_assert(sizeof(Result<Ok, Failed&>) == sizeof(uintptr_t),
21 "Result with empty value type should be pointer-sized");
22 static_assert(sizeof(Result<int*, Failed&>) == sizeof(uintptr_t),
23 "Result with two aligned pointer types should be pointer-sized");
24 static_assert(sizeof(Result<char*, Failed*>) > sizeof(char*),
25 "Result with unaligned success type `char*` must not be pointer-sized");
26 static_assert(sizeof(Result<int*, char*>) > sizeof(char*),
27 "Result with unaligned error type `char*` must not be pointer-sized");
28
29 enum Foo8 : uint8_t {};
30 enum Foo16 : uint16_t {};
31 enum Foo32 : uint32_t {};
32 static_assert(sizeof(Result<Ok, Foo8>) <= sizeof(uintptr_t),
33 "Result with small types should be pointer-sized");
34 static_assert(sizeof(Result<Ok, Foo16>) <= sizeof(uintptr_t),
35 "Result with small types should be pointer-sized");
36 static_assert(sizeof(Foo32) >= sizeof(uintptr_t) ||
37 sizeof(Result<Ok, Foo32>) <= sizeof(uintptr_t),
38 "Result with small types should be pointer-sized");
39
40 static_assert(sizeof(Result<Foo16, Foo8>) <= sizeof(uintptr_t),
41 "Result with small types should be pointer-sized");
42 static_assert(sizeof(Result<Foo8, Foo16>) <= sizeof(uintptr_t),
43 "Result with small types should be pointer-sized");
44 static_assert(sizeof(Foo32) >= sizeof(uintptr_t) ||
45 sizeof(Result<Foo32, Foo16>) <= sizeof(uintptr_t),
46 "Result with small types should be pointer-sized");
47 static_assert(sizeof(Foo32) >= sizeof(uintptr_t) ||
48 sizeof(Result<Foo16, Foo32>) <= sizeof(uintptr_t),
49 "Result with small types should be pointer-sized");
50
51 static GenericErrorResult<Failed&>
Fail()52 Fail()
53 {
54 static Failed failed;
55 return Err<Failed&>(failed);
56 }
57
58 static Result<Ok, Failed&>
Task1(bool pass)59 Task1(bool pass)
60 {
61 if (!pass) {
62 return Fail(); // implicit conversion from GenericErrorResult to Result
63 }
64 return Ok();
65 }
66
67 static Result<int, Failed&>
Task2(bool pass,int value)68 Task2(bool pass, int value)
69 {
70 MOZ_TRY(Task1(pass)); // converts one type of result to another in the error case
71 return value; // implicit conversion from T to Result<T, E>
72 }
73
74 static Result<int, Failed&>
Task3(bool pass1,bool pass2,int value)75 Task3(bool pass1, bool pass2, int value)
76 {
77 int x, y;
78 MOZ_TRY_VAR(x, Task2(pass1, value));
79 MOZ_TRY_VAR(y, Task2(pass2, value));
80 return x + y;
81 }
82
83 static void
BasicTests()84 BasicTests()
85 {
86 MOZ_RELEASE_ASSERT(Task1(true).isOk());
87 MOZ_RELEASE_ASSERT(!Task1(true).isErr());
88 MOZ_RELEASE_ASSERT(!Task1(false).isOk());
89 MOZ_RELEASE_ASSERT(Task1(false).isErr());
90
91 // MOZ_TRY works.
92 MOZ_RELEASE_ASSERT(Task2(true, 3).isOk());
93 MOZ_RELEASE_ASSERT(Task2(true, 3).unwrap() == 3);
94 MOZ_RELEASE_ASSERT(Task2(true, 3).unwrapOr(6) == 3);
95 MOZ_RELEASE_ASSERT(Task2(false, 3).isErr());
96 MOZ_RELEASE_ASSERT(Task2(false, 3).unwrapOr(6) == 6);
97
98 // MOZ_TRY_VAR works.
99 MOZ_RELEASE_ASSERT(Task3(true, true, 3).isOk());
100 MOZ_RELEASE_ASSERT(Task3(true, true, 3).unwrap() == 6);
101 MOZ_RELEASE_ASSERT(Task3(true, false, 3).isErr());
102 MOZ_RELEASE_ASSERT(Task3(false, true, 3).isErr());
103 MOZ_RELEASE_ASSERT(Task3(false, true, 3).unwrapOr(6) == 6);
104
105 // Lvalues should work too.
106 {
107 Result<Ok, Failed&> res = Task1(true);
108 MOZ_RELEASE_ASSERT(res.isOk());
109 MOZ_RELEASE_ASSERT(!res.isErr());
110
111 res = Task1(false);
112 MOZ_RELEASE_ASSERT(!res.isOk());
113 MOZ_RELEASE_ASSERT(res.isErr());
114 }
115
116 {
117 Result<int, Failed&> res = Task2(true, 3);
118 MOZ_RELEASE_ASSERT(res.isOk());
119 MOZ_RELEASE_ASSERT(res.unwrap() == 3);
120
121 res = Task2(false, 4);
122 MOZ_RELEASE_ASSERT(res.isErr());
123 }
124
125 // Some tests for pointer tagging.
126 {
127 int i = 123;
128 double d = 3.14;
129
130 Result<int*, double&> res = &i;
131 static_assert(sizeof(res) == sizeof(uintptr_t),
132 "should use pointer tagging to fit in a word");
133
134 MOZ_RELEASE_ASSERT(res.isOk());
135 MOZ_RELEASE_ASSERT(*res.unwrap() == 123);
136
137 res = Err(d);
138 MOZ_RELEASE_ASSERT(res.isErr());
139 MOZ_RELEASE_ASSERT(&res.unwrapErr() == &d);
140 MOZ_RELEASE_ASSERT(res.unwrapErr() == 3.14);
141 }
142 }
143
144
145 /* * */
146
147 struct Snafu : Failed {};
148
149 static Result<Ok, Snafu*>
Explode()150 Explode()
151 {
152 static Snafu snafu;
153 return Err(&snafu);
154 }
155
156 static Result<Ok, Failed*>
ErrorGeneralization()157 ErrorGeneralization()
158 {
159 MOZ_TRY(Explode()); // change error type from Snafu* to more general Failed*
160 return Ok();
161 }
162
163 static void
TypeConversionTests()164 TypeConversionTests()
165 {
166 MOZ_RELEASE_ASSERT(ErrorGeneralization().isErr());
167 }
168
169 static void
EmptyValueTest()170 EmptyValueTest()
171 {
172 struct Fine {};
173 mozilla::Result<Fine, int&> res((Fine()));
174 res.unwrap();
175 MOZ_RELEASE_ASSERT(res.isOk());
176 static_assert(sizeof(res) == sizeof(uintptr_t),
177 "Result with empty value type should be pointer-sized");
178 }
179
180 static void
ReferenceTest()181 ReferenceTest()
182 {
183 struct MyError { int x = 0; };
184 MyError merror;
185 Result<int, MyError&> res(merror);
186 MOZ_RELEASE_ASSERT(&res.unwrapErr() == &merror);
187 }
188
189 static void
MapTest()190 MapTest()
191 {
192 struct MyError {
193 int x;
194
195 explicit MyError(int y) : x(y) { }
196 };
197
198 // Mapping over success values.
199 Result<int, MyError> res(5);
200 bool invoked = false;
201 auto res2 = res.map([&invoked](int x) {
202 MOZ_RELEASE_ASSERT(x == 5);
203 invoked = true;
204 return "hello";
205 });
206 MOZ_RELEASE_ASSERT(res2.isOk());
207 MOZ_RELEASE_ASSERT(invoked);
208 MOZ_RELEASE_ASSERT(strcmp(res2.unwrap(), "hello") == 0);
209
210 // Mapping over error values.
211 MyError err(1);
212 Result<char, MyError> res3(err);
213 MOZ_RELEASE_ASSERT(res3.isErr());
214 Result<char, MyError> res4 = res3.map([](int x) {
215 MOZ_RELEASE_ASSERT(false);
216 return 'a';
217 });
218 MOZ_RELEASE_ASSERT(res4.isErr());
219 MOZ_RELEASE_ASSERT(res4.unwrapErr().x == err.x);
220
221 // Function pointers instead of lamdbas as the mapping function.
222 Result<const char*, MyError> res5("hello");
223 auto res6 = res5.map(strlen);
224 MOZ_RELEASE_ASSERT(res6.isOk());
225 MOZ_RELEASE_ASSERT(res6.unwrap() == 5);
226 }
227
228 static void
AndThenTest()229 AndThenTest()
230 {
231 // `andThen`ing over success results.
232 Result<int, const char*> r1(10);
233 Result<int, const char*> r2 = r1.andThen([](int x) {
234 return Result<int, const char*>(x + 1);
235 });
236 MOZ_RELEASE_ASSERT(r2.isOk());
237 MOZ_RELEASE_ASSERT(r2.unwrap() == 11);
238
239 // `andThen`ing over error results.
240 Result<int, const char*> r3("error");
241 Result<int, const char*> r4 = r3.andThen([](int x) {
242 MOZ_RELEASE_ASSERT(false);
243 return Result<int, const char*>(1);
244 });
245 MOZ_RELEASE_ASSERT(r4.isErr());
246 MOZ_RELEASE_ASSERT(r3.unwrapErr() == r4.unwrapErr());
247 }
248
249 /* * */
250
main()251 int main()
252 {
253 BasicTests();
254 TypeConversionTests();
255 EmptyValueTest();
256 ReferenceTest();
257 MapTest();
258 AndThenTest();
259 return 0;
260 }
261