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) {
81148114 This function returns a compile-time constant, which is the type of the
81158115 expression passed as an argument. The expression is evaluated.
81168116 </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#}
81188134 {#header_close#}
81198135
81208136 {#header_open|@unionInit#}
src/all_types.hpp+8
......@@ -2104,6 +2104,7 @@ enum ScopeId {
21042104 ScopeIdFnDef,
21052105 ScopeIdCompTime,
21062106 ScopeIdRuntime,
2107 ScopeIdTypeOf,
21072108};
21082109
21092110struct Scope {
......@@ -2244,6 +2245,13 @@ struct ScopeFnDef {
22442245 ZigFn *fn_entry;
22452246};
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
22472255// synchronized with code in define_builtin_compile_vars
22482256enum AtomicOrder {
22492257 AtomicOrderUnordered,
src/analyze.cpp+22
......@@ -197,6 +197,12 @@ Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent) {
197197 return &scope->base;
198198}
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
200206ZigType *get_scope_import(Scope *scope) {
201207 while (scope) {
202208 if (scope->id == ScopeIdDecls) {
......@@ -209,6 +215,22 @@ ZigType *get_scope_import(Scope *scope) {
209215 zig_unreachable();
210216}
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
212234static ZigType *new_container_type_entry(CodeGen *g, ZigTypeId id, AstNode *source_node, Scope *parent_scope,
213235 Buf *bare_name)
214236{
src/analyze.hpp+2
......@@ -85,6 +85,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
8585ZigFn *scope_fn_entry(Scope *scope);
8686ZigPackage *scope_package(Scope *scope);
8787ZigType *get_scope_import(Scope *scope);
88ScopeTypeOf *get_scope_typeof(Scope *scope);
8889void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, Scope *parent_scope);
8990ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name,
9091 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);
112113ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry);
113114Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent);
114115Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstruction *is_comptime);
116Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent);
115117
116118void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str);
117119ConstExprValue *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) {
645645 case ScopeIdSuspend:
646646 case ScopeIdCompTime:
647647 case ScopeIdRuntime:
648 case ScopeIdTypeOf:
648649 return get_di_scope(g, scope->parent);
649650 }
650651 zig_unreachable();
......@@ -3757,6 +3758,7 @@ static void render_async_var_decls(CodeGen *g, Scope *scope) {
37573758 case ScopeIdSuspend:
37583759 case ScopeIdCompTime:
37593760 case ScopeIdRuntime:
3761 case ScopeIdTypeOf:
37603762 scope = scope->parent;
37613763 continue;
37623764 }
......@@ -5942,12 +5944,17 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) {
59425944
59435945 for (size_t block_i = 0; block_i < executable->basic_block_list.length; block_i += 1) {
59445946 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 }
59455950 assert(current_block->llvm_block);
59465951 LLVMPositionBuilderAtEnd(g->builder, current_block->llvm_block);
59475952 for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) {
59485953 IrInstruction *instruction = current_block->instruction_list.at(instr_i);
59495954 if (instruction->ref_count == 0 && !ir_has_side_effects(instruction))
59505955 continue;
5956 if (get_scope_typeof(instruction->scope) != nullptr)
5957 continue;
59515958
59525959 if (!g->strip_debug_symbols) {
59535960 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
33443344 case ScopeIdSuspend:
33453345 case ScopeIdCompTime:
33463346 case ScopeIdRuntime:
3347 case ScopeIdTypeOf:
33473348 scope = scope->parent;
33483349 continue;
33493350 case ScopeIdDeferExpr:
......@@ -3399,6 +3400,7 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o
33993400 case ScopeIdSuspend:
34003401 case ScopeIdCompTime:
34013402 case ScopeIdRuntime:
3403 case ScopeIdTypeOf:
34023404 scope = scope->parent;
34033405 continue;
34043406 case ScopeIdDeferExpr:
......@@ -4379,8 +4381,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
43794381 zig_unreachable();
43804382 case BuiltinFnIdTypeof:
43814383 {
4384 Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope);
4385
43824386 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);
43844388 if (arg == irb->codegen->invalid_instruction)
43854389 return arg;
43864390
......@@ -8269,6 +8273,10 @@ static ConstExprValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec
82698273 break;
82708274 }
82718275 }
8276 if (get_scope_typeof(instruction->scope) != nullptr) {
8277 // doesn't count, it's inside a @typeOf()
8278 continue;
8279 }
82728280 exec_add_error_node(codegen, exec, instruction->source_node,
82738281 buf_sprintf("unable to evaluate constant expression"));
82748282 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" {
8989 expect(@sizeOf(S.Foo) == 4);
9090 expect(@sizeOf(S.Bar) == 8);
9191}
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}