From 97ab720d84ac6af5174ed93f371fedfa2331445d Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Wed, 23 Sep 2020 11:49:44 -0600 Subject: [PATCH 1/4] stage1: Add alignment to TypeInfo.Fn --- lib/std/builtin.zig | 1 + src/stage1/ir.cpp | 37 +++++++++++++++++------------- test/stage1/behavior/type_info.zig | 6 ++++- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index d80d0e88fe..34452bee09 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -343,6 +343,7 @@ pub const TypeInfo = union(enum) { /// therefore must be kept in sync with the compiler implementation. pub const Fn = struct { calling_convention: CallingConvention, + alignment: u29, is_generic: bool, is_var_args: bool, return_type: ?type, diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 8da207cab7..6d309ef16b 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -25564,7 +25564,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Fn", nullptr); - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 5); + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 6); result->data.x_struct.fields = fields; // calling_convention: TypeInfo.CallingConvention @@ -25572,30 +25572,35 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy fields[0]->special = ConstValSpecialStatic; fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention"); bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc); - // is_generic: bool - ensure_field_index(result->type, "is_generic", 1); - bool is_generic = type_entry->data.fn.is_generic; + // alignment: u29 + ensure_field_index(result->type, "alignment", 1); fields[1]->special = ConstValSpecialStatic; - fields[1]->type = ira->codegen->builtin_types.entry_bool; - fields[1]->data.x_bool = is_generic; - // is_varargs: bool - ensure_field_index(result->type, "is_var_args", 2); - bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args; + fields[1]->type = ira->codegen->builtin_types.entry_u29; + bigint_init_unsigned(&fields[1]->data.x_bigint, type_entry->data.fn.fn_type_id.alignment); + // is_generic: bool + ensure_field_index(result->type, "is_generic", 2); + bool is_generic = type_entry->data.fn.is_generic; fields[2]->special = ConstValSpecialStatic; fields[2]->type = ira->codegen->builtin_types.entry_bool; - fields[2]->data.x_bool = type_entry->data.fn.fn_type_id.is_var_args; - // return_type: ?type - ensure_field_index(result->type, "return_type", 3); + fields[2]->data.x_bool = is_generic; + // is_varargs: bool + ensure_field_index(result->type, "is_var_args", 3); + bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args; fields[3]->special = ConstValSpecialStatic; - fields[3]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); + fields[3]->type = ira->codegen->builtin_types.entry_bool; + fields[3]->data.x_bool = is_varargs; + // return_type: ?type + ensure_field_index(result->type, "return_type", 4); + fields[4]->special = ConstValSpecialStatic; + fields[4]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.fn.fn_type_id.return_type == nullptr) - fields[3]->data.x_optional = nullptr; + fields[4]->data.x_optional = nullptr; else { ZigValue *return_type = ira->codegen->pass1_arena->create(); return_type->special = ConstValSpecialStatic; return_type->type = ira->codegen->builtin_types.entry_type; return_type->data.x_type = type_entry->data.fn.fn_type_id.return_type; - fields[3]->data.x_optional = return_type; + fields[4]->data.x_optional = return_type; } // args: []TypeInfo.FnArg ZigType *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg", nullptr); @@ -25611,7 +25616,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy fn_arg_array->data.x_array.special = ConstArraySpecialNone; fn_arg_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(fn_arg_count); - init_const_slice(ira->codegen, fields[4], fn_arg_array, 0, fn_arg_count, false); + init_const_slice(ira->codegen, fields[5], fn_arg_array, 0, fn_arg_count, false); for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { FnTypeParamInfo *fn_param_info = &type_entry->data.fn.fn_type_id.param_info[fn_arg_index]; diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig index f5b37fe3f0..bb6dede96d 100644 --- a/test/stage1/behavior/type_info.zig +++ b/test/stage1/behavior/type_info.zig @@ -273,11 +273,14 @@ test "type info: function type info" { fn testFunction() void { const fn_info = @typeInfo(@TypeOf(foo)); expect(fn_info == .Fn); + expect(fn_info.Fn.alignment == 0); expect(fn_info.Fn.calling_convention == .C); expect(!fn_info.Fn.is_generic); expect(fn_info.Fn.args.len == 2); expect(fn_info.Fn.is_var_args); expect(fn_info.Fn.return_type.? == usize); + const fn_aligned_info = @typeInfo(@TypeOf(fooAligned)); + expect(fn_aligned_info.Fn.alignment == 4); const test_instance: TestStruct = undefined; const bound_fn_info = @typeInfo(@TypeOf(test_instance.foo)); @@ -285,7 +288,8 @@ fn testFunction() void { expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); } -extern fn foo(a: usize, b: bool, ...) usize; +extern fn foo(a: usize, b: bool, ...) callconv(.C) usize; +extern fn fooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize; test "typeInfo with comptime parameter in struct fn def" { const S = struct { From e18fdc12b06cb904814700a96dce5ba419d08364 Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Wed, 23 Sep 2020 13:12:26 -0600 Subject: [PATCH 2/4] stage1: Implement @Type for Fn and BoundFn --- src/stage1/ir.cpp | 117 ++++++++++++++++++++++++++++++++-- test/compile_errors.zig | 47 ++++++++++++++ test/stage1/behavior/type.zig | 21 ++++++ 3 files changed, 179 insertions(+), 6 deletions(-) diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 6d309ef16b..162fa1bea5 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -25607,8 +25607,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy if ((err = type_resolve(ira->codegen, type_info_fn_arg_type, ResolveStatusSizeKnown))) { zig_unreachable(); } - size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count - - (is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC); + size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count; ZigValue *fn_arg_array = ira->codegen->pass1_arena->create(); fn_arg_array->special = ConstValSpecialStatic; @@ -25757,6 +25756,17 @@ static Error get_const_field_bool(IrAnalyze *ira, AstNode *source_node, ZigValue return ErrorNone; } +static Error get_const_field_u29(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, + const char *name, size_t field_index, uint32_t *out) +{ + ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); + if (value == nullptr) + return ErrorSemanticAnalyzeFail; + assert(value->type == ira->codegen->builtin_types.entry_u29); + *out = bigint_as_u32(&value->data.x_bigint); + return ErrorNone; +} + static BigInt *get_const_field_lit_int(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index) { ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); @@ -26332,10 +26342,105 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI return entry; } case ZigTypeIdFn: - case ZigTypeIdBoundFn: - ir_add_error(ira, source_instr, buf_sprintf( - "@Type not available for 'TypeInfo.%s'", type_id_name(tagTypeId))); - return ira->codegen->invalid_inst_gen->value->type; + case ZigTypeIdBoundFn: { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "Fn", nullptr)); + + ZigValue *cc_value = get_const_field(ira, source_instr->source_node, payload, "calling_convention", 0); + if (cc_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + assert(cc_value->special == ConstValSpecialStatic); + assert(cc_value->type == get_builtin_type(ira->codegen, "CallingConvention")); + CallingConvention cc = (CallingConvention)bigint_as_u32(&cc_value->data.x_enum_tag); + + uint32_t alignment; + if ((err = get_const_field_u29(ira, source_instr->source_node, payload, "alignment", 1, &alignment))) + return ira->codegen->invalid_inst_gen->value->type; + + Error err; + bool is_generic; + if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_generic", 2, &is_generic))) + return ira->codegen->invalid_inst_gen->value->type; + if (is_generic) { + ir_add_error(ira, source_instr, buf_sprintf("TypeInfo.Fn.is_generic must be false for @Type")); + return ira->codegen->invalid_inst_gen->value->type; + } + + bool is_var_args; + if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_var_args", 3, &is_var_args))) + return ira->codegen->invalid_inst_gen->value->type; + if (is_var_args && cc != CallingConventionC) { + ir_add_error(ira, source_instr, buf_sprintf("varargs functions must have C calling convention")); + return ira->codegen->invalid_inst_gen->value->type; + } + + ZigType *return_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "return_type", 4); + if (return_type == nullptr) { + ir_add_error(ira, source_instr, buf_sprintf("TypeInfo.Fn.return_type must be non-null for @Type")); + return ira->codegen->invalid_inst_gen->value->type; + } + + ZigValue *args_value = get_const_field(ira, source_instr->source_node, payload, "args", 5); + if (args_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + assert(args_value->special == ConstValSpecialStatic); + assert(is_slice(args_value->type)); + ZigValue *args_ptr = args_value->data.x_struct.fields[slice_ptr_index]; + ZigValue *args_len_value = args_value->data.x_struct.fields[slice_len_index]; + size_t args_len = bigint_as_usize(&args_len_value->data.x_bigint); + + FnTypeId fn_type_id = {}; + fn_type_id.return_type = return_type; + fn_type_id.param_info = heap::c_allocator.allocate(args_len); + fn_type_id.param_count = args_len; + fn_type_id.next_param_index = args_len; + fn_type_id.is_var_args = is_var_args; + fn_type_id.cc = cc; + fn_type_id.alignment = alignment; + + assert(args_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray); + assert(args_ptr->data.x_ptr.data.base_array.elem_index == 0); + ZigValue *args_arr = args_ptr->data.x_ptr.data.base_array.array_val; + assert(args_arr->special == ConstValSpecialStatic); + assert(args_arr->data.x_array.special == ConstArraySpecialNone); + for (size_t i = 0; i < args_len; i++) { + ZigValue *arg_value = &args_arr->data.x_array.data.s_none.elements[i]; + assert(arg_value->type == ir_type_info_get_type(ira, "FnArg", nullptr)); + FnTypeParamInfo *info = &fn_type_id.param_info[i]; + Error err; + bool is_generic; + if ((err = get_const_field_bool(ira, source_instr->source_node, arg_value, "is_generic", 0, &is_generic))) + return ira->codegen->invalid_inst_gen->value->type; + if (is_generic) { + ir_add_error(ira, source_instr, buf_sprintf("TypeInfo.FnArg.is_generic must be false for @Type")); + return ira->codegen->invalid_inst_gen->value->type; + } + if ((err = get_const_field_bool(ira, source_instr->source_node, arg_value, "is_noalias", 1, &info->is_noalias))) + return ira->codegen->invalid_inst_gen->value->type; + ZigType *type = get_const_field_meta_type_optional( + ira, source_instr->source_node, arg_value, "arg_type", 2); + if (type == nullptr) { + ir_add_error(ira, source_instr, buf_sprintf("TypeInfo.FnArg.arg_type must be non-null for @Type")); + return ira->codegen->invalid_inst_gen->value->type; + } + info->type = type; + } + + ZigType *entry = get_fn_type(ira->codegen, &fn_type_id); + + switch (tagTypeId) { + case ZigTypeIdFn: + return entry; + case ZigTypeIdBoundFn: { + ZigType *bound_fn_entry = new_type_table_entry(ZigTypeIdBoundFn); + bound_fn_entry->name = *buf_sprintf("(bound %s)", buf_ptr(&entry->name)); + bound_fn_entry->data.bound_fn.fn_type = entry; + return bound_fn_entry; + } + default: + zig_unreachable(); + } + } } zig_unreachable(); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index fef0a64762..b1e23d40cb 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -72,6 +72,53 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:15:23: error: enum field missing: 'arst'", "tmp.zig:27:24: note: referenced here", }); + cases.add("@Type(.Fn) with is_generic = true", + \\const Foo = @Type(.{ + \\ .Fn = .{ + \\ .calling_convention = .Unspecified, + \\ .alignment = 0, + \\ .is_generic = true, + \\ .is_var_args = false, + \\ .return_type = u0, + \\ .args = &[_]@import("builtin").TypeInfo.FnArg{}, + \\ }, + \\}); + \\comptime { _ = Foo; } + , &[_][]const u8{ + "tmp.zig:1:20: error: TypeInfo.Fn.is_generic must be false for @Type", + }); + + cases.add("@Type(.Fn) with is_var_args = true and non-C callconv", + \\const Foo = @Type(.{ + \\ .Fn = .{ + \\ .calling_convention = .Unspecified, + \\ .alignment = 0, + \\ .is_generic = false, + \\ .is_var_args = true, + \\ .return_type = u0, + \\ .args = &[_]@import("builtin").TypeInfo.FnArg{}, + \\ }, + \\}); + \\comptime { _ = Foo; } + , &[_][]const u8{ + "tmp.zig:1:20: error: varargs functions must have C calling convention", + }); + + cases.add("@Type(.Fn) with return_type = null", + \\const Foo = @Type(.{ + \\ .Fn = .{ + \\ .calling_convention = .Unspecified, + \\ .alignment = 0, + \\ .is_generic = false, + \\ .is_var_args = false, + \\ .return_type = null, + \\ .args = &[_]@import("builtin").TypeInfo.FnArg{}, + \\ }, + \\}); + \\comptime { _ = Foo; } + , &[_][]const u8{ + "tmp.zig:1:20: error: TypeInfo.Fn.return_type must be non-null for @Type", + }); cases.add("@Type for union with opaque field", \\const TypeInfo = @import("builtin").TypeInfo; diff --git a/test/stage1/behavior/type.zig b/test/stage1/behavior/type.zig index 1bb0823e74..936ed0486c 100644 --- a/test/stage1/behavior/type.zig +++ b/test/stage1/behavior/type.zig @@ -416,3 +416,24 @@ test "Type.Union from regular enum" { _ = T; _ = @typeInfo(T).Union; } + +test "Type.Fn" { + const foo = struct { + fn func(a: usize, b: bool) align(4) callconv(.C) usize { + return 0; + } + }.func; + const Foo = @Type(@typeInfo(@TypeOf(foo))); + const foo_2: Foo = foo; +} + +test "Type.BoundFn" { + const TestStruct = packed struct { + pub fn foo(self: *const @This()) align(4) callconv(.Unspecified) void {} + }; + const test_instance: TestStruct = undefined; + testing.expect(std.meta.eql( + @typeName(@TypeOf(test_instance.foo)), + @typeName(@Type(@typeInfo(@TypeOf(test_instance.foo)))), + )); +} From 96a151d4b83ea4148db4d13b5576897645209f46 Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Thu, 24 Sep 2020 05:03:19 -0600 Subject: [PATCH 3/4] Skip @Type/@typeInfo Fn/BoundFn tests on wasm32/wasm64 --- test/stage1/behavior/type.zig | 6 ++++++ test/stage1/behavior/type_info.zig | 2 ++ 2 files changed, 8 insertions(+) diff --git a/test/stage1/behavior/type.zig b/test/stage1/behavior/type.zig index 936ed0486c..60a23ffa94 100644 --- a/test/stage1/behavior/type.zig +++ b/test/stage1/behavior/type.zig @@ -418,6 +418,9 @@ test "Type.Union from regular enum" { } test "Type.Fn" { + // wasm doesn't support align attributes on functions + if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest; + const foo = struct { fn func(a: usize, b: bool) align(4) callconv(.C) usize { return 0; @@ -428,6 +431,9 @@ test "Type.Fn" { } test "Type.BoundFn" { + // wasm doesn't support align attributes on functions + if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest; + const TestStruct = packed struct { pub fn foo(self: *const @This()) align(4) callconv(.Unspecified) void {} }; diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig index bb6dede96d..8b413bf031 100644 --- a/test/stage1/behavior/type_info.zig +++ b/test/stage1/behavior/type_info.zig @@ -266,6 +266,8 @@ const TestStruct = packed struct { }; test "type info: function type info" { + // wasm doesn't support align attributes on functions + if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest; testFunction(); comptime testFunction(); } From 183d1d4ba1a855cc85ac93f51dec5f4b2301ce1d Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Sat, 26 Sep 2020 12:00:41 -0600 Subject: [PATCH 4/4] Switch TypeInfo.Fn.alignment to comptime_int from u29 All integers in TypeInfo are intentionally comptime_int: https://github.com/ziglang/zig/issues/1683 --- lib/std/builtin.zig | 2 +- src/stage1/ir.cpp | 19 ++++--------------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 34452bee09..92fa78bc39 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -343,7 +343,7 @@ pub const TypeInfo = union(enum) { /// therefore must be kept in sync with the compiler implementation. pub const Fn = struct { calling_convention: CallingConvention, - alignment: u29, + alignment: comptime_int, is_generic: bool, is_var_args: bool, return_type: ?type, diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 162fa1bea5..045f1ad784 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -25575,7 +25575,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy // alignment: u29 ensure_field_index(result->type, "alignment", 1); fields[1]->special = ConstValSpecialStatic; - fields[1]->type = ira->codegen->builtin_types.entry_u29; + fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; bigint_init_unsigned(&fields[1]->data.x_bigint, type_entry->data.fn.fn_type_id.alignment); // is_generic: bool ensure_field_index(result->type, "is_generic", 2); @@ -25756,17 +25756,6 @@ static Error get_const_field_bool(IrAnalyze *ira, AstNode *source_node, ZigValue return ErrorNone; } -static Error get_const_field_u29(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, - const char *name, size_t field_index, uint32_t *out) -{ - ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); - if (value == nullptr) - return ErrorSemanticAnalyzeFail; - assert(value->type == ira->codegen->builtin_types.entry_u29); - *out = bigint_as_u32(&value->data.x_bigint); - return ErrorNone; -} - static BigInt *get_const_field_lit_int(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index) { ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); @@ -26353,8 +26342,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI assert(cc_value->type == get_builtin_type(ira->codegen, "CallingConvention")); CallingConvention cc = (CallingConvention)bigint_as_u32(&cc_value->data.x_enum_tag); - uint32_t alignment; - if ((err = get_const_field_u29(ira, source_instr->source_node, payload, "alignment", 1, &alignment))) + BigInt *alignment = get_const_field_lit_int(ira, source_instr->source_node, payload, "alignment", 1); + if (alignment == nullptr) return ira->codegen->invalid_inst_gen->value->type; Error err; @@ -26396,7 +26385,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI fn_type_id.next_param_index = args_len; fn_type_id.is_var_args = is_var_args; fn_type_id.cc = cc; - fn_type_id.alignment = alignment; + fn_type_id.alignment = bigint_as_u32(alignment); assert(args_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray); assert(args_ptr->data.x_ptr.data.base_array.elem_index == 0);