Commit 2e512a0e6e

Andrew Kelley <superjoe30@gmail.com>
2017-04-28 01:40:35
add compile error for returning local variable address
closes #344
1 parent 1691074
src/all_types.hpp
@@ -201,6 +201,12 @@ enum RuntimeHintMaybe {
     RuntimeHintMaybeNonNull,
 };
 
+enum RuntimeHintPtr {
+    RuntimeHintPtrUnknown,
+    RuntimeHintPtrStack,
+    RuntimeHintPtrNonStack,
+};
+
 struct ConstFn {
     FnTableEntry *fn_entry;
     bool is_inline;
@@ -233,6 +239,7 @@ struct ConstExprValue {
         // populated if special == ConstValSpecialRuntime
         RuntimeHintErrorUnion rh_error_union;
         RuntimeHintMaybe rh_maybe;
+        RuntimeHintPtr rh_ptr;
     } data;
 };
 
src/ir.cpp
@@ -7538,6 +7538,13 @@ static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira,
     if (casted_value == ira->codegen->invalid_instruction)
         return ir_unreach_error(ira);
 
+    if (casted_value->value.special == ConstValSpecialRuntime &&
+        casted_value->value.type->id == TypeTableEntryIdPointer &&
+        casted_value->value.data.rh_ptr == RuntimeHintPtrStack)
+    {
+        ir_add_error(ira, casted_value, buf_sprintf("function returns address of local variable"));
+        return ir_unreach_error(ira);
+    }
     ir_build_return_from(&ira->new_irb, &return_instruction->base, casted_value);
     return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
 }
@@ -8467,6 +8474,10 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
                 instruction->scope, instruction->source_node, var, is_const, is_volatile);
         var_ptr_instruction->value.type = get_pointer_to_type(ira->codegen, var->value->type, var->src_is_const);
         type_ensure_zero_bits_known(ira->codegen, var->value->type);
+
+        bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr);
+        var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack;
+
         return var_ptr_instruction;
     }
 }
std/build.zig
@@ -887,7 +887,7 @@ pub const LibExeObjStep = struct {
             %%zig_args.append(lib_path);
         }
 
-        %%builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
+        %return builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
 
         if (self.kind == Kind.Lib and !self.static) {
             // sym link for libfoo.so.1 to libfoo.so.1.2.3
@@ -994,7 +994,7 @@ pub const TestStep = struct {
             %%zig_args.append(lib_path);
         }
 
-        %%builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
+        %return builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
     }
 };
 
test/compile_errors.zig
@@ -1610,4 +1610,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
     ,
         ".tmp_source.zig:3:5: error: cannot set section of external function 'foo'",
         ".tmp_source.zig:1:8: note: declared here");
+
+    cases.add("returning address of local variable",
+        \\export fn foo() -> &i32 {
+        \\    var a: i32 = undefined;
+        \\    return &a;
+        \\}
+    ,
+        ".tmp_source.zig:3:13: error: function returns address of local variable");
 }