Skip to content

Commit 5048a46

Browse files
Merge pull request #122 from Browsercore/error_union
Handle Error union return type
2 parents 5403691 + 7f18fe4 commit 5048a46

File tree

3 files changed

+106
-22
lines changed

3 files changed

+106
-22
lines changed

src/engines/v8/generate.zig

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ const TPL = @import("v8.zig").TPL;
2020
// Utils functions
2121
// ---------------
2222

23+
fn throwError(msg: []const u8, js_res: v8.ReturnValue, isolate: v8.Isolate) void {
24+
const err = v8.String.initUtf8(isolate, msg);
25+
const exception = v8.Exception.initError(err);
26+
js_res.set(isolate.throwException(exception));
27+
}
28+
2329
fn throwTypeError(msg: []const u8, js_res: v8.ReturnValue, isolate: v8.Isolate) void {
2430
const err = v8.String.initUtf8(isolate, msg);
2531
const exception = v8.Exception.initTypeError(err);
@@ -290,35 +296,35 @@ fn setReturnType(
290296
ctx: v8.Context,
291297
isolate: v8.Isolate,
292298
) !v8.Value {
293-
const under_opt = comptime ret.underOpt();
294-
const val_T = if (under_opt) |T| T else ret.T;
295-
var val: val_T = undefined;
299+
const info = @typeInfo(@TypeOf(res));
300+
301+
// Error Union
302+
if (info == .ErrorUnion) {
303+
const unwrapped = try res;
304+
return setReturnType(alloc, all_T, ret, unwrapped, ctx, isolate);
305+
}
296306

297307
// Optional
298-
if (under_opt != null) {
308+
if (info == .Optional) {
299309
if (res == null) {
300310
// if null just return JS null
301311
return isolate.initNull().toValue();
302-
} else {
303-
// otherwise replace with the underlying type
304-
val = res.?;
305312
}
306-
} else {
307-
val = res;
313+
return setReturnType(alloc, all_T, ret, res.?, ctx, isolate);
308314
}
309315

310316
// Union type
311317
if (comptime ret.union_T) |union_types| {
312318
// retrieve the active field and setReturntype accordingly
313-
const activeTag = @tagName(std.meta.activeTag(val));
319+
const activeTag = @tagName(std.meta.activeTag(res));
314320
// TODO: better algorythm?
315321
inline for (union_types) |tt| {
316322
if (std.mem.eql(u8, activeTag, tt.name.?)) {
317323
return setReturnType(
318324
alloc,
319325
all_T,
320326
tt,
321-
@field(val, tt.name.?),
327+
@field(res, tt.name.?),
322328
ctx,
323329
isolate,
324330
);
@@ -340,7 +346,7 @@ fn setReturnType(
340346
alloc,
341347
all_T,
342348
field,
343-
@field(val, name),
349+
@field(res, name),
344350
ctx,
345351
isolate,
346352
);
@@ -363,7 +369,7 @@ fn setReturnType(
363369
alloc,
364370
all_T[index],
365371
ret,
366-
val,
372+
res,
367373
js_obj,
368374
isolate,
369375
) catch unreachable;
@@ -374,7 +380,7 @@ fn setReturnType(
374380

375381
const js_val = nativeToJS(
376382
ret.underT(),
377-
val,
383+
res,
378384
isolate,
379385
) catch unreachable; // NOTE: should not happen has types have been checked at reflect
380386
return js_val;
@@ -466,6 +472,7 @@ fn generateConstructor(
466472
info.getThis(),
467473
isolate,
468474
) catch unreachable;
475+
// TODO: we choose for now not throw user error
469476
}
470477
}.constructor;
471478
}
@@ -512,7 +519,10 @@ fn generateGetter(
512519
res,
513520
isolate.getCurrentContext(),
514521
isolate,
515-
) catch unreachable;
522+
) catch |err| {
523+
// TODO: how to handle internal errors vs user errors
524+
return throwError(@errorName(err), info.getReturnValue(), isolate);
525+
};
516526
info.getReturnValue().setValueHandle(js_val.handle);
517527
}
518528
}.getter;
@@ -629,7 +639,10 @@ fn generateMethod(
629639
res,
630640
ctx,
631641
isolate,
632-
) catch unreachable;
642+
) catch |err| {
643+
// TODO: how to handle internal errors vs user errors
644+
return throwError(@errorName(err), info.getReturnValue(), isolate);
645+
};
633646
info.getReturnValue().setValueHandle(js_val.handle);
634647

635648
// sync callback

src/reflect.zig

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const internal_types = [_]type{
4242
};
4343

