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