diff --git a/src/all_types.hpp b/src/all_types.hpp index 29801307f..6c3351ac9 100644 --- a/src/all_types.hpp +++ b/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; }; diff --git a/src/ir.cpp b/src/ir.cpp index 8026d6f98..bd65e2d53 100644 --- a/src/ir.cpp +++ b/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; } } diff --git a/std/build.zig b/std/build.zig index 46155ade3..c4657671e 100644 --- a/std/build.zig +++ b/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()); } }; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 771972fcf..9f0b9de23 100644 --- a/test/compile_errors.zig +++ b/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"); }