diff --git a/src/all_types.hpp b/src/all_types.hpp index 1a97cf281469..4253a346f887 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1534,6 +1534,7 @@ enum BuiltinFnId { BuiltinFnIdMemberName, BuiltinFnIdField, BuiltinFnIdTypeInfo, + BuiltinFnIdType, BuiltinFnIdHasField, BuiltinFnIdTypeof, BuiltinFnIdAddWithOverflow, @@ -2436,6 +2437,7 @@ enum IrInstructionId { IrInstructionIdByteOffsetOf, IrInstructionIdBitOffsetOf, IrInstructionIdTypeInfo, + IrInstructionIdType, IrInstructionIdHasField, IrInstructionIdTypeId, IrInstructionIdSetEvalBranchQuota, @@ -3472,6 +3474,12 @@ struct IrInstructionTypeInfo { IrInstruction *type_value; }; +struct IrInstructionType { + IrInstruction base; + + IrInstruction *type_info; +}; + struct IrInstructionHasField { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index d1499592d2f9..d7d4b4443711 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5770,6 +5770,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdByteOffsetOf: case IrInstructionIdBitOffsetOf: case IrInstructionIdTypeInfo: + case IrInstructionIdType: case IrInstructionIdHasField: case IrInstructionIdTypeId: case IrInstructionIdSetEvalBranchQuota: @@ -7597,6 +7598,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2); create_builtin_fn(g, BuiltinFnIdField, "field", 2); create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1); + create_builtin_fn(g, BuiltinFnIdType, "Type", 1); create_builtin_fn(g, BuiltinFnIdHasField, "hasField", 2); create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); diff --git a/src/ir.cpp b/src/ir.cpp index 7415a2dd6b18..953467c185e0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -912,6 +912,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeInfo *) { return IrInstructionIdTypeInfo; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionType *) { + return IrInstructionIdType; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionHasField *) { return IrInstructionIdHasField; } @@ -2907,6 +2911,15 @@ static IrInstruction *ir_build_type_info(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } +static IrInstruction *ir_build_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_info) { + IrInstructionType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_info = type_info; + + ir_ref_instruction(type_info, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_type_id(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { @@ -5046,6 +5059,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *type_info = ir_build_type_info(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, type_info, lval, result_loc); } + case BuiltinFnIdType: + { + AstNode *arg_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg = ir_gen_node(irb, arg_node, scope); + if (arg == irb->codegen->invalid_instruction) + return arg; + + IrInstruction *type = ir_build_type(irb, scope, node, arg); + return ir_lval_wrap(irb, scope, type, lval, result_loc); + } case BuiltinFnIdBreakpoint: return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval, result_loc); case BuiltinFnIdReturnAddress: @@ -20104,6 +20127,18 @@ static uint32_t ptr_len_to_size_enum_index(PtrLen ptr_len) { zig_unreachable(); } +static PtrLen size_enum_index_to_ptr_len(uint32_t size_enum_index) { + switch (size_enum_index) { + case 0: + return PtrLenSingle; + case 1: + return PtrLenUnknown; + case 3: + return PtrLenC; + } + zig_unreachable(); +} + static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_entry) { Error err; ZigType *attrs_type; @@ -20789,6 +20824,147 @@ static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira, return result; } +static ConstExprValue *get_const_field(IrAnalyze *ira, ConstExprValue *struct_value, const char *name, size_t field_index) +{ + ensure_field_index(struct_value->type, name, field_index); + assert(struct_value->data.x_struct.fields[field_index].special == ConstValSpecialStatic); + return &struct_value->data.x_struct.fields[field_index]; +} + +static bool get_const_field_bool(IrAnalyze *ira, ConstExprValue *struct_value, const char *name, size_t field_index) +{ + ConstExprValue *value = get_const_field(ira, struct_value, name, field_index); + assert(value->type == ira->codegen->builtin_types.entry_bool); + return value->data.x_bool; +} + +static BigInt *get_const_field_lit_int(IrAnalyze *ira, ConstExprValue *struct_value, const char *name, size_t field_index) +{ + ConstExprValue *value = get_const_field(ira, struct_value, name, field_index); + assert(value->type == ira->codegen->builtin_types.entry_num_lit_int); + return &value->data.x_bigint; +} + +static ZigType *get_const_field_meta_type(IrAnalyze *ira, ConstExprValue *struct_value, const char *name, size_t field_index) +{ + ConstExprValue *value = get_const_field(ira, struct_value, name, field_index); + assert(value->type == ira->codegen->builtin_types.entry_type); + return value->data.x_type; +} + +static ZigType *type_info_to_type(IrAnalyze *ira, IrInstruction *instruction, ZigTypeId tagTypeId, ConstExprValue *payload) { + switch (tagTypeId) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdMetaType: + return ira->codegen->builtin_types.entry_type; + case ZigTypeIdVoid: + return ira->codegen->builtin_types.entry_void; + case ZigTypeIdBool: + return ira->codegen->builtin_types.entry_bool; + case ZigTypeIdUnreachable: + return ira->codegen->builtin_types.entry_unreachable; + case ZigTypeIdInt: + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "Int", nullptr)); + return get_int_type(ira->codegen, + get_const_field_bool(ira, payload, "is_signed", 0), + bigint_as_u32(get_const_field_lit_int(ira, payload, "bits", 1))); + case ZigTypeIdFloat: + { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "Float", nullptr)); + uint32_t bits = bigint_as_u32(get_const_field_lit_int(ira, payload, "bits", 0)); + switch (bits) { + case 16: return ira->codegen->builtin_types.entry_f16; + case 32: return ira->codegen->builtin_types.entry_f32; + case 64: return ira->codegen->builtin_types.entry_f64; + case 128: return ira->codegen->builtin_types.entry_f128; + } + ir_add_error(ira, instruction, + buf_sprintf("%d-bit float unsupported", bits)); + return nullptr; + } + case ZigTypeIdPointer: + { + ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr); + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == type_info_pointer_type); + ConstExprValue *size_value = get_const_field(ira, payload, "size", 0); + assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type)); + uint32_t size_enum_index = bigint_as_u32(&size_value->data.x_enum_tag); + PtrLen ptr_len; + if (size_enum_index == 2) { + ptr_len = PtrLenUnknown; // TODO: is this right? + } else { + ptr_len = size_enum_index_to_ptr_len(size_enum_index); + } + ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, + get_const_field_meta_type(ira, payload, "child", 4), + get_const_field_bool(ira, payload, "is_const", 1), + get_const_field_bool(ira, payload, "is_volatile", 2), + ptr_len, + bigint_as_u32(get_const_field_lit_int(ira, payload, "alignment", 3)), + 0, // bit_offset_in_host??? + 0, // host_int_bytes??? + get_const_field_bool(ira, payload, "is_allowzero", 5) + ); + if (size_enum_index != 2) + return ptr_type; + return get_slice_type(ira->codegen, ptr_type); + } + case ZigTypeIdComptimeFloat: + return ira->codegen->builtin_types.entry_num_lit_float; + case ZigTypeIdComptimeInt: + return ira->codegen->builtin_types.entry_num_lit_int; + case ZigTypeIdUndefined: + return ira->codegen->builtin_types.entry_undef; + case ZigTypeIdNull: + return ira->codegen->builtin_types.entry_null; + case ZigTypeIdArray: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdOpaque: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + case ZigTypeIdVector: + case ZigTypeIdEnumLiteral: + ir_add_error(ira, instruction, buf_sprintf( + "TODO implement @Type forr 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907\n", type_id_name(tagTypeId))); + return nullptr; + case ZigTypeIdUnion: + case ZigTypeIdFn: + case ZigTypeIdBoundFn: + case ZigTypeIdArgTuple: + case ZigTypeIdStruct: + ir_add_error(ira, instruction, buf_sprintf( + "@Type not availble for 'TypeInfo.%s'\n", type_id_name(tagTypeId))); + return nullptr; + } + zig_unreachable(); +} + +static IrInstruction *ir_analyze_instruction_type(IrAnalyze *ira, IrInstructionType *instruction) { + IrInstruction *type_info_ir = instruction->type_info->child; + if (type_is_invalid(type_info_ir->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *casted_ir = ir_implicit_cast(ira, type_info_ir, ir_type_info_get_type(ira, nullptr, nullptr)); + if (type_is_invalid(casted_ir->value.type)) + return ira->codegen->invalid_instruction; + + ConstExprValue *type_info_value = ir_resolve_const(ira, casted_ir, UndefBad); + if (!type_info_value) + return ira->codegen->invalid_instruction; + ZigTypeId typeId = type_id_at_index(bigint_as_usize(&type_info_value->data.x_union.tag)); + ZigType *type = type_info_to_type(ira, type_info_ir, typeId, type_info_value->data.x_union.payload); + if (!type) + return ira->codegen->invalid_instruction; + return ir_const_type(ira, &instruction->base, type); +} + static IrInstruction *ir_analyze_instruction_type_id(IrAnalyze *ira, IrInstructionTypeId *instruction) { @@ -25295,6 +25471,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction return ir_analyze_instruction_bit_offset_of(ira, (IrInstructionBitOffsetOf *)instruction); case IrInstructionIdTypeInfo: return ir_analyze_instruction_type_info(ira, (IrInstructionTypeInfo *) instruction); + case IrInstructionIdType: + return ir_analyze_instruction_type(ira, (IrInstructionType *)instruction); case IrInstructionIdHasField: return ir_analyze_instruction_has_field(ira, (IrInstructionHasField *) instruction); case IrInstructionIdTypeId: @@ -25598,6 +25776,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdByteOffsetOf: case IrInstructionIdBitOffsetOf: case IrInstructionIdTypeInfo: + case IrInstructionIdType: case IrInstructionIdHasField: case IrInstructionIdTypeId: case IrInstructionIdAlignCast: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 6de585ec6fcb..25eb01365ff6 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -280,6 +280,8 @@ static const char* ir_instruction_type_str(IrInstruction* instruction) { return "BitOffsetOf"; case IrInstructionIdTypeInfo: return "TypeInfo"; + case IrInstructionIdType: + return "Type"; case IrInstructionIdHasField: return "HasField"; case IrInstructionIdTypeId: @@ -1627,6 +1629,12 @@ static void ir_print_type_info(IrPrint *irp, IrInstructionTypeInfo *instruction) fprintf(irp->f, ")"); } +static void ir_print_type(IrPrint *irp, IrInstructionType *instruction) { + fprintf(irp->f, "@Type("); + ir_print_other_instruction(irp, instruction->type_info); + fprintf(irp->f, ")"); +} + static void ir_print_has_field(IrPrint *irp, IrInstructionHasField *instruction) { fprintf(irp->f, "@hasField("); ir_print_other_instruction(irp, instruction->container_type); @@ -2258,6 +2266,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool case IrInstructionIdTypeInfo: ir_print_type_info(irp, (IrInstructionTypeInfo *)instruction); break; + case IrInstructionIdType: + ir_print_type(irp, (IrInstructionType *)instruction); + break; case IrInstructionIdHasField: ir_print_has_field(irp, (IrInstructionHasField *)instruction); break; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 871ff63e2365..1438a5953983 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,58 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { + + cases.add( + "wrong type for @Type", + \\export fn entry() void { + \\ _ = @Type(0); + \\} + , + "tmp.zig:2:15: error: expected type 'builtin.TypeInfo', found 'comptime_int'", + ); + + cases.add( + "@Type with non-constant expression", + \\const builtin = @import("builtin"); + \\var globalTypeInfo : builtin.TypeInfo = undefined; + \\export fn entry() void { + \\ _ = @Type(globalTypeInfo); + \\} + , + "tmp.zig:4:15: error: unable to evaluate constant expression", + ); + + cases.add( + "@Type with TypeInfo.Int", + \\const builtin = @import("builtin"); + \\export fn entry() void { + \\ _ = @Type(builtin.TypeInfo.Int { + \\ .is_signed = true, + \\ .bits = 8, + \\ }); + \\} + , + "tmp.zig:3:36: error: expected type 'builtin.TypeInfo', found 'builtin.Int'", + ); + + cases.add( + "Struct unavailable for @Type", + \\export fn entry() void { + \\ _ = @Type(@typeInfo(struct { })); + \\} + , + "tmp.zig:2:15: error: @Type not availble for 'TypeInfo.Struct'", + ); + + cases.add( + "array not implemented for @Type", + \\export fn entry() void { + \\ _ = @Type(@typeInfo(enum{x})); + \\} + , + "tmp.zig:2:15: error: TODO implement @Type forr 'TypeInfo.Enum': see https://github.com/ziglang/zig/issues/2907", + ); + cases.add( "wrong type for result ptr to @asyncCall", \\export fn entry() void { diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 23ec3e53ce6a..db6cdad3b103 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -93,6 +93,7 @@ comptime { _ = @import("behavior/this.zig"); _ = @import("behavior/truncate.zig"); _ = @import("behavior/try.zig"); + _ = @import("behavior/type.zig"); _ = @import("behavior/type_info.zig"); _ = @import("behavior/typename.zig"); _ = @import("behavior/undefined.zig"); diff --git a/test/stage1/behavior/type.zig b/test/stage1/behavior/type.zig new file mode 100644 index 000000000000..318e07fbdfb7 --- /dev/null +++ b/test/stage1/behavior/type.zig @@ -0,0 +1,111 @@ +const builtin = @import("builtin"); +const TypeInfo = builtin.TypeInfo; + +const std = @import("std"); +const testing = std.testing; + +fn testTypes(comptime types: []const type) void { + inline for (types) |testType| { + testing.expect(testType == @Type(@typeInfo(testType))); + } +} + +test "Type.MetaType" { + testing.expect(type == @Type(TypeInfo { .Type = undefined })); + testTypes([_]type {type}); +} + +test "Type.Void" { + testing.expect(void == @Type(TypeInfo { .Void = undefined })); + testTypes([_]type {void}); +} + +test "Type.Bool" { + testing.expect(bool == @Type(TypeInfo { .Bool = undefined })); + testTypes([_]type {bool}); +} + +test "Type.NoReturn" { + testing.expect(noreturn == @Type(TypeInfo { .NoReturn = undefined })); + testTypes([_]type {noreturn}); +} + +test "Type.Int" { + testing.expect(u1 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = false, .bits = 1 } })); + testing.expect(i1 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = true, .bits = 1 } })); + testing.expect(u8 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = false, .bits = 8 } })); + testing.expect(i8 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = true, .bits = 8 } })); + testing.expect(u64 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = false, .bits = 64 } })); + testing.expect(i64 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = true, .bits = 64 } })); + testTypes([_]type {u8,u32,i64}); + // TODO: should this work? + //testing.expect(u1 == @Type(TypeInfo.Int { .is_signed = false, .bits = 1 } )); +} + +test "Type.Float" { + testing.expect(f16 == @Type(TypeInfo { .Float = TypeInfo.Float { .bits = 16 } })); + testing.expect(f32 == @Type(TypeInfo { .Float = TypeInfo.Float { .bits = 32 } })); + testing.expect(f64 == @Type(TypeInfo { .Float = TypeInfo.Float { .bits = 64 } })); + testing.expect(f128 == @Type(TypeInfo { .Float = TypeInfo.Float { .bits = 128 } })); + testTypes([_]type {f16, f32, f64, f128}); + // error: 17-bit float unsupported + //testing.expect(f16 == @Type(TypeInfo { .Float = TypeInfo.Float { .bits = 17 } })); +} + +test "Type.Pointer" { + testTypes([_]type { + // One Value Pointer Types + *u8, *const u8, + *volatile u8, *const volatile u8, + *align(4) u8, *const align(4) u8, + *volatile align(4) u8, *const volatile align(4) u8, + *align(8) u8, *const align(8) u8, + *volatile align(8) u8, *const volatile align(8) u8, + *allowzero u8, *const allowzero u8, + *volatile allowzero u8, *const volatile allowzero u8, + *align(4) allowzero u8, *const align(4) allowzero u8, + *volatile align(4) allowzero u8, *const volatile align(4) allowzero u8, + // Many Values Pointer Types + [*]u8, [*]const u8, + [*]volatile u8, [*]const volatile u8, + [*]align(4) u8, [*]const align(4) u8, + [*]volatile align(4) u8, [*]const volatile align(4) u8, + [*]align(8) u8, [*]const align(8) u8, + [*]volatile align(8) u8, [*]const volatile align(8) u8, + [*]allowzero u8, [*]const allowzero u8, + [*]volatile allowzero u8, [*]const volatile allowzero u8, + [*]align(4) allowzero u8, [*]const align(4) allowzero u8, + [*]volatile align(4) allowzero u8, [*]const volatile align(4) allowzero u8, + // Slice Types + []u8, []const u8, + []volatile u8, []const volatile u8, + []align(4) u8, []const align(4) u8, + []volatile align(4) u8, []const volatile align(4) u8, + []align(8) u8, []const align(8) u8, + []volatile align(8) u8, []const volatile align(8) u8, + []allowzero u8, []const allowzero u8, + []volatile allowzero u8, []const volatile allowzero u8, + []align(4) allowzero u8, []const align(4) allowzero u8, + []volatile align(4) allowzero u8, []const volatile align(4) allowzero u8, + // C Pointer Types + [*c]u8, [*c]const u8, + [*c]volatile u8, [*c]const volatile u8, + [*c]align(4) u8, [*c]const align(4) u8, + [*c]volatile align(4) u8, [*c]const volatile align(4) u8, + [*c]align(8) u8, [*c]const align(8) u8, + [*c]volatile align(8) u8, [*c]const volatile align(8) u8, + }); +} + +test "Type.ComptimeFloat" { + testTypes([_]type {comptime_float}); +} +test "Type.ComptimeInt" { + testTypes([_]type {comptime_int}); +} +test "Type.Undefined" { + testTypes([_]type {@typeOf(undefined)}); +} +test "Type.Null" { + testTypes([_]type {@typeOf(null)}); +}