Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stage2: add error for returning pointer to local variable #11153

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 128 additions & 14 deletions src/Sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ comptime_args_fn_inst: Zir.Inst.Index = 0,
/// Sema will set this to null when it takes ownership.
preallocated_new_func: ?*Module.Fn = null,

stack_pointers: std.AutoHashMapUnmanaged(Air.Inst.Ref, LazySrcLoc) = .{},

const std = @import("std");
const mem = std.mem;
const Allocator = std.mem.Allocator;
Expand Down Expand Up @@ -506,6 +508,7 @@ pub fn deinit(sema: *Sema) void {
sema.air_values.deinit(gpa);
sema.inst_map.deinit(gpa);
sema.decl_val_table.deinit(gpa);
sema.stack_pointers.deinit(gpa);
sema.* = undefined;
}

Expand Down Expand Up @@ -2424,7 +2427,9 @@ fn zirAllocExtended(
});
try sema.requireRuntimeBlock(block, src);
try sema.resolveTypeLayout(block, src, var_ty);
return block.addTy(.alloc, ptr_type);
const alloc = try block.addTy(.alloc, ptr_type);
try sema.stack_pointers.putNoClobber(sema.gpa, alloc, src);
return alloc;
}

// `Sema.addConstant` does not add the instruction to the block because it is
Expand All @@ -2437,6 +2442,7 @@ fn zirAllocExtended(
);
try sema.requireFunctionBlock(block, src);
try block.instructions.append(sema.gpa, Air.refToIndex(result).?);
try sema.stack_pointers.putNoClobber(sema.gpa, result, src);
return result;
}

Expand Down Expand Up @@ -2500,7 +2506,9 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
});
try sema.requireRuntimeBlock(block, var_decl_src);
try sema.resolveTypeFully(block, ty_src, var_ty);
return block.addTy(.alloc, ptr_type);
const alloc = try block.addTy(.alloc, ptr_type);
try sema.stack_pointers.putNoClobber(sema.gpa, alloc, var_decl_src);
return alloc;
}

fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
Expand All @@ -2522,7 +2530,9 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
});
try sema.requireRuntimeBlock(block, var_decl_src);
try sema.resolveTypeFully(block, ty_src, var_ty);
return block.addTy(.alloc, ptr_type);
const alloc = try block.addTy(.alloc, ptr_type);
try sema.stack_pointers.putNoClobber(sema.gpa, alloc, var_decl_src);
return alloc;
}

fn zirAllocInferred(
Expand Down Expand Up @@ -2558,6 +2568,7 @@ fn zirAllocInferred(
);
try sema.requireFunctionBlock(block, src);
try block.instructions.append(sema.gpa, Air.refToIndex(result).?);
try sema.stack_pointers.putNoClobber(sema.gpa, result, src);
return result;
}

Expand Down Expand Up @@ -3497,6 +3508,26 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
}
}
}
// Check for array/struct init returning pointer to local
if (Zir.refToIndex(extra.lhs)) |ptr_index| blk: {
const base_index = Zir.refToIndex(switch (zir_tags[ptr_index]) {
.field_ptr => sema.code.extraData(Zir.Inst.Field, zir_datas[ptr_index].pl_node.payload_index).data.lhs,
.elem_ptr_imm => sema.code.extraData(Zir.Inst.ElemPtrImm, zir_datas[ptr_index].pl_node.payload_index).data.ptr,
else => break :blk,
}) orelse break :blk;

const container_ptr_index = switch (zir_tags[base_index]) {
.array_base_ptr,
.field_base_ptr,
=> Zir.refToIndex(zir_datas[base_index].un_node.operand) orelse break :blk,
.coerce_result_ptr => Zir.refToIndex(zir_datas[base_index].bin.rhs) orelse break :blk,
else => break :blk,
};
if (zir_tags[container_ptr_index] != .extended) break :blk;
if (zir_datas[container_ptr_index].extended.opcode != .ret_ptr) break :blk;

try sema.checkReturnPtrToLocal(block, src, operand);
}

return sema.storePtr(block, src, ptr, operand);
}
Expand Down Expand Up @@ -6287,7 +6318,11 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
return sema.addConstant(Type.usize, ptr_val);
}
try sema.requireRuntimeBlock(block, ptr_src);
return block.addUnOp(.ptrtoint, ptr);
const res = try block.addUnOp(.ptrtoint, ptr);
if (sema.stack_pointers.get(ptr)) |some| {
try sema.stack_pointers.put(sema.gpa, res, some);
}
return res;
}

fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
Expand All @@ -6313,7 +6348,11 @@ fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
const field_name = sema.code.nullTerminatedString(extra.field_name_start);
const object_ptr = sema.resolveInst(extra.lhs);
return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);
const res = try sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);
if (sema.stack_pointers.get(object_ptr)) |some| {
try sema.stack_pointers.put(sema.gpa, res, some);
}
return res;
}

