authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-05 20:32:06-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-05 20:32:06-04:00
log1d8b8ad687facd25a27b3ff6d083812b45cd529f
tree8f21e06cbbd22156a8d36f214aa4e52bff8f175c
parent8400163e0245f68833e29db22ae3bcc1fb8bf9ae
signaturelock-open Commit is signed but in an unrecognized format.

add compile error for using outer scoped runtime variables

from a fn defined inside it. closes #876

6 files changed, 80 insertions(+), 16 deletions(-)

src/all_types.hpp+1
...@@ -2350,6 +2350,7 @@ struct IrInstructionVarPtr {...@@ -2350,6 +2350,7 @@ struct IrInstructionVarPtr {
2350 IrInstruction base;2350 IrInstruction base;
23512351
2352 ZigVar *var;2352 ZigVar *var;
2353 ScopeFnDef *crossed_fndef_scope;
2353};2354};
23542355
2355struct IrInstructionCall {2356struct IrInstructionCall {
src/analyze.cpp+13-4
...@@ -3519,7 +3519,7 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf...@@ -3519,7 +3519,7 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf
3519 if (!type_is_invalid(value->type)) {3519 if (!type_is_invalid(value->type)) {
3520 variable_entry->align_bytes = get_abi_alignment(g, value->type);3520 variable_entry->align_bytes = get_abi_alignment(g, value->type);
35213521
3522 ZigVar *existing_var = find_variable(g, parent_scope, name);3522 ZigVar *existing_var = find_variable(g, parent_scope, name, nullptr);
3523 if (existing_var && !existing_var->shadowable) {3523 if (existing_var && !existing_var->shadowable) {
3524 ErrorMsg *msg = add_node_error(g, source_node,3524 ErrorMsg *msg = add_node_error(g, source_node,
3525 buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));3525 buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
...@@ -3726,12 +3726,16 @@ Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) {...@@ -3726,12 +3726,16 @@ Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) {
3726 return nullptr;3726 return nullptr;
3727}3727}
37283728
3729ZigVar *find_variable(CodeGen *g, Scope *scope, Buf *name) {3729ZigVar *find_variable(CodeGen *g, Scope *scope, Buf *name, ScopeFnDef **crossed_fndef_scope) {
3730 ScopeFnDef *my_crossed_fndef_scope = nullptr;
3730 while (scope) {3731 while (scope) {
3731 if (scope->id == ScopeIdVarDecl) {3732 if (scope->id == ScopeIdVarDecl) {
3732 ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;3733 ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
3733 if (buf_eql_buf(name, &var_scope->var->name))3734 if (buf_eql_buf(name, &var_scope->var->name)) {
3735 if (crossed_fndef_scope != nullptr)
3736 *crossed_fndef_scope = my_crossed_fndef_scope;
3734 return var_scope->var;3737 return var_scope->var;
3738 }
3735 } else if (scope->id == ScopeIdDecls) {3739 } else if (scope->id == ScopeIdDecls) {
3736 ScopeDecls *decls_scope = (ScopeDecls *)scope;3740 ScopeDecls *decls_scope = (ScopeDecls *)scope;
3737 auto entry = decls_scope->decl_table.maybe_get(name);3741 auto entry = decls_scope->decl_table.maybe_get(name);
...@@ -3739,10 +3743,15 @@ ZigVar *find_variable(CodeGen *g, Scope *scope, Buf *name) {...@@ -3739,10 +3743,15 @@ ZigVar *find_variable(CodeGen *g, Scope *scope, Buf *name) {
3739 Tld *tld = entry->value;3743 Tld *tld = entry->value;
3740 if (tld->id == TldIdVar) {3744 if (tld->id == TldIdVar) {
3741 TldVar *tld_var = (TldVar *)tld;3745 TldVar *tld_var = (TldVar *)tld;
3742 if (tld_var->var)3746 if (tld_var->var) {
3747 if (crossed_fndef_scope != nullptr)
3748 *crossed_fndef_scope = nullptr;
3743 return tld_var->var;3749 return tld_var->var;
3750 }
3744 }3751 }
3745 }3752 }
3753 } else if (scope->id == ScopeIdFnDef) {
3754 my_crossed_fndef_scope = (ScopeFnDef *)scope;
3746 }3755 }
3747 scope = scope->parent;3756 scope = scope->parent;
3748 }3757 }
src/analyze.hpp+1-1
...@@ -48,7 +48,7 @@ bool type_has_bits(ZigType *type_entry);...@@ -48,7 +48,7 @@ bool type_has_bits(ZigType *type_entry);
48ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code);48ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code);
4949
5050
51ZigVar *find_variable(CodeGen *g, Scope *orig_context, Buf *name);51ZigVar *find_variable(CodeGen *g, Scope *orig_context, Buf *name, ScopeFnDef **crossed_fndef_scope);
52Tld *find_decl(CodeGen *g, Scope *scope, Buf *name);52Tld *find_decl(CodeGen *g, Scope *scope, Buf *name);
53void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *source_node);53void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *source_node);
54bool type_is_codegen_pointer(ZigType *type);54bool type_is_codegen_pointer(ZigType *type);
src/ir.cpp+30-11
...@@ -1114,15 +1114,22 @@ static IrInstruction *ir_build_bin_op_from(IrBuilder *irb, IrInstruction *old_in...@@ -1114,15 +1114,22 @@ static IrInstruction *ir_build_bin_op_from(IrBuilder *irb, IrInstruction *old_in
1114 return new_instruction;1114 return new_instruction;
1115}1115}
11161116
1117static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var) {1117static IrInstruction *ir_build_var_ptr_x(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var,
1118 ScopeFnDef *crossed_fndef_scope)
1119{
1118 IrInstructionVarPtr *instruction = ir_build_instruction<IrInstructionVarPtr>(irb, scope, source_node);1120 IrInstructionVarPtr *instruction = ir_build_instruction<IrInstructionVarPtr>(irb, scope, source_node);
1119 instruction->var = var;1121 instruction->var = var;
1122 instruction->crossed_fndef_scope = crossed_fndef_scope;
11201123
1121 ir_ref_var(var);1124 ir_ref_var(var);
11221125
1123 return &instruction->base;1126 return &instruction->base;
1124}1127}
11251128
1129static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var) {
1130 return ir_build_var_ptr_x(irb, scope, source_node, var, nullptr);
1131}
1132
1126static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *array_ptr,1133static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *array_ptr,
1127 IrInstruction *elem_index, bool safety_check_on, PtrLen ptr_len)1134 IrInstruction *elem_index, bool safety_check_on, PtrLen ptr_len)
1128{1135{
...@@ -3336,7 +3343,7 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s...@@ -3336,7 +3343,7 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s
3336 buf_init_from_buf(&variable_entry->name, name);3343 buf_init_from_buf(&variable_entry->name, name);
33373344
3338 if (!skip_name_check) {3345 if (!skip_name_check) {
3339 ZigVar *existing_var = find_variable(codegen, parent_scope, name);3346 ZigVar *existing_var = find_variable(codegen, parent_scope, name, nullptr);
3340 if (existing_var && !existing_var->shadowable) {3347 if (existing_var && !existing_var->shadowable) {
3341 ErrorMsg *msg = add_node_error(codegen, node,3348 ErrorMsg *msg = add_node_error(codegen, node,
3342 buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));3349 buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
...@@ -3799,9 +3806,10 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node,...@@ -3799,9 +3806,10 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node,
3799 }3806 }
3800 }3807 }
38013808
3802 ZigVar *var = find_variable(irb->codegen, scope, variable_name);3809 ScopeFnDef *crossed_fndef_scope;
3810 ZigVar *var = find_variable(irb->codegen, scope, variable_name, &crossed_fndef_scope);
3803 if (var) {3811 if (var) {
3804 IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var);3812 IrInstruction *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope);
3805 if (lval == LValPtr)3813 if (lval == LValPtr)
3806 return var_ptr;3814 return var_ptr;
3807 else3815 else
...@@ -5822,7 +5830,9 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod...@@ -5822,7 +5830,9 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod
5822 output_types[i] = return_type;5830 output_types[i] = return_type;
5823 } else {5831 } else {
5824 Buf *variable_name = asm_output->variable_name;5832 Buf *variable_name = asm_output->variable_name;
5825 ZigVar *var = find_variable(irb->codegen, scope, variable_name);5833 // TODO there is some duplication here with ir_gen_symbol. I need to do a full audit of how
5834 // inline assembly works. https://github.com/ziglang/zig/issues/215
5835 ZigVar *var = find_variable(irb->codegen, scope, variable_name, nullptr);
5826 if (var) {5836 if (var) {
5827 output_vars[i] = var;5837 output_vars[i] = var;
5828 } else {5838 } else {
...@@ -14157,17 +14167,26 @@ static ZigType *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPhi *phi...@@ -14157,17 +14167,26 @@ static ZigType *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPhi *phi
14157 return resolved_type;14167 return resolved_type;
14158}14168}
1415914169
14160static ZigType *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruction,14170static ZigType *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var) {
14161 ZigVar *var)
14162{
14163 IrInstruction *result = ir_get_var_ptr(ira, instruction, var);14171 IrInstruction *result = ir_get_var_ptr(ira, instruction, var);
14164 ir_link_new_instruction(result, instruction);14172 ir_link_new_instruction(result, instruction);
14165 return result->value.type;14173 return result->value.type;
14166}14174}
1416714175
14168static ZigType *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) {14176static ZigType *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *instruction) {
14169 ZigVar *var = var_ptr_instruction->var;14177 ZigVar *var = instruction->var;
14170 return ir_analyze_var_ptr(ira, &var_ptr_instruction->base, var);14178 IrInstruction *result = ir_get_var_ptr(ira, &instruction->base, var);
14179 if (instruction->crossed_fndef_scope != nullptr && !instr_is_comptime(result)) {
14180 ErrorMsg *msg = ir_add_error(ira, &instruction->base,
14181 buf_sprintf("'%s' not accessible from inner function", buf_ptr(&var->name)));
14182 add_error_note(ira->codegen, msg, instruction->crossed_fndef_scope->base.source_node,
14183 buf_sprintf("crossed function definition here"));
14184 add_error_note(ira->codegen, msg, var->decl_node,
14185 buf_sprintf("declared here"));
14186 return ira->codegen->builtin_types.entry_invalid;
14187 }
14188 ir_link_new_instruction(result, &instruction->base);
14189 return result->value.type;
14171}14190}
1417214191
14173static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) {14192static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) {
test/cases/fn.zig+15
...@@ -176,3 +176,18 @@ test "pass by non-copying value as method, at comptime" {...@@ -176,3 +176,18 @@ test "pass by non-copying value as method, at comptime" {
176 assert(pt.addPointCoords() == 3);176 assert(pt.addPointCoords() == 3);
177 }177 }
178}178}
179
180fn outer(y: u32) fn (u32) u32 {
181 const Y = @typeOf(y);
182 const st = struct {
183 fn get(z: u32) u32 {
184 return z + @sizeOf(Y);
185 }
186 };
187 return st.get;
188}
189
190test "return inner function which references comptime variable of outer function" {
191 var func = outer(10);
192 assert(func(3) == 7);
193}
test/compile_errors.zig+20
...@@ -1,6 +1,26 @@...@@ -1,6 +1,26 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.CompileErrorContext) void {3pub fn addCases(cases: *tests.CompileErrorContext) void {
4 cases.add(
5 "accessing runtime parameter from outer function",
6 \\fn outer(y: u32) fn (u32) u32 {
7 \\ const st = struct {
8 \\ fn get(z: u32) u32 {
9 \\ return z + y;
10 \\ }
11 \\ };
12 \\ return st.get;
13 \\}
14 \\export fn entry() void {
15 \\ var func = outer(10);
16 \\ var x = func(3);
17 \\}
18 ,
19 ".tmp_source.zig:4:24: error: 'y' not accessible from inner function",
20 ".tmp_source.zig:3:28: note: crossed function definition here",
21 ".tmp_source.zig:1:10: note: declared here",
22 );
23
4 cases.add(24 cases.add(
5 "non int passed to @intToFloat",25 "non int passed to @intToFloat",
6 \\export fn entry() void {26 \\export fn entry() void {