mirror of
https://github.com/ziglang/zig.git
synced 2026-02-20 16:24:51 +00:00
Merge branch 'LemonBoy-fix-439'
This commit is contained in:
commit
378bf1c3b7
@ -8371,12 +8371,14 @@ test "integer truncation" {
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@TypeOf#}
|
||||
<pre>{#syntax#}@TypeOf(expression) type{#endsyntax#}</pre>
|
||||
<pre>{#syntax#}@TypeOf(...) type{#endsyntax#}</pre>
|
||||
<p>
|
||||
This function returns a compile-time constant, which is the type of the
|
||||
expression passed as an argument. The expression is evaluated.
|
||||
{#syntax#}@TypeOf{#endsyntax#} is a special builtin function that takes any (nonzero) number of expressions
|
||||
as parameters and returns the type of the result, using {#link|Peer Type Resolution#}.
|
||||
</p>
|
||||
<p>
|
||||
The expressions are evaluated, however they are guaranteed to have no <em>runtime</em> side-effects:
|
||||
</p>
|
||||
<p>{#syntax#}@TypeOf{#endsyntax#} guarantees no run-time side-effects within the expression:</p>
|
||||
{#code_begin|test#}
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
@ -293,7 +293,7 @@ test "math.min" {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max(x: var, y: var) @TypeOf(x + y) {
|
||||
pub fn max(x: var, y: var) @TypeOf(x, y) {
|
||||
return if (x > y) x else y;
|
||||
}
|
||||
|
||||
|
||||
@ -3286,7 +3286,11 @@ struct IrInstGenUnreachable {
|
||||
struct IrInstSrcTypeOf {
|
||||
IrInstSrc base;
|
||||
|
||||
IrInstSrc *value;
|
||||
union {
|
||||
IrInstSrc *scalar; // value_count == 1
|
||||
IrInstSrc **list; // value_count > 1
|
||||
} value;
|
||||
size_t value_count;
|
||||
};
|
||||
|
||||
struct IrInstSrcSetCold {
|
||||
|
||||
@ -8142,7 +8142,7 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
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);
|
||||
create_builtin_fn(g, BuiltinFnIdTypeof, "TypeOf", SIZE_MAX);
|
||||
create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4);
|
||||
create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4);
|
||||
create_builtin_fn(g, BuiltinFnIdMulWithOverflow, "mulWithOverflow", 4);
|
||||
|
||||
77
src/ir.cpp
77
src/ir.cpp
@ -2766,9 +2766,24 @@ static IrInstGen *ir_build_load_ptr_gen(IrAnalyze *ira, IrInst *source_instructi
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstSrc *ir_build_typeof(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
|
||||
static IrInstSrc *ir_build_typeof_n(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstSrc **values, size_t value_count)
|
||||
{
|
||||
assert(value_count >= 2);
|
||||
|
||||
IrInstSrcTypeOf *instruction = ir_build_instruction<IrInstSrcTypeOf>(irb, scope, source_node);
|
||||
instruction->value = value;
|
||||
instruction->value.list = values;
|
||||
instruction->value_count = value_count;
|
||||
|
||||
for (size_t i = 0; i < value_count; i++)
|
||||
ir_ref_instruction(values[i], irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstSrc *ir_build_typeof_1(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
|
||||
IrInstSrcTypeOf *instruction = ir_build_instruction<IrInstSrcTypeOf>(irb, scope, source_node);
|
||||
instruction->value.scalar = value;
|
||||
|
||||
ir_ref_instruction(value, irb->current_basic_block);
|
||||
|
||||
@ -6043,7 +6058,7 @@ static IrInstSrc *ir_gen_fn_call_with_args(IrBuilderSrc *irb, Scope *scope, AstN
|
||||
if (fn_ref == irb->codegen->invalid_inst_src)
|
||||
return fn_ref;
|
||||
|
||||
IrInstSrc *fn_type = ir_build_typeof(irb, scope, source_node, fn_ref);
|
||||
IrInstSrc *fn_type = ir_build_typeof_1(irb, scope, source_node, fn_ref);
|
||||
|
||||
IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(args_len);
|
||||
for (size_t i = 0; i < args_len; i += 1) {
|
||||
@ -6104,12 +6119,33 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod
|
||||
{
|
||||
Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope);
|
||||
|
||||
AstNode *arg_node = node->data.fn_call_expr.params.at(0);
|
||||
IrInstSrc *arg = ir_gen_node(irb, arg_node, sub_scope);
|
||||
if (arg == irb->codegen->invalid_inst_src)
|
||||
return arg;
|
||||
size_t arg_count = node->data.fn_call_expr.params.length;
|
||||
|
||||
IrInstSrc *type_of = ir_build_typeof(irb, scope, node, arg);
|
||||
IrInstSrc *type_of;
|
||||
|
||||
if (arg_count == 0) {
|
||||
add_node_error(irb->codegen, node,
|
||||
buf_sprintf("expected at least 1 argument, found 0"));
|
||||
return irb->codegen->invalid_inst_src;
|
||||
} else if (arg_count == 1) {
|
||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, sub_scope);
|
||||
if (arg0_value == irb->codegen->invalid_inst_src)
|
||||
return arg0_value;
|
||||
|
||||
type_of = ir_build_typeof_1(irb, scope, node, arg0_value);
|
||||
} else {
|
||||
IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
|
||||
for (size_t i = 0; i < arg_count; i += 1) {
|
||||
AstNode *arg_node = node->data.fn_call_expr.params.at(i);
|
||||
IrInstSrc *arg = ir_gen_node(irb, arg_node, sub_scope);
|
||||
if (arg == irb->codegen->invalid_inst_src)
|
||||
return irb->codegen->invalid_inst_src;
|
||||
args[i] = arg;
|
||||
}
|
||||
|
||||
type_of = ir_build_typeof_n(irb, scope, node, args, arg_count);
|
||||
}
|
||||
return ir_lval_wrap(irb, scope, type_of, lval, result_loc);
|
||||
}
|
||||
case BuiltinFnIdSetCold:
|
||||
@ -21662,10 +21698,31 @@ static IrInstGen *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstSrcLoadP
|
||||
}
|
||||
|
||||
static IrInstGen *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstSrcTypeOf *typeof_instruction) {
|
||||
IrInstGen *expr_value = typeof_instruction->value->child;
|
||||
ZigType *type_entry = expr_value->value->type;
|
||||
ZigType *type_entry;
|
||||
|
||||
const size_t value_count = typeof_instruction->value_count;
|
||||
|
||||
// Fast path for the common case of TypeOf with a single argument
|
||||
if (value_count < 2) {
|
||||
type_entry = typeof_instruction->value.scalar->child->value->type;
|
||||
} else {
|
||||
IrInstGen **args = heap::c_allocator.allocate<IrInstGen*>(value_count);
|
||||
for (size_t i = 0; i < value_count; i += 1) {
|
||||
IrInstGen *value = typeof_instruction->value.list[i]->child;
|
||||
if (type_is_invalid(value->value->type))
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
args[i] = value;
|
||||
}
|
||||
|
||||
type_entry = ir_resolve_peer_types(ira, typeof_instruction->base.base.source_node,
|
||||
nullptr, args, value_count);
|
||||
|
||||
heap::c_allocator.deallocate(args, value_count);
|
||||
}
|
||||
|
||||
if (type_is_invalid(type_entry))
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
|
||||
return ir_const_type(ira, &typeof_instruction->base.base, type_entry);
|
||||
}
|
||||
|
||||
|
||||
@ -1118,7 +1118,13 @@ static void ir_print_vector_store_elem(IrPrintGen *irp, IrInstGenVectorStoreElem
|
||||
|
||||
static void ir_print_typeof(IrPrintSrc *irp, IrInstSrcTypeOf *instruction) {
|
||||
fprintf(irp->f, "@TypeOf(");
|
||||
ir_print_other_inst_src(irp, instruction->value);
|
||||
if (instruction->value_count == 1) {
|
||||
ir_print_other_inst_src(irp, instruction->value.scalar);
|
||||
} else {
|
||||
for (size_t i = 0; i < instruction->value_count; i += 1) {
|
||||
ir_print_other_inst_src(irp, instruction->value.list[i]);
|
||||
}
|
||||
}
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,24 @@ const tests = @import("tests.zig");
|
||||
const std = @import("std");
|
||||
|
||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
cases.addTest("@TypeOf with no arguments",
|
||||
\\export fn entry() void {
|
||||
\\ _ = @TypeOf();
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:9: error: expected at least 1 argument, found 0",
|
||||
});
|
||||
|
||||
cases.addTest("@TypeOf with incompatible arguments",
|
||||
\\export fn entry() void {
|
||||
\\ var var_1: f32 = undefined;
|
||||
\\ var var_2: u32 = undefined;
|
||||
\\ _ = @TypeOf(var_1, var_2);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:4:9: error: incompatible types: 'f32' and 'u32'",
|
||||
});
|
||||
|
||||
cases.addTest("type mismatch with tuple concatenation",
|
||||
\\export fn entry() void {
|
||||
\\ var x = .{};
|
||||
|
||||
@ -105,6 +105,29 @@ test "@TypeOf() has no runtime side effects" {
|
||||
expect(data == 0);
|
||||
}
|
||||
|
||||
test "@TypeOf() with multiple arguments" {
|
||||
{
|
||||
var var_1: u32 = undefined;
|
||||
var var_2: u8 = undefined;
|
||||
var var_3: u64 = undefined;
|
||||
comptime expect(@TypeOf(var_1, var_2, var_3) == u64);
|
||||
}
|
||||
{
|
||||
var var_1: f16 = undefined;
|
||||
var var_2: f32 = undefined;
|
||||
var var_3: f64 = undefined;
|
||||
comptime expect(@TypeOf(var_1, var_2, var_3) == f64);
|
||||
}
|
||||
{
|
||||
var var_1: u16 = undefined;
|
||||
comptime expect(@TypeOf(var_1, 0xffff) == u16);
|
||||
}
|
||||
{
|
||||
var var_1: f32 = undefined;
|
||||
comptime expect(@TypeOf(var_1, 3.1415) == f32);
|
||||
}
|
||||
}
|
||||
|
||||
test "branching logic inside @TypeOf" {
|
||||
const S = struct {
|
||||
var data: i32 = 0;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user