fn zirFieldCallBind(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
Expand Down Expand Up @@ -6411,7 +6450,11 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air

const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
const operand = sema.resolveInst(extra.rhs);
return sema.bitCast(block, dest_ty, operand, operand_src);
const res = try sema.bitCast(block, dest_ty, operand, operand_src);
if (sema.stack_pointers.get(operand)) |some| {
try sema.stack_pointers.put(sema.gpa, res, some);
}
return res;
}

fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
Expand Down Expand Up @@ -8546,7 +8589,13 @@ fn zirArithmetic(
const lhs = sema.resolveInst(extra.lhs);
const rhs = sema.resolveInst(extra.rhs);

return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src);
const res = try sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src);
if (sema.stack_pointers.get(lhs)) |some| {
try sema.stack_pointers.put(sema.gpa, res, some);
} else if (sema.stack_pointers.get(rhs)) |some| {
try sema.stack_pointers.put(sema.gpa, res, some);
}
return res;
}

fn zirOverflowArithmetic(
Expand Down Expand Up @@ -11454,11 +11503,27 @@ fn analyzeRet(
return always_noreturn;
}

try sema.checkReturnPtrToLocal(block, src, operand);

try sema.resolveTypeLayout(block, src, sema.fn_ret_ty);
_ = try block.addUnOp(.ret, operand);
return always_noreturn;
}

fn checkReturnPtrToLocal(sema: *Sema, block: *Block, src: LazySrcLoc, maybe_ptr: Air.Inst.Ref) !void {
if (block.inlining != null) return;
if (sema.typeOf(maybe_ptr).zigTypeTag() != .Pointer) return;
if (sema.stack_pointers.get(maybe_ptr)) |some| {
const msg = msg: {
const msg = try sema.errMsg(block, src, "function returns pointer to local variable", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(block, some, msg, "local variable declared here", .{});
break :msg msg;
};
return sema.failWithOwnedErrorMsg(block, msg);
}
}

fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
// extend this swich as additional operators are implemented
return switch (tag) {
Expand Down Expand Up @@ -11763,6 +11828,7 @@ fn zirStructInit(

if (is_ref) {
const alloc = try block.addTy(.alloc, resolved_ty);
try sema.stack_pointers.putNoClobber(sema.gpa, alloc, src);
const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty);
try sema.storePtr(block, src, field_ptr, init_inst);
return alloc;
Expand Down Expand Up @@ -11814,6 +11880,7 @@ fn finishStructInit(

if (is_ref) {
const alloc = try block.addTy(.alloc, struct_ty);
try sema.stack_pointers.putNoClobber(sema.gpa, alloc, src);
for (field_inits) |field_init, i_usize| {
const i = @intCast(u32, i_usize);
const field_src = src;
Expand Down Expand Up @@ -11882,6 +11949,7 @@ fn zirStructInitAnon(
.@"addrspace" = target_util.defaultAddressSpace(target, .local),
});
const alloc = try block.addTy(.alloc, alloc_ty);
try sema.stack_pointers.putNoClobber(sema.gpa, alloc, src);
var extra_index = extra.end;
for (types) |field_ty, i_usize| {
const i = @intCast(u32, i_usize);
Expand Down Expand Up @@ -11980,6 +12048,7 @@ fn zirArrayInit(
.@"addrspace" = target_util.defaultAddressSpace(target, .local),
});
const alloc = try block.addTy(.alloc, alloc_ty);
try sema.stack_pointers.putNoClobber(sema.gpa, alloc, src);

const elem_ptr_ty = try Type.ptr(sema.arena, target, .{
.mutable = true,
Expand Down Expand Up @@ -12048,6 +12117,7 @@ fn zirArrayInitAnon(
.@"addrspace" = target_util.defaultAddressSpace(target, .local),
});
const alloc = try block.addTy(.alloc, alloc_ty);
try sema.stack_pointers.putNoClobber(sema.gpa, alloc, src);
for (operands) |operand, i_usize| {
const i = @intCast(u32, i_usize);
const field_ptr_ty = try Type.ptr(sema.arena, target, .{
Expand Down Expand Up @@ -12808,7 +12878,11 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
try sema.addSafetyCheck(block, is_aligned, .incorrect_alignment);
}
}
return block.addBitCast(type_res, operand_coerced);
const res = try block.addBitCast(type_res, operand_coerced);
if (sema.stack_pointers.get(operand_res)) |some| {
try sema.stack_pointers.put(sema.gpa, res, some);
}
return res;
}

fn zirErrSetCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
Expand Down Expand Up @@ -12889,7 +12963,11 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
}
};

return sema.coerceCompatiblePtrs(block, aligned_dest_ty, ptr, operand_src);
const res = try sema.coerceCompatiblePtrs(block, aligned_dest_ty, ptr, operand_src);
if (sema.stack_pointers.get(operand)) |some| {
try sema.stack_pointers.put(sema.gpa, res, some);
}
return res;
}

fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
Expand Down Expand Up @@ -12985,7 +13063,11 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
.@"volatile" = ptr_info.@"volatile",
.size = ptr_info.size,
});
return sema.coerceCompatiblePtrs(block, dest_ty, ptr, ptr_src);
const res = try sema.coerceCompatiblePtrs(block, dest_ty, ptr, ptr_src);
if (sema.stack_pointers.get(ptr)) |some| {
try sema.stack_pointers.put(sema.gpa, res, some);
}
return res;
}

