parent
6ab8b2aab4
commit
a223063923
|
@ -8114,7 +8114,23 @@ pub const TypeInfo = union(TypeId) {
|
|||
This function returns a compile-time constant, which is the type of the
|
||||
expression passed as an argument. The expression is evaluated.
|
||||
</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;
|
||||
|
||||
test "no runtime side effects" {
|
||||
var data: i32 = 0;
|
||||
const T = @typeOf(foo(i32, &data));
|
||||
comptime assert(T == i32);
|
||||
assert(data == 0);
|
||||
}
|
||||
|
||||
fn foo(comptime T: type, ptr: *T) T {
|
||||
ptr.* += 1;
|
||||
return ptr.*;
|
||||
}
|
||||
{#code_end#}
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@unionInit#}
|
||||
|
|
|
@ -2104,6 +2104,7 @@ enum ScopeId {
|
|||
ScopeIdFnDef,
|
||||
ScopeIdCompTime,
|
||||
ScopeIdRuntime,
|
||||
ScopeIdTypeOf,
|
||||
};
|
||||
|
||||
struct Scope {
|
||||
|
@ -2244,6 +2245,13 @@ struct ScopeFnDef {
|
|||
ZigFn *fn_entry;
|
||||
};
|
||||
|
||||
// This scope is created for a @typeOf.
|
||||
// All runtime side-effects are elided within it.
|
||||
// NodeTypeFnCallExpr
|
||||
struct ScopeTypeOf {
|
||||
Scope base;
|
||||
};
|
||||
|
||||
// synchronized with code in define_builtin_compile_vars
|
||||
enum AtomicOrder {
|
||||
AtomicOrderUnordered,
|
||||
|
|
|
@ -197,6 +197,12 @@ Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent) {
|
|||
return &scope->base;
|
||||
}
|
||||
|
||||
Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent) {
|
||||
ScopeTypeOf *scope = allocate<ScopeTypeOf>(1);
|
||||
init_scope(g, &scope->base, ScopeIdTypeOf, node, parent);
|
||||
return &scope->base;
|
||||
}
|
||||
|
||||
ZigType *get_scope_import(Scope *scope) {
|
||||
while (scope) {
|
||||
if (scope->id == ScopeIdDecls) {
|
||||
|
@ -209,6 +215,22 @@ ZigType *get_scope_import(Scope *scope) {
|
|||
zig_unreachable();
|
||||
}
|
||||
|
||||
ScopeTypeOf *get_scope_typeof(Scope *scope) {
|
||||
while (scope) {
|
||||
switch (scope->id) {
|
||||
case ScopeIdTypeOf:
|
||||
return reinterpret_cast<ScopeTypeOf *>(scope);
|
||||
case ScopeIdFnDef:
|
||||
case ScopeIdDecls:
|
||||
return nullptr;
|
||||
default:
|
||||
scope = scope->parent;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static ZigType *new_container_type_entry(CodeGen *g, ZigTypeId id, AstNode *source_node, Scope *parent_scope,
|
||||
Buf *bare_name)
|
||||
{
|
||||
|
|
|
@ -85,6 +85,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
|
|||
ZigFn *scope_fn_entry(Scope *scope);
|
||||
ZigPackage *scope_package(Scope *scope);
|
||||
ZigType *get_scope_import(Scope *scope);
|
||||
ScopeTypeOf *get_scope_typeof(Scope *scope);
|
||||
void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, Scope *parent_scope);
|
||||
ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name,
|
||||
bool is_const, ConstExprValue *init_value, Tld *src_tld, ZigType *var_type);
|
||||
|
@ -112,6 +113,7 @@ ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent);
|
|||
ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry);
|
||||
Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent);
|
||||
Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstruction *is_comptime);
|
||||
Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent);
|
||||
|
||||
void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str);
|
||||
ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str);
|
||||
|
|
|
@ -645,6 +645,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
|
|||
case ScopeIdSuspend:
|
||||
case ScopeIdCompTime:
|
||||
case ScopeIdRuntime:
|
||||
case ScopeIdTypeOf:
|
||||
return get_di_scope(g, scope->parent);
|
||||
}
|
||||
zig_unreachable();
|
||||
|
@ -3757,6 +3758,7 @@ static void render_async_var_decls(CodeGen *g, Scope *scope) {
|
|||
case ScopeIdSuspend:
|
||||
case ScopeIdCompTime:
|
||||
case ScopeIdRuntime:
|
||||
case ScopeIdTypeOf:
|
||||
scope = scope->parent;
|
||||
continue;
|
||||
}
|
||||
|
@ -5942,12 +5944,17 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) {
|
|||
|
||||
for (size_t block_i = 0; block_i < executable->basic_block_list.length; block_i += 1) {
|
||||
IrBasicBlock *current_block = executable->basic_block_list.at(block_i);
|
||||
if (get_scope_typeof(current_block->scope) != nullptr) {
|
||||
LLVMBuildBr(g->builder, current_block->llvm_block);
|
||||
}
|
||||
assert(current_block->llvm_block);
|
||||
LLVMPositionBuilderAtEnd(g->builder, current_block->llvm_block);
|
||||
for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) {
|
||||
IrInstruction *instruction = current_block->instruction_list.at(instr_i);
|
||||
if (instruction->ref_count == 0 && !ir_has_side_effects(instruction))
|
||||
continue;
|
||||
if (get_scope_typeof(instruction->scope) != nullptr)
|
||||
continue;
|
||||
|
||||
if (!g->strip_debug_symbols) {
|
||||
set_debug_location(g, instruction);
|
||||
|
|
10
src/ir.cpp
10
src/ir.cpp
|
@ -3344,6 +3344,7 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco
|
|||
case ScopeIdSuspend:
|
||||
case ScopeIdCompTime:
|
||||
case ScopeIdRuntime:
|
||||
case ScopeIdTypeOf:
|
||||
scope = scope->parent;
|
||||
continue;
|
||||
case ScopeIdDeferExpr:
|
||||
|
@ -3399,6 +3400,7 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o
|
|||
case ScopeIdSuspend:
|
||||
case ScopeIdCompTime:
|
||||
case ScopeIdRuntime:
|
||||
case ScopeIdTypeOf:
|
||||
scope = scope->parent;
|
||||
continue;
|
||||
case ScopeIdDeferExpr:
|
||||
|
@ -4379,8 +4381,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
|||
zig_unreachable();
|
||||
case BuiltinFnIdTypeof:
|
||||
{
|
||||
Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope);
|
||||
|
||||
AstNode *arg_node = node->data.fn_call_expr.params.at(0);
|
||||
IrInstruction *arg = ir_gen_node(irb, arg_node, scope);
|
||||
IrInstruction *arg = ir_gen_node(irb, arg_node, sub_scope);
|
||||
if (arg == irb->codegen->invalid_instruction)
|
||||
return arg;
|
||||
|
||||
|
@ -8269,6 +8273,10 @@ static ConstExprValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (get_scope_typeof(instruction->scope) != nullptr) {
|
||||
// doesn't count, it's inside a @typeOf()
|
||||
continue;
|
||||
}
|
||||
exec_add_error_node(codegen, exec, instruction->source_node,
|
||||
buf_sprintf("unable to evaluate constant expression"));
|
||||
return &codegen->invalid_instruction->value;
|
||||
|
|
|
@ -89,3 +89,29 @@ test "@sizeOf(T) == 0 doesn't force resolving struct size" {
|
|||
expect(@sizeOf(S.Foo) == 4);
|
||||
expect(@sizeOf(S.Bar) == 8);
|
||||
}
|
||||
|
||||
test "@typeOf() has no runtime side effects" {
|
||||
const S = struct {
|
||||
fn foo(comptime T: type, ptr: *T) T {
|
||||
ptr.* += 1;
|
||||
return ptr.*;
|
||||
}
|
||||
};
|
||||
var data: i32 = 0;
|
||||
const T = @typeOf(S.foo(i32, &data));
|
||||
comptime expect(T == i32);
|
||||
expect(data == 0);
|
||||
}
|
||||
|
||||
test "branching logic inside @typeOf" {
|
||||
const S = struct {
|
||||
var data: i32 = 0;
|
||||
fn foo() anyerror!i32 {
|
||||
data += 1;
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
const T = @typeOf(S.foo() catch undefined);
|
||||
comptime expect(T == i32);
|
||||
expect(S.data == 0);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user