From 6ae47c6f439d92b984697a484bfedf18b1123f3b Mon Sep 17 00:00:00 2001 From: flysand7 Date: Fri, 12 Jul 2024 18:04:39 +1100 Subject: [PATCH 01/34] [sys/windows] Add advapi bindings, process-related functions and context-related stuff --- core/sys/windows/advapi32.odin | 173 ++++++++++++++++++++++++++++++++- core/sys/windows/kernel32.odin | 45 +++++++++ core/sys/windows/types.odin | 153 +++++++++++++++++++++++++---- core/sys/windows/util.odin | 4 +- 4 files changed, 354 insertions(+), 21 deletions(-) diff --git a/core/sys/windows/advapi32.odin b/core/sys/windows/advapi32.odin index 163bf2a5e..4a7074506 100644 --- a/core/sys/windows/advapi32.odin +++ b/core/sys/windows/advapi32.odin @@ -18,6 +18,14 @@ foreign advapi32 { OpenAsSelf: BOOL, TokenHandle: ^HANDLE) -> BOOL --- + GetTokenInformation :: proc ( + TokenHandle: HANDLE, + TokenInformationClass: TOKEN_INFORMATION_CLASS, + TokenInformation: LPVOID, + TokenInformationLength: DWORD, + ReturnLength: PDWORD, + ) -> BOOL --- + CryptAcquireContextW :: proc(hProv: ^HCRYPTPROV, szContainer, szProvider: wstring, dwProvType, dwFlags: DWORD) -> DWORD --- CryptGenRandom :: proc(hProv: HCRYPTPROV, dwLen: DWORD, buf: LPVOID) -> DWORD --- CryptReleaseContext :: proc(hProv: HCRYPTPROV, dwFlags: DWORD) -> DWORD --- @@ -44,7 +52,17 @@ foreign advapi32 { cbSid: ^DWORD, ReferencedDomainName: wstring, cchReferencedDomainName: ^DWORD, - peUse: ^SID_TYPE, + peUse: PSID_NAME_USE, + ) -> BOOL --- + + LookupAccountSidW :: proc ( + lpSystemName: LPCWSTR, + Sid: PSID, + Name: LPWSTR, + cchName: LPDWORD, + ReferencedDomainName: LPWSTR, + cchReferencedDomainName: LPDWORD, + peUse: PSID_NAME_USE, ) -> BOOL --- CreateProcessWithLogonW :: proc( @@ -164,3 +182,156 @@ foreign advapi32 { AccessStatus: LPBOOL, ) -> BOOL --- } + +PTOKEN_INFORMATION_CLASS :: ^TOKEN_INFORMATION_CLASS +TOKEN_INFORMATION_CLASS :: enum i32 { + TokenUser = 1, + TokenGroups, + TokenPrivileges, + TokenOwner, + TokenPrimaryGroup, + TokenDefaultDacl, + TokenSource, + TokenType, + TokenImpersonationLevel, + TokenStatistics, + TokenRestrictedSids, + TokenSessionId, + TokenGroupsAndPrivileges, + TokenSessionReference, + TokenSandBoxInert, + TokenAuditPolicy, + TokenOrigin, + TokenElevationType, + TokenLinkedToken, + TokenElevation, + TokenHasRestrictions, + TokenAccessInformation, + TokenVirtualizationAllowed, + TokenVirtualizationEnabled, + TokenIntegrityLevel, + TokenUIAccess, + TokenMandatoryPolicy, + TokenLogonSid, + TokenIsAppContainer, + TokenCapabilities, + TokenAppContainerSid, + TokenAppContainerNumber, + TokenUserClaimAttributes, + TokenDeviceClaimAttributes, + TokenRestrictedUserClaimAttributes, + TokenRestrictedDeviceClaimAttributes, + TokenDeviceGroups, + TokenRestrictedDeviceGroups, + TokenSecurityAttributes, + TokenIsRestricted, + TokenProcessTrustLevel, + TokenPrivateNameSpace, + TokenSingletonAttributes, + TokenBnoIsolation, + TokenChildProcessFlags, + TokenIsLessPrivilegedAppContainer, + TokenIsSandboxed, + TokenIsAppSilo, + TokenLoggingInformation, + MaxTokenInfoClass, +} + +PSID_NAME_USE :: ^SID_NAME_USE +SID_NAME_USE :: enum i32 { + SidTypeUser = 1, + SidTypeGroup, + SidTypeDomain, + SidTypeAlias, + SidTypeWellKnownGroup, + SidTypeDeletedAccount, + SidTypeInvalid, + SidTypeUnknown, + SidTypeComputer, + SidTypeLabel, + SidTypeLogonSession, +} + +PTOKEN_USER :: ^TOKEN_USER +TOKEN_USER :: struct { + User: SID_AND_ATTRIBUTES, +} + +PSID_AND_ATTRIBUTES :: ^SID_AND_ATTRIBUTES +SID_AND_ATTRIBUTES :: struct { + Sid: rawptr, + Attributes: ULONG, +} + +PTOKEN_TYPE :: ^TOKEN_TYPE +TOKEN_TYPE :: enum { + TokenPrimary = 1, + TokenImpersonation = 2, +} + +PTOKEN_STATISTICS :: ^TOKEN_STATISTICS +TOKEN_STATISTICS :: struct { + TokenId: LUID, + AuthenticationId: LUID, + ExpirationTime: LARGE_INTEGER, + TokenType: TOKEN_TYPE, + ImpersonationLevel: SECURITY_IMPERSONATION_LEVEL, + DynamicCharged: DWORD, + DynamicAvailable: DWORD, + GroupCount: DWORD, + PrivilegeCount: DWORD, + ModifiedId: LUID, +} + + +TOKEN_SOURCE_LENGTH :: 8 +PTOKEN_SOURCE :: ^TOKEN_SOURCE +TOKEN_SOURCE :: struct { + SourceName: [TOKEN_SOURCE_LENGTH]CHAR, + SourceIdentifier: LUID, +} + + +PTOKEN_PRIVILEGES :: ^TOKEN_PRIVILEGES +TOKEN_PRIVILEGES :: struct { + PrivilegeCount: DWORD, + Privileges: [0]LUID_AND_ATTRIBUTES, +} + +PTOKEN_PRIMARY_GROUP :: ^TOKEN_PRIMARY_GROUP +TOKEN_PRIMARY_GROUP :: struct { + PrimaryGroup: PSID, +} + +PTOKEN_OWNER :: ^TOKEN_OWNER +TOKEN_OWNER :: struct { + Owner: PSID, +} + +PTOKEN_GROUPS_AND_PRIVILEGES :: ^TOKEN_GROUPS_AND_PRIVILEGES +TOKEN_GROUPS_AND_PRIVILEGES :: struct { + SidCount: DWORD, + SidLength: DWORD, + Sids: PSID_AND_ATTRIBUTES, + RestrictedSidCount: DWORD, + RestrictedSidLength: DWORD, + RestrictedSids: PSID_AND_ATTRIBUTES, + PrivilegeCount: DWORD, + PrivilegeLength: DWORD, + Privileges: PLUID_AND_ATTRIBUTES, + AuthenticationId: LUID, +} + +PTOKEN_DEFAULT_DACL :: ^TOKEN_DEFAULT_DACL +TOKEN_DEFAULT_DACL :: struct { + DefaultDacl: PACL, +} + +PACL :: ^ACL +ACL :: struct { + AclRevision: BYTE, + Sbz1: BYTE, + AclSize: WORD, + AceCount: WORD, + Sbz2: WORD, +} diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index a29dde7b5..92d561c30 100755 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -233,6 +233,12 @@ foreign kernel32 { QueryPerformanceCounter :: proc(lpPerformanceCount: ^LARGE_INTEGER) -> BOOL --- GetExitCodeProcess :: proc(hProcess: HANDLE, lpExitCode: LPDWORD) -> BOOL --- TerminateProcess :: proc(hProcess: HANDLE, uExitCode: UINT) -> BOOL --- + OpenProcess :: proc(dwDesiredAccess: DWORD, bInheritHandle: BOOL, dwProcessId: DWORD) -> HANDLE --- + OpenThread :: proc(dwDesiredAccess: DWORD, bInheritHandle: BOOL, dwThreadId: DWORD) -> HANDLE --- + GetThreadContext :: proc( + hThread: HANDLE, + lpContext: LPCONTEXT, + ) -> BOOL --- CreateProcessW :: proc( lpApplicationName: LPCWSTR, lpCommandLine: LPWSTR, @@ -543,6 +549,45 @@ THREAD_PRIORITY_IDLE :: THREAD_BASE_PRIORITY_IDLE THREAD_MODE_BACKGROUND_BEGIN :: 0x00010000 THREAD_MODE_BACKGROUND_END :: 0x00020000 +PROCESS_ALL_ACCESS :: 0x000F0000 | SYNCHRONIZE | 0xFFFF +PROCESS_CREATE_PROCESS :: 0x0080 +PROCESS_CREATE_THREAD :: 0x0002 +PROCESS_DUP_HANDLE :: 0x0040 +PROCESS_QUERY_INFORMATION :: 0x0400 +PROCESS_QUERY_LIMITED_INFORMATION :: 0x1000 +PROCESS_SET_INFORMATION :: 0x0200 +PROCESS_SET_QUOTA :: 0x0100 +PROCESS_SUSPEND_RESUME :: 0x0800 +PROCESS_TERMINATE :: 0x0001 +PROCESS_VM_OPERATION :: 0x0008 +PROCESS_VM_READ :: 0x0010 +PROCESS_VM_WRITE :: 0x0020 + +THREAD_ALL_ACCESS :: \ + THREAD_DIRECT_IMPERSONATION | + THREAD_GET_CONTEXT | + THREAD_IMPERSONATE | + THREAD_QUERY_INFORMATION | + THREAD_QUERY_LIMITED_INFORMATION | + THREAD_SET_CONTEXT | + THREAD_SET_INFORMATION | + THREAD_SET_LIMITED_INFORMATION | + THREAD_SET_THREAD_TOKEN | + THREAD_SUSPEND_RESUME | + THREAD_TERMINATE | + SYNCHRONIZE +THREAD_DIRECT_IMPERSONATION :: 0x0200 +THREAD_GET_CONTEXT :: 0x0008 +THREAD_IMPERSONATE :: 0x0100 +THREAD_QUERY_INFORMATION :: 0x0040 +THREAD_QUERY_LIMITED_INFORMATION :: 0x0800 +THREAD_SET_CONTEXT :: 0x0010 +THREAD_SET_INFORMATION :: 0x0020 +THREAD_SET_LIMITED_INFORMATION :: 0x0400 +THREAD_SET_THREAD_TOKEN :: 0x0080 +THREAD_SUSPEND_RESUME :: 0x0002 +THREAD_TERMINATE :: 0x0001 + COPY_FILE_FAIL_IF_EXISTS :: 0x00000001 COPY_FILE_RESTARTABLE :: 0x00000002 COPY_FILE_OPEN_SOURCE_FOR_WRITE :: 0x00000004 diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index e568a7bc7..92c6023eb 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -64,6 +64,7 @@ LONG_PTR :: int UINT_PTR :: uintptr ULONG :: c_ulong ULONGLONG :: c_ulonglong +LONGLONG :: c_longlong UCHAR :: BYTE NTSTATUS :: c.long COLORREF :: DWORD @@ -2145,6 +2146,7 @@ SECURITY_IMPERSONATION_LEVEL :: enum { SECURITY_INFORMATION :: DWORD ANYSIZE_ARRAY :: 1 +PLUID_AND_ATTRIBUTES :: ^LUID_AND_ATTRIBUTES LUID_AND_ATTRIBUTES :: struct { Luid: LUID, Attributes: DWORD, @@ -2570,7 +2572,139 @@ EXCEPTION_RECORD :: struct { ExceptionInformation: [EXCEPTION_MAXIMUM_PARAMETERS]LPVOID, } -CONTEXT :: struct{} // TODO(bill) + +CONTEXT :: struct { + P1Home: DWORD64, + P2Home: DWORD64, + P3Home: DWORD64, + P4Home: DWORD64, + P5Home: DWORD64, + P6Home: DWORD64, + ContextFlags: DWORD, + MxCsr: DWORD, + SegCs: WORD, + SegDs: WORD, + SegEs: WORD, + SegFs: WORD, + SegGs: WORD, + SegSs: WORD, + EFlags: DWORD, + Dr0: DWORD64, + Dr1: DWORD64, + Dr2: DWORD64, + Dr3: DWORD64, + Dr6: DWORD64, + Dr7: DWORD64, + Rax: DWORD64, + Rcx: DWORD64, + Rdx: DWORD64, + Rbx: DWORD64, + Rsp: DWORD64, + Rbp: DWORD64, + Rsi: DWORD64, + Rdi: DWORD64, + R8: DWORD64, + R9: DWORD64, + R10: DWORD64, + R11: DWORD64, + R12: DWORD64, + R13: DWORD64, + R14: DWORD64, + R15: DWORD64, + Rip: DWORD64, + _: struct #raw_union { + FltSave: XMM_SAVE_AREA32, + Q: [16]NEON128, + D: [32]ULONGLONG, + _: struct { + Header: [2]M128A, + Legacy: [8]M128A, + Xmm0: M128A, + Xmm1: M128A, + Xmm2: M128A, + Xmm3: M128A, + Xmm4: M128A, + Xmm5: M128A, + Xmm6: M128A, + Xmm7: M128A, + Xmm8: M128A, + Xmm9: M128A, + Xmm10: M128A, + Xmm11: M128A, + Xmm12: M128A, + Xmm13: M128A, + Xmm14: M128A, + Xmm15: M128A, + }, + S: [32]DWORD, + }, + VectorRegister: [26]M128A, + VectorControl: DWORD64, + DebugControl: DWORD64, + LastBranchToRip: DWORD64, + LastBranchFromRip: DWORD64, + LastExceptionToRip: DWORD64, + LastExceptionFromRip: DWORD64, +} + +PCONTEXT :: ^CONTEXT +LPCONTEXT :: ^CONTEXT + +when size_of(uintptr) == 32 { + XSAVE_FORMAT :: struct #align(16) { + ControlWord: WORD, + StatusWord: WORD, + TagWord: BYTE, + Reserved1: BYTE, + ErrorOpcode: WORD, + ErrorOffset: DWORD, + ErrorSelector: WORD, + Reserved2: WORD, + DataOffset: DWORD, + DataSelector: WORD, + Reserved3: WORD, + MxCsr: DWORD, + MxCsr_Mask: DWORD, + FloatRegisters: [8]M128A, + // 32-bit specific + XmmRegisters: [8]M128A, + Reserved4: [192]BYTE, + StackControl: [7]DWORD, + Cr0NpxState: DWORD, + } +} else { + XSAVE_FORMAT :: struct #align(16) { + ControlWord: WORD, + StatusWord: WORD, + TagWord: BYTE, + Reserved1: BYTE, + ErrorOpcode: WORD, + ErrorOffset: DWORD, + ErrorSelector: WORD, + Reserved2: WORD, + DataOffset: DWORD, + DataSelector: WORD, + Reserved3: WORD, + MxCsr: DWORD, + MxCsr_Mask: DWORD, + FloatRegisters: [8]M128A, + // 64-bit specific + XmmRegisters: [16]M128A, + Reserved4: [96]BYTE, + } +} + +XMM_SAVE_AREA32 :: XSAVE_FORMAT + +M128A :: struct { + Low: ULONGLONG, + High: LONGLONG, +} + +NEON128 :: struct { + Low: ULONGLONG, + High: LONGLONG, +} EXCEPTION_POINTERS :: struct { ExceptionRecord: ^EXCEPTION_RECORD, @@ -2733,23 +2867,6 @@ PROFILEINFOW :: struct { hProfile: HANDLE, } -// Used in LookupAccountNameW -SID_NAME_USE :: distinct DWORD - -SID_TYPE :: enum SID_NAME_USE { - User = 1, - Group, - Domain, - Alias, - WellKnownGroup, - DeletedAccount, - Invalid, - Unknown, - Computer, - Label, - LogonSession, -} - SECURITY_MAX_SID_SIZE :: 68 // https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid diff --git a/core/sys/windows/util.odin b/core/sys/windows/util.odin index be0d8c02b..64d623000 100644 --- a/core/sys/windows/util.odin +++ b/core/sys/windows/util.odin @@ -202,7 +202,7 @@ get_computer_name_and_account_sid :: proc(username: string) -> (computer_name: s username_w := utf8_to_utf16(username, context.temp_allocator) cbsid: DWORD computer_name_size: DWORD - pe_use := SID_TYPE.User + pe_use := SID_NAME_USE.SidTypeUser res := LookupAccountNameW( nil, // Look on this computer first @@ -244,7 +244,7 @@ get_sid :: proc(username: string, sid: ^SID) -> (ok: bool) { username_w := utf8_to_utf16(username, context.temp_allocator) cbsid: DWORD computer_name_size: DWORD - pe_use := SID_TYPE.User + pe_use := SID_NAME_USE.SidTypeUser res := LookupAccountNameW( nil, // Look on this computer first From a7d95ab3e1392ba5facb044a321c578565566fdb Mon Sep 17 00:00:00 2001 From: flysand7 Date: Fri, 12 Jul 2024 20:23:19 +1100 Subject: [PATCH 02/34] [core/os]: Fix read_console not stopping the read when newline can't fit in output slice --- core/os/file_windows.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/os/file_windows.odin b/core/os/file_windows.odin index 3efe30d17..d900c5e70 100644 --- a/core/os/file_windows.odin +++ b/core/os/file_windows.odin @@ -125,7 +125,7 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) { src := buf8[:buf8_len] ctrl_z := false - for i := 0; i < len(src) && n+i < len(b); i += 1 { + for i := 0; i < len(src) && n < len(b); i += 1 { x := src[i] if x == 0x1a { // ctrl-z ctrl_z = true From b4683f4399a7c83152e986ee8c61acabee806962 Mon Sep 17 00:00:00 2001 From: xzores Date: Fri, 12 Jul 2024 23:57:45 +0200 Subject: [PATCH 03/34] Update stb_truetype.odin --- vendor/stb/truetype/stb_truetype.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/stb/truetype/stb_truetype.odin b/vendor/stb/truetype/stb_truetype.odin index 6993cd2b7..e6defff5f 100644 --- a/vendor/stb/truetype/stb_truetype.odin +++ b/vendor/stb/truetype/stb_truetype.odin @@ -568,7 +568,7 @@ foreign stbtt { // some of the values for the IDs are below; for more see the truetype spec: // http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html // http://www.microsoft.com/typography/otspec/name.htm - GetFontNameString :: proc(font: ^fontinfo, length: c.int, platformID: PLATFORM_ID, encodingID, languageID, nameID: c.int) -> cstring --- + GetFontNameString :: proc(font: ^fontinfo, length: ^c.int, platformID: PLATFORM_ID, encodingID, languageID, nameID: c.int) -> cstring --- } From 64ae99f016998fe78a5a0304767463c46050567a Mon Sep 17 00:00:00 2001 From: VladPavliuk Date: Sat, 13 Jul 2024 14:13:59 +0300 Subject: [PATCH 04/34] Add support of `ignore` tag for `json.marshal` --- core/encoding/json/marshal.odin | 4 +++- tests/core/encoding/json/test_core_json.odin | 21 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 0464c24d1..99ae81485 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -406,7 +406,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: ti := runtime.type_info_base(type_info_of(v.id)) info := ti.variant.(runtime.Type_Info_Struct) first_iteration := true - for name, i in info.names { + fields_loop: for name, i in info.names { omitempty := false json_name, extra := json_name_from_tag_value(reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json")) @@ -414,6 +414,8 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: switch flag { case "omitempty": omitempty = true + case "ignore": + continue fields_loop } } diff --git a/tests/core/encoding/json/test_core_json.odin b/tests/core/encoding/json/test_core_json.odin index 92c050952..9fde5a443 100644 --- a/tests/core/encoding/json/test_core_json.odin +++ b/tests/core/encoding/json/test_core_json.odin @@ -368,4 +368,25 @@ utf8_string_of_multibyte_characters :: proc(t: ^testing.T) { val, err := json.parse_string(`"🐛✅"`) defer json.destroy_value(val) testing.expectf(t, err == nil, "Expected `json.parse` to return nil, got %v", err) +} + +@test +struct_with_ignore_tags :: proc(t: ^testing.T) { + My_Struct :: struct { + a: string `json:"_,ignore"`, + } + + my_struct := My_Struct{ + a = "test", + } + + my_struct_marshaled, marshal_err := json.marshal(my_struct) + defer delete(my_struct_marshaled) + + testing.expectf(t, marshal_err == nil, "Expected `json.marshal` to return nil error, got %v", marshal_err) + + my_struct_json := transmute(string)my_struct_marshaled + expected_json := `{}` + + testing.expectf(t, expected_json == my_struct_json, "Expected `json.marshal` to return %s, got %s", expected_json, my_struct_json) } \ No newline at end of file From 75076e2d649e8ec9df541a9bfb4e3de408a9167f Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Sat, 13 Jul 2024 16:01:33 +0200 Subject: [PATCH 05/34] RenderPassEncoderSetPushConstants should take a rawptr instead of cstring for the data --- vendor/wgpu/wgpu.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/wgpu/wgpu.odin b/vendor/wgpu/wgpu.odin index 74df83fde..4efe572cf 100644 --- a/vendor/wgpu/wgpu.odin +++ b/vendor/wgpu/wgpu.odin @@ -1676,7 +1676,7 @@ when ODIN_OS != .JS { GetVersion :: proc() -> u32 --- - RenderPassEncoderSetPushConstants :: proc(encoder: RenderPassEncoder, stages: ShaderStageFlags, offset: u32, sizeBytes: u32, data: cstring) --- + RenderPassEncoderSetPushConstants :: proc(encoder: RenderPassEncoder, stages: ShaderStageFlags, offset: u32, sizeBytes: u32, data: rawptr) --- RenderPassEncoderMultiDrawIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) --- RenderPassEncoderMultiDrawIndexedIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) --- From 76fe5d1346e623b535d5168ec36e1254ef769c68 Mon Sep 17 00:00:00 2001 From: VladPavliuk Date: Sun, 14 Jul 2024 00:21:05 +0300 Subject: [PATCH 06/34] Align ignore syntax of json tags with fmt, cbor --- core/encoding/json/marshal.odin | 9 ++++++--- tests/core/encoding/json/test_core_json.odin | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 99ae81485..30426f911 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -406,16 +406,19 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: ti := runtime.type_info_base(type_info_of(v.id)) info := ti.variant.(runtime.Type_Info_Struct) first_iteration := true - fields_loop: for name, i in info.names { + for name, i in info.names { omitempty := false json_name, extra := json_name_from_tag_value(reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json")) + + if json_name == "-" { + continue + } + for flag in strings.split_iterator(&extra, ",") { switch flag { case "omitempty": omitempty = true - case "ignore": - continue fields_loop } } diff --git a/tests/core/encoding/json/test_core_json.odin b/tests/core/encoding/json/test_core_json.odin index 9fde5a443..a50dd7fe0 100644 --- a/tests/core/encoding/json/test_core_json.odin +++ b/tests/core/encoding/json/test_core_json.odin @@ -373,7 +373,7 @@ utf8_string_of_multibyte_characters :: proc(t: ^testing.T) { @test struct_with_ignore_tags :: proc(t: ^testing.T) { My_Struct :: struct { - a: string `json:"_,ignore"`, + a: string `json:"-"`, } my_struct := My_Struct{ From b686b072d5e6bba55af194db52dac1cf02cbdda1 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sun, 14 Jul 2024 14:59:44 +1100 Subject: [PATCH 07/34] [os2/file]: Fixes related to handle inheritance All file handles created on Windows used to be made non-inheritable, by forcing the .Close_On_Exec flag in _open() function. In addition, there was an issue with security descriptor being freed before use, which has been fixed. --- core/os/os2/file_windows.odin | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 37f8f44de..b88ee8a69 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -75,11 +75,9 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (han access |= win32.FILE_APPEND_DATA } share_mode := u32(win32.FILE_SHARE_READ | win32.FILE_SHARE_WRITE) - sa: ^win32.SECURITY_ATTRIBUTES - if .Close_On_Exec not_in flags { - sa = &win32.SECURITY_ATTRIBUTES{} - sa.nLength = size_of(win32.SECURITY_ATTRIBUTES) - sa.bInheritHandle = true + sa := win32.SECURITY_ATTRIBUTES { + nLength = size_of(win32.SECURITY_ATTRIBUTES), + bInheritHandle = .Close_On_Exec not_in flags, } create_mode: u32 = win32.OPEN_EXISTING @@ -101,7 +99,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (han // NOTE(bill): Open has just asked to create a file in read-only mode. // If the file already exists, to make it akin to a *nix open call, // the call preserves the existing permissions. - h := win32.CreateFileW(path, access, share_mode, sa, win32.TRUNCATE_EXISTING, win32.FILE_ATTRIBUTE_NORMAL, nil) + h := win32.CreateFileW(path, access, share_mode, &sa, win32.TRUNCATE_EXISTING, win32.FILE_ATTRIBUTE_NORMAL, nil) if h == win32.INVALID_HANDLE { switch e := win32.GetLastError(); e { case win32.ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, win32.ERROR_PATH_NOT_FOUND: @@ -114,7 +112,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (han } } } - h := win32.CreateFileW(path, access, share_mode, sa, create_mode, attrs, nil) + h := win32.CreateFileW(path, access, share_mode, &sa, create_mode, attrs, nil) if h == win32.INVALID_HANDLE { return 0, _get_platform_error() } @@ -124,7 +122,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (han _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) { flags := flags if flags != nil else {.Read} - handle := _open_internal(name, flags + {.Close_On_Exec}, perm) or_return + handle := _open_internal(name, flags, perm) or_return return _new_file(handle, name), nil } From cdede4928cbbe38e043f3a784020b2ed40c5470a Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Sat, 13 Jul 2024 23:16:22 -0700 Subject: [PATCH 08/34] move to a growing queue --- src/thread_pool.cpp | 106 +++++++++++++++++++++++++++++--------------- src/threading.cpp | 39 +++++++++++----- 2 files changed, 99 insertions(+), 46 deletions(-) diff --git a/src/thread_pool.cpp b/src/thread_pool.cpp index 5dbbe37c4..2b176db1c 100644 --- a/src/thread_pool.cpp +++ b/src/thread_pool.cpp @@ -16,7 +16,6 @@ struct ThreadPool { std::atomic running; Futex tasks_available; - Futex tasks_left; }; @@ -46,7 +45,7 @@ gb_internal void thread_pool_destroy(ThreadPool *pool) { for_array_off(i, 1, pool->threads) { Thread *t = &pool->threads[i]; - pool->tasks_available.fetch_add(1, std::memory_order_relaxed); + pool->tasks_available.fetch_add(1, std::memory_order_acquire); futex_broadcast(&pool->tasks_available); thread_join_and_destroy(t); } @@ -54,51 +53,86 @@ gb_internal void thread_pool_destroy(ThreadPool *pool) { gb_free(pool->threads_allocator, pool->threads.data); } +TaskRingBuffer *taskring_grow(TaskRingBuffer *ring, ssize_t bottom, ssize_t top) { + TaskRingBuffer *new_ring = taskring_init(ring->size * 2); + for (ssize_t i = top; i < bottom; i++) { + new_ring->buffer[i % new_ring->size] = ring->buffer[i % ring->size]; + } + return new_ring; +} + void thread_pool_queue_push(Thread *thread, WorkerTask task) { - u64 capture; - u64 new_capture; - do { - capture = thread->head_and_tail.load(); + ssize_t bot = thread->queue.bottom.load(std::memory_order_relaxed); + ssize_t top = thread->queue.top.load(std::memory_order_acquire); + TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_relaxed); - u64 mask = thread->capacity - 1; - u64 head = (capture >> 32) & mask; - u64 tail = ((u32)capture) & mask; + ssize_t size = bot - top; + if (size > (cur_ring->size - 1)) { + // Queue is full + thread->queue.ring = taskring_grow(thread->queue.ring, bot, top); + cur_ring = thread->queue.ring.load(std::memory_order_relaxed); + } - u64 new_head = (head + 1) & mask; - GB_ASSERT_MSG(new_head != tail, "Thread Queue Full!"); - - // This *must* be done in here, to avoid a potential race condition where we no longer own the slot by the time we're assigning - thread->queue[head] = task; - new_capture = (new_head << 32) | tail; - } while (!thread->head_and_tail.compare_exchange_weak(capture, new_capture)); + cur_ring->buffer[bot % cur_ring->size] = task; + std::atomic_thread_fence(std::memory_order_release); + thread->queue.bottom.store(bot + 1, std::memory_order_relaxed); thread->pool->tasks_left.fetch_add(1, std::memory_order_release); thread->pool->tasks_available.fetch_add(1, std::memory_order_relaxed); futex_broadcast(&thread->pool->tasks_available); } -bool thread_pool_queue_pop(Thread *thread, WorkerTask *task) { - u64 capture; - u64 new_capture; - do { - capture = thread->head_and_tail.load(std::memory_order_acquire); +bool thread_pool_queue_take(Thread *thread, WorkerTask *task) { + ssize_t bot = thread->queue.bottom.load(std::memory_order_relaxed) - 1; + TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_relaxed); + thread->queue.bottom.store(bot, std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_seq_cst); - u64 mask = thread->capacity - 1; - u64 head = (capture >> 32) & mask; - u64 tail = ((u32)capture) & mask; + ssize_t top = thread->queue.top.load(std::memory_order_relaxed); + if (top <= bot) { - u64 new_tail = (tail + 1) & mask; - if (tail == head) { - return false; + // Queue is not empty + *task = cur_ring->buffer[bot % cur_ring->size]; + if (top == bot) { + // Only one entry left in queue + if (!thread->queue.top.compare_exchange_strong(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) { + // Race failed + thread->queue.bottom.store(bot + 1, std::memory_order_relaxed); + return false; + } + + thread->queue.bottom.store(bot + 1, std::memory_order_relaxed); + return true; } - // Making a copy of the task before we increment the tail, avoiding the same potential race condition as above - *task = thread->queue[tail]; + // We got a task without hitting a race + return true; + } else { + // Queue is empty + thread->queue.bottom.store(bot + 1, std::memory_order_relaxed); + return false; + } +} - new_capture = (head << 32) | new_tail; - } while (!thread->head_and_tail.compare_exchange_weak(capture, new_capture, std::memory_order_release)); +bool thread_pool_queue_steal(Thread *thread, WorkerTask *task) { + ssize_t top = thread->queue.top.load(std::memory_order_acquire); + std::atomic_thread_fence(std::memory_order_seq_cst); + ssize_t bot = thread->queue.bottom.load(std::memory_order_acquire); - return true; + bool ret = false; + if (top < bot) { + // Queue is not empty + TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_consume); + *task = cur_ring->buffer[top % cur_ring->size]; + + if (!thread->queue.top.compare_exchange_strong(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) { + // Race failed + ret = false; + } else { + ret = true; + } + } + return ret; } gb_internal bool thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data) { @@ -115,12 +149,11 @@ gb_internal void thread_pool_wait(ThreadPool *pool) { while (pool->tasks_left.load(std::memory_order_acquire)) { // if we've got tasks on our queue, run them - while (thread_pool_queue_pop(current_thread, &task)) { + while (thread_pool_queue_take(current_thread, &task)) { task.do_work(task.data); pool->tasks_left.fetch_sub(1, std::memory_order_release); } - // is this mem-barriered enough? // This *must* be executed in this order, so the futex wakes immediately // if rem_tasks has changed since we checked last, otherwise the program @@ -145,7 +178,7 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) { usize finished_tasks = 0; i32 state; - while (thread_pool_queue_pop(current_thread, &task)) { + while (thread_pool_queue_take(current_thread, &task)) { task.do_work(task.data); pool->tasks_left.fetch_sub(1, std::memory_order_release); @@ -167,7 +200,7 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) { Thread *thread = &pool->threads.data[idx]; WorkerTask task; - if (thread_pool_queue_pop(thread, &task)) { + if (thread_pool_queue_steal(thread, &task)) { task.do_work(task.data); pool->tasks_left.fetch_sub(1, std::memory_order_release); @@ -182,6 +215,7 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) { // if we've done all our work, and there's nothing to steal, go to sleep state = pool->tasks_available.load(std::memory_order_acquire); + if (!pool->running) { break; } futex_wait(&pool->tasks_available, state); main_loop_continue:; diff --git a/src/threading.cpp b/src/threading.cpp index 717dcb874..dda98631b 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -46,6 +46,18 @@ typedef struct WorkerTask { void *data; } WorkerTask; +typedef struct TaskRingBuffer { + std::atomic size; + std::atomic buffer; +} TaskRingBuffer; + +typedef struct TaskQueue { + std::atomic top; + std::atomic bottom; + + std::atomic ring; +} TaskQueue; + struct Thread { #if defined(GB_SYSTEM_WINDOWS) void *win32_handle; @@ -54,12 +66,9 @@ struct Thread { #endif isize idx; - - WorkerTask *queue; - size_t capacity; - std::atomic head_and_tail; - isize stack_size; + + struct TaskQueue queue; struct ThreadPool *pool; }; @@ -551,6 +560,18 @@ gb_internal void *internal_thread_proc(void *arg) { } #endif +TaskRingBuffer *taskring_init(ssize_t size) { + TaskRingBuffer *ring = (TaskRingBuffer *)gb_alloc(heap_allocator(), sizeof(TaskRingBuffer)); + ring->size = size; + ring->buffer = (WorkerTask *)gb_alloc_array(heap_allocator(), WorkerTask, ring->size); + return ring; +} + +void thread_queue_destroy(TaskQueue *q) { + gb_free(heap_allocator(), (*q->ring).buffer); + gb_free(heap_allocator(), q->ring); +} + gb_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) { gb_zero_item(t); #if defined(GB_SYSTEM_WINDOWS) @@ -559,14 +580,12 @@ gb_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) { t->posix_handle = 0; #endif - t->capacity = 1 << 14; // must be a power of 2 - t->queue = gb_alloc_array(heap_allocator(), WorkerTask, t->capacity); - t->head_and_tail = 0; + // Size must be a power of 2 + t->queue.ring = taskring_init(1 << 14); t->pool = pool; t->idx = idx; } - gb_internal void thread_init_and_start(ThreadPool *pool, Thread *t, isize idx) { thread_init(pool, t, idx); isize stack_size = 0; @@ -598,7 +617,7 @@ gb_internal void thread_join_and_destroy(Thread *t) { t->posix_handle = 0; #endif - gb_free(heap_allocator(), t->queue); + thread_queue_destroy(&t->queue); } gb_internal void thread_set_name(Thread *t, char const *name) { From 4420128dc1d15775d1f56d47b858d8ffe75e4b9f Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Sun, 14 Jul 2024 00:29:58 -0700 Subject: [PATCH 09/34] handle steal-fail vs steal-empty --- src/thread_pool.cpp | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/thread_pool.cpp b/src/thread_pool.cpp index 2b176db1c..da7e724a8 100644 --- a/src/thread_pool.cpp +++ b/src/thread_pool.cpp @@ -10,6 +10,12 @@ gb_internal void thread_pool_destroy(ThreadPool *pool); gb_internal bool thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data); gb_internal void thread_pool_wait(ThreadPool *pool); +enum GrabState { + GrabSuccess = 0, + GrabEmpty = 1, + GrabFailed = 2, +}; + struct ThreadPool { gbAllocator threads_allocator; Slice threads; @@ -82,7 +88,7 @@ void thread_pool_queue_push(Thread *thread, WorkerTask task) { futex_broadcast(&thread->pool->tasks_available); } -bool thread_pool_queue_take(Thread *thread, WorkerTask *task) { +GrabState thread_pool_queue_take(Thread *thread, WorkerTask *task) { ssize_t bot = thread->queue.bottom.load(std::memory_order_relaxed) - 1; TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_relaxed); thread->queue.bottom.store(bot, std::memory_order_relaxed); @@ -98,28 +104,28 @@ bool thread_pool_queue_take(Thread *thread, WorkerTask *task) { if (!thread->queue.top.compare_exchange_strong(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) { // Race failed thread->queue.bottom.store(bot + 1, std::memory_order_relaxed); - return false; + return GrabEmpty; } thread->queue.bottom.store(bot + 1, std::memory_order_relaxed); - return true; + return GrabSuccess; } // We got a task without hitting a race - return true; + return GrabSuccess; } else { // Queue is empty thread->queue.bottom.store(bot + 1, std::memory_order_relaxed); - return false; + return GrabEmpty; } } -bool thread_pool_queue_steal(Thread *thread, WorkerTask *task) { +GrabState thread_pool_queue_steal(Thread *thread, WorkerTask *task) { ssize_t top = thread->queue.top.load(std::memory_order_acquire); std::atomic_thread_fence(std::memory_order_seq_cst); ssize_t bot = thread->queue.bottom.load(std::memory_order_acquire); - bool ret = false; + GrabState ret = GrabEmpty; if (top < bot) { // Queue is not empty TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_consume); @@ -127,9 +133,9 @@ bool thread_pool_queue_steal(Thread *thread, WorkerTask *task) { if (!thread->queue.top.compare_exchange_strong(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) { // Race failed - ret = false; + ret = GrabFailed; } else { - ret = true; + ret = GrabSuccess; } } return ret; @@ -149,7 +155,7 @@ gb_internal void thread_pool_wait(ThreadPool *pool) { while (pool->tasks_left.load(std::memory_order_acquire)) { // if we've got tasks on our queue, run them - while (thread_pool_queue_take(current_thread, &task)) { + while (!thread_pool_queue_take(current_thread, &task)) { task.do_work(task.data); pool->tasks_left.fetch_sub(1, std::memory_order_release); } @@ -178,7 +184,7 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) { usize finished_tasks = 0; i32 state; - while (thread_pool_queue_take(current_thread, &task)) { + while (!thread_pool_queue_take(current_thread, &task)) { task.do_work(task.data); pool->tasks_left.fetch_sub(1, std::memory_order_release); @@ -200,7 +206,13 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) { Thread *thread = &pool->threads.data[idx]; WorkerTask task; - if (thread_pool_queue_steal(thread, &task)) { + + GrabState ret = thread_pool_queue_steal(thread, &task); + if (ret == GrabFailed) { + goto main_loop_continue; + } else if (ret == GrabEmpty) { + continue; + } else if (ret == GrabSuccess) { task.do_work(task.data); pool->tasks_left.fetch_sub(1, std::memory_order_release); From 64feb7599e8ec01c2ec7c8d709df1cc70651c06b Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Sun, 14 Jul 2024 00:33:40 -0700 Subject: [PATCH 10/34] move to isize --- src/thread_pool.cpp | 18 +++++++++--------- src/threading.cpp | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/thread_pool.cpp b/src/thread_pool.cpp index da7e724a8..bf953ddd0 100644 --- a/src/thread_pool.cpp +++ b/src/thread_pool.cpp @@ -59,20 +59,20 @@ gb_internal void thread_pool_destroy(ThreadPool *pool) { gb_free(pool->threads_allocator, pool->threads.data); } -TaskRingBuffer *taskring_grow(TaskRingBuffer *ring, ssize_t bottom, ssize_t top) { +TaskRingBuffer *taskring_grow(TaskRingBuffer *ring, isize bottom, isize top) { TaskRingBuffer *new_ring = taskring_init(ring->size * 2); - for (ssize_t i = top; i < bottom; i++) { + for (isize i = top; i < bottom; i++) { new_ring->buffer[i % new_ring->size] = ring->buffer[i % ring->size]; } return new_ring; } void thread_pool_queue_push(Thread *thread, WorkerTask task) { - ssize_t bot = thread->queue.bottom.load(std::memory_order_relaxed); - ssize_t top = thread->queue.top.load(std::memory_order_acquire); + isize bot = thread->queue.bottom.load(std::memory_order_relaxed); + isize top = thread->queue.top.load(std::memory_order_acquire); TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_relaxed); - ssize_t size = bot - top; + isize size = bot - top; if (size > (cur_ring->size - 1)) { // Queue is full thread->queue.ring = taskring_grow(thread->queue.ring, bot, top); @@ -89,12 +89,12 @@ void thread_pool_queue_push(Thread *thread, WorkerTask task) { } GrabState thread_pool_queue_take(Thread *thread, WorkerTask *task) { - ssize_t bot = thread->queue.bottom.load(std::memory_order_relaxed) - 1; + isize bot = thread->queue.bottom.load(std::memory_order_relaxed) - 1; TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_relaxed); thread->queue.bottom.store(bot, std::memory_order_relaxed); std::atomic_thread_fence(std::memory_order_seq_cst); - ssize_t top = thread->queue.top.load(std::memory_order_relaxed); + isize top = thread->queue.top.load(std::memory_order_relaxed); if (top <= bot) { // Queue is not empty @@ -121,9 +121,9 @@ GrabState thread_pool_queue_take(Thread *thread, WorkerTask *task) { } GrabState thread_pool_queue_steal(Thread *thread, WorkerTask *task) { - ssize_t top = thread->queue.top.load(std::memory_order_acquire); + isize top = thread->queue.top.load(std::memory_order_acquire); std::atomic_thread_fence(std::memory_order_seq_cst); - ssize_t bot = thread->queue.bottom.load(std::memory_order_acquire); + isize bot = thread->queue.bottom.load(std::memory_order_acquire); GrabState ret = GrabEmpty; if (top < bot) { diff --git a/src/threading.cpp b/src/threading.cpp index dda98631b..ac79efb05 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -47,13 +47,13 @@ typedef struct WorkerTask { } WorkerTask; typedef struct TaskRingBuffer { - std::atomic size; + std::atomic size; std::atomic buffer; } TaskRingBuffer; typedef struct TaskQueue { - std::atomic top; - std::atomic bottom; + std::atomic top; + std::atomic bottom; std::atomic ring; } TaskQueue; @@ -560,7 +560,7 @@ gb_internal void *internal_thread_proc(void *arg) { } #endif -TaskRingBuffer *taskring_init(ssize_t size) { +TaskRingBuffer *taskring_init(isize size) { TaskRingBuffer *ring = (TaskRingBuffer *)gb_alloc(heap_allocator(), sizeof(TaskRingBuffer)); ring->size = size; ring->buffer = (WorkerTask *)gb_alloc_array(heap_allocator(), WorkerTask, ring->size); From edc793d7c123a38826860ef72684308902a7012c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 11:39:05 +0100 Subject: [PATCH 11/34] Add `#no_capture args: ..T` to reuse the backing array stack memory --- core/fmt/fmt.odin | 58 +++++++++++++++++++-------------------- core/fmt/fmt_js.odin | 24 ++++++++-------- core/fmt/fmt_os.odin | 24 ++++++++-------- src/check_expr.cpp | 17 ++++++++++++ src/check_type.cpp | 17 ++++++++++++ src/checker.cpp | 1 + src/checker.hpp | 7 +++++ src/entity.cpp | 2 +- src/llvm_backend.hpp | 7 +++++ src/llvm_backend_proc.cpp | 27 +++++++++++++++++- src/parser.cpp | 1 + src/parser.hpp | 8 ++++-- 12 files changed, 136 insertions(+), 57 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 234f4afbd..e56211346 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -125,7 +125,7 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist // Returns: A formatted string. // @(require_results) -aprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> string { +aprint :: proc(#no_capture args: ..any, sep := " ", allocator := context.allocator) -> string { str: strings.Builder strings.builder_init(&str, allocator) return sbprint(&str, ..args, sep=sep) @@ -141,7 +141,7 @@ aprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> strin // Returns: A formatted string with a newline character at the end. // @(require_results) -aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> string { +aprintln :: proc(#no_capture args: ..any, sep := " ", allocator := context.allocator) -> string { str: strings.Builder strings.builder_init(&str, allocator) return sbprintln(&str, ..args, sep=sep) @@ -158,7 +158,7 @@ aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> str // Returns: A formatted string. The returned string must be freed accordingly. // @(require_results) -aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newline := false) -> string { +aprintf :: proc(fmt: string, #no_capture args: ..any, allocator := context.allocator, newline := false) -> string { str: strings.Builder strings.builder_init(&str, allocator) return sbprintf(&str, fmt, ..args, newline=newline) @@ -174,7 +174,7 @@ aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newlin // Returns: A formatted string. The returned string must be freed accordingly. // @(require_results) -aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string { +aprintfln :: proc(fmt: string, #no_capture args: ..any, allocator := context.allocator) -> string { return aprintf(fmt, ..args, allocator=allocator, newline=true) } // Creates a formatted string @@ -188,7 +188,7 @@ aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> s // Returns: A formatted string. // @(require_results) -tprint :: proc(args: ..any, sep := " ") -> string { +tprint :: proc(#no_capture args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) return sbprint(&str, ..args, sep=sep) @@ -204,7 +204,7 @@ tprint :: proc(args: ..any, sep := " ") -> string { // Returns: A formatted string with a newline character at the end. // @(require_results) -tprintln :: proc(args: ..any, sep := " ") -> string { +tprintln :: proc(#no_capture args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) return sbprintln(&str, ..args, sep=sep) @@ -221,7 +221,7 @@ tprintln :: proc(args: ..any, sep := " ") -> string { // Returns: A formatted string. // @(require_results) -tprintf :: proc(fmt: string, args: ..any, newline := false) -> string { +tprintf :: proc(fmt: string, #no_capture args: ..any, newline := false) -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) return sbprintf(&str, fmt, ..args, newline=newline) @@ -237,7 +237,7 @@ tprintf :: proc(fmt: string, args: ..any, newline := false) -> string { // Returns: A formatted string. // @(require_results) -tprintfln :: proc(fmt: string, args: ..any) -> string { +tprintfln :: proc(fmt: string, #no_capture args: ..any) -> string { return tprintf(fmt, ..args, newline=true) } // Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer. @@ -249,7 +249,7 @@ tprintfln :: proc(fmt: string, args: ..any) -> string { // // Returns: A formatted string // -bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string { +bprint :: proc(buf: []byte, #no_capture args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf) return sbprint(&sb, ..args, sep=sep) } @@ -262,7 +262,7 @@ bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string { // // Returns: A formatted string with a newline character at the end // -bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string { +bprintln :: proc(buf: []byte, #no_capture args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf) return sbprintln(&sb, ..args, sep=sep) } @@ -276,7 +276,7 @@ bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string { // // Returns: A formatted string // -bprintf :: proc(buf: []byte, fmt: string, args: ..any, newline := false) -> string { +bprintf :: proc(buf: []byte, fmt: string, #no_capture args: ..any, newline := false) -> string { sb := strings.builder_from_bytes(buf) return sbprintf(&sb, fmt, ..args, newline=newline) } @@ -289,7 +289,7 @@ bprintf :: proc(buf: []byte, fmt: string, args: ..any, newline := false) -> stri // // Returns: A formatted string // -bprintfln :: proc(buf: []byte, fmt: string, args: ..any) -> string { +bprintfln :: proc(buf: []byte, fmt: string, #no_capture args: ..any) -> string { return bprintf(buf, fmt, ..args, newline=true) } // Runtime assertion with a formatted message @@ -301,14 +301,14 @@ bprintfln :: proc(buf: []byte, fmt: string, args: ..any) -> string { // - loc: The location of the caller // @(disabled=ODIN_DISABLE_ASSERT) -assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) { +assertf :: proc(condition: bool, fmt: string, #no_capture args: ..any, loc := #caller_location) { if !condition { // NOTE(dragos): We are using the same trick as in builtin.assert // to improve performance to make the CPU not // execute speculatively, making it about an order of // magnitude faster @(cold) - internal :: proc(loc: runtime.Source_Code_Location, fmt: string, args: ..any) { + internal :: proc(loc: runtime.Source_Code_Location, fmt: string, #no_capture args: ..any) { p := context.assertion_failure_proc if p == nil { p = runtime.default_assertion_failure_proc @@ -326,7 +326,7 @@ assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_locati // - args: A variadic list of arguments to be formatted // - loc: The location of the caller // -panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { +panicf :: proc(fmt: string, #no_capture args: ..any, loc := #caller_location) -> ! { p := context.assertion_failure_proc if p == nil { p = runtime.default_assertion_failure_proc @@ -346,7 +346,7 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { // Returns: A formatted C string // @(require_results) -caprintf :: proc(format: string, args: ..any, newline := false) -> cstring { +caprintf :: proc(format: string, #no_capture args: ..any, newline := false) -> cstring { str: strings.Builder strings.builder_init(&str) sbprintf(&str, format, ..args, newline=newline) @@ -365,7 +365,7 @@ caprintf :: proc(format: string, args: ..any, newline := false) -> cstring { // Returns: A formatted C string // @(require_results) -caprintfln :: proc(format: string, args: ..any) -> cstring { +caprintfln :: proc(format: string, #no_capture args: ..any) -> cstring { return caprintf(format, ..args, newline=true) } // Creates a formatted C string @@ -379,7 +379,7 @@ caprintfln :: proc(format: string, args: ..any) -> cstring { // Returns: A formatted C string. // @(require_results) -ctprint :: proc(args: ..any, sep := " ") -> cstring { +ctprint :: proc(#no_capture args: ..any, sep := " ") -> cstring { str: strings.Builder strings.builder_init(&str, context.temp_allocator) sbprint(&str, ..args, sep=sep) @@ -399,7 +399,7 @@ ctprint :: proc(args: ..any, sep := " ") -> cstring { // Returns: A formatted C string // @(require_results) -ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring { +ctprintf :: proc(format: string, #no_capture args: ..any, newline := false) -> cstring { str: strings.Builder strings.builder_init(&str, context.temp_allocator) sbprintf(&str, format, ..args, newline=newline) @@ -418,7 +418,7 @@ ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring { // Returns: A formatted C string // @(require_results) -ctprintfln :: proc(format: string, args: ..any) -> cstring { +ctprintfln :: proc(format: string, #no_capture args: ..any) -> cstring { return ctprintf(format, ..args, newline=true) } // Formats using the default print settings and writes to the given strings.Builder @@ -430,7 +430,7 @@ ctprintfln :: proc(format: string, args: ..any) -> cstring { // // Returns: A formatted string // -sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { +sbprint :: proc(buf: ^strings.Builder, #no_capture args: ..any, sep := " ") -> string { wprint(strings.to_writer(buf), ..args, sep=sep, flush=true) return strings.to_string(buf^) } @@ -443,7 +443,7 @@ sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { // // Returns: The resulting formatted string // -sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { +sbprintln :: proc(buf: ^strings.Builder, #no_capture args: ..any, sep := " ") -> string { wprintln(strings.to_writer(buf), ..args, sep=sep, flush=true) return strings.to_string(buf^) } @@ -457,7 +457,7 @@ sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { // // Returns: The resulting formatted string // -sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any, newline := false) -> string { +sbprintf :: proc(buf: ^strings.Builder, fmt: string, #no_capture args: ..any, newline := false) -> string { wprintf(strings.to_writer(buf), fmt, ..args, flush=true, newline=newline) return strings.to_string(buf^) } @@ -469,7 +469,7 @@ sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any, newline := fal // // Returns: A formatted string // -sbprintfln :: proc(buf: ^strings.Builder, format: string, args: ..any) -> string { +sbprintfln :: proc(buf: ^strings.Builder, format: string, #no_capture args: ..any) -> string { return sbprintf(buf, format, ..args, newline=true) } // Formats and writes to an io.Writer using the default print settings @@ -481,7 +481,7 @@ sbprintfln :: proc(buf: ^strings.Builder, format: string, args: ..any) -> string // // Returns: The number of bytes written // -wprint :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int { +wprint :: proc(w: io.Writer, #no_capture args: ..any, sep := " ", flush := true) -> int { fi: Info fi.writer = w @@ -522,7 +522,7 @@ wprint :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int { // // Returns: The number of bytes written // -wprintln :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int { +wprintln :: proc(w: io.Writer, #no_capture args: ..any, sep := " ", flush := true) -> int { fi: Info fi.writer = w @@ -549,11 +549,11 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int { // // Returns: The number of bytes written // -wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline := false) -> int { +wprintf :: proc(w: io.Writer, fmt: string, #no_capture args: ..any, flush := true, newline := false) -> int { MAX_CHECKED_ARGS :: 64 assert(len(args) <= MAX_CHECKED_ARGS, "number of args > 64 is unsupported") - parse_options :: proc(fi: ^Info, fmt: string, index, end: int, unused_args: ^bit_set[0 ..< MAX_CHECKED_ARGS], args: ..any) -> int { + parse_options :: proc(fi: ^Info, fmt: string, index, end: int, unused_args: ^bit_set[0 ..< MAX_CHECKED_ARGS], #no_capture args: ..any) -> int { i := index // Prefix @@ -809,7 +809,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline : // // Returns: The number of bytes written. // -wprintfln :: proc(w: io.Writer, format: string, args: ..any, flush := true) -> int { +wprintfln :: proc(w: io.Writer, format: string, #no_capture args: ..any, flush := true) -> int { return wprintf(w, format, ..args, flush=flush, newline=true) } // Writes a ^runtime.Type_Info value to an io.Writer diff --git a/core/fmt/fmt_js.odin b/core/fmt/fmt_js.odin index acf218eb5..4389b8d87 100644 --- a/core/fmt/fmt_js.odin +++ b/core/fmt/fmt_js.odin @@ -43,7 +43,7 @@ fd_to_writer :: proc(fd: os.Handle, loc := #caller_location) -> io.Writer { } // fprint formats using the default print settings and writes to fd -fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #caller_location) -> int { +fprint :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true, loc := #caller_location) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -54,7 +54,7 @@ fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #ca } // fprintln formats using the default print settings and writes to fd -fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #caller_location) -> int { +fprintln :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true, loc := #caller_location) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -66,7 +66,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := # } // fprintf formats according to the specified format string and writes to fd -fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false, loc := #caller_location) -> int { +fprintf :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true, newline := false, loc := #caller_location) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -78,24 +78,24 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline } // fprintfln formats according to the specified format string and writes to fd, followed by a newline. -fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, loc := #caller_location) -> int { +fprintfln :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true, loc := #caller_location) -> int { return fprintf(fd, fmt, ..args, flush=flush, newline=true, loc=loc) } // print formats using the default print settings and writes to stdout -print :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(w=stdout, args=args, sep=sep, flush=flush) } +print :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprint(w=stdout, args=args, sep=sep, flush=flush) } // println formats using the default print settings and writes to stdout -println :: proc(args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stdout, args=args, sep=sep, flush=flush) } +println :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stdout, args=args, sep=sep, flush=flush) } // printf formats according to the specififed format string and writes to stdout -printf :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush) } +printf :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush) } // printfln formats according to the specified format string and writes to stdout, followed by a newline. -printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) } +printfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) } // eprint formats using the default print settings and writes to stderr -eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(w=stderr, args=args, sep=sep, flush=flush) } +eprint :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprint(w=stderr, args=args, sep=sep, flush=flush) } // eprintln formats using the default print settings and writes to stderr -eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stderr, args=args, sep=sep, flush=flush) } +eprintln :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stderr, args=args, sep=sep, flush=flush) } // eprintf formats according to the specififed format string and writes to stderr -eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stderr, fmt, ..args, flush=flush) } +eprintf :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stderr, fmt, ..args, flush=flush) } // eprintfln formats according to the specified format string and writes to stderr, followed by a newline. -eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) } +eprintfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) } diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin index 9de0d43be..538f7a08b 100644 --- a/core/fmt/fmt_os.odin +++ b/core/fmt/fmt_os.odin @@ -9,7 +9,7 @@ import "core:io" import "core:bufio" // fprint formats using the default print settings and writes to fd -fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int { +fprint :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -20,7 +20,7 @@ fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int { } // fprintln formats using the default print settings and writes to fd -fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int { +fprintln :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -31,7 +31,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int { return wprintln(w, ..args, sep=sep, flush=flush) } // fprintf formats according to the specified format string and writes to fd -fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false) -> int { +fprintf :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true, newline := false) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -42,7 +42,7 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline return wprintf(w, fmt, ..args, flush=flush, newline=newline) } // fprintfln formats according to the specified format string and writes to fd, followed by a newline. -fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int { +fprintfln :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(fd, fmt, ..args, flush=flush, newline=true) } fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info, flush := true) -> (n: int, err: io.Error) { @@ -67,19 +67,19 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid, flush := true) -> (n: int, err: } // print formats using the default print settings and writes to os.stdout -print :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) } +print :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) } // println formats using the default print settings and writes to os.stdout -println :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) } +println :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) } // printf formats according to the specified format string and writes to os.stdout -printf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) } +printf :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) } // printfln formats according to the specified format string and writes to os.stdout, followed by a newline. -printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush, newline=true) } +printfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush, newline=true) } // eprint formats using the default print settings and writes to os.stderr -eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) } +eprint :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) } // eprintln formats using the default print settings and writes to os.stderr -eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) } +eprintln :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) } // eprintf formats according to the specified format string and writes to os.stderr -eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) } +eprintf :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) } // eprintfln formats according to the specified format string and writes to os.stderr, followed by a newline. -eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush, newline=true) } +eprintfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush, newline=true) } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 12acca0cb..645d8ac5a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6033,6 +6033,23 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A Entity *vt = pt->params->Tuple.variables[pt->variadic_index]; o.type = vt->type; + + // NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameter that use `#no_capture` + // on the variadic parameter + if (c->decl && (vt->flags & EntityFlag_NoCapture)) { + bool found = false; + for (NoCaptureData &nc : c->decl->no_captures) { + if (are_types_identical(vt->type, nc.slice_type)) { + nc.max_count = gb_max(nc.max_count, variadic_operands.count); + found = true; + break; + } + } + if (!found) { + array_add(&c->decl->no_captures, NoCaptureData{vt->type, variadic_operands.count}); + } + } + } else { dummy_argument_count += 1; o.type = t_untyped_nil; diff --git a/src/check_type.cpp b/src/check_type.cpp index dd8559114..d1c9bb381 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1953,6 +1953,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para error(name, "'#by_ptr' can only be applied to variable fields"); p->flags &= ~FieldFlag_by_ptr; } + if (p->flags&FieldFlag_no_capture) { + error(name, "'#no_capture' can only be applied to variable variadic fields"); + p->flags &= ~FieldFlag_no_capture; + } param = alloc_entity_type_name(scope, name->Ident.token, type, EntityState_Resolved); param->TypeName.is_type_alias = true; @@ -2054,6 +2058,15 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para p->flags &= ~FieldFlag_by_ptr; // Remove the flag } } + if (p->flags&FieldFlag_no_capture) { + if (!(is_variadic && variadic_index == variables.count)) { + error(name, "'#no_capture' can only be applied to a variadic parameter"); + p->flags &= ~FieldFlag_no_capture; + } else if (p->flags & FieldFlag_c_vararg) { + error(name, "'#no_capture' cannot be applied to a #c_vararg parameter"); + p->flags &= ~FieldFlag_no_capture; + } + } if (is_poly_name) { if (p->flags&FieldFlag_no_alias) { @@ -2115,6 +2128,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para if (p->flags&FieldFlag_by_ptr) { param->flags |= EntityFlag_ByPtr; } + if (p->flags&FieldFlag_no_capture) { + param->flags |= EntityFlag_NoCapture; + } + param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it add_entity(ctx, scope, name, param); diff --git a/src/checker.cpp b/src/checker.cpp index 8756cce1a..abacc13cb 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -184,6 +184,7 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { ptr_set_init(&d->deps, 0); ptr_set_init(&d->type_info_deps, 0); d->labels.allocator = heap_allocator(); + d->no_captures.allocator = heap_allocator(); } gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) { diff --git a/src/checker.hpp b/src/checker.hpp index 781737140..17722f6b6 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -181,6 +181,11 @@ char const *ProcCheckedState_strings[ProcCheckedState_COUNT] { "Checked", }; +struct NoCaptureData { + Type *slice_type; // ..elem_type + isize max_count; +}; + // DeclInfo is used to store information of certain declarations to allow for "any order" usage struct DeclInfo { DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment @@ -219,6 +224,8 @@ struct DeclInfo { Array labels; + Array no_captures; + // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time struct lbModule *code_gen_module; }; diff --git a/src/entity.cpp b/src/entity.cpp index 41d84e0f7..db6ffdd52 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -45,7 +45,7 @@ enum EntityFlag : u64 { EntityFlag_Value = 1ull<<11, EntityFlag_BitFieldField = 1ull<<12, - + EntityFlag_NoCapture = 1ull<<13, // #no_capture EntityFlag_PolyConst = 1ull<<15, EntityFlag_NotExported = 1ull<<16, diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 005358734..dd1041702 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -296,6 +296,11 @@ enum lbProcedureFlag : u32 { lbProcedureFlag_DebugAllocaCopy = 1<<1, }; +struct lbNoCaptureData { + Type *slice_type; + lbAddr base_array; +}; + struct lbProcedure { u32 flags; u16 state_flags; @@ -336,6 +341,8 @@ struct lbProcedure { bool in_multi_assignment; Array raw_input_parameters; + Array no_captures; + LLVMValueRef temp_callee_return_struct_memory; Ast *curr_stmt; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 610c34de2..ec244e185 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -517,6 +517,7 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) { lb_start_block(p, p->entry_block); map_init(&p->direct_parameters); + p->no_captures.allocator = heap_allocator(); GB_ASSERT(p->type != nullptr); @@ -3450,8 +3451,32 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } isize slice_len = var_args.count; if (slice_len > 0) { + lbAddr base_array = {}; + if (e->flags & EntityFlag_NoCapture) { + for (lbNoCaptureData const &nc : p->no_captures) { + if (are_types_identical(nc.slice_type, slice_type)) { + base_array = nc.base_array; + break; + } + } + DeclInfo *d = decl_info_of_entity(p->entity); + if (d != nullptr && base_array.addr.value == nullptr) { + for (NoCaptureData const &nc : d->no_captures) { + if (are_types_identical(nc.slice_type, slice_type)) { + base_array = lb_add_local_generated(p, alloc_type_array(elem_type, nc.max_count), true); + array_add(&p->no_captures, lbNoCaptureData{slice_type, base_array}); + break; + } + } + } + } + + if (base_array.addr.value == nullptr) { + base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true); + } + GB_ASSERT(base_array.addr.value != nullptr); + lbAddr slice = lb_add_local_generated(p, slice_type, true); - lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true); for (isize i = 0; i < var_args.count; i++) { lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i); diff --git a/src/parser.cpp b/src/parser.cpp index 9ce3d563d..a6a146cfd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -4014,6 +4014,7 @@ struct ParseFieldPrefixMapping { gb_global ParseFieldPrefixMapping const parse_field_prefix_mappings[] = { {str_lit("using"), Token_using, FieldFlag_using}, {str_lit("no_alias"), Token_Hash, FieldFlag_no_alias}, + {str_lit("no_capture"), Token_Hash, FieldFlag_no_capture}, {str_lit("c_vararg"), Token_Hash, FieldFlag_c_vararg}, {str_lit("const"), Token_Hash, FieldFlag_const}, {str_lit("any_int"), Token_Hash, FieldFlag_any_int}, diff --git a/src/parser.hpp b/src/parser.hpp index 86b3393af..15176f789 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -330,9 +330,10 @@ enum FieldFlag : u32 { FieldFlag_subtype = 1<<7, FieldFlag_by_ptr = 1<<8, FieldFlag_no_broadcast = 1<<9, // disallow array programming + FieldFlag_no_capture = 1<<10, // Internal use by the parser only - FieldFlag_Tags = 1<<10, + FieldFlag_Tags = 1<<11, FieldFlag_Results = 1<<16, @@ -340,7 +341,10 @@ enum FieldFlag : u32 { FieldFlag_Invalid = 1u<<31, // Parameter List Restrictions - FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast, + FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg| + FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast| + FieldFlag_no_capture, + FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags, }; From 7e4e3429d7fa1dd3bfa4b8336f8b844a9a9f8ca7 Mon Sep 17 00:00:00 2001 From: Ronald Date: Sun, 14 Jul 2024 11:48:34 +0100 Subject: [PATCH 12/34] Fix logic bug in core/encoding/ini/ini.odin The load_map_from_path had incorrect logic where it would return false for ok when err was equal to nil and true when there was an error. --- core/encoding/ini/ini.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/encoding/ini/ini.odin b/core/encoding/ini/ini.odin index eb0ad9e7c..91a1adcf7 100644 --- a/core/encoding/ini/ini.odin +++ b/core/encoding/ini/ini.odin @@ -121,7 +121,7 @@ load_map_from_path :: proc(path: string, allocator: runtime.Allocator, options : data := os.read_entire_file(path, allocator) or_return defer delete(data, allocator) m, err = load_map_from_string(string(data), allocator, options) - ok = err != nil + ok = err == nil defer if !ok { delete_map(m) } From c7bd9547529a4957e56c7302c5eaca650258ecdc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 11:56:04 +0100 Subject: [PATCH 13/34] Add more uses of `#no_capture` --- base/runtime/core_builtin.odin | 14 ++++---- base/runtime/core_builtin_soa.odin | 6 ++-- base/runtime/print.odin | 4 +-- core/container/small_array/small_array.odin | 2 +- core/encoding/xml/tokenizer.odin | 6 ++-- core/log/log.odin | 32 +++++++++---------- core/math/big/helpers.odin | 10 +++--- core/math/big/internal.odin | 8 ++--- core/odin/parser/parser.odin | 12 +++---- core/odin/tokenizer/tokenizer.odin | 6 ++-- core/testing/testing.odin | 10 +++--- core/text/scanner/scanner.odin | 2 +- core/text/table/table.odin | 2 +- core/unicode/tools/generate_entity_table.odin | 2 +- tests/core/encoding/xml/test_core_xml.odin | 2 +- tests/core/fmt/test_core_fmt.odin | 2 +- tests/documentation/documentation_tester.odin | 2 +- vendor/OpenGL/wrappers.odin | 2 +- vendor/raylib/raylib.odin | 4 +-- 19 files changed, 64 insertions(+), 64 deletions(-) diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index ff87316f2..5f6e1ec16 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -484,7 +484,7 @@ non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc : return _append_elem(array, arg, false, loc=loc) } -_append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, loc := #caller_location, args: ..E) -> (n: int, err: Allocator_Error) #optional_allocator_error { +_append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, loc := #caller_location, args: []E) -> (n: int, err: Allocator_Error) #optional_allocator_error { if array == nil { return 0, nil } @@ -524,13 +524,13 @@ _append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, l } @builtin -append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { - return _append_elems(array, true, loc, ..args) +append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast #no_capture args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { + return _append_elems(array, true, loc, args) } @builtin -non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { - return _append_elems(array, false, loc, ..args) +non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast #no_capture args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { + return _append_elems(array, false, loc, args) } // The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type @@ -617,7 +617,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast arg: E, } @builtin -inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast #no_capture args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { if array == nil { return } @@ -679,7 +679,7 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle @builtin -assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast #no_capture args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { new_size := index + len(args) if len(args) == 0 { ok = true diff --git a/base/runtime/core_builtin_soa.odin b/base/runtime/core_builtin_soa.odin index f1b17cbef..108183ea6 100644 --- a/base/runtime/core_builtin_soa.odin +++ b/base/runtime/core_builtin_soa.odin @@ -342,17 +342,17 @@ _append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broa } @builtin -append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast #no_capture args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { return _append_soa_elems(array, true, args=args, loc=loc) } @builtin -non_zero_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +non_zero_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast #no_capture args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { return _append_soa_elems(array, false, args=args, loc=loc) } -_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast args: []E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { if array == nil { return } diff --git a/base/runtime/print.odin b/base/runtime/print.odin index 0262e8ef6..ecd301d21 100644 --- a/base/runtime/print.odin +++ b/base/runtime/print.odin @@ -72,7 +72,7 @@ when !ODIN_NO_RTTI { print_string("") } } - println_any :: #force_no_inline proc "contextless" (args: ..any) { + println_any :: #force_no_inline proc "contextless" (#no_capture args: ..any) { context = default_context() loop: for arg, i in args { assert(arg.id != nil) @@ -127,7 +127,7 @@ print_string :: #force_no_inline proc "contextless" (str: string) -> (n: int) { return } -print_strings :: #force_no_inline proc "contextless" (args: ..string) -> (n: int) { +print_strings :: #force_no_inline proc "contextless" (#no_capture args: ..string) -> (n: int) { for str in args { m, err := stderr_write(transmute([]byte)str) n += m diff --git a/core/container/small_array/small_array.odin b/core/container/small_array/small_array.odin index b2068469d..a698d965c 100644 --- a/core/container/small_array/small_array.odin +++ b/core/container/small_array/small_array.odin @@ -139,7 +139,7 @@ clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) { resize(a, 0) } -push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) { +push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), #no_capture items: ..T) { n := copy(a.data[a.len:], items[:]) a.len += n } diff --git a/core/encoding/xml/tokenizer.odin b/core/encoding/xml/tokenizer.odin index a2bbaf28e..8a26f1bce 100644 --- a/core/encoding/xml/tokenizer.odin +++ b/core/encoding/xml/tokenizer.odin @@ -17,7 +17,7 @@ import "core:fmt" import "core:unicode" import "core:unicode/utf8" -Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any) +Error_Handler :: #type proc(pos: Pos, fmt: string, #no_capture args: ..any) Token :: struct { kind: Token_Kind, @@ -112,13 +112,13 @@ offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> Pos { } } -default_error_handler :: proc(pos: Pos, msg: string, args: ..any) { +default_error_handler :: proc(pos: Pos, msg: string, #no_capture args: ..any) { fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column) fmt.eprintf(msg, ..args) fmt.eprintf("\n") } -error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) { +error :: proc(t: ^Tokenizer, offset: int, msg: string, #no_capture args: ..any) { pos := offset_to_pos(t, offset) if t.err != nil { t.err(pos, msg, ..args) diff --git a/core/log/log.odin b/core/log/log.odin index 0d89fdb74..35dff086f 100644 --- a/core/log/log.odin +++ b/core/log/log.odin @@ -75,43 +75,43 @@ nil_logger :: proc() -> Logger { return Logger{nil_logger_proc, nil, Level.Debug, nil} } -debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) { +debugf :: proc(fmt_str: string, #no_capture args: ..any, location := #caller_location) { logf(.Debug, fmt_str, ..args, location=location) } -infof :: proc(fmt_str: string, args: ..any, location := #caller_location) { +infof :: proc(fmt_str: string, #no_capture args: ..any, location := #caller_location) { logf(.Info, fmt_str, ..args, location=location) } -warnf :: proc(fmt_str: string, args: ..any, location := #caller_location) { +warnf :: proc(fmt_str: string, #no_capture args: ..any, location := #caller_location) { logf(.Warning, fmt_str, ..args, location=location) } -errorf :: proc(fmt_str: string, args: ..any, location := #caller_location) { +errorf :: proc(fmt_str: string, #no_capture args: ..any, location := #caller_location) { logf(.Error, fmt_str, ..args, location=location) } -fatalf :: proc(fmt_str: string, args: ..any, location := #caller_location) { +fatalf :: proc(fmt_str: string, #no_capture args: ..any, location := #caller_location) { logf(.Fatal, fmt_str, ..args, location=location) } -debug :: proc(args: ..any, sep := " ", location := #caller_location) { +debug :: proc(#no_capture args: ..any, sep := " ", location := #caller_location) { log(.Debug, ..args, sep=sep, location=location) } -info :: proc(args: ..any, sep := " ", location := #caller_location) { +info :: proc(#no_capture args: ..any, sep := " ", location := #caller_location) { log(.Info, ..args, sep=sep, location=location) } -warn :: proc(args: ..any, sep := " ", location := #caller_location) { +warn :: proc(#no_capture args: ..any, sep := " ", location := #caller_location) { log(.Warning, ..args, sep=sep, location=location) } -error :: proc(args: ..any, sep := " ", location := #caller_location) { +error :: proc(#no_capture args: ..any, sep := " ", location := #caller_location) { log(.Error, ..args, sep=sep, location=location) } -fatal :: proc(args: ..any, sep := " ", location := #caller_location) { +fatal :: proc(#no_capture args: ..any, sep := " ", location := #caller_location) { log(.Fatal, ..args, sep=sep, location=location) } -panic :: proc(args: ..any, location := #caller_location) -> ! { +panic :: proc(#no_capture args: ..any, location := #caller_location) -> ! { log(.Fatal, ..args, location=location) runtime.panic("log.panic", location) } -panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> ! { +panicf :: proc(fmt_str: string, #no_capture args: ..any, location := #caller_location) -> ! { logf(.Fatal, fmt_str, ..args, location=location) runtime.panic("log.panicf", location) } @@ -133,14 +133,14 @@ assert :: proc(condition: bool, message := "", loc := #caller_location) { } @(disabled=ODIN_DISABLE_ASSERT) -assertf :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_location) { +assertf :: proc(condition: bool, fmt_str: string, #no_capture args: ..any, loc := #caller_location) { if !condition { // NOTE(dragos): We are using the same trick as in builtin.assert // to improve performance to make the CPU not // execute speculatively, making it about an order of // magnitude faster @(cold) - internal :: proc(loc: runtime.Source_Code_Location, fmt_str: string, args: ..any) { + internal :: proc(loc: runtime.Source_Code_Location, fmt_str: string, #no_capture args: ..any) { p := context.assertion_failure_proc if p == nil { p = runtime.default_assertion_failure_proc @@ -155,7 +155,7 @@ assertf :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_lo -log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location) { +log :: proc(level: Level, #no_capture args: ..any, sep := " ", location := #caller_location) { logger := context.logger if logger.procedure == nil { return @@ -167,7 +167,7 @@ log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location) logger.procedure(logger.data, level, str, logger.options, location) } -logf :: proc(level: Level, fmt_str: string, args: ..any, location := #caller_location) { +logf :: proc(level: Level, fmt_str: string, #no_capture args: ..any, location := #caller_location) { logger := context.logger if logger.procedure == nil { return diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index ee09bb2c7..c82b5eead 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -404,7 +404,7 @@ clear_if_uninitialized_single :: proc(arg: ^Int, allocator := context.allocator) return #force_inline internal_clear_if_uninitialized_single(arg, allocator) } -clear_if_uninitialized_multi :: proc(args: ..^Int, allocator := context.allocator) -> (err: Error) { +clear_if_uninitialized_multi :: proc(#no_capture args: ..^Int, allocator := context.allocator) -> (err: Error) { args := args assert_if_nil(..args) @@ -420,7 +420,7 @@ error_if_immutable_single :: proc(arg: ^Int) -> (err: Error) { return nil } -error_if_immutable_multi :: proc(args: ..^Int) -> (err: Error) { +error_if_immutable_multi :: proc(#no_capture args: ..^Int) -> (err: Error) { for i in args { if i != nil && .Immutable in i.flags { return .Assignment_To_Immutable } } @@ -431,7 +431,7 @@ error_if_immutable :: proc {error_if_immutable_single, error_if_immutable_multi, /* Allocates several `Int`s at once. */ -int_init_multi :: proc(integers: ..^Int, allocator := context.allocator) -> (err: Error) { +int_init_multi :: proc(#no_capture integers: ..^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(..integers) integers := integers @@ -812,13 +812,13 @@ assert_if_nil :: proc{ assert_if_nil_rat, } -assert_if_nil_int :: #force_inline proc(integers: ..^Int, loc := #caller_location) { +assert_if_nil_int :: #force_inline proc(#no_capture integers: ..^Int, loc := #caller_location) { for i in integers { assert(i != nil, "(nil)", loc) } } -assert_if_nil_rat :: #force_inline proc(rationals: ..^Rat, loc := #caller_location) { +assert_if_nil_rat :: #force_inline proc(#no_capture rationals: ..^Rat, loc := #caller_location) { for r in rationals { assert(r != nil, "(nil)", loc) } diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index c9b331e55..9eaa0c8e1 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -1844,7 +1844,7 @@ internal_root_n :: proc { internal_int_root_n, } Deallocates the backing memory of one or more `Int`s. Asssumes none of the `integers` to be a `nil`. */ -internal_int_destroy :: proc(integers: ..^Int) { +internal_int_destroy :: proc(#no_capture integers: ..^Int) { integers := integers for &a in integers { @@ -2872,7 +2872,7 @@ internal_clear_if_uninitialized_single :: proc(arg: ^Int, allocator := context.a return err } -internal_clear_if_uninitialized_multi :: proc(args: ..^Int, allocator := context.allocator) -> (err: Error) { +internal_clear_if_uninitialized_multi :: proc(#no_capture args: ..^Int, allocator := context.allocator) -> (err: Error) { context.allocator = allocator for i in args { @@ -2890,7 +2890,7 @@ internal_error_if_immutable_single :: proc(arg: ^Int) -> (err: Error) { return nil } -internal_error_if_immutable_multi :: proc(args: ..^Int) -> (err: Error) { +internal_error_if_immutable_multi :: proc(#no_capture args: ..^Int) -> (err: Error) { for i in args { if i != nil && .Immutable in i.flags { return .Assignment_To_Immutable } } @@ -2901,7 +2901,7 @@ internal_error_if_immutable :: proc {internal_error_if_immutable_single, interna /* Allocates several `Int`s at once. */ -internal_int_init_multi :: proc(integers: ..^Int, allocator := context.allocator) -> (err: Error) { +internal_int_init_multi :: proc(#no_capture integers: ..^Int, allocator := context.allocator) -> (err: Error) { context.allocator = allocator integers := integers diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index dec892f84..3ae3b5dba 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -5,8 +5,8 @@ import "core:odin/tokenizer" import "core:fmt" -Warning_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any) -Error_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any) +Warning_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, #no_capture args: ..any) +Error_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, #no_capture args: ..any) Flag :: enum u32 { Optional_Semicolons, @@ -67,25 +67,25 @@ Import_Decl_Kind :: enum { -default_warning_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) { +default_warning_handler :: proc(pos: tokenizer.Pos, msg: string, #no_capture args: ..any) { fmt.eprintf("%s(%d:%d): Warning: ", pos.file, pos.line, pos.column) fmt.eprintf(msg, ..args) fmt.eprintf("\n") } -default_error_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) { +default_error_handler :: proc(pos: tokenizer.Pos, msg: string, #no_capture args: ..any) { fmt.eprintf("%s(%d:%d): ", pos.file, pos.line, pos.column) fmt.eprintf(msg, ..args) fmt.eprintf("\n") } -warn :: proc(p: ^Parser, pos: tokenizer.Pos, msg: string, args: ..any) { +warn :: proc(p: ^Parser, pos: tokenizer.Pos, msg: string, #no_capture args: ..any) { if p.warn != nil { p.warn(pos, msg, ..args) } p.file.syntax_warning_count += 1 } -error :: proc(p: ^Parser, pos: tokenizer.Pos, msg: string, args: ..any) { +error :: proc(p: ^Parser, pos: tokenizer.Pos, msg: string, #no_capture args: ..any) { if p.err != nil { p.err(pos, msg, ..args) } diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index 62170aa10..c5992e5f4 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -4,7 +4,7 @@ import "core:fmt" import "core:unicode" import "core:unicode/utf8" -Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any) +Error_Handler :: #type proc(pos: Pos, fmt: string, #no_capture args: ..any) Flag :: enum { Insert_Semicolon, @@ -62,13 +62,13 @@ offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> Pos { } } -default_error_handler :: proc(pos: Pos, msg: string, args: ..any) { +default_error_handler :: proc(pos: Pos, msg: string, #no_capture args: ..any) { fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column) fmt.eprintf(msg, ..args) fmt.eprintf("\n") } -error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) { +error :: proc(t: ^Tokenizer, offset: int, msg: string, #no_capture args: ..any) { pos := offset_to_pos(t, offset) if t.err != nil { t.err(pos, msg, ..args) diff --git a/core/testing/testing.odin b/core/testing/testing.odin index 29fe853ef..04dc79095 100644 --- a/core/testing/testing.odin +++ b/core/testing/testing.odin @@ -53,12 +53,12 @@ T :: struct { @(deprecated="prefer `log.error`") -error :: proc(t: ^T, args: ..any, loc := #caller_location) { +error :: proc(t: ^T, #no_capture args: ..any, loc := #caller_location) { pkg_log.error(..args, location = loc) } @(deprecated="prefer `log.errorf`") -errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) { +errorf :: proc(t: ^T, format: string, #no_capture args: ..any, loc := #caller_location) { pkg_log.errorf(format, ..args, location = loc) } @@ -87,12 +87,12 @@ failed :: proc(t: ^T) -> bool { } @(deprecated="prefer `log.info`") -log :: proc(t: ^T, args: ..any, loc := #caller_location) { +log :: proc(t: ^T, #no_capture args: ..any, loc := #caller_location) { pkg_log.info(..args, location = loc) } @(deprecated="prefer `log.infof`") -logf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) { +logf :: proc(t: ^T, format: string, #no_capture args: ..any, loc := #caller_location) { pkg_log.infof(format, ..args, location = loc) } @@ -121,7 +121,7 @@ expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bo return ok } -expectf :: proc(t: ^T, ok: bool, format: string, args: ..any, loc := #caller_location) -> bool { +expectf :: proc(t: ^T, ok: bool, format: string, #no_capture args: ..any, loc := #caller_location) -> bool { if !ok { pkg_log.errorf(format, ..args, location=loc) } diff --git a/core/text/scanner/scanner.odin b/core/text/scanner/scanner.odin index d27c66f24..6eb366b49 100644 --- a/core/text/scanner/scanner.odin +++ b/core/text/scanner/scanner.odin @@ -250,7 +250,7 @@ error :: proc(s: ^Scanner, msg: string) { } } -errorf :: proc(s: ^Scanner, format: string, args: ..any) { +errorf :: proc(s: ^Scanner, format: string, #no_capture args: ..any) { error(s, fmt.tprintf(format, ..args)) } diff --git a/core/text/table/table.odin b/core/text/table/table.odin index 27c99b1f1..4d0baef64 100644 --- a/core/text/table/table.odin +++ b/core/text/table/table.odin @@ -145,7 +145,7 @@ set_cell_value_and_alignment :: proc(tbl: ^Table, row, col: int, value: any, ali cell.alignment = alignment } -format :: proc(tbl: ^Table, _fmt: string, args: ..any) -> string { +format :: proc(tbl: ^Table, _fmt: string, #no_capture args: ..any) -> string { context.allocator = tbl.format_allocator return fmt.aprintf(_fmt, ..args) } diff --git a/core/unicode/tools/generate_entity_table.odin b/core/unicode/tools/generate_entity_table.odin index 16baa1adf..238a3e219 100644 --- a/core/unicode/tools/generate_entity_table.odin +++ b/core/unicode/tools/generate_entity_table.odin @@ -12,7 +12,7 @@ import "core:fmt" /* Silent error handler for the parser. */ -Error_Handler :: proc(pos: xml.Pos, fmt: string, args: ..any) {} +Error_Handler :: proc(pos: xml.Pos, fmt: string, #no_capture args: ..any) {} OPTIONS :: xml.Options{ flags = { .Ignore_Unsupported, }, expected_doctype = "unicode", } diff --git a/tests/core/encoding/xml/test_core_xml.odin b/tests/core/encoding/xml/test_core_xml.odin index b29431e10..811ee27dc 100644 --- a/tests/core/encoding/xml/test_core_xml.odin +++ b/tests/core/encoding/xml/test_core_xml.odin @@ -8,7 +8,7 @@ import "core:fmt" import "core:log" import "core:hash" -Silent :: proc(pos: xml.Pos, format: string, args: ..any) {} +Silent :: proc(pos: xml.Pos, format: string, #no_capture args: ..any) {} OPTIONS :: xml.Options{ flags = { .Ignore_Unsupported, .Intern_Comments, }, expected_doctype = "", diff --git a/tests/core/fmt/test_core_fmt.odin b/tests/core/fmt/test_core_fmt.odin index 49142e24d..0aaef93a4 100644 --- a/tests/core/fmt/test_core_fmt.odin +++ b/tests/core/fmt/test_core_fmt.odin @@ -373,7 +373,7 @@ test_odin_value_export :: proc(t: ^testing.T) { } @(private) -check :: proc(t: ^testing.T, exp: string, format: string, args: ..any, loc := #caller_location) { +check :: proc(t: ^testing.T, exp: string, format: string, #no_capture args: ..any, loc := #caller_location) { got := fmt.tprintf(format, ..args) testing.expectf(t, got == exp, "(%q, %v): %q != %q", format, args, got, exp, loc = loc) } diff --git a/tests/documentation/documentation_tester.odin b/tests/documentation/documentation_tester.odin index 8a798d6c5..7fc37b9f0 100644 --- a/tests/documentation/documentation_tester.odin +++ b/tests/documentation/documentation_tester.odin @@ -51,7 +51,7 @@ common_prefix :: proc(strs: []string) -> string { return prefix } -errorf :: proc(format: string, args: ..any) -> ! { +errorf :: proc(format: string, #no_capture args: ..any) -> ! { fmt.eprintf("%s ", os.args[0]) fmt.eprintf(format, ..args) fmt.eprintln() diff --git a/vendor/OpenGL/wrappers.odin b/vendor/OpenGL/wrappers.odin index 1eb8fc72f..971452be5 100644 --- a/vendor/OpenGL/wrappers.odin +++ b/vendor/OpenGL/wrappers.odin @@ -754,7 +754,7 @@ when !GL_DEBUG { MultiDrawElementsIndirectCount :: proc "c" (mode: i32, type: i32, indirect: [^]DrawElementsIndirectCommand, drawcount: i32, maxdrawcount, stride: i32) { impl_MultiDrawElementsIndirectCount(mode, type, indirect, drawcount, maxdrawcount, stride) } PolygonOffsetClamp :: proc "c" (factor, units, clamp: f32) { impl_PolygonOffsetClamp(factor, units, clamp) } } else { - debug_helper :: proc"c"(from_loc: runtime.Source_Code_Location, num_ret: int, args: ..any, loc := #caller_location) { + debug_helper :: proc"c"(from_loc: runtime.Source_Code_Location, num_ret: int, #no_capture args: ..any, loc := #caller_location) { context = runtime.default_context() Error_Enum :: enum { diff --git a/vendor/raylib/raylib.odin b/vendor/raylib/raylib.odin index 3d1b74058..c2995ba36 100644 --- a/vendor/raylib/raylib.odin +++ b/vendor/raylib/raylib.odin @@ -1667,7 +1667,7 @@ IsGestureDetected :: proc "c" (gesture: Gesture) -> bool { // Text formatting with variables (sprintf style) -TextFormat :: proc(text: cstring, args: ..any) -> cstring { +TextFormat :: proc(text: cstring, #no_capture args: ..any) -> cstring { @static buffers: [MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH]byte @static index: u32 @@ -1683,7 +1683,7 @@ TextFormat :: proc(text: cstring, args: ..any) -> cstring { } // Text formatting with variables (sprintf style) and allocates (must be freed with 'MemFree') -TextFormatAlloc :: proc(text: cstring, args: ..any) -> cstring { +TextFormatAlloc :: proc(text: cstring, #no_capture args: ..any) -> cstring { str := fmt.tprintf(string(text), ..args) return strings.clone_to_cstring(str, MemAllocator()) } From 3ba19d94cfe9fbef9ac9c26e0bb49c54d568919c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 11:58:26 +0100 Subject: [PATCH 14/34] Add `#no_capture` to `core:odin/ast` --- core/odin/ast/ast.odin | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 92d00b47c..0ae822e21 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -599,6 +599,7 @@ Field_Flag :: enum { Subtype, By_Ptr, No_Broadcast, + No_Capture, Results, Tags, @@ -619,6 +620,7 @@ field_flag_strings := [Field_Flag]string{ .Subtype = "#subtype", .By_Ptr = "#by_ptr", .No_Broadcast = "#no_broadcast", + .No_Capture = "#no_capture", .Results = "results", .Tags = "field tag", @@ -634,6 +636,7 @@ field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{ {"subtype", .Subtype}, {"by_ptr", .By_Ptr}, {"no_broadcast", .No_Broadcast}, + {"no_capture", .No_Capture}, } From 891cf54b5c56bd31bcfdac14f0b72d489999bffc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 12:03:34 +0100 Subject: [PATCH 15/34] Add `f16` to `#c_vararg` promotion rules --- src/types.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/types.cpp b/src/types.cpp index c3a5fb539..92b187cdb 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2923,11 +2923,14 @@ gb_internal Type *c_vararg_promote_type(Type *type) { if (core->kind == Type_Basic) { switch (core->Basic.kind) { + case Basic_f16: case Basic_f32: case Basic_UntypedFloat: return t_f64; + case Basic_f16le: case Basic_f32le: return t_f64le; + case Basic_f16be: case Basic_f32be: return t_f64be; From 8642d719f0ece3625d535d47b41ff4d35072f47f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 12:19:47 +0100 Subject: [PATCH 16/34] Imply `#no_capture` to all variadic parameters --- base/runtime/core_builtin.odin | 8 +-- base/runtime/core_builtin_soa.odin | 4 +- base/runtime/print.odin | 4 +- core/container/small_array/small_array.odin | 2 +- core/encoding/xml/tokenizer.odin | 6 +- core/fmt/fmt.odin | 58 +++++++++---------- core/fmt/fmt_js.odin | 24 ++++---- core/fmt/fmt_os.odin | 24 ++++---- core/log/log.odin | 32 +++++----- core/math/big/helpers.odin | 10 ++-- core/math/big/internal.odin | 8 +-- core/odin/parser/parser.odin | 12 ++-- core/odin/tokenizer/tokenizer.odin | 6 +- core/testing/testing.odin | 10 ++-- core/text/scanner/scanner.odin | 2 +- core/text/table/table.odin | 2 +- core/unicode/tools/generate_entity_table.odin | 2 +- src/check_type.cpp | 10 +++- src/llvm_backend.hpp | 1 - src/parser.hpp | 5 +- tests/core/encoding/xml/test_core_xml.odin | 2 +- tests/core/fmt/test_core_fmt.odin | 2 +- tests/documentation/documentation_tester.odin | 2 +- vendor/OpenGL/wrappers.odin | 2 +- vendor/raylib/raylib.odin | 4 +- 25 files changed, 125 insertions(+), 117 deletions(-) diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index 5f6e1ec16..d68eefe23 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -524,12 +524,12 @@ _append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, l } @builtin -append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast #no_capture args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { return _append_elems(array, true, loc, args) } @builtin -non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast #no_capture args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { return _append_elems(array, false, loc, args) } @@ -617,7 +617,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast arg: E, } @builtin -inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast #no_capture args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { if array == nil { return } @@ -679,7 +679,7 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle @builtin -assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast #no_capture args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { new_size := index + len(args) if len(args) == 0 { ok = true diff --git a/base/runtime/core_builtin_soa.odin b/base/runtime/core_builtin_soa.odin index 108183ea6..7f7f5f086 100644 --- a/base/runtime/core_builtin_soa.odin +++ b/base/runtime/core_builtin_soa.odin @@ -342,12 +342,12 @@ _append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broa } @builtin -append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast #no_capture args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { return _append_soa_elems(array, true, args=args, loc=loc) } @builtin -non_zero_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast #no_capture args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +non_zero_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { return _append_soa_elems(array, false, args=args, loc=loc) } diff --git a/base/runtime/print.odin b/base/runtime/print.odin index ecd301d21..0262e8ef6 100644 --- a/base/runtime/print.odin +++ b/base/runtime/print.odin @@ -72,7 +72,7 @@ when !ODIN_NO_RTTI { print_string("") } } - println_any :: #force_no_inline proc "contextless" (#no_capture args: ..any) { + println_any :: #force_no_inline proc "contextless" (args: ..any) { context = default_context() loop: for arg, i in args { assert(arg.id != nil) @@ -127,7 +127,7 @@ print_string :: #force_no_inline proc "contextless" (str: string) -> (n: int) { return } -print_strings :: #force_no_inline proc "contextless" (#no_capture args: ..string) -> (n: int) { +print_strings :: #force_no_inline proc "contextless" (args: ..string) -> (n: int) { for str in args { m, err := stderr_write(transmute([]byte)str) n += m diff --git a/core/container/small_array/small_array.odin b/core/container/small_array/small_array.odin index a698d965c..b2068469d 100644 --- a/core/container/small_array/small_array.odin +++ b/core/container/small_array/small_array.odin @@ -139,7 +139,7 @@ clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) { resize(a, 0) } -push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), #no_capture items: ..T) { +push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) { n := copy(a.data[a.len:], items[:]) a.len += n } diff --git a/core/encoding/xml/tokenizer.odin b/core/encoding/xml/tokenizer.odin index 8a26f1bce..a2bbaf28e 100644 --- a/core/encoding/xml/tokenizer.odin +++ b/core/encoding/xml/tokenizer.odin @@ -17,7 +17,7 @@ import "core:fmt" import "core:unicode" import "core:unicode/utf8" -Error_Handler :: #type proc(pos: Pos, fmt: string, #no_capture args: ..any) +Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any) Token :: struct { kind: Token_Kind, @@ -112,13 +112,13 @@ offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> Pos { } } -default_error_handler :: proc(pos: Pos, msg: string, #no_capture args: ..any) { +default_error_handler :: proc(pos: Pos, msg: string, args: ..any) { fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column) fmt.eprintf(msg, ..args) fmt.eprintf("\n") } -error :: proc(t: ^Tokenizer, offset: int, msg: string, #no_capture args: ..any) { +error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) { pos := offset_to_pos(t, offset) if t.err != nil { t.err(pos, msg, ..args) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index e56211346..234f4afbd 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -125,7 +125,7 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist // Returns: A formatted string. // @(require_results) -aprint :: proc(#no_capture args: ..any, sep := " ", allocator := context.allocator) -> string { +aprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> string { str: strings.Builder strings.builder_init(&str, allocator) return sbprint(&str, ..args, sep=sep) @@ -141,7 +141,7 @@ aprint :: proc(#no_capture args: ..any, sep := " ", allocator := context.allocat // Returns: A formatted string with a newline character at the end. // @(require_results) -aprintln :: proc(#no_capture args: ..any, sep := " ", allocator := context.allocator) -> string { +aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> string { str: strings.Builder strings.builder_init(&str, allocator) return sbprintln(&str, ..args, sep=sep) @@ -158,7 +158,7 @@ aprintln :: proc(#no_capture args: ..any, sep := " ", allocator := context.alloc // Returns: A formatted string. The returned string must be freed accordingly. // @(require_results) -aprintf :: proc(fmt: string, #no_capture args: ..any, allocator := context.allocator, newline := false) -> string { +aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newline := false) -> string { str: strings.Builder strings.builder_init(&str, allocator) return sbprintf(&str, fmt, ..args, newline=newline) @@ -174,7 +174,7 @@ aprintf :: proc(fmt: string, #no_capture args: ..any, allocator := context.alloc // Returns: A formatted string. The returned string must be freed accordingly. // @(require_results) -aprintfln :: proc(fmt: string, #no_capture args: ..any, allocator := context.allocator) -> string { +aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string { return aprintf(fmt, ..args, allocator=allocator, newline=true) } // Creates a formatted string @@ -188,7 +188,7 @@ aprintfln :: proc(fmt: string, #no_capture args: ..any, allocator := context.all // Returns: A formatted string. // @(require_results) -tprint :: proc(#no_capture args: ..any, sep := " ") -> string { +tprint :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) return sbprint(&str, ..args, sep=sep) @@ -204,7 +204,7 @@ tprint :: proc(#no_capture args: ..any, sep := " ") -> string { // Returns: A formatted string with a newline character at the end. // @(require_results) -tprintln :: proc(#no_capture args: ..any, sep := " ") -> string { +tprintln :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) return sbprintln(&str, ..args, sep=sep) @@ -221,7 +221,7 @@ tprintln :: proc(#no_capture args: ..any, sep := " ") -> string { // Returns: A formatted string. // @(require_results) -tprintf :: proc(fmt: string, #no_capture args: ..any, newline := false) -> string { +tprintf :: proc(fmt: string, args: ..any, newline := false) -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) return sbprintf(&str, fmt, ..args, newline=newline) @@ -237,7 +237,7 @@ tprintf :: proc(fmt: string, #no_capture args: ..any, newline := false) -> strin // Returns: A formatted string. // @(require_results) -tprintfln :: proc(fmt: string, #no_capture args: ..any) -> string { +tprintfln :: proc(fmt: string, args: ..any) -> string { return tprintf(fmt, ..args, newline=true) } // Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer. @@ -249,7 +249,7 @@ tprintfln :: proc(fmt: string, #no_capture args: ..any) -> string { // // Returns: A formatted string // -bprint :: proc(buf: []byte, #no_capture args: ..any, sep := " ") -> string { +bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf) return sbprint(&sb, ..args, sep=sep) } @@ -262,7 +262,7 @@ bprint :: proc(buf: []byte, #no_capture args: ..any, sep := " ") -> string { // // Returns: A formatted string with a newline character at the end // -bprintln :: proc(buf: []byte, #no_capture args: ..any, sep := " ") -> string { +bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf) return sbprintln(&sb, ..args, sep=sep) } @@ -276,7 +276,7 @@ bprintln :: proc(buf: []byte, #no_capture args: ..any, sep := " ") -> string { // // Returns: A formatted string // -bprintf :: proc(buf: []byte, fmt: string, #no_capture args: ..any, newline := false) -> string { +bprintf :: proc(buf: []byte, fmt: string, args: ..any, newline := false) -> string { sb := strings.builder_from_bytes(buf) return sbprintf(&sb, fmt, ..args, newline=newline) } @@ -289,7 +289,7 @@ bprintf :: proc(buf: []byte, fmt: string, #no_capture args: ..any, newline := fa // // Returns: A formatted string // -bprintfln :: proc(buf: []byte, fmt: string, #no_capture args: ..any) -> string { +bprintfln :: proc(buf: []byte, fmt: string, args: ..any) -> string { return bprintf(buf, fmt, ..args, newline=true) } // Runtime assertion with a formatted message @@ -301,14 +301,14 @@ bprintfln :: proc(buf: []byte, fmt: string, #no_capture args: ..any) -> string { // - loc: The location of the caller // @(disabled=ODIN_DISABLE_ASSERT) -assertf :: proc(condition: bool, fmt: string, #no_capture args: ..any, loc := #caller_location) { +assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) { if !condition { // NOTE(dragos): We are using the same trick as in builtin.assert // to improve performance to make the CPU not // execute speculatively, making it about an order of // magnitude faster @(cold) - internal :: proc(loc: runtime.Source_Code_Location, fmt: string, #no_capture args: ..any) { + internal :: proc(loc: runtime.Source_Code_Location, fmt: string, args: ..any) { p := context.assertion_failure_proc if p == nil { p = runtime.default_assertion_failure_proc @@ -326,7 +326,7 @@ assertf :: proc(condition: bool, fmt: string, #no_capture args: ..any, loc := #c // - args: A variadic list of arguments to be formatted // - loc: The location of the caller // -panicf :: proc(fmt: string, #no_capture args: ..any, loc := #caller_location) -> ! { +panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { p := context.assertion_failure_proc if p == nil { p = runtime.default_assertion_failure_proc @@ -346,7 +346,7 @@ panicf :: proc(fmt: string, #no_capture args: ..any, loc := #caller_location) -> // Returns: A formatted C string // @(require_results) -caprintf :: proc(format: string, #no_capture args: ..any, newline := false) -> cstring { +caprintf :: proc(format: string, args: ..any, newline := false) -> cstring { str: strings.Builder strings.builder_init(&str) sbprintf(&str, format, ..args, newline=newline) @@ -365,7 +365,7 @@ caprintf :: proc(format: string, #no_capture args: ..any, newline := false) -> c // Returns: A formatted C string // @(require_results) -caprintfln :: proc(format: string, #no_capture args: ..any) -> cstring { +caprintfln :: proc(format: string, args: ..any) -> cstring { return caprintf(format, ..args, newline=true) } // Creates a formatted C string @@ -379,7 +379,7 @@ caprintfln :: proc(format: string, #no_capture args: ..any) -> cstring { // Returns: A formatted C string. // @(require_results) -ctprint :: proc(#no_capture args: ..any, sep := " ") -> cstring { +ctprint :: proc(args: ..any, sep := " ") -> cstring { str: strings.Builder strings.builder_init(&str, context.temp_allocator) sbprint(&str, ..args, sep=sep) @@ -399,7 +399,7 @@ ctprint :: proc(#no_capture args: ..any, sep := " ") -> cstring { // Returns: A formatted C string // @(require_results) -ctprintf :: proc(format: string, #no_capture args: ..any, newline := false) -> cstring { +ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring { str: strings.Builder strings.builder_init(&str, context.temp_allocator) sbprintf(&str, format, ..args, newline=newline) @@ -418,7 +418,7 @@ ctprintf :: proc(format: string, #no_capture args: ..any, newline := false) -> c // Returns: A formatted C string // @(require_results) -ctprintfln :: proc(format: string, #no_capture args: ..any) -> cstring { +ctprintfln :: proc(format: string, args: ..any) -> cstring { return ctprintf(format, ..args, newline=true) } // Formats using the default print settings and writes to the given strings.Builder @@ -430,7 +430,7 @@ ctprintfln :: proc(format: string, #no_capture args: ..any) -> cstring { // // Returns: A formatted string // -sbprint :: proc(buf: ^strings.Builder, #no_capture args: ..any, sep := " ") -> string { +sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { wprint(strings.to_writer(buf), ..args, sep=sep, flush=true) return strings.to_string(buf^) } @@ -443,7 +443,7 @@ sbprint :: proc(buf: ^strings.Builder, #no_capture args: ..any, sep := " ") -> s // // Returns: The resulting formatted string // -sbprintln :: proc(buf: ^strings.Builder, #no_capture args: ..any, sep := " ") -> string { +sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { wprintln(strings.to_writer(buf), ..args, sep=sep, flush=true) return strings.to_string(buf^) } @@ -457,7 +457,7 @@ sbprintln :: proc(buf: ^strings.Builder, #no_capture args: ..any, sep := " ") -> // // Returns: The resulting formatted string // -sbprintf :: proc(buf: ^strings.Builder, fmt: string, #no_capture args: ..any, newline := false) -> string { +sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any, newline := false) -> string { wprintf(strings.to_writer(buf), fmt, ..args, flush=true, newline=newline) return strings.to_string(buf^) } @@ -469,7 +469,7 @@ sbprintf :: proc(buf: ^strings.Builder, fmt: string, #no_capture args: ..any, ne // // Returns: A formatted string // -sbprintfln :: proc(buf: ^strings.Builder, format: string, #no_capture args: ..any) -> string { +sbprintfln :: proc(buf: ^strings.Builder, format: string, args: ..any) -> string { return sbprintf(buf, format, ..args, newline=true) } // Formats and writes to an io.Writer using the default print settings @@ -481,7 +481,7 @@ sbprintfln :: proc(buf: ^strings.Builder, format: string, #no_capture args: ..an // // Returns: The number of bytes written // -wprint :: proc(w: io.Writer, #no_capture args: ..any, sep := " ", flush := true) -> int { +wprint :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int { fi: Info fi.writer = w @@ -522,7 +522,7 @@ wprint :: proc(w: io.Writer, #no_capture args: ..any, sep := " ", flush := true) // // Returns: The number of bytes written // -wprintln :: proc(w: io.Writer, #no_capture args: ..any, sep := " ", flush := true) -> int { +wprintln :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int { fi: Info fi.writer = w @@ -549,11 +549,11 @@ wprintln :: proc(w: io.Writer, #no_capture args: ..any, sep := " ", flush := tru // // Returns: The number of bytes written // -wprintf :: proc(w: io.Writer, fmt: string, #no_capture args: ..any, flush := true, newline := false) -> int { +wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline := false) -> int { MAX_CHECKED_ARGS :: 64 assert(len(args) <= MAX_CHECKED_ARGS, "number of args > 64 is unsupported") - parse_options :: proc(fi: ^Info, fmt: string, index, end: int, unused_args: ^bit_set[0 ..< MAX_CHECKED_ARGS], #no_capture args: ..any) -> int { + parse_options :: proc(fi: ^Info, fmt: string, index, end: int, unused_args: ^bit_set[0 ..< MAX_CHECKED_ARGS], args: ..any) -> int { i := index // Prefix @@ -809,7 +809,7 @@ wprintf :: proc(w: io.Writer, fmt: string, #no_capture args: ..any, flush := tru // // Returns: The number of bytes written. // -wprintfln :: proc(w: io.Writer, format: string, #no_capture args: ..any, flush := true) -> int { +wprintfln :: proc(w: io.Writer, format: string, args: ..any, flush := true) -> int { return wprintf(w, format, ..args, flush=flush, newline=true) } // Writes a ^runtime.Type_Info value to an io.Writer diff --git a/core/fmt/fmt_js.odin b/core/fmt/fmt_js.odin index 4389b8d87..acf218eb5 100644 --- a/core/fmt/fmt_js.odin +++ b/core/fmt/fmt_js.odin @@ -43,7 +43,7 @@ fd_to_writer :: proc(fd: os.Handle, loc := #caller_location) -> io.Writer { } // fprint formats using the default print settings and writes to fd -fprint :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true, loc := #caller_location) -> int { +fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #caller_location) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -54,7 +54,7 @@ fprint :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true } // fprintln formats using the default print settings and writes to fd -fprintln :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true, loc := #caller_location) -> int { +fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #caller_location) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -66,7 +66,7 @@ fprintln :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := tr } // fprintf formats according to the specified format string and writes to fd -fprintf :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true, newline := false, loc := #caller_location) -> int { +fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false, loc := #caller_location) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -78,24 +78,24 @@ fprintf :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := tr } // fprintfln formats according to the specified format string and writes to fd, followed by a newline. -fprintfln :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true, loc := #caller_location) -> int { +fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, loc := #caller_location) -> int { return fprintf(fd, fmt, ..args, flush=flush, newline=true, loc=loc) } // print formats using the default print settings and writes to stdout -print :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprint(w=stdout, args=args, sep=sep, flush=flush) } +print :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(w=stdout, args=args, sep=sep, flush=flush) } // println formats using the default print settings and writes to stdout -println :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stdout, args=args, sep=sep, flush=flush) } +println :: proc(args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stdout, args=args, sep=sep, flush=flush) } // printf formats according to the specififed format string and writes to stdout -printf :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush) } +printf :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush) } // printfln formats according to the specified format string and writes to stdout, followed by a newline. -printfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) } +printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) } // eprint formats using the default print settings and writes to stderr -eprint :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprint(w=stderr, args=args, sep=sep, flush=flush) } +eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(w=stderr, args=args, sep=sep, flush=flush) } // eprintln formats using the default print settings and writes to stderr -eprintln :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stderr, args=args, sep=sep, flush=flush) } +eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stderr, args=args, sep=sep, flush=flush) } // eprintf formats according to the specififed format string and writes to stderr -eprintf :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stderr, fmt, ..args, flush=flush) } +eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stderr, fmt, ..args, flush=flush) } // eprintfln formats according to the specified format string and writes to stderr, followed by a newline. -eprintfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) } +eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) } diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin index 538f7a08b..9de0d43be 100644 --- a/core/fmt/fmt_os.odin +++ b/core/fmt/fmt_os.odin @@ -9,7 +9,7 @@ import "core:io" import "core:bufio" // fprint formats using the default print settings and writes to fd -fprint :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true) -> int { +fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -20,7 +20,7 @@ fprint :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true } // fprintln formats using the default print settings and writes to fd -fprintln :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true) -> int { +fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -31,7 +31,7 @@ fprintln :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := tr return wprintln(w, ..args, sep=sep, flush=flush) } // fprintf formats according to the specified format string and writes to fd -fprintf :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true, newline := false) -> int { +fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -42,7 +42,7 @@ fprintf :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := tr return wprintf(w, fmt, ..args, flush=flush, newline=newline) } // fprintfln formats according to the specified format string and writes to fd, followed by a newline. -fprintfln :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true) -> int { +fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int { return fprintf(fd, fmt, ..args, flush=flush, newline=true) } fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info, flush := true) -> (n: int, err: io.Error) { @@ -67,19 +67,19 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid, flush := true) -> (n: int, err: } // print formats using the default print settings and writes to os.stdout -print :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) } +print :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) } // println formats using the default print settings and writes to os.stdout -println :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) } +println :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) } // printf formats according to the specified format string and writes to os.stdout -printf :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) } +printf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) } // printfln formats according to the specified format string and writes to os.stdout, followed by a newline. -printfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush, newline=true) } +printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush, newline=true) } // eprint formats using the default print settings and writes to os.stderr -eprint :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) } +eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) } // eprintln formats using the default print settings and writes to os.stderr -eprintln :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) } +eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) } // eprintf formats according to the specified format string and writes to os.stderr -eprintf :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) } +eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) } // eprintfln formats according to the specified format string and writes to os.stderr, followed by a newline. -eprintfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush, newline=true) } +eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush, newline=true) } diff --git a/core/log/log.odin b/core/log/log.odin index 35dff086f..0d89fdb74 100644 --- a/core/log/log.odin +++ b/core/log/log.odin @@ -75,43 +75,43 @@ nil_logger :: proc() -> Logger { return Logger{nil_logger_proc, nil, Level.Debug, nil} } -debugf :: proc(fmt_str: string, #no_capture args: ..any, location := #caller_location) { +debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Debug, fmt_str, ..args, location=location) } -infof :: proc(fmt_str: string, #no_capture args: ..any, location := #caller_location) { +infof :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Info, fmt_str, ..args, location=location) } -warnf :: proc(fmt_str: string, #no_capture args: ..any, location := #caller_location) { +warnf :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Warning, fmt_str, ..args, location=location) } -errorf :: proc(fmt_str: string, #no_capture args: ..any, location := #caller_location) { +errorf :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Error, fmt_str, ..args, location=location) } -fatalf :: proc(fmt_str: string, #no_capture args: ..any, location := #caller_location) { +fatalf :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Fatal, fmt_str, ..args, location=location) } -debug :: proc(#no_capture args: ..any, sep := " ", location := #caller_location) { +debug :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Debug, ..args, sep=sep, location=location) } -info :: proc(#no_capture args: ..any, sep := " ", location := #caller_location) { +info :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Info, ..args, sep=sep, location=location) } -warn :: proc(#no_capture args: ..any, sep := " ", location := #caller_location) { +warn :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Warning, ..args, sep=sep, location=location) } -error :: proc(#no_capture args: ..any, sep := " ", location := #caller_location) { +error :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Error, ..args, sep=sep, location=location) } -fatal :: proc(#no_capture args: ..any, sep := " ", location := #caller_location) { +fatal :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Fatal, ..args, sep=sep, location=location) } -panic :: proc(#no_capture args: ..any, location := #caller_location) -> ! { +panic :: proc(args: ..any, location := #caller_location) -> ! { log(.Fatal, ..args, location=location) runtime.panic("log.panic", location) } -panicf :: proc(fmt_str: string, #no_capture args: ..any, location := #caller_location) -> ! { +panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> ! { logf(.Fatal, fmt_str, ..args, location=location) runtime.panic("log.panicf", location) } @@ -133,14 +133,14 @@ assert :: proc(condition: bool, message := "", loc := #caller_location) { } @(disabled=ODIN_DISABLE_ASSERT) -assertf :: proc(condition: bool, fmt_str: string, #no_capture args: ..any, loc := #caller_location) { +assertf :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_location) { if !condition { // NOTE(dragos): We are using the same trick as in builtin.assert // to improve performance to make the CPU not // execute speculatively, making it about an order of // magnitude faster @(cold) - internal :: proc(loc: runtime.Source_Code_Location, fmt_str: string, #no_capture args: ..any) { + internal :: proc(loc: runtime.Source_Code_Location, fmt_str: string, args: ..any) { p := context.assertion_failure_proc if p == nil { p = runtime.default_assertion_failure_proc @@ -155,7 +155,7 @@ assertf :: proc(condition: bool, fmt_str: string, #no_capture args: ..any, loc : -log :: proc(level: Level, #no_capture args: ..any, sep := " ", location := #caller_location) { +log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location) { logger := context.logger if logger.procedure == nil { return @@ -167,7 +167,7 @@ log :: proc(level: Level, #no_capture args: ..any, sep := " ", location := #call logger.procedure(logger.data, level, str, logger.options, location) } -logf :: proc(level: Level, fmt_str: string, #no_capture args: ..any, location := #caller_location) { +logf :: proc(level: Level, fmt_str: string, args: ..any, location := #caller_location) { logger := context.logger if logger.procedure == nil { return diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index c82b5eead..ee09bb2c7 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -404,7 +404,7 @@ clear_if_uninitialized_single :: proc(arg: ^Int, allocator := context.allocator) return #force_inline internal_clear_if_uninitialized_single(arg, allocator) } -clear_if_uninitialized_multi :: proc(#no_capture args: ..^Int, allocator := context.allocator) -> (err: Error) { +clear_if_uninitialized_multi :: proc(args: ..^Int, allocator := context.allocator) -> (err: Error) { args := args assert_if_nil(..args) @@ -420,7 +420,7 @@ error_if_immutable_single :: proc(arg: ^Int) -> (err: Error) { return nil } -error_if_immutable_multi :: proc(#no_capture args: ..^Int) -> (err: Error) { +error_if_immutable_multi :: proc(args: ..^Int) -> (err: Error) { for i in args { if i != nil && .Immutable in i.flags { return .Assignment_To_Immutable } } @@ -431,7 +431,7 @@ error_if_immutable :: proc {error_if_immutable_single, error_if_immutable_multi, /* Allocates several `Int`s at once. */ -int_init_multi :: proc(#no_capture integers: ..^Int, allocator := context.allocator) -> (err: Error) { +int_init_multi :: proc(integers: ..^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(..integers) integers := integers @@ -812,13 +812,13 @@ assert_if_nil :: proc{ assert_if_nil_rat, } -assert_if_nil_int :: #force_inline proc(#no_capture integers: ..^Int, loc := #caller_location) { +assert_if_nil_int :: #force_inline proc(integers: ..^Int, loc := #caller_location) { for i in integers { assert(i != nil, "(nil)", loc) } } -assert_if_nil_rat :: #force_inline proc(#no_capture rationals: ..^Rat, loc := #caller_location) { +assert_if_nil_rat :: #force_inline proc(rationals: ..^Rat, loc := #caller_location) { for r in rationals { assert(r != nil, "(nil)", loc) } diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 9eaa0c8e1..c9b331e55 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -1844,7 +1844,7 @@ internal_root_n :: proc { internal_int_root_n, } Deallocates the backing memory of one or more `Int`s. Asssumes none of the `integers` to be a `nil`. */ -internal_int_destroy :: proc(#no_capture integers: ..^Int) { +internal_int_destroy :: proc(integers: ..^Int) { integers := integers for &a in integers { @@ -2872,7 +2872,7 @@ internal_clear_if_uninitialized_single :: proc(arg: ^Int, allocator := context.a return err } -internal_clear_if_uninitialized_multi :: proc(#no_capture args: ..^Int, allocator := context.allocator) -> (err: Error) { +internal_clear_if_uninitialized_multi :: proc(args: ..^Int, allocator := context.allocator) -> (err: Error) { context.allocator = allocator for i in args { @@ -2890,7 +2890,7 @@ internal_error_if_immutable_single :: proc(arg: ^Int) -> (err: Error) { return nil } -internal_error_if_immutable_multi :: proc(#no_capture args: ..^Int) -> (err: Error) { +internal_error_if_immutable_multi :: proc(args: ..^Int) -> (err: Error) { for i in args { if i != nil && .Immutable in i.flags { return .Assignment_To_Immutable } } @@ -2901,7 +2901,7 @@ internal_error_if_immutable :: proc {internal_error_if_immutable_single, interna /* Allocates several `Int`s at once. */ -internal_int_init_multi :: proc(#no_capture integers: ..^Int, allocator := context.allocator) -> (err: Error) { +internal_int_init_multi :: proc(integers: ..^Int, allocator := context.allocator) -> (err: Error) { context.allocator = allocator integers := integers diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 3ae3b5dba..dec892f84 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -5,8 +5,8 @@ import "core:odin/tokenizer" import "core:fmt" -Warning_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, #no_capture args: ..any) -Error_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, #no_capture args: ..any) +Warning_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any) +Error_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any) Flag :: enum u32 { Optional_Semicolons, @@ -67,25 +67,25 @@ Import_Decl_Kind :: enum { -default_warning_handler :: proc(pos: tokenizer.Pos, msg: string, #no_capture args: ..any) { +default_warning_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) { fmt.eprintf("%s(%d:%d): Warning: ", pos.file, pos.line, pos.column) fmt.eprintf(msg, ..args) fmt.eprintf("\n") } -default_error_handler :: proc(pos: tokenizer.Pos, msg: string, #no_capture args: ..any) { +default_error_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) { fmt.eprintf("%s(%d:%d): ", pos.file, pos.line, pos.column) fmt.eprintf(msg, ..args) fmt.eprintf("\n") } -warn :: proc(p: ^Parser, pos: tokenizer.Pos, msg: string, #no_capture args: ..any) { +warn :: proc(p: ^Parser, pos: tokenizer.Pos, msg: string, args: ..any) { if p.warn != nil { p.warn(pos, msg, ..args) } p.file.syntax_warning_count += 1 } -error :: proc(p: ^Parser, pos: tokenizer.Pos, msg: string, #no_capture args: ..any) { +error :: proc(p: ^Parser, pos: tokenizer.Pos, msg: string, args: ..any) { if p.err != nil { p.err(pos, msg, ..args) } diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index c5992e5f4..62170aa10 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -4,7 +4,7 @@ import "core:fmt" import "core:unicode" import "core:unicode/utf8" -Error_Handler :: #type proc(pos: Pos, fmt: string, #no_capture args: ..any) +Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any) Flag :: enum { Insert_Semicolon, @@ -62,13 +62,13 @@ offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> Pos { } } -default_error_handler :: proc(pos: Pos, msg: string, #no_capture args: ..any) { +default_error_handler :: proc(pos: Pos, msg: string, args: ..any) { fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column) fmt.eprintf(msg, ..args) fmt.eprintf("\n") } -error :: proc(t: ^Tokenizer, offset: int, msg: string, #no_capture args: ..any) { +error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) { pos := offset_to_pos(t, offset) if t.err != nil { t.err(pos, msg, ..args) diff --git a/core/testing/testing.odin b/core/testing/testing.odin index 04dc79095..29fe853ef 100644 --- a/core/testing/testing.odin +++ b/core/testing/testing.odin @@ -53,12 +53,12 @@ T :: struct { @(deprecated="prefer `log.error`") -error :: proc(t: ^T, #no_capture args: ..any, loc := #caller_location) { +error :: proc(t: ^T, args: ..any, loc := #caller_location) { pkg_log.error(..args, location = loc) } @(deprecated="prefer `log.errorf`") -errorf :: proc(t: ^T, format: string, #no_capture args: ..any, loc := #caller_location) { +errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) { pkg_log.errorf(format, ..args, location = loc) } @@ -87,12 +87,12 @@ failed :: proc(t: ^T) -> bool { } @(deprecated="prefer `log.info`") -log :: proc(t: ^T, #no_capture args: ..any, loc := #caller_location) { +log :: proc(t: ^T, args: ..any, loc := #caller_location) { pkg_log.info(..args, location = loc) } @(deprecated="prefer `log.infof`") -logf :: proc(t: ^T, format: string, #no_capture args: ..any, loc := #caller_location) { +logf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) { pkg_log.infof(format, ..args, location = loc) } @@ -121,7 +121,7 @@ expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bo return ok } -expectf :: proc(t: ^T, ok: bool, format: string, #no_capture args: ..any, loc := #caller_location) -> bool { +expectf :: proc(t: ^T, ok: bool, format: string, args: ..any, loc := #caller_location) -> bool { if !ok { pkg_log.errorf(format, ..args, location=loc) } diff --git a/core/text/scanner/scanner.odin b/core/text/scanner/scanner.odin index 6eb366b49..d27c66f24 100644 --- a/core/text/scanner/scanner.odin +++ b/core/text/scanner/scanner.odin @@ -250,7 +250,7 @@ error :: proc(s: ^Scanner, msg: string) { } } -errorf :: proc(s: ^Scanner, format: string, #no_capture args: ..any) { +errorf :: proc(s: ^Scanner, format: string, args: ..any) { error(s, fmt.tprintf(format, ..args)) } diff --git a/core/text/table/table.odin b/core/text/table/table.odin index 4d0baef64..27c99b1f1 100644 --- a/core/text/table/table.odin +++ b/core/text/table/table.odin @@ -145,7 +145,7 @@ set_cell_value_and_alignment :: proc(tbl: ^Table, row, col: int, value: any, ali cell.alignment = alignment } -format :: proc(tbl: ^Table, _fmt: string, #no_capture args: ..any) -> string { +format :: proc(tbl: ^Table, _fmt: string, args: ..any) -> string { context.allocator = tbl.format_allocator return fmt.aprintf(_fmt, ..args) } diff --git a/core/unicode/tools/generate_entity_table.odin b/core/unicode/tools/generate_entity_table.odin index 238a3e219..16baa1adf 100644 --- a/core/unicode/tools/generate_entity_table.odin +++ b/core/unicode/tools/generate_entity_table.odin @@ -12,7 +12,7 @@ import "core:fmt" /* Silent error handler for the parser. */ -Error_Handler :: proc(pos: xml.Pos, fmt: string, #no_capture args: ..any) {} +Error_Handler :: proc(pos: xml.Pos, fmt: string, args: ..any) {} OPTIONS :: xml.Options{ flags = { .Ignore_Unsupported, }, expected_doctype = "unicode", } diff --git a/src/check_type.cpp b/src/check_type.cpp index d1c9bb381..466b9b3cd 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2065,9 +2065,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para } else if (p->flags & FieldFlag_c_vararg) { error(name, "'#no_capture' cannot be applied to a #c_vararg parameter"); p->flags &= ~FieldFlag_no_capture; + } else { + error(name, "'#no_capture' is already implied on all variadic parameter"); } } - if (is_poly_name) { if (p->flags&FieldFlag_no_alias) { error(name, "'#no_alias' can only be applied to non constant values"); @@ -2085,6 +2086,11 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para error(name, "'#by_ptr' can only be applied to variable fields"); p->flags &= ~FieldFlag_by_ptr; } + if (p->flags&FieldFlag_no_capture) { + error(name, "'#no_capture' can only be applied to variable fields"); + p->flags &= ~FieldFlag_no_capture; + } + if (!is_type_polymorphic(type) && check_constant_parameter_value(type, params[i])) { // failed @@ -2104,6 +2110,8 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para param->flags |= EntityFlag_Ellipsis; if (is_c_vararg) { param->flags |= EntityFlag_CVarArg; + } else { + param->flags |= EntityFlag_NoCapture; } } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index dd1041702..71fa1dbd0 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -344,7 +344,6 @@ struct lbProcedure { Array no_captures; LLVMValueRef temp_callee_return_struct_memory; - Ast *curr_stmt; Array scope_stack; diff --git a/src/parser.hpp b/src/parser.hpp index 15176f789..451cdf53d 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -330,10 +330,11 @@ enum FieldFlag : u32 { FieldFlag_subtype = 1<<7, FieldFlag_by_ptr = 1<<8, FieldFlag_no_broadcast = 1<<9, // disallow array programming - FieldFlag_no_capture = 1<<10, + + FieldFlag_no_capture = 1<<11, // Internal use by the parser only - FieldFlag_Tags = 1<<11, + FieldFlag_Tags = 1<<15, FieldFlag_Results = 1<<16, diff --git a/tests/core/encoding/xml/test_core_xml.odin b/tests/core/encoding/xml/test_core_xml.odin index 811ee27dc..b29431e10 100644 --- a/tests/core/encoding/xml/test_core_xml.odin +++ b/tests/core/encoding/xml/test_core_xml.odin @@ -8,7 +8,7 @@ import "core:fmt" import "core:log" import "core:hash" -Silent :: proc(pos: xml.Pos, format: string, #no_capture args: ..any) {} +Silent :: proc(pos: xml.Pos, format: string, args: ..any) {} OPTIONS :: xml.Options{ flags = { .Ignore_Unsupported, .Intern_Comments, }, expected_doctype = "", diff --git a/tests/core/fmt/test_core_fmt.odin b/tests/core/fmt/test_core_fmt.odin index 0aaef93a4..49142e24d 100644 --- a/tests/core/fmt/test_core_fmt.odin +++ b/tests/core/fmt/test_core_fmt.odin @@ -373,7 +373,7 @@ test_odin_value_export :: proc(t: ^testing.T) { } @(private) -check :: proc(t: ^testing.T, exp: string, format: string, #no_capture args: ..any, loc := #caller_location) { +check :: proc(t: ^testing.T, exp: string, format: string, args: ..any, loc := #caller_location) { got := fmt.tprintf(format, ..args) testing.expectf(t, got == exp, "(%q, %v): %q != %q", format, args, got, exp, loc = loc) } diff --git a/tests/documentation/documentation_tester.odin b/tests/documentation/documentation_tester.odin index 7fc37b9f0..8a798d6c5 100644 --- a/tests/documentation/documentation_tester.odin +++ b/tests/documentation/documentation_tester.odin @@ -51,7 +51,7 @@ common_prefix :: proc(strs: []string) -> string { return prefix } -errorf :: proc(format: string, #no_capture args: ..any) -> ! { +errorf :: proc(format: string, args: ..any) -> ! { fmt.eprintf("%s ", os.args[0]) fmt.eprintf(format, ..args) fmt.eprintln() diff --git a/vendor/OpenGL/wrappers.odin b/vendor/OpenGL/wrappers.odin index 971452be5..1eb8fc72f 100644 --- a/vendor/OpenGL/wrappers.odin +++ b/vendor/OpenGL/wrappers.odin @@ -754,7 +754,7 @@ when !GL_DEBUG { MultiDrawElementsIndirectCount :: proc "c" (mode: i32, type: i32, indirect: [^]DrawElementsIndirectCommand, drawcount: i32, maxdrawcount, stride: i32) { impl_MultiDrawElementsIndirectCount(mode, type, indirect, drawcount, maxdrawcount, stride) } PolygonOffsetClamp :: proc "c" (factor, units, clamp: f32) { impl_PolygonOffsetClamp(factor, units, clamp) } } else { - debug_helper :: proc"c"(from_loc: runtime.Source_Code_Location, num_ret: int, #no_capture args: ..any, loc := #caller_location) { + debug_helper :: proc"c"(from_loc: runtime.Source_Code_Location, num_ret: int, args: ..any, loc := #caller_location) { context = runtime.default_context() Error_Enum :: enum { diff --git a/vendor/raylib/raylib.odin b/vendor/raylib/raylib.odin index c2995ba36..0dd3bd4fd 100644 --- a/vendor/raylib/raylib.odin +++ b/vendor/raylib/raylib.odin @@ -1667,7 +1667,7 @@ IsGestureDetected :: proc "c" (gesture: Gesture) -> bool { // Text formatting with variables (sprintf style) -TextFormat :: proc(text: cstring, #no_capture args: ..any) -> cstring { +TextFormat :: proc(text: cstring, args: ..any) -> cstring { @static buffers: [MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH]byte @static index: u32 @@ -1683,7 +1683,7 @@ TextFormat :: proc(text: cstring, #no_capture args: ..any) -> cstring { } // Text formatting with variables (sprintf style) and allocates (must be freed with 'MemFree') -TextFormatAlloc :: proc(text: cstring, #no_capture args: ..any) -> cstring { +TextFormatAlloc :: proc(text: cstring, args: ..any) -> cstring { str := fmt.tprintf(string(text), ..args) return strings.clone_to_cstring(str, MemAllocator()) } From 3dff83f3dc2914cdfb9a8f19cf990682cda41b03 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 12:39:30 +0100 Subject: [PATCH 17/34] Mock out `#no_capture` for future use --- src/check_expr.cpp | 13 ++++++------- src/check_type.cpp | 32 ++++++++++++++++++++++++-------- src/checker.cpp | 2 +- src/checker.hpp | 4 ++-- src/llvm_abi.cpp | 6 ++++++ src/llvm_backend.hpp | 4 ++-- src/llvm_backend_proc.cpp | 32 +++++++++++++------------------- 7 files changed, 54 insertions(+), 39 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 645d8ac5a..4edd34990 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6034,19 +6034,18 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A Entity *vt = pt->params->Tuple.variables[pt->variadic_index]; o.type = vt->type; - // NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameter that use `#no_capture` - // on the variadic parameter - if (c->decl && (vt->flags & EntityFlag_NoCapture)) { + // NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameters with the backing array + if (c->decl) { bool found = false; - for (NoCaptureData &nc : c->decl->no_captures) { - if (are_types_identical(vt->type, nc.slice_type)) { - nc.max_count = gb_max(nc.max_count, variadic_operands.count); + for (auto &vr : c->decl->variadic_reuses) { + if (are_types_identical(vt->type, vr.slice_type)) { + vr.max_count = gb_max(vr.max_count, variadic_operands.count); found = true; break; } } if (!found) { - array_add(&c->decl->no_captures, NoCaptureData{vt->type, variadic_operands.count}); + array_add(&c->decl->variadic_reuses, VariadicReuseData{vt->type, variadic_operands.count}); } } diff --git a/src/check_type.cpp b/src/check_type.cpp index 466b9b3cd..7b75bf503 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1954,7 +1954,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para p->flags &= ~FieldFlag_by_ptr; } if (p->flags&FieldFlag_no_capture) { - error(name, "'#no_capture' can only be applied to variable variadic fields"); + error(name, "'#no_capture' can only be applied to variable fields"); p->flags &= ~FieldFlag_no_capture; } @@ -2059,16 +2059,32 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para } } if (p->flags&FieldFlag_no_capture) { - if (!(is_variadic && variadic_index == variables.count)) { - error(name, "'#no_capture' can only be applied to a variadic parameter"); - p->flags &= ~FieldFlag_no_capture; - } else if (p->flags & FieldFlag_c_vararg) { - error(name, "'#no_capture' cannot be applied to a #c_vararg parameter"); - p->flags &= ~FieldFlag_no_capture; + if (is_variadic && variadic_index == variables.count) { + if (p->flags & FieldFlag_c_vararg) { + error(name, "'#no_capture' cannot be applied to a #c_vararg parameter"); + p->flags &= ~FieldFlag_no_capture; + } else { + error(name, "'#no_capture' is already implied on all variadic parameter"); + } + } else if (is_type_polymorphic(type)) { + // ignore } else { - error(name, "'#no_capture' is already implied on all variadic parameter"); + if (is_type_internally_pointer_like(type)) { + // okay + } else if (is_type_slice(type) || is_type_string(type)) { + // okay + } else if (is_type_dynamic_array(type)) { + // okay + } else { + ERROR_BLOCK(); + error(name, "'#no_capture' can only be applied to pointer-like types, slices, strings, and dynamic arrays"); + error_line("\t'#no_capture' does not currently do anything useful\n"); + p->flags &= ~FieldFlag_no_capture; + } } } + + if (is_poly_name) { if (p->flags&FieldFlag_no_alias) { error(name, "'#no_alias' can only be applied to non constant values"); diff --git a/src/checker.cpp b/src/checker.cpp index abacc13cb..9adf4ef3c 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -184,7 +184,7 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { ptr_set_init(&d->deps, 0); ptr_set_init(&d->type_info_deps, 0); d->labels.allocator = heap_allocator(); - d->no_captures.allocator = heap_allocator(); + d->variadic_reuses.allocator = heap_allocator(); } gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) { diff --git a/src/checker.hpp b/src/checker.hpp index 17722f6b6..2fadbe56a 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -181,7 +181,7 @@ char const *ProcCheckedState_strings[ProcCheckedState_COUNT] { "Checked", }; -struct NoCaptureData { +struct VariadicReuseData { Type *slice_type; // ..elem_type isize max_count; }; @@ -224,7 +224,7 @@ struct DeclInfo { Array labels; - Array no_captures; + Array variadic_reuses; // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time struct lbModule *code_gen_module; diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index b2e485d01..9a3479b34 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -15,6 +15,7 @@ struct lbArgType { LLVMAttributeRef align_attribute; // Optional i64 byval_alignment; bool is_byval; + bool no_capture; }; @@ -159,6 +160,11 @@ gb_internal void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType LLVMAddAttributeAtIndex(fn, arg_index+1, arg->align_attribute); } + if (arg->no_capture) { + LLVMAddAttributeAtIndex(fn, arg_index+1, nocapture_attr); + } + + if (ft->multiple_return_original_type) { if (ft->original_arg_count <= i) { LLVMAddAttributeAtIndex(fn, arg_index+1, noalias_attr); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 71fa1dbd0..24494e2af 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -296,7 +296,7 @@ enum lbProcedureFlag : u32 { lbProcedureFlag_DebugAllocaCopy = 1<<1, }; -struct lbNoCaptureData { +struct lbVariadicReuseData { Type *slice_type; lbAddr base_array; }; @@ -341,7 +341,7 @@ struct lbProcedure { bool in_multi_assignment; Array raw_input_parameters; - Array no_captures; + Array variadic_reuses; LLVMValueRef temp_callee_return_struct_memory; Ast *curr_stmt; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index ec244e185..1585df865 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -517,7 +517,7 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) { lb_start_block(p, p->entry_block); map_init(&p->direct_parameters); - p->no_captures.allocator = heap_allocator(); + p->variadic_reuses.allocator = heap_allocator(); GB_ASSERT(p->type != nullptr); @@ -3452,27 +3452,21 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { isize slice_len = var_args.count; if (slice_len > 0) { lbAddr base_array = {}; - if (e->flags & EntityFlag_NoCapture) { - for (lbNoCaptureData const &nc : p->no_captures) { - if (are_types_identical(nc.slice_type, slice_type)) { - base_array = nc.base_array; + for (auto const &vr : p->variadic_reuses) { + if (are_types_identical(vr.slice_type, slice_type)) { + base_array = vr.base_array; + break; + } + } + DeclInfo *d = decl_info_of_entity(p->entity); + if (d != nullptr && base_array.addr.value == nullptr) { + for (auto const &vr : d->variadic_reuses) { + if (are_types_identical(vr.slice_type, slice_type)) { + base_array = lb_add_local_generated(p, alloc_type_array(elem_type, vr.max_count), true); + array_add(&p->variadic_reuses, lbVariadicReuseData{slice_type, base_array}); break; } } - DeclInfo *d = decl_info_of_entity(p->entity); - if (d != nullptr && base_array.addr.value == nullptr) { - for (NoCaptureData const &nc : d->no_captures) { - if (are_types_identical(nc.slice_type, slice_type)) { - base_array = lb_add_local_generated(p, alloc_type_array(elem_type, nc.max_count), true); - array_add(&p->no_captures, lbNoCaptureData{slice_type, base_array}); - break; - } - } - } - } - - if (base_array.addr.value == nullptr) { - base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true); } GB_ASSERT(base_array.addr.value != nullptr); From 5ce6676914f5daadf42613574d4700c2750275de Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 12:41:16 +0100 Subject: [PATCH 18/34] Make `#no_capture` map to `nocapture` --- src/llvm_backend_proc.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 1585df865..825434c31 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -253,6 +253,9 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i if (e->flags&EntityFlag_NoAlias) { lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias"); } + if (e->flags&EntityFlag_NoCapture) { + lb_add_proc_attribute_at_index(p, offset+parameter_index, "nocapture"); + } parameter_index += 1; } } From 3533094cc2fc8cae8229b9887efae4541ccd278b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 12:44:13 +0100 Subject: [PATCH 19/34] Restrict `#no_capture` to pointer-like types only --- src/check_type.cpp | 6 +----- src/llvm_backend_proc.cpp | 4 +++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 7b75bf503..d0dddb62b 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2071,13 +2071,9 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para } else { if (is_type_internally_pointer_like(type)) { // okay - } else if (is_type_slice(type) || is_type_string(type)) { - // okay - } else if (is_type_dynamic_array(type)) { - // okay } else { ERROR_BLOCK(); - error(name, "'#no_capture' can only be applied to pointer-like types, slices, strings, and dynamic arrays"); + error(name, "'#no_capture' can only be applied to pointer-like types"); error_line("\t'#no_capture' does not currently do anything useful\n"); p->flags &= ~FieldFlag_no_capture; } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 825434c31..272ffb474 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -254,7 +254,9 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias"); } if (e->flags&EntityFlag_NoCapture) { - lb_add_proc_attribute_at_index(p, offset+parameter_index, "nocapture"); + if (is_type_internally_pointer_like(e->type)) { + lb_add_proc_attribute_at_index(p, offset+parameter_index, "nocapture"); + } } parameter_index += 1; } From 5027c7081e02cc208a354cbad2fbeb90ac297b03 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 12:50:33 +0100 Subject: [PATCH 20/34] Reuse slice variable for variadic parameters --- src/llvm_backend.hpp | 1 + src/llvm_backend_proc.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 24494e2af..100748038 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -299,6 +299,7 @@ enum lbProcedureFlag : u32 { struct lbVariadicReuseData { Type *slice_type; lbAddr base_array; + lbAddr slice_addr; }; struct lbProcedure { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 272ffb474..bc85b14c2 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3457,9 +3457,12 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { isize slice_len = var_args.count; if (slice_len > 0) { lbAddr base_array = {}; + lbAddr slice = {}; + for (auto const &vr : p->variadic_reuses) { if (are_types_identical(vr.slice_type, slice_type)) { base_array = vr.base_array; + slice = vr.slice_addr; break; } } @@ -3468,14 +3471,15 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { for (auto const &vr : d->variadic_reuses) { if (are_types_identical(vr.slice_type, slice_type)) { base_array = lb_add_local_generated(p, alloc_type_array(elem_type, vr.max_count), true); - array_add(&p->variadic_reuses, lbVariadicReuseData{slice_type, base_array}); + slice = lb_add_local_generated(p, slice_type, true); + array_add(&p->variadic_reuses, lbVariadicReuseData{slice_type, base_array, slice}); break; } } } GB_ASSERT(base_array.addr.value != nullptr); + GB_ASSERT(slice.addr.value != nullptr); - lbAddr slice = lb_add_local_generated(p, slice_type, true); for (isize i = 0; i < var_args.count; i++) { lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i); From 8650180cccad02b39a00dcd05f696a8e3879c29e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 12:59:55 +0100 Subject: [PATCH 21/34] Change Odin calling convention to not do a copy on `byval` for SysV --- src/llvm_abi.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 9a3479b34..c21cd0a46 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -651,10 +651,10 @@ namespace lbAbiAmd64SysV { if (is_mem_cls(cls, attribute_kind)) { LLVMAttributeRef attribute = nullptr; if (attribute_kind == Amd64TypeAttribute_ByVal) { - // if (!is_calling_convention_odin(calling_convention)) { - return lb_arg_type_indirect_byval(c, type); - // } - // attribute = nullptr; + if (is_calling_convention_odin(calling_convention)) { + return lb_arg_type_indirect(type, attribute); + } + return lb_arg_type_indirect_byval(c, type); } else if (attribute_kind == Amd64TypeAttribute_StructRect) { attribute = lb_create_enum_attribute_with_type(c, "sret", type); } From 0a530b5ce8642508c5df2fdb9ec43682be07d5cd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 13:20:12 +0100 Subject: [PATCH 22/34] Add error for `#no_capture` being reserved for future use --- src/check_type.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index d0dddb62b..fea937e4e 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2070,7 +2070,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para // ignore } else { if (is_type_internally_pointer_like(type)) { - // okay + error(name, "'#no_capture' is currently reserved for future use"); } else { ERROR_BLOCK(); error(name, "'#no_capture' can only be applied to pointer-like types"); From 6959554040d85597300ab2ce6c25852d18e61923 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 13:44:47 +0100 Subject: [PATCH 23/34] Calculate size and alignment, and reuse memory for all variadic calls within a procedure body --- src/check_decl.cpp | 9 +++++++++ src/checker.cpp | 2 ++ src/checker.hpp | 4 +++- src/llvm_backend.hpp | 6 +++--- src/llvm_backend_proc.cpp | 25 +++++++++++++++++-------- 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 7d81d102d..6828774e4 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1869,5 +1869,14 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de add_deps_from_child_to_parent(decl); + for (VariadicReuseData const &vr : decl->variadic_reuses) { + GB_ASSERT(vr.slice_type->kind == Type_Slice); + Type *elem = vr.slice_type->Slice.elem; + i64 size = type_size_of(elem); + i64 align = type_align_of(elem); + decl->variadic_reuse_max_bytes = gb_max(decl->variadic_reuse_max_bytes, size*vr.max_count); + decl->variadic_reuse_max_align = gb_max(decl->variadic_reuse_max_align, align); + } + return true; } diff --git a/src/checker.cpp b/src/checker.cpp index 9adf4ef3c..336440d32 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -185,6 +185,8 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { ptr_set_init(&d->type_info_deps, 0); d->labels.allocator = heap_allocator(); d->variadic_reuses.allocator = heap_allocator(); + d->variadic_reuse_max_bytes = 0; + d->variadic_reuse_max_align = 1; } gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) { diff --git a/src/checker.hpp b/src/checker.hpp index 2fadbe56a..d76e4c7d0 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -183,7 +183,7 @@ char const *ProcCheckedState_strings[ProcCheckedState_COUNT] { struct VariadicReuseData { Type *slice_type; // ..elem_type - isize max_count; + i64 max_count; }; // DeclInfo is used to store information of certain declarations to allow for "any order" usage @@ -225,6 +225,8 @@ struct DeclInfo { Array labels; Array variadic_reuses; + i64 variadic_reuse_max_bytes; + i64 variadic_reuse_max_align; // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time struct lbModule *code_gen_module; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 100748038..deb05528f 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -296,9 +296,8 @@ enum lbProcedureFlag : u32 { lbProcedureFlag_DebugAllocaCopy = 1<<1, }; -struct lbVariadicReuseData { +struct lbVariadicReuseSlices { Type *slice_type; - lbAddr base_array; lbAddr slice_addr; }; @@ -342,7 +341,8 @@ struct lbProcedure { bool in_multi_assignment; Array raw_input_parameters; - Array variadic_reuses; + Array variadic_reuses; + lbAddr variadic_reuse_base_array_ptr; LLVMValueRef temp_callee_return_struct_memory; Ast *curr_stmt; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index bc85b14c2..7a895fbdd 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3456,39 +3456,48 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } isize slice_len = var_args.count; if (slice_len > 0) { - lbAddr base_array = {}; lbAddr slice = {}; for (auto const &vr : p->variadic_reuses) { if (are_types_identical(vr.slice_type, slice_type)) { - base_array = vr.base_array; slice = vr.slice_addr; break; } } + DeclInfo *d = decl_info_of_entity(p->entity); - if (d != nullptr && base_array.addr.value == nullptr) { + if (d != nullptr && slice.addr.value == nullptr) { for (auto const &vr : d->variadic_reuses) { if (are_types_identical(vr.slice_type, slice_type)) { - base_array = lb_add_local_generated(p, alloc_type_array(elem_type, vr.max_count), true); slice = lb_add_local_generated(p, slice_type, true); - array_add(&p->variadic_reuses, lbVariadicReuseData{slice_type, base_array, slice}); + array_add(&p->variadic_reuses, lbVariadicReuseSlices{slice_type, slice}); break; } } } - GB_ASSERT(base_array.addr.value != nullptr); + + lbValue base_array_ptr = p->variadic_reuse_base_array_ptr.addr; + if (d != nullptr && base_array_ptr.value == nullptr) { + i64 max_bytes = d->variadic_reuse_max_bytes; + i64 max_align = gb_max(d->variadic_reuse_max_align, 16); + p->variadic_reuse_base_array_ptr = lb_add_local_generated(p, alloc_type_array(t_u8, max_bytes), true); + lb_try_update_alignment(p->variadic_reuse_base_array_ptr.addr, cast(unsigned)max_align); + base_array_ptr = p->variadic_reuse_base_array_ptr.addr; + } + + GB_ASSERT(base_array_ptr.value != nullptr); GB_ASSERT(slice.addr.value != nullptr); + base_array_ptr = lb_emit_conv(p, base_array_ptr, alloc_type_pointer(alloc_type_array(elem_type, slice_len))); for (isize i = 0; i < var_args.count; i++) { - lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i); + lbValue addr = lb_emit_array_epi(p, base_array_ptr, cast(i32)i); lbValue var_arg = var_args[i]; var_arg = lb_emit_conv(p, var_arg, elem_type); lb_emit_store(p, addr, var_arg); } - lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0); + lbValue base_elem = lb_emit_array_epi(p, base_array_ptr, 0); lbValue len = lb_const_int(p->module, t_int, slice_len); lb_fill_slice(p, slice, base_elem, len); From 2e0c5fefdedbeb1d9008a6f05a5c73c9fca7602f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 14:07:36 +0100 Subject: [PATCH 24/34] Reuse the slice value too for variadic parameters (LLVM >= 13) --- src/llvm_backend_proc.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 7a895fbdd..5270d6c30 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3469,7 +3469,18 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { if (d != nullptr && slice.addr.value == nullptr) { for (auto const &vr : d->variadic_reuses) { if (are_types_identical(vr.slice_type, slice_type)) { + #if LLVM_VERSION_MAJOR >= 13 + // NOTE(bill): No point wasting even more memory, just reuse this stack variable too + if (p->variadic_reuses.count > 0) { + slice = p->variadic_reuses[0].slice_addr; + } else { + slice = lb_add_local_generated(p, slice_type, true); + } + // NOTE(bill): Change the underlying type to match the specific type + slice.addr.type = alloc_type_pointer(slice_type); + #else slice = lb_add_local_generated(p, slice_type, true); + #endif array_add(&p->variadic_reuses, lbVariadicReuseSlices{slice_type, slice}); break; } From d1450e3d880ec70b306dc735f7f694f265348ef1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 14:44:22 +0100 Subject: [PATCH 25/34] Fix styling issues --- src/thread_pool.cpp | 39 ++++++++++++++++++++------------------- src/threading.cpp | 12 ++++++------ 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/thread_pool.cpp b/src/thread_pool.cpp index bf953ddd0..62cca6de6 100644 --- a/src/thread_pool.cpp +++ b/src/thread_pool.cpp @@ -11,14 +11,14 @@ gb_internal bool thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, vo gb_internal void thread_pool_wait(ThreadPool *pool); enum GrabState { - GrabSuccess = 0, - GrabEmpty = 1, - GrabFailed = 2, + Grab_Success = 0, + Grab_Empty = 1, + Grab_Failed = 2, }; struct ThreadPool { - gbAllocator threads_allocator; - Slice threads; + gbAllocator threads_allocator; + Slice threads; std::atomic running; Futex tasks_available; @@ -59,8 +59,8 @@ gb_internal void thread_pool_destroy(ThreadPool *pool) { gb_free(pool->threads_allocator, pool->threads.data); } -TaskRingBuffer *taskring_grow(TaskRingBuffer *ring, isize bottom, isize top) { - TaskRingBuffer *new_ring = taskring_init(ring->size * 2); +TaskRingBuffer *task_ring_grow(TaskRingBuffer *ring, isize bottom, isize top) { + TaskRingBuffer *new_ring = task_ring_init(ring->size * 2); for (isize i = top; i < bottom; i++) { new_ring->buffer[i % new_ring->size] = ring->buffer[i % ring->size]; } @@ -75,7 +75,7 @@ void thread_pool_queue_push(Thread *thread, WorkerTask task) { isize size = bot - top; if (size > (cur_ring->size - 1)) { // Queue is full - thread->queue.ring = taskring_grow(thread->queue.ring, bot, top); + thread->queue.ring = task_ring_grow(thread->queue.ring, bot, top); cur_ring = thread->queue.ring.load(std::memory_order_relaxed); } @@ -104,19 +104,19 @@ GrabState thread_pool_queue_take(Thread *thread, WorkerTask *task) { if (!thread->queue.top.compare_exchange_strong(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) { // Race failed thread->queue.bottom.store(bot + 1, std::memory_order_relaxed); - return GrabEmpty; + return Grab_Empty; } thread->queue.bottom.store(bot + 1, std::memory_order_relaxed); - return GrabSuccess; + return Grab_Success; } // We got a task without hitting a race - return GrabSuccess; + return Grab_Success; } else { // Queue is empty thread->queue.bottom.store(bot + 1, std::memory_order_relaxed); - return GrabEmpty; + return Grab_Empty; } } @@ -125,7 +125,7 @@ GrabState thread_pool_queue_steal(Thread *thread, WorkerTask *task) { std::atomic_thread_fence(std::memory_order_seq_cst); isize bot = thread->queue.bottom.load(std::memory_order_acquire); - GrabState ret = GrabEmpty; + GrabState ret = Grab_Empty; if (top < bot) { // Queue is not empty TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_consume); @@ -133,9 +133,9 @@ GrabState thread_pool_queue_steal(Thread *thread, WorkerTask *task) { if (!thread->queue.top.compare_exchange_strong(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) { // Race failed - ret = GrabFailed; + ret = Grab_Failed; } else { - ret = GrabSuccess; + ret = Grab_Success; } } return ret; @@ -208,11 +208,10 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) { WorkerTask task; GrabState ret = thread_pool_queue_steal(thread, &task); - if (ret == GrabFailed) { - goto main_loop_continue; - } else if (ret == GrabEmpty) { + switch (ret) { + case Grab_Empty: continue; - } else if (ret == GrabSuccess) { + case Grab_Success: task.do_work(task.data); pool->tasks_left.fetch_sub(1, std::memory_order_release); @@ -220,6 +219,8 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) { futex_signal(&pool->tasks_left); } + /*fallthrough*/ + case Grab_Failed: goto main_loop_continue; } } diff --git a/src/threading.cpp b/src/threading.cpp index ac79efb05..ff0fdfcde 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -66,9 +66,9 @@ struct Thread { #endif isize idx; - isize stack_size; + isize stack_size; - struct TaskQueue queue; + struct TaskQueue queue; struct ThreadPool *pool; }; @@ -560,10 +560,10 @@ gb_internal void *internal_thread_proc(void *arg) { } #endif -TaskRingBuffer *taskring_init(isize size) { - TaskRingBuffer *ring = (TaskRingBuffer *)gb_alloc(heap_allocator(), sizeof(TaskRingBuffer)); +TaskRingBuffer *task_ring_init(isize size) { + TaskRingBuffer *ring = gb_alloc_item(heap_allocator(), TaskRingBuffer); ring->size = size; - ring->buffer = (WorkerTask *)gb_alloc_array(heap_allocator(), WorkerTask, ring->size); + ring->buffer = gb_alloc_array(heap_allocator(), WorkerTask, ring->size); return ring; } @@ -581,7 +581,7 @@ gb_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) { #endif // Size must be a power of 2 - t->queue.ring = taskring_init(1 << 14); + t->queue.ring = task_ring_init(1 << 14); t->pool = pool; t->idx = idx; } From 3d38f14202773d80293fd8f72b677b635e6ea639 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 14:51:22 +0100 Subject: [PATCH 26/34] Use `fstat` on `os2.File` directly --- core/os/os2/errors.odin | 2 ++ core/os/os2/file.odin | 2 +- core/os/os2/file_linux.odin | 25 ++++++++++--------------- core/os/os2/file_windows.odin | 1 + core/os/os2/stat.odin | 8 +++++--- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/core/os/os2/errors.odin b/core/os/os2/errors.odin index 51d8314b4..8961bf599 100644 --- a/core/os/os2/errors.odin +++ b/core/os/os2/errors.odin @@ -22,6 +22,7 @@ General_Error :: enum u32 { Invalid_File, Invalid_Dir, Invalid_Path, + Invalid_Callback, Pattern_Has_Separator, @@ -64,6 +65,7 @@ error_string :: proc(ferr: Error) -> string { case .Invalid_File: return "invalid file" case .Invalid_Dir: return "invalid directory" case .Invalid_Path: return "invalid path" + case .Invalid_Callback: return "invalid callback" case .Unsupported: return "unsupported" case .Pattern_Has_Separator: return "pattern has separator" } diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin index 236423163..dc618db37 100644 --- a/core/os/os2/file.odin +++ b/core/os/os2/file.odin @@ -7,7 +7,7 @@ import "base:runtime" File :: struct { impl: _File, stream: io.Stream, - user_fstat: Fstat_Callback, + fstat: Fstat_Callback, } File_Mode :: distinct u32 diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 8e7db9751..eaded51c9 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -90,22 +90,17 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, er _new_file :: proc(fd: uintptr, _: string = "") -> ^File { file := new(File, file_allocator()) - _construct_file(file, fd, "") - return file -} - -_construct_file :: proc(file: ^File, fd: uintptr, _: string = "") { - file^ = { - impl = { - fd = linux.Fd(fd), - allocator = file_allocator(), - name = _get_full_path(file.impl.fd, file.impl.allocator), - }, - stream = { - data = file, - procedure = _file_stream_proc, - }, + file.impl = { + fd = linux.Fd(fd), + allocator = file_allocator(), + name = _get_full_path(file.impl.fd, file.impl.allocator), } + file.stream = { + data = file, + procedure = _file_stream_proc, + } + file.fstat = _fstat + return file } _destroy :: proc(f: ^File) -> Error { diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index b88ee8a69..8953edafb 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -151,6 +151,7 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File { data = f, procedure = _file_stream_proc, } + f.fstat = _fstat return f } diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin index f79ad9165..1e7f9d225 100644 --- a/core/os/os2/stat.odin +++ b/core/os/os2/stat.odin @@ -29,10 +29,12 @@ file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) { @(require_results) fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { - if f != nil && f.user_fstat != nil { - return f->user_fstat(allocator) + if f == nil { + return {}, nil + } else if f.fstat != nil { + return f->fstat(allocator) } - return _fstat(f, allocator) + return {}, .Invalid_Callback } @(require_results) From d90d7ed00280d997f6022420a5185a395d719b4d Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sun, 14 Jul 2024 16:00:55 +0200 Subject: [PATCH 27/34] Fix off-by-one in queue `back` and `back_ptr` procs --- core/container/queue/queue.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/container/queue/queue.odin b/core/container/queue/queue.odin index e7a60dde0..f83a5f2b7 100644 --- a/core/container/queue/queue.odin +++ b/core/container/queue/queue.odin @@ -95,11 +95,11 @@ front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T { } back :: proc(q: ^$Q/Queue($T)) -> T { - idx := (q.offset+uint(q.len))%builtin.len(q.data) + idx := (q.offset+uint(q.len - 1))%builtin.len(q.data) return q.data[idx] } back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T { - idx := (q.offset+uint(q.len))%builtin.len(q.data) + idx := (q.offset+uint(q.len - 1))%builtin.len(q.data) return &q.data[idx] } From 4f73b35da5e9f84b0851f2ba66907a4f8de057ff Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 15:09:33 +0100 Subject: [PATCH 28/34] Make `os2.File` a more generic interface --- core/os/os2/file.odin | 4 +- core/os/os2/file_linux.odin | 93 +++++++++++++++++++++-------------- core/os/os2/file_windows.odin | 71 +++++++++++++++----------- core/os/os2/stat_linux.odin | 3 +- core/os/os2/stat_windows.odin | 6 +-- 5 files changed, 103 insertions(+), 74 deletions(-) diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin index dc618db37..be03155ff 100644 --- a/core/os/os2/file.odin +++ b/core/os/os2/file.odin @@ -5,9 +5,9 @@ import "core:time" import "base:runtime" File :: struct { - impl: _File, + impl: rawptr, stream: io.Stream, - fstat: Fstat_Callback, + fstat: Fstat_Callback, } File_Mode :: distinct u32 diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index eaded51c9..d2196b97b 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -7,13 +7,14 @@ import "base:runtime" import "core:sys/linux" _File :: struct { + using file: File, name: string, fd: linux.Fd, allocator: runtime.Allocator, } -_stdin : File = { - impl = { +_stdin := File{ + impl = &_File{ name = "/proc/self/fd/0", fd = 0, allocator = _file_allocator(), @@ -21,9 +22,10 @@ _stdin : File = { stream = { procedure = _file_stream_proc, }, + fstat = _fstat, } -_stdout : File = { - impl = { +_stdout := File{ + impl = &_File{ name = "/proc/self/fd/1", fd = 1, allocator = _file_allocator(), @@ -31,9 +33,10 @@ _stdout : File = { stream = { procedure = _file_stream_proc, }, + fstat = _fstat, } -_stderr : File = { - impl = { +_stderr := File{ + impl = &_File{ name = "/proc/self/fd/2", fd = 2, allocator = _file_allocator(), @@ -41,6 +44,7 @@ _stderr : File = { stream = { procedure = _file_stream_proc, }, + fstat = _fstat, } @init @@ -89,35 +93,35 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, er } _new_file :: proc(fd: uintptr, _: string = "") -> ^File { - file := new(File, file_allocator()) - file.impl = { - fd = linux.Fd(fd), - allocator = file_allocator(), - name = _get_full_path(file.impl.fd, file.impl.allocator), - } - file.stream = { - data = file, + impl := new(_File, file_allocator()) + impl.fd = linux.Fd(fd) + impl.allocator = file_allocator() + impl.name = _get_full_path(impl.fd, impl.allocator) + impl.file.stream = { + data = &impl.file, procedure = _file_stream_proc, } - file.fstat = _fstat - return file + impl.fstat = _fstat + return impl } _destroy :: proc(f: ^File) -> Error { - if f == nil { + if f == nil || f.impl == nil { return nil } - delete(f.impl.name, f.impl.allocator) - free(f, f.impl.allocator) + impl := (^_File)(f.impl) + delete(impl.name, impl.allocator) + free(f, impl.allocator) return nil } _close :: proc(f: ^File) -> Error { - if f == nil { + if f == nil || f.impl == nil { return nil } - errno := linux.close(f.impl.fd) + impl := (^_File)(f.impl) + errno := linux.close(impl.fd) if errno == .EBADF { // avoid possible double free return _get_platform_error(errno) } @@ -126,18 +130,20 @@ _close :: proc(f: ^File) -> Error { } _fd :: proc(f: ^File) -> uintptr { - if f == nil { + if f == nil || f.impl == nil { return ~uintptr(0) } - return uintptr(f.impl.fd) + impl := (^_File)(f.impl) + return uintptr(impl.fd) } _name :: proc(f: ^File) -> string { - return f.impl.name if f != nil else "" + return (^_File)(f.impl).name if f != nil && f.impl != nil else "" } _seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) { - n, errno := linux.lseek(f.impl.fd, offset, linux.Seek_Whence(whence)) + impl := (^_File)(f.impl) + n, errno := linux.lseek(impl.fd, offset, linux.Seek_Whence(whence)) if errno != .NONE { return -1, _get_platform_error(errno) } @@ -148,7 +154,8 @@ _read :: proc(f: ^File, p: []byte) -> (i64, Error) { if len(p) == 0 { return 0, nil } - n, errno := linux.read(f.impl.fd, p[:]) + impl := (^_File)(f.impl) + n, errno := linux.read(impl.fd, p[:]) if errno != .NONE { return -1, _get_platform_error(errno) } @@ -159,8 +166,8 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) { if offset < 0 { return 0, .Invalid_Offset } - - n, errno := linux.pread(f.impl.fd, p[:], offset) + impl := (^_File)(f.impl) + n, errno := linux.pread(impl.fd, p[:], offset) if errno != .NONE { return -1, _get_platform_error(errno) } @@ -174,7 +181,8 @@ _write :: proc(f: ^File, p: []byte) -> (i64, Error) { if len(p) == 0 { return 0, nil } - n, errno := linux.write(f.impl.fd, p[:]) + impl := (^_File)(f.impl) + n, errno := linux.write(impl.fd, p[:]) if errno != .NONE { return -1, _get_platform_error(errno) } @@ -186,7 +194,8 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) { return 0, .Invalid_Offset } - n, errno := linux.pwrite(f.impl.fd, p[:], offset) + impl := (^_File)(f.impl) + n, errno := linux.pwrite(impl.fd, p[:], offset) if errno != .NONE { return -1, _get_platform_error(errno) } @@ -195,7 +204,8 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) { _file_size :: proc(f: ^File) -> (n: i64, err: Error) { s: linux.Stat = --- - errno := linux.fstat(f.impl.fd, &s) + impl := (^_File)(f.impl) + errno := linux.fstat(impl.fd, &s) if errno != .NONE { return -1, _get_platform_error(errno) } @@ -203,15 +213,18 @@ _file_size :: proc(f: ^File) -> (n: i64, err: Error) { } _sync :: proc(f: ^File) -> Error { - return _get_platform_error(linux.fsync(f.impl.fd)) + impl := (^_File)(f.impl) + return _get_platform_error(linux.fsync(impl.fd)) } _flush :: proc(f: ^File) -> Error { - return _get_platform_error(linux.fsync(f.impl.fd)) + impl := (^_File)(f.impl) + return _get_platform_error(linux.fsync(impl.fd)) } _truncate :: proc(f: ^File, size: i64) -> Error { - return _get_platform_error(linux.ftruncate(f.impl.fd, size)) + impl := (^_File)(f.impl) + return _get_platform_error(linux.ftruncate(impl.fd, size)) } _remove :: proc(name: string) -> Error { @@ -287,7 +300,8 @@ _chdir :: proc(name: string) -> Error { } _fchdir :: proc(f: ^File) -> Error { - return _get_platform_error(linux.fchdir(f.impl.fd)) + impl := (^_File)(f.impl) + return _get_platform_error(linux.fchdir(impl.fd)) } _chmod :: proc(name: string, mode: File_Mode) -> Error { @@ -297,7 +311,8 @@ _chmod :: proc(name: string, mode: File_Mode) -> Error { } _fchmod :: proc(f: ^File, mode: File_Mode) -> Error { - return _get_platform_error(linux.fchmod(f.impl.fd, transmute(linux.Mode)(u32(mode)))) + impl := (^_File)(f.impl) + return _get_platform_error(linux.fchmod(impl.fd, transmute(linux.Mode)(u32(mode)))) } // NOTE: will throw error without super user priviledges @@ -316,7 +331,8 @@ _lchown :: proc(name: string, uid, gid: int) -> Error { // NOTE: will throw error without super user priviledges _fchown :: proc(f: ^File, uid, gid: int) -> Error { - return _get_platform_error(linux.fchown(f.impl.fd, linux.Uid(uid), linux.Gid(gid))) + impl := (^_File)(f.impl) + return _get_platform_error(linux.fchown(impl.fd, linux.Uid(uid), linux.Gid(gid))) } _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error { @@ -346,7 +362,8 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error { uint(mtime._nsec) % uint(time.Second), }, } - return _get_platform_error(linux.utimensat(f.impl.fd, nil, ×[0], nil)) + impl := (^_File)(f.impl) + return _get_platform_error(linux.utimensat(impl.fd, nil, ×[0], nil)) } _exists :: proc(name: string) -> bool { diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 8953edafb..0ccea7f96 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -24,6 +24,8 @@ _File_Kind :: enum u8 { } _File :: struct { + using file: File, + fd: rawptr, name: string, wname: win32.wstring, @@ -130,12 +132,12 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File { if handle == INVALID_HANDLE { return nil } - f := new(File, file_allocator()) + f := new(_File, file_allocator()) - f.impl.allocator = file_allocator() - f.impl.fd = rawptr(handle) - f.impl.name, _ = clone_string(name, f.impl.allocator) - f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator) + f.allocator = file_allocator() + f.fd = rawptr(handle) + f.name, _ = clone_string(name, f.allocator) + f.wname = win32.utf8_to_wstring(name, f.allocator) handle := _handle(f) kind := _File_Kind.File @@ -145,7 +147,7 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File { if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE { kind = .Pipe } - f.impl.kind = kind + f.kind = kind f.stream = { data = f, @@ -157,37 +159,38 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File { } _fd :: proc(f: ^File) -> uintptr { - if f == nil { + if f == nil || f.impl == nil { return INVALID_HANDLE } - return uintptr(f.impl.fd) + return uintptr((^_File)(f.impl).fd) } _destroy :: proc(f: ^File) -> Error { - if f == nil { + if f == nil || f.impl == nil { return nil } - a := f.impl.allocator - free(f.impl.wname, a) - delete(f.impl.name, a) - free(f, a) + _f := (^_File)(f.impl) + a := _f.allocator + free(_f.wname, a) + delete(_f.name, a) + free(_f, a) return nil } _close :: proc(f: ^File) -> Error { - if f == nil { + if f == nil || f.impl == nil { return nil } - if !win32.CloseHandle(win32.HANDLE(f.impl.fd)) { + if !win32.CloseHandle(win32.HANDLE((^_File)(f.impl).fd)) { return .Closed } return _destroy(f) } _name :: proc(f: ^File) -> string { - return f.impl.name if f != nil else "" + return (^_File)(f.impl).name if f != nil && f.impl != nil else "" } _seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) { @@ -195,11 +198,13 @@ _seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Er if handle == win32.INVALID_HANDLE { return 0, .Invalid_File } - if f.impl.kind == .Pipe { + impl := (^_File)(f.impl) + + if impl.kind == .Pipe { return 0, .Invalid_File } - sync.guard(&f.impl.rw_mutex) + sync.guard(&impl.rw_mutex) w: u32 switch whence { @@ -274,12 +279,13 @@ _read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) { total_read: int length := len(p) - sync.shared_guard(&f.impl.rw_mutex) // multiple readers + impl := (^_File)(f.impl) + sync.shared_guard(&impl.rw_mutex) // multiple readers - if sync.guard(&f.impl.p_mutex) { + if sync.guard(&impl.p_mutex) { to_read := min(win32.DWORD(length), MAX_RW) ok: win32.BOOL - if f.impl.kind == .Console { + if impl.kind == .Console { n, cerr := read_console(handle, p[total_read:][:to_read]) total_read += n if cerr != nil { @@ -326,7 +332,8 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) { return } - sync.guard(&f.impl.p_mutex) + impl := (^_File)(f.impl) + sync.guard(&impl.p_mutex) p, offset := p, offset for len(p) > 0 { @@ -349,7 +356,8 @@ _write :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) { handle := _handle(f) - sync.guard(&f.impl.rw_mutex) + impl := (^_File)(f.impl) + sync.guard(&impl.rw_mutex) for total_write < length { remaining := length - total_write to_write := win32.DWORD(min(i32(remaining), MAX_RW)) @@ -390,7 +398,8 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) { return } - sync.guard(&f.impl.p_mutex) + impl := (^_File)(f.impl) + sync.guard(&impl.p_mutex) p, offset := p, offset for len(p) > 0 { m := pwrite(f, p, offset) or_return @@ -403,7 +412,8 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) { _file_size :: proc(f: ^File) -> (n: i64, err: Error) { length: win32.LARGE_INTEGER - if f.impl.kind == .Pipe { + impl := (^_File)(f.impl) + if impl.kind == .Pipe { return 0, .No_Size } handle := _handle(f) @@ -428,7 +438,7 @@ _flush :: proc(f: ^File) -> Error { } _truncate :: proc(f: ^File, size: i64) -> Error { - if f == nil { + if f == nil || f.impl == nil { return nil } curr_off := seek(f, 0, .Current) or_return @@ -615,17 +625,18 @@ _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, er _fchdir :: proc(f: ^File) -> Error { - if f == nil { + if f == nil || f.impl == nil { return nil } - if !win32.SetCurrentDirectoryW(f.impl.wname) { + impl := (^_File)(f.impl) + if !win32.SetCurrentDirectoryW(impl.wname) { return _get_platform_error() } return nil } _fchmod :: proc(f: ^File, mode: File_Mode) -> Error { - if f == nil { + if f == nil || f.impl == nil { return nil } d: win32.BY_HANDLE_FILE_INFORMATION @@ -680,7 +691,7 @@ _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error { return _fchtimes(f, atime, mtime) } _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error { - if f == nil { + if f == nil || f.impl == nil { return nil } d: win32.BY_HANDLE_FILE_INFORMATION diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin index c0b3088b4..a6df4e67a 100644 --- a/core/os/os2/stat_linux.odin +++ b/core/os/os2/stat_linux.odin @@ -7,7 +7,8 @@ import "core:sys/linux" import "core:path/filepath" _fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { - return _fstat_internal(f.impl.fd, allocator) + impl := (^_File)(f.impl) + return _fstat_internal(impl.fd, allocator) } _fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (File_Info, Error) { diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index 03ad2052f..5f7daa062 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -7,7 +7,7 @@ import "core:strings" import win32 "core:sys/windows" _fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { - if f == nil || f.impl.fd == nil { + if f == nil || (^_File)(f.impl).fd == nil { return {}, nil } @@ -122,7 +122,7 @@ _cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) { - if f == nil || f.impl.fd == nil { + if f == nil || (^_File)(f.impl).fd == nil { return "", nil } h := _handle(f) @@ -138,7 +138,7 @@ _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (strin } _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) { - if f == nil || f.impl.fd == nil { + if f == nil || (^_File)(f.impl).fd == nil { return nil, nil } h := _handle(f) From 5de6016e7ff1d959c5deed3b878a75e5e078b5b2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 15:26:59 +0100 Subject: [PATCH 29/34] Clean up `os2.File.impl` usage --- core/os/os2/file_linux.odin | 90 +++++++++++------------ core/os/os2/file_windows.odin | 132 +++++++++++++++++----------------- core/os/os2/stat_linux.odin | 2 +- core/os/os2/stat_windows.odin | 6 +- 4 files changed, 111 insertions(+), 119 deletions(-) diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index d2196b97b..482c9b5b4 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -6,15 +6,15 @@ import "core:time" import "base:runtime" import "core:sys/linux" -_File :: struct { - using file: File, +File_Impl :: struct { + file: File, name: string, fd: linux.Fd, allocator: runtime.Allocator, } _stdin := File{ - impl = &_File{ + impl = &File_Impl{ name = "/proc/self/fd/0", fd = 0, allocator = _file_allocator(), @@ -25,7 +25,7 @@ _stdin := File{ fstat = _fstat, } _stdout := File{ - impl = &_File{ + impl = &File_Impl{ name = "/proc/self/fd/1", fd = 1, allocator = _file_allocator(), @@ -36,7 +36,7 @@ _stdout := File{ fstat = _fstat, } _stderr := File{ - impl = &_File{ + impl = &File_Impl{ name = "/proc/self/fd/2", fd = 2, allocator = _file_allocator(), @@ -93,35 +93,35 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, er } _new_file :: proc(fd: uintptr, _: string = "") -> ^File { - impl := new(_File, file_allocator()) + impl := new(File_Impl, file_allocator()) + impl.file.impl = impl impl.fd = linux.Fd(fd) impl.allocator = file_allocator() impl.name = _get_full_path(impl.fd, impl.allocator) impl.file.stream = { - data = &impl.file, + data = impl, procedure = _file_stream_proc, } - impl.fstat = _fstat - return impl + impl.file.fstat = _fstat + return &impl.file } -_destroy :: proc(f: ^File) -> Error { - if f == nil || f.impl == nil { +_destroy :: proc(f: ^File_Impl) -> Error { + if f == nil { return nil } - impl := (^_File)(f.impl) - delete(impl.name, impl.allocator) - free(f, impl.allocator) + a := f.allocator + delete(f.name, a) + free(f, a) return nil } -_close :: proc(f: ^File) -> Error { - if f == nil || f.impl == nil { +_close :: proc(f: ^File_Impl) -> Error { + if f == nil{ return nil } - impl := (^_File)(f.impl) - errno := linux.close(impl.fd) + errno := linux.close(f.fd) if errno == .EBADF { // avoid possible double free return _get_platform_error(errno) } @@ -133,41 +133,38 @@ _fd :: proc(f: ^File) -> uintptr { if f == nil || f.impl == nil { return ~uintptr(0) } - impl := (^_File)(f.impl) + impl := (^File_Impl)(f.impl) return uintptr(impl.fd) } _name :: proc(f: ^File) -> string { - return (^_File)(f.impl).name if f != nil && f.impl != nil else "" + return (^File_Impl)(f.impl).name if f != nil && f.impl != nil else "" } -_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) { - impl := (^_File)(f.impl) - n, errno := linux.lseek(impl.fd, offset, linux.Seek_Whence(whence)) +_seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) { + n, errno := linux.lseek(f.fd, offset, linux.Seek_Whence(whence)) if errno != .NONE { return -1, _get_platform_error(errno) } return n, nil } -_read :: proc(f: ^File, p: []byte) -> (i64, Error) { +_read :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) { if len(p) == 0 { return 0, nil } - impl := (^_File)(f.impl) - n, errno := linux.read(impl.fd, p[:]) + n, errno := linux.read(f.fd, p[:]) if errno != .NONE { return -1, _get_platform_error(errno) } return i64(n), n == 0 ? io.Error.EOF : nil } -_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) { +_read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) { if offset < 0 { return 0, .Invalid_Offset } - impl := (^_File)(f.impl) - n, errno := linux.pread(impl.fd, p[:], offset) + n, errno := linux.pread(f.fd, p[:], offset) if errno != .NONE { return -1, _get_platform_error(errno) } @@ -177,35 +174,31 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) { return i64(n), nil } -_write :: proc(f: ^File, p: []byte) -> (i64, Error) { +_write :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) { if len(p) == 0 { return 0, nil } - impl := (^_File)(f.impl) - n, errno := linux.write(impl.fd, p[:]) + n, errno := linux.write(f.fd, p[:]) if errno != .NONE { return -1, _get_platform_error(errno) } return i64(n), nil } -_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) { +_write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) { if offset < 0 { return 0, .Invalid_Offset } - - impl := (^_File)(f.impl) - n, errno := linux.pwrite(impl.fd, p[:], offset) + n, errno := linux.pwrite(f.fd, p[:], offset) if errno != .NONE { return -1, _get_platform_error(errno) } return i64(n), nil } -_file_size :: proc(f: ^File) -> (n: i64, err: Error) { +_file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) { s: linux.Stat = --- - impl := (^_File)(f.impl) - errno := linux.fstat(impl.fd, &s) + errno := linux.fstat(f.fd, &s) if errno != .NONE { return -1, _get_platform_error(errno) } @@ -213,17 +206,16 @@ _file_size :: proc(f: ^File) -> (n: i64, err: Error) { } _sync :: proc(f: ^File) -> Error { - impl := (^_File)(f.impl) + impl := (^File_Impl)(f.impl) return _get_platform_error(linux.fsync(impl.fd)) } -_flush :: proc(f: ^File) -> Error { - impl := (^_File)(f.impl) - return _get_platform_error(linux.fsync(impl.fd)) +_flush :: proc(f: ^File_Impl) -> Error { + return _get_platform_error(linux.fsync(f.fd)) } _truncate :: proc(f: ^File, size: i64) -> Error { - impl := (^_File)(f.impl) + impl := (^File_Impl)(f.impl) return _get_platform_error(linux.ftruncate(impl.fd, size)) } @@ -300,7 +292,7 @@ _chdir :: proc(name: string) -> Error { } _fchdir :: proc(f: ^File) -> Error { - impl := (^_File)(f.impl) + impl := (^File_Impl)(f.impl) return _get_platform_error(linux.fchdir(impl.fd)) } @@ -311,7 +303,7 @@ _chmod :: proc(name: string, mode: File_Mode) -> Error { } _fchmod :: proc(f: ^File, mode: File_Mode) -> Error { - impl := (^_File)(f.impl) + impl := (^File_Impl)(f.impl) return _get_platform_error(linux.fchmod(impl.fd, transmute(linux.Mode)(u32(mode)))) } @@ -331,7 +323,7 @@ _lchown :: proc(name: string, uid, gid: int) -> Error { // NOTE: will throw error without super user priviledges _fchown :: proc(f: ^File, uid, gid: int) -> Error { - impl := (^_File)(f.impl) + impl := (^File_Impl)(f.impl) return _get_platform_error(linux.fchown(impl.fd, linux.Uid(uid), linux.Gid(gid))) } @@ -362,7 +354,7 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error { uint(mtime._nsec) % uint(time.Second), }, } - impl := (^_File)(f.impl) + impl := (^File_Impl)(f.impl) return _get_platform_error(linux.utimensat(impl.fd, nil, ×[0], nil)) } @@ -455,7 +447,7 @@ _read_entire_pseudo_file_cstring :: proc(name: cstring, allocator: runtime.Alloc @(private="package") _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { - f := (^File)(stream_data) + f := (^File_Impl)(stream_data) ferr: Error switch mode { case .Read: diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 0ccea7f96..b11e7745f 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -17,19 +17,19 @@ _ERROR_BAD_NETPATH :: 53 MAX_RW :: 1<<30 -_File_Kind :: enum u8 { +File_Impl_Kind :: enum u8 { File, Console, Pipe, } -_File :: struct { - using file: File, +File_Impl :: struct { + file: File, fd: rawptr, name: string, wname: win32.wstring, - kind: _File_Kind, + kind: File_Impl_Kind, allocator: runtime.Allocator, @@ -132,79 +132,81 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File { if handle == INVALID_HANDLE { return nil } - f := new(_File, file_allocator()) + impl := new(File_Impl, file_allocator()) + impl.file.impl = impl - f.allocator = file_allocator() - f.fd = rawptr(handle) - f.name, _ = clone_string(name, f.allocator) - f.wname = win32.utf8_to_wstring(name, f.allocator) + impl.allocator = file_allocator() + impl.fd = rawptr(handle) + impl.name, _ = clone_string(name, impl.allocator) + impl.wname = win32.utf8_to_wstring(name, impl.allocator) - handle := _handle(f) - kind := _File_Kind.File + handle := _handle(&impl.file) + kind := File_Impl_Kind.File if m: u32; win32.GetConsoleMode(handle, &m) { kind = .Console } if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE { kind = .Pipe } - f.kind = kind + impl.kind = kind - f.stream = { - data = f, + impl.file.stream = { + data = impl, procedure = _file_stream_proc, } - f.fstat = _fstat + impl.file.fstat = _fstat - return f + return &impl.file } _fd :: proc(f: ^File) -> uintptr { if f == nil || f.impl == nil { return INVALID_HANDLE } - return uintptr((^_File)(f.impl).fd) + return uintptr((^File_Impl)(f.impl).fd) } -_destroy :: proc(f: ^File) -> Error { - if f == nil || f.impl == nil { +_destroy :: proc(f: ^File_Impl) -> Error { + if f == nil { return nil } - _f := (^_File)(f.impl) - a := _f.allocator - free(_f.wname, a) - delete(_f.name, a) - free(_f, a) + a := f.allocator + err0 := free(f.wname, a) + err1 := delete(f.name, a) + err2 := free(f, a) + err0 or_return + err1 or_return + err2 or_return return nil } -_close :: proc(f: ^File) -> Error { - if f == nil || f.impl == nil { +_close :: proc(f: ^File_Impl) -> Error { + if f == nil { return nil } - if !win32.CloseHandle(win32.HANDLE((^_File)(f.impl).fd)) { + if !win32.CloseHandle(win32.HANDLE(f.fd)) { return .Closed } return _destroy(f) } _name :: proc(f: ^File) -> string { - return (^_File)(f.impl).name if f != nil && f.impl != nil else "" + return (^File_Impl)(f.impl).name if f != nil && f.impl != nil else "" } -_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) { - handle := _handle(f) +_seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) { + handle := _handle(&f.file) if handle == win32.INVALID_HANDLE { return 0, .Invalid_File } - impl := (^_File)(f.impl) - if impl.kind == .Pipe { + if f.kind == .Pipe { return 0, .Invalid_File } - sync.guard(&impl.rw_mutex) + sync.guard(&f.rw_mutex) w: u32 switch whence { @@ -222,7 +224,7 @@ _seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Er return i64(hi)<<32 + i64(dw_ptr), nil } -_read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) { +_read :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) { read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) { if len(b) == 0 { return 0, nil @@ -273,19 +275,18 @@ _read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) { return } - handle := _handle(f) + handle := _handle(&f.file) single_read_length: win32.DWORD total_read: int length := len(p) - impl := (^_File)(f.impl) - sync.shared_guard(&impl.rw_mutex) // multiple readers + sync.shared_guard(&f.rw_mutex) // multiple readers - if sync.guard(&impl.p_mutex) { + if sync.guard(&f.p_mutex) { to_read := min(win32.DWORD(length), MAX_RW) ok: win32.BOOL - if impl.kind == .Console { + if f.kind == .Console { n, cerr := read_console(handle, p[total_read:][:to_read]) total_read += n if cerr != nil { @@ -305,15 +306,15 @@ _read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) { return i64(total_read), err } -_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) { - pread :: proc(f: ^File, data: []byte, offset: i64) -> (n: i64, err: Error) { +_read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (n: i64, err: Error) { + pread :: proc(f: ^File_Impl, data: []byte, offset: i64) -> (n: i64, err: Error) { buf := data if len(buf) > MAX_RW { buf = buf[:MAX_RW] } - curr_offset := seek(f, offset, .Current) or_return - defer seek(f, curr_offset, .Start) + curr_offset := _seek(f, offset, .Current) or_return + defer _seek(f, curr_offset, .Start) o := win32.OVERLAPPED{ OffsetHigh = u32(offset>>32), @@ -322,7 +323,7 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) { // TODO(bill): Determine the correct behaviour for consoles - h := _handle(f) + h := _handle(&f.file) done: win32.DWORD if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) { err = _get_platform_error() @@ -332,8 +333,7 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) { return } - impl := (^_File)(f.impl) - sync.guard(&impl.p_mutex) + sync.guard(&f.p_mutex) p, offset := p, offset for len(p) > 0 { @@ -345,7 +345,7 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) { return } -_write :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) { +_write :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) { if len(p) == 0 { return } @@ -354,10 +354,9 @@ _write :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) { total_write: i64 length := i64(len(p)) - handle := _handle(f) + handle := _handle(&f.file) - impl := (^_File)(f.impl) - sync.guard(&impl.rw_mutex) + sync.guard(&f.rw_mutex) for total_write < length { remaining := length - total_write to_write := win32.DWORD(min(i32(remaining), MAX_RW)) @@ -373,22 +372,22 @@ _write :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) { return i64(total_write), nil } -_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) { - pwrite :: proc(f: ^File, data: []byte, offset: i64) -> (n: i64, err: Error) { +_write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (n: i64, err: Error) { + pwrite :: proc(f: ^File_Impl, data: []byte, offset: i64) -> (n: i64, err: Error) { buf := data if len(buf) > MAX_RW { buf = buf[:MAX_RW] } - curr_offset := seek(f, offset, .Current) or_return - defer seek(f, curr_offset, .Start) + curr_offset := _seek(f, offset, .Current) or_return + defer _seek(f, curr_offset, .Start) o := win32.OVERLAPPED{ OffsetHigh = u32(offset>>32), Offset = u32(offset), } - h := _handle(f) + h := _handle(&f.file) done: win32.DWORD if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) { err = _get_platform_error() @@ -398,8 +397,7 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) { return } - impl := (^_File)(f.impl) - sync.guard(&impl.p_mutex) + sync.guard(&f.p_mutex) p, offset := p, offset for len(p) > 0 { m := pwrite(f, p, offset) or_return @@ -410,13 +408,12 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) { return } -_file_size :: proc(f: ^File) -> (n: i64, err: Error) { +_file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) { length: win32.LARGE_INTEGER - impl := (^_File)(f.impl) - if impl.kind == .Pipe { + if f.kind == .Pipe { return 0, .No_Size } - handle := _handle(f) + handle := _handle(&f.file) if !win32.GetFileSizeEx(handle, &length) { err = _get_platform_error() } @@ -426,11 +423,14 @@ _file_size :: proc(f: ^File) -> (n: i64, err: Error) { _sync :: proc(f: ^File) -> Error { - return _flush(f) + if f != nil && f.impl != nil { + return _flush((^File_Impl)(f.impl)) + } + return nil } -_flush :: proc(f: ^File) -> Error { - handle := _handle(f) +_flush :: proc(f: ^File_Impl) -> Error { + handle := _handle(&f.file) if !win32.FlushFileBuffers(handle) { return _get_platform_error() } @@ -628,7 +628,7 @@ _fchdir :: proc(f: ^File) -> Error { if f == nil || f.impl == nil { return nil } - impl := (^_File)(f.impl) + impl := (^File_Impl)(f.impl) if !win32.SetCurrentDirectoryW(impl.wname) { return _get_platform_error() } @@ -747,7 +747,7 @@ _is_dir :: proc(path: string) -> bool { @(private="package") _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { - f := (^File)(stream_data) + f := (^File_Impl)(stream_data) ferr: Error switch mode { case .Read: diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin index a6df4e67a..ee4011f96 100644 --- a/core/os/os2/stat_linux.odin +++ b/core/os/os2/stat_linux.odin @@ -7,7 +7,7 @@ import "core:sys/linux" import "core:path/filepath" _fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { - impl := (^_File)(f.impl) + impl := (^File_Impl)(f.impl) return _fstat_internal(impl.fd, allocator) } diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index 5f7daa062..0a18433c1 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -7,7 +7,7 @@ import "core:strings" import win32 "core:sys/windows" _fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { - if f == nil || (^_File)(f.impl).fd == nil { + if f == nil || (^File_Impl)(f.impl).fd == nil { return {}, nil } @@ -122,7 +122,7 @@ _cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) { - if f == nil || (^_File)(f.impl).fd == nil { + if f == nil { return "", nil } h := _handle(f) @@ -138,7 +138,7 @@ _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (strin } _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) { - if f == nil || (^_File)(f.impl).fd == nil { + if f == nil { return nil, nil } h := _handle(f) From 556355ef054bc5139a24f8b5dbd210049e908c95 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 15:30:40 +0100 Subject: [PATCH 30/34] Disallow global use of target specific procedure calls --- src/check_expr.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4edd34990..3b1f86114 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7904,12 +7904,15 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c // NOTE: Due to restrictions in LLVM you can not inline calls with a superset of features. if (is_call_inlined) { - GB_ASSERT(c->curr_proc_decl); - GB_ASSERT(c->curr_proc_decl->entity); - GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc); - String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature; - if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) { - error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid)); + if (c->curr_proc_decl == nullptr) { + error(call, "Inlined procedure which enables target feature '%.*s' cannot be used at the global/file scope", LIT(invalid)); + } else { + GB_ASSERT(c->curr_proc_decl->entity); + GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc); + String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature; + if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) { + error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid)); + } } } } From 11e2aa2d519a9522bc6a71abf4868216ed236c89 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 15:31:40 +0100 Subject: [PATCH 31/34] Improve error message --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3b1f86114..82f64738f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7905,7 +7905,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c // NOTE: Due to restrictions in LLVM you can not inline calls with a superset of features. if (is_call_inlined) { if (c->curr_proc_decl == nullptr) { - error(call, "Inlined procedure which enables target feature '%.*s' cannot be used at the global/file scope", LIT(invalid)); + error(call, "Calling a '#force_inline' procedure that enables target features is not allowed at file scope"); } else { GB_ASSERT(c->curr_proc_decl->entity); GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc); From a8673784180968f7552d26705582d6c938e97a5e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 15:55:26 +0100 Subject: [PATCH 32/34] Default to `-o:minimal` again --- src/build_settings.cpp | 6 +++++- src/llvm_backend.cpp | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 4d3e20a7a..32640d732 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1649,7 +1649,11 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta if (!bc->custom_optimization_level) { // NOTE(bill): when building with `-debug` but not specifying an optimization level // default to `-o:none` to improve the debug symbol generation by default - bc->optimization_level = -1; // -o:none + if (bc->ODIN_DEBUG) { + bc->optimization_level = -1; // -o:none + } else { + bc->optimization_level = 0; // -o:minimal + } } bc->optimization_level = gb_clamp(bc->optimization_level, -1, 3); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 52661dfa7..ae46186ed 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1570,6 +1570,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { switch (build_context.optimization_level) { case -1: + array_add(&passes, "function(annotation-remarks)"); break; case 0: array_add(&passes, "always-inline"); From 6feace23511eaf5f9fa981e8fbfd3087331a0cc4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 16:05:07 +0100 Subject: [PATCH 33/34] Add pseudo flag `-fast-build` --- src/main.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index e6a0aecf0..96225d14d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -399,6 +399,8 @@ enum BuildFlagKind { BuildFlag_Sanitize, + BuildFlag_FastBuild, + #if defined(GB_SYSTEM_WINDOWS) BuildFlag_IgnoreVsSearch, BuildFlag_ResourceFile, @@ -605,6 +607,9 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_Sanitize, str_lit("sanitize"), BuildFlagParam_String, Command__does_build, true); + add_flag(&build_flags, BuildFlag_FastBuild, str_lit("fast-build"), BuildFlagParam_String, Command__does_build); + + #if defined(GB_SYSTEM_WINDOWS) add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build); @@ -1441,6 +1446,13 @@ gb_internal bool parse_build_flags(Array args) { } break; + + case BuildFlag_FastBuild: + build_context.custom_optimization_level = true; + build_context.optimization_level = -1; + build_context.use_separate_modules = true; + break; + #if defined(GB_SYSTEM_WINDOWS) case BuildFlag_IgnoreVsSearch: { GB_ASSERT(value.kind == ExactValue_Invalid); From e7d37607ef9ce54a80d83230150874b71d628d6d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 16:05:47 +0100 Subject: [PATCH 34/34] Fix parameter to none --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 96225d14d..388184be9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -607,7 +607,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_Sanitize, str_lit("sanitize"), BuildFlagParam_String, Command__does_build, true); - add_flag(&build_flags, BuildFlag_FastBuild, str_lit("fast-build"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_FastBuild, str_lit("fast-build"), BuildFlagParam_None, Command__does_build); #if defined(GB_SYSTEM_WINDOWS)