fn zirBitCount(
Expand Down Expand Up @@ -14209,7 +14291,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
}

try sema.requireRuntimeBlock(block, src);
return block.addInst(.{
const res = try block.addInst(.{
.tag = .field_parent_ptr,
.data = .{ .ty_pl = .{
.ty = try sema.addType(result_ptr),
Expand All @@ -14219,6 +14301,10 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
}),
} },
});
if (sema.stack_pointers.get(field_ptr)) |some| {
try sema.stack_pointers.put(sema.gpa, res, some);
}
return res;
}

fn zirMinMax(
Expand Down Expand Up @@ -16296,6 +16382,20 @@ fn coerce(
dest_ty_unresolved: Type,
inst: Air.Inst.Ref,
inst_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
const res = try sema.coerceNoPtrCheck(block, dest_ty_unresolved, inst, inst_src);
if (sema.stack_pointers.get(inst)) |some| {
try sema.stack_pointers.put(sema.gpa, res, some);
}
return res;
}

fn coerceNoPtrCheck(
sema: *Sema,
block: *Block,
dest_ty_unresolved: Type,
inst: Air.Inst.Ref,
inst_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
switch (dest_ty_unresolved.tag()) {
.var_args_param => return sema.coerceVarArgParam(block, inst, inst_src),
Expand Down Expand Up @@ -18287,7 +18387,13 @@ fn analyzeRef(
try sema.storePtr(block, src, alloc, operand);

// TODO: Replace with sema.coerce when that supports adding pointer constness.
return sema.bitCast(block, ptr_type, alloc, src);
const res = try sema.bitCast(block, ptr_type, alloc, src);
if (operand_ty.zigTypeTag() != .Pointer) {
try sema.stack_pointers.putNoClobber(sema.gpa, res, src);
} else if (sema.stack_pointers.get(operand)) |some| {
try sema.stack_pointers.putNoClobber(sema.gpa, res, some);
}
return res;
}

fn analyzeLoad(
Expand Down Expand Up @@ -18557,7 +18663,11 @@ fn analyzeSlice(

const opt_new_ptr_val = try sema.resolveMaybeUndefVal(block, ptr_src, new_ptr);
const new_ptr_val = opt_new_ptr_val orelse {
return block.addBitCast(return_ty, new_ptr);
const res = try block.addBitCast(return_ty, new_ptr);
if (sema.stack_pointers.get(ptr_or_slice)) |some| {
try sema.stack_pointers.put(sema.gpa, res, some);
}
return res;
};

if (!new_ptr_val.isUndef()) {
Expand All @@ -18584,7 +18694,7 @@ fn analyzeSlice(
});

try sema.requireRuntimeBlock(block, src);
return block.addInst(.{
const res = try block.addInst(.{
.tag = .slice,
.data = .{ .ty_pl = .{
.ty = try sema.addType(return_ty),
Expand All @@ -18594,6 +18704,10 @@ fn analyzeSlice(
}),
} },
});
if (sema.stack_pointers.get(ptr_or_slice)) |some| {
try sema.stack_pointers.put(sema.gpa, res, some);
}
return res;
}

/// Asserts that lhs and rhs types are both numeric.
Expand Down
21 changes: 20 additions & 1 deletion src/codegen/llvm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,7 @@ pub const DeclGen = struct {
const llvm_fn = dg.llvmModule().addFunctionInAddressSpace(fqn, fn_type, llvm_addrspace);
gop.value_ptr.* = llvm_fn;

const is_extern = decl.val.tag() == .extern_fn;
const is_extern = decl.isExtern();
if (!is_extern) {
llvm_fn.setLinkage(.Internal);
llvm_fn.setUnnamedAddr(.True);
Expand Down Expand Up @@ -954,6 +954,25 @@ pub const DeclGen = struct {
const llvm_global = dg.object.llvm_module.addGlobalInAddressSpace(llvm_type, fqn, llvm_addrspace);
gop.value_ptr.* = llvm_global;

// This is needed for declarations created by `@extern`.
if (decl.isExtern()) {
llvm_global.setValueName(decl.name);
llvm_global.setUnnamedAddr(.False);
llvm_global.setLinkage(.External);
if (decl.val.castTag(.variable)) |variable| {
const single_threaded = dg.module.comp.bin_file.options.single_threaded;
if (variable.data.is_threadlocal and !single_threaded) {
llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);
} else {
llvm_global.setThreadLocalMode(.NotThreadLocal);
}
if (variable.data.is_weak_linkage) llvm_global.setLinkage(.ExternalWeak);
}
} else {
llvm_global.setLinkage(.Internal);
llvm_global.setUnnamedAddr(.True);
}

return llvm_global;
}

Expand Down
Loading