authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-31 10:38:18-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-31 11:00:31-04:00
loga2230639232c069e4052a2e994dd5c0bd4e2517f
treee8bdb0438078f740d1509ded7e4b67162e7ac8e8
parent6ab8b2aab4b146a7d1d882686199eace19989011
signaturelock-open Commit is signed but in an unrecognized format.

`@typeOf` now guarantees no runtime side effects

related: #1627

7 files changed, 90 insertions(+), 1 deletions(-)

doc/langref.html.in+16
...@@ -8114,7 +8114,23 @@ pub const TypeInfo = union(TypeId) {...@@ -8114,7 +8114,23 @@ pub const TypeInfo = union(TypeId) {
8114 This function returns a compile-time constant, which is the type of the8114 This function returns a compile-time constant, which is the type of the
8115 expression passed as an argument. The expression is evaluated.8115 expression passed as an argument. The expression is evaluated.
8116 </p>8116 </p>
8117 <p>{#syntax#}@typeOf{#endsyntax#} guarantees no run-time side-effects within the expression:</p>
8118 {#code_begin|test#}
8119const std = @import("std");
8120const assert = std.debug.assert;
8121
8122test "no runtime side effects" {
8123 var data: i32 = 0;
8124 const T = @typeOf(foo(i32, &data));
8125 comptime assert(T == i32);
8126 assert(data == 0);
8127}
81178128
8129fn foo(comptime T: type, ptr: *T) T {
8130 ptr.* += 1;
8131 return ptr.*;
8132}
8133 {#code_end#}
8118 {#header_close#}8134 {#header_close#}
81198135
8120 {#header_open|@unionInit#}8136 {#header_open|@unionInit#}
src/all_types.hpp+8
...@@ -2104,6 +2104,7 @@ enum ScopeId {...@@ -2104,6 +2104,7 @@ enum ScopeId {
2104 ScopeIdFnDef,2104 ScopeIdFnDef,
2105 ScopeIdCompTime,2105 ScopeIdCompTime,
2106 ScopeIdRuntime,2106 ScopeIdRuntime,
2107 ScopeIdTypeOf,
2107};2108};
21082109
2109struct Scope {2110struct Scope {
...@@ -2244,6 +2245,13 @@ struct ScopeFnDef {...@@ -2244,6 +2245,13 @@ struct ScopeFnDef {
2244 ZigFn *fn_entry;2245 ZigFn *fn_entry;
2245};2246};
22462247
2248// This scope is created for a @typeOf.
2249// All runtime side-effects are elided within it.
2250// NodeTypeFnCallExpr
2251struct ScopeTypeOf {
2252 Scope base;
2253};
2254
2247// synchronized with code in define_builtin_compile_vars2255// synchronized with code in define_builtin_compile_vars
2248enum AtomicOrder {2256enum AtomicOrder {
2249 AtomicOrderUnordered,2257 AtomicOrderUnordered,
src/analyze.cpp+22
...@@ -197,6 +197,12 @@ Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent) {...@@ -197,6 +197,12 @@ Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent) {
197 return &scope->base;197 return &scope->base;
198}198}
199199
200Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent) {
201 ScopeTypeOf *scope = allocate<ScopeTypeOf>(1);
202 init_scope(g, &scope->base, ScopeIdTypeOf, node, parent);
203 return &scope->base;
204}
205
200ZigType *get_scope_import(Scope *scope) {206ZigType *get_scope_import(Scope *scope) {
201 while (scope) {207 while (scope) {
202 if (scope->id == ScopeIdDecls) {208 if (scope->id == ScopeIdDecls) {
...@@ -209,6 +215,22 @@ ZigType *get_scope_import(Scope *scope) {...@@ -209,6 +215,22 @@ ZigType *get_scope_import(Scope *scope) {
209 zig_unreachable();215 zig_unreachable();
210}216}
211217
218ScopeTypeOf *get_scope_typeof(Scope *scope) {
219 while (scope) {
220 switch (scope->id) {
221 case ScopeIdTypeOf:
222 return reinterpret_cast<ScopeTypeOf *>(scope);
223 case ScopeIdFnDef:
224 case ScopeIdDecls:
225 return nullptr;
226 default:
227 scope = scope->parent;
228 continue;
229 }
230 }
231 zig_unreachable();
232}
233
212static ZigType *new_container_type_entry(CodeGen *g, ZigTypeId id, AstNode *source_node, Scope *parent_scope,234static ZigType *new_container_type_entry(CodeGen *g, ZigTypeId id, AstNode *source_node, Scope *parent_scope,
213 Buf *bare_name)235 Buf *bare_name)
214{236{
src/analyze.hpp+2
...@@ -85,6 +85,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);...@@ -85,6 +85,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
85ZigFn *scope_fn_entry(Scope *scope);85ZigFn *scope_fn_entry(Scope *scope);
86ZigPackage *scope_package(Scope *scope);86ZigPackage *scope_package(Scope *scope);
87ZigType *get_scope_import(Scope *scope);87ZigType *get_scope_import(Scope *scope);
88ScopeTypeOf *get_scope_typeof(Scope *scope);
88void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, Scope *parent_scope);89void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, Scope *parent_scope);
89ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name,90ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name,
90 bool is_const, ConstExprValue *init_value, Tld *src_tld, ZigType *var_type);91 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);...@@ -112,6 +113,7 @@ ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent);
112ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry);113ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry);
113Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent);114Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent);
114Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstruction *is_comptime);115Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstruction *is_comptime);
116Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent);
115117
116void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str);118void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str);
117ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str);119ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str);
src/codegen.cpp+7
...@@ -645,6 +645,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {...@@ -645,6 +645,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
645 case ScopeIdSuspend:645 case ScopeIdSuspend:
646 case ScopeIdCompTime:646 case ScopeIdCompTime:
647 case ScopeIdRuntime:647 case ScopeIdRuntime:
648 case ScopeIdTypeOf:
648 return get_di_scope(g, scope->parent);649 return get_di_scope(g, scope->parent);
649 }650 }
650 zig_unreachable();651 zig_unreachable();
...@@ -3757,6 +3758,7 @@ static void render_async_var_decls(CodeGen *g, Scope *scope) {...@@ -3757,6 +3758,7 @@ static void render_async_var_decls(CodeGen *g, Scope *scope) {
3757 case ScopeIdSuspend:3758 case ScopeIdSuspend:
3758 case ScopeIdCompTime:3759 case ScopeIdCompTime:
3759 case ScopeIdRuntime:3760 case ScopeIdRuntime:
3761 case ScopeIdTypeOf:
3760 scope = scope->parent;3762 scope = scope->parent;
3761 continue;3763 continue;
3762 }3764 }
...@@ -5942,12 +5944,17 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) {...@@ -5942,12 +5944,17 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) {
59425944
5943 for (size_t block_i = 0; block_i < executable->basic_block_list.length; block_i += 1) {5945 for (size_t block_i = 0; block_i < executable->basic_block_list.length; block_i += 1) {
5944 IrBasicBlock *current_block = executable->basic_block_list.at(block_i);5946 IrBasicBlock *current_block = executable->basic_block_list.at(block_i);
5947 if (get_scope_typeof(current_block->scope) != nullptr) {
5948 LLVMBuildBr(g->builder, current_block->llvm_block);
5949 }
5945 assert(current_block->llvm_block);5950 assert(current_block->llvm_block);
5946 LLVMPositionBuilderAtEnd(g->builder, current_block->llvm_block);5951 LLVMPositionBuilderAtEnd(g->builder, current_block->llvm_block);
5947 for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) {5952 for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) {
5948 IrInstruction *instruction = current_block->instruction_list.at(instr_i);5953 IrInstruction *instruction = current_block->instruction_list.at(instr_i);
5949 if (instruction->ref_count == 0 && !ir_has_side_effects(instruction))5954 if (instruction->ref_count == 0 && !ir_has_side_effects(instruction))
5950 continue;5955 continue;
5956 if (get_scope_typeof(instruction->scope) != nullptr)
5957 continue;
59515958
5952 if (!g->strip_debug_symbols) {5959 if (!g->strip_debug_symbols) {
5953 set_debug_location(g, instruction);5960 set_debug_location(g, instruction);
src/ir.cpp+9-1
...@@ -3344,6 +3344,7 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco...@@ -3344,6 +3344,7 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco
3344 case ScopeIdSuspend:3344 case ScopeIdSuspend:
3345 case ScopeIdCompTime:3345 case ScopeIdCompTime:
3346 case ScopeIdRuntime:3346 case ScopeIdRuntime:
3347 case ScopeIdTypeOf:
3347 scope = scope->parent;3348 scope = scope->parent;
3348 continue;3349 continue;
3349 case ScopeIdDeferExpr:3350 case ScopeIdDeferExpr:
...@@ -3399,6 +3400,7 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o...@@ -3399,6 +3400,7 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o
3399 case ScopeIdSuspend:3400 case ScopeIdSuspend:
3400 case ScopeIdCompTime:3401 case ScopeIdCompTime:
3401 case ScopeIdRuntime:3402 case ScopeIdRuntime:
3403 case ScopeIdTypeOf:
3402 scope = scope->parent;3404 scope = scope->parent;
3403 continue;3405 continue;
3404 case ScopeIdDeferExpr:3406 case ScopeIdDeferExpr:
...@@ -4379,8 +4381,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4379,8 +4381,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4379 zig_unreachable();4381 zig_unreachable();
4380 case BuiltinFnIdTypeof:4382 case BuiltinFnIdTypeof:
4381 {4383 {
4384 Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope);
4385
4382 AstNode *arg_node = node->data.fn_call_expr.params.at(0);4386 AstNode *arg_node = node->data.fn_call_expr.params.at(0);
4383 IrInstruction *arg = ir_gen_node(irb, arg_node, scope);4387 IrInstruction *arg = ir_gen_node(irb, arg_node, sub_scope);
4384 if (arg == irb->codegen->invalid_instruction)4388 if (arg == irb->codegen->invalid_instruction)
4385 return arg;4389 return arg;
43864390
...@@ -8269,6 +8273,10 @@ static ConstExprValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec...@@ -8269,6 +8273,10 @@ static ConstExprValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec
8269 break;8273 break;
8270 }8274 }
8271 }8275 }
8276 if (get_scope_typeof(instruction->scope) != nullptr) {
8277 // doesn't count, it's inside a @typeOf()
8278 continue;
8279 }
8272 exec_add_error_node(codegen, exec, instruction->source_node,8280 exec_add_error_node(codegen, exec, instruction->source_node,
8273 buf_sprintf("unable to evaluate constant expression"));8281 buf_sprintf("unable to evaluate constant expression"));
8274 return &codegen->invalid_instruction->value;8282 return &codegen->invalid_instruction->value;
test/stage1/behavior/sizeof_and_typeof.zig+26
...@@ -89,3 +89,29 @@ test "@sizeOf(T) == 0 doesn't force resolving struct size" {...@@ -89,3 +89,29 @@ test "@sizeOf(T) == 0 doesn't force resolving struct size" {
89 expect(@sizeOf(S.Foo) == 4);89 expect(@sizeOf(S.Foo) == 4);
90 expect(@sizeOf(S.Bar) == 8);90 expect(@sizeOf(S.Bar) == 8);
91}91}
92
93test "@typeOf() has no runtime side effects" {
94 const S = struct {
95 fn foo(comptime T: type, ptr: *T) T {
96 ptr.* += 1;
97 return ptr.*;
98 }
99 };
100 var data: i32 = 0;
101 const T = @typeOf(S.foo(i32, &data));
102 comptime expect(T == i32);
103 expect(data == 0);
104}
105
106test "branching logic inside @typeOf" {
107 const S = struct {
108 var data: i32 = 0;
109 fn foo() anyerror!i32 {
110 data += 1;
111 return undefined;
112 }
113 };
114 const T = @typeOf(S.foo() catch undefined);
115 comptime expect(T == i32);
116 expect(S.data == 0);
117}