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

Added documentation for Result Location Semantics #5035

Closed
wants to merge 13 commits into from
48 changes: 45 additions & 3 deletions doc/langref.html.in
Original file line number Diff line number Diff line change
Expand Up @@ -5458,9 +5458,51 @@ test "@intToPtr for pointer to zero bit type" {
{#header_close#}

{#header_open|Result Location Semantics#}
<p>
<a href="https://github.com/ziglang/zig/issues/2809">TODO add documentation for this</a>
</p>
<p>Certain expressions which manipulate a value in memory will directly initialize the final location of
that memory, to avoid making multiple allocations and copying between them (note: "allocation" here means
of registers or stack space, not dynamic as is usually meant):</p>
{#code_begin|syntax#}
pub fn main() void {
// foo() allocates its result directly in x -- only one allocation is made
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Allocation" is usually considered dynamic allocation, but that's not what's happening here.
Here we're talking about more general allocation of registers or stack space for variables.
For field2 below, not even that - the allocation of space was already done by var y so we're just referring to that space instead.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, what would the appropriate term for that be then?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure. I guess you could just clarify it; that the term refers to allocation of variables, which is either on the stack or in a register, rather than a dynamic allocation. 🤔

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about just qualify the allocation as a stack allocation?

Copy link
Contributor

@Tetralux Tetralux Apr 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it may not (and probably won't) always be a stack allocation, and you might care---Zig being a systems language---about that fact.

OTOH, you could argue that putting it in a register is an optimization, and that it's easier to think about if you just talk about the stack -- even though it's an optimization that always happens.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, then 'non-heap' allocation, if you want to factor in compiler optimization potentials.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, cleared up and rebased. Now it should be ready for merge.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also subscribed to #2761 and #2765 so I can fix those points when it's relevant (haven't yet gotten the hang of inline testing).

var x = foo();

// Result location is properly communicated through struct fields and if expressions
var y = Data {
.field1 = 26,
.field2 = if (condition) bar() else baz(),
};

// Unwrapping errors or optionals currently does not elide copy (https://github.com/ziglang/zig/issues/2765)
var z = quux() catch unreachable; // Value is allocated twice
}

fn foo() Large {
// Direct return correctly passes result location to callsite
return Large {
// ...
};
}

fn bar() Large {
// Assignment to a variable always makes another allocation, even when this variable is returned
// -- automatic copy elision in this case is planned (https://github.com/ziglang/zig/issues/2765)
var l = Large {};
}

fn quux() !Large {
return Large {};
}
{#code_end#}
<p>The current result location semantics elide copies from the following locations where possible:</p>
<ul>
<li>Variable declaration bodies</li>
<li>Struct, array and union initializations</li>
<li>If statement branches</li>
<li>Switch statement arms</li>
<li>Function returns</li>
<li>{#syntax#}catch{#endsyntax#} and {#syntax#}orelse{#endsyntax#} default statements</li>
<li>Arguments to {#link|bitCast#}</li>
</ul>
{#header_close#}

{#header_open|usingnamespace#}
Expand Down
2 changes: 1 addition & 1 deletion lib/std/build/emit_raw.zig
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const BinaryElfOutput = struct {
sort.sort(*BinaryElfSegment, self.segments.span(), segmentSortCompare);

if (self.segments.items.len > 0) {
const firstSegment = self.segments.at(0);
const firstSegment = self.segments.items[0];
if (firstSegment.firstSection) |firstSection| {
const diff = firstSection.elfOffset - firstSegment.elfOffset;

Expand Down
1 change: 1 addition & 0 deletions lib/std/os/windows.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub const psapi = @import("windows/psapi.zig");
pub const shell32 = @import("windows/shell32.zig");
pub const user32 = @import("windows/user32.zig");
pub const ws2_32 = @import("windows/ws2_32.zig");
pub const gdi32 = @import("windows/gdi32.zig");

pub usingnamespace @import("windows/bits.zig");

Expand Down
2 changes: 2 additions & 0 deletions lib/std/os/windows/bits.zig
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ pub const HINSTANCE = *@OpaqueType();
pub const HMENU = *@OpaqueType();
pub const HMODULE = *@OpaqueType();
pub const HWND = *@OpaqueType();
pub const HDC = *@OpaqueType();
pub const HGLRC = *@OpaqueType();
pub const FARPROC = *@OpaqueType();
pub const INT = c_int;
pub const LPBYTE = *BYTE;
Expand Down
45 changes: 45 additions & 0 deletions lib/std/os/windows/gdi32.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
usingnamespace @import("bits.zig");

pub const PIXELFORMATDESCRIPTOR = extern struct {
nSize: WORD = @sizeOf(PIXELFORMATDESCRIPTOR),
nVersion: WORD,
dwFlags: DWORD,
iPixelType: BYTE,
cColorBits: BYTE,
cRedBits: BYTE,
cRedShift: BYTE,
cGreenBits: BYTE,
cGreenShift: BYTE,
cBlueBits: BYTE,
cBlueShift: BYTE,
cAlphaBits: BYTE,
cAlphaShift: BYTE,
cAccumBits: BYTE,
cAccumRedBits: BYTE,
cAccumGreenBits: BYTE,
cAccumBlueBits: BYTE,
cAccumAlphaBits: BYTE,
cDepthBits: BYTE,
cStencilBits: BYTE,
cAuxBuffers: BYTE,
iLayerType: BYTE,
bReserved: BYTE,
dwLayerMask: DWORD,
dwVisibleMask: DWORD,
dwDamageMask: DWORD,
};

pub extern "gdi32" fn SetPixelFormat(
hdc: ?HDC,
format: i32,
ppfd: ?*const PIXELFORMATDESCRIPTOR,
) callconv(.Stdcall) bool;

pub extern "gdi32" fn ChoosePixelFormat(
hdc: ?HDC,
ppfd: ?*const PIXELFORMATDESCRIPTOR,
) callconv(.Stdcall) i32;

pub extern "gdi32" fn SwapBuffers(hdc: ?HDC) callconv(.Stdcall) bool;
pub extern "gdi32" fn wglCreateContext(hdc: ?HDC) callconv(.Stdcall) ?HGLRC;
pub extern "gdi32" fn wglMakeCurrent(hdc: ?HDC, hglrc: ?HGLRC) callconv(.Stdcall) bool;
137 changes: 136 additions & 1 deletion lib/std/os/windows/user32.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,104 @@
usingnamespace @import("bits.zig");

// PM
pub const PM_REMOVE = 0x0001;
pub const PM_NOREMOVE = 0x0000;
pub const PM_NOYIELD = 0x0002;

// WM
pub const WM_NULL = 0x0000;
pub const WM_CREATE = 0x0001;
pub const WM_DESTROY = 0x0002;
pub const WM_MOVE = 0x0003;
pub const WM_SIZE = 0x0005;

pub const WM_ACTIVATE = 0x0006;
pub const WM_PAINT = 0x000F;
pub const WM_CLOSE = 0x0010;
pub const WM_QUIT = 0x0012;
pub const WM_SETFOCUS = 0x0007;

pub const WM_KILLFOCUS = 0x0008;
pub const WM_ENABLE = 0x000A;
pub const WM_SETREDRAW = 0x000B;

pub const WM_SYSCOLORCHANGE = 0x0015;
pub const WM_SHOWWINDOW = 0x0018;

pub const WM_WINDOWPOSCHANGING = 0x0046;
pub const WM_WINDOWPOSCHANGED = 0x0047;
pub const WM_POWER = 0x0048;

pub const WM_CONTEXTMENU = 0x007B;
pub const WM_STYLECHANGING = 0x007C;
pub const WM_STYLECHANGED = 0x007D;
pub const WM_DISPLAYCHANGE = 0x007E;
pub const WM_GETICON = 0x007F;
pub const WM_SETICON = 0x0080;

pub const WM_INPUT_DEVICE_CHANGE = 0x00fe;
pub const WM_INPUT = 0x00FF;
pub const WM_KEYFIRST = 0x0100;
pub const WM_KEYDOWN = 0x0100;
pub const WM_KEYUP = 0x0101;
pub const WM_CHAR = 0x0102;
pub const WM_DEADCHAR = 0x0103;
pub const WM_SYSKEYDOWN = 0x0104;
pub const WM_SYSKEYUP = 0x0105;
pub const WM_SYSCHAR = 0x0106;
pub const WM_SYSDEADCHAR = 0x0107;
pub const WM_UNICHAR = 0x0109;
pub const WM_KEYLAST = 0x0109;

pub const WM_COMMAND = 0x0111;
pub const WM_SYSCOMMAND = 0x0112;
pub const WM_TIMER = 0x0113;

pub const WM_MOUSEFIRST = 0x0200;
pub const WM_MOUSEMOVE = 0x0200;
pub const WM_LBUTTONDOWN = 0x0201;
pub const WM_LBUTTONUP = 0x0202;
pub const WM_LBUTTONDBLCLK = 0x0203;
pub const WM_RBUTTONDOWN = 0x0204;
pub const WM_RBUTTONUP = 0x0205;
pub const WM_RBUTTONDBLCLK = 0x0206;
pub const WM_MBUTTONDOWN = 0x0207;
pub const WM_MBUTTONUP = 0x0208;
pub const WM_MBUTTONDBLCLK = 0x0209;
pub const WM_MOUSEWHEEL = 0x020A;
pub const WM_XBUTTONDOWN = 0x020B;
pub const WM_XBUTTONUP = 0x020C;
pub const WM_XBUTTONDBLCLK = 0x020D;

// WA
pub const WA_INACTIVE = 0;
pub const WA_ACTIVE = 0x0006;
pub const WM_ACTIVATE = 0x0006;

// WS
pub const WS_OVERLAPPED = 0x00000000;
pub const WS_CAPTION = 0x00C00000;
pub const WS_SYSMENU = 0x00080000;
pub const WS_THICKFRAME = 0x00040000;
pub const WS_MINIMIZEBOX = 0x00020000;
pub const WS_MAXIMIZEBOX = 0x00010000;

// PFD
pub const PFD_DRAW_TO_WINDOW = 0x00000004;
pub const PFD_SUPPORT_OPENGL = 0x00000020;
pub const PFD_DOUBLEBUFFER = 0x00000001;
pub const PFD_MAIN_PLANE = 0;
pub const PFD_TYPE_RGBA = 0;

// CS
pub const CS_HREDRAW = 0x0002;
pub const CS_VREDRAW = 0x0001;
pub const CS_OWNDC = 0x0020;

// SW
pub const SW_HIDE = 0;
pub const SW_SHOW = 5;

pub const WNDPROC = fn (HWND, UINT, WPARAM, LPARAM) callconv(.Stdcall) LRESULT;

pub const WNDCLASSEXA = extern struct {
Expand All @@ -17,6 +116,20 @@ pub const WNDCLASSEXA = extern struct {
hIconSm: ?HICON,
};

pub const POINT = extern struct {
x: c_long, y: c_long
};

pub const MSG = extern struct {
hWnd: ?HWND,
message: UINT,
wParam: WPARAM,
lParam: LPARAM,
time: DWORD,
pt: POINT,
lPrivate: DWORD,
};

pub extern "user32" fn CreateWindowExA(
dwExStyle: DWORD,
lpClassName: LPCSTR,
Expand All @@ -32,6 +145,28 @@ pub extern "user32" fn CreateWindowExA(
lpParam: ?LPVOID,
) callconv(.Stdcall) ?HWND;

pub extern "user32" fn RegisterClassExA(*const WNDCLASSEXA) callconv(.Stdcall) c_ushort;
pub extern "user32" fn DefWindowProcA(HWND, Msg: UINT, WPARAM, LPARAM) callconv(.Stdcall) LRESULT;
pub extern "user32" fn GetModuleHandleA(lpModuleName: ?LPCSTR) callconv(.Stdcall) HMODULE;
pub extern "user32" fn ShowWindow(hWnd: ?HWND, nCmdShow: i32) callconv(.Stdcall) bool;
pub extern "user32" fn UpdateWindow(hWnd: ?HWND) callconv(.Stdcall) bool;
pub extern "user32" fn GetDC(hWnd: ?HWND) callconv(.Stdcall) ?HDC;

pub extern "user32" fn RegisterClassExA(*const WNDCLASSEXA) callconv(.Stdcall) c_ushort;
pub extern "user32" fn PeekMessageA(
lpMsg: ?*MSG,
hWnd: ?HWND,
wMsgFilterMin: UINT,
wMsgFilterMax: UINT,
wRemoveMsg: UINT,
) callconv(.Stdcall) bool;

pub extern "user32" fn GetMessageA(
lpMsg: ?*MSG,
hWnd: ?HWND,
wMsgFilterMin: UINT,
wMsgFilterMax: UINT,
) callconv(.Stdcall) bool;

pub extern "user32" fn TranslateMessage(lpMsg: *const MSG) callconv(.Stdcall) bool;
pub extern "user32" fn DispatchMessageA(lpMsg: *const MSG) callconv(.Stdcall) LRESULT;
pub extern "user32" fn PostQuitMessage(nExitCode: i32) callconv(.Stdcall) void;
2 changes: 1 addition & 1 deletion src/ir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16974,7 +16974,7 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in

if (!instr_is_comptime(op2)) {
ir_add_error(ira, &bin_op_instruction->base.base,
buf_sprintf("LHS of shift must be an integer type, or RHS must be compile-time known"));
buf_sprintf("LHS of shift must be a fixed-width integer type, or RHS must be compile-time known"));
return ira->codegen->invalid_inst_gen;
}

Expand Down
1 change: 1 addition & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
" build-obj [source] create object from source or assembly\n"
" builtin show the source code of @import(\"builtin\")\n"
" cc use Zig as a drop-in C compiler\n"
" c++ use Zig as a drop-in C++ compiler\n"
" fmt parse files and render in canonical zig format\n"
" id print the base64-encoded compiler id\n"
" init-exe initialize a `zig build` application in the cwd\n"
Expand Down
2 changes: 1 addition & 1 deletion test/compile_errors.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5925,7 +5925,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ return 0x11 << x;
\\}
, &[_][]const u8{
"tmp.zig:2:17: error: LHS of shift must be an integer type, or RHS must be compile-time known",
"tmp.zig:2:17: error: LHS of shift must be a fixed-width integer type, or RHS must be compile-time known",
});

cases.add("shifting RHS is log2 of LHS int bit width",
Expand Down