Description
This issue is split from #287. What the result location mechanism has accomplished is that expressions do not introduce copies for intermediate values. For example:
const Point = struct {
x: i32,
y: i32,
};
fn foo() Point {
return bar();
}
fn bar() Point {
return Point{
.x = 1,
.y = 2,
};
}
test "result location" {
var point = foo();
}
Previously, the code return bar()
would introduce an extra copy, so the body of the function foo
would needlessly copy the point before returning it. This copying would happen at every expression, recursively when the type is an aggregate type (such as struct
). Now that the result location mechanism is merged into master, you can see that the foo
function does not introduce an extra copy:
define internal fastcc void @foo(%Point* nonnull sret) unnamed_addr #2 !dbg !35 {
Entry:
call fastcc void @bar(%Point* sret %0), !dbg !44
ret void, !dbg !46
}
However, if the expression unwraps an optional:
fn foo() Point {
return bar().?;
}
fn bar() ?Point {
return Point{
.x = 1,
.y = 2,
};
}
Now there must be a temporary stack variable and a memcpy:
define internal fastcc void @foo(%Point* nonnull sret) unnamed_addr #2 !dbg !35 {
Entry:
%1 = alloca { %Point, i1 }, align 4
call fastcc void @bar({ %Point, i1 }* sret %1), !dbg !44
%2 = getelementptr inbounds { %Point, i1 }, { %Point, i1 }* %1, i32 0, i32 1, !dbg !46
%3 = load i1, i1* %2, align 1, !dbg !46
br i1 %3, label %UnwrapOptionalOk, label %UnwrapOptionalFail, !dbg !46
UnwrapOptionalFail: ; preds = %Entry
tail call fastcc void @panic(%"[]u8"* @1, %builtin.StackTrace* null), !dbg !46
unreachable, !dbg !46
UnwrapOptionalOk: ; preds = %Entry
%4 = getelementptr inbounds { %Point, i1 }, { %Point, i1 }* %1, i32 0, i32 0, !dbg !46
%5 = bitcast %Point* %4 to i8*, !dbg !46
%6 = bitcast %Point* %0 to i8*, !dbg !46
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %6, i8* align 4 %5, i64 8, i1 false), !dbg !46
ret void, !dbg !47
}
The equivalent is true for the zig code with catch
:
fn foo() Point {
return bar() catch unreachable;
}
fn bar() anyerror!Point {
return Point{
.x = 1,
.y = 2,
};
}
This means that when writing a function that returns type ?T
or !T
, one cannot be sure that the return result location will not be copied.
That's what this issue is meant to address. A solution to this issue will mean that calling such functions will not require a temporary variable or memcpy the result.
One thing to note is that wrapping an optional or error union does not have a copy; it is only unwrapping that is in question here:
fn foo() ?Point {
return bar();
}
fn bar() Point {
return Point{
.x = 1,
.y = 2,
};
}
You can see there is no copy here:
define internal fastcc void @foo({ %Point, i1 }* nonnull sret) unnamed_addr #2 !dbg !35 {
Entry:
%1 = getelementptr inbounds { %Point, i1 }, { %Point, i1 }* %0, i32 0, i32 1, !dbg !49
store i1 true, i1* %1, !dbg !49
%2 = getelementptr inbounds { %Point, i1 }, { %Point, i1 }* %0, i32 0, i32 0, !dbg !49
call fastcc void @bar(%Point* sret %2), !dbg !49
ret void, !dbg !51
}
Related issue: #2765