4444
// Type describes the reflect information of an individual value, either:
45-
// - an input paramater of a function
45+
// - an input parameter of a function
4646
// - or a return value of a function
4747
pub const Type = struct {
4848
T: type, // could be pointer or concrete
@@ -73,9 +73,24 @@ pub const Type = struct {
7373
// - the underlying type, ie. the concrete one
7474
// - and all the successive stages
7575

76+
// !Type
77+
fn _underErr(comptime T: type) ?type {
78+
const info = @typeInfo(T);
79+
if (info != .ErrorUnion) {
80+
return null;
81+
}
82+
return info.ErrorUnion.payload;
83+
}
84+
pub inline fn underErr(comptime self: Type) ?type {
85+
std.debug.assert(@inComptime());
86+
return Type._underErr(self.T);
87+
}
88+
89+
// !?Type (from underErr)
7690
// ?Type
7791
fn _underOpt(comptime T: type) ?type {
78-
const info = @typeInfo(T);
92+
const TT = Type._underErr(T) orelse T;
93+
const info = @typeInfo(TT);
7994
if (info != .Optional) {
8095
return null;
8196
}
@@ -86,10 +101,18 @@ pub const Type = struct {
86101
return Type._underOpt(self.T);
87102
}
88103

89-
// ?*Type
104+
// !?*Type, ?*Type (from underOpt)
105+
// !*Type (from underErr)
90106
// *Type
91107
fn _underPtr(comptime T: type) ?type {
92-
const TT = Type._underOpt(T) orelse T;
108+
var TT: type = undefined;
109+
if (Type._underOpt(T)) |t| {
110+
TT = t;
111+
} else if (Type._underErr(T)) |t| {
112+
TT = t;
113+
} else {
114+
TT = T;
115+
}
93116
const info = @typeInfo(TT);
94117
if (info == .Pointer and info.Pointer.size != .Slice) {
95118
// NOTE: slice are pointers but we handle them as single value
@@ -102,9 +125,14 @@ pub const Type = struct {
102125
return Type._underPtr(self.T);
103126
}
104127

128+
// !?*Type, ?*Type, !*Type, *Type (from underPtr)
129+
// !?Type, ?Type (from underOpt)
130+
// !Type (from underErr)
131+
// Type
105132
fn _underT(comptime T: type) type {
106133
if (Type._underPtr(T)) |TT| return TT;
107134
if (Type._underOpt(T)) |TT| return TT;
135+
if (Type._underErr(T)) |TT| return TT;
108136
return T;
109137
}
110138
pub inline fn underT(comptime self: Type) type {
@@ -482,6 +510,11 @@ pub const Func = struct {
482510
if (Type._is_variadic(args_types[i].underT()) and i < (args.len - 1)) {
483511
return error.FuncVariadicNotLastOne;
484512
}
513+
514+
// error union prohibited for args
515+
if (args_types[i].underErr() != null) {
516+
return error.FuncErrorUnionArg;
517+
}
485518
}
486519

487520
// first optional arg
@@ -1258,6 +1291,7 @@ const Error = error{
12581291
FuncMultiCbk,
12591292
FuncVariadicNotLastOne,
12601293
FuncReturnTypeVariadic,
1294+
FuncErrorUnionArg,
12611295

12621296
// type errors
12631297
TypeTaggedUnion,
@@ -1367,6 +1401,9 @@ const TestFuncVariadicNotLastOne = struct {
13671401
const TestFuncReturnTypeVariadic = struct {
13681402
pub fn _example(_: TestFuncReturnTypeVariadic) ?VariadicBool {}
13691403
};
1404+
const TestFuncErrorUnionArg = struct {
1405+
pub fn _example(_: TestFuncErrorUnionArg, _: anyerror!void) void {}
1406+
};
13701407

13711408
// types tests
13721409
const TestTaggedUnion = union {
@@ -1477,6 +1514,10 @@ pub fn tests() !void {
14771514
.{TestFuncReturnTypeVariadic},
14781515
error.FuncReturnTypeVariadic,
14791516
);
1517+
try ensureErr(
1518+
.{TestFuncErrorUnionArg},
1519+
error.FuncErrorUnionArg,
1520+
);
14801521

14811522
// types checks
14821523
try ensureErr(

src/tests/types_complex_test.zig

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,33 @@ const MyVariadic = struct {
5151
}
5252
};
5353

54+
const MyErrorUnion = struct {
55+
pub fn constructor() MyErrorUnion {
56+
return .{};
57+
}
58+
59+
pub fn get_withoutError(_: MyErrorUnion) !u8 {
60+
return 0;
61+
}
62+
63+
pub fn get_withError(_: MyErrorUnion) !u8 {
64+
return error.MyError;
65+
}
66+
67+
pub fn _funcWithoutError(_: MyErrorUnion) !void {}
68+
69+
pub fn _funcWithError(_: MyErrorUnion) !void {
70+
return error.MyError;
71+
}
72+
};
73+
5474
// generate API, comptime
55-
pub fn generate() ![]public.API {
56-
return try public.compile(.{
75+
pub fn generate() []public.API {
76+
return public.compile(.{
5777
MyIterable,
5878
MyList,
5979
MyVariadic,
80+
MyErrorUnion,
6081
});
6182
}
6283

@@ -93,4 +114,13 @@ pub fn exec(
93114
.{ .src = "myVariadic.empty()", .ex = "true" },
94115
};
95116
try tests.checkCases(js_env, &variadic);
117+
118+
var error_union = [_]tests.Case{
119+
.{ .src = "let myErrorUnion = new MyErrorUnion();", .ex = "undefined" },
120+
.{ .src = "myErrorUnion.withoutError", .ex = "0" },
121+
.{ .src = "var myErrorGetter = ''; try {myErrorUnion.withError} catch (error) {myErrorGetter = error}; myErrorGetter", .ex = "Error: MyError" },
122+
.{ .src = "myErrorUnion.funcWithoutError()", .ex = "undefined" },
123+
.{ .src = "var myErrorFunc = ''; try {myErrorUnion.funcWithError()} catch (error) {myErrorFunc = error}; myErrorFunc", .ex = "Error: MyError" },
124+
};
125+
try tests.checkCases(js_env, &error_union);
96126
}

0 commit comments

Comments
 (0)