authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-02 14:51:29-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-02 14:51:29-05:00
log446324a1d82514157f5a11a6ca775711bc48c419
tree3a6c1fb0fe76ff999cc89a03aa7f09fdc8cf5c5b
parent7fc8dd66427dd0abed5ca9195e6afdbb687bd5f5
parent403a1fe5d767e801b58fc706eaa54f1171ae27de
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11025 from Vexu/stage2

stage2: implement `@extern`

6 files changed, 178 insertions(+), 31 deletions(-)

lib/std/special/test_runner.zig+2-3
...@@ -46,10 +46,9 @@ pub fn main() void {...@@ -46,10 +46,9 @@ pub fn main() void {
4646
47 var leaks: usize = 0;47 var leaks: usize = 0;
48 for (test_fn_list) |test_fn, i| {48 for (test_fn_list) |test_fn, i| {
49 const gpa_works = builtin.zig_backend == .stage1 or builtin.os.tag != .macos;49 std.testing.allocator_instance = .{};
50 if (gpa_works) std.testing.allocator_instance = .{};
51 defer {50 defer {
52 if (gpa_works and std.testing.allocator_instance.deinit()) {51 if (std.testing.allocator_instance.deinit()) {
53 leaks += 1;52 leaks += 1;
54 }53 }
55 }54 }
src/Module.zig+1
...@@ -1529,6 +1529,7 @@ pub const Var = struct {...@@ -1529,6 +1529,7 @@ pub const Var = struct {
1529 is_extern: bool,1529 is_extern: bool,
1530 is_mutable: bool,1530 is_mutable: bool,
1531 is_threadlocal: bool,1531 is_threadlocal: bool,
1532 is_weak_linkage: bool,
15321533
1533 pub fn deinit(variable: *Var, gpa: Allocator) void {1534 pub fn deinit(variable: *Var, gpa: Allocator) void {
1534 if (variable.lib_name) |lib_name| {1535 if (variable.lib_name) |lib_name| {
src/Sema.zig+134-23
...@@ -12371,7 +12371,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -12371,7 +12371,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
12371 const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };12371 const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
12372 const type_res = try sema.resolveType(block, src, extra.lhs);12372 const type_res = try sema.resolveType(block, src, extra.lhs);
12373 try sema.checkPtrType(block, type_src, type_res);12373 try sema.checkPtrType(block, type_src, type_res);
12374 _ = try sema.resolveTypeLayout(block, src, type_res.childType());12374 try sema.resolveTypeLayout(block, src, type_res.elemType2());
12375 const ptr_align = type_res.ptrAlignment(sema.mod.getTarget());12375 const ptr_align = type_res.ptrAlignment(sema.mod.getTarget());
1237612376
12377 if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {12377 if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {
...@@ -13019,20 +13019,24 @@ fn resolveExportOptions(...@@ -13019,20 +13019,24 @@ fn resolveExportOptions(
13019) CompileError!std.builtin.ExportOptions {13019) CompileError!std.builtin.ExportOptions {
13020 const export_options_ty = try sema.getBuiltinType(block, src, "ExportOptions");13020 const export_options_ty = try sema.getBuiltinType(block, src, "ExportOptions");
13021 const air_ref = sema.resolveInst(zir_ref);13021 const air_ref = sema.resolveInst(zir_ref);
13022 const coerced = try sema.coerce(block, export_options_ty, air_ref, src);13022 const options = try sema.coerce(block, export_options_ty, air_ref, src);
13023 const val = try sema.resolveConstValue(block, src, coerced);13023
13024 const fields = val.castTag(.@"struct").?.data;13024 const name = try sema.fieldVal(block, src, options, "name", src);
13025 const struct_obj = export_options_ty.castTag(.@"struct").?.data;13025 const name_val = try sema.resolveConstValue(block, src, name);
13026 const name_index = struct_obj.fields.getIndex("name").?;13026
13027 const linkage_index = struct_obj.fields.getIndex("linkage").?;13027 const linkage = try sema.fieldVal(block, src, options, "linkage", src);
13028 const section_index = struct_obj.fields.getIndex("section").?;13028 const linkage_val = try sema.resolveConstValue(block, src, linkage);
13029 if (!fields[section_index].isNull()) {13029
13030 const section = try sema.fieldVal(block, src, options, "section", src);
13031 const section_val = try sema.resolveConstValue(block, src, section);
13032
13033 if (!section_val.isNull()) {
13030 return sema.fail(block, src, "TODO: implement exporting with linksection", .{});13034 return sema.fail(block, src, "TODO: implement exporting with linksection", .{});
13031 }13035 }
13032 const name_ty = Type.initTag(.const_slice_u8);13036 const name_ty = Type.initTag(.const_slice_u8);
13033 return std.builtin.ExportOptions{13037 return std.builtin.ExportOptions{
13034 .name = try fields[name_index].toAllocatedBytes(name_ty, sema.arena),13038 .name = try name_val.toAllocatedBytes(name_ty, sema.arena),
13035 .linkage = fields[linkage_index].toEnum(std.builtin.GlobalLinkage),13039 .linkage = linkage_val.toEnum(std.builtin.GlobalLinkage),
13036 .section = null, // TODO13040 .section = null, // TODO
13037 };13041 };
13038}13042}
...@@ -13384,17 +13388,19 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -13384,17 +13388,19 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
13384 const args = sema.resolveInst(extra.data.args);13388 const args = sema.resolveInst(extra.data.args);
1338513389
13386 const modifier: std.builtin.CallOptions.Modifier = modifier: {13390 const modifier: std.builtin.CallOptions.Modifier = modifier: {
13387 const export_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions");13391 const call_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions");
13388 const coerced_options = try sema.coerce(block, export_options_ty, options, options_src);13392 const coerced_options = try sema.coerce(block, call_options_ty, options, options_src);
13389 const options_val = try sema.resolveConstValue(block, options_src, coerced_options);13393
13390 const fields = options_val.castTag(.@"struct").?.data;13394 const modifier = try sema.fieldVal(block, options_src, coerced_options, "modifier", options_src);
13391 const struct_obj = export_options_ty.castTag(.@"struct").?.data;13395 const modifier_val = try sema.resolveConstValue(block, options_src, modifier);
13392 const modifier_index = struct_obj.fields.getIndex("modifier").?;13396
13393 const stack_index = struct_obj.fields.getIndex("stack").?;13397 const stack = try sema.fieldVal(block, options_src, coerced_options, "stack", options_src);
13394 if (!fields[stack_index].isNull()) {13398 const stack_val = try sema.resolveConstValue(block, options_src, stack);
13399
13400 if (!stack_val.isNull()) {
13395 return sema.fail(block, options_src, "TODO: implement @call with stack", .{});13401 return sema.fail(block, options_src, "TODO: implement @call with stack", .{});
13396 }13402 }
13397 break :modifier fields[modifier_index].toEnum(std.builtin.CallOptions.Modifier);13403 break :modifier modifier_val.toEnum(std.builtin.CallOptions.Modifier);
13398 };13404 };
1339913405
13400 const args_ty = sema.typeOf(args);13406 const args_ty = sema.typeOf(args);
...@@ -13743,6 +13749,7 @@ fn zirVarExtended(...@@ -13743,6 +13749,7 @@ fn zirVarExtended(
13743 .is_extern = small.is_extern,13749 .is_extern = small.is_extern,
13744 .is_mutable = true, // TODO get rid of this unused field13750 .is_mutable = true, // TODO get rid of this unused field
13745 .is_threadlocal = small.is_threadlocal,13751 .is_threadlocal = small.is_threadlocal,
13752 .is_weak_linkage = false,
13746 .lib_name = null,13753 .lib_name = null,
13747 };13754 };
1374813755
...@@ -13937,7 +13944,103 @@ fn zirBuiltinExtern(...@@ -13937,7 +13944,103 @@ fn zirBuiltinExtern(
13937) CompileError!Air.Inst.Ref {13944) CompileError!Air.Inst.Ref {
13938 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;13945 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
13939 const src: LazySrcLoc = .{ .node_offset = extra.node };13946 const src: LazySrcLoc = .{ .node_offset = extra.node };
13940 return sema.fail(block, src, "TODO: implement Sema.zirBuiltinExtern", .{});13947 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
13948 const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
13949
13950 var ty = try sema.resolveType(block, ty_src, extra.lhs);
13951 const options_inst = sema.resolveInst(extra.rhs);
13952
13953 const options = options: {
13954 const extern_options_ty = try sema.getBuiltinType(block, options_src, "ExternOptions");
13955 const options = try sema.coerce(block, extern_options_ty, options_inst, options_src);
13956
13957 const name = try sema.fieldVal(block, options_src, options, "name", options_src);
13958 const name_val = try sema.resolveConstValue(block, options_src, name);
13959
13960 const library_name_inst = try sema.fieldVal(block, options_src, options, "library_name", options_src);
13961 const library_name_val = try sema.resolveConstValue(block, options_src, library_name_inst);
13962
13963 const linkage = try sema.fieldVal(block, options_src, options, "linkage", options_src);
13964 const linkage_val = try sema.resolveConstValue(block, options_src, linkage);
13965
13966 const is_thread_local = try sema.fieldVal(block, options_src, options, "is_thread_local", options_src);
13967 const is_thread_local_val = try sema.resolveConstValue(block, options_src, is_thread_local);
13968
13969 var library_name: ?[]const u8 = null;
13970 if (!library_name_val.isNull()) {
13971 const payload = library_name_val.castTag(.opt_payload).?.data;
13972 library_name = try payload.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena);
13973 }
13974
13975 break :options std.builtin.ExternOptions{
13976 .name = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena),
13977 .library_name = library_name,
13978 .linkage = linkage_val.toEnum(std.builtin.GlobalLinkage),
13979 .is_thread_local = is_thread_local_val.toBool(),
13980 };
13981 };
13982
13983 if (!ty.isPtrAtRuntime()) {
13984 return sema.fail(block, options_src, "expected (optional) pointer", .{});
13985 }
13986
13987 if (options.name.len == 0) {
13988 return sema.fail(block, options_src, "extern symbol name cannot be empty", .{});
13989 }
13990
13991 if (options.linkage != .Weak and options.linkage != .Strong) {
13992 return sema.fail(block, options_src, "extern symbol must use strong or weak linkage", .{});
13993 }
13994
13995 if (options.linkage == .Weak and !ty.ptrAllowsZero()) {
13996 ty = try Type.optional(sema.arena, ty);
13997 }
13998
13999 // TODO check duplicate extern
14000
14001 const new_decl = try sema.mod.allocateNewDecl(try sema.gpa.dupeZ(u8, options.name), sema.owner_decl.src_namespace, sema.owner_decl.src_node, null);
14002 errdefer new_decl.destroy(sema.mod);
14003
14004 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
14005 errdefer new_decl_arena.deinit();
14006 const new_decl_arena_allocator = new_decl_arena.allocator();
14007
14008 const new_var = try new_decl_arena_allocator.create(Module.Var);
14009 errdefer new_decl_arena_allocator.destroy(new_var);
14010
14011 new_var.* = .{
14012 .owner_decl = sema.owner_decl,
14013 .init = Value.initTag(.unreachable_value),
14014 .is_extern = true,
14015 .is_mutable = false, // TODO get rid of this unused field
14016 .is_threadlocal = options.is_thread_local,
14017 .is_weak_linkage = options.linkage == .Weak,
14018 .lib_name = null,
14019 };
14020
14021 if (options.library_name) |library_name| {
14022 if (library_name.len == 0) {
14023 return sema.fail(block, options_src, "library name name cannot be empty", .{});
14024 }
14025 new_var.lib_name = try sema.handleExternLibName(block, options_src, library_name);
14026 }
14027
14028 new_decl.src_line = sema.owner_decl.src_line;
14029 new_decl.ty = try ty.copy(new_decl_arena_allocator);
14030 new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var);
14031 new_decl.align_val = Value.@"null";
14032 new_decl.linksection_val = Value.@"null";
14033 new_decl.has_tv = true;
14034 new_decl.analysis = .complete;
14035 new_decl.generation = sema.mod.generation;
14036
14037 const arena_state = try new_decl_arena_allocator.create(std.heap.ArenaAllocator.State);
14038 arena_state.* = new_decl_arena.state;
14039 new_decl.value_arena = arena_state;
14040
14041 const ref = try sema.analyzeDeclRef(new_decl);
14042 try sema.requireRuntimeBlock(block, src);
14043 return block.addBitCast(ty, ref);
13941}14044}
1394214045
13943fn requireFunctionBlock(sema: *Sema, block: *Block, src: LazySrcLoc) !void {14046fn requireFunctionBlock(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
...@@ -15409,6 +15512,14 @@ fn coerce(...@@ -15409,6 +15512,14 @@ fn coerce(
15409 return sema.addConstant(dest_ty, Value.@"null");15512 return sema.addConstant(dest_ty, Value.@"null");
15410 }15513 }
1541115514
15515 // cast from ?*T and ?[*]T to ?*anyopaque
15516 // but don't do it if the source type is a double pointer
15517 if (dest_ty.isPtrLikeOptional() and dest_ty.elemType2().tag() == .anyopaque and
15518 inst_ty.isPtrLikeOptional() and inst_ty.elemType2().zigTypeTag() != .Pointer)
15519 {
15520 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
15521 }
15522
15412 // T to ?T15523 // T to ?T
15413 const child_type = try dest_ty.optionalChildAlloc(sema.arena);15524 const child_type = try dest_ty.optionalChildAlloc(sema.arena);
15414 const intermediate = try sema.coerce(block, child_type, inst, inst_src);15525 const intermediate = try sema.coerce(block, child_type, inst, inst_src);
...@@ -16688,6 +16799,7 @@ fn coerceCompatiblePtrs(...@@ -16688,6 +16799,7 @@ fn coerceCompatiblePtrs(
16688 inst: Air.Inst.Ref,16799 inst: Air.Inst.Ref,
16689 inst_src: LazySrcLoc,16800 inst_src: LazySrcLoc,
16690) !Air.Inst.Ref {16801) !Air.Inst.Ref {
16802 // TODO check const/volatile/alignment
16691 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {16803 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
16692 // The comptime Value representation is compatible with both types.16804 // The comptime Value representation is compatible with both types.
16693 return sema.addConstant(dest_ty, val);16805 return sema.addConstant(dest_ty, val);
...@@ -18403,6 +18515,7 @@ fn resolveInferredErrorSet(sema: *Sema, inferred_error_set: *Module.Fn.InferredE...@@ -18403,6 +18515,7 @@ fn resolveInferredErrorSet(sema: *Sema, inferred_error_set: *Module.Fn.InferredE
18403 if (inferred_error_set.is_resolved) {18515 if (inferred_error_set.is_resolved) {
18404 return;18516 return;
18405 }18517 }
18518 inferred_error_set.is_resolved = true;
1840618519
18407 var it = inferred_error_set.inferred_error_sets.keyIterator();18520 var it = inferred_error_set.inferred_error_sets.keyIterator();
18408 while (it.next()) |other_error_set_ptr| {18521 while (it.next()) |other_error_set_ptr| {
...@@ -18423,8 +18536,6 @@ fn resolveInferredErrorSet(sema: *Sema, inferred_error_set: *Module.Fn.InferredE...@@ -18423,8 +18536,6 @@ fn resolveInferredErrorSet(sema: *Sema, inferred_error_set: *Module.Fn.InferredE
18423 if (other_error_set_ptr.*.is_anyerror)18536 if (other_error_set_ptr.*.is_anyerror)
18424 inferred_error_set.is_anyerror = true;18537 inferred_error_set.is_anyerror = true;
18425 }18538 }
18426
18427 inferred_error_set.is_resolved = true;
18428}18539}
1842918540
18430fn resolveInferredErrorSetTy(sema: *Sema, ty: Type) CompileError!void {18541fn resolveInferredErrorSetTy(sema: *Sema, ty: Type) CompileError!void {
src/codegen/llvm.zig+16-2
...@@ -548,6 +548,10 @@ pub const Object = struct {...@@ -548,6 +548,10 @@ pub const Object = struct {
548 llvm_global.setValueName(decl.name);548 llvm_global.setValueName(decl.name);
549 llvm_global.setUnnamedAddr(.False);549 llvm_global.setUnnamedAddr(.False);
550 llvm_global.setLinkage(.External);550 llvm_global.setLinkage(.External);
551 if (decl.val.castTag(.variable)) |variable| {
552 if (variable.data.is_threadlocal) llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);
553 if (variable.data.is_weak_linkage) llvm_global.setLinkage(.ExternalWeak);
554 }
551 } else if (exports.len != 0) {555 } else if (exports.len != 0) {
552 const exp_name = exports[0].options.name;556 const exp_name = exports[0].options.name;
553 llvm_global.setValueName2(exp_name.ptr, exp_name.len);557 llvm_global.setValueName2(exp_name.ptr, exp_name.len);
...@@ -558,6 +562,9 @@ pub const Object = struct {...@@ -558,6 +562,9 @@ pub const Object = struct {
558 .Weak => llvm_global.setLinkage(.WeakODR),562 .Weak => llvm_global.setLinkage(.WeakODR),
559 .LinkOnce => llvm_global.setLinkage(.LinkOnceODR),563 .LinkOnce => llvm_global.setLinkage(.LinkOnceODR),
560 }564 }
565 if (decl.val.castTag(.variable)) |variable| {
566 if (variable.data.is_threadlocal) llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);
567 }
561 // If a Decl is exported more than one time (which is rare),568 // If a Decl is exported more than one time (which is rare),
562 // we add aliases for all but the first export.569 // we add aliases for all but the first export.
563 // TODO LLVM C API does not support deleting aliases. We need to570 // TODO LLVM C API does not support deleting aliases. We need to
...@@ -788,8 +795,15 @@ pub const DeclGen = struct {...@@ -788,8 +795,15 @@ pub const DeclGen = struct {
788 const llvm_global = dg.object.llvm_module.addGlobalInAddressSpace(llvm_type, fqn, llvm_addrspace);795 const llvm_global = dg.object.llvm_module.addGlobalInAddressSpace(llvm_type, fqn, llvm_addrspace);
789 gop.value_ptr.* = llvm_global;796 gop.value_ptr.* = llvm_global;
790797
791 const is_extern = decl.val.tag() == .unreachable_value;798 if (decl.isExtern()) {
792 if (!is_extern) {799 llvm_global.setValueName(decl.name);
800 llvm_global.setUnnamedAddr(.False);
801 llvm_global.setLinkage(.External);
802 if (decl.val.castTag(.variable)) |variable| {
803 if (variable.data.is_threadlocal) llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);
804 if (variable.data.is_weak_linkage) llvm_global.setLinkage(.ExternalWeak);
805 }
806 } else {
793 llvm_global.setLinkage(.Internal);807 llvm_global.setLinkage(.Internal);
794 llvm_global.setUnnamedAddr(.True);808 llvm_global.setUnnamedAddr(.True);
795 }809 }
src/codegen/llvm/bindings.zig+11
...@@ -120,6 +120,9 @@ pub const Value = opaque {...@@ -120,6 +120,9 @@ pub const Value = opaque {
120 pub const setUnnamedAddr = LLVMSetUnnamedAddr;120 pub const setUnnamedAddr = LLVMSetUnnamedAddr;
121 extern fn LLVMSetUnnamedAddr(Global: *const Value, HasUnnamedAddr: Bool) void;121 extern fn LLVMSetUnnamedAddr(Global: *const Value, HasUnnamedAddr: Bool) void;
122122
123 pub const setThreadLocalMode = LLVMSetThreadLocalMode;
124 extern fn LLVMSetThreadLocalMode(Global: *const Value, Mode: ThreadLocalMode) void;
125
123 pub const deleteGlobal = LLVMDeleteGlobal;126 pub const deleteGlobal = LLVMDeleteGlobal;
124 extern fn LLVMDeleteGlobal(GlobalVar: *const Value) void;127 extern fn LLVMDeleteGlobal(GlobalVar: *const Value) void;
125128
...@@ -1230,6 +1233,14 @@ pub const Linkage = enum(c_uint) {...@@ -1230,6 +1233,14 @@ pub const Linkage = enum(c_uint) {
1230 LinkerPrivateWeak,1233 LinkerPrivateWeak,
1231};1234};
12321235
1236pub const ThreadLocalMode = enum(c_uint) {
1237 NotThreadLocal,
1238 GeneralDynamicTLSModel,
1239 LocalDynamicTLSModel,
1240 InitialExecTLSModel,
1241 LocalExecTLSModel,
1242};
1243
1233pub const AtomicOrdering = enum(c_uint) {1244pub const AtomicOrdering = enum(c_uint) {
1234 NotAtomic = 0,1245 NotAtomic = 0,
1235 Unordered = 1,1246 Unordered = 1,
src/type.zig+14-3
...@@ -2168,7 +2168,14 @@ pub const Type = extern union {...@@ -2168,7 +2168,14 @@ pub const Type = extern union {
2168 .mut_slice,2168 .mut_slice,
2169 .optional_single_const_pointer,2169 .optional_single_const_pointer,
2170 .optional_single_mut_pointer,2170 .optional_single_mut_pointer,
2171 => return self.cast(Payload.ElemType).?.data.abiAlignment(target),2171 => {
2172 const child_type = self.cast(Payload.ElemType).?.data;
2173 if (child_type.zigTypeTag() == .Opaque) {
2174 return 1;
2175 } else {
2176 return child_type.abiAlignment(target);
2177 }
2178 },
21722179
2173 .manyptr_u8,2180 .manyptr_u8,
2174 .manyptr_const_u8,2181 .manyptr_const_u8,
...@@ -2181,10 +2188,13 @@ pub const Type = extern union {...@@ -2181,10 +2188,13 @@ pub const Type = extern union {
2181 const ptr_info = self.castTag(.pointer).?.data;2188 const ptr_info = self.castTag(.pointer).?.data;
2182 if (ptr_info.@"align" != 0) {2189 if (ptr_info.@"align" != 0) {
2183 return ptr_info.@"align";2190 return ptr_info.@"align";
2191 } else if (ptr_info.pointee_type.zigTypeTag() == .Opaque) {
2192 return 1;
2184 } else {2193 } else {
2185 return ptr_info.pointee_type.abiAlignment(target);2194 return ptr_info.pointee_type.abiAlignment(target);
2186 }2195 }
2187 },2196 },
2197 .optional => return self.castTag(.optional).?.data.ptrAlignment(target),
21882198
2189 else => unreachable,2199 else => unreachable,
2190 }2200 }
...@@ -3235,8 +3245,9 @@ pub const Type = extern union {...@@ -3235,8 +3245,9 @@ pub const Type = extern union {
3235 return child_ty;3245 return child_ty;
3236 }3246 }
3237 },3247 },
32383248 .optional => ty.castTag(.optional).?.data.childType(),
3239 // TODO handle optionals3249 .optional_single_mut_pointer => ty.castPointer().?.data,
3250 .optional_single_const_pointer => ty.castPointer().?.data,
32403251
3241 else => unreachable,3252 else => unreachable,
3242 };3253 };