commit
e207031837
|
@ -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: comptime_int,
|
||||
is_generic: bool,
|
||||
is_var_args: bool,
|
||||
return_type: ?type,
|
||||
|
|
|
@ -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,38 +25572,42 @@ 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_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);
|
||||
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<ZigValue>();
|
||||
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);
|
||||
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<ZigValue>();
|
||||
fn_arg_array->special = ConstValSpecialStatic;
|
||||
|
@ -25611,7 +25615,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<ZigValue>(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];
|
||||
|
@ -26327,10 +26331,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);
|
||||
|
||||
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;
|
||||
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<FnTypeParamInfo>(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 = bigint_as_u32(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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -416,3 +416,30 @@ test "Type.Union from regular enum" {
|
|||
_ = T;
|
||||
_ = @typeInfo(T).Union;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}.func;
|
||||
const Foo = @Type(@typeInfo(@TypeOf(foo)));
|
||||
const foo_2: Foo = foo;
|
||||
}
|
||||
|
||||
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 {}
|
||||
};
|
||||
const test_instance: TestStruct = undefined;
|
||||
testing.expect(std.meta.eql(
|
||||
@typeName(@TypeOf(test_instance.foo)),
|
||||
@typeName(@Type(@typeInfo(@TypeOf(test_instance.foo)))),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
@ -273,11 +275,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 +290,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 {
|
||||
|
|
Loading…
Reference in New Issue