Commit b728cb6d4e

Jonathan Marler <jonathan.j.marler@hp.com>
2019-08-23 17:33:33
Add @Type builtin
1 parent 77a5f88
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;
 
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);
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<IrInstructionType>(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:
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;
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)});
+}
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");
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 {