diff --git a/src/AstGen.zig b/src/AstGen.zig index 6c3ce1251..c7f589644 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1183,13 +1183,6 @@ fn blockExprStmts( // in the above while loop. const zir_tags = gz.astgen.instructions.items(.tag); switch (zir_tags[inst]) { - .@"const" => { - const tv = gz.astgen.instructions.items(.data)[inst].@"const"; - break :b switch (tv.ty.zigTypeTag()) { - .NoReturn, .Void => true, - else => false, - }; - }, // For some instructions, swap in a slightly different ZIR tag // so we can avoid a separate ensure_result_used instruction. .call_none_chkused => unreachable, @@ -1335,6 +1328,8 @@ fn blockExprStmts( .union_decl, .enum_decl, .opaque_decl, + .int_to_enum, + .enum_to_int, => break :b false, // ZIR instructions that are always either `noreturn` or `void`. @@ -1823,15 +1818,18 @@ fn containerDecl( defer bit_bag.deinit(gpa); var cur_bit_bag: u32 = 0; - var member_index: usize = 0; - while (true) { - const member_node = container_decl.ast.members[member_index]; + var field_index: usize = 0; + for (container_decl.ast.members) |member_node| { const member = switch (node_tags[member_node]) { .container_field_init => tree.containerFieldInit(member_node), .container_field_align => tree.containerFieldAlign(member_node), .container_field => tree.containerField(member_node), - else => unreachable, + else => continue, }; + if (field_index % 16 == 0 and field_index != 0) { + try bit_bag.append(gpa, cur_bit_bag); + cur_bit_bag = 0; + } if (member.comptime_token) |comptime_token| { return mod.failTok(scope, comptime_token, "TODO implement comptime struct fields", .{}); } @@ -1858,17 +1856,9 @@ fn containerDecl( fields_data.appendAssumeCapacity(@enumToInt(default_inst)); } - member_index += 1; - if (member_index < container_decl.ast.members.len) { - if (member_index % 16 == 0) { - try bit_bag.append(gpa, cur_bit_bag); - cur_bit_bag = 0; - } - } else { - break; - } + field_index += 1; } - const empty_slot_count = 16 - ((member_index - 1) % 16); + const empty_slot_count = 16 - ((field_index - 1) % 16); cur_bit_bag >>= @intCast(u5, empty_slot_count * 2); const result = try gz.addPlNode(tag, node, zir.Inst.StructDecl{ @@ -1885,6 +1875,45 @@ fn containerDecl( return mod.failTok(scope, container_decl.ast.main_token, "TODO AstGen for union decl", .{}); }, .keyword_enum => { + if (container_decl.layout_token) |t| { + return mod.failTok(scope, t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{}); + } + // Count total fields as well as how many have explicitly provided tag values. + const counts = blk: { + var values: usize = 0; + var total: usize = 0; + for (container_decl.ast.members) |member_node| { + const member = switch (node_tags[member_node]) { + .container_field_init => tree.containerFieldInit(member_node), + .container_field_align => tree.containerFieldAlign(member_node), + .container_field => tree.containerField(member_node), + else => continue, + }; + if (member.comptime_token) |comptime_token| { + return mod.failTok(scope, comptime_token, "enum fields cannot be marked comptime", .{}); + } + if (member.ast.type_expr != 0) { + return mod.failNode(scope, member.ast.type_expr, "enum fields do not have types", .{}); + } + if (member.ast.align_expr != 0) { + return mod.failNode(scope, member.ast.align_expr, "enum fields do not have alignments", .{}); + } + total += 1; + if (member.ast.value_expr != 0) { + values += 1; + } + } + break :blk .{ + .total = total, + .values = values, + }; + }; + if (counts.values == 0) { + // No explicitly provided tag values! In this case, we can construct the + // enum type in AstGen and it will be correctly shared by all generic function + // instantiations and comptime function calls. + return mod.failNode(scope, node, "TODO AstGen for simple enum decl", .{}); + } return mod.failTok(scope, container_decl.ast.main_token, "TODO AstGen for enum decl", .{}); }, .keyword_opaque => { @@ -1905,7 +1934,10 @@ fn errorSetDecl( const tree = gz.tree(); const main_tokens = tree.nodes.items(.main_token); const token_tags = tree.tokens.items(.tag); - const arena = gz.astgen.arena; + + // This needs to be changed to create a Decl and reference that Decl. + // The AstGen arena memory will be destroyed after Sema. + //const arena = gz.astgen.arena; // Count how many fields there are. const error_token = main_tokens[node]; @@ -1922,37 +1954,39 @@ fn errorSetDecl( } else unreachable; // TODO should not need else unreachable here }; - const fields = try arena.alloc([]const u8, count); - { - var tok_i = error_token + 2; - var field_i: usize = 0; - while (true) : (tok_i += 1) { - switch (token_tags[tok_i]) { - .doc_comment, .comma => {}, - .identifier => { - fields[field_i] = try mod.identifierTokenString(scope, tok_i); - field_i += 1; - }, - .r_brace => break, - else => unreachable, - } - } - } - const error_set = try arena.create(Module.ErrorSet); - error_set.* = .{ - .owner_decl = gz.astgen.decl, - .node_offset = gz.astgen.decl.nodeIndexToRelative(node), - .names_ptr = fields.ptr, - .names_len = @intCast(u32, fields.len), - }; - const error_set_ty = try Type.Tag.error_set.create(arena, error_set); - const typed_value = try arena.create(TypedValue); - typed_value.* = .{ - .ty = Type.initTag(.type), - .val = try Value.Tag.ty.create(arena, error_set_ty), - }; - const result = try gz.addConst(typed_value); - return rvalue(gz, scope, rl, result, node); + @panic("TODO update AstGen.errorSetDecl"); + + //const fields = try arena.alloc([]const u8, count); + //{ + // var tok_i = error_token + 2; + // var field_i: usize = 0; + // while (true) : (tok_i += 1) { + // switch (token_tags[tok_i]) { + // .doc_comment, .comma => {}, + // .identifier => { + // fields[field_i] = try mod.identifierTokenString(scope, tok_i); + // field_i += 1; + // }, + // .r_brace => break, + // else => unreachable, + // } + // } + //} + //const error_set = try arena.create(Module.ErrorSet); + //error_set.* = .{ + // .owner_decl = gz.astgen.decl, + // .node_offset = gz.astgen.decl.nodeIndexToRelative(node), + // .names_ptr = fields.ptr, + // .names_len = @intCast(u32, fields.len), + //}; + //const error_set_ty = try Type.Tag.error_set.create(arena, error_set); + //const typed_value = try arena.create(TypedValue); + //typed_value.* = .{ + // .ty = Type.initTag(.type), + // .val = try Value.Tag.ty.create(arena, error_set_ty), + //}; + //const result = try gz.addConst(typed_value); + //return rvalue(gz, scope, rl, result, node); } fn orelseCatchExpr( @@ -3955,6 +3989,20 @@ fn builtinCall( .bit_cast => return bitCast(gz, scope, rl, node, params[0], params[1]), .TypeOf => return typeOf(gz, scope, rl, node, params), + .int_to_enum => { + const result = try gz.addPlNode(.int_to_enum, node, zir.Inst.Bin{ + .lhs = try typeExpr(gz, scope, params[0]), + .rhs = try expr(gz, scope, .none, params[1]), + }); + return rvalue(gz, scope, rl, result, node); + }, + + .enum_to_int => { + const operand = try expr(gz, scope, .none, params[0]); + const result = try gz.addUnNode(.enum_to_int, operand, node); + return rvalue(gz, scope, rl, result, node); + }, + .add_with_overflow, .align_cast, .align_of, @@ -3981,7 +4029,6 @@ fn builtinCall( .div_floor, .div_trunc, .embed_file, - .enum_to_int, .error_name, .error_return_trace, .err_set_cast, @@ -3991,7 +4038,6 @@ fn builtinCall( .float_to_int, .has_decl, .has_field, - .int_to_enum, .int_to_float, .int_to_ptr, .memcpy, diff --git a/src/BuiltinFn.zig b/src/BuiltinFn.zig index 1710169dc..a5d15554a 100644 --- a/src/BuiltinFn.zig +++ b/src/BuiltinFn.zig @@ -484,7 +484,7 @@ pub const list = list: { "@intToEnum", .{ .tag = .int_to_enum, - .param_count = 1, + .param_count = 2, }, }, .{ diff --git a/src/Module.zig b/src/Module.zig index bab61730e..0c1bb7311 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -387,6 +387,23 @@ pub const Struct = struct { } }; +/// Represents the data that an enum declaration provides. +pub const Enum = struct { + owner_decl: *Decl, + /// An integer type which is used for the numerical value of the enum. + tag_ty: Type, + /// Set of field names in declaration order. + fields: std.StringArrayHashMapUnmanaged(void), + /// If this is `null`, then this is an enum with no value overrides; + /// it is only a list of tag names. Otherwise, this contains the + /// tag values. Corresponds to `fields`. + field_values: ?[*]Value, + /// Represents the declarations inside this struct. + container: Scope.Container, + /// Offset from `owner_decl`, points to the enum decl AST node. + node_offset: i32, +}; + /// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. /// Extern functions do not have this data structure; they are represented by /// the `Decl` only, with a `Value` tag of `extern_fn`. diff --git a/src/Sema.zig b/src/Sema.zig index 0b5a21ce4..4db879aac 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -168,7 +168,6 @@ pub fn analyzeBody( .cmp_lte => try sema.zirCmp(block, inst, .lte), .cmp_neq => try sema.zirCmp(block, inst, .neq), .coerce_result_ptr => try sema.zirCoerceResultPtr(block, inst), - .@"const" => try sema.zirConst(block, inst), .decl_ref => try sema.zirDeclRef(block, inst), .decl_val => try sema.zirDeclVal(block, inst), .load => try sema.zirLoad(block, inst), @@ -179,6 +178,8 @@ pub fn analyzeBody( .elem_val_node => try sema.zirElemValNode(block, inst), .enum_literal => try sema.zirEnumLiteral(block, inst), .enum_literal_small => try sema.zirEnumLiteralSmall(block, inst), + .enum_to_int => try sema.zirEnumToInt(block, inst), + .int_to_enum => try sema.zirIntToEnum(block, inst), .err_union_code => try sema.zirErrUnionCode(block, inst), .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst), .err_union_payload_safe => try sema.zirErrUnionPayload(block, inst, true), @@ -498,18 +499,6 @@ fn resolveInstConst( }; } -fn zirConst(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { - const tracy = trace(@src()); - defer tracy.end(); - - const tv_ptr = sema.code.instructions.items(.data)[inst].@"const"; - // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions - // after analysis. This happens, for example, with variable declaration initialization - // expressions. - const typed_value_copy = try tv_ptr.copy(sema.arena); - return sema.mod.constInst(sema.arena, .unneeded, typed_value_copy); -} - fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1852,6 +1841,92 @@ fn zirEnumLiteralSmall(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) I }); } +fn zirEnumToInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { + const mod = sema.mod; + const arena = sema.arena; + const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const src = inst_data.src(); + const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const operand = try sema.resolveInst(inst_data.operand); + + const enum_tag: *Inst = switch (operand.ty.zigTypeTag()) { + .Enum => operand, + .Union => { + //if (!operand.ty.unionHasTag()) { + // return mod.fail( + // &block.base, + // operand_src, + // "untagged union '{}' cannot be converted to integer", + // .{dest_ty_src}, + // ); + //} + return mod.fail(&block.base, operand_src, "TODO zirEnumToInt for tagged unions", .{}); + }, + else => { + return mod.fail(&block.base, operand_src, "expected enum or tagged union, found {}", .{ + operand.ty, + }); + }, + }; + + const enum_obj = enum_tag.castTag(.@"enum").?.data; + const int_tag_ty = enum_obj.tag_ty; + + if (enum_tag.ty.onePossibleValue()) |opv| { + return mod.constInst(arena, src, .{ + .ty = int_tag_ty, + .val = opv, + }); + } + + if (enum_tag.value()) |enum_tag_val| { + const tv = enum_tag_val.enumToInt(enum_tag.ty); + return mod.constInst(arena, src, tv); + } + + try sema.requireRuntimeBlock(block, src); + return block.addUnOp(src, int_tag_ty, .bitcast, enum_tag); +} + +fn zirIntToEnum(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { + const mod = sema.mod; + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; + const src = inst_data.src(); + const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; + const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); + const operand = try sema.resolveInst(extra.rhs); + + if (dest_ty.zigTypeTag() != .Enum) { + return mod.fail(&block.base, dest_ty_src, "expected enum, found {}", .{dest_ty}); + } + + if (!dest_ty.enumIsExhaustive()) { + if (operand.value()) |int_val| { + return mod.constInst(arena, src, .{ + .ty = dest_ty, + .val = int_val, + }); + } + } + + if (try sema.resolveDefinedValue(block, operand_src, operand)) |int_val| { + if (!dest_ty.enumHasInt(int_val)) { + return mod.fail(&block.base, src, "enum '{}' has no tag with value {}", .{ + dest_ty, int_val, + }); + } + return mod.constInst(arena, src, .{ + .ty = dest_ty, + .val = int_val, + }); + } + + try sema.requireRuntimeBlock(block, src); + return block.addUnOp(src, dest_ty, .bitcast, operand); +} + /// Pointer in, pointer out. fn zirOptionalPayloadPtr( sema: *Sema, diff --git a/src/type.zig b/src/type.zig index f5ce296e4..254983027 100644 --- a/src/type.zig +++ b/src/type.zig @@ -96,6 +96,7 @@ pub const Type = extern union { .empty_struct => return .Struct, .empty_struct_literal => return .Struct, .@"struct" => return .Struct, + .@"enum" => return .Enum, .var_args_param => unreachable, // can be any type } @@ -614,6 +615,7 @@ pub const Type = extern union { .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name), .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope), .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct), + .@"enum" => return self.copyPayloadShallow(allocator, Payload.Enum), .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque), } } @@ -629,8 +631,8 @@ pub const Type = extern union { self: Type, comptime fmt: []const u8, options: std.fmt.FormatOptions, - out_stream: anytype, - ) @TypeOf(out_stream).Error!void { + writer: anytype, + ) @TypeOf(writer).Error!void { comptime assert(fmt.len == 0); var ty = self; while (true) { @@ -670,132 +672,145 @@ pub const Type = extern union { .comptime_float, .noreturn, .var_args_param, - => return out_stream.writeAll(@tagName(t)), - - .enum_literal => return out_stream.writeAll("@Type(.EnumLiteral)"), - .@"null" => return out_stream.writeAll("@Type(.Null)"), - .@"undefined" => return out_stream.writeAll("@Type(.Undefined)"), - - .empty_struct, .empty_struct_literal => return out_stream.writeAll("struct {}"), - .@"struct" => return out_stream.writeAll("(struct)"), - .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"), - .const_slice_u8 => return out_stream.writeAll("[]const u8"), - .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), - .fn_void_no_args => return out_stream.writeAll("fn() void"), - .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), - .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"), - .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"), + => return writer.writeAll(@tagName(t)), + + .enum_literal => return writer.writeAll("@Type(.EnumLiteral)"), + .@"null" => return writer.writeAll("@Type(.Null)"), + .@"undefined" => return writer.writeAll("@Type(.Undefined)"), + + .empty_struct, .empty_struct_literal => return writer.writeAll("struct {}"), + + .@"struct" => { + const struct_obj = self.castTag(.@"struct").?.data; + return struct_obj.renderName(writer); + }, + .@"enum" => { + const enum_obj = self.castTag(.@"enum").?.data; + return enum_obj.renderName(writer); + }, + .@"opaque" => { + const opaque_obj = self.castTag(.@"opaque").?.data; + return opaque_obj.renderName(writer); + }, + + .anyerror_void_error_union => return writer.writeAll("anyerror!void"), + .const_slice_u8 => return writer.writeAll("[]const u8"), + .fn_noreturn_no_args => return writer.writeAll("fn() noreturn"), + .fn_void_no_args => return writer.writeAll("fn() void"), + .fn_naked_noreturn_no_args => return writer.writeAll("fn() callconv(.Naked) noreturn"), + .fn_ccc_void_no_args => return writer.writeAll("fn() callconv(.C) void"), + .single_const_pointer_to_comptime_int => return writer.writeAll("*const comptime_int"), .function => { const payload = ty.castTag(.function).?.data; - try out_stream.writeAll("fn("); + try writer.writeAll("fn("); for (payload.param_types) |param_type, i| { - if (i != 0) try out_stream.writeAll(", "); - try param_type.format("", .{}, out_stream); + if (i != 0) try writer.writeAll(", "); + try param_type.format("", .{}, writer); } if (payload.is_var_args) { if (payload.param_types.len != 0) { - try out_stream.writeAll(", "); + try writer.writeAll(", "); } - try out_stream.writeAll("..."); + try writer.writeAll("..."); } - try out_stream.writeAll(") callconv(."); - try out_stream.writeAll(@tagName(payload.cc)); - try out_stream.writeAll(")"); + try writer.writeAll(") callconv(."); + try writer.writeAll(@tagName(payload.cc)); + try writer.writeAll(")"); ty = payload.return_type; continue; }, .array_u8 => { const len = ty.castTag(.array_u8).?.data; - return out_stream.print("[{d}]u8", .{len}); + return writer.print("[{d}]u8", .{len}); }, .array_u8_sentinel_0 => { const len = ty.castTag(.array_u8_sentinel_0).?.data; - return out_stream.print("[{d}:0]u8", .{len}); + return writer.print("[{d}:0]u8", .{len}); }, .array => { const payload = ty.castTag(.array).?.data; - try out_stream.print("[{d}]", .{payload.len}); + try writer.print("[{d}]", .{payload.len}); ty = payload.elem_type; continue; }, .array_sentinel => { const payload = ty.castTag(.array_sentinel).?.data; - try out_stream.print("[{d}:{}]", .{ payload.len, payload.sentinel }); + try writer.print("[{d}:{}]", .{ payload.len, payload.sentinel }); ty = payload.elem_type; continue; }, .single_const_pointer => { const pointee_type = ty.castTag(.single_const_pointer).?.data; - try out_stream.writeAll("*const "); + try writer.writeAll("*const "); ty = pointee_type; continue; }, .single_mut_pointer => { const pointee_type = ty.castTag(.single_mut_pointer).?.data; - try out_stream.writeAll("*"); + try writer.writeAll("*"); ty = pointee_type; continue; }, .many_const_pointer => { const pointee_type = ty.castTag(.many_const_pointer).?.data; - try out_stream.writeAll("[*]const "); + try writer.writeAll("[*]const "); ty = pointee_type; continue; }, .many_mut_pointer => { const pointee_type = ty.castTag(.many_mut_pointer).?.data; - try out_stream.writeAll("[*]"); + try writer.writeAll("[*]"); ty = pointee_type; continue; }, .c_const_pointer => { const pointee_type = ty.castTag(.c_const_pointer).?.data; - try out_stream.writeAll("[*c]const "); + try writer.writeAll("[*c]const "); ty = pointee_type; continue; }, .c_mut_pointer => { const pointee_type = ty.castTag(.c_mut_pointer).?.data; - try out_stream.writeAll("[*c]"); + try writer.writeAll("[*c]"); ty = pointee_type; continue; }, .const_slice => { const pointee_type = ty.castTag(.const_slice).?.data; - try out_stream.writeAll("[]const "); + try writer.writeAll("[]const "); ty = pointee_type; continue; }, .mut_slice => { const pointee_type = ty.castTag(.mut_slice).?.data; - try out_stream.writeAll("[]"); + try writer.writeAll("[]"); ty = pointee_type; continue; }, .int_signed => { const bits = ty.castTag(.int_signed).?.data; - return out_stream.print("i{d}", .{bits}); + return writer.print("i{d}", .{bits}); }, .int_unsigned => { const bits = ty.castTag(.int_unsigned).?.data; - return out_stream.print("u{d}", .{bits}); + return writer.print("u{d}", .{bits}); }, .optional => { const child_type = ty.castTag(.optional).?.data; - try out_stream.writeByte('?'); + try writer.writeByte('?'); ty = child_type; continue; }, .optional_single_const_pointer => { const pointee_type = ty.castTag(.optional_single_const_pointer).?.data; - try out_stream.writeAll("?*const "); + try writer.writeAll("?*const "); ty = pointee_type; continue; }, .optional_single_mut_pointer => { const pointee_type = ty.castTag(.optional_single_mut_pointer).?.data; - try out_stream.writeAll("?*"); + try writer.writeAll("?*"); ty = pointee_type; continue; }, @@ -804,48 +819,46 @@ pub const Type = extern union { const payload = ty.castTag(.pointer).?.data; if (payload.sentinel) |some| switch (payload.size) { .One, .C => unreachable, - .Many => try out_stream.print("[*:{}]", .{some}), - .Slice => try out_stream.print("[:{}]", .{some}), + .Many => try writer.print("[*:{}]", .{some}), + .Slice => try writer.print("[:{}]", .{some}), } else switch (payload.size) { - .One => try out_stream.writeAll("*"), - .Many => try out_stream.writeAll("[*]"), - .C => try out_stream.writeAll("[*c]"), - .Slice => try out_stream.writeAll("[]"), + .One => try writer.writeAll("*"), + .Many => try writer.writeAll("[*]"), + .C => try writer.writeAll("[*c]"), + .Slice => try writer.writeAll("[]"), } if (payload.@"align" != 0) { - try out_stream.print("align({d}", .{payload.@"align"}); + try writer.print("align({d}", .{payload.@"align"}); if (payload.bit_offset != 0) { - try out_stream.print(":{d}:{d}", .{ payload.bit_offset, payload.host_size }); + try writer.print(":{d}:{d}", .{ payload.bit_offset, payload.host_size }); } - try out_stream.writeAll(") "); + try writer.writeAll(") "); } - if (!payload.mutable) try out_stream.writeAll("const "); - if (payload.@"volatile") try out_stream.writeAll("volatile "); - if (payload.@"allowzero") try out_stream.writeAll("allowzero "); + if (!payload.mutable) try writer.writeAll("const "); + if (payload.@"volatile") try writer.writeAll("volatile "); + if (payload.@"allowzero") try writer.writeAll("allowzero "); ty = payload.pointee_type; continue; }, .error_union => { const payload = ty.castTag(.error_union).?.data; - try payload.error_set.format("", .{}, out_stream); - try out_stream.writeAll("!"); + try payload.error_set.format("", .{}, writer); + try writer.writeAll("!"); ty = payload.payload; continue; }, .error_set => { const error_set = ty.castTag(.error_set).?.data; - return out_stream.writeAll(std.mem.spanZ(error_set.owner_decl.name)); + return writer.writeAll(std.mem.spanZ(error_set.owner_decl.name)); }, .error_set_single => { const name = ty.castTag(.error_set_single).?.data; - return out_stream.print("error{{{s}}}", .{name}); + return writer.print("error{{{s}}}", .{name}); }, - .inferred_alloc_const => return out_stream.writeAll("(inferred_alloc_const)"), - .inferred_alloc_mut => return out_stream.writeAll("(inferred_alloc_mut)"), - // TODO use declaration name - .@"opaque" => return out_stream.writeAll("opaque {}"), + .inferred_alloc_const => return writer.writeAll("(inferred_alloc_const)"), + .inferred_alloc_mut => return writer.writeAll("(inferred_alloc_mut)"), } unreachable; } @@ -954,6 +967,10 @@ pub const Type = extern union { return false; } }, + .@"enum" => { + const enum_obj = self.castTag(.@"enum").?.data; + return enum_obj.fields.count() >= 2; + }, // TODO lazy types .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0, @@ -1112,11 +1129,35 @@ pub const Type = extern union { } else if (!payload.payload.hasCodeGenBits()) { return payload.error_set.abiAlignment(target); } - @panic("TODO abiAlignment error union"); + return std.math.max( + payload.payload.abiAlignment(target), + payload.error_set.abiAlignment(target), + ); }, .@"struct" => { - @panic("TODO abiAlignment struct"); + // TODO take into account field alignment + // also make this possible to fail, and lazy + // I think we need to move all the functions from type.zig which can + // fail into Sema. + // Probably will need to introduce multi-stage struct resolution just + // like we have in stage1. + const struct_obj = self.castTag(.@"struct").?.data; + var biggest: u32 = 0; + for (struct_obj.fields.entries.items) |entry| { + const field_ty = entry.value.ty; + if (!field_ty.hasCodeGenBits()) continue; + const field_align = field_ty.abiAlignment(target); + if (field_align > biggest) { + return field_align; + } + } + assert(biggest != 0); + return biggest; + }, + .@"enum" => { + const enum_obj = self.castTag(.@"enum").?.data; + return enum_obj.tag_ty.abiAlignment(target); }, .c_void, @@ -1166,6 +1207,10 @@ pub const Type = extern union { .@"struct" => { @panic("TODO abiSize struct"); }, + .@"enum" => { + const enum_obj = self.castTag(.@"enum").?.data; + return enum_obj.tag_ty.abiSize(target); + }, .u8, .i8, @@ -1278,74 +1323,6 @@ pub const Type = extern union { pub fn isSinglePointer(self: Type) bool { return switch (self.tag()) { - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .f16, - .f32, - .f64, - .f128, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .const_slice_u8, - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .int_unsigned, - .int_signed, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .@"opaque", - .var_args_param, - => false, - .single_const_pointer, .single_mut_pointer, .single_const_pointer_to_comptime_int, @@ -1354,73 +1331,14 @@ pub const Type = extern union { => true, .pointer => self.castTag(.pointer).?.data.size == .One, + + else => false, }; } /// Asserts the `Type` is a pointer. pub fn ptrSize(self: Type) std.builtin.TypeInfo.Pointer.Size { return switch (self.tag()) { - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .f16, - .f32, - .f64, - .f128, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .int_unsigned, - .int_signed, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .empty_struct, - .empty_struct_literal, - .@"opaque", - .@"struct", - .var_args_param, - => unreachable, - .const_slice, .mut_slice, .const_slice_u8, @@ -1442,159 +1360,26 @@ pub const Type = extern union { => .One, .pointer => self.castTag(.pointer).?.data.size, + + else => unreachable, }; } pub fn isSlice(self: Type) bool { return switch (self.tag()) { - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .f16, - .f32, - .f64, - .f128, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .single_const_pointer_to_comptime_int, - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .int_unsigned, - .int_signed, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"struct", - .@"opaque", - .var_args_param, - => false, - .const_slice, .mut_slice, .const_slice_u8, => true, .pointer => self.castTag(.pointer).?.data.size == .Slice, + + else => false, }; } pub fn isConstPtr(self: Type) bool { return switch (self.tag()) { - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .f16, - .f32, - .f64, - .f128, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .int_unsigned, - .int_signed, - .single_mut_pointer, - .many_mut_pointer, - .c_mut_pointer, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .mut_slice, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"struct", - .@"opaque", - .var_args_param, - => false, - .single_const_pointer, .many_const_pointer, .c_const_pointer, @@ -1604,170 +1389,40 @@ pub const Type = extern union { => true, .pointer => !self.castTag(.pointer).?.data.mutable, + + else => false, }; } pub fn isVolatilePtr(self: Type) bool { return switch (self.tag()) { - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .f16, - .f32, - .f64, - .f128, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .int_unsigned, - .int_signed, - .single_mut_pointer, - .single_const_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"struct", - .@"opaque", - .var_args_param, - => false, - .pointer => { const payload = self.castTag(.pointer).?.data; return payload.@"volatile"; }, + else => false, }; } pub fn isAllowzeroPtr(self: Type) bool { return switch (self.tag()) { - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .f16, - .f32, - .f64, - .f128, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .int_unsigned, - .int_signed, - .single_mut_pointer, - .single_const_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"struct", - .@"opaque", - .var_args_param, - => false, - .pointer => { const payload = self.castTag(.pointer).?.data; return payload.@"allowzero"; }, + else => false, + }; + } + + pub fn isCPtr(self: Type) bool { + return switch (self.tag()) { + .c_const_pointer, + .c_mut_pointer, + => return true, + + .pointer => self.castTag(.pointer).?.data.size == .C, + + else => return false, }; } @@ -1833,64 +1488,6 @@ pub const Type = extern union { /// Asserts the type is a pointer or array type. pub fn elemType(self: Type) Type { return switch (self.tag()) { - .u8 => unreachable, - .i8 => unreachable, - .u16 => unreachable, - .i16 => unreachable, - .u32 => unreachable, - .i32 => unreachable, - .u64 => unreachable, - .i64 => unreachable, - .u128 => unreachable, - .i128 => unreachable, - .usize => unreachable, - .isize => unreachable, - .c_short => unreachable, - .c_ushort => unreachable, - .c_int => unreachable, - .c_uint => unreachable, - .c_long => unreachable, - .c_ulong => unreachable, - .c_longlong => unreachable, - .c_ulonglong => unreachable, - .c_longdouble => unreachable, - .f16 => unreachable, - .f32 => unreachable, - .f64 => unreachable, - .f128 => unreachable, - .c_void => unreachable, - .bool => unreachable, - .void => unreachable, - .type => unreachable, - .anyerror => unreachable, - .comptime_int => unreachable, - .comptime_float => unreachable, - .noreturn => unreachable, - .@"null" => unreachable, - .@"undefined" => unreachable, - .fn_noreturn_no_args => unreachable, - .fn_void_no_args => unreachable, - .fn_naked_noreturn_no_args => unreachable, - .fn_ccc_void_no_args => unreachable, - .function => unreachable, - .int_unsigned => unreachable, - .int_signed => unreachable, - .optional => unreachable, - .optional_single_const_pointer => unreachable, - .optional_single_mut_pointer => unreachable, - .enum_literal => unreachable, - .error_union => unreachable, - .anyerror_void_error_union => unreachable, - .error_set => unreachable, - .error_set_single => unreachable, - .@"struct" => unreachable, - .empty_struct => unreachable, - .empty_struct_literal => unreachable, - .inferred_alloc_const => unreachable, - .inferred_alloc_mut => unreachable, - .@"opaque" => unreachable, - .var_args_param => unreachable, - .array => self.castTag(.array).?.data.elem_type, .array_sentinel => self.castTag(.array_sentinel).?.data.elem_type, .single_const_pointer, @@ -1902,9 +1499,12 @@ pub const Type = extern union { .const_slice, .mut_slice, => self.castPointer().?.data, + .array_u8, .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8), .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), .pointer => self.castTag(.pointer).?.data.pointee_type, + + else => unreachable, }; } @@ -1972,96 +1572,120 @@ pub const Type = extern union { /// Asserts the type is an array or vector. pub fn arrayLen(self: Type) u64 { return switch (self.tag()) { - .u8, + .array => self.castTag(.array).?.data.len, + .array_sentinel => self.castTag(.array_sentinel).?.data.len, + .array_u8 => self.castTag(.array_u8).?.data, + .array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data, + + else => unreachable, + }; + } + + /// Asserts the type is an array, pointer or vector. + pub fn sentinel(self: Type) ?Value { + return switch (self.tag()) { + .single_const_pointer, + .single_mut_pointer, + .many_const_pointer, + .many_mut_pointer, + .c_const_pointer, + .c_mut_pointer, + .single_const_pointer_to_comptime_int, + .array, + .array_u8, + => return null, + + .pointer => return self.castTag(.pointer).?.data.sentinel, + .array_sentinel => return self.castTag(.array_sentinel).?.data.sentinel, + .array_u8_sentinel_0 => return Value.initTag(.zero), + + else => unreachable, + }; + } + + /// Returns true if and only if the type is a fixed-width integer. + pub fn isInt(self: Type) bool { + return self.isSignedInt() or self.isUnsignedInt(); + } + + /// Returns true if and only if the type is a fixed-width, signed integer. + pub fn isSignedInt(self: Type) bool { + return switch (self.tag()) { + .int_signed, .i8, - .u16, + .isize, + .c_short, + .c_int, + .c_long, + .c_longlong, .i16, - .u32, .i32, - .u64, .i64, - .u128, .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .f16, - .f32, - .f64, - .f128, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .pointer, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .int_unsigned, - .int_signed, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"opaque", - .var_args_param, - => unreachable, + => true, - .array => self.castTag(.array).?.data.len, - .array_sentinel => self.castTag(.array_sentinel).?.data.len, - .array_u8 => self.castTag(.array_u8).?.data, - .array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data, + else => false, }; } - /// Asserts the type is an array, pointer or vector. - pub fn sentinel(self: Type) ?Value { + /// Returns true if and only if the type is a fixed-width, unsigned integer. + pub fn isUnsignedInt(self: Type) bool { return switch (self.tag()) { + .int_unsigned, .u8, - .i8, + .usize, + .c_ushort, + .c_uint, + .c_ulong, + .c_ulonglong, .u16, - .i16, .u32, - .i32, .u64, - .i64, .u128, - .i128, + => true, + + else => false, + }; + } + + /// Asserts the type is an integer. + pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } { + return switch (self.tag()) { + .int_unsigned => .{ + .signedness = .unsigned, + .bits = self.castTag(.int_unsigned).?.data, + }, + .int_signed => .{ + .signedness = .signed, + .bits = self.castTag(.int_signed).?.data, + }, + .u8 => .{ .signedness = .unsigned, .bits = 8 }, + .i8 => .{ .signedness = .signed, .bits = 8 }, + .u16 => .{ .signedness = .unsigned, .bits = 16 }, + .i16 => .{ .signedness = .signed, .bits = 16 }, + .u32 => .{ .signedness = .unsigned, .bits = 32 }, + .i32 => .{ .signedness = .signed, .bits = 32 }, + .u64 => .{ .signedness = .unsigned, .bits = 64 }, + .i64 => .{ .signedness = .signed, .bits = 64 }, + .u128 => .{ .signedness = .unsigned, .bits = 128 }, + .i128 => .{ .signedness = .signed, .bits = 128 }, + .usize => .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, + .isize => .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, + .c_short => .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) }, + .c_ushort => .{ .signedness = .unsigned, .bits = CType.ushort.sizeInBits(target) }, + .c_int => .{ .signedness = .signed, .bits = CType.int.sizeInBits(target) }, + .c_uint => .{ .signedness = .unsigned, .bits = CType.uint.sizeInBits(target) }, + .c_long => .{ .signedness = .signed, .bits = CType.long.sizeInBits(target) }, + .c_ulong => .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) }, + .c_longlong => .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) }, + .c_ulonglong => .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) }, + + else => unreachable, + }; + } + + pub fn isNamedInt(self: Type) bool { + return switch (self.tag()) { .usize, .isize, .c_short, @@ -2072,399 +1696,13 @@ pub const Type = extern union { .c_ulong, .c_longlong, .c_ulonglong, - .c_longdouble, - .f16, - .f32, - .f64, - .f128, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .const_slice, - .mut_slice, - .const_slice_u8, - .int_unsigned, - .int_signed, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"opaque", - .var_args_param, - => unreachable, - - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .single_const_pointer_to_comptime_int, - .array, - .array_u8, - => return null, + => true, - .pointer => return self.castTag(.pointer).?.data.sentinel, - .array_sentinel => return self.castTag(.array_sentinel).?.data.sentinel, - .array_u8_sentinel_0 => return Value.initTag(.zero), + else => false, }; } - /// Returns true if and only if the type is a fixed-width integer. - pub fn isInt(self: Type) bool { - return self.isSignedInt() or self.isUnsignedInt(); - } - - /// Returns true if and only if the type is a fixed-width, signed integer. - pub fn isSignedInt(self: Type) bool { - return switch (self.tag()) { - .f16, - .f32, - .f64, - .f128, - .c_longdouble, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .pointer, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .int_unsigned, - .u8, - .usize, - .c_ushort, - .c_uint, - .c_ulong, - .c_ulonglong, - .u16, - .u32, - .u64, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"opaque", - .var_args_param, - => false, - - .int_signed, - .i8, - .isize, - .c_short, - .c_int, - .c_long, - .c_longlong, - .i16, - .i32, - .i64, - .u128, - .i128, - => true, - }; - } - - /// Returns true if and only if the type is a fixed-width, unsigned integer. - pub fn isUnsignedInt(self: Type) bool { - return switch (self.tag()) { - .f16, - .f32, - .f64, - .f128, - .c_longdouble, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .pointer, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .int_signed, - .i8, - .isize, - .c_short, - .c_int, - .c_long, - .c_longlong, - .i16, - .i32, - .i64, - .u128, - .i128, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"opaque", - .var_args_param, - => false, - - .int_unsigned, - .u8, - .usize, - .c_ushort, - .c_uint, - .c_ulong, - .c_ulonglong, - .u16, - .u32, - .u64, - => true, - }; - } - - /// Asserts the type is an integer. - pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } { - return switch (self.tag()) { - .f16, - .f32, - .f64, - .f128, - .c_longdouble, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .pointer, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"opaque", - .var_args_param, - => unreachable, - - .int_unsigned => .{ - .signedness = .unsigned, - .bits = self.castTag(.int_unsigned).?.data, - }, - .int_signed => .{ - .signedness = .signed, - .bits = self.castTag(.int_signed).?.data, - }, - .u8 => .{ .signedness = .unsigned, .bits = 8 }, - .i8 => .{ .signedness = .signed, .bits = 8 }, - .u16 => .{ .signedness = .unsigned, .bits = 16 }, - .i16 => .{ .signedness = .signed, .bits = 16 }, - .u32 => .{ .signedness = .unsigned, .bits = 32 }, - .i32 => .{ .signedness = .signed, .bits = 32 }, - .u64 => .{ .signedness = .unsigned, .bits = 64 }, - .i64 => .{ .signedness = .signed, .bits = 64 }, - .u128 => .{ .signedness = .unsigned, .bits = 128 }, - .i128 => .{ .signedness = .signed, .bits = 128 }, - .usize => .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, - .isize => .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, - .c_short => .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) }, - .c_ushort => .{ .signedness = .unsigned, .bits = CType.ushort.sizeInBits(target) }, - .c_int => .{ .signedness = .signed, .bits = CType.int.sizeInBits(target) }, - .c_uint => .{ .signedness = .unsigned, .bits = CType.uint.sizeInBits(target) }, - .c_long => .{ .signedness = .signed, .bits = CType.long.sizeInBits(target) }, - .c_ulong => .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) }, - .c_longlong => .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) }, - .c_ulonglong => .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) }, - }; - } - - pub fn isNamedInt(self: Type) bool { - return switch (self.tag()) { - .f16, - .f32, - .f64, - .f128, - .c_longdouble, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .pointer, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .int_unsigned, - .int_signed, - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"opaque", - .var_args_param, - => false, - - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - => true, - }; - } - - pub fn isFloat(self: Type) bool { + pub fn isFloat(self: Type) bool { return switch (self.tag()) { .f16, .f32, @@ -2499,74 +1737,7 @@ pub const Type = extern union { .fn_ccc_void_no_args => 0, .function => self.castTag(.function).?.data.param_types.len, - .f16, - .f32, - .f64, - .f128, - .c_longdouble, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .pointer, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .int_unsigned, - .int_signed, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"opaque", - .var_args_param, - => unreachable, + else => unreachable, }; } @@ -2583,400 +1754,61 @@ pub const Type = extern union { std.mem.copy(Type, types, payload.param_types); }, - .f16, - .f32, - .f64, - .f128, - .c_longdouble, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .pointer, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .int_unsigned, - .int_signed, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"opaque", - .var_args_param, - => unreachable, - } - } - - /// Asserts the type is a function. - pub fn fnParamType(self: Type, index: usize) Type { - switch (self.tag()) { - .function => { - const payload = self.castTag(.function).?.data; - return payload.param_types[index]; - }, - - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .f16, - .f32, - .f64, - .f128, - .c_longdouble, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .pointer, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .int_unsigned, - .int_signed, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"opaque", - .var_args_param, - => unreachable, + else => unreachable, } } - /// Asserts the type is a function. - pub fn fnReturnType(self: Type) Type { - return switch (self.tag()) { - .fn_noreturn_no_args => Type.initTag(.noreturn), - .fn_naked_noreturn_no_args => Type.initTag(.noreturn), - - .fn_void_no_args, - .fn_ccc_void_no_args, - => Type.initTag(.void), - - .function => self.castTag(.function).?.data.return_type, - - .f16, - .f32, - .f64, - .f128, - .c_longdouble, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .pointer, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .int_unsigned, - .int_signed, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"opaque", - .var_args_param, - => unreachable, - }; - } - - /// Asserts the type is a function. - pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention { - return switch (self.tag()) { - .fn_noreturn_no_args => .Unspecified, - .fn_void_no_args => .Unspecified, - .fn_naked_noreturn_no_args => .Naked, - .fn_ccc_void_no_args => .C, - .function => self.castTag(.function).?.data.cc, - - .f16, - .f32, - .f64, - .f128, - .c_longdouble, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .pointer, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .int_unsigned, - .int_signed, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"opaque", - .var_args_param, - => unreachable, - }; - } - - /// Asserts the type is a function. - pub fn fnIsVarArgs(self: Type) bool { - return switch (self.tag()) { - .fn_noreturn_no_args => false, - .fn_void_no_args => false, - .fn_naked_noreturn_no_args => false, - .fn_ccc_void_no_args => false, - .function => self.castTag(.function).?.data.is_var_args, - - .f16, - .f32, - .f64, - .f128, - .c_longdouble, - .c_void, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"null", - .@"undefined", - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .pointer, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .int_unsigned, - .int_signed, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"opaque", - .var_args_param, - => unreachable, + /// Asserts the type is a function. + pub fn fnParamType(self: Type, index: usize) Type { + switch (self.tag()) { + .function => { + const payload = self.castTag(.function).?.data; + return payload.param_types[index]; + }, + + else => unreachable, + } + } + + /// Asserts the type is a function. + pub fn fnReturnType(self: Type) Type { + return switch (self.tag()) { + .fn_noreturn_no_args => Type.initTag(.noreturn), + .fn_naked_noreturn_no_args => Type.initTag(.noreturn), + + .fn_void_no_args, + .fn_ccc_void_no_args, + => Type.initTag(.void), + + .function => self.castTag(.function).?.data.return_type, + + else => unreachable, + }; + } + + /// Asserts the type is a function. + pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention { + return switch (self.tag()) { + .fn_noreturn_no_args => .Unspecified, + .fn_void_no_args => .Unspecified, + .fn_naked_noreturn_no_args => .Naked, + .fn_ccc_void_no_args => .C, + .function => self.castTag(.function).?.data.cc, + + else => unreachable, + }; + } + + /// Asserts the type is a function. + pub fn fnIsVarArgs(self: Type) bool { + return switch (self.tag()) { + .fn_noreturn_no_args => false, + .fn_void_no_args => false, + .fn_naked_noreturn_no_args => false, + .fn_ccc_void_no_args => false, + .function => self.castTag(.function).?.data.is_var_args, + + else => unreachable, }; } @@ -3013,50 +1845,7 @@ pub const Type = extern union { .int_signed, => true, - .c_void, - .bool, - .void, - .type, - .anyerror, - .noreturn, - .@"null", - .@"undefined", - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .pointer, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"opaque", - .var_args_param, - => false, + else => false, }; } @@ -3127,6 +1916,18 @@ pub const Type = extern union { } return Value.initTag(.empty_struct_value); }, + .@"enum" => { + const enum_obj = self.castTag(.@"enum").?.data; + if (enum_obj.fields.count() == 1) { + if (enum_obj.field_values) |values_list| { + return values_list[0]; + } else { + return Type.initTag(.zero); + } + } else { + return null; + } + }, .empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value), .void => return Value.initTag(.void_value), @@ -3166,87 +1967,6 @@ pub const Type = extern union { }; } - pub fn isCPtr(self: Type) bool { - return switch (self.tag()) { - .f16, - .f32, - .f64, - .f128, - .c_longdouble, - .comptime_int, - .comptime_float, - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .bool, - .type, - .anyerror, - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .c_void, - .void, - .noreturn, - .@"null", - .@"undefined", - .int_unsigned, - .int_signed, - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .const_slice, - .mut_slice, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .@"struct", - .empty_struct, - .empty_struct_literal, - .inferred_alloc_const, - .inferred_alloc_mut, - .@"opaque", - .var_args_param, - => return false, - - .c_const_pointer, - .c_mut_pointer, - => return true, - - .pointer => self.castTag(.pointer).?.data.size == .C, - }; - } - pub fn isIndexable(self: Type) bool { const zig_tag = self.zigTypeTag(); // TODO tuples are indexable @@ -3257,80 +1977,12 @@ pub const Type = extern union { /// Asserts that the type is a container. (note: ErrorSet is not a container). pub fn getContainerScope(self: Type) *Module.Scope.Container { return switch (self.tag()) { - .f16, - .f32, - .f64, - .f128, - .c_longdouble, - .comptime_int, - .comptime_float, - .u8, - .i8, - .u16, - .i16, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .bool, - .type, - .anyerror, - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .single_const_pointer_to_comptime_int, - .const_slice_u8, - .c_void, - .void, - .noreturn, - .@"null", - .@"undefined", - .int_unsigned, - .int_signed, - .array, - .array_sentinel, - .array_u8, - .array_u8_sentinel_0, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .const_slice, - .mut_slice, - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .error_union, - .anyerror_void_error_union, - .error_set, - .error_set_single, - .c_const_pointer, - .c_mut_pointer, - .pointer, - .inferred_alloc_const, - .inferred_alloc_mut, - .var_args_param, - .empty_struct_literal, - => unreachable, - .@"struct" => &self.castTag(.@"struct").?.data.container, + .@"enum" => &self.castTag(.@"enum").?.data.container, .empty_struct => self.castTag(.empty_struct).?.data, .@"opaque" => &self.castTag(.@"opaque").?.data, + + else => unreachable, }; } @@ -3482,6 +2134,7 @@ pub const Type = extern union { empty_struct, @"opaque", @"struct", + @"enum", pub const last_no_payload_tag = Tag.inferred_alloc_const; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -3568,6 +2221,7 @@ pub const Type = extern union { .error_set_single => Payload.Name, .@"opaque" => Payload.Opaque, .@"struct" => Payload.Struct, + .@"enum" => Payload.Enum, .empty_struct => Payload.ContainerScope, }; } @@ -3705,6 +2359,11 @@ pub const Type = extern union { base: Payload = .{ .tag = .@"struct" }, data: *Module.Struct, }; + + pub const Enum = struct { + base: Payload = .{ .tag = .@"enum" }, + data: *Module.Enum, + }; }; }; diff --git a/src/zir.zig b/src/zir.zig index c70ef17fc..e64f16336 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -267,9 +267,6 @@ pub const Inst = struct { /// only the taken branch is analyzed. The then block and else block must /// terminate with an "inline" variant of a noreturn instruction. condbr_inline, - /// A comptime known value. - /// Uses the `const` union field. - @"const", /// A struct type definition. Contains references to ZIR instructions for /// the field types, defaults, and alignments. /// Uses the `pl_node` union field. Payload is `StructDecl`. @@ -667,6 +664,12 @@ pub const Inst = struct { /// A struct literal with a specified type, with no fields. /// Uses the `un_node` field. struct_init_empty, + /// Converts an integer into an enum value. + /// Uses `pl_node` with payload `Bin`. `lhs` is enum type, `rhs` is operand. + int_to_enum, + /// Converts an enum value into an integer. Resulting type will be the tag type + /// of the enum. Uses `un_node`. + enum_to_int, /// Returns whether the instruction is one of the control flow "noreturn" types. /// Function calls do not count. @@ -712,7 +715,6 @@ pub const Inst = struct { .cmp_gt, .cmp_neq, .coerce_result_ptr, - .@"const", .struct_decl, .struct_decl_packed, .struct_decl_extern, @@ -822,6 +824,8 @@ pub const Inst = struct { .switch_block_ref_under_multi, .validate_struct_init_ptr, .struct_init_empty, + .int_to_enum, + .enum_to_int, => false, .@"break", @@ -1184,7 +1188,6 @@ pub const Inst = struct { } }, bin: Bin, - @"const": *TypedValue, /// For strings which may contain null bytes. str: struct { /// Offset into `string_bytes`. @@ -1581,6 +1584,7 @@ const Writer = struct { .typeof, .typeof_elem, .struct_init_empty, + .enum_to_int, => try self.writeUnNode(stream, inst), .ref, @@ -1594,7 +1598,6 @@ const Writer = struct { => try self.writeBoolBr(stream, inst), .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst), - .@"const" => try self.writeConst(stream, inst), .param_type => try self.writeParamType(stream, inst), .ptr_type_simple => try self.writePtrTypeSimple(stream, inst), .ptr_type => try self.writePtrType(stream, inst), @@ -1647,6 +1650,7 @@ const Writer = struct { .merge_error_sets, .bit_and, .bit_or, + .int_to_enum, => try self.writePlNodeBin(stream, inst), .call, @@ -1773,15 +1777,6 @@ const Writer = struct { try stream.writeAll("TODO)"); } - fn writeConst( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const inst_data = self.code.instructions.items(.data)[inst].@"const"; - try stream.writeAll("TODO)"); - } - fn writeParamType( self: *Writer, stream: anytype,