aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRilleP <rikkypikki@hotmail.com>2024-04-10 19:10:33 +0200
committerGitHub <noreply@github.com>2024-04-10 19:10:33 +0200
commit95a38d5a96322cd9adbb03bd5b7425b93469e62f (patch)
tree8cdc3b962506ddc4b7b1883f0e2ecb9dce54e2f9 /src
parent239d4e10762a12e96280bd91003acbf2170cadf2 (diff)
parent13e459980b0143b49762cdfd04dd5cf1bbf83daa (diff)
Merge branch 'master' into parsing-package-fixes
Diffstat (limited to 'src')
-rw-r--r--src/array.cpp7
-rw-r--r--src/bug_report.cpp2134
-rw-r--r--src/build_settings.cpp245
-rw-r--r--src/check_builtin.cpp229
-rw-r--r--src/check_decl.cpp6
-rw-r--r--src/check_expr.cpp694
-rw-r--r--src/check_stmt.cpp195
-rw-r--r--src/check_type.cpp1063
-rw-r--r--src/checker.cpp364
-rw-r--r--src/checker.hpp30
-rw-r--r--src/checker_builtin_procs.hpp24
-rw-r--r--src/common.cpp23
-rw-r--r--src/docs.cpp4
-rw-r--r--src/docs_format.cpp25
-rw-r--r--src/docs_writer.cpp41
-rw-r--r--src/entity.cpp27
-rw-r--r--src/error.cpp352
-rw-r--r--src/gb/gb.h62
-rw-r--r--src/linker.cpp131
-rw-r--r--src/llvm_abi.cpp101
-rw-r--r--src/llvm_backend.cpp95
-rw-r--r--src/llvm_backend.hpp18
-rw-r--r--src/llvm_backend_const.cpp48
-rw-r--r--src/llvm_backend_debug.cpp958
-rw-r--r--src/llvm_backend_expr.cpp243
-rw-r--r--src/llvm_backend_general.cpp107
-rw-r--r--src/llvm_backend_opt.cpp19
-rw-r--r--src/llvm_backend_proc.cpp133
-rw-r--r--src/llvm_backend_stmt.cpp126
-rw-r--r--src/llvm_backend_type.cpp1006
-rw-r--r--src/llvm_backend_utility.cpp72
-rw-r--r--src/main.cpp97
-rw-r--r--src/parser.cpp307
-rw-r--r--src/parser.hpp24
-rw-r--r--src/parser_pos.cpp3
-rw-r--r--src/path.cpp922
-rw-r--r--src/string.cpp23
-rw-r--r--src/threading.cpp267
-rw-r--r--src/tilde.cpp1
-rw-r--r--src/timings.cpp23
-rw-r--r--src/tokenizer.cpp1
-rw-r--r--src/types.cpp382
42 files changed, 6488 insertions, 4144 deletions
diff --git a/src/array.cpp b/src/array.cpp
index 4583a31a9..ec2c97d0e 100644
--- a/src/array.cpp
+++ b/src/array.cpp
@@ -52,6 +52,13 @@ template <typename T> gb_internal T *array_end_ptr(Array<T> *array);
template <typename T>
+gb_internal void array_sort(Array<T> &array, gbCompareProc compare_proc) {
+ gb_sort_array(array.data, array.count, compare_proc);
+}
+
+
+
+template <typename T>
struct Slice {
T *data;
isize count;
diff --git a/src/bug_report.cpp b/src/bug_report.cpp
index ac3805919..e919ba67b 100644
--- a/src/bug_report.cpp
+++ b/src/bug_report.cpp
@@ -1,1061 +1,1073 @@
-/*
- Gather and print platform and version info to help with reporting Odin bugs.
-*/
-
-#if !defined(GB_COMPILER_MSVC)
- #if defined(GB_CPU_X86)
- #include <cpuid.h>
- #endif
-#endif
-
-#if defined(GB_SYSTEM_LINUX)
- #include <sys/utsname.h>
- #include <sys/sysinfo.h>
-#endif
-
-#if defined(GB_SYSTEM_OSX)
- #include <sys/sysctl.h>
-#endif
-
-#if defined(GB_SYSTEM_OPENBSD)
- #include <sys/sysctl.h>
- #include <sys/utsname.h>
-#endif
-
-#if defined(GB_SYSTEM_FREEBSD)
- #include <sys/sysctl.h>
-#endif
-
-/*
- NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
-*/
-#if defined(GB_SYSTEM_WINDOWS)
-gb_internal void report_windows_product_type(DWORD ProductType) {
- switch (ProductType) {
- case PRODUCT_ULTIMATE:
- gb_printf("Ultimate");
- break;
-
- case PRODUCT_HOME_BASIC:
- gb_printf("Home Basic");
- break;
-
- case PRODUCT_HOME_PREMIUM:
- gb_printf("Home Premium");
- break;
-
- case PRODUCT_ENTERPRISE:
- gb_printf("Enterprise");
- break;
-
- case PRODUCT_CORE:
- gb_printf("Home Basic");
- break;
-
- case PRODUCT_HOME_BASIC_N:
- gb_printf("Home Basic N");
- break;
-
- case PRODUCT_EDUCATION:
- gb_printf("Education");
- break;
-
- case PRODUCT_EDUCATION_N:
- gb_printf("Education N");
- break;
-
- case PRODUCT_BUSINESS:
- gb_printf("Business");
- break;
-
- case PRODUCT_STANDARD_SERVER:
- gb_printf("Standard Server");
- break;
-
- case PRODUCT_DATACENTER_SERVER:
- gb_printf("Datacenter");
- break;
-
- case PRODUCT_SMALLBUSINESS_SERVER:
- gb_printf("Windows Small Business Server");
- break;
-
- case PRODUCT_ENTERPRISE_SERVER:
- gb_printf("Enterprise Server");
- break;
-
- case PRODUCT_STARTER:
- gb_printf("Starter");
- break;
-
- case PRODUCT_DATACENTER_SERVER_CORE:
- gb_printf("Datacenter Server Core");
- break;
-
- case PRODUCT_STANDARD_SERVER_CORE:
- gb_printf("Server Standard Core");
- break;
-
- case PRODUCT_ENTERPRISE_SERVER_CORE:
- gb_printf("Enterprise Server Core");
- break;
-
- case PRODUCT_BUSINESS_N:
- gb_printf("Business N");
- break;
-
- case PRODUCT_HOME_SERVER:
- gb_printf("Home Server");
- break;
-
- case PRODUCT_SERVER_FOR_SMALLBUSINESS:
- gb_printf("Windows Server 2008 for Windows Essential Server Solutions");
- break;
-
- case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
- gb_printf("Small Business Server Premium");
- break;
-
- case PRODUCT_HOME_PREMIUM_N:
- gb_printf("Home Premium N");
- break;
-
- case PRODUCT_ENTERPRISE_N:
- gb_printf("Enterprise N");
- break;
-
- case PRODUCT_ULTIMATE_N:
- gb_printf("Ultimate N");
- break;
-
- case PRODUCT_HYPERV:
- gb_printf("HyperV");
- break;
-
- case PRODUCT_STARTER_N:
- gb_printf("Starter N");
- break;
-
- case PRODUCT_PROFESSIONAL:
- gb_printf("Professional");
- break;
-
- case PRODUCT_PROFESSIONAL_N:
- gb_printf("Professional N");
- break;
-
- case PRODUCT_UNLICENSED:
- gb_printf("Unlicensed");
- break;
-
- default:
- gb_printf("Unknown Edition (%08x)", cast(unsigned)ProductType);
- }
-}
-#endif
-
-gb_internal void odin_cpuid(int leaf, int result[]) {
- #if defined(GB_CPU_ARM)
- return;
-
- #elif defined(GB_CPU_X86)
-
- #if defined(GB_COMPILER_MSVC)
- __cpuid(result, leaf);
- #else
- __get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]);
- #endif
-
- #endif
-}
-
-gb_internal void report_cpu_info() {
- gb_printf("\tCPU: ");
-
- #if defined(GB_CPU_X86)
-
- /*
- Get extended leaf info
- */
- int cpu[4];
-
- odin_cpuid(0x80000000, &cpu[0]);
- int number_of_extended_ids = cpu[0];
-
- int brand[0x12] = {};
-
- /*
- Read CPU brand if supported.
- */
- if (number_of_extended_ids >= 0x80000004) {
- odin_cpuid(0x80000002, &brand[0]);
- odin_cpuid(0x80000003, &brand[4]);
- odin_cpuid(0x80000004, &brand[8]);
-
- /*
- Some CPUs like ` Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz` may include leading spaces. Trim them.
- */
- char * brand_name = (char *)&brand[0];
- for (; brand_name[0] == ' '; brand_name++) {}
-
- gb_printf("%s\n", brand_name);
- } else {
- gb_printf("Unable to retrieve.\n");
- }
-
- #elif defined(GB_CPU_ARM)
- /*
- TODO(Jeroen): On *nix, perhaps query `/proc/cpuinfo`.
- */
- #if defined(GB_ARCH_64_BIT)
- gb_printf("ARM64\n");
- #else
- gb_printf("ARM\n");
- #endif
- #else
- gb_printf("Unknown\n");
- #endif
-}
-
-/*
- Report the amount of installed RAM.
-*/
-gb_internal void report_ram_info() {
- gb_printf("\tRAM: ");
-
- #if defined(GB_SYSTEM_WINDOWS)
- MEMORYSTATUSEX statex;
- statex.dwLength = sizeof(statex);
- GlobalMemoryStatusEx (&statex);
-
- gb_printf("%lld MiB\n", statex.ullTotalPhys / gb_megabytes(1));
-
- #elif defined(GB_SYSTEM_LINUX)
- /*
- Retrieve RAM info using `sysinfo()`,
- */
- struct sysinfo info;
- int result = sysinfo(&info);
-
- if (result == 0x0) {
- gb_printf("%lu MiB\n", info.totalram * info.mem_unit / gb_megabytes(1));
- } else {
- gb_printf("Unknown.\n");
- }
- #elif defined(GB_SYSTEM_OSX)
- uint64_t ram_amount;
- size_t val_size = sizeof(ram_amount);
-
- int mibs[] = { CTL_HW, HW_MEMSIZE };
- if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
- gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
- }
- #elif defined(GB_SYSTEM_OPENBSD)
- uint64_t ram_amount;
- size_t val_size = sizeof(ram_amount);
-
- int mibs[] = { CTL_HW, HW_PHYSMEM64 };
- if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
- gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
- }
- #elif defined(GB_SYSTEM_FREEBSD)
- uint64_t ram_amount;
- size_t val_size = sizeof(ram_amount);
-
- int mibs[] = { CTL_HW, HW_PHYSMEM };
- if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
- gb_printf("%lu MiB\n", ram_amount / gb_megabytes(1));
- }
- #else
- gb_printf("Unknown.\n");
- #endif
-}
-
-gb_internal void report_os_info() {
- gb_printf("\tOS: ");
-
- #if defined(GB_SYSTEM_WINDOWS)
- /*
- NOTE(Jeroen):
- `GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10.
- `RtlGetVersion` will return the true version.
-
- Rather than include the WinDDK, we ask the kernel directly.
-
- `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release)
-
- */
- OSVERSIONINFOEXW osvi;
- ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW));
- osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
-
- typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(OSVERSIONINFOW*);
- typedef BOOL (WINAPI* GetProductInfoPtr)(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion, PDWORD pdwReturnedProductType);
-
- // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion
- RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlGetVersion");
- // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo
- GetProductInfoPtr GetProductInfo = (GetProductInfoPtr)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo");
-
- NTSTATUS status = {};
- DWORD ProductType = {};
- if (RtlGetVersion != nullptr) {
- status = RtlGetVersion((OSVERSIONINFOW*)&osvi);
- }
-
- if (RtlGetVersion == nullptr || status != 0x0) {
- gb_printf("Windows (Unknown Version)");
- } else {
- if (GetProductInfo != nullptr) {
- GetProductInfo(osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &ProductType);
- }
-
- if (false) {
- gb_printf("dwMajorVersion: %u\n", cast(unsigned)osvi.dwMajorVersion);
- gb_printf("dwMinorVersion: %u\n", cast(unsigned)osvi.dwMinorVersion);
- gb_printf("dwBuildNumber: %u\n", cast(unsigned)osvi.dwBuildNumber);
- gb_printf("dwPlatformId: %u\n", cast(unsigned)osvi.dwPlatformId);
- gb_printf("wServicePackMajor: %u\n", cast(unsigned)osvi.wServicePackMajor);
- gb_printf("wServicePackMinor: %u\n", cast(unsigned)osvi.wServicePackMinor);
- gb_printf("wSuiteMask: %u\n", cast(unsigned)osvi.wSuiteMask);
- gb_printf("wProductType: %u\n", cast(unsigned)osvi.wProductType);
- }
-
- gb_printf("Windows ");
-
- switch (osvi.dwMajorVersion) {
- case 10:
- /*
- Windows 10 (Pro), Windows 2016 Server, Windows 2019 Server, Windows 2022 Server
- */
- switch (osvi.wProductType) {
- case VER_NT_WORKSTATION: // Workstation
- if (osvi.dwBuildNumber < 22000) {
- gb_printf("10 ");
- } else {
- gb_printf("11 ");
- }
-
- report_windows_product_type(ProductType);
-
- break;
- default: // Server or Domain Controller
- switch(osvi.dwBuildNumber) {
- case 14393:
- gb_printf("2016 Server");
- break;
- case 17763:
- gb_printf("2019 Server");
- break;
- case 20348:
- gb_printf("2022 Server");
- break;
- default:
- gb_printf("Unknown Server");
- break;
- }
- }
- break;
- case 6:
- switch (osvi.dwMinorVersion) {
- case 0:
- switch (osvi.wProductType) {
- case VER_NT_WORKSTATION:
- gb_printf("Windows Vista ");
- report_windows_product_type(ProductType);
- break;
- case 3:
- gb_printf("Windows Server 2008");
- break;
- }
- break;
-
- case 1:
- switch (osvi.wProductType) {
- case VER_NT_WORKSTATION:
- gb_printf("Windows 7 ");
- report_windows_product_type(ProductType);
- break;
- case 3:
- gb_printf("Windows Server 2008 R2");
- break;
- }
- break;
- case 2:
- switch (osvi.wProductType) {
- case VER_NT_WORKSTATION:
- gb_printf("Windows 8 ");
- report_windows_product_type(ProductType);
- break;
- case 3:
- gb_printf("Windows Server 2012");
- break;
- }
- break;
- case 3:
- switch (osvi.wProductType) {
- case VER_NT_WORKSTATION:
- gb_printf("Windows 8.1 ");
- report_windows_product_type(ProductType);
- break;
- case 3:
- gb_printf("Windows Server 2012 R2");
- break;
- }
- break;
- }
- break;
- case 5:
- switch (osvi.dwMinorVersion) {
- case 0:
- gb_printf("Windows 2000");
- break;
- case 1:
- gb_printf("Windows XP");
- break;
- case 2:
- gb_printf("Windows Server 2003");
- break;
- }
- break;
- default:
- break;
- }
-
- /*
- Grab Windows DisplayVersion (like 20H02)
- */
- LPDWORD ValueType = {};
- DWORD UBR;
- char DisplayVersion[256];
- DWORD ValueSize = 256;
-
- status = RegGetValue(
- HKEY_LOCAL_MACHINE,
- TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
- TEXT("DisplayVersion"),
- RRF_RT_REG_SZ,
- ValueType,
- DisplayVersion,
- &ValueSize
- );
-
- if (status == 0x0) {
- gb_printf(" (version: %s)", DisplayVersion);
- }
-
- /*
- Now print build number.
- */
- gb_printf(", build %u", cast(unsigned)osvi.dwBuildNumber);
-
- ValueSize = sizeof(UBR);
- status = RegGetValue(
- HKEY_LOCAL_MACHINE,
- TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
- TEXT("UBR"),
- RRF_RT_REG_DWORD,
- ValueType,
- &UBR,
- &ValueSize
- );
-
- if (status == 0x0) {
- gb_printf(".%u", cast(unsigned)UBR);
- }
- gb_printf("\n");
- }
- #elif defined(GB_SYSTEM_LINUX)
- /*
- Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
- */
- gbAllocator a = heap_allocator();
-
- gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release");
- defer (gb_file_free_contents(&release));
-
- b32 found = 0;
- if (release.size) {
- char *start = (char *)release.data;
- char *end = (char *)release.data + release.size;
- const char *needle = "PRETTY_NAME=\"";
- isize needle_len = gb_strlen((needle));
-
- char *c = start;
- for (; c < end; c++) {
- if (gb_strncmp(c, needle, needle_len) == 0) {
- found = 1;
- start = c + needle_len;
- break;
- }
- }
-
- if (found) {
- for (c = start; c < end; c++) {
- if (*c == '"') {
- // Found the closing quote. Replace it with \0
- *c = 0;
- gb_printf("%s", (char *)start);
- break;
- } else if (*c == '\n') {
- found = 0;
- }
- }
- }
- }
-
- if (!found) {
- gb_printf("Unknown Linux Distro");
- }
-
- /*
- Print kernel info using `uname()` syscall, https://linux.die.net/man/2/uname
- */
- char buffer[1024];
- uname((struct utsname *)&buffer[0]);
-
- struct utsname *info;
- info = (struct utsname *)&buffer[0];
-
- gb_printf(", %s %s\n", info->sysname, info->release);
-
- #elif defined(GB_SYSTEM_OSX)
- struct Darwin_To_Release {
- const char* build; // 21G83
- int darwin[3]; // Darwin kernel triplet
- const char* os_name; // OS X, MacOS
- struct {
- const char* name; // Monterey, Mojave, etc.
- int version[3]; // 12.4, etc.
- } release;
- };
-
- Darwin_To_Release macos_release_map[] = {
- {"8A428", { 8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}},
- {"8A432", { 8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}},
- {"8B15", { 8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}},
- {"8B17", { 8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}},
- {"8C46", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8C47", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8E102", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8E45", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8E90", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8F46", { 8, 3, 0}, "macOS", {"Tiger", {10, 4, 3}}},
- {"8G32", { 8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}},
- {"8G1165", { 8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}},
- {"8H14", { 8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}},
- {"8G1454", { 8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}},
- {"8I127", { 8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}},
- {"8I1119", { 8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}},
- {"8J135", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- {"8J2135a", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- {"8K1079", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- {"8N5107", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- {"8L127", { 8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}},
- {"8L2127", { 8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}},
- {"8P135", { 8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}},
- {"8P2137", { 8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}},
- {"8R218", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
- {"8R2218", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
- {"8R2232", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
- {"8S165", { 8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}},
- {"8S2167", { 8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}},
- {"9A581", { 9, 0, 0}, "macOS", {"Leopard", {10, 5, 0}}},
- {"9B18", { 9, 1, 0}, "macOS", {"Leopard", {10, 5, 1}}},
- {"9B2117", { 9, 1, 1}, "macOS", {"Leopard", {10, 5, 1}}},
- {"9C31", { 9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}},
- {"9C7010", { 9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}},
- {"9D34", { 9, 3, 0}, "macOS", {"Leopard", {10, 5, 3}}},
- {"9E17", { 9, 4, 0}, "macOS", {"Leopard", {10, 5, 4}}},
- {"9F33", { 9, 5, 0}, "macOS", {"Leopard", {10, 5, 5}}},
- {"9G55", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
- {"9G66", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
- {"9G71", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
- {"9J61", { 9, 7, 0}, "macOS", {"Leopard", {10, 5, 7}}},
- {"9L30", { 9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}},
- {"9L34", { 9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}},
- {"10A432", {10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}},
- {"10A433", {10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}},
- {"10B504", {10, 1, 0}, "macOS", {"Snow Leopard", {10, 6, 1}}},
- {"10C540", {10, 2, 0}, "macOS", {"Snow Leopard", {10, 6, 2}}},
- {"10D573", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
- {"10D575", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
- {"10D578", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
- {"10F569", {10, 4, 0}, "macOS", {"Snow Leopard", {10, 6, 4}}},
- {"10H574", {10, 5, 0}, "macOS", {"Snow Leopard", {10, 6, 5}}},
- {"10J567", {10, 6, 0}, "macOS", {"Snow Leopard", {10, 6, 6}}},
- {"10J869", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
- {"10J3250", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
- {"10J4138", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
- {"10K540", {10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}},
- {"10K549", {10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}},
- {"11A511", {11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}},
- {"11A511s", {11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}},
- {"11A2061", {11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}},
- {"11A2063", {11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}},
- {"11B26", {11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}},
- {"11B2118", {11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}},
- {"11C74", {11, 2, 0}, "macOS", {"Lion", {10, 7, 2}}},
- {"11D50", {11, 3, 0}, "macOS", {"Lion", {10, 7, 3}}},
- {"11E53", {11, 4, 0}, "macOS", {"Lion", {10, 7, 4}}},
- {"11G56", {11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}},
- {"11G63", {11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}},
- {"12A269", {12, 0, 0}, "macOS", {"Mountain Lion", {10, 8, 0}}},
- {"12B19", {12, 1, 0}, "macOS", {"Mountain Lion", {10, 8, 1}}},
- {"12C54", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- {"12C60", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- {"12C2034", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- {"12C3104", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- {"12D78", {12, 3, 0}, "macOS", {"Mountain Lion", {10, 8, 3}}},
- {"12E55", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
- {"12E3067", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
- {"12E4022", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
- {"12F37", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F45", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F2501", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F2518", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F2542", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F2560", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"13A603", {13, 0, 0}, "macOS", {"Mavericks", {10, 9, 0}}},
- {"13B42", {13, 0, 0}, "macOS", {"Mavericks", {10, 9, 1}}},
- {"13C64", {13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}},
- {"13C1021", {13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}},
- {"13D65", {13, 2, 0}, "macOS", {"Mavericks", {10, 9, 3}}},
- {"13E28", {13, 3, 0}, "macOS", {"Mavericks", {10, 9, 4}}},
- {"13F34", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1066", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1077", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1096", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1112", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1134", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1507", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1603", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1712", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1808", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1911", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"14A389", {14, 0, 0}, "macOS", {"Yosemite", {10, 10, 0}}},
- {"14B25", {14, 0, 0}, "macOS", {"Yosemite", {10, 10, 1}}},
- {"14C109", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14C1510", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14C2043", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14C1514", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14C2513", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14D131", {14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}},
- {"14D136", {14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}},
- {"14E46", {14, 4, 0}, "macOS", {"Yosemite", {10, 10, 4}}},
- {"14F27", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1021", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1505", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1509", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1605", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1713", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1808", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1909", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1912", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2009", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2109", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2315", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2411", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2511", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"15A284", {15, 0, 0}, "macOS", {"El Capitan", {10, 11, 0}}},
- {"15B42", {15, 0, 0}, "macOS", {"El Capitan", {10, 11, 1}}},
- {"15C50", {15, 2, 0}, "macOS", {"El Capitan", {10, 11, 2}}},
- {"15D21", {15, 3, 0}, "macOS", {"El Capitan", {10, 11, 3}}},
- {"15E65", {15, 4, 0}, "macOS", {"El Capitan", {10, 11, 4}}},
- {"15F34", {15, 5, 0}, "macOS", {"El Capitan", {10, 11, 5}}},
- {"15G31", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1004", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1011", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1108", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1212", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1217", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1421", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1510", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1611", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G17023", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G18013", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G19009", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G20015", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G21013", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G22010", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"16A323", {16, 0, 0}, "macOS", {"Sierra", {10, 12, 0}}},
- {"16B2555", {16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}},
- {"16B2657", {16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}},
- {"16C67", {16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}},
- {"16C68", {16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}},
- {"16D32", {16, 4, 0}, "macOS", {"Sierra", {10, 12, 3}}},
- {"16E195", {16, 5, 0}, "macOS", {"Sierra", {10, 12, 4}}},
- {"16F73", {16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}},
- {"16F2073", {16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}},
- {"16G29", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1036", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1114", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1212", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1314", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1408", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1510", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1618", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1710", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1815", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1917", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1918", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G2016", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G2127", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G2128", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G2136", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"17A365", {17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}},
- {"17A405", {17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}},
- {"17B48", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
- {"17B1002", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
- {"17B1003", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
- {"17C88", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- {"17C89", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- {"17C205", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- {"17C2205", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- {"17D47", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- {"17D2047", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- {"17D102", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- {"17D2102", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- {"17E199", {17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}},
- {"17E202", {17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}},
- {"17F77", {17, 6, 0}, "macOS", {"High Sierra", {10, 13, 5}}},
- {"17G65", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G2208", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G2307", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G3025", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G4015", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G5019", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G6029", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G6030", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G7024", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G8029", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G8030", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G8037", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G9016", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G10021", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G11023", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G12034", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G13033", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G13035", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G14019", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G14033", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G14042", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"18A391", {18, 0, 0}, "macOS", {"Mojave", {10, 14, 0}}},
- {"18B75", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
- {"18B2107", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
- {"18B3094", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
- {"18C54", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 2}}},
- {"18D42", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
- {"18D43", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
- {"18D109", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
- {"18E226", {18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}},
- {"18E227", {18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}},
- {"18F132", {18, 6, 0}, "macOS", {"Mojave", {10, 14, 5}}},
- {"18G84", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G87", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G95", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G103", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G1012", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G2022", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G3020", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G4032", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G5033", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G6020", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G6032", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G6042", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G7016", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G8012", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G8022", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G9028", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G9216", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G9323", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"19A583", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
- {"19A602", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
- {"19A603", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
- {"19B88", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 1}}},
- {"19C57", {19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}},
- {"19C58", {19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}},
- {"19D76", {19, 3, 0}, "macOS", {"Catalina", {10, 15, 3}}},
- {"19E266", {19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}},
- {"19E287", {19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}},
- {"19F96", {19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}},
- {"19F101", {19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}},
- {"19G73", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}},
- {"19G2021", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}},
- {"19H2", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H4", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H15", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H114", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H512", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H524", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1030", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1217", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1323", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1417", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1419", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1519", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1615", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1713", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1715", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1824", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1922", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H2026", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"20A2411", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 0}}},
- {"20B29", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}},
- {"20B50", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}},
- {"20C69", {20, 2, 0}, "macOS", {"Big Sur", {11, 1, 0}}},
- {"20D64", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 0}}},
- {"20D74", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}},
- {"20D75", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}},
- {"20D80", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 2}}},
- {"20D91", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 3}}},
- {"20E232", {20, 4, 0}, "macOS", {"Big Sur", {11, 3, 0}}},
- {"20E241", {20, 4, 0}, "macOS", {"Big Sur", {11, 3, 1}}},
- {"20F71", {20, 5, 0}, "macOS", {"Big Sur", {11, 4, 0}}},
- {"20G71", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 0}}},
- {"20G80", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 1}}},
- {"20G95", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 2}}},
- {"20G165", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 0}}},
- {"20G224", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 1}}},
- {"20G314", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 2}}},
- {"20G415", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 3}}},
- {"20G417", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 4}}},
- {"20G527", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 5}}},
- {"20G624", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 6}}},
- {"20G630", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 7}}},
- {"20G730", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 8}}},
- {"20G817", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 0}}},
- {"20G918", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 1}}},
- {"20G1020", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 2}}},
- {"20G1116", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 3}}},
- {"20G1120", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 4}}},
- {"20G1225", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 5}}},
- {"20G1231", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 6}}},
- {"20G1345", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 7}}},
- {"20G1351", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 8}}},
- {"20G1426", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 9}}},
- {"20G1427", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 10}}},
- {"21A344", {21, 0, 1}, "macOS", {"Monterey", {12, 0, 0}}},
- {"21A559", {21, 1, 0}, "macOS", {"Monterey", {12, 0, 1}}},
- {"21C52", {21, 2, 0}, "macOS", {"Monterey", {12, 1, 0}}},
- {"21D49", {21, 3, 0}, "macOS", {"Monterey", {12, 2, 0}}},
- {"21D62", {21, 3, 0}, "macOS", {"Monterey", {12, 2, 1}}},
- {"21E230", {21, 4, 0}, "macOS", {"Monterey", {12, 3, 0}}},
- {"21E258", {21, 4, 0}, "macOS", {"Monterey", {12, 3, 1}}},
- {"21F79", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
- {"21F2081", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
- {"21F2092", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
- {"21G72", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}},
- {"21G83", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}},
- {"21G115", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 0}}},
- {"21G217", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 1}}},
- {"21G320", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 2}}},
- {"21G419", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 3}}},
- {"21G526", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 4}}},
- {"21G531", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 5}}},
- {"21G646", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 6}}},
- {"21G651", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 7}}},
- {"21G725", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 8}}},
- {"21G726", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 9}}},
- {"21G816", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 0}}},
- {"21G920", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 1}}},
- {"21G1974", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 2}}},
- {"22A380", {13, 0, 0}, "macOS", {"Ventura", {22, 1, 0}}},
- {"22A400", {13, 0, 1}, "macOS", {"Ventura", {22, 1, 0}}},
- {"22C65", {13, 1, 0}, "macOS", {"Ventura", {22, 2, 0}}},
- {"22D49", {13, 2, 0}, "macOS", {"Ventura", {22, 3, 0}}},
- {"22D68", {13, 2, 1}, "macOS", {"Ventura", {22, 3, 0}}},
- {"22E252", {13, 3, 0}, "macOS", {"Ventura", {22, 4, 0}}},
- {"22E261", {13, 3, 1}, "macOS", {"Ventura", {22, 4, 0}}},
- {"22F66", {13, 4, 0}, "macOS", {"Ventura", {22, 5, 0}}},
- {"22F82", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
- {"22E772610a", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
- {"22F770820d", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
- {"22G74", {13, 5, 0}, "macOS", {"Ventura", {22, 6, 0}}},
- {"22G90", {13, 5, 1}, "macOS", {"Ventura", {22, 6, 0}}},
- {"22G91", {13, 5, 2}, "macOS", {"Ventura", {22, 6, 0}}},
- {"22G120", {13, 6, 0}, "macOS", {"Ventura", {22, 6, 0}}},
- {"22G313", {13, 6, 1}, "macOS", {"Ventura", {22, 6, 0}}},
- {"22G320", {13, 6, 2}, "macOS", {"Ventura", {22, 6, 0}}},
- {"23A344", {23, 0, 0}, "macOS", {"Sonoma", {14, 0, 0}}},
- {"23B74", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 0}}},
- {"23B81", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 1}}},
- {"23B92", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 2}}},
- {"23C64", {23, 2, 0}, "macOS", {"Sonoma", {14, 2, 0}}},
- {"23C71", {23, 2, 0}, "macOS", {"Sonoma", {14, 2, 1}}},
- };
-
-
- b32 build_found = 1;
- b32 darwin_found = 1;
- uint32_t major, minor, patch;
-
- #define MACOS_VERSION_BUFFER_SIZE 100
- char build_buffer[MACOS_VERSION_BUFFER_SIZE];
- char darwin_buffer[MACOS_VERSION_BUFFER_SIZE];
- size_t build_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1;
- size_t darwin_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1;
- #undef MACOS_VERSION_BUFFER_SIZE
-
- int build_mibs[] = { CTL_KERN, KERN_OSVERSION };
- if (sysctl(build_mibs, 2, build_buffer, &build_buffer_size, NULL, 0) == -1) {
- build_found = 0;
- }
-
- int darwin_mibs[] = { CTL_KERN, KERN_OSRELEASE };
- if (sysctl(darwin_mibs, 2, darwin_buffer, &darwin_buffer_size, NULL, 0) == -1) {
- gb_printf("macOS Unknown\n");
- return;
- } else {
- if (sscanf(darwin_buffer, "%u.%u.%u", &major, &minor, &patch) != 3) {
- darwin_found = 0;
- }
- }
-
- // Scan table for match on BUILD
- int macos_release_count = sizeof(macos_release_map) / sizeof(macos_release_map[0]);
- Darwin_To_Release build_match = {};
- Darwin_To_Release kernel_match = {};
-
- for (int build = 0; build < macos_release_count; build++) {
- Darwin_To_Release rel = macos_release_map[build];
-
- // Do we have an exact match on the BUILD?
- if (gb_strcmp(rel.build, (const char *)build_buffer) == 0) {
- build_match = rel;
- break;
- }
-
- // Do we have an exact Darwin match?
- if (rel.darwin[0] == major && rel.darwin[1] == minor && rel.darwin[2] == patch) {
- kernel_match = rel;
- }
-
- // Major kernel version needs to match exactly,
- if (rel.darwin[0] == major) {
- // No major version match yet.
- if (!kernel_match.os_name) {
- kernel_match = rel;
- }
- if (minor >= rel.darwin[1]) {
- kernel_match = rel;
- if (patch >= rel.darwin[2]) {
- kernel_match = rel;
- }
- }
- }
- }
-
- Darwin_To_Release match = {};
- if(!build_match.build) {
- match = kernel_match;
- } else {
- match = build_match;
- }
-
- if (match.os_name) {
- gb_printf("%s %s %d", match.os_name, match.release.name, match.release.version[0]);
- if (match.release.version[1] > 0 || match.release.version[2] > 0) {
- gb_printf(".%d", match.release.version[1]);
- }
- if (match.release.version[2] > 0) {
- gb_printf(".%d", match.release.version[2]);
- }
- if (build_found) {
- gb_printf(" (build: %s, kernel: %d.%d.%d)\n", build_buffer, match.darwin[0], match.darwin[1], match.darwin[2]);
- } else {
- gb_printf(" (build: %s?, kernel: %d.%d.%d)\n", match.build, match.darwin[0], match.darwin[1], match.darwin[2]);
- }
- return;
- }
-
- if (build_found && darwin_found) {
- gb_printf("macOS Unknown (build: %s, kernel: %d.%d.%d)\n", build_buffer, major, minor, patch);
- return;
- } else if (build_found) {
- gb_printf("macOS Unknown (build: %s)\n", build_buffer);
- return;
- } else if (darwin_found) {
- gb_printf("macOS Unknown (kernel: %d.%d.%d)\n", major, minor, patch);
- return;
- }
- #elif defined(GB_SYSTEM_OPENBSD)
- struct utsname un;
-
- if (uname(&un) != -1) {
- gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine);
- } else {
- gb_printf("OpenBSD: Unknown\n");
- }
- #elif defined(GB_SYSTEM_FREEBSD)
- #define freebsd_version_buffer 129
- char buffer[freebsd_version_buffer];
- size_t buffer_size = freebsd_version_buffer - 1;
- #undef freebsd_version_buffer
-
- int mibs[] = { CTL_KERN, KERN_VERSION };
- if (sysctl(mibs, 2, buffer, &buffer_size, NULL, 0) == -1) {
- gb_printf("FreeBSD: Unknown\n");
- } else {
- // KERN_VERSION can end in a \n, replace it with a \0
- for (int i = 0; i < buffer_size; i += 1) {
- if (buffer[i] == '\n') buffer[i] = 0;
- }
- gb_printf("%s", &buffer[0]);
-
- // Retrieve kernel revision using `sysctl`, e.g. 199506
- mibs[1] = KERN_OSREV;
- uint64_t revision;
- size_t revision_size = sizeof(revision);
-
- if (sysctl(mibs, 2, &revision, &revision_size, NULL, 0) == -1) {
- gb_printf("\n");
- } else {
- gb_printf(", revision %ld\n", revision);
- }
- }
- #else
- gb_printf("Unknown");
- #endif
-}
-
-// NOTE(Jeroen): `odin report` prints some system information for easier bug reporting.
-gb_internal void print_bug_report_help() {
- gb_printf("Where to find more information and get into contact when you encounter a bug:\n\n");
- gb_printf("\tWebsite: https://odin-lang.org\n");
- gb_printf("\tGitHub: https://github.com/odin-lang/Odin/issues\n");
- /*
- Uncomment and update URL once we have a Discord vanity URL. For now people can get here from the site.
- gb_printf("\tDiscord: https://discord.com/invite/sVBPHEv\n");
- */
- gb_printf("\n\n");
-
- gb_printf("Useful information to add to a bug report:\n\n");
-
- gb_printf("\tOdin: %.*s", LIT(ODIN_VERSION));
-
- #ifdef NIGHTLY
- gb_printf("-nightly");
- #endif
-
- #ifdef GIT_SHA
- gb_printf(":%s", GIT_SHA);
- #endif
-
- gb_printf("\n");
-
- /*
- Print OS information.
- */
- report_os_info();
-
- /*
- Now print CPU info.
- */
- report_cpu_info();
-
- /*
- And RAM info.
- */
- report_ram_info();
-} \ No newline at end of file
+/*
+ Gather and print platform and version info to help with reporting Odin bugs.
+*/
+
+#if !defined(GB_COMPILER_MSVC)
+ #if defined(GB_CPU_X86)
+ #include <cpuid.h>
+ #endif
+#endif
+
+#if defined(GB_SYSTEM_LINUX)
+ #include <sys/utsname.h>
+ #include <sys/sysinfo.h>
+#endif
+
+#if defined(GB_SYSTEM_OSX)
+ #include <sys/sysctl.h>
+#endif
+
+#if defined(GB_SYSTEM_OPENBSD)
+ #include <sys/sysctl.h>
+ #include <sys/utsname.h>
+#endif
+
+#if defined(GB_SYSTEM_FREEBSD)
+ #include <sys/sysctl.h>
+#endif
+
+/*
+ NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
+*/
+#if defined(GB_SYSTEM_WINDOWS)
+gb_internal void report_windows_product_type(DWORD ProductType) {
+ switch (ProductType) {
+ case PRODUCT_ULTIMATE:
+ gb_printf("Ultimate");
+ break;
+
+ case PRODUCT_HOME_BASIC:
+ gb_printf("Home Basic");
+ break;
+
+ case PRODUCT_HOME_PREMIUM:
+ gb_printf("Home Premium");
+ break;
+
+ case PRODUCT_ENTERPRISE:
+ gb_printf("Enterprise");
+ break;
+
+ case PRODUCT_CORE:
+ gb_printf("Home Basic");
+ break;
+
+ case PRODUCT_HOME_BASIC_N:
+ gb_printf("Home Basic N");
+ break;
+
+ case PRODUCT_EDUCATION:
+ gb_printf("Education");
+ break;
+
+ case PRODUCT_EDUCATION_N:
+ gb_printf("Education N");
+ break;
+
+ case PRODUCT_BUSINESS:
+ gb_printf("Business");
+ break;
+
+ case PRODUCT_STANDARD_SERVER:
+ gb_printf("Standard Server");
+ break;
+
+ case PRODUCT_DATACENTER_SERVER:
+ gb_printf("Datacenter");
+ break;
+
+ case PRODUCT_SMALLBUSINESS_SERVER:
+ gb_printf("Windows Small Business Server");
+ break;
+
+ case PRODUCT_ENTERPRISE_SERVER:
+ gb_printf("Enterprise Server");
+ break;
+
+ case PRODUCT_STARTER:
+ gb_printf("Starter");
+ break;
+
+ case PRODUCT_DATACENTER_SERVER_CORE:
+ gb_printf("Datacenter Server Core");
+ break;
+
+ case PRODUCT_STANDARD_SERVER_CORE:
+ gb_printf("Server Standard Core");
+ break;
+
+ case PRODUCT_ENTERPRISE_SERVER_CORE:
+ gb_printf("Enterprise Server Core");
+ break;
+
+ case PRODUCT_BUSINESS_N:
+ gb_printf("Business N");
+ break;
+
+ case PRODUCT_HOME_SERVER:
+ gb_printf("Home Server");
+ break;
+
+ case PRODUCT_SERVER_FOR_SMALLBUSINESS:
+ gb_printf("Windows Server 2008 for Windows Essential Server Solutions");
+ break;
+
+ case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
+ gb_printf("Small Business Server Premium");
+ break;
+
+ case PRODUCT_HOME_PREMIUM_N:
+ gb_printf("Home Premium N");
+ break;
+
+ case PRODUCT_ENTERPRISE_N:
+ gb_printf("Enterprise N");
+ break;
+
+ case PRODUCT_ULTIMATE_N:
+ gb_printf("Ultimate N");
+ break;
+
+ case PRODUCT_HYPERV:
+ gb_printf("HyperV");
+ break;
+
+ case PRODUCT_STARTER_N:
+ gb_printf("Starter N");
+ break;
+
+ case PRODUCT_PROFESSIONAL:
+ gb_printf("Professional");
+ break;
+
+ case PRODUCT_PROFESSIONAL_N:
+ gb_printf("Professional N");
+ break;
+
+ case PRODUCT_UNLICENSED:
+ gb_printf("Unlicensed");
+ break;
+
+ default:
+ gb_printf("Unknown Edition (%08x)", cast(unsigned)ProductType);
+ }
+}
+#endif
+
+gb_internal void odin_cpuid(int leaf, int result[]) {
+ #if defined(GB_CPU_ARM)
+ return;
+
+ #elif defined(GB_CPU_X86)
+
+ #if defined(GB_COMPILER_MSVC)
+ __cpuid(result, leaf);
+ #else
+ __get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]);
+ #endif
+
+ #endif
+}
+
+gb_internal void report_cpu_info() {
+ gb_printf("\tCPU: ");
+
+ #if defined(GB_CPU_X86)
+
+ /*
+ Get extended leaf info
+ */
+ int cpu[4];
+
+ odin_cpuid(0x80000000, &cpu[0]);
+ int number_of_extended_ids = cpu[0];
+
+ int brand[0x12] = {};
+
+ /*
+ Read CPU brand if supported.
+ */
+ if (number_of_extended_ids >= 0x80000004) {
+ odin_cpuid(0x80000002, &brand[0]);
+ odin_cpuid(0x80000003, &brand[4]);
+ odin_cpuid(0x80000004, &brand[8]);
+
+ /*
+ Some CPUs like ` Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz` may include leading spaces. Trim them.
+ */
+ char * brand_name = (char *)&brand[0];
+ for (; brand_name[0] == ' '; brand_name++) {}
+
+ gb_printf("%s\n", brand_name);
+ } else {
+ gb_printf("Unable to retrieve.\n");
+ }
+
+ #elif defined(GB_CPU_ARM)
+ /*
+ TODO(Jeroen): On *nix, perhaps query `/proc/cpuinfo`.
+ */
+ #if defined(GB_ARCH_64_BIT)
+ gb_printf("ARM64\n");
+ #else
+ gb_printf("ARM\n");
+ #endif
+ #else
+ gb_printf("Unknown\n");
+ #endif
+}
+
+/*
+ Report the amount of installed RAM.
+*/
+gb_internal void report_ram_info() {
+ gb_printf("\tRAM: ");
+
+ #if defined(GB_SYSTEM_WINDOWS)
+ MEMORYSTATUSEX statex;
+ statex.dwLength = sizeof(statex);
+ GlobalMemoryStatusEx (&statex);
+
+ gb_printf("%lld MiB\n", statex.ullTotalPhys / gb_megabytes(1));
+
+ #elif defined(GB_SYSTEM_LINUX)
+ /*
+ Retrieve RAM info using `sysinfo()`,
+ */
+ struct sysinfo info;
+ int result = sysinfo(&info);
+
+ if (result == 0x0) {
+ gb_printf("%lu MiB\n", info.totalram * info.mem_unit / gb_megabytes(1));
+ } else {
+ gb_printf("Unknown.\n");
+ }
+ #elif defined(GB_SYSTEM_OSX)
+ uint64_t ram_amount;
+ size_t val_size = sizeof(ram_amount);
+
+ int mibs[] = { CTL_HW, HW_MEMSIZE };
+ if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+ gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
+ }
+ #elif defined(GB_SYSTEM_OPENBSD)
+ uint64_t ram_amount;
+ size_t val_size = sizeof(ram_amount);
+
+ int mibs[] = { CTL_HW, HW_PHYSMEM64 };
+ if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+ gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
+ }
+ #elif defined(GB_SYSTEM_FREEBSD)
+ uint64_t ram_amount;
+ size_t val_size = sizeof(ram_amount);
+
+ int mibs[] = { CTL_HW, HW_PHYSMEM };
+ if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+ gb_printf("%lu MiB\n", ram_amount / gb_megabytes(1));
+ }
+ #else
+ gb_printf("Unknown.\n");
+ #endif
+}
+
+gb_internal void report_os_info() {
+ gb_printf("\tOS: ");
+
+ #if defined(GB_SYSTEM_WINDOWS)
+ /*
+ NOTE(Jeroen):
+ `GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10.
+ `RtlGetVersion` will return the true version.
+
+ Rather than include the WinDDK, we ask the kernel directly.
+
+ `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release)
+
+ */
+ OSVERSIONINFOEXW osvi;
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
+
+ typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(OSVERSIONINFOW*);
+ typedef BOOL (WINAPI* GetProductInfoPtr)(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion, PDWORD pdwReturnedProductType);
+
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion
+ RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlGetVersion");
+ // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo
+ GetProductInfoPtr GetProductInfo = (GetProductInfoPtr)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo");
+
+ NTSTATUS status = {};
+ DWORD ProductType = {};
+ if (RtlGetVersion != nullptr) {
+ status = RtlGetVersion((OSVERSIONINFOW*)&osvi);
+ }
+
+ if (RtlGetVersion == nullptr || status != 0x0) {
+ gb_printf("Windows (Unknown Version)");
+ } else {
+ if (GetProductInfo != nullptr) {
+ GetProductInfo(osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &ProductType);
+ }
+
+ if (false) {
+ gb_printf("dwMajorVersion: %u\n", cast(unsigned)osvi.dwMajorVersion);
+ gb_printf("dwMinorVersion: %u\n", cast(unsigned)osvi.dwMinorVersion);
+ gb_printf("dwBuildNumber: %u\n", cast(unsigned)osvi.dwBuildNumber);
+ gb_printf("dwPlatformId: %u\n", cast(unsigned)osvi.dwPlatformId);
+ gb_printf("wServicePackMajor: %u\n", cast(unsigned)osvi.wServicePackMajor);
+ gb_printf("wServicePackMinor: %u\n", cast(unsigned)osvi.wServicePackMinor);
+ gb_printf("wSuiteMask: %u\n", cast(unsigned)osvi.wSuiteMask);
+ gb_printf("wProductType: %u\n", cast(unsigned)osvi.wProductType);
+ }
+
+ gb_printf("Windows ");
+
+ switch (osvi.dwMajorVersion) {
+ case 10:
+ /*
+ Windows 10 (Pro), Windows 2016 Server, Windows 2019 Server, Windows 2022 Server
+ */
+ switch (osvi.wProductType) {
+ case VER_NT_WORKSTATION: // Workstation
+ if (osvi.dwBuildNumber < 22000) {
+ gb_printf("10 ");
+ } else {
+ gb_printf("11 ");
+ }
+
+ report_windows_product_type(ProductType);
+
+ break;
+ default: // Server or Domain Controller
+ switch(osvi.dwBuildNumber) {
+ case 14393:
+ gb_printf("2016 Server");
+ break;
+ case 17763:
+ gb_printf("2019 Server");
+ break;
+ case 20348:
+ gb_printf("2022 Server");
+ break;
+ default:
+ gb_printf("Unknown Server");
+ break;
+ }
+ }
+ break;
+ case 6:
+ switch (osvi.dwMinorVersion) {
+ case 0:
+ switch (osvi.wProductType) {
+ case VER_NT_WORKSTATION:
+ gb_printf("Windows Vista ");
+ report_windows_product_type(ProductType);
+ break;
+ case 3:
+ gb_printf("Windows Server 2008");
+ break;
+ }
+ break;
+
+ case 1:
+ switch (osvi.wProductType) {
+ case VER_NT_WORKSTATION:
+ gb_printf("Windows 7 ");
+ report_windows_product_type(ProductType);
+ break;
+ case 3:
+ gb_printf("Windows Server 2008 R2");
+ break;
+ }
+ break;
+ case 2:
+ switch (osvi.wProductType) {
+ case VER_NT_WORKSTATION:
+ gb_printf("Windows 8 ");
+ report_windows_product_type(ProductType);
+ break;
+ case 3:
+ gb_printf("Windows Server 2012");
+ break;
+ }
+ break;
+ case 3:
+ switch (osvi.wProductType) {
+ case VER_NT_WORKSTATION:
+ gb_printf("Windows 8.1 ");
+ report_windows_product_type(ProductType);
+ break;
+ case 3:
+ gb_printf("Windows Server 2012 R2");
+ break;
+ }
+ break;
+ }
+ break;
+ case 5:
+ switch (osvi.dwMinorVersion) {
+ case 0:
+ gb_printf("Windows 2000");
+ break;
+ case 1:
+ gb_printf("Windows XP");
+ break;
+ case 2:
+ gb_printf("Windows Server 2003");
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ Grab Windows DisplayVersion (like 20H02)
+ */
+ LPDWORD ValueType = {};
+ DWORD UBR;
+ char DisplayVersion[256];
+ DWORD ValueSize = 256;
+
+ status = RegGetValue(
+ HKEY_LOCAL_MACHINE,
+ TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
+ TEXT("DisplayVersion"),
+ RRF_RT_REG_SZ,
+ ValueType,
+ DisplayVersion,
+ &ValueSize
+ );
+
+ if (status == 0x0) {
+ gb_printf(" (version: %s)", DisplayVersion);
+ }
+
+ /*
+ Now print build number.
+ */
+ gb_printf(", build %u", cast(unsigned)osvi.dwBuildNumber);
+
+ ValueSize = sizeof(UBR);
+ status = RegGetValue(
+ HKEY_LOCAL_MACHINE,
+ TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
+ TEXT("UBR"),
+ RRF_RT_REG_DWORD,
+ ValueType,
+ &UBR,
+ &ValueSize
+ );
+
+ if (status == 0x0) {
+ gb_printf(".%u", cast(unsigned)UBR);
+ }
+ gb_printf("\n");
+ }
+ #elif defined(GB_SYSTEM_LINUX)
+ /*
+ Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
+ */
+ gbAllocator a = heap_allocator();
+
+ gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release");
+ defer (gb_file_free_contents(&release));
+
+ b32 found = 0;
+ if (release.size) {
+ char *start = (char *)release.data;
+ char *end = (char *)release.data + release.size;
+ const char *needle = "PRETTY_NAME=\"";
+ isize needle_len = gb_strlen((needle));
+
+ char *c = start;
+ for (; c < end; c++) {
+ if (gb_strncmp(c, needle, needle_len) == 0) {
+ found = 1;
+ start = c + needle_len;
+ break;
+ }
+ }
+
+ if (found) {
+ for (c = start; c < end; c++) {
+ if (*c == '"') {
+ // Found the closing quote. Replace it with \0
+ *c = 0;
+ gb_printf("%s", (char *)start);
+ break;
+ } else if (*c == '\n') {
+ found = 0;
+ }
+ }
+ }
+ }
+
+ if (!found) {
+ gb_printf("Unknown Linux Distro");
+ }
+
+ /*
+ Print kernel info using `uname()` syscall, https://linux.die.net/man/2/uname
+ */
+ char buffer[1024];
+ uname((struct utsname *)&buffer[0]);
+
+ struct utsname *info;
+ info = (struct utsname *)&buffer[0];
+
+ gb_printf(", %s %s\n", info->sysname, info->release);
+
+ #elif defined(GB_SYSTEM_OSX)
+ struct Darwin_To_Release {
+ const char* build; // 21G83
+ int darwin[3]; // Darwin kernel triplet
+ const char* os_name; // OS X, MacOS
+ struct {
+ const char* name; // Monterey, Mojave, etc.
+ int version[3]; // 12.4, etc.
+ } release;
+ };
+
+ Darwin_To_Release macos_release_map[] = {
+ {"8A428", { 8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}},
+ {"8A432", { 8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}},
+ {"8B15", { 8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}},
+ {"8B17", { 8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}},
+ {"8C46", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
+ {"8C47", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
+ {"8E102", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
+ {"8E45", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
+ {"8E90", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
+ {"8F46", { 8, 3, 0}, "macOS", {"Tiger", {10, 4, 3}}},
+ {"8G32", { 8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}},
+ {"8G1165", { 8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}},
+ {"8H14", { 8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}},
+ {"8G1454", { 8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}},
+ {"8I127", { 8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}},
+ {"8I1119", { 8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}},
+ {"8J135", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
+ {"8J2135a", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
+ {"8K1079", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
+ {"8N5107", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
+ {"8L127", { 8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}},
+ {"8L2127", { 8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}},
+ {"8P135", { 8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}},
+ {"8P2137", { 8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}},
+ {"8R218", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
+ {"8R2218", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
+ {"8R2232", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
+ {"8S165", { 8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}},
+ {"8S2167", { 8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}},
+ {"9A581", { 9, 0, 0}, "macOS", {"Leopard", {10, 5, 0}}},
+ {"9B18", { 9, 1, 0}, "macOS", {"Leopard", {10, 5, 1}}},
+ {"9B2117", { 9, 1, 1}, "macOS", {"Leopard", {10, 5, 1}}},
+ {"9C31", { 9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}},
+ {"9C7010", { 9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}},
+ {"9D34", { 9, 3, 0}, "macOS", {"Leopard", {10, 5, 3}}},
+ {"9E17", { 9, 4, 0}, "macOS", {"Leopard", {10, 5, 4}}},
+ {"9F33", { 9, 5, 0}, "macOS", {"Leopard", {10, 5, 5}}},
+ {"9G55", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
+ {"9G66", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
+ {"9G71", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
+ {"9J61", { 9, 7, 0}, "macOS", {"Leopard", {10, 5, 7}}},
+ {"9L30", { 9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}},
+ {"9L34", { 9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}},
+ {"10A432", {10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}},
+ {"10A433", {10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}},
+ {"10B504", {10, 1, 0}, "macOS", {"Snow Leopard", {10, 6, 1}}},
+ {"10C540", {10, 2, 0}, "macOS", {"Snow Leopard", {10, 6, 2}}},
+ {"10D573", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
+ {"10D575", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
+ {"10D578", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
+ {"10F569", {10, 4, 0}, "macOS", {"Snow Leopard", {10, 6, 4}}},
+ {"10H574", {10, 5, 0}, "macOS", {"Snow Leopard", {10, 6, 5}}},
+ {"10J567", {10, 6, 0}, "macOS", {"Snow Leopard", {10, 6, 6}}},
+ {"10J869", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
+ {"10J3250", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
+ {"10J4138", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
+ {"10K540", {10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}},
+ {"10K549", {10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}},
+ {"11A511", {11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}},
+ {"11A511s", {11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}},
+ {"11A2061", {11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}},
+ {"11A2063", {11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}},
+ {"11B26", {11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}},
+ {"11B2118", {11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}},
+ {"11C74", {11, 2, 0}, "macOS", {"Lion", {10, 7, 2}}},
+ {"11D50", {11, 3, 0}, "macOS", {"Lion", {10, 7, 3}}},
+ {"11E53", {11, 4, 0}, "macOS", {"Lion", {10, 7, 4}}},
+ {"11G56", {11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}},
+ {"11G63", {11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}},
+ {"12A269", {12, 0, 0}, "macOS", {"Mountain Lion", {10, 8, 0}}},
+ {"12B19", {12, 1, 0}, "macOS", {"Mountain Lion", {10, 8, 1}}},
+ {"12C54", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
+ {"12C60", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
+ {"12C2034", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
+ {"12C3104", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
+ {"12D78", {12, 3, 0}, "macOS", {"Mountain Lion", {10, 8, 3}}},
+ {"12E55", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
+ {"12E3067", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
+ {"12E4022", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
+ {"12F37", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
+ {"12F45", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
+ {"12F2501", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
+ {"12F2518", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
+ {"12F2542", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
+ {"12F2560", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
+ {"13A603", {13, 0, 0}, "macOS", {"Mavericks", {10, 9, 0}}},
+ {"13B42", {13, 0, 0}, "macOS", {"Mavericks", {10, 9, 1}}},
+ {"13C64", {13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}},
+ {"13C1021", {13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}},
+ {"13D65", {13, 2, 0}, "macOS", {"Mavericks", {10, 9, 3}}},
+ {"13E28", {13, 3, 0}, "macOS", {"Mavericks", {10, 9, 4}}},
+ {"13F34", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
+ {"13F1066", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
+ {"13F1077", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
+ {"13F1096", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
+ {"13F1112", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
+ {"13F1134", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
+ {"13F1507", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
+ {"13F1603", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
+ {"13F1712", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
+ {"13F1808", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
+ {"13F1911", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
+ {"14A389", {14, 0, 0}, "macOS", {"Yosemite", {10, 10, 0}}},
+ {"14B25", {14, 0, 0}, "macOS", {"Yosemite", {10, 10, 1}}},
+ {"14C109", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
+ {"14C1510", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
+ {"14C2043", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
+ {"14C1514", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
+ {"14C2513", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
+ {"14D131", {14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}},
+ {"14D136", {14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}},
+ {"14E46", {14, 4, 0}, "macOS", {"Yosemite", {10, 10, 4}}},
+ {"14F27", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
+ {"14F1021", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
+ {"14F1505", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
+ {"14F1509", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
+ {"14F1605", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
+ {"14F1713", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
+ {"14F1808", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
+ {"14F1909", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
+ {"14F1912", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
+ {"14F2009", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
+ {"14F2109", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
+ {"14F2315", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
+ {"14F2411", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
+ {"14F2511", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
+ {"15A284", {15, 0, 0}, "macOS", {"El Capitan", {10, 11, 0}}},
+ {"15B42", {15, 0, 0}, "macOS", {"El Capitan", {10, 11, 1}}},
+ {"15C50", {15, 2, 0}, "macOS", {"El Capitan", {10, 11, 2}}},
+ {"15D21", {15, 3, 0}, "macOS", {"El Capitan", {10, 11, 3}}},
+ {"15E65", {15, 4, 0}, "macOS", {"El Capitan", {10, 11, 4}}},
+ {"15F34", {15, 5, 0}, "macOS", {"El Capitan", {10, 11, 5}}},
+ {"15G31", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"15G1004", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"15G1011", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"15G1108", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"15G1212", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"15G1217", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"15G1421", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"15G1510", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"15G1611", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"15G17023", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"15G18013", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"15G19009", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"15G20015", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"15G21013", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"15G22010", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
+ {"16A323", {16, 0, 0}, "macOS", {"Sierra", {10, 12, 0}}},
+ {"16B2555", {16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}},
+ {"16B2657", {16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}},
+ {"16C67", {16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}},
+ {"16C68", {16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}},
+ {"16D32", {16, 4, 0}, "macOS", {"Sierra", {10, 12, 3}}},
+ {"16E195", {16, 5, 0}, "macOS", {"Sierra", {10, 12, 4}}},
+ {"16F73", {16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}},
+ {"16F2073", {16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}},
+ {"16G29", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G1036", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G1114", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G1212", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G1314", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G1408", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G1510", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G1618", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G1710", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G1815", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G1917", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G1918", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G2016", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G2127", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G2128", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"16G2136", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
+ {"17A365", {17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}},
+ {"17A405", {17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}},
+ {"17B48", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
+ {"17B1002", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
+ {"17B1003", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
+ {"17C88", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
+ {"17C89", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
+ {"17C205", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
+ {"17C2205", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
+ {"17D47", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
+ {"17D2047", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
+ {"17D102", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
+ {"17D2102", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
+ {"17E199", {17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}},
+ {"17E202", {17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}},
+ {"17F77", {17, 6, 0}, "macOS", {"High Sierra", {10, 13, 5}}},
+ {"17G65", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G2208", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G2307", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G3025", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G4015", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G5019", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G6029", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G6030", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G7024", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G8029", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G8030", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G8037", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G9016", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G10021", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G11023", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G12034", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G13033", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G13035", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G14019", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G14033", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"17G14042", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
+ {"18A391", {18, 0, 0}, "macOS", {"Mojave", {10, 14, 0}}},
+ {"18B75", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
+ {"18B2107", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
+ {"18B3094", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
+ {"18C54", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 2}}},
+ {"18D42", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
+ {"18D43", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
+ {"18D109", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
+ {"18E226", {18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}},
+ {"18E227", {18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}},
+ {"18F132", {18, 6, 0}, "macOS", {"Mojave", {10, 14, 5}}},
+ {"18G84", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G87", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G95", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G103", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G1012", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G2022", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G3020", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G4032", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G5033", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G6020", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G6032", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G6042", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G7016", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G8012", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G8022", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G9028", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G9216", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"18G9323", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
+ {"19A583", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
+ {"19A602", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
+ {"19A603", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
+ {"19B88", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 1}}},
+ {"19C57", {19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}},
+ {"19C58", {19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}},
+ {"19D76", {19, 3, 0}, "macOS", {"Catalina", {10, 15, 3}}},
+ {"19E266", {19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}},
+ {"19E287", {19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}},
+ {"19F96", {19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}},
+ {"19F101", {19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}},
+ {"19G73", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}},
+ {"19G2021", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}},
+ {"19H2", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H4", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H15", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H114", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H512", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H524", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H1030", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H1217", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H1323", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H1417", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H1419", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H1519", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H1615", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H1713", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H1715", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H1824", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H1922", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"19H2026", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
+ {"20A2411", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 0}}},
+ {"20B29", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}},
+ {"20B50", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}},
+ {"20C69", {20, 2, 0}, "macOS", {"Big Sur", {11, 1, 0}}},
+ {"20D64", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 0}}},
+ {"20D74", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}},
+ {"20D75", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}},
+ {"20D80", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 2}}},
+ {"20D91", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 3}}},
+ {"20E232", {20, 4, 0}, "macOS", {"Big Sur", {11, 3, 0}}},
+ {"20E241", {20, 4, 0}, "macOS", {"Big Sur", {11, 3, 1}}},
+ {"20F71", {20, 5, 0}, "macOS", {"Big Sur", {11, 4, 0}}},
+ {"20G71", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 0}}},
+ {"20G80", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 1}}},
+ {"20G95", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 2}}},
+ {"20G165", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 0}}},
+ {"20G224", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 1}}},
+ {"20G314", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 2}}},
+ {"20G415", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 3}}},
+ {"20G417", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 4}}},
+ {"20G527", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 5}}},
+ {"20G624", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 6}}},
+ {"20G630", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 7}}},
+ {"20G730", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 8}}},
+ {"20G817", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 0}}},
+ {"20G918", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 1}}},
+ {"20G1020", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 2}}},
+ {"20G1116", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 3}}},
+ {"20G1120", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 4}}},
+ {"20G1225", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 5}}},
+ {"20G1231", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 6}}},
+ {"20G1345", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 7}}},
+ {"20G1351", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 8}}},
+ {"20G1426", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 9}}},
+ {"20G1427", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 10}}},
+ {"21A344", {21, 0, 1}, "macOS", {"Monterey", {12, 0, 0}}},
+ {"21A559", {21, 1, 0}, "macOS", {"Monterey", {12, 0, 1}}},
+ {"21C52", {21, 2, 0}, "macOS", {"Monterey", {12, 1, 0}}},
+ {"21D49", {21, 3, 0}, "macOS", {"Monterey", {12, 2, 0}}},
+ {"21D62", {21, 3, 0}, "macOS", {"Monterey", {12, 2, 1}}},
+ {"21E230", {21, 4, 0}, "macOS", {"Monterey", {12, 3, 0}}},
+ {"21E258", {21, 4, 0}, "macOS", {"Monterey", {12, 3, 1}}},
+ {"21F79", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
+ {"21F2081", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
+ {"21F2092", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
+ {"21G72", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}},
+ {"21G83", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}},
+ {"21G115", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 0}}},
+ {"21G217", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 1}}},
+ {"21G320", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 2}}},
+ {"21G419", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 3}}},
+ {"21G526", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 4}}},
+ {"21G531", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 5}}},
+ {"21G646", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 6}}},
+ {"21G651", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 7}}},
+ {"21G725", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 8}}},
+ {"21G726", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 9}}},
+ {"21G816", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 0}}},
+ {"21G920", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 1}}},
+ {"21G1974", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 2}}},
+ {"22A380", {13, 0, 0}, "macOS", {"Ventura", {22, 1, 0}}},
+ {"22A400", {13, 0, 1}, "macOS", {"Ventura", {22, 1, 0}}},
+ {"22C65", {13, 1, 0}, "macOS", {"Ventura", {22, 2, 0}}},
+ {"22D49", {13, 2, 0}, "macOS", {"Ventura", {22, 3, 0}}},
+ {"22D68", {13, 2, 1}, "macOS", {"Ventura", {22, 3, 0}}},
+ {"22E252", {13, 3, 0}, "macOS", {"Ventura", {22, 4, 0}}},
+ {"22E261", {13, 3, 1}, "macOS", {"Ventura", {22, 4, 0}}},
+ {"22F66", {13, 4, 0}, "macOS", {"Ventura", {22, 5, 0}}},
+ {"22F82", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
+ {"22E772610a", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
+ {"22F770820d", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
+ {"22G74", {13, 5, 0}, "macOS", {"Ventura", {22, 6, 0}}},
+ {"22G90", {13, 5, 1}, "macOS", {"Ventura", {22, 6, 0}}},
+ {"22G91", {13, 5, 2}, "macOS", {"Ventura", {22, 6, 0}}},
+ {"22G120", {13, 6, 0}, "macOS", {"Ventura", {22, 6, 0}}},
+ {"22G313", {13, 6, 1}, "macOS", {"Ventura", {22, 6, 0}}},
+ {"22G320", {13, 6, 2}, "macOS", {"Ventura", {22, 6, 0}}},
+ {"23A344", {23, 0, 0}, "macOS", {"Sonoma", {14, 0, 0}}},
+ {"23B74", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 0}}},
+ {"23B81", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 1}}},
+ {"23B2082", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 1}}},
+ {"23B92", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 2}}},
+ {"23B2091", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 2}}},
+ {"23C64", {23, 2, 0}, "macOS", {"Sonoma", {14, 2, 0}}},
+ {"23C71", {23, 2, 0}, "macOS", {"Sonoma", {14, 2, 1}}},
+ {"23D56", {23, 3, 0}, "macOS", {"Sonoma", {14, 3, 0}}},
+ {"23D60", {23, 3, 0}, "macOS", {"Sonoma", {14, 3, 1}}},
+ {"23E214", {23, 4, 0}, "macOS", {"Sonoma", {14, 4, 0}}},
+ {"23E224", {23, 4, 0}, "macOS", {"Sonoma", {14, 4, 1}}},
+ };
+
+
+ b32 build_found = 1;
+ b32 darwin_found = 1;
+ uint32_t major, minor, patch;
+
+ #define MACOS_VERSION_BUFFER_SIZE 100
+ char build_buffer[MACOS_VERSION_BUFFER_SIZE];
+ char darwin_buffer[MACOS_VERSION_BUFFER_SIZE];
+ size_t build_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1;
+ size_t darwin_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1;
+ #undef MACOS_VERSION_BUFFER_SIZE
+
+ int build_mibs[] = { CTL_KERN, KERN_OSVERSION };
+ if (sysctl(build_mibs, 2, build_buffer, &build_buffer_size, NULL, 0) == -1) {
+ build_found = 0;
+ }
+
+ int darwin_mibs[] = { CTL_KERN, KERN_OSRELEASE };
+ if (sysctl(darwin_mibs, 2, darwin_buffer, &darwin_buffer_size, NULL, 0) == -1) {
+ gb_printf("macOS Unknown\n");
+ return;
+ } else {
+ if (sscanf(darwin_buffer, "%u.%u.%u", &major, &minor, &patch) != 3) {
+ darwin_found = 0;
+ }
+ }
+
+ // Scan table for match on BUILD
+ int macos_release_count = sizeof(macos_release_map) / sizeof(macos_release_map[0]);
+ Darwin_To_Release build_match = {};
+ Darwin_To_Release kernel_match = {};
+
+ for (int build = 0; build < macos_release_count; build++) {
+ Darwin_To_Release rel = macos_release_map[build];
+
+ // Do we have an exact match on the BUILD?
+ if (gb_strcmp(rel.build, (const char *)build_buffer) == 0) {
+ build_match = rel;
+ break;
+ }
+
+ // Do we have an exact Darwin match?
+ if (rel.darwin[0] == major && rel.darwin[1] == minor && rel.darwin[2] == patch) {
+ kernel_match = rel;
+ }
+
+ // Major kernel version needs to match exactly,
+ if (rel.darwin[0] == major) {
+ // No major version match yet.
+ if (!kernel_match.os_name) {
+ kernel_match = rel;
+ }
+ if (minor >= rel.darwin[1]) {
+ kernel_match = rel;
+ if (patch >= rel.darwin[2]) {
+ kernel_match = rel;
+ }
+ }
+ }
+ }
+
+ Darwin_To_Release match = {};
+ if(!build_match.build) {
+ match = kernel_match;
+ } else {
+ match = build_match;
+ }
+
+ if (match.os_name) {
+ gb_printf("%s %s %d", match.os_name, match.release.name, match.release.version[0]);
+ if (match.release.version[1] > 0 || match.release.version[2] > 0) {
+ gb_printf(".%d", match.release.version[1]);
+ }
+ if (match.release.version[2] > 0) {
+ gb_printf(".%d", match.release.version[2]);
+ }
+ if (build_found) {
+ gb_printf(" (build: %s, kernel: %d.%d.%d)\n", build_buffer, match.darwin[0], match.darwin[1], match.darwin[2]);
+ } else {
+ gb_printf(" (build: %s?, kernel: %d.%d.%d)\n", match.build, match.darwin[0], match.darwin[1], match.darwin[2]);
+ }
+ return;
+ }
+
+ if (build_found && darwin_found) {
+ gb_printf("macOS Unknown (build: %s, kernel: %d.%d.%d)\n", build_buffer, major, minor, patch);
+ return;
+ } else if (build_found) {
+ gb_printf("macOS Unknown (build: %s)\n", build_buffer);
+ return;
+ } else if (darwin_found) {
+ gb_printf("macOS Unknown (kernel: %d.%d.%d)\n", major, minor, patch);
+ return;
+ }
+ #elif defined(GB_SYSTEM_OPENBSD)
+ struct utsname un;
+
+ if (uname(&un) != -1) {
+ gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine);
+ } else {
+ gb_printf("OpenBSD: Unknown\n");
+ }
+ #elif defined(GB_SYSTEM_FREEBSD)
+ #define freebsd_version_buffer 129
+ char buffer[freebsd_version_buffer];
+ size_t buffer_size = freebsd_version_buffer - 1;
+ #undef freebsd_version_buffer
+
+ int mibs[] = { CTL_KERN, KERN_VERSION };
+ if (sysctl(mibs, 2, buffer, &buffer_size, NULL, 0) == -1) {
+ gb_printf("FreeBSD: Unknown\n");
+ } else {
+ // KERN_VERSION can end in a \n, replace it with a \0
+ for (int i = 0; i < buffer_size; i += 1) {
+ if (buffer[i] == '\n') buffer[i] = 0;
+ }
+ gb_printf("%s", &buffer[0]);
+
+ // Retrieve kernel revision using `sysctl`, e.g. 199506
+ mibs[1] = KERN_OSREV;
+ uint64_t revision;
+ size_t revision_size = sizeof(revision);
+
+ if (sysctl(mibs, 2, &revision, &revision_size, NULL, 0) == -1) {
+ gb_printf("\n");
+ } else {
+ gb_printf(", revision %ld\n", revision);
+ }
+ }
+ #else
+ gb_printf("Unknown");
+ #endif
+}
+
+gb_internal void report_backend_info() {
+ gb_printf("\tBackend: LLVM %s\n", LLVM_VERSION_STRING);
+}
+
+// NOTE(Jeroen): `odin report` prints some system information for easier bug reporting.
+gb_internal void print_bug_report_help() {
+ gb_printf("Where to find more information and get into contact when you encounter a bug:\n\n");
+ gb_printf("\tWebsite: https://odin-lang.org\n");
+ gb_printf("\tGitHub: https://github.com/odin-lang/Odin/issues\n");
+ /*
+ Uncomment and update URL once we have a Discord vanity URL. For now people can get here from the site.
+ gb_printf("\tDiscord: https://discord.com/invite/sVBPHEv\n");
+ */
+ gb_printf("\n\n");
+
+ gb_printf("Useful information to add to a bug report:\n\n");
+
+ gb_printf("\tOdin: %.*s", LIT(ODIN_VERSION));
+
+ #ifdef NIGHTLY
+ gb_printf("-nightly");
+ #endif
+
+ #ifdef GIT_SHA
+ gb_printf(":%s", GIT_SHA);
+ #endif
+
+ gb_printf("\n");
+
+ /*
+ Print OS information.
+ */
+ report_os_info();
+
+ /*
+ Now print CPU info.
+ */
+ report_cpu_info();
+
+ /*
+ And RAM info.
+ */
+ report_ram_info();
+
+ report_backend_info();
+}
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index af518bcb4..30d6f0b3c 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -18,6 +18,7 @@ enum TargetOsKind : u16 {
TargetOs_essence,
TargetOs_freebsd,
TargetOs_openbsd,
+ TargetOs_haiku,
TargetOs_wasi,
TargetOs_js,
@@ -78,6 +79,7 @@ gb_global String target_os_names[TargetOs_COUNT] = {
str_lit("essence"),
str_lit("freebsd"),
str_lit("openbsd"),
+ str_lit("haiku"),
str_lit("wasi"),
str_lit("js"),
@@ -323,6 +325,7 @@ struct BuildContext {
bool ODIN_DEBUG; // Odin in debug mode
bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not
bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing)
+ bool ODIN_DEFAULT_TO_PANIC_ALLOCATOR; // Whether the default allocator is a "panic" allocator or not (i.e. panics on any call to it)
bool ODIN_FOREIGN_ERROR_PROCEDURES;
bool ODIN_VALGRIND_SUPPORT;
@@ -389,6 +392,7 @@ struct BuildContext {
bool warnings_as_errors;
bool hide_error_line;
bool terse_errors;
+ bool json_errors;
bool has_ansi_terminal_colours;
bool ignore_lazy;
@@ -408,7 +412,9 @@ struct BuildContext {
bool dynamic_map_calls;
- bool obfuscate_source_code_locations;
+ bool obfuscate_source_code_locations;
+
+ bool min_link_libs;
RelocMode reloc_mode;
bool disable_red_zone;
@@ -422,6 +428,7 @@ struct BuildContext {
Array<String> extra_packages;
StringSet test_names;
+ bool test_all_packages;
gbAffinity affinity;
isize thread_count;
@@ -431,7 +438,9 @@ struct BuildContext {
BlockingMutex target_features_mutex;
StringSet target_features_set;
String target_features_string;
+
String minimum_os_version_string;
+ bool minimum_os_version_string_given;
};
gb_global BuildContext build_context = {0};
@@ -505,15 +514,15 @@ gb_global TargetMetrics target_darwin_amd64 = {
TargetOs_darwin,
TargetArch_amd64,
8, 8, 8, 16,
- str_lit("x86_64-apple-darwin"),
+ str_lit("x86_64-apple-macosx"), // NOTE: Changes during initialization based on build flags.
str_lit("e-m:o-i64:64-f80:128-n8:16:32:64-S128"),
};
gb_global TargetMetrics target_darwin_arm64 = {
TargetOs_darwin,
TargetArch_arm64,
- 8, 8, 8, 16,
- str_lit("arm64-apple-macosx11.0.0"),
+ 8, 8, 16, 16,
+ str_lit("arm64-apple-macosx"), // NOTE: Changes during initialization based on build flags.
str_lit("e-m:o-i64:64-i128:128-n32:64-S128"),
};
@@ -540,6 +549,13 @@ gb_global TargetMetrics target_openbsd_amd64 = {
str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
};
+gb_global TargetMetrics target_haiku_amd64 = {
+ TargetOs_haiku,
+ TargetArch_amd64,
+ 8, 8, 8, 16,
+ str_lit("x86_64-unknown-haiku"),
+};
+
gb_global TargetMetrics target_essence_amd64 = {
TargetOs_essence,
TargetArch_amd64,
@@ -639,6 +655,7 @@ gb_global NamedTargetMetrics named_targets[] = {
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
{ str_lit("openbsd_amd64"), &target_openbsd_amd64 },
+ { str_lit("haiku_amd64"), &target_haiku_amd64 },
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
@@ -870,11 +887,63 @@ gb_internal String internal_odin_root_dir(void) {
return path;
}
+#elif defined(GB_SYSTEM_HAIKU)
+
+#include <FindDirectory.h>
+
+gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_);
+
+gb_internal String internal_odin_root_dir(void) {
+ String path = global_module_path;
+ isize len, i;
+ u8 *text;
+
+ if (global_module_path_set) {
+ return global_module_path;
+ }
+
+ auto path_buf = array_make<char>(heap_allocator(), 300);
+ defer (array_free(&path_buf));
+
+ len = 0;
+ for (;;) {
+ u32 sz = path_buf.count;
+ int res = find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, nullptr, &path_buf[0], sz);
+ if(res == B_OK) {
+ len = sz;
+ break;
+ } else {
+ array_resize(&path_buf, sz + 1);
+ }
+ }
+
+ mutex_lock(&string_buffer_mutex);
+ defer (mutex_unlock(&string_buffer_mutex));
+
+ text = gb_alloc_array(permanent_allocator(), u8, len + 1);
+ gb_memmove(text, &path_buf[0], len);
+
+ path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
+
+ for (i = path.len-1; i >= 0; i--) {
+ u8 c = path[i];
+ if (c == '/' || c == '\\') {
+ break;
+ }
+ path.len--;
+ }
+
+ global_module_path = path;
+ global_module_path_set = true;
+
+ return path;
+}
+
#elif defined(GB_SYSTEM_OSX)
#include <mach-o/dyld.h>
-gb_internal String path_to_fullpath(gbAllocator a, String s);
+gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_);
gb_internal String internal_odin_root_dir(void) {
String path = global_module_path;
@@ -886,6 +955,7 @@ gb_internal String internal_odin_root_dir(void) {
}
auto path_buf = array_make<char>(heap_allocator(), 300);
+ defer (array_free(&path_buf));
len = 0;
for (;;) {
@@ -905,7 +975,7 @@ gb_internal String internal_odin_root_dir(void) {
text = gb_alloc_array(permanent_allocator(), u8, len + 1);
gb_memmove(text, &path_buf[0], len);
- path = path_to_fullpath(heap_allocator(), make_string(text, len));
+ path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
for (i = path.len-1; i >= 0; i--) {
u8 c = path[i];
@@ -918,9 +988,6 @@ gb_internal String internal_odin_root_dir(void) {
global_module_path = path;
global_module_path_set = true;
-
- // array_free(&path_buf);
-
return path;
}
#else
@@ -928,7 +995,7 @@ gb_internal String internal_odin_root_dir(void) {
// NOTE: Linux / Unix is unfinished and not tested very well.
#include <sys/stat.h>
-gb_internal String path_to_fullpath(gbAllocator a, String s);
+gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_);
gb_internal String internal_odin_root_dir(void) {
String path = global_module_path;
@@ -1070,7 +1137,7 @@ gb_internal String internal_odin_root_dir(void) {
gb_memmove(text, &path_buf[0], len);
- path = path_to_fullpath(heap_allocator(), make_string(text, len));
+ path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
for (i = path.len-1; i >= 0; i--) {
u8 c = path[i];
if (c == '/' || c == '\\') {
@@ -1089,7 +1156,7 @@ gb_internal String internal_odin_root_dir(void) {
gb_global BlockingMutex fullpath_mutex;
#if defined(GB_SYSTEM_WINDOWS)
-gb_internal String path_to_fullpath(gbAllocator a, String s) {
+gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) {
String result = {};
String16 string16 = string_to_string16(heap_allocator(), s);
@@ -1115,27 +1182,70 @@ gb_internal String path_to_fullpath(gbAllocator a, String s) {
result.text[i] = '/';
}
}
+ if (ok_) *ok_ = true;
} else {
+ if (ok_) *ok_ = false;
mutex_unlock(&fullpath_mutex);
}
return result;
}
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
-gb_internal String path_to_fullpath(gbAllocator a, String s) {
+
+struct PathToFullpathResult {
+ String result;
+ bool ok;
+};
+
+gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) {
+ static gb_thread_local StringMap<PathToFullpathResult> cache;
+
+ PathToFullpathResult *cached = string_map_get(&cache, s);
+ if (cached != nullptr) {
+ if (ok_) *ok_ = cached->ok;
+ return copy_string(a, cached->result);
+ }
+
char *p;
- mutex_lock(&fullpath_mutex);
p = realpath(cast(char *)s.text, 0);
- mutex_unlock(&fullpath_mutex);
- if(p == nullptr) return String{};
- return make_string_c(p);
+ defer (free(p));
+ if(p == nullptr) {
+ if (ok_) *ok_ = false;
+
+ // Path doesn't exist or is malformed, Windows's `GetFullPathNameW` does not check for
+ // existence of the file where `realpath` does, which causes different behaviour between platforms.
+ // Two things could be done here:
+ // 1. clean the path and resolve it manually, just like the Windows function does,
+ // probably requires porting `filepath.clean` from Odin and doing some more processing.
+ // 2. just return a copy of the original path.
+ //
+ // I have opted for 2 because it is much simpler + we already return `ok = false` + further
+ // checks and processes will use the path and cause errors (which we want).
+ String result = copy_string(a, s);
+
+ PathToFullpathResult cached_result = {};
+ cached_result.result = copy_string(permanent_allocator(), result);
+ cached_result.ok = false;
+ string_map_set(&cache, copy_string(permanent_allocator(), s), cached_result);
+
+ return result;
+ }
+ if (ok_) *ok_ = true;
+ String result = copy_string(a, make_string_c(p));
+
+ PathToFullpathResult cached_result = {};
+ cached_result.result = copy_string(permanent_allocator(), result);
+ cached_result.ok = true;
+ string_map_set(&cache, copy_string(permanent_allocator(), s), cached_result);
+
+ return result;
}
#else
#error Implement system
#endif
-gb_internal String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
+gb_internal String get_fullpath_relative(gbAllocator a, String base_dir, String path, bool *ok_) {
u8 *str = gb_alloc_array(heap_allocator(), u8, base_dir.len+1+path.len+1);
defer (gb_free(heap_allocator(), str));
@@ -1157,11 +1267,31 @@ gb_internal String get_fullpath_relative(gbAllocator a, String base_dir, String
String res = make_string(str, i);
res = string_trim_whitespace(res);
- return path_to_fullpath(a, res);
+ return path_to_fullpath(a, res, ok_);
}
-gb_internal String get_fullpath_core(gbAllocator a, String path) {
+gb_internal String get_fullpath_base_collection(gbAllocator a, String path, bool *ok_) {
+ String module_dir = odin_root_dir();
+
+ String base = str_lit("base/");
+
+ isize str_len = module_dir.len + base.len + path.len;
+ u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
+ defer (gb_free(heap_allocator(), str));
+
+ isize i = 0;
+ gb_memmove(str+i, module_dir.text, module_dir.len); i += module_dir.len;
+ gb_memmove(str+i, base.text, base.len); i += base.len;
+ gb_memmove(str+i, path.text, path.len); i += path.len;
+ str[i] = 0;
+
+ String res = make_string(str, i);
+ res = string_trim_whitespace(res);
+ return path_to_fullpath(a, res, ok_);
+}
+
+gb_internal String get_fullpath_core_collection(gbAllocator a, String path, bool *ok_) {
String module_dir = odin_root_dir();
String core = str_lit("core/");
@@ -1178,14 +1308,21 @@ gb_internal String get_fullpath_core(gbAllocator a, String path) {
String res = make_string(str, i);
res = string_trim_whitespace(res);
- return path_to_fullpath(a, res);
+ return path_to_fullpath(a, res, ok_);
}
gb_internal bool show_error_line(void) {
- return !build_context.hide_error_line;
+ return !build_context.hide_error_line && !build_context.json_errors;
+}
+
+gb_internal bool terse_errors(void) {
+ return build_context.terse_errors;
+}
+gb_internal bool json_errors(void) {
+ return build_context.json_errors;
}
gb_internal bool has_ansi_terminal_colours(void) {
- return build_context.has_ansi_terminal_colours;
+ return build_context.has_ansi_terminal_colours && !json_errors();
}
gb_internal bool has_asm_extension(String const &path) {
@@ -1273,6 +1410,8 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
metrics = &target_freebsd_amd64;
#elif defined(GB_SYSTEM_OPENBSD)
metrics = &target_openbsd_amd64;
+ #elif defined(GB_SYSTEM_HAIKU)
+ metrics = &target_haiku_amd64;
#elif defined(GB_CPU_ARM)
metrics = &target_linux_arm64;
#else
@@ -1309,19 +1448,25 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
}
bc->metrics = *metrics;
- switch (subtarget) {
- case Subtarget_Default:
- break;
- case Subtarget_iOS:
- GB_ASSERT(metrics->os == TargetOs_darwin);
- if (metrics->arch == TargetArch_arm64) {
- bc->metrics.target_triplet = str_lit("arm64-apple-ios");
- } else if (metrics->arch == TargetArch_amd64) {
- bc->metrics.target_triplet = str_lit("x86_64-apple-ios");
- } else {
- GB_PANIC("Unknown architecture for darwin");
+ if (metrics->os == TargetOs_darwin) {
+ if (!bc->minimum_os_version_string_given) {
+ bc->minimum_os_version_string = str_lit("11.0.0");
+ }
+
+ switch (subtarget) {
+ case Subtarget_Default:
+ bc->metrics.target_triplet = concatenate_strings(permanent_allocator(), bc->metrics.target_triplet, bc->minimum_os_version_string);
+ break;
+ case Subtarget_iOS:
+ if (metrics->arch == TargetArch_arm64) {
+ bc->metrics.target_triplet = str_lit("arm64-apple-ios");
+ } else if (metrics->arch == TargetArch_amd64) {
+ bc->metrics.target_triplet = str_lit("x86_64-apple-ios");
+ } else {
+ GB_PANIC("Unknown architecture for darwin");
+ }
+ break;
}
- break;
}
bc->ODIN_OS = target_os_names[metrics->os];
@@ -1367,6 +1512,7 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
bc->link_flags = str_lit("/machine:x64 ");
break;
case TargetOs_darwin:
+ bc->link_flags = str_lit("-arch x86_64 ");
break;
case TargetOs_linux:
bc->link_flags = str_lit("-arch x86-64 ");
@@ -1377,6 +1523,9 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
case TargetOs_openbsd:
bc->link_flags = str_lit("-arch x86-64 ");
break;
+ case TargetOs_haiku:
+ bc->link_flags = str_lit("-arch x86-64 ");
+ break;
}
} else if (bc->metrics.arch == TargetArch_i386) {
switch (bc->metrics.os) {
@@ -1454,6 +1603,16 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
break;
}
}
+
+ if (bc->metrics.os == TargetOs_freestanding) {
+ bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR = !bc->ODIN_DEFAULT_TO_PANIC_ALLOCATOR;
+ } else if (is_arch_wasm()) {
+ if (bc->metrics.os == TargetOs_js || bc->metrics.os == TargetOs_wasi) {
+ // TODO(bill): Should these even have a default "heap-like" allocator?
+ }
+ bc->ODIN_DEFAULT_TO_PANIC_ALLOCATOR = true;
+ bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR = !bc->ODIN_DEFAULT_TO_PANIC_ALLOCATOR;
+ }
}
#if defined(GB_SYSTEM_WINDOWS)
@@ -1588,8 +1747,8 @@ gb_internal bool init_build_paths(String init_filename) {
produces_output_file = true;
}
-
- if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR) {
+ if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR ||
+ build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR) {
bc->no_dynamic_literals = true;
}
@@ -1836,6 +1995,18 @@ gb_internal bool init_build_paths(String init_filename) {
}
}
+ if (build_context.no_crt && !build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR && !build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR) {
+ switch (build_context.metrics.os) {
+ case TargetOs_linux:
+ case TargetOs_darwin:
+ case TargetOs_essence:
+ case TargetOs_freebsd:
+ case TargetOs_openbsd:
+ case TargetOs_haiku:
+ gb_printf_err("-no-crt on unix systems requires either -default-to-nil-allocator or -default-to-panic-allocator to also be present because the default allocator requires crt\n");
+ return false;
+ }
+ }
if (bc->target_features_string.len != 0) {
enable_target_feature({}, bc->target_features_string);
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index 09ca0bc23..c3c217ec7 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -89,6 +89,7 @@ gb_internal void check_or_else_split_types(CheckerContext *c, Operand *x, String
gb_internal void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint) {
+ ERROR_BLOCK();
gbString t = type_to_string(x.type);
error(x.expr, "'%.*s' does not return a value, value is of type %s", LIT(name), t);
if (is_type_union(type_deref(x.type))) {
@@ -1264,6 +1265,139 @@ gb_internal LoadDirectiveResult check_load_directive(CheckerContext *c, Operand
}
+gb_internal int file_cache_sort_cmp(void const *x, void const *y) {
+ LoadFileCache const *a = *(LoadFileCache const **)(x);
+ LoadFileCache const *b = *(LoadFileCache const **)(y);
+ return string_compare(a->path, b->path);
+}
+
+gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint, bool err_on_not_found) {
+ ast_node(ce, CallExpr, call);
+ ast_node(bd, BasicDirective, ce->proc);
+ String name = bd->name.string;
+ GB_ASSERT(name == "load_directory");
+
+ if (ce->args.count != 1) {
+ error(ce->args[0], "'#%.*s' expects 1 argument, got %td", LIT(name), ce->args.count);
+ return LoadDirective_Error;
+ }
+
+ Ast *arg = ce->args[0];
+ Operand o = {};
+ check_expr(c, &o, arg);
+ if (o.mode != Addressing_Constant) {
+ error(arg, "'#%.*s' expected a constant string argument", LIT(name));
+ return LoadDirective_Error;
+ }
+
+ if (!is_type_string(o.type)) {
+ gbString str = type_to_string(o.type);
+ error(arg, "'#%.*s' expected a constant string, got %s", LIT(name), str);
+ gb_string_free(str);
+ return LoadDirective_Error;
+ }
+
+ GB_ASSERT(o.value.kind == ExactValue_String);
+
+ init_core_load_directory_file(c->checker);
+
+ operand->type = t_load_directory_file_slice;
+ operand->mode = Addressing_Value;
+
+
+ String original_string = o.value.value_string;
+ String path;
+ if (gb_path_is_absolute((char*)original_string.text)) {
+ path = original_string;
+ } else {
+ String base_dir = dir_from_path(get_file_path_string(call->file_id));
+
+ BlockingMutex *ignore_mutex = nullptr;
+ bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path);
+ gb_unused(ok);
+ }
+ MUTEX_GUARD(&c->info->load_directory_mutex);
+
+
+ gbFileError file_error = gbFileError_None;
+
+ Array<LoadFileCache *> file_caches = {};
+
+ LoadDirectoryCache **cache_ptr = string_map_get(&c->info->load_directory_cache, path);
+ LoadDirectoryCache *cache = cache_ptr ? *cache_ptr : nullptr;
+ if (cache) {
+ file_error = cache->file_error;
+ }
+ defer ({
+ if (cache == nullptr) {
+ LoadDirectoryCache *new_cache = gb_alloc_item(permanent_allocator(), LoadDirectoryCache);
+ new_cache->path = path;
+ new_cache->files = file_caches;
+ new_cache->file_error = file_error;
+ string_map_set(&c->info->load_directory_cache, path, new_cache);
+
+ map_set(&c->info->load_directory_map, call, new_cache);
+ } else {
+ cache->file_error = file_error;
+ }
+ });
+
+
+ LoadDirectiveResult result = LoadDirective_Success;
+
+
+ if (cache == nullptr) {
+ Array<FileInfo> list = {};
+ ReadDirectoryError rd_err = read_directory(path, &list);
+ defer (array_free(&list));
+
+ if (list.count == 1) {
+ GB_ASSERT(path != list[0].fullpath);
+ }
+
+
+ switch (rd_err) {
+ case ReadDirectory_InvalidPath:
+ error(call, "%.*s error - invalid path: %.*s", LIT(name), LIT(original_string));
+ return LoadDirective_NotFound;
+ case ReadDirectory_NotExists:
+ error(call, "%.*s error - path does not exist: %.*s", LIT(name), LIT(original_string));
+ return LoadDirective_NotFound;
+ case ReadDirectory_Permission:
+ error(call, "%.*s error - unknown error whilst reading path, %.*s", LIT(name), LIT(original_string));
+ return LoadDirective_Error;
+ case ReadDirectory_NotDir:
+ error(call, "%.*s error - expected a directory, got a file: %.*s", LIT(name), LIT(original_string));
+ return LoadDirective_Error;
+ case ReadDirectory_Empty:
+ error(call, "%.*s error - empty directory: %.*s", LIT(name), LIT(original_string));
+ return LoadDirective_NotFound;
+ case ReadDirectory_Unknown:
+ error(call, "%.*s error - unknown error whilst reading path %.*s", LIT(name), LIT(original_string));
+ return LoadDirective_Error;
+ }
+
+ isize files_to_reserve = list.count+1; // always reserve 1
+
+ file_caches = array_make<LoadFileCache *>(heap_allocator(), 0, files_to_reserve);
+
+ for (FileInfo fi : list) {
+ LoadFileCache *cache = nullptr;
+ if (cache_load_file_directive(c, call, fi.fullpath, err_on_not_found, &cache)) {
+ array_add(&file_caches, cache);
+ } else {
+ result = LoadDirective_Error;
+ }
+ }
+
+ array_sort(file_caches, file_cache_sort_cmp);
+
+ }
+
+ return result;
+}
+
+
gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) {
ast_node(ce, CallExpr, call);
@@ -1291,6 +1425,8 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
operand->mode = Addressing_Value;
} else if (name == "load") {
return check_load_directive(c, operand, call, type_hint, true) == LoadDirective_Success;
+ } else if (name == "load_directory") {
+ return check_load_directory_directive(c, operand, call, type_hint, true) == LoadDirective_Success;
} else if (name == "load_hash") {
if (ce->args.count != 2) {
if (ce->args.count == 0) {
@@ -1408,58 +1544,6 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
return true;
}
return false;
- } else if (name == "load_or") {
- error(call, "'#load_or' has now been removed in favour of '#load(path) or_else default'");
-
- if (ce->args.count != 2) {
- if (ce->args.count == 0) {
- error(ce->close, "'#load_or' expects 2 arguments, got 0");
- } else {
- error(ce->args[0], "'#load_or' expects 2 arguments, got %td", ce->args.count);
- }
- return false;
- }
-
- Ast *arg = ce->args[0];
- Operand o = {};
- check_expr(c, &o, arg);
- if (o.mode != Addressing_Constant) {
- error(arg, "'#load_or' expected a constant string argument");
- return false;
- }
-
- if (!is_type_string(o.type)) {
- gbString str = type_to_string(o.type);
- error(arg, "'#load_or' expected a constant string, got %s", str);
- gb_string_free(str);
- return false;
- }
-
- Ast *default_arg = ce->args[1];
- Operand default_op = {};
- check_expr_with_type_hint(c, &default_op, default_arg, t_u8_slice);
- if (default_op.mode != Addressing_Constant) {
- error(arg, "'#load_or' expected a constant '[]byte' argument");
- return false;
- }
-
- if (!are_types_identical(base_type(default_op.type), t_u8_slice)) {
- gbString str = type_to_string(default_op.type);
- error(arg, "'#load_or' expected a constant '[]byte', got %s", str);
- gb_string_free(str);
- return false;
- }
- GB_ASSERT(o.value.kind == ExactValue_String);
- String original_string = o.value.value_string;
-
- operand->type = t_u8_slice;
- operand->mode = Addressing_Constant;
- LoadFileCache *cache = nullptr;
- if (cache_load_file_directive(c, call, original_string, false, &cache)) {
- operand->value = exact_value_string(cache->data);
- } else {
- operand->value = default_op.value;
- }
} else if (name == "assert") {
if (ce->args.count != 1 && ce->args.count != 2) {
error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count);
@@ -1482,6 +1566,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
}
if (!operand->value.value_bool) {
+ ERROR_BLOCK();
gbString arg1 = expr_to_string(ce->args[0]);
gbString arg2 = {};
@@ -1507,6 +1592,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
operand->type = t_untyped_bool;
operand->mode = Addressing_Constant;
} else if (name == "panic") {
+ ERROR_BLOCK();
if (ce->args.count != 1) {
error(call, "'#panic' expects 1 argument, got %td", ce->args.count);
return false;
@@ -3307,6 +3393,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
elem->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count);
elem->Struct.node = dummy_node_struct;
type_set_offsets(elem);
+ wait_signal_set(&elem->Struct.fields_wait_signal);
}
Type *soa_type = make_soa_struct_slice(c, dummy_node_soa, nullptr, elem);
@@ -3405,7 +3492,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
} else {
GB_ASSERT(t->kind == Type_Matrix);
- operand->type = alloc_type_matrix(t->Matrix.elem, t->Matrix.column_count, t->Matrix.row_count);
+ operand->type = alloc_type_matrix(t->Matrix.elem, t->Matrix.column_count, t->Matrix.row_count, nullptr, nullptr, t->Matrix.is_row_major);
}
operand->type = check_matrix_type_hint(operand->type, type_hint);
break;
@@ -3473,7 +3560,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
operand->mode = Addressing_Value;
- operand->type = alloc_type_matrix(elem, xt->Array.count, yt->Array.count);
+ operand->type = alloc_type_matrix(elem, xt->Array.count, yt->Array.count, nullptr, nullptr, false);
operand->type = check_matrix_type_hint(operand->type, type_hint);
break;
}
@@ -3680,6 +3767,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
soa_struct->Struct.tags[i] = old_struct->Struct.tags[i];
}
}
+ wait_signal_set(&soa_struct->Struct.fields_wait_signal);
Token token = {};
token.string = str_lit("Base_Type");
@@ -4001,8 +4089,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
}
- operand->mode = Addressing_OptionalOk;
- operand->type = default_type(x.type);
+ operand->mode = Addressing_Value;
+ operand->type = make_optional_ok_type(default_type(x.type));
}
break;
@@ -4845,6 +4933,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case TargetOs_essence:
case TargetOs_freebsd:
case TargetOs_openbsd:
+ case TargetOs_haiku:
switch (build_context.metrics.arch) {
case TargetArch_i386:
case TargetArch_amd64:
@@ -4892,8 +4981,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
switch (bt->kind) {
case Type_Basic:
switch (bt->Basic.kind) {
+ case Basic_complex32: operand->type = t_f16; break;
case Basic_complex64: operand->type = t_f32; break;
case Basic_complex128: operand->type = t_f64; break;
+ case Basic_quaternion64: operand->type = t_f16; break;
case Basic_quaternion128: operand->type = t_f32; break;
case Basic_quaternion256: operand->type = t_f64; break;
}
@@ -5684,7 +5775,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return false;
}
- operand->value = exact_value_bool(is_type_subtype_of(op_src.type, op_dst.type));
+ operand->value = exact_value_bool(is_type_subtype_of_and_allow_polymorphic(op_src.type, op_dst.type));
operand->mode = Addressing_Constant;
operand->type = t_untyped_bool;
} break;
@@ -5735,6 +5826,26 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
break;
+ case BuiltinProc_type_bit_set_backing_type:
+ {
+ Operand op = {};
+ Type *type = check_type(c, ce->args[0]);
+ Type *bt = base_type(type);
+ if (bt == nullptr || bt == t_invalid) {
+ error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ if (bt->kind != Type_BitSet) {
+ gbString s = type_to_string(type);
+ error(ce->args[0], "Expected a bit_set type for '%.*s', got %s", LIT(builtin_name), s);
+ return false;
+ }
+
+ operand->mode = Addressing_Type;
+ operand->type = bit_set_to_int(bt);
+ break;
+ }
+
case BuiltinProc_type_equal_proc:
{
Operand op = {};
@@ -5903,6 +6014,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return false;
}
+ enable_target_feature({}, str_lit("atomics"));
+
Operand ptr = {};
Operand expected = {};
Operand timeout = {};
@@ -5955,6 +6068,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return false;
}
+ enable_target_feature({}, str_lit("atomics"));
+
Operand ptr = {};
Operand waiters = {};
check_expr(c, &ptr, ce->args[0]); if (ptr.mode == Addressing_Invalid) return false;
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index ed3a109c2..952a877a4 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -210,6 +210,7 @@ gb_internal bool is_type_distinct(Ast *node) {
case Ast_UnionType:
case Ast_EnumType:
case Ast_ProcType:
+ case Ast_BitFieldType:
return true;
case Ast_PointerType:
@@ -1143,7 +1144,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
"\tat %s",
LIT(name), token_pos_to_string(pos));
}
- } else if (!are_types_identical(this_type, other_type)) {
+ } else if (!signature_parameter_similar_enough(this_type, other_type)) {
error(d->proc_lit,
"Foreign entity '%.*s' previously declared elsewhere with a different type\n"
"\tat %s",
@@ -1284,7 +1285,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
TokenPos pos = f->token.pos;
Type *this_type = base_type(e->type);
Type *other_type = base_type(f->type);
- if (!are_types_identical(this_type, other_type)) {
+ if (!signature_parameter_similar_enough(this_type, other_type)) {
error(e->token,
"Foreign entity '%.*s' previously declared elsewhere with a different type\n"
"\tat %s",
@@ -1629,6 +1630,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
Entity *uvar = entry.uvar;
Entity *prev = scope_insert_no_mutex(ctx->scope, uvar);
if (prev != nullptr) {
+ ERROR_BLOCK();
error(e->token, "Namespace collision while 'using' procedure argument '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string));
error_line("%.*s != %.*s\n", LIT(uvar->token.string), LIT(prev->token.string));
break;
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index bc7ff1bbb..b893b3a00 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -79,7 +79,6 @@ gb_internal Type * check_type_expr (CheckerContext *c, Ast *exp
gb_internal Type * make_optional_ok_type (Type *value, bool typed=true);
gb_internal Entity * check_selector (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint);
gb_internal Entity * check_ident (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name);
-gb_internal Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure);
gb_internal void check_not_tuple (CheckerContext *c, Operand *operand);
gb_internal void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type);
gb_internal gbString expr_to_string (Ast *expression);
@@ -100,7 +99,7 @@ gb_internal void check_union_type (CheckerContext *c, Type *un
gb_internal Type * check_init_variable (CheckerContext *c, Entity *e, Operand *operand, String context_name);
-gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type);
+gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size=0);
gb_internal void add_map_key_type_dependencies(CheckerContext *ctx, Type *key);
gb_internal Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
@@ -108,7 +107,7 @@ gb_internal Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_
gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint);
-gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_);
+gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_, bool change_operand=true);
gb_internal void check_or_else_right_type(CheckerContext *c, Ast *expr, String const &name, Type *right_type);
gb_internal void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_);
@@ -119,6 +118,12 @@ gb_internal bool is_diverging_expr(Ast *expr);
gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_);
+gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr);
+
+gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array<Operand> const &ordered_operands);
+
+gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finish);
+
enum LoadDirectiveResult {
LoadDirective_Success = 0,
LoadDirective_Error = 1,
@@ -621,7 +626,7 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type);
#define MAXIMUM_TYPE_DISTANCE 10
-gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) {
+gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming) {
if (c == nullptr) {
GB_ASSERT(operand->mode == Addressing_Value);
GB_ASSERT(is_type_typed(operand->type));
@@ -830,7 +835,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
if (dst->Union.variants.count == 1) {
Type *vt = dst->Union.variants[0];
- i64 score = check_distance_between_types(c, operand, vt);
+ i64 score = check_distance_between_types(c, operand, vt, allow_array_programming);
if (score >= 0) {
return score+2;
}
@@ -838,7 +843,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
i64 prev_lowest_score = -1;
i64 lowest_score = -1;
for (Type *vt : dst->Union.variants) {
- i64 score = check_distance_between_types(c, operand, vt);
+ i64 score = check_distance_between_types(c, operand, vt, allow_array_programming);
if (score >= 0) {
if (lowest_score < 0) {
lowest_score = score;
@@ -861,14 +866,14 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
}
if (is_type_relative_pointer(dst)) {
- i64 score = check_distance_between_types(c, operand, dst->RelativePointer.pointer_type);
+ i64 score = check_distance_between_types(c, operand, dst->RelativePointer.pointer_type, allow_array_programming);
if (score >= 0) {
return score+2;
}
}
if (is_type_relative_multi_pointer(dst)) {
- i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type);
+ i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type, allow_array_programming);
if (score >= 0) {
return score+2;
}
@@ -894,19 +899,21 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
}
}
- if (is_type_array(dst)) {
- Type *elem = base_array_type(dst);
- i64 distance = check_distance_between_types(c, operand, elem);
- if (distance >= 0) {
- return distance + 6;
+ if (allow_array_programming) {
+ if (is_type_array(dst)) {
+ Type *elem = base_array_type(dst);
+ i64 distance = check_distance_between_types(c, operand, elem, allow_array_programming);
+ if (distance >= 0) {
+ return distance + 6;
+ }
}
- }
- if (is_type_simd_vector(dst)) {
- Type *dst_elem = base_array_type(dst);
- i64 distance = check_distance_between_types(c, operand, dst_elem);
- if (distance >= 0) {
- return distance + 6;
+ if (is_type_simd_vector(dst)) {
+ Type *dst_elem = base_array_type(dst);
+ i64 distance = check_distance_between_types(c, operand, dst_elem, allow_array_programming);
+ if (distance >= 0) {
+ return distance + 6;
+ }
}
}
@@ -916,7 +923,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
}
if (dst->Matrix.row_count == dst->Matrix.column_count) {
Type *dst_elem = base_array_type(dst);
- i64 distance = check_distance_between_types(c, operand, dst_elem);
+ i64 distance = check_distance_between_types(c, operand, dst_elem, allow_array_programming);
if (distance >= 0) {
return distance + 7;
}
@@ -964,9 +971,9 @@ gb_internal i64 assign_score_function(i64 distance, bool is_variadic=false) {
}
-gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false) {
+gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false, bool allow_array_programming=true) {
i64 score = 0;
- i64 distance = check_distance_between_types(c, operand, type);
+ i64 distance = check_distance_between_types(c, operand, type, allow_array_programming);
bool ok = distance >= 0;
if (ok) {
score = assign_score_function(distance, is_variadic);
@@ -976,9 +983,9 @@ gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *o
}
-gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) {
+gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming=true) {
i64 score = 0;
- return check_is_assignable_to_with_score(c, operand, type, &score);
+ return check_is_assignable_to_with_score(c, operand, type, &score, /*is_variadic*/false, allow_array_programming);
}
gb_internal bool internal_check_is_assignable_to(Type *src, Type *dst) {
@@ -1170,6 +1177,16 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
type_str, type_extra,
LIT(context_name));
check_assignment_error_suggestion(c, operand, type);
+
+ if (context_name == "procedure argument") {
+ Type *src = base_type(operand->type);
+ Type *dst = base_type(type);
+ if (is_type_slice(src) && are_types_identical(src->Slice.elem, dst)) {
+ gbString a = expr_to_string(operand->expr);
+ error_line("\tSuggestion: Did you mean to pass the slice into the variadic parameter with ..%s?\n\n", a);
+ gb_string_free(a);
+ }
+ }
}
break;
}
@@ -1241,7 +1258,7 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
}
case Type_Pointer:
if (source->kind == Type_Pointer) {
- isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->Pointer.elem);
+ isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->Pointer.elem, /*level*/0, /*src_is_ptr*/false, /*allow_polymorphic*/true);
if (level > 0) {
return true;
}
@@ -1413,7 +1430,9 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
return ok;
}
- // return check_is_assignable_to(c, &o, poly);
+
+ // NOTE(bill): Check for subtypes of
+ // return check_is_assignable_to(c, &o, poly); // && is_type_subtype_of_and_allow_polymorphic(o.type, poly);
}
return false;
case Type_Tuple:
@@ -1526,6 +1545,55 @@ gb_internal bool check_cycle(CheckerContext *c, Entity *curr, bool report) {
return false;
}
+struct CIdentSuggestion {
+ String name;
+ String msg;
+};
+
+// NOTE(bill): this linear look-up table might be slow but because it's an error case, it should be fine
+gb_internal CIdentSuggestion const c_ident_suggestions[] = {
+ {str_lit("while"), str_lit("'for'? Odin only has one loop construct: 'for'")},
+
+ {str_lit("sizeof"), str_lit("'size_of'?")},
+ {str_lit("alignof"), str_lit("'align_of'?")},
+ {str_lit("offsetof"), str_lit("'offset_of'?")},
+
+ {str_lit("_Bool"), str_lit("'bool'?")},
+
+ {str_lit("char"), str_lit("'u8', 'i8', or 'c.char' (which is part of 'core:c')?")},
+ {str_lit("short"), str_lit("'i16' or 'c.short' (which is part of 'core:c')?")},
+ {str_lit("long"), str_lit("'c.long' (which is part of 'core:c')?")},
+ {str_lit("float"), str_lit("'f32'?")},
+ {str_lit("double"), str_lit("'f64'?")},
+ {str_lit("unsigned"), str_lit("'c.uint' (which is part of 'core:c')?")},
+ {str_lit("signed"), str_lit("'c.int' (which is part of 'core:c')?")},
+
+ {str_lit("size_t"), str_lit("'uint', or 'c.size_t' (which is part of 'core:c')?")},
+ {str_lit("ssize_t"), str_lit("'int', or 'c.ssize_t' (which is part of 'core:c')?")},
+
+ {str_lit("uintptr_t"), str_lit("'uintptr'?")},
+ {str_lit("intptr_t"), str_lit("'uintptr' or `int` or something else?")},
+ {str_lit("ptrdiff_t"), str_lit("'int' or 'c.ptrdiff_t' (which is part of 'core:c')?")},
+ {str_lit("intmax_t"), str_lit("'c.intmax_t' (which is part of 'core:c')?")},
+ {str_lit("uintmax_t"), str_lit("'c.uintmax_t' (which is part of 'core:c')?")},
+
+ {str_lit("uint8_t"), str_lit("'u8'?")},
+ {str_lit("int8_t"), str_lit("'i8'?")},
+ {str_lit("uint16_t"), str_lit("'u16'?")},
+ {str_lit("int16_t"), str_lit("'i16'?")},
+ {str_lit("uint32_t"), str_lit("'u32'?")},
+ {str_lit("int32_t"), str_lit("'i32'?")},
+ {str_lit("uint64_t"), str_lit("'u64'?")},
+ {str_lit("int64_t"), str_lit("'i64'?")},
+ {str_lit("uint128_t"), str_lit("'u128'?")},
+ {str_lit("int128_t"), str_lit("'i128'?")},
+
+ {str_lit("float32"), str_lit("'f32'?")},
+ {str_lit("float64"), str_lit("'f64'?")},
+ {str_lit("float32_t"), str_lit("'f32'?")},
+ {str_lit("float64_t"), str_lit("'f64'?")},
+};
+
gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name) {
GB_ASSERT(n->kind == Ast_Ident);
o->mode = Addressing_Invalid;
@@ -1537,7 +1605,16 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
if (is_blank_ident(name)) {
error(n, "'_' cannot be used as a value");
} else {
+ ERROR_BLOCK();
error(n, "Undeclared name: %.*s", LIT(name));
+
+ // NOTE(bill): Loads of checks for C programmers
+
+ for (CIdentSuggestion const &suggestion : c_ident_suggestions) {
+ if (name == suggestion.name) {
+ error_line("\tSuggestion: Did you mean %.*s\n", LIT(suggestion.msg));
+ }
+ }
}
o->type = t_invalid;
o->mode = Addressing_Invalid;
@@ -1663,7 +1740,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
if (check_cycle(c, e, true)) {
o->type = t_invalid;
}
- if (o->type != nullptr && type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
+ if (o->type != nullptr && o->type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
o->type = base_type(o->type);
}
@@ -1736,6 +1813,21 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
}
break;
+ case Token_Mul:
+ {
+ ERROR_BLOCK();
+ error(op, "Operator '%.*s' is not a valid unary operator in Odin", LIT(op.string));
+ if (is_type_pointer(o->type)) {
+ str = expr_to_string(o->expr);
+ error_line("\tSuggestion: Did you mean '%s^'?\n", str);
+ o->type = type_deref(o->type);
+ } else if (is_type_multi_pointer(o->type)) {
+ str = expr_to_string(o->expr);
+ error_line("\tSuggestion: The value is a multi-pointer, did you mean '%s[0]'?\n", str);
+ o->type = type_deref(o->type, true);
+ }
+ }
+ break;
default:
error(op, "Unknown operator '%.*s'", LIT(op.string));
return false;
@@ -1884,33 +1976,55 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
BigInt i = v.value_integer;
- i64 bit_size = type_size_of(type);
+ i64 byte_size = type_size_of(type);
BigInt umax = {};
BigInt imin = {};
BigInt imax = {};
- if (bit_size < 16) {
- big_int_from_u64(&umax, unsigned_integer_maxs[bit_size]);
- big_int_from_i64(&imin, signed_integer_mins[bit_size]);
- big_int_from_i64(&imax, signed_integer_maxs[bit_size]);
- } else {
+ if (c->bit_field_bit_size > 0) {
+ i64 bit_size = gb_min(cast(i64)(8*byte_size), cast(i64)c->bit_field_bit_size);
+
big_int_from_u64(&umax, 1);
big_int_from_i64(&imin, 1);
big_int_from_i64(&imax, 1);
- BigInt bi128 = {};
- BigInt bi127 = {};
- big_int_from_i64(&bi128, 128);
- big_int_from_i64(&bi127, 127);
+ BigInt bu = {};
+ BigInt bi = {};
+ big_int_from_i64(&bu, bit_size);
+ big_int_from_i64(&bi, bit_size-1);
- big_int_shl_eq(&umax, &bi128);
+ big_int_shl_eq(&umax, &bu);
mp_decr(&umax);
- big_int_shl_eq(&imin, &bi127);
+ big_int_shl_eq(&imin, &bi);
big_int_neg(&imin, &imin);
- big_int_shl_eq(&imax, &bi127);
+ big_int_shl_eq(&imax, &bi);
mp_decr(&imax);
+ } else {
+ if (byte_size < 16) {
+ big_int_from_u64(&umax, unsigned_integer_maxs[byte_size]);
+ big_int_from_i64(&imin, signed_integer_mins[byte_size]);
+ big_int_from_i64(&imax, signed_integer_maxs[byte_size]);
+ } else {
+ big_int_from_u64(&umax, 1);
+ big_int_from_i64(&imin, 1);
+ big_int_from_i64(&imax, 1);
+
+ BigInt bi128 = {};
+ BigInt bi127 = {};
+ big_int_from_i64(&bi128, 128);
+ big_int_from_i64(&bi127, 127);
+
+ big_int_shl_eq(&umax, &bi128);
+ mp_decr(&umax);
+
+ big_int_shl_eq(&imin, &bi127);
+ big_int_neg(&imin, &imin);
+
+ big_int_shl_eq(&imax, &bi127);
+ mp_decr(&imax);
+ }
}
switch (type->Basic.kind) {
@@ -2069,11 +2183,17 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
}
-gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type) {
+gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size=0) {
if (is_type_integer(type) && o->value.kind == ExactValue_Integer) {
gbString b = type_to_string(type);
i64 sz = type_size_of(type);
+ i64 bit_size = 8*sz;
+ bool size_changed = false;
+ if (max_bit_size > 0) {
+ size_changed = (bit_size != max_bit_size);
+ bit_size = gb_min(bit_size, max_bit_size);
+ }
BigInt *bi = &o->value.value_integer;
if (is_type_unsigned(type)) {
if (big_int_is_neg(bi)) {
@@ -2081,25 +2201,36 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o,
} else {
BigInt one = big_int_make_u64(1);
BigInt max_size = big_int_make_u64(1);
- BigInt bits = big_int_make_i64(8*sz);
+ BigInt bits = big_int_make_i64(bit_size);
big_int_shl_eq(&max_size, &bits);
big_int_sub_eq(&max_size, &one);
String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
- error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+
+ if (size_changed) {
+ error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str));
+ } else {
+ error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+ }
}
} else {
BigInt zero = big_int_make_u64(0);
BigInt one = big_int_make_u64(1);
BigInt max_size = big_int_make_u64(1);
- BigInt bits = big_int_make_i64(8*sz - 1);
+ BigInt bits = big_int_make_i64(bit_size - 1);
big_int_shl_eq(&max_size, &bits);
+
+ String max_size_str = {};
if (big_int_is_neg(bi)) {
big_int_neg(&max_size, &max_size);
- String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
- error_line("\tThe minimum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+ max_size_str = big_int_to_string(temporary_allocator(), &max_size);
} else {
big_int_sub_eq(&max_size, &one);
- String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
+ max_size_str = big_int_to_string(temporary_allocator(), &max_size);
+ }
+
+ if (size_changed) {
+ error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str));
+ } else {
error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
}
}
@@ -2110,7 +2241,7 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o,
}
return false;
}
-gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
+gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size) {
gbString a = expr_to_string(o->expr);
gbString b = type_to_string(type);
defer(
@@ -2141,8 +2272,23 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o
error_line("\t whereas slices in general are assumed to be mutable.\n");
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
error_line("\tSuggestion: the expression may be casted to %s\n", b);
- } else if (check_integer_exceed_suggestion(c, o, type)) {
+ } else if (check_integer_exceed_suggestion(c, o, type, max_bit_size)) {
return;
+ } else if (is_expr_inferred_fixed_array(c->type_hint_expr) && is_type_array_like(type) && is_type_array_like(o->type)) {
+ gbString s = expr_to_string(c->type_hint_expr);
+ error_line("\tSuggestion: make sure that `%s` is attached to the compound literal directly\n", s);
+ gb_string_free(s);
+ } else if (is_type_pointer(type) &&
+ o->mode == Addressing_Variable &&
+ are_types_identical(type_deref(type), o->type)) {
+ gbString s = expr_to_string(o->expr);
+ error_line("\tSuggestion: Did you mean `&%s`\n", s);
+ gb_string_free(s);
+ } else if (is_type_pointer(o->type) &&
+ are_types_identical(type_deref(o->type), type)) {
+ gbString s = expr_to_string(o->expr);
+ error_line("\tSuggestion: Did you mean `%s^`\n", s);
+ gb_string_free(s);
}
}
@@ -2215,13 +2361,18 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ
if (!is_type_integer(o->type) && is_type_integer(type)) {
error(o->expr, "'%s' truncated to '%s', got %s", a, b, s);
} else {
+ i64 max_bit_size = 0;
+ if (ctx->bit_field_bit_size) {
+ max_bit_size = ctx->bit_field_bit_size;
+ }
+
if (are_types_identical(o->type, type)) {
error(o->expr, "Numeric value '%s' from '%s' cannot be represented by '%s'", s, a, b);
} else {
error(o->expr, "Cannot convert numeric value '%s' from '%s' to '%s' from '%s'", s, a, b, c);
}
- check_assignment_error_suggestion(ctx, o, type);
+ check_assignment_error_suggestion(ctx, o, type, max_bit_size);
}
} else {
error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s);
@@ -2232,6 +2383,11 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ
}
gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) {
+ if (o->expr && o->expr->kind == Ast_SelectorExpr) {
+ if (o->expr->SelectorExpr.is_bit_field) {
+ return true;
+ }
+ }
if (o->mode == Addressing_OptionalOk) {
Ast *expr = unselector_expr(o->expr);
if (expr->kind != Ast_TypeAssertion) {
@@ -2263,10 +2419,6 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) {
}
gb_internal void check_old_for_or_switch_value_usage(Ast *expr) {
- if (!(build_context.strict_style || (check_vet_flags(expr) & VetFlag_Style))) {
- return;
- }
-
Entity *e = entity_of_node(expr);
if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) {
GB_ASSERT(e->kind == Entity_Variable);
@@ -2276,7 +2428,7 @@ gb_internal void check_old_for_or_switch_value_usage(Ast *expr) {
if ((e->flags & EntityFlag_ForValue) != 0) {
Type *parent_type = type_deref(e->Variable.for_loop_parent_type);
- error(expr, "Assuming a for-in defined value is addressable as the iterable is passed by value has been disallowed with '-strict-style'.");
+ error(expr, "Assuming a for-in defined value is addressable as the iterable is passed by value has been disallowed.");
if (is_type_map(parent_type)) {
error_line("\tSuggestion: Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string));
@@ -2286,7 +2438,7 @@ gb_internal void check_old_for_or_switch_value_usage(Ast *expr) {
} else {
GB_ASSERT((e->flags & EntityFlag_SwitchValue) != 0);
- error(expr, "Assuming a switch-in defined value is addressable as the iterable is passed by value has been disallowed with '-strict-style'.");
+ error(expr, "Assuming a switch-in defined value is addressable as the iterable is passed by value has been disallowed.");
error_line("\tSuggestion: Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string));
}
}
@@ -2304,6 +2456,8 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
Entity *e = entity_of_node(ue->expr);
if (e != nullptr && (e->flags & EntityFlag_Param) != 0) {
error(op, "Cannot take the pointer address of '%s' which is a procedure parameter", str);
+ } else if (e != nullptr && (e->flags & EntityFlag_BitFieldField) != 0) {
+ error(op, "Cannot take the pointer address of '%s' which is a bit_field's field", str);
} else {
switch (o->mode) {
case Addressing_Constant:
@@ -2877,6 +3031,13 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
}
}
+ if (is_type_bit_field(src)) {
+ return are_types_identical(core_type(src->BitField.backing_type), dst);
+ }
+ if (is_type_bit_field(dst)) {
+ return are_types_identical(src, core_type(dst->BitField.backing_type));
+ }
+
if (is_type_integer(src) && is_type_rune(dst)) {
return true;
}
@@ -2988,6 +3149,13 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
}
// proc <-> proc
if (is_type_proc(src) && is_type_proc(dst)) {
+ if (is_type_polymorphic(dst)) {
+ if (is_type_polymorphic(src) &&
+ operand->mode == Addressing_Variable) {
+ return true;
+ }
+ return false;
+ }
return true;
}
@@ -3000,6 +3168,14 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
return true;
}
+
+ if (is_type_array(dst)) {
+ Type *elem = base_array_type(dst);
+ if (check_is_castable_to(c, operand, elem)) {
+ return true;
+ }
+ }
+
if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
if (src->SimdVector.count != dst->SimdVector.count) {
return false;
@@ -3067,7 +3243,6 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
bool is_const_expr = x->mode == Addressing_Constant;
bool can_convert = check_cast_internal(c, x, type);
-
if (!can_convert) {
TEMPORARY_ALLOCATOR_GUARD();
gbString expr_str = expr_to_string(x->expr, temporary_allocator());
@@ -3108,6 +3283,26 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
final_type = default_type(x->type);
}
update_untyped_expr_type(c, x->expr, final_type, true);
+ } else {
+ Type *src = core_type(x->type);
+ Type *dst = core_type(type);
+ if (src != dst) {
+ bool const REQUIRE = true;
+ if (is_type_integer_128bit(src) && is_type_float(dst)) {
+ add_package_dependency(c, "runtime", "floattidf_unsigned", REQUIRE);
+ add_package_dependency(c, "runtime", "floattidf", REQUIRE);
+ } else if (is_type_integer_128bit(dst) && is_type_float(src)) {
+ add_package_dependency(c, "runtime", "fixunsdfti", REQUIRE);
+ add_package_dependency(c, "runtime", "fixunsdfdi", REQUIRE);
+ } else if (src == t_f16 && is_type_float(dst)) {
+ add_package_dependency(c, "runtime", "gnu_h2f_ieee", REQUIRE);
+ add_package_dependency(c, "runtime", "extendhfsf2", REQUIRE);
+ } else if (is_type_float(dst) && dst == t_f16) {
+ add_package_dependency(c, "runtime", "truncsfhf2", REQUIRE);
+ add_package_dependency(c, "runtime", "truncdfhf2", REQUIRE);
+ add_package_dependency(c, "runtime", "gnu_f2h_ieee", REQUIRE);
+ }
+ }
}
x->type = type;
@@ -3247,6 +3442,13 @@ gb_internal Type *check_matrix_type_hint(Type *matrix, Type *type_hint) {
Type *th = base_type(type_hint);
if (are_types_identical(th, xt)) {
return type_hint;
+ } else if (xt->kind == Type_Matrix && th->kind == Type_Matrix) {
+ if (!are_types_identical(xt->Matrix.elem, th->Matrix.elem)) {
+ // ignore
+ } if (xt->Matrix.row_count == th->Matrix.row_count &&
+ xt->Matrix.column_count == th->Matrix.column_count) {
+ return type_hint;
+ }
} else if (xt->kind == Type_Matrix && th->kind == Type_Array) {
if (!are_types_identical(xt->Matrix.elem, th->Array.elem)) {
// ignore
@@ -3281,6 +3483,11 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand
if (xt->Matrix.column_count != yt->Matrix.row_count) {
goto matrix_error;
}
+
+ if (xt->Matrix.is_row_major != yt->Matrix.is_row_major) {
+ goto matrix_error;
+ }
+
x->mode = Addressing_Value;
if (are_types_identical(xt, yt)) {
if (!is_type_named(x->type) && is_type_named(y->type)) {
@@ -3288,7 +3495,8 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand
x->type = y->type;
}
} else {
- x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count);
+ bool is_row_major = xt->Matrix.is_row_major && yt->Matrix.is_row_major;
+ x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count, nullptr, nullptr, is_row_major);
}
goto matrix_success;
} else if (yt->kind == Type_Array) {
@@ -3302,10 +3510,10 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand
// Treat arrays as column vectors
x->mode = Addressing_Value;
- if (type_hint == nullptr && xt->Matrix.row_count == yt->Array.count) {
+ if (xt->Matrix.row_count == yt->Array.count) {
x->type = y->type;
} else {
- x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, 1);
+ x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, 1, nullptr, nullptr, xt->Matrix.is_row_major);
}
goto matrix_success;
}
@@ -3333,10 +3541,10 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand
// Treat arrays as row vectors
x->mode = Addressing_Value;
- if (type_hint == nullptr && yt->Matrix.column_count == xt->Array.count) {
+ if (yt->Matrix.column_count == xt->Array.count) {
x->type = x->type;
} else {
- x->type = alloc_type_matrix(yt->Matrix.elem, 1, yt->Matrix.column_count);
+ x->type = alloc_type_matrix(yt->Matrix.elem, 1, yt->Matrix.column_count, nullptr, nullptr, yt->Matrix.is_row_major);
}
goto matrix_success;
} else if (are_types_identical(yt->Matrix.elem, xt)) {
@@ -3729,8 +3937,16 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
return;
}
- if (op.kind == Token_Quo || op.kind == Token_QuoEq) {
- Type *bt = base_type(x->type);
+ bool REQUIRE = true;
+
+ Type *bt = base_type(x->type);
+ if (op.kind == Token_Mod || op.kind == Token_ModEq ||
+ op.kind == Token_ModMod || op.kind == Token_ModModEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_u128: add_package_dependency(c, "runtime", "umodti3", REQUIRE); break;
+ case Basic_i128: add_package_dependency(c, "runtime", "modti3", REQUIRE); break;
+ }
+ } else if (op.kind == Token_Quo || op.kind == Token_QuoEq) {
if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
case Basic_complex32: add_package_dependency(c, "runtime", "quo_complex32"); break;
case Basic_complex64: add_package_dependency(c, "runtime", "quo_complex64"); break;
@@ -3738,13 +3954,32 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
case Basic_quaternion64: add_package_dependency(c, "runtime", "quo_quaternion64"); break;
case Basic_quaternion128: add_package_dependency(c, "runtime", "quo_quaternion128"); break;
case Basic_quaternion256: add_package_dependency(c, "runtime", "quo_quaternion256"); break;
+
+ case Basic_u128: add_package_dependency(c, "runtime", "udivti3", REQUIRE); break;
+ case Basic_i128: add_package_dependency(c, "runtime", "divti3", REQUIRE); break;
}
} else if (op.kind == Token_Mul || op.kind == Token_MulEq) {
- Type *bt = base_type(x->type);
if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
- case Basic_quaternion64: add_package_dependency(c, "runtime", "mul_quaternion64"); break;
+ case Basic_quaternion64: add_package_dependency(c, "runtime", "mul_quaternion64"); break;
case Basic_quaternion128: add_package_dependency(c, "runtime", "mul_quaternion128"); break;
case Basic_quaternion256: add_package_dependency(c, "runtime", "mul_quaternion256"); break;
+
+
+ case Basic_u128:
+ case Basic_i128:
+ if (is_arch_wasm()) {
+ add_package_dependency(c, "runtime", "__multi3", REQUIRE);
+ }
+ break;
+ }
+ } else if (op.kind == Token_Shl || op.kind == Token_ShlEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_u128:
+ case Basic_i128:
+ if (is_arch_wasm()) {
+ add_package_dependency(c, "runtime", "__ashlti3", REQUIRE);
+ }
+ break;
}
}
@@ -3894,7 +4129,7 @@ gb_internal void update_untyped_expr_value(CheckerContext *c, Ast *e, ExactValue
}
}
-gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type) {
+gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type, bool ignore_error_block=false) {
gbString expr_str = expr_to_string(operand->expr);
gbString type_str = type_to_string(target_type);
gbString from_type_str = type_to_string(operand->type);
@@ -3908,7 +4143,9 @@ gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type
}
}
}
- ERROR_BLOCK();
+ if (!ignore_error_block) {
+ begin_error_block();
+ }
error(operand->expr, "Cannot convert untyped value '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text);
if (operand->value.kind == ExactValue_String) {
@@ -3923,6 +4160,11 @@ gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type
gb_string_free(type_str);
gb_string_free(expr_str);
operand->mode = Addressing_Invalid;
+
+ if (!ignore_error_block) {
+ end_error_block();
+ }
+
}
gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) {
@@ -4052,7 +4294,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
operand->mode = Addressing_Invalid;
ERROR_BLOCK();
- convert_untyped_error(c, operand, target_type);
+ convert_untyped_error(c, operand, target_type, true);
error_line("\tNote: Only a square matrix types can be initialized with a scalar value\n");
return;
} else {
@@ -4115,7 +4357,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
GB_ASSERT(first_success_index >= 0);
operand->mode = Addressing_Invalid;
- convert_untyped_error(c, operand, target_type);
+ convert_untyped_error(c, operand, target_type, true);
error_line("Ambiguous type conversion to '%s', which variant did you mean:\n\t", type_str);
i32 j = 0;
@@ -4140,9 +4382,10 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
ERROR_BLOCK();
operand->mode = Addressing_Invalid;
- convert_untyped_error(c, operand, target_type);
+ convert_untyped_error(c, operand, target_type, true);
if (count > 0) {
error_line("'%s' is a union which only excepts the following types:\n", type_str);
+
error_line("\t");
for (i32 i = 0; i < count; i++) {
Type *v = t->Union.variants[i];
@@ -4352,7 +4595,8 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
String name = fv->field->Ident.token.string;
Selection sub_sel = lookup_field(node->tav.type, name, false);
defer (array_free(&sub_sel.index));
- if (sub_sel.index[0] == index) {
+ if (sub_sel.index.count > 0 &&
+ sub_sel.index[0] == index) {
value = fv->value->tav.value;
found = true;
break;
@@ -4575,7 +4819,8 @@ gb_internal bool is_entity_declared_for_selector(Entity *entity, Scope *import_s
if (entity->kind == Entity_Builtin) {
// NOTE(bill): Builtin's are in the universal scope which is part of every scopes hierarchy
// This means that we should just ignore the found result through it
- *allow_builtin = entity->scope == import_scope || entity->scope != builtin_pkg->scope;
+ *allow_builtin = entity->scope == import_scope ||
+ (entity->scope != builtin_pkg->scope && entity->scope != intrinsics_pkg->scope);
} else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) {
is_declared = false;
}
@@ -4673,10 +4918,18 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
Selection sel = {}; // NOTE(bill): Not used if it's an import name
if (!c->allow_arrow_right_selector_expr && se->token.kind == Token_ArrowRight) {
+ ERROR_BLOCK();
error(node, "Illegal use of -> selector shorthand outside of a call");
- operand->mode = Addressing_Invalid;
- operand->expr = node;
- return nullptr;
+ gbString x = expr_to_string(se->expr);
+ gbString y = expr_to_string(se->selector);
+ error_line("\tSuggestion: Did you mean '%s.%s'?\n", x, y);
+ gb_string_free(y);
+ gb_string_free(x);
+
+ // TODO(bill): Should this terminate here or propagate onwards?
+ // operand->mode = Addressing_Invalid;
+ // operand->expr = node;
+ // return nullptr;
}
operand->expr = node;
@@ -4785,6 +5038,9 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
}
}
+ if (operand->type && is_type_soa_struct(type_deref(operand->type))) {
+ complete_soa_type(c->checker, type_deref(operand->type), false);
+ }
if (entity == nullptr && selector->kind == Ast_Ident) {
String field_name = selector->Ident.token.string;
@@ -4806,7 +5062,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
}
}
- if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) {
+ if (entity == nullptr && selector->kind == Ast_Ident && (is_type_array(type_deref(operand->type)) || is_type_simd_vector(type_deref(operand->type)))) {
String field_name = selector->Ident.token.string;
if (1 < field_name.len && field_name.len <= 4) {
u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'};
@@ -4861,8 +5117,10 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
Type *original_type = operand->type;
Type *array_type = base_type(type_deref(original_type));
- GB_ASSERT(array_type->kind == Type_Array);
- i64 array_count = array_type->Array.count;
+ GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector);
+
+ i64 array_count = get_array_type_count(array_type);
+
for (u8 i = 0; i < index_count; i++) {
u8 idx = indices>>(i*2) & 3;
if (idx >= array_count) {
@@ -4882,7 +5140,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
se->swizzle_count = index_count;
se->swizzle_indices = indices;
-
AddressingMode prev_mode = operand->mode;
operand->mode = Addressing_SwizzleValue;
operand->type = determine_swizzle_array_type(original_type, type_hint, index_count);
@@ -4896,6 +5153,10 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
break;
}
+ if (array_type->kind == Type_SimdVector) {
+ operand->mode = Addressing_Value;
+ }
+
Entity *swizzle_entity = alloc_entity_variable(nullptr, make_token_ident(field_name), operand->type, EntityState_Resolved);
add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
return swizzle_entity;
@@ -5011,6 +5272,11 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
operand->type = entity->type;
operand->expr = node;
+ if (entity->flags & EntityFlag_BitFieldField) {
+ add_package_dependency(c, "runtime", "__write_bits");
+ add_package_dependency(c, "runtime", "__read_bits");
+ }
+
switch (entity->kind) {
case Entity_Constant:
operand->value = entity->Constant.value;
@@ -5024,6 +5290,9 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
}
break;
case Entity_Variable:
+ if (sel.is_bit_field) {
+ se->is_bit_field = true;
+ }
if (sel.indirect) {
operand->mode = Addressing_Variable;
} else if (operand->mode == Addressing_Context) {
@@ -5572,10 +5841,14 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
Operand *variadic_operand = &ordered_operands[pt->variadic_index];
if (vari_expand) {
- GB_ASSERT(variadic_operands.count != 0);
- *variadic_operand = variadic_operands[0];
- variadic_operand->type = default_type(variadic_operand->type);
- actually_variadic = true;
+ if (variadic_operands.count == 0) {
+ error(call, "'..' in the wrong position");
+ } else {
+ GB_ASSERT(variadic_operands.count != 0);
+ *variadic_operand = variadic_operands[0];
+ variadic_operand->type = default_type(variadic_operand->type);
+ actually_variadic = true;
+ }
} else {
AstFile *f = call->file();
@@ -5639,31 +5912,28 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error) -> i64 {
+ bool allow_array_programming = !(e && (e->flags & EntityFlag_NoBroadcast));
i64 s = 0;
- if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic)) {
+ if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic, allow_array_programming)) {
bool ok = false;
- if (e && e->flags & EntityFlag_AnyInt) {
+ if (e && (e->flags & EntityFlag_AnyInt)) {
if (is_type_integer(param_type)) {
ok = check_is_castable_to(c, o, param_type);
}
}
+ if (!allow_array_programming && check_is_assignable_to_with_score(c, o, param_type, nullptr, param_is_variadic, !allow_array_programming)) {
+ if (show_error) {
+ error(o->expr, "'#no_broadcast' disallows automatic broadcasting a value across all elements of an array-like type in a procedure argument");
+ }
+ }
if (ok) {
s = assign_score_function(MAXIMUM_TYPE_DISTANCE);
} else {
if (show_error) {
check_assignment(c, o, param_type, str_lit("procedure argument"));
-
- Type *src = base_type(o->type);
- Type *dst = base_type(param_type);
- if (is_type_slice(src) && are_types_identical(src->Slice.elem, dst)) {
- gbString a = expr_to_string(o->expr);
- error_line("\tSuggestion: Did you mean to pass the slice into the variadic parameter with ..%s?\n\n", a);
- gb_string_free(a);
- }
}
err = CallArgumentError_WrongTypes;
}
-
} else if (show_error) {
check_assignment(c, o, param_type, str_lit("procedure argument"));
}
@@ -5748,12 +6018,13 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
if (param_is_variadic) {
continue;
}
- score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error);
+ score += eval_param_and_score(c, o, e->type, err, false, e, show_error);
}
}
if (variadic) {
- Type *slice = pt->params->Tuple.variables[pt->variadic_index]->type;
+ Entity *var_entity = pt->params->Tuple.variables[pt->variadic_index];
+ Type *slice = var_entity->type;
GB_ASSERT(is_type_slice(slice));
Type *elem = base_type(slice)->Slice.elem;
Type *t = elem;
@@ -5779,7 +6050,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
return CallArgumentError_MultipleVariadicExpand;
}
}
- score += eval_param_and_score(c, o, t, err, true, nullptr, show_error);
+ score += eval_param_and_score(c, o, t, err, true, var_entity, show_error);
}
}
@@ -5907,7 +6178,10 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco
}
}
- if (call_expr) error(call_expr, "at caller location");
+ if (call_expr) {
+ TokenPos pos = ast_token(call_expr).pos;
+ error_line("%s at caller location\n", token_pos_to_string(pos));
+ }
}
return false;
}
@@ -6294,7 +6568,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
}
if (valids.count > 1) {
- gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp);
+ array_sort(valids, valid_index_and_score_cmp);
i64 best_score = valids[0].score;
Entity *best_entity = proc_entities[valids[0].index];
GB_ASSERT(best_entity != nullptr);
@@ -6669,7 +6943,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
isize index = lookup_polymorphic_record_parameter(original_type, name);
if (index >= 0) {
TypeTuple *params = get_record_polymorphic_params(original_type);
- Entity *e = params->variables[i];
+ Entity *e = params->variables[index];
if (e->kind == Entity_Constant) {
check_expr_with_type_hint(c, &operands[i], fv->value, e->type);
continue;
@@ -6720,7 +6994,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
Array<Operand> ordered_operands = operands;
if (!named_fields) {
- ordered_operands = array_make<Operand>(permanent_allocator(), param_count);
+ ordered_operands = array_make<Operand>(permanent_allocator(), operands.count);
array_copy(&ordered_operands, operands, 0);
} else {
TEMPORARY_ALLOCATOR_GUARD();
@@ -6908,8 +7182,12 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
}
{
- bool failure = false;
- Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands, &failure);
+ GenTypesData *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(c, original_type);
+
+ mutex_lock(&found_gen_types->mutex);
+ defer (mutex_unlock(&found_gen_types->mutex));
+ Entity *found_entity = find_polymorphic_record_entity(found_gen_types, param_count, ordered_operands);
+
if (found_entity) {
operand->mode = Addressing_Type;
operand->type = found_entity->type;
@@ -7053,8 +7331,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
name == "defined" ||
name == "config" ||
name == "load" ||
- name == "load_hash" ||
- name == "load_or"
+ name == "load_directory" ||
+ name == "load_hash"
) {
operand->mode = Addressing_Builtin;
operand->builtin_id = BuiltinProc_DIRECTIVE;
@@ -7674,7 +7952,7 @@ gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o,
}
-gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_) {
+gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_, bool change_operand) {
switch (x->mode) {
case Addressing_MapIndex:
case Addressing_OptionalOk:
@@ -7692,22 +7970,28 @@ gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type *
Type *pt = base_type(type_of_expr(expr->CallExpr.proc));
if (is_type_proc(pt)) {
Type *tuple = pt->Proc.results;
- add_type_and_value(c, x->expr, x->mode, tuple, x->value);
if (pt->Proc.result_count >= 2) {
if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type;
}
- expr->CallExpr.optional_ok_one = false;
- x->type = tuple;
+ if (change_operand) {
+ expr->CallExpr.optional_ok_one = false;
+ x->type = tuple;
+ add_type_and_value(c, x->expr, x->mode, tuple, x->value);
+ }
return;
}
}
Type *tuple = make_optional_ok_type(x->type);
+
if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type;
- add_type_and_value(c, x->expr, x->mode, tuple, x->value);
- x->type = tuple;
- GB_ASSERT(is_type_tuple(type_of_expr(x->expr)));
+
+ if (change_operand) {
+ add_type_and_value(c, x->expr, x->mode, tuple, x->value);
+ x->type = tuple;
+ GB_ASSERT(is_type_tuple(type_of_expr(x->expr)));
+ }
}
@@ -7873,19 +8157,31 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
o->mode = Addressing_Constant;
String name = bd->name.string;
if (name == "file") {
+ String file = get_file_path_string(bd->token.pos.file_id);
+ if (build_context.obfuscate_source_code_locations) {
+ file = obfuscate_string(file, "F");
+ }
o->type = t_untyped_string;
- o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id));
+ o->value = exact_value_string(file);
} else if (name == "line") {
+ i32 line = bd->token.pos.line;
+ if (build_context.obfuscate_source_code_locations) {
+ line = obfuscate_i32(line);
+ }
o->type = t_untyped_integer;
- o->value = exact_value_i64(bd->token.pos.line);
+ o->value = exact_value_i64(line);
} else if (name == "procedure") {
if (c->curr_proc_decl == nullptr) {
error(node, "#procedure may only be used within procedures");
o->type = t_untyped_string;
o->value = exact_value_string(str_lit(""));
} else {
+ String p = c->proc_name;
+ if (build_context.obfuscate_source_code_locations) {
+ p = obfuscate_string(p, "P");
+ }
o->type = t_untyped_string;
- o->value = exact_value_string(c->proc_name);
+ o->value = exact_value_string(p);
}
} else if (name == "caller_location") {
init_core_source_code_location(c->checker);
@@ -7904,6 +8200,7 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
name == "config" ||
name == "load" ||
name == "load_hash" ||
+ name == "load_directory" ||
name == "load_or"
) {
error(node, "'#%.*s' must be used as a call", LIT(name));
@@ -8195,6 +8492,7 @@ gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *no
// NOTE(bill): allow implicit conversion between boolean types
// within 'or_return' to improve the experience using third-party code
} else if (!check_is_assignable_to(c, &rhs, end_type)) {
+ ERROR_BLOCK();
// TODO(bill): better error message
gbString a = type_to_string(right_type);
gbString b = type_to_string(end_type);
@@ -8277,6 +8575,7 @@ gb_internal ExprKind check_or_branch_expr(CheckerContext *c, Operand *o, Ast *no
switch (be->token.kind) {
case Token_or_break:
+ node->viral_state_flags |= ViralStateFlag_ContainsOrBreak;
if ((c->stmt_flags & Stmt_BreakAllowed) == 0 && label == nullptr) {
error(be->token, "'%.*s' only allowed in non-inline loops or 'switch' statements", LIT(name));
}
@@ -8339,50 +8638,74 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
StringMap<String> fields_visited_through_raw_union = {};
defer (string_map_destroy(&fields_visited_through_raw_union));
+ String assignment_str = str_lit("structure literal");
+ if (bt->kind == Type_BitField) {
+ assignment_str = str_lit("bit_field literal");
+ }
+
for (Ast *elem : elems) {
if (elem->kind != Ast_FieldValue) {
error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
continue;
}
ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
+ Ast *ident = fv->field;
+ if (ident->kind == Ast_ImplicitSelectorExpr) {
+ gbString expr_str = expr_to_string(ident);
+ error(ident, "Field names do not start with a '.', remove the '.' in structure literal", expr_str);
+ gb_string_free(expr_str);
+
+ ident = ident->ImplicitSelectorExpr.selector;
+ }
+ if (ident->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(ident);
error(elem, "Invalid field name '%s' in structure literal", expr_str);
gb_string_free(expr_str);
continue;
}
- String name = fv->field->Ident.token.string;
+ String name = ident->Ident.token.string;
Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
bool is_unknown = sel.entity == nullptr;
if (is_unknown) {
- error(fv->field, "Unknown field '%.*s' in structure literal", LIT(name));
+ error(ident, "Unknown field '%.*s' in structure literal", LIT(name));
continue;
}
- Entity *field = bt->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
+ Entity *field = nullptr;
+ if (bt->kind == Type_Struct) {
+ field = bt->Struct.fields[sel.index[0]];
+ } else if (bt->kind == Type_BitField) {
+ field = bt->BitField.fields[sel.index[0]];
+ } else {
+ GB_PANIC("Unknown type");
+ }
+
+
+ add_entity_use(c, ident, field);
if (string_set_update(&fields_visited, name)) {
if (sel.index.count > 1) {
if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) {
- error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
+ error(ident, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
} else {
- error(fv->field, "Duplicate or reused field '%.*s' in structure literal", LIT(sel.entity->token.string));
+ error(ident, "Duplicate or reused field '%.*s' in %.*s", LIT(sel.entity->token.string), LIT(assignment_str));
}
} else {
- error(fv->field, "Duplicate field '%.*s' in structure literal", LIT(field->token.string));
+ error(ident, "Duplicate field '%.*s' in %.*s", LIT(field->token.string), LIT(assignment_str));
}
continue;
} else if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) {
- error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
+ error(ident, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
continue;
}
if (sel.indirect) {
- error(fv->field, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a structure literal", cast(int)sel.index.count-1, LIT(name));
+ error(ident, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a %.*s", cast(int)sel.index.count-1, LIT(name), LIT(assignment_str));
continue;
}
if (sel.index.count > 1) {
+ GB_ASSERT(bt->kind == Type_Struct);
+
if (is_constant) {
Type *ft = type;
for (i32 index : sel.index) {
@@ -8443,10 +8766,35 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
is_constant = check_is_operand_compound_lit_constant(c, &o);
}
- check_assignment(c, &o, field->type, str_lit("structure literal"));
+ u8 prev_bit_field_bit_size = c->bit_field_bit_size;
+ if (field->kind == Entity_Variable && field->Variable.bit_field_bit_size) {
+ // HACK NOTE(bill): This is a bit of a hack, but it will work fine for this use case
+ c->bit_field_bit_size = field->Variable.bit_field_bit_size;
+ }
+
+ check_assignment(c, &o, field->type, assignment_str);
+
+ c->bit_field_bit_size = prev_bit_field_bit_size;
}
}
+gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) {
+ type_expr = unparen_expr(type_expr);
+ if (type_expr == nullptr) {
+ return false;
+ }
+
+ // [?]Type
+ if (type_expr->kind == Ast_ArrayType && type_expr->ArrayType.count != nullptr) {
+ Ast *count = type_expr->ArrayType.count;
+ if (count->kind == Ast_UnaryExpr &&
+ count->UnaryExpr.op.kind == Token_Question) {
+ return true;
+ }
+ }
+ return false;
+}
+
gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = Expr_Expr;
ast_node(cl, CompoundLit, node);
@@ -8457,20 +8805,31 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
bool is_to_be_determined_array_count = false;
bool is_constant = true;
- if (cl->type != nullptr) {
+
+ Ast *type_expr = cl->type;
+
+ bool used_type_hint_expr = false;
+ if (type_expr == nullptr && c->type_hint_expr != nullptr) {
+ if (is_expr_inferred_fixed_array(c->type_hint_expr)) {
+ type_expr = clone_ast(c->type_hint_expr);
+ used_type_hint_expr = true;
+ }
+ }
+
+ if (type_expr != nullptr) {
type = nullptr;
// [?]Type
- if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) {
- Ast *count = cl->type->ArrayType.count;
+ if (type_expr->kind == Ast_ArrayType && type_expr->ArrayType.count != nullptr) {
+ Ast *count = type_expr->ArrayType.count;
if (count->kind == Ast_UnaryExpr &&
count->UnaryExpr.op.kind == Token_Question) {
- type = alloc_type_array(check_type(c, cl->type->ArrayType.elem), -1);
+ type = alloc_type_array(check_type(c, type_expr->ArrayType.elem), -1);
is_to_be_determined_array_count = true;
}
if (cl->elems.count > 0) {
- if (cl->type->ArrayType.tag != nullptr) {
- Ast *tag = cl->type->ArrayType.tag;
+ if (type_expr->ArrayType.tag != nullptr) {
+ Ast *tag = type_expr->ArrayType.tag;
GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name.string;
if (name == "soa") {
@@ -8480,9 +8839,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
}
}
- if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) {
+ if (type_expr->kind == Ast_DynamicArrayType && type_expr->DynamicArrayType.tag != nullptr) {
if (cl->elems.count > 0) {
- Ast *tag = cl->type->DynamicArrayType.tag;
+ Ast *tag = type_expr->DynamicArrayType.tag;
GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name.string;
if (name == "soa") {
@@ -8493,7 +8852,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
if (type == nullptr) {
- type = check_type(c, cl->type);
+ type = check_type(c, type_expr);
}
}
@@ -8541,6 +8900,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
break;
}
+ wait_signal_until_available(&t->Struct.fields_wait_signal);
isize field_count = t->Struct.fields.count;
isize min_field_count = t->Struct.fields.count;
for (isize i = min_field_count-1; i >= 0; i--) {
@@ -9245,6 +9605,21 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
break;
}
+ case Type_BitField: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+ is_constant = false;
+ if (cl->elems[0]->kind != Ast_FieldValue) {
+ gbString type_str = type_to_string(type);
+ error(node, "%s ('bit_field') compound literals are only allowed to contain 'field = value' elements", type_str);
+ gb_string_free(type_str);
+ } else {
+ check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
+ }
+ break;
+ }
+
default: {
if (cl->elems.count == 0) {
@@ -9690,6 +10065,7 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node,
bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint);
if (is_const) {
if (index < 0) {
+ ERROR_BLOCK();
gbString str = expr_to_string(o->expr);
error(o->expr, "Cannot index a constant '%s'", str);
if (!build_context.terse_errors) {
@@ -9706,6 +10082,7 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node,
bool finish = false;
o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish);
if (!success) {
+ ERROR_BLOCK();
gbString str = expr_to_string(o->expr);
error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index);
if (!build_context.terse_errors) {
@@ -9896,6 +10273,7 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
}
}
if (!all_constant) {
+ ERROR_BLOCK();
gbString str = expr_to_string(o->expr);
error(o->expr, "Cannot slice '%s' with non-constant indices", str);
if (!build_context.terse_errors) {
@@ -10087,6 +10465,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
case_end;
case_ast_node(re, OrReturnExpr, node);
+ node->viral_state_flags |= ViralStateFlag_ContainsOrReturn;
return check_or_return_expr(c, o, node, type_hint);
case_end;
@@ -10849,6 +11228,9 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
if (f->flags&FieldFlag_any_int) {
str = gb_string_appendc(str, "#any_int ");
}
+ if (f->flags&FieldFlag_no_broadcast) {
+ str = gb_string_appendc(str, "#no_broadcast ");
+ }
if (f->flags&FieldFlag_const) {
str = gb_string_appendc(str, "#const ");
}
@@ -10915,6 +11297,18 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
if (field->flags&FieldFlag_c_vararg) {
str = gb_string_appendc(str, "#c_vararg ");
}
+ if (field->flags&FieldFlag_any_int) {
+ str = gb_string_appendc(str, "#any_int ");
+ }
+ if (field->flags&FieldFlag_no_broadcast) {
+ str = gb_string_appendc(str, "#no_broadcast ");
+ }
+ if (field->flags&FieldFlag_const) {
+ str = gb_string_appendc(str, "#const ");
+ }
+ if (field->flags&FieldFlag_subtype) {
+ str = gb_string_appendc(str, "#subtype ");
+ }
str = write_expr_to_string(str, field->type, shorthand);
}
@@ -11058,6 +11452,32 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
case_end;
+ case_ast_node(f, BitFieldField, node);
+ str = write_expr_to_string(str, f->name, shorthand);
+ str = gb_string_appendc(str, ": ");
+ str = write_expr_to_string(str, f->type, shorthand);
+ str = gb_string_appendc(str, " | ");
+ str = write_expr_to_string(str, f->bit_size, shorthand);
+ case_end;
+ case_ast_node(bf, BitFieldType, node);
+ str = gb_string_appendc(str, "bit_field ");
+ if (!shorthand) {
+ str = write_expr_to_string(str, bf->backing_type, shorthand);
+ }
+ str = gb_string_appendc(str, " {");
+ if (shorthand) {
+ str = gb_string_appendc(str, "...");
+ } else {
+ for_array(i, bf->fields) {
+ if (i > 0) {
+ str = gb_string_appendc(str, ", ");
+ }
+ str = write_expr_to_string(str, bf->fields[i], false);
+ }
+ }
+ str = gb_string_appendc(str, "}");
+ case_end;
+
case_ast_node(ia, InlineAsmExpr, node);
str = gb_string_appendc(str, "asm(");
for_array(i, ia->param_types) {
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index d56e5e212..fc3b9aa43 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -221,6 +221,12 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
return true;
}
break;
+
+ case Ast_ExprStmt:
+ if (stmt->ExprStmt.expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) {
+ return true;
+ }
+ break;
}
return false;
@@ -485,7 +491,17 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
}
}
+ Entity *lhs_e = entity_of_node(lhs->expr);
+ u8 prev_bit_field_bit_size = ctx->bit_field_bit_size;
+ if (lhs_e && lhs_e->kind == Entity_Variable && lhs_e->Variable.bit_field_bit_size) {
+ // HACK NOTE(bill): This is a bit of a hack, but it will work fine for this use case
+ ctx->bit_field_bit_size = lhs_e->Variable.bit_field_bit_size;
+ }
+
check_assignment(ctx, rhs, assignment_type, str_lit("assignment"));
+
+ ctx->bit_field_bit_size = prev_bit_field_bit_size;
+
if (rhs->mode == Addressing_Invalid) {
return nullptr;
}
@@ -724,6 +740,25 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us,
return true;
}
+gb_internal void error_var_decl_identifier(Ast *name) {
+ GB_ASSERT(name != nullptr);
+ GB_ASSERT(name->kind != Ast_Ident);
+
+ ERROR_BLOCK();
+ gbString s = expr_to_string(name);
+ defer (gb_string_free(s));
+
+ error(name, "A variable declaration must be an identifier, got '%s'", s);
+ if (name->kind == Ast_Implicit) {
+ String imp = name->Implicit.string;
+ if (imp == "context") {
+ error_line("\tSuggestion: '%.*s' is a reserved keyword, would 'ctx' suffice?\n", LIT(imp));
+ } else {
+ error_line("\tNote: '%.*s' is a reserved keyword\n", LIT(imp));
+ }
+ }
+}
+
gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
ast_node(irs, UnrollRangeStmt, node);
check_open_scope(ctx, node);
@@ -835,7 +870,7 @@ gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod
entity = found;
}
} else {
- error(name, "A variable declaration must be an identifier");
+ error_var_decl_identifier(name);
}
if (entity == nullptr) {
@@ -867,6 +902,7 @@ gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod
}
if (ctx->inline_for_depth >= MAX_INLINE_FOR_DEPTH && prev_inline_for_depth < MAX_INLINE_FOR_DEPTH) {
+ ERROR_BLOCK();
if (prev_inline_for_depth > 0) {
error(node, "Nested '#unroll for' loop cannot be inlined as it exceeds the maximum '#unroll for' depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH);
} else {
@@ -1349,6 +1385,8 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
}
if (unhandled.count > 0) {
+ ERROR_BLOCK();
+
if (unhandled.count == 1) {
gbString s = type_to_string(unhandled[0]);
error_no_newline(node, "Unhandled switch case: %s", s);
@@ -1447,25 +1485,6 @@ gb_internal bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *i
return id != BuiltinProc_Invalid;
}
-gb_internal bool check_expr_is_stack_variable(Ast *expr) {
- /*
- expr = unparen_expr(expr);
- Entity *e = entity_of_node(expr);
- if (e && e->kind == Entity_Variable) {
- if (e->flags & (EntityFlag_Static|EntityFlag_Using|EntityFlag_ImplicitReference|EntityFlag_ForValue)) {
- // okay
- } else if (e->Variable.thread_local_model.len != 0) {
- // okay
- } else if (e->scope) {
- if ((e->scope->flags & (ScopeFlag_Global|ScopeFlag_File|ScopeFlag_Type)) == 0) {
- return true;
- }
- }
- }
- */
- return false;
-}
-
gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
ast_node(rs, RangeStmt, node);
@@ -1479,6 +1498,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
auto vals = array_make<Type *>(temporary_allocator(), 0, 2);
auto entities = array_make<Entity *>(temporary_allocator(), 0, 2);
bool is_map = false;
+ bool is_bit_set = false;
bool use_by_reference_for_value = false;
bool is_soa = false;
bool is_reverse = rs->reverse;
@@ -1524,11 +1544,25 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
array_add(&vals, operand.type);
array_add(&vals, t_int);
add_type_info_type(ctx, operand.type);
+ if (build_context.no_rtti) {
+ error(node, "Iteration over an enum type is not allowed runtime type information (RTTI) has been disallowed");
+ }
goto skip_expr_range_stmt;
}
} else if (operand.mode != Addressing_Invalid) {
+ if (operand.mode == Addressing_OptionalOk || operand.mode == Addressing_OptionalOkPtr) {
+ Ast *expr = unparen_expr(operand.expr);
+ if (expr->kind != Ast_TypeAssertion) { // Only for procedure calls
+ Type *end_type = nullptr;
+ check_promote_optional_ok(ctx, &operand, nullptr, &end_type, false);
+ if (is_type_boolean(end_type)) {
+ check_promote_optional_ok(ctx, &operand, nullptr, &end_type, true);
+ }
+ }
+ }
bool is_ptr = is_type_pointer(operand.type);
Type *t = base_type(type_deref(operand.type));
+
switch (t->kind) {
case Type_Basic:
if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
@@ -1543,6 +1577,17 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
}
break;
+ case Type_BitSet:
+ array_add(&vals, t->BitSet.elem);
+ max_val_count = 1;
+ is_bit_set = true;
+ is_possibly_addressable = false;
+ add_type_info_type(ctx, operand.type);
+ if (build_context.no_rtti && is_type_enum(t->BitSet.elem)) {
+ error(node, "Iteration over a bit_set of an enum is not allowed runtime type information (RTTI) has been disallowed");
+ }
+ break;
+
case Type_EnumeratedArray:
if (is_ptr) use_by_reference_for_value = true;
array_add(&vals, t->EnumeratedArray.elem);
@@ -1582,6 +1627,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
{
isize count = t->Tuple.variables.count;
if (count < 1 || count > 3) {
+ ERROR_BLOCK();
check_not_tuple(ctx, &operand);
error_line("\tMultiple return valued parameters in a range statement are limited to a maximum of 2 usable values with a trailing boolean for the conditional\n");
break;
@@ -1637,6 +1683,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
defer (gb_string_free(s));
defer (gb_string_free(t));
+ ERROR_BLOCK();
+
error(operand.expr, "Cannot iterate over '%s' of type '%s'", s, t);
if (rs->vals.count == 1) {
@@ -1695,7 +1743,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
if (is_possibly_addressable && i == addressable_index) {
entity->flags &= ~EntityFlag_Value;
} else {
- char const *idx_name = is_map ? "key" : "index";
+ char const *idx_name = is_map ? "key" : is_bit_set ? "element" : "index";
error(token, "The %s variable '%.*s' cannot be made addressable", idx_name, LIT(str));
}
} else if (i == addressable_index && use_by_reference_for_value) {
@@ -1718,9 +1766,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
entity = found;
}
} else {
- gbString s = expr_to_string(lhs[i]);
- error(name, "A variable declaration must be an identifier, got %s", s);
- gb_string_free(s);
+ error_var_decl_identifier(name);
}
if (entity == nullptr) {
@@ -1772,7 +1818,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
for (Ast *name : vd->names) {
Entity *entity = nullptr;
if (name->kind != Ast_Ident) {
- error(name, "A variable declaration must be an identifier");
+ error_var_decl_identifier(name);
} else {
Token token = name->Ident.token;
String str = token.string;
@@ -1895,17 +1941,19 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
e->Variable.thread_local_model = ac.thread_local_model;
}
- if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) {
- // error(e->token, "@(thread_local) is not supported for this target platform");
- }
-
-
if (ac.is_static && ac.thread_local_model != "") {
error(e->token, "The 'static' attribute is not needed if 'thread_local' is applied");
}
}
+ // NOTE(bill): This is to improve error handling for things like `x: [?]T = {...}`
+ Ast *prev_type_hint_expr = ctx->type_hint_expr;
+ ctx->type_hint_expr = vd->type;
+
check_init_variables(ctx, entities, entity_count, vd->values, str_lit("variable declaration"));
+
+ ctx->type_hint_expr = prev_type_hint_expr;
+
check_arity_match(ctx, vd, false);
for (isize i = 0; i < entity_count; i++) {
@@ -1934,7 +1982,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
TokenPos pos = f->token.pos;
Type *this_type = base_type(e->type);
Type *other_type = base_type(f->type);
- if (!are_types_identical(this_type, other_type)) {
+ if (!signature_parameter_similar_enough(this_type, other_type)) {
error(e->token,
"Foreign entity '%.*s' previously declared elsewhere with a different type\n"
"\tat %s",
@@ -2027,13 +2075,13 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) {
}
Ast *expr = strip_or_return_expr(operand.expr);
- if (expr->kind == Ast_CallExpr) {
+ if (expr && expr->kind == Ast_CallExpr) {
BuiltinProcId builtin_id = BuiltinProc_Invalid;
bool do_require = false;
AstCallExpr *ce = &expr->CallExpr;
Type *t = base_type(type_of_expr(ce->proc));
- if (t->kind == Type_Proc) {
+ if (t && t->kind == Type_Proc) {
do_require = t->Proc.require_results;
} else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
auto const &bp = builtin_procs[builtin_id];
@@ -2045,7 +2093,7 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) {
gb_string_free(expr_str);
}
return;
- } else if (expr->kind == Ast_SelectorCallExpr) {
+ } else if (expr && expr->kind == Ast_SelectorCallExpr) {
BuiltinProcId builtin_id = BuiltinProc_Invalid;
bool do_require = false;
@@ -2071,6 +2119,9 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) {
}
return;
}
+
+ ERROR_BLOCK();
+
gbString expr_str = expr_to_string(operand.expr);
error(node, "Expression is not used: '%s'", expr_str);
gb_string_free(expr_str);
@@ -2264,29 +2315,6 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
if (is_type_untyped(o->type)) {
update_untyped_expr_type(ctx, o->expr, e->type, true);
}
-
-
- // NOTE(bill): This is very basic escape analysis
- // This needs to be improved tremendously, and a lot of it done during the
- // middle-end (or LLVM side) to improve checks and error messages
- Ast *expr = unparen_expr(o->expr);
- if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
- Ast *x = unparen_expr(expr->UnaryExpr.expr);
- if (x->kind == Ast_CompoundLit) {
- error(expr, "Cannot return the address to a stack value from a procedure");
- } else if (x->kind == Ast_IndexExpr) {
- Ast *array = x->IndexExpr.expr;
- if (is_type_array_like(type_of_expr(array)) && check_expr_is_stack_variable(array)) {
- gbString t = type_to_string(type_of_expr(array));
- error(expr, "Cannot return the address to an element of stack variable from a procedure, of type %s", t);
- gb_string_free(t);
- }
- } else {
- if (check_expr_is_stack_variable(x)) {
- error(expr, "Cannot return the address to a stack variable from a procedure");
- }
- }
- }
}
}
@@ -2294,16 +2322,51 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
if (o.expr == nullptr) {
continue;
}
- if (o.expr->kind != Ast_CompoundLit || !is_type_slice(o.type)) {
- continue;
- }
- ast_node(cl, CompoundLit, o.expr);
- if (cl->elems.count == 0) {
- continue;
+ Ast *expr = unparen_expr(o.expr);
+
+ auto unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) {
+ gbString s = expr_to_string(o.expr);
+ if (extra_type) {
+ gbString t = type_to_string(extra_type);
+ error(o.expr, "It is unsafe to return %s ('%s') of type ('%s') from a procedure, as it uses the current stack frame's memory", msg, s, t);
+ gb_string_free(t);
+ } else {
+ error(o.expr, "It is unsafe to return %s ('%s') from a procedure, as it uses the current stack frame's memory", msg, s);
+ }
+ gb_string_free(s);
+ };
+
+
+ // NOTE(bill): This is very basic escape analysis
+ // This needs to be improved tremendously, and a lot of it done during the
+ // middle-end (or LLVM side) to improve checks and error messages
+ if (expr->kind == Ast_CompoundLit && is_type_slice(o.type)) {
+ ast_node(cl, CompoundLit, expr);
+ if (cl->elems.count == 0) {
+ continue;
+ }
+ unsafe_return_error(o, "a compound literal of a slice");
+ } else if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
+ Ast *x = unparen_expr(expr->UnaryExpr.expr);
+ Entity *e = entity_of_node(x);
+ if (is_entity_local_variable(e)) {
+ unsafe_return_error(o, "the address of a local variable");
+ } else if(x->kind == Ast_CompoundLit) {
+ unsafe_return_error(o, "the address of a compound literal");
+ } else if (x->kind == Ast_IndexExpr) {
+ Entity *f = entity_of_node(x->IndexExpr.expr);
+ if (is_type_array_like(f->type) || is_type_matrix(f->type)) {
+ if (is_entity_local_variable(f)) {
+ unsafe_return_error(o, "the address of an indexed variable", f->type);
+ }
+ }
+ } else if (x->kind == Ast_MatrixIndexExpr) {
+ Entity *f = entity_of_node(x->MatrixIndexExpr.expr);
+ if (is_type_matrix(f->type) && is_entity_local_variable(f)) {
+ unsafe_return_error(o, "the address of an indexed variable", f->type);
+ }
+ }
}
- gbString s = type_to_string(o.type);
- error(o.expr, "It is unsafe to return a compound literal of a slice ('%s') with elements from a procedure, as the contents of the slice uses the current stack frame's memory", s);
- gb_string_free(s);
}
}
diff --git a/src/check_type.cpp b/src/check_type.cpp
index a95026711..f1d991acb 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -1,4 +1,6 @@
gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location);
+gb_internal Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand const &operand);
+gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array<Operand> const *operands);
gb_internal void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) {
t = base_type(t);
@@ -87,6 +89,8 @@ gb_internal bool does_field_type_allow_using(Type *t) {
return true;
} else if (is_type_array(t)) {
return t->Array.count <= 4;
+ } else if (is_type_bit_field(t)) {
+ return true;
}
return false;
}
@@ -219,13 +223,13 @@ gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entit
}
-gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) {
+gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_, char const *msg) {
GB_ASSERT(align_ != nullptr);
Operand o = {};
check_expr(ctx, &o, node);
if (o.mode != Addressing_Constant) {
if (o.mode != Addressing_Invalid) {
- error(node, "#align must be a constant");
+ error(node, "#%s must be a constant", msg);
}
return false;
}
@@ -237,13 +241,13 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_)
if (v.used > 1) {
gbAllocator a = heap_allocator();
String str = big_int_to_string(a, &v);
- error(node, "#align too large, %.*s", LIT(str));
+ error(node, "#%s too large, %.*s", msg, LIT(str));
gb_free(a, str.text);
return false;
}
i64 align = big_int_to_i64(&v);
if (align < 1 || !gb_is_power_of_two(cast(isize)align)) {
- error(node, "#align must be a power of 2, got %lld", align);
+ error(node, "#%s must be a power of 2, got %lld", msg, align);
return false;
}
*align_ = align;
@@ -251,89 +255,26 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_)
}
}
- error(node, "#align must be an integer");
+ error(node, "#%s must be an integer", msg);
return false;
}
-gb_internal Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure) {
- rw_mutex_shared_lock(&ctx->info->gen_types_mutex); // @@global
-
- auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
- if (found_gen_types == nullptr) {
- rw_mutex_shared_unlock(&ctx->info->gen_types_mutex); // @@global
- return nullptr;
- }
-
- rw_mutex_shared_lock(&found_gen_types->mutex); // @@local
- defer (rw_mutex_shared_unlock(&found_gen_types->mutex)); // @@local
-
- rw_mutex_shared_unlock(&ctx->info->gen_types_mutex); // @@global
-
- for (Entity *e : found_gen_types->types) {
- Type *t = base_type(e->type);
- TypeTuple *tuple = nullptr;
- switch (t->kind) {
- case Type_Struct:
- if (t->Struct.polymorphic_params) {
- tuple = &t->Struct.polymorphic_params->Tuple;
- }
- break;
- case Type_Union:
- if (t->Union.polymorphic_params) {
- tuple = &t->Union.polymorphic_params->Tuple;
- }
- break;
- }
- GB_ASSERT_MSG(tuple != nullptr, "%s :: %s", type_to_string(e->type), type_to_string(t));
- GB_ASSERT(param_count == tuple->variables.count);
-
- bool skip = false;
-
- for (isize j = 0; j < param_count; j++) {
- Entity *p = tuple->variables[j];
- Operand o = {};
- if (j < ordered_operands.count) {
- o = ordered_operands[j];
- }
- if (o.expr == nullptr) {
- continue;
- }
- Entity *oe = entity_of_node(o.expr);
- if (p == oe) {
- // NOTE(bill): This is the same type, make sure that it will be be same thing and use that
- // Saves on a lot of checking too below
- continue;
- }
+gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type) {
+ mutex_lock(&ctx->info->gen_types_mutex); // @@global
- if (p->kind == Entity_TypeName) {
- if (is_type_polymorphic(o.type)) {
- // NOTE(bill): Do not add polymorphic version to the gen_types
- skip = true;
- break;
- }
- if (!are_types_identical(o.type, p->type)) {
- skip = true;
- break;
- }
- } else if (p->kind == Entity_Constant) {
- if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) {
- skip = true;
- break;
- }
- if (!are_types_identical(o.type, p->type)) {
- skip = true;
- break;
- }
- } else {
- GB_PANIC("Unknown entity kind");
- }
- }
- if (!skip) {
- return e;
- }
+ GenTypesData *found_gen_types = nullptr;
+ auto *found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type);
+ if (found_gen_types_ptr == nullptr) {
+ GenTypesData *gen_types = gb_alloc_item(permanent_allocator(), GenTypesData);
+ gen_types->types = array_make<Entity *>(heap_allocator());
+ map_set(&ctx->info->gen_types, original_type, gen_types);
+ found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type);
}
- return nullptr;
+ found_gen_types = *found_gen_types_ptr;
+ GB_ASSERT(found_gen_types != nullptr);
+ mutex_unlock(&ctx->info->gen_types_mutex); // @@global
+ return found_gen_types;
}
@@ -363,19 +304,16 @@ gb_internal void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, T
// TODO(bill): Is this even correct? Or should the metadata be copied?
e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata;
- rw_mutex_lock(&ctx->info->gen_types_mutex);
- auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
- if (found_gen_types) {
- rw_mutex_lock(&found_gen_types->mutex);
- array_add(&found_gen_types->types, e);
- rw_mutex_unlock(&found_gen_types->mutex);
- } else {
- GenTypesData gen_types = {};
- gen_types.types = array_make<Entity *>(heap_allocator());
- array_add(&gen_types.types, e);
- map_set(&ctx->info->gen_types, original_type, gen_types);
+ auto *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(ctx, original_type);
+ mutex_lock(&found_gen_types->mutex);
+ defer (mutex_unlock(&found_gen_types->mutex));
+
+ for (Entity *prev : found_gen_types->types) {
+ if (prev == e) {
+ return;
+ }
}
- rw_mutex_unlock(&ctx->info->gen_types_mutex);
+ array_add(&found_gen_types->types, e);
}
@@ -393,7 +331,6 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
bool *is_polymorphic_,
Ast *node, Array<Operand> *poly_operands) {
Type *polymorphic_params_type = nullptr;
- bool can_check_fields = true;
GB_ASSERT(is_polymorphic_ != nullptr);
if (polymorphic_params == nullptr) {
@@ -403,6 +340,17 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
return polymorphic_params_type;
}
+
+ // bool is_variadic = false;
+ // isize variadic_index = 0;
+ // bool success = false;
+ // isize specialization_count = 0;
+ // polymorphic_params_type = check_get_params(ctx, ctx->scope, polymorphic_params, &is_variadic, &variadic_index, &success, &specialization_count, poly_operands);
+ // if (success) {
+ // return nullptr;
+ // }
+
+ bool can_check_fields = true;
ast_node(field_list, FieldList, polymorphic_params);
Slice<Ast *> params = field_list->list;
if (params.count != 0) {
@@ -417,11 +365,13 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
auto entities = array_make<Entity *>(permanent_allocator(), 0, variable_count);
+ i32 field_group_index = -1;
for_array(i, params) {
Ast *param = params[i];
if (param->kind != Ast_Field) {
continue;
}
+ field_group_index += 1;
ast_node(p, Field, param);
Ast *type_expr = p->type;
Ast *default_value = unparen_expr(p->default_value);
@@ -481,7 +431,7 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
type = t_invalid;
}
- if (is_type_polymorphic_type) {
+ if (is_type_polymorphic_type && !is_type_proc(type)) {
gbString str = type_to_string(type);
error(params[i], "Parameter types cannot be polymorphic, got %s", str);
gb_string_free(str);
@@ -523,13 +473,18 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
e->TypeName.is_type_alias = true;
e->flags |= EntityFlag_PolyConst;
} else {
- if (is_type_polymorphic(base_type(operand.type))) {
+ Type *t = operand.type;
+ if (is_type_proc(type)) {
+ t = determine_type_from_polymorphic(ctx, type, operand);
+ }
+ if (is_type_polymorphic(base_type(t))) {
*is_polymorphic_ = true;
can_check_fields = false;
}
if (e == nullptr) {
- e = alloc_entity_constant(scope, token, operand.type, operand.value);
+ e = alloc_entity_const_param(scope, token, t, operand.value, is_type_polymorphic(t));
e->Constant.param_value = param_value;
+ e->Constant.field_group_index = field_group_index;
}
}
} else {
@@ -538,7 +493,8 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
e->TypeName.is_type_alias = true;
e->flags |= EntityFlag_PolyConst;
} else {
- e = alloc_entity_constant(scope, token, type, param_value.value);
+ e = alloc_entity_const_param(scope, token, type, param_value.value, is_type_polymorphic(type));
+ e->Constant.field_group_index = field_group_index;
e->Constant.param_value = param_value;
}
}
@@ -559,7 +515,6 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
if (!*is_polymorphic_) {
*is_polymorphic_ = polymorphic_params != nullptr && poly_operands == nullptr;
}
-
return polymorphic_params_type;
}
@@ -591,6 +546,73 @@ gb_internal bool check_record_poly_operand_specialization(CheckerContext *ctx, T
return true;
}
+gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array<Operand> const &ordered_operands) {
+ for (Entity *e : found_gen_types->types) {
+ Type *t = base_type(e->type);
+ TypeTuple *tuple = nullptr;
+ switch (t->kind) {
+ case Type_Struct:
+ if (t->Struct.polymorphic_params) {
+ tuple = &t->Struct.polymorphic_params->Tuple;
+ }
+ break;
+ case Type_Union:
+ if (t->Union.polymorphic_params) {
+ tuple = &t->Union.polymorphic_params->Tuple;
+ }
+ break;
+ }
+ GB_ASSERT_MSG(tuple != nullptr, "%s :: %s", type_to_string(e->type), type_to_string(t));
+ GB_ASSERT(param_count == tuple->variables.count);
+
+ bool skip = false;
+
+ for (isize j = 0; j < param_count; j++) {
+ Entity *p = tuple->variables[j];
+ Operand o = {};
+ if (j < ordered_operands.count) {
+ o = ordered_operands[j];
+ }
+ if (o.expr == nullptr) {
+ continue;
+ }
+ Entity *oe = entity_of_node(o.expr);
+ if (p == oe) {
+ // NOTE(bill): This is the same type, make sure that it will be be same thing and use that
+ // Saves on a lot of checking too below
+ continue;
+ }
+
+ if (p->kind == Entity_TypeName) {
+ if (is_type_polymorphic(o.type)) {
+ // NOTE(bill): Do not add polymorphic version to the gen_types
+ skip = true;
+ break;
+ }
+ if (!are_types_identical(o.type, p->type)) {
+ skip = true;
+ break;
+ }
+ } else if (p->kind == Entity_Constant) {
+ if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) {
+ skip = true;
+ break;
+ }
+ if (!are_types_identical(o.type, p->type)) {
+ skip = true;
+ break;
+ }
+ } else {
+ GB_PANIC("Unknown entity kind");
+ }
+ }
+ if (!skip) {
+ return e;
+ }
+ }
+ return nullptr;
+};
+
gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {
GB_ASSERT(is_type_struct(struct_type));
@@ -613,9 +635,6 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *
scope_reserve(ctx->scope, min_field_count);
- rw_mutex_lock(&struct_type->Struct.fields_mutex);
- defer (rw_mutex_unlock(&struct_type->Struct.fields_mutex));
-
if (st->is_raw_union && min_field_count > 1) {
struct_type->Struct.is_raw_union = true;
context = str_lit("struct #raw_union");
@@ -643,18 +662,29 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *
gb_unused(where_clause_ok);
}
check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context);
+ wait_signal_set(&struct_type->Struct.fields_wait_signal);
}
- if (st->align != nullptr) {
- if (st->is_packed) {
- syntax_error(st->align, "'#align' cannot be applied with '#packed'");
- return;
- }
- i64 custom_align = 1;
- if (check_custom_align(ctx, st->align, &custom_align)) {
- struct_type->Struct.custom_align = custom_align;
- }
+#define ST_ALIGN(_name) if (st->_name != nullptr) { \
+ if (st->is_packed) { \
+ syntax_error(st->_name, "'#%s' cannot be applied with '#packed'", #_name); \
+ return; \
+ } \
+ i64 align = 1; \
+ if (check_custom_align(ctx, st->_name, &align, #_name)) { \
+ struct_type->Struct.custom_##_name = align; \
+ } \
}
+
+ ST_ALIGN(field_align);
+ ST_ALIGN(align);
+ if (struct_type->Struct.custom_align < struct_type->Struct.custom_field_align) {
+ warning(st->align, "#align(%lld) is defined to be less than #field_name(%lld)",
+ cast(long long)struct_type->Struct.custom_align,
+ cast(long long)struct_type->Struct.custom_field_align);
+ }
+
+#undef ST_ALIGN
}
gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {
GB_ASSERT(is_type_union(union_type));
@@ -746,7 +776,7 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no
if (ut->align != nullptr) {
i64 custom_align = 1;
- if (check_custom_align(ctx, ut->align, &custom_align)) {
+ if (check_custom_align(ctx, ut->align, &custom_align, "align")) {
if (variants.count == 0) {
error(ut->align, "An empty union cannot have a custom alignment");
} else {
@@ -760,6 +790,9 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam
ast_node(et, EnumType, node);
GB_ASSERT(is_type_enum(enum_type));
+ enum_type->Enum.base_type = t_int;
+ enum_type->Enum.scope = ctx->scope;
+
Type *base_type = t_int;
if (et->base_type != nullptr) {
base_type = check_type(ctx, et->base_type);
@@ -781,7 +814,6 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam
// NOTE(bill): Must be up here for the 'check_init_constant' system
enum_type->Enum.base_type = base_type;
- enum_type->Enum.scope = ctx->scope;
auto fields = array_make<Entity *>(permanent_allocator(), 0, et->fields.count);
@@ -898,6 +930,202 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam
enum_type->Enum.max_value_index = max_value_index;
}
+gb_internal bool is_valid_bit_field_backing_type(Type *type) {
+ if (type == nullptr) {
+ return false;
+ }
+ type = base_type(type);
+ if (is_type_untyped(type)) {
+ return false;
+ }
+ if (is_type_integer(type)) {
+ return true;
+ }
+ if (type->kind == Type_Array) {
+ return is_type_integer(type->Array.elem);
+ }
+ return false;
+}
+
+gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Type *named_type, Ast *node) {
+ ast_node(bf, BitFieldType, node);
+ GB_ASSERT(is_type_bit_field(bit_field_type));
+
+ Type *backing_type = check_type(ctx, bf->backing_type);
+ if (backing_type == nullptr || !is_valid_bit_field_backing_type(backing_type)) {
+ error(node, "Backing type for a bit_field must be an integer or an array of an integer");
+ return;
+ }
+
+ bit_field_type->BitField.backing_type = backing_type;
+ bit_field_type->BitField.scope = ctx->scope;
+
+ auto fields = array_make<Entity *>(permanent_allocator(), 0, bf->fields.count);
+ auto bit_sizes = array_make<u8> (permanent_allocator(), 0, bf->fields.count);
+ auto tags = array_make<String> (permanent_allocator(), 0, bf->fields.count);
+
+ u64 maximum_bit_size = 8 * type_size_of(backing_type);
+ u64 total_bit_size = 0;
+
+ for_array(i, bf->fields) {
+ i32 field_src_index = cast(i32)i;
+ Ast *field = bf->fields[i];
+ if (field->kind != Ast_BitFieldField) {
+ error(field, "Invalid AST for a bit_field");
+ continue;
+ }
+ ast_node(f, BitFieldField, field);
+ if (f->name == nullptr || f->name->kind != Ast_Ident) {
+ error(field, "A bit_field's field name must be an identifier");
+ continue;
+ }
+ CommentGroup *docs = f->docs;
+ CommentGroup *comment = f->comment;
+
+ String name = f->name->Ident.token.string;
+
+ if (f->type == nullptr) {
+ error(field, "A bit_field's field must have a type");
+ continue;
+ }
+
+ Type *type = check_type(ctx, f->type);
+ if (type_size_of(type) > 8) {
+ error(f->type, "The type of a bit_field's field must be <= 8 bytes, got %lld", cast(long long)type_size_of(type));
+ }
+
+ if (is_type_untyped(type)) {
+ gbString s = type_to_string(type);
+ error(f->type, "The type of a bit_field's field must be a typed integer, enum, or boolean, got %s", s);
+ gb_string_free(s);
+ } else if (!(is_type_integer(type) || is_type_enum(type) || is_type_boolean(type))) {
+ gbString s = type_to_string(type);
+ error(f->type, "The type of a bit_field's field must be an integer, enum, or boolean, got %s", s);
+ gb_string_free(s);
+ }
+
+ if (f->bit_size == nullptr) {
+ error(field, "A bit_field's field must have a specified bit size");
+ continue;
+ }
+
+
+ Operand o = {};
+ check_expr(ctx, &o, f->bit_size);
+ if (o.mode != Addressing_Constant) {
+ error(f->bit_size, "A bit_field's specified bit size must be a constant");
+ o.mode = Addressing_Invalid;
+ }
+ if (o.value.kind == ExactValue_Float) {
+ o.value = exact_value_to_integer(o.value);
+ }
+ if (f->bit_size->kind == Ast_BinaryExpr && f->bit_size->BinaryExpr.op.kind == Token_Or) {
+ gbString s = expr_to_string(f->bit_size);
+ error(f->bit_size, "Wrap the expression in parentheses, e.g. (%s)", s);
+ gb_string_free(s);
+ }
+
+ ExactValue bit_size = o.value;
+
+ if (bit_size.kind != ExactValue_Integer) {
+ gbString s = expr_to_string(f->bit_size);
+ error(f->bit_size, "Expected an integer constant value for the specified bit size, got %s", s);
+ gb_string_free(s);
+ }
+
+ if (scope_lookup_current(ctx->scope, name) != nullptr) {
+ error(f->name, "'%.*s' is already declared in this bit_field", LIT(name));
+ } else {
+ i64 bit_size_i64 = exact_value_to_i64(bit_size);
+ u8 bit_size_u8 = 0;
+ if (bit_size_i64 <= 0) {
+ error(f->bit_size, "A bit_field's specified bit size cannot be <= 0, got %lld", cast(long long)bit_size_i64);
+ bit_size_i64 = 1;
+ }
+ if (bit_size_i64 > 64) {
+ error(f->bit_size, "A bit_field's specified bit size cannot exceed 64 bits, got %lld", cast(long long)bit_size_i64);
+ bit_size_i64 = 64;
+ }
+ i64 sz = 8*type_size_of(type);
+ if (bit_size_i64 > sz) {
+ error(f->bit_size, "A bit_field's specified bit size cannot exceed its type, got %lld, expect <=%lld", cast(long long)bit_size_i64, cast(long long)sz);
+ bit_size_i64 = sz;
+ }
+
+ bit_size_u8 = cast(u8)bit_size_i64;
+
+ Entity *e = alloc_entity_field(ctx->scope, f->name->Ident.token, type, false, field_src_index);
+ e->Variable.docs = docs;
+ e->Variable.comment = comment;
+ e->Variable.bit_field_bit_size = bit_size_u8;
+ e->flags |= EntityFlag_BitFieldField;
+
+ add_entity(ctx, ctx->scope, nullptr, e);
+ array_add(&fields, e);
+ array_add(&bit_sizes, bit_size_u8);
+
+ String tag = f->tag.string;
+ if (tag.len != 0 && !unquote_string(permanent_allocator(), &tag, 0, tag.text[0] == '`')) {
+ error(f->tag, "Invalid string literal");
+ tag = {};
+ }
+ array_add(&tags, tag);
+
+ add_entity_use(ctx, field, e);
+ }
+ }
+
+ GB_ASSERT(fields.count <= bf->fields.count);
+
+ auto bit_offsets = slice_make<i64>(permanent_allocator(), fields.count);
+ i64 curr_offset = 0;
+ for_array(i, bit_sizes) {
+ bit_offsets[i] = curr_offset;
+ curr_offset += cast(i64)bit_sizes[i];
+ }
+
+ if (total_bit_size > maximum_bit_size) {
+ gbString s = type_to_string(backing_type);
+ error(node, "The numbers required %llu exceeds the backing type's (%s) bit size %llu",
+ cast(unsigned long long)total_bit_size,
+ s,
+ cast(unsigned long long)maximum_bit_size);
+ gb_string_free(s);
+ }
+
+ if (bit_sizes.count > 0 && is_type_integer(backing_type)) {
+ bool all_booleans = is_type_boolean(fields[0]->type);
+ bool all_ones = bit_sizes[0] == 1;
+ if (all_ones && all_booleans) {
+ for_array(i, bit_sizes) {
+ all_ones = bit_sizes[i] == 1;
+ if (!all_ones) {
+ break;
+ }
+ all_booleans = is_type_boolean(fields[i]->type);
+ if (!all_booleans) {
+ break;
+ }
+ }
+ if (all_ones && all_booleans) {
+ if (build_context.vet_flags & VetFlag_Style) {
+ char const *msg = "This 'bit_field' is better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer (-vet-style)";
+ error(node, msg);
+ } else {
+ char const *msg = "This 'bit_field' might be better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer";
+ warning(node, msg);
+ }
+ }
+ }
+ }
+
+
+ bit_field_type->BitField.fields = slice_from_array(fields);
+ bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes);
+ bit_field_type->BitField.bit_offsets = bit_offsets;
+ bit_field_type->BitField.tags = tags.data;
+}
+
gb_internal bool is_type_valid_bit_set_range(Type *t) {
if (is_type_integer(t)) {
return true;
@@ -1047,7 +1275,7 @@ gb_internal void check_bit_set_type(CheckerContext *c, Type *type, Type *named_t
}
if (!is_valid) {
if (actual_lower != lower) {
- error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internal the lower changed was changed 0 as an underlying type was set)", bits, bits_required);
+ error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internally the lower bound was changed to 0 as an underlying type was set)", bits, bits_required);
} else {
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, bits_required);
}
@@ -1117,7 +1345,7 @@ gb_internal void check_bit_set_type(CheckerContext *c, Type *type, Type *named_t
if (upper - lower >= bits) {
i64 bits_required = upper-lower+1;
if (lower_changed) {
- error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internal the lower changed was changed 0 as an underlying type was set)", bits, bits_required);
+ error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internally the lower bound was changed to 0 as an underlying type was set)", bits, bits_required);
} else {
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, bits_required);
}
@@ -1461,7 +1689,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
bool is_using = (p->flags&FieldFlag_using) != 0;
if ((check_vet_flags(param) & VetFlag_UsingParam) && is_using) {
ERROR_BLOCK();
- error(param, "'using' on a procedure parameter is now allowed when '-vet' or '-vet-using-param' is applied");
+ error(param, "'using' on a procedure parameter is not allowed when '-vet' or '-vet-using-param' is applied");
error_line("\t'using' is considered bad practice to use as a statement/procedure parameter outside of immediate refactoring\n");
}
@@ -1644,6 +1872,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
error(name, "'#any_int' can only be applied to variable fields");
p->flags &= ~FieldFlag_any_int;
}
+ if (p->flags&FieldFlag_no_broadcast) {
+ error(name, "'#no_broadcast' can only be applied to variable fields");
+ p->flags &= ~FieldFlag_no_broadcast;
+ }
if (p->flags&FieldFlag_by_ptr) {
error(name, "'#by_ptr' can only be applied to variable fields");
p->flags &= ~FieldFlag_by_ptr;
@@ -1701,7 +1933,13 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
}
}
}
- if (type != t_invalid && !check_is_assignable_to(ctx, &op, type)) {
+
+ bool allow_array_programming = true;
+ if (p->flags&FieldFlag_no_broadcast) {
+ allow_array_programming = false;
+ }
+
+ if (type != t_invalid && !check_is_assignable_to(ctx, &op, type, allow_array_programming)) {
bool ok = true;
if (p->flags&FieldFlag_any_int) {
if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
@@ -1777,6 +2015,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
if (p->flags&FieldFlag_no_alias) {
param->flags |= EntityFlag_NoAlias;
}
+ if (p->flags&FieldFlag_no_broadcast) {
+ param->flags |= EntityFlag_NoBroadcast;
+ }
+
if (p->flags&FieldFlag_any_int) {
if (!is_type_integer(param->type) && !is_type_enum(param->type)) {
gbString str = type_to_string(param->type);
@@ -2157,6 +2399,8 @@ gb_internal i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
return 0;
}
+ ERROR_BLOCK();
+
gbString s = expr_to_string(o->expr);
error(e, "Array count must be a constant integer, got %s", s);
gb_string_free(s);
@@ -2226,6 +2470,31 @@ gb_internal void map_cell_size_and_len(Type *type, i64 *size_, i64 *len_) {
if (len_) *len_ = len;
}
+gb_internal Type *get_map_cell_type(Type *type) {
+ i64 size, len;
+ i64 elem_size = type_size_of(type);
+ map_cell_size_and_len(type, &size, &len);
+
+ if (size == len*elem_size) {
+ return type;
+ }
+
+ i64 padding = size - len*elem_size;
+ GB_ASSERT(padding > 0);
+
+ // Padding exists
+ Type *s = alloc_type_struct();
+ Scope *scope = create_scope(nullptr, nullptr);
+ s->Struct.fields = slice_make<Entity *>(permanent_allocator(), 2);
+ s->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("v"), alloc_type_array(type, len), false, 0, EntityState_Resolved);
+ s->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("_"), alloc_type_array(t_u8, padding), false, 1, EntityState_Resolved);
+ s->Struct.scope = scope;
+ wait_signal_set(&s->Struct.fields_wait_signal);
+ gb_unused(type_size_of(s));
+
+ return s;
+}
+
gb_internal void init_map_internal_types(Type *type) {
GB_ASSERT(type->kind == Type_Map);
GB_ASSERT(t_allocator != nullptr);
@@ -2236,6 +2505,43 @@ gb_internal void init_map_internal_types(Type *type) {
GB_ASSERT(key != nullptr);
GB_ASSERT(value != nullptr);
+
+
+ Type *key_cell = get_map_cell_type(key);
+ Type *value_cell = get_map_cell_type(value);
+
+ Type *metadata_type = alloc_type_struct();
+ Scope *metadata_scope = create_scope(nullptr, nullptr);
+ metadata_type->Struct.fields = slice_make<Entity *>(permanent_allocator(), 5);
+ metadata_type->Struct.fields[0] = alloc_entity_field(metadata_scope, make_token_ident("key"), key, false, 0, EntityState_Resolved);
+ metadata_type->Struct.fields[1] = alloc_entity_field(metadata_scope, make_token_ident("value"), value, false, 1, EntityState_Resolved);
+ metadata_type->Struct.fields[2] = alloc_entity_field(metadata_scope, make_token_ident("hash"), t_uintptr, false, 2, EntityState_Resolved);
+ metadata_type->Struct.fields[3] = alloc_entity_field(metadata_scope, make_token_ident("key_cell"), key_cell, false, 3, EntityState_Resolved);
+ metadata_type->Struct.fields[4] = alloc_entity_field(metadata_scope, make_token_ident("value_cell"), value_cell, false, 4, EntityState_Resolved);
+ metadata_type->Struct.scope = metadata_scope;
+ metadata_type->Struct.node = nullptr;
+ wait_signal_set(&metadata_type->Struct.fields_wait_signal);
+
+ gb_unused(type_size_of(metadata_type));
+
+ // NOTE(bill): ^struct{key: Key, value: Value, hash: uintptr}
+ metadata_type = alloc_type_pointer(metadata_type);
+
+
+ Scope *scope = create_scope(nullptr, nullptr);
+ Type *debug_type = alloc_type_struct();
+ debug_type->Struct.fields = slice_make<Entity *>(permanent_allocator(), 3);
+ debug_type->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("data"), metadata_type, false, 0, EntityState_Resolved);
+ debug_type->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("len"), t_int, false, 1, EntityState_Resolved);
+ debug_type->Struct.fields[2] = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, 2, EntityState_Resolved);
+ debug_type->Struct.scope = scope;
+ debug_type->Struct.node = nullptr;
+ wait_signal_set(&debug_type->Struct.fields_wait_signal);
+
+ gb_unused(type_size_of(debug_type));
+
+ type->Map.debug_metadata_type = debug_type;
+
type->Map.lookup_result_type = make_optional_ok_type(value);
}
@@ -2368,11 +2674,123 @@ gb_internal void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node)
}
type_assign:;
- *type = alloc_type_matrix(elem, row_count, column_count, generic_row, generic_column);
+ *type = alloc_type_matrix(elem, row_count, column_count, generic_row, generic_column, mt->is_row_major);
return;
}
+struct SoaTypeWorkerData {
+ CheckerContext ctx;
+ Type * type;
+ bool wait_to_finish;
+};
+
+
+gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finish) {
+ Type *original_type = t;
+ gb_unused(original_type);
+
+ t = base_type(t);
+ if (t == nullptr || !is_type_soa_struct(t)) {
+ return true;
+ }
+
+ MUTEX_GUARD(&t->Struct.soa_mutex);
+
+ if (t->Struct.fields_wait_signal.futex.load()) {
+ return true;
+ }
+
+ isize field_count = 0;
+ i32 extra_field_count = 0;
+ switch (t->Struct.soa_kind) {
+ case StructSoa_Fixed: extra_field_count = 0; break;
+ case StructSoa_Slice: extra_field_count = 1; break;
+ case StructSoa_Dynamic: extra_field_count = 3; break;
+ }
+
+ Scope *scope = t->Struct.scope;
+ i64 soa_count = t->Struct.soa_count;
+ Type *elem = t->Struct.soa_elem;
+ Type *old_struct = base_type(elem);
+ GB_ASSERT(old_struct->kind == Type_Struct);
+
+ if (wait_to_finish) {
+ wait_signal_until_available(&old_struct->Struct.fields_wait_signal);
+ } else {
+ GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load());
+ }
+
+ field_count = old_struct->Struct.fields.count;
+
+ t->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
+ t->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
+
+
+ auto const &add_entity = [](Scope *scope, Entity *entity) {
+ String name = entity->token.string;
+ if (!is_blank_ident(name)) {
+ Entity *ie = scope_insert(scope, entity);
+ if (ie != nullptr) {
+ redeclaration_error(name, entity, ie);
+ }
+ }
+ };
+
+
+ for_array(i, old_struct->Struct.fields) {
+ Entity *old_field = old_struct->Struct.fields[i];
+ if (old_field->kind == Entity_Variable) {
+ Type *field_type = nullptr;
+ if (t->Struct.soa_kind == StructSoa_Fixed) {
+ GB_ASSERT(soa_count >= 0);
+ field_type = alloc_type_array(old_field->type, soa_count);
+ } else {
+ field_type = alloc_type_pointer(old_field->type);
+ }
+ Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_index);
+ t->Struct.fields[i] = new_field;
+ add_entity(scope, new_field);
+ new_field->flags |= EntityFlag_Used;
+ } else {
+ t->Struct.fields[i] = old_field;
+ }
+
+ t->Struct.tags[i] = old_struct->Struct.tags[i];
+ }
+
+ if (t->Struct.soa_kind != StructSoa_Fixed) {
+ Entity *len_field = alloc_entity_field(scope, make_token_ident("__$len"), t_int, false, cast(i32)field_count+0);
+ t->Struct.fields[field_count+0] = len_field;
+ add_entity(scope, len_field);
+ len_field->flags |= EntityFlag_Used;
+
+ if (t->Struct.soa_kind == StructSoa_Dynamic) {
+ Entity *cap_field = alloc_entity_field(scope, make_token_ident("__$cap"), t_int, false, cast(i32)field_count+1);
+ t->Struct.fields[field_count+1] = cap_field;
+ add_entity(scope, cap_field);
+ cap_field->flags |= EntityFlag_Used;
+
+ init_mem_allocator(checker);
+ Entity *allocator_field = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, cast(i32)field_count+2);
+ t->Struct.fields[field_count+2] = allocator_field;
+ add_entity(scope, allocator_field);
+ allocator_field->flags |= EntityFlag_Used;
+ }
+ }
+
+ // add_type_info_type(ctx, original_type);
+
+ wait_signal_set(&t->Struct.fields_wait_signal);
+ return true;
+}
+
+gb_internal WORKER_TASK_PROC(complete_soa_type_worker) {
+ SoaTypeWorkerData *wd = cast(SoaTypeWorkerData *)data;
+ complete_soa_type(wd->ctx.checker, wd->type, wd->wait_to_finish);
+ return 0;
+}
+
gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem, i64 count, Type *generic_type, StructSoaKind soa_kind) {
@@ -2380,15 +2798,16 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
bool is_polymorphic = is_type_polymorphic(elem);
- if ((!is_polymorphic || soa_kind == StructSoa_Fixed) && !is_type_struct(elem) && !is_type_raw_union(elem) && !(is_type_array(elem) && bt_elem->Array.count <= 4)) {
+ if (!is_polymorphic && !is_type_struct(elem) && !is_type_raw_union(elem) && !(is_type_array(elem) && bt_elem->Array.count <= 4)) {
gbString str = type_to_string(elem);
error(elem_expr, "Invalid type for an #soa array, expected a struct or array of length 4 or below, got '%s'", str);
gb_string_free(str);
return alloc_type_array(elem, count, generic_type);
}
- Type *soa_struct = nullptr;
- Scope *scope = nullptr;
+ Type * soa_struct = nullptr;
+ Scope *scope = nullptr;
+ bool is_complete = false;
isize field_count = 0;
i32 extra_field_count = 0;
@@ -2397,39 +2816,43 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
case StructSoa_Slice: extra_field_count = 1; break;
case StructSoa_Dynamic: extra_field_count = 3; break;
}
- if (is_polymorphic && soa_kind != StructSoa_Fixed) {
+
+ soa_struct = alloc_type_struct();
+ soa_struct->Struct.soa_kind = soa_kind;
+ soa_struct->Struct.soa_elem = elem;
+ soa_struct->Struct.is_polymorphic = is_polymorphic;
+ soa_struct->Struct.node = array_typ_expr;
+
+ if (count > I32_MAX) {
+ count = I32_MAX;
+ error(array_typ_expr, "Array count too large for an #soa struct, got %lld", cast(long long)count);
+ }
+ soa_struct->Struct.soa_count = cast(i32)count;
+
+ scope = create_scope(ctx->info, ctx->scope);
+ soa_struct->Struct.scope = scope;
+
+ if (elem && elem->kind == Type_Named) {
+ add_declaration_dependency(ctx, elem->Named.type_name);
+ }
+
+ if (is_polymorphic) {
field_count = 0;
- soa_struct = alloc_type_struct();
soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
- soa_struct->Struct.node = array_typ_expr;
- soa_struct->Struct.soa_kind = soa_kind;
- soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = 0;
- soa_struct->Struct.is_polymorphic = true;
- scope = create_scope(ctx->info, ctx->scope);
- soa_struct->Struct.scope = scope;
+ is_complete = true;
+
} else if (is_type_array(elem)) {
Type *old_array = base_type(elem);
field_count = cast(isize)old_array->Array.count;
- soa_struct = alloc_type_struct();
soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
- soa_struct->Struct.node = array_typ_expr;
- soa_struct->Struct.soa_kind = soa_kind;
- soa_struct->Struct.soa_elem = elem;
- if (count > I32_MAX) {
- count = I32_MAX;
- error(array_typ_expr, "Array count too large for an #soa struct, got %lld", cast(long long)count);
- }
- soa_struct->Struct.soa_count = cast(i32)count;
- scope = create_scope(ctx->info, ctx->scope);
string_map_init(&scope->elements, 8);
- soa_struct->Struct.scope = scope;
String params_xyzw[4] = {
str_lit("x"),
@@ -2455,65 +2878,57 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
add_entity_use(ctx, nullptr, new_field);
}
+ is_complete = true;
+
} else {
GB_ASSERT(is_type_struct(elem));
Type *old_struct = base_type(elem);
- field_count = old_struct->Struct.fields.count;
- soa_struct = alloc_type_struct();
- soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
- soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
- soa_struct->Struct.node = array_typ_expr;
- soa_struct->Struct.soa_kind = soa_kind;
- soa_struct->Struct.soa_elem = elem;
- if (count > I32_MAX) {
- count = I32_MAX;
- error(array_typ_expr, "Array count too large for an #soa struct, got %lld", cast(long long)count);
- }
- soa_struct->Struct.soa_count = cast(i32)count;
-
- scope = create_scope(ctx->info, old_struct->Struct.scope->parent);
- soa_struct->Struct.scope = scope;
-
- for_array(i, old_struct->Struct.fields) {
- Entity *old_field = old_struct->Struct.fields[i];
- if (old_field->kind == Entity_Variable) {
- Type *field_type = nullptr;
- if (soa_kind == StructSoa_Fixed) {
- GB_ASSERT(count >= 0);
- field_type = alloc_type_array(old_field->type, count);
+ if (old_struct->Struct.fields_wait_signal.futex.load()) {
+ field_count = old_struct->Struct.fields.count;
+
+ soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
+ soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
+
+ for_array(i, old_struct->Struct.fields) {
+ Entity *old_field = old_struct->Struct.fields[i];
+ if (old_field->kind == Entity_Variable) {
+ Type *field_type = nullptr;
+ if (soa_kind == StructSoa_Fixed) {
+ GB_ASSERT(count >= 0);
+ field_type = alloc_type_array(old_field->type, count);
+ } else {
+ field_type = alloc_type_pointer(old_field->type);
+ }
+ Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_index);
+ soa_struct->Struct.fields[i] = new_field;
+ add_entity(ctx, scope, nullptr, new_field);
+ add_entity_use(ctx, nullptr, new_field);
} else {
- field_type = alloc_type_pointer(old_field->type);
+ soa_struct->Struct.fields[i] = old_field;
}
- Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_index);
- soa_struct->Struct.fields[i] = new_field;
- add_entity(ctx, scope, nullptr, new_field);
- add_entity_use(ctx, nullptr, new_field);
- } else {
- soa_struct->Struct.fields[i] = old_field;
- }
- soa_struct->Struct.tags[i] = old_struct->Struct.tags[i];
+ soa_struct->Struct.tags[i] = old_struct->Struct.tags[i];
+ }
+ is_complete = true;
}
}
- if (soa_kind != StructSoa_Fixed) {
- Entity *len_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count+0);
+ if (is_complete && soa_kind != StructSoa_Fixed) {
+ Entity *len_field = alloc_entity_field(scope, make_token_ident("__$len"), t_int, false, cast(i32)field_count+0);
soa_struct->Struct.fields[field_count+0] = len_field;
add_entity(ctx, scope, nullptr, len_field);
add_entity_use(ctx, nullptr, len_field);
if (soa_kind == StructSoa_Dynamic) {
- Entity *cap_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count+1);
+ Entity *cap_field = alloc_entity_field(scope, make_token_ident("__$cap"), t_int, false, cast(i32)field_count+1);
soa_struct->Struct.fields[field_count+1] = cap_field;
add_entity(ctx, scope, nullptr, cap_field);
add_entity_use(ctx, nullptr, cap_field);
- Token token = {};
- token.string = str_lit("allocator");
init_mem_allocator(ctx->checker);
- Entity *allocator_field = alloc_entity_field(scope, token, t_allocator, false, cast(i32)field_count+2);
+ Entity *allocator_field = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, cast(i32)field_count+2);
soa_struct->Struct.fields[field_count+2] = allocator_field;
add_entity(ctx, scope, nullptr, allocator_field);
add_entity_use(ctx, nullptr, allocator_field);
@@ -2525,7 +2940,18 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved);
add_entity(ctx, scope, nullptr, base_type_entity);
- add_type_info_type(ctx, soa_struct);
+ if (is_complete) {
+ add_type_info_type(ctx, soa_struct);
+ wait_signal_set(&soa_struct->Struct.fields_wait_signal);
+ } else {
+ SoaTypeWorkerData *wd = gb_alloc_item(permanent_allocator(), SoaTypeWorkerData);
+ wd->ctx = *ctx;
+ wd->type = soa_struct;
+ wd->wait_to_finish = true;
+
+ mpsc_enqueue(&ctx->checker->soa_types_to_complete, soa_struct);
+ thread_pool_add_task(complete_soa_type_worker, wd);
+ }
return soa_struct;
}
@@ -2544,6 +2970,113 @@ gb_internal Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_
return make_soa_struct_internal(ctx, array_typ_expr, elem_expr, elem, -1, nullptr, StructSoa_Dynamic);
}
+gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) {
+ ast_node(at, ArrayType, e);
+ if (at->count != nullptr) {
+ Operand o = {};
+ i64 count = check_array_count(ctx, &o, at->count);
+ Type *generic_type = nullptr;
+
+ Type *elem = check_type_expr(ctx, at->elem, nullptr);
+
+ if (o.mode == Addressing_Type && o.type->kind == Type_Generic) {
+ generic_type = o.type;
+ } else if (o.mode == Addressing_Type && is_type_enum(o.type)) {
+ Type *index = o.type;
+ Type *bt = base_type(index);
+ GB_ASSERT(bt->kind == Type_Enum);
+
+ Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, bt->Enum.fields.count, Token_Invalid);
+
+ bool is_sparse = false;
+ if (at->tag != nullptr) {
+ GB_ASSERT(at->tag->kind == Ast_BasicDirective);
+ String name = at->tag->BasicDirective.name.string;
+ if (name == "sparse") {
+ is_sparse = true;
+ } else {
+ error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name));
+ }
+ }
+
+ if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) {
+ ERROR_BLOCK();
+
+ error(e, "Non-contiguous enumeration used as an index in an enumerated array");
+ long long ea_count = cast(long long)t->EnumeratedArray.count;
+ long long enum_count = cast(long long)bt->Enum.fields.count;
+ error_line("\tenumerated array length: %lld\n", ea_count);
+ error_line("\tenum field count: %lld\n", enum_count);
+ error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n");
+ if (2*enum_count < ea_count) {
+ error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n");
+ error_line("\t this warning will be removed if #sparse is applied\n");
+ }
+ }
+ t->EnumeratedArray.is_sparse = is_sparse;
+
+ *type = t;
+
+ return;
+ }
+
+ if (count < 0) {
+ error(at->count, "? can only be used in conjuction with compound literals");
+ count = 0;
+ }
+
+
+ if (at->tag != nullptr) {
+ GB_ASSERT(at->tag->kind == Ast_BasicDirective);
+ String name = at->tag->BasicDirective.name.string;
+ if (name == "soa") {
+ *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type);
+ } else if (name == "simd") {
+ if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) {
+ gbString str = type_to_string(elem);
+ error(at->elem, "Invalid element type for #simd, expected an integer, float, or boolean with no specific endianness, got '%s'", str);
+ gb_string_free(str);
+ *type = alloc_type_array(elem, count, generic_type);
+ return;
+ }
+
+ if (generic_type != nullptr) {
+ // Ignore
+ } else if (count < 1 || !is_power_of_two(count)) {
+ error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count);
+ *type = alloc_type_array(elem, count, generic_type);
+ return;
+ }
+
+ *type = alloc_type_simd_vector(count, elem, generic_type);
+
+ if (count > SIMD_ELEMENT_COUNT_MAX) {
+ error(at->count, "#simd support a maximum element count of %d, got %lld", SIMD_ELEMENT_COUNT_MAX, cast(long long)count);
+ }
+ } else {
+ error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name));
+ *type = alloc_type_array(elem, count, generic_type);
+ }
+ } else {
+ *type = alloc_type_array(elem, count, generic_type);
+ }
+ } else {
+ Type *elem = check_type(ctx, at->elem);
+
+ if (at->tag != nullptr) {
+ GB_ASSERT(at->tag->kind == Ast_BasicDirective);
+ String name = at->tag->BasicDirective.name.string;
+ if (name == "soa") {
+ *type = make_soa_struct_slice(ctx, e, at->elem, elem);
+ } else {
+ error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name));
+ *type = alloc_type_slice(elem);
+ }
+ } else {
+ *type = alloc_type_slice(elem);
+ }
+ }
+}
gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) {
GB_ASSERT_NOT_NULL(type);
if (e == nullptr) {
@@ -2699,20 +3232,22 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
Type *elem = t_invalid;
Operand o = {};
+
check_expr_or_type(&c, &o, pt->type);
if (o.mode != Addressing_Invalid && o.mode != Addressing_Type) {
- // NOTE(bill): call check_type_expr again to get a consistent error message
- ERROR_BLOCK();
- elem = check_type_expr(&c, pt->type, nullptr);
if (o.mode == Addressing_Variable) {
gbString s = expr_to_string(pt->type);
- error_line("\tSuggestion: ^ is used for pointer types, did you mean '&%s'?\n", s);
+ error(e, "^ is used for pointer types, did you mean '&%s'?", s);
gb_string_free(s);
+ } else {
+ // NOTE(bill): call check_type_expr again to get a consistent error message
+ elem = check_type_expr(&c, pt->type, nullptr);
}
} else {
elem = o.type;
}
+
if (pt->tag != nullptr) {
GB_ASSERT(pt->tag->kind == Ast_BasicDirective);
String name = pt->tag->BasicDirective.name.string;
@@ -2782,109 +3317,7 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
case_end;
case_ast_node(at, ArrayType, e);
- if (at->count != nullptr) {
- Operand o = {};
- i64 count = check_array_count(ctx, &o, at->count);
- Type *generic_type = nullptr;
-
- Type *elem = check_type_expr(ctx, at->elem, nullptr);
-
- if (o.mode == Addressing_Type && o.type->kind == Type_Generic) {
- generic_type = o.type;
- } else if (o.mode == Addressing_Type && is_type_enum(o.type)) {
- Type *index = o.type;
- Type *bt = base_type(index);
- GB_ASSERT(bt->kind == Type_Enum);
-
- Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, bt->Enum.fields.count, Token_Invalid);
-
- bool is_sparse = false;
- if (at->tag != nullptr) {
- GB_ASSERT(at->tag->kind == Ast_BasicDirective);
- String name = at->tag->BasicDirective.name.string;
- if (name == "sparse") {
- is_sparse = true;
- } else {
- error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name));
- }
- }
-
- if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) {
- error(e, "Non-contiguous enumeration used as an index in an enumerated array");
- long long ea_count = cast(long long)t->EnumeratedArray.count;
- long long enum_count = cast(long long)bt->Enum.fields.count;
- error_line("\tenumerated array length: %lld\n", ea_count);
- error_line("\tenum field count: %lld\n", enum_count);
- error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n");
- if (2*enum_count < ea_count) {
- error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n");
- error_line("\t this warning will be removed if #sparse is applied\n");
- }
- }
- t->EnumeratedArray.is_sparse = is_sparse;
-
- *type = t;
-
- goto array_end;
- }
-
- if (count < 0) {
- error(at->count, "? can only be used in conjuction with compound literals");
- count = 0;
- }
-
-
- if (at->tag != nullptr) {
- GB_ASSERT(at->tag->kind == Ast_BasicDirective);
- String name = at->tag->BasicDirective.name.string;
- if (name == "soa") {
- *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type);
- } else if (name == "simd") {
- if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) {
- gbString str = type_to_string(elem);
- error(at->elem, "Invalid element type for #simd, expected an integer, float, or boolean with no specific endianness, got '%s'", str);
- gb_string_free(str);
- *type = alloc_type_array(elem, count, generic_type);
- goto array_end;
- }
-
- if (generic_type != nullptr) {
- // Ignore
- } else if (count < 1 || !is_power_of_two(count)) {
- error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count);
- *type = alloc_type_array(elem, count, generic_type);
- goto array_end;
- }
-
- *type = alloc_type_simd_vector(count, elem, generic_type);
-
- if (count > SIMD_ELEMENT_COUNT_MAX) {
- error(at->count, "#simd support a maximum element count of %d, got %lld", SIMD_ELEMENT_COUNT_MAX, cast(long long)count);
- }
- } else {
- error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name));
- *type = alloc_type_array(elem, count, generic_type);
- }
- } else {
- *type = alloc_type_array(elem, count, generic_type);
- }
- } else {
- Type *elem = check_type(ctx, at->elem);
-
- if (at->tag != nullptr) {
- GB_ASSERT(at->tag->kind == Ast_BasicDirective);
- String name = at->tag->BasicDirective.name.string;
- if (name == "soa") {
- *type = make_soa_struct_slice(ctx, e, at->elem, elem);
- } else {
- error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name));
- *type = alloc_type_slice(elem);
- }
- } else {
- *type = alloc_type_slice(elem);
- }
- }
- array_end:
+ check_array_type_internal(ctx, e, type, named_type);
set_base_type(named_type, *type);
return true;
case_end;
@@ -2959,6 +3392,20 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
return true;
case_end;
+ case_ast_node(bf, BitFieldType, e);
+ bool ips = ctx->in_polymorphic_specialization;
+ defer (ctx->in_polymorphic_specialization = ips);
+ ctx->in_polymorphic_specialization = false;
+
+ *type = alloc_type_bit_field();
+ set_base_type(named_type, *type);
+ check_open_scope(ctx, e);
+ check_bit_field_type(ctx, *type, named_type, e);
+ check_close_scope(ctx);
+ (*type)->BitField.node = e;
+ return true;
+ case_end;
+
case_ast_node(pt, ProcType, e);
bool ips = ctx->in_polymorphic_specialization;
@@ -3040,9 +3487,51 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type)
if (!ok) {
gbString err_str = expr_to_string(e);
+ defer (gb_string_free(err_str));
+
+ begin_error_block();
error(e, "'%s' is not a type", err_str);
- gb_string_free(err_str);
+
type = t_invalid;
+
+
+ // NOTE(bill): Check for common mistakes from C programmers
+ // e.g. T[] and T[N]
+ // e.g. *T
+ Ast *node = unparen_expr(e);
+ if (node && node->kind == Ast_IndexExpr) {
+ gbString index_str = nullptr;
+ if (node->IndexExpr.index) {
+ index_str = expr_to_string(node->IndexExpr.index);
+ }
+ defer (gb_string_free(index_str));
+
+ gbString type_str = expr_to_string(node->IndexExpr.expr);
+ defer (gb_string_free(type_str));
+
+ error_line("\tSuggestion: Did you mean '[%s]%s'?\n", index_str ? index_str : "", type_str);
+ end_error_block();
+
+ // NOTE(bill): Minimize error propagation of bad array syntax by treating this like a type
+ if (node->IndexExpr.expr != nullptr) {
+ Ast *pseudo_array_expr = ast_array_type(e->file(), ast_token(node->IndexExpr.expr), node->IndexExpr.index, node->IndexExpr.expr);
+ check_array_type_internal(ctx, pseudo_array_expr, &type, nullptr);
+ }
+ } else if (node && node->kind == Ast_UnaryExpr && node->UnaryExpr.op.kind == Token_Mul) {
+ gbString type_str = expr_to_string(node->UnaryExpr.expr);
+ defer (gb_string_free(type_str));
+
+ error_line("\tSuggestion: Did you mean '^%s'?\n", type_str);
+ end_error_block();
+
+ // NOTE(bill): Minimize error propagation of bad array syntax by treating this like a type
+ if (node->UnaryExpr.expr != nullptr) {
+ Ast *pseudo_pointer_expr = ast_pointer_type(e->file(), ast_token(node->UnaryExpr.expr), node->UnaryExpr.expr);
+ return check_type_expr(ctx, pseudo_pointer_expr, named_type);
+ }
+ } else {
+ end_error_block();
+ }
}
if (type == nullptr) {
diff --git a/src/checker.cpp b/src/checker.cpp
index 917340a20..244e7efbd 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -224,7 +224,7 @@ gb_internal Scope *create_scope(CheckerInfo *info, Scope *parent) {
if (parent != nullptr && parent != builtin_pkg->scope) {
Scope *prev_head_child = parent->head_child.exchange(s, std::memory_order_acq_rel);
if (prev_head_child) {
- prev_head_child->next.store(s, std::memory_order_release);
+ s->next.store(prev_head_child, std::memory_order_release);
}
}
@@ -313,6 +313,7 @@ gb_internal void add_scope(CheckerContext *c, Ast *node, Scope *scope) {
case Ast_StructType: node->StructType.scope = scope; break;
case Ast_UnionType: node->UnionType.scope = scope; break;
case Ast_EnumType: node->EnumType.scope = scope; break;
+ case Ast_BitFieldType: node->BitFieldType.scope = scope; break;
default: GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind]));
}
}
@@ -334,6 +335,7 @@ gb_internal Scope *scope_of_node(Ast *node) {
case Ast_StructType: return node->StructType.scope;
case Ast_UnionType: return node->UnionType.scope;
case Ast_EnumType: return node->EnumType.scope;
+ case Ast_BitFieldType: return node->BitFieldType.scope;
}
GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind]));
return nullptr;
@@ -355,6 +357,7 @@ gb_internal void check_open_scope(CheckerContext *c, Ast *node) {
case Ast_EnumType:
case Ast_UnionType:
case Ast_BitSetType:
+ case Ast_BitFieldType:
scope->flags |= ScopeFlag_Type;
break;
}
@@ -700,6 +703,15 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
array_add(&vetted_entities, ve_unused);
} else if (is_shadowed) {
array_add(&vetted_entities, ve_shadowed);
+ } else if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using)) == 0) {
+ i64 sz = type_size_of(e->type);
+ // TODO(bill): When is a good size warn?
+ // Is 128 KiB good enough?
+ if (sz >= 1ll<<17) {
+ gbString type_str = type_to_string(e->type);
+ warning(e->token, "Declaration of '%.*s' may cause a stack overflow due to its type '%s' having a size of %lld bytes", LIT(e->token.string), type_str, cast(long long)sz);
+ gb_string_free(type_str);
+ }
}
}
rw_mutex_shared_unlock(&scope->mutex);
@@ -731,17 +743,6 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
break;
}
}
-
- if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using)) == 0) {
- i64 sz = type_size_of(e->type);
- // TODO(bill): When is a good size warn?
- // Is 128 KiB good enough?
- if (sz >= 1ll<<17) {
- gbString type_str = type_to_string(e->type);
- warning(e->token, "Declaration of '%.*s' may cause a stack overflow due to its type '%s' having a size of %lld bytes", LIT(name), type_str, cast(long long)sz);
- gb_string_free(type_str);
- }
- }
}
array_free(&vetted_entities);
@@ -770,15 +771,17 @@ gb_internal void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *
rw_mutex_unlock(&d->type_info_deps_mutex);
}
-gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) {
+
+gb_internal AstPackage *get_runtime_package(CheckerInfo *info) {
+ String name = str_lit("runtime");
gbAllocator a = heap_allocator();
- String path = get_fullpath_core(a, name);
+ String path = get_fullpath_base_collection(a, name, nullptr);
defer (gb_free(a, path.text));
auto found = string_map_get(&info->packages, path);
if (found == nullptr) {
gb_printf_err("Name: %.*s\n", LIT(name));
gb_printf_err("Fullpath: %.*s\n", LIT(path));
-
+
for (auto const &entry : info->packages) {
gb_printf_err("%.*s\n", LIT(entry.key));
}
@@ -787,14 +790,37 @@ gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) {
return *found;
}
+gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) {
+ if (name == "runtime") {
+ return get_runtime_package(info);
+ }
+
+ gbAllocator a = heap_allocator();
+ String path = get_fullpath_core_collection(a, name, nullptr);
+ defer (gb_free(a, path.text));
+ auto found = string_map_get(&info->packages, path);
+ if (found == nullptr) {
+ gb_printf_err("Name: %.*s\n", LIT(name));
+ gb_printf_err("Fullpath: %.*s\n", LIT(path));
+
+ for (auto const &entry : info->packages) {
+ gb_printf_err("%.*s\n", LIT(entry.key));
+ }
+ GB_ASSERT_MSG(found != nullptr, "Missing core package %.*s", LIT(name));
+ }
+ return *found;
+}
-gb_internal void add_package_dependency(CheckerContext *c, char const *package_name, char const *name) {
+gb_internal void add_package_dependency(CheckerContext *c, char const *package_name, char const *name, bool required=false) {
String n = make_string_c(name);
AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
Entity *e = scope_lookup(p->scope, n);
GB_ASSERT_MSG(e != nullptr, "%s", name);
GB_ASSERT(c->decl != nullptr);
e->flags |= EntityFlag_Used;
+ if (required) {
+ e->flags |= EntityFlag_Require;
+ }
add_dependency(c->info, c->decl, e);
}
@@ -982,6 +1008,7 @@ gb_internal void init_universal(void) {
{"Linux", TargetOs_linux},
{"Essence", TargetOs_essence},
{"FreeBSD", TargetOs_freebsd},
+ {"Haiku", TargetOs_haiku},
{"OpenBSD", TargetOs_openbsd},
{"WASI", TargetOs_wasi},
{"JS", TargetOs_js},
@@ -1068,24 +1095,43 @@ gb_internal void init_universal(void) {
scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name);
}
-
- add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG);
- add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT);
- add_global_bool_constant("ODIN_DEFAULT_TO_NIL_ALLOCATOR", bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR);
- add_global_bool_constant("ODIN_NO_DYNAMIC_LITERALS", bc->no_dynamic_literals);
- add_global_bool_constant("ODIN_NO_CRT", bc->no_crt);
- add_global_bool_constant("ODIN_USE_SEPARATE_MODULES", bc->use_separate_modules);
- add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test);
- add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point);
- add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES);
- add_global_bool_constant("ODIN_NO_RTTI", bc->no_rtti);
-
- add_global_bool_constant("ODIN_VALGRIND_SUPPORT", bc->ODIN_VALGRIND_SUPPORT);
- add_global_bool_constant("ODIN_TILDE", bc->tilde_backend);
+ {
+ int minimum_os_version = 0;
+ if (build_context.minimum_os_version_string != "") {
+ int major, minor, revision = 0;
+ sscanf(cast(const char *)(build_context.minimum_os_version_string.text), "%d.%d.%d", &major, &minor, &revision);
+ minimum_os_version = (major*10000)+(minor*100)+revision;
+ }
+ add_global_constant("ODIN_MINIMUM_OS_VERSION", t_untyped_integer, exact_value_i64(minimum_os_version));
+ }
+
+ add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG);
+ add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT);
+ add_global_bool_constant("ODIN_DEFAULT_TO_NIL_ALLOCATOR", bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR);
+ add_global_bool_constant("ODIN_DEFAULT_TO_PANIC_ALLOCATOR", bc->ODIN_DEFAULT_TO_PANIC_ALLOCATOR);
+ add_global_bool_constant("ODIN_NO_DYNAMIC_LITERALS", bc->no_dynamic_literals);
+ add_global_bool_constant("ODIN_NO_CRT", bc->no_crt);
+ add_global_bool_constant("ODIN_USE_SEPARATE_MODULES", bc->use_separate_modules);
+ add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test);
+ add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point);
+ add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES);
+ add_global_bool_constant("ODIN_NO_RTTI", bc->no_rtti);
+
+ add_global_bool_constant("ODIN_VALGRIND_SUPPORT", bc->ODIN_VALGRIND_SUPPORT);
+ add_global_bool_constant("ODIN_TILDE", bc->tilde_backend);
add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp()));
- add_global_bool_constant("__ODIN_LLVM_F16_SUPPORTED", lb_use_new_pass_system());
+ {
+ bool f16_supported = lb_use_new_pass_system();
+ if (is_arch_wasm()) {
+ f16_supported = false;
+ } else if (build_context.metrics.os == TargetOs_darwin && build_context.metrics.arch == TargetArch_amd64) {
+ // NOTE(laytan): See #3222 for my ramblings on this.
+ f16_supported = false;
+ }
+ add_global_bool_constant("__ODIN_LLVM_F16_SUPPORTED", f16_supported);
+ }
{
GlobalEnumValue values[3] = {
@@ -1174,7 +1220,7 @@ gb_internal void init_universal(void) {
}
if (defined_values_double_declaration) {
- gb_exit(1);
+ exit_with_errors();
}
@@ -1188,9 +1234,9 @@ gb_internal void init_universal(void) {
// intrinsics types for objective-c stuff
{
- t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct());
- t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct());
- t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct());
+ t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct_complete());
+ t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct_complete());
+ t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct_complete());
t_objc_id = alloc_type_pointer(t_objc_object);
t_objc_SEL = alloc_type_pointer(t_objc_selector);
@@ -1231,6 +1277,9 @@ gb_internal void init_checker_info(CheckerInfo *i) {
mpsc_init(&i->required_global_variable_queue, a); // 1<<10);
mpsc_init(&i->required_foreign_imports_through_force_queue, a); // 1<<10);
mpsc_init(&i->intrinsics_entry_point_usage, a); // 1<<10); // just waste some memory here, even if it probably never used
+
+ string_map_init(&i->load_directory_cache);
+ map_init(&i->load_directory_map);
}
gb_internal void destroy_checker_info(CheckerInfo *i) {
@@ -1254,6 +1303,8 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
map_destroy(&i->objc_msgSend_types);
string_map_destroy(&i->load_file_cache);
+ string_map_destroy(&i->load_directory_cache);
+ map_destroy(&i->load_directory_map);
}
gb_internal CheckerContext make_checker_context(Checker *c) {
@@ -1327,6 +1378,7 @@ gb_internal void init_checker(Checker *c) {
array_init(&c->nested_proc_lits, heap_allocator(), 0, 1<<20);
mpsc_init(&c->global_untyped_queue, a); // , 1<<20);
+ mpsc_init(&c->soa_types_to_complete, a); // , 1<<20);
c->builtin_ctx = make_checker_context(c);
}
@@ -1339,6 +1391,7 @@ gb_internal void destroy_checker(Checker *c) {
array_free(&c->nested_proc_lits);
array_free(&c->procs_to_check);
mpsc_destroy(&c->global_untyped_queue);
+ mpsc_destroy(&c->soa_types_to_complete);
}
@@ -1638,6 +1691,26 @@ gb_internal bool add_entity_with_name(CheckerContext *c, Scope *scope, Ast *iden
}
return true;
}
+
+gb_internal bool add_entity_with_name(CheckerInfo *info, Scope *scope, Ast *identifier, Entity *entity, String name) {
+ if (scope == nullptr) {
+ return false;
+ }
+
+
+ if (!is_blank_ident(name)) {
+ Entity *ie = scope_insert(scope, entity);
+ if (ie != nullptr) {
+ return redeclaration_error(name, entity, ie);
+ }
+ }
+ if (identifier != nullptr) {
+ GB_ASSERT(entity->file != nullptr);
+ add_entity_definition(info, identifier, entity);
+ }
+ return true;
+}
+
gb_internal bool add_entity(CheckerContext *c, Scope *scope, Ast *identifier, Entity *entity) {
return add_entity_with_name(c, scope, identifier, entity, entity->token.string);
}
@@ -1962,6 +2035,8 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
break;
case Type_Struct:
+ if (bt->Struct.fields_wait_signal.futex.load() == 0)
+ return;
if (bt->Struct.scope != nullptr) {
for (auto const &entry : bt->Struct.scope->elements) {
Entity *e = entry.value;
@@ -1982,7 +2057,9 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->Struct.polymorphic_params);
for_array(i, bt->Struct.fields) {
Entity *f = bt->Struct.fields[i];
- add_type_info_type_internal(c, f->type);
+ if (f && f->type) {
+ add_type_info_type_internal(c, f->type);
+ }
}
add_comparison_procedures_for_fields(c, bt);
break;
@@ -2029,6 +2106,12 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->SoaPointer.elem);
break;
+ case Type_BitField:
+ add_type_info_type_internal(c, bt->BitField.backing_type);
+ for (Entity *f : bt->BitField.fields) {
+ add_type_info_type_internal(c, f->type);
+ }
+ break;
case Type_Generic:
break;
@@ -2278,6 +2361,13 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
add_min_dep_type_info(c, bt->SoaPointer.elem);
break;
+ case Type_BitField:
+ add_min_dep_type_info(c, bt->BitField.backing_type);
+ for (Entity *f : bt->BitField.fields) {
+ add_min_dep_type_info(c, f->type);
+ }
+ break;
+
default:
GB_PANIC("Unhandled type: %*.s", LIT(type_strings[bt->kind]));
break;
@@ -2345,6 +2435,43 @@ gb_internal void force_add_dependency_entity(Checker *c, Scope *scope, String co
add_dependency_to_set(c, e);
}
+gb_internal void collect_testing_procedures_of_package(Checker *c, AstPackage *pkg) {
+ AstPackage *testing_package = get_core_package(&c->info, str_lit("testing"));
+ Scope *testing_scope = testing_package->scope;
+ Entity *test_signature = scope_lookup_current(testing_scope, str_lit("Test_Signature"));
+
+ Scope *s = pkg->scope;
+ for (auto const &entry : s->elements) {
+ Entity *e = entry.value;
+ if (e->kind != Entity_Procedure) {
+ continue;
+ }
+
+ if ((e->flags & EntityFlag_Test) == 0) {
+ continue;
+ }
+
+ String name = e->token.string;
+
+ bool is_tester = true;
+
+ Type *t = base_type(e->type);
+ GB_ASSERT(t->kind == Type_Proc);
+ if (are_types_identical(t, base_type(test_signature->type))) {
+ // Good
+ } else {
+ gbString str = type_to_string(t);
+ error(e->token, "Testing procedures must have a signature type of proc(^testing.T), got %s", str);
+ gb_string_free(str);
+ is_tester = false;
+ }
+
+ if (is_tester) {
+ add_dependency_to_set(c, e);
+ array_add(&c->info.testing_procedures, e);
+ }
+ }
+}
gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *start) {
for_array(i, c->info.definitions) {
@@ -2448,41 +2575,13 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
}
}
-
- Entity *test_signature = scope_lookup_current(testing_scope, str_lit("Test_Signature"));
-
-
AstPackage *pkg = c->info.init_package;
- Scope *s = pkg->scope;
+ collect_testing_procedures_of_package(c, pkg);
- for (auto const &entry : s->elements) {
- Entity *e = entry.value;
- if (e->kind != Entity_Procedure) {
- continue;
- }
-
- if ((e->flags & EntityFlag_Test) == 0) {
- continue;
- }
-
- String name = e->token.string;
-
- bool is_tester = true;
-
- Type *t = base_type(e->type);
- GB_ASSERT(t->kind == Type_Proc);
- if (are_types_identical(t, base_type(test_signature->type))) {
- // Good
- } else {
- gbString str = type_to_string(t);
- error(e->token, "Testing procedures must have a signature type of proc(^testing.T), got %s", str);
- gb_string_free(str);
- is_tester = false;
- }
-
- if (is_tester) {
- add_dependency_to_set(c, e);
- array_add(&c->info.testing_procedures, e);
+ if (build_context.test_all_packages) {
+ for (auto const &entry : c->info.packages) {
+ AstPackage *pkg = entry.value;
+ collect_testing_procedures_of_package(c, pkg);
}
}
} else if (start != nullptr) {
@@ -2517,13 +2616,11 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
// Odin internal procedures
str_lit("__init_context"),
- str_lit("cstring_to_string"),
+ // str_lit("cstring_to_string"),
str_lit("_cleanup_runtime"),
// Pseudo-CRT required procedures
str_lit("memset"),
- str_lit("memcpy"),
- str_lit("memmove"),
// Utility procedures
str_lit("memory_equal"),
@@ -2531,22 +2628,28 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
str_lit("memory_compare_zero"),
);
- FORCE_ADD_RUNTIME_ENTITIES(!build_context.tilde_backend,
- // Extended data type internal procedures
- str_lit("umodti3"),
- str_lit("udivti3"),
- str_lit("modti3"),
- str_lit("divti3"),
- str_lit("fixdfti"),
- str_lit("fixunsdfti"),
- str_lit("fixunsdfdi"),
- str_lit("floattidf"),
- str_lit("floattidf_unsigned"),
- str_lit("truncsfhf2"),
- str_lit("truncdfhf2"),
- str_lit("gnu_h2f_ieee"),
- str_lit("gnu_f2h_ieee"),
- str_lit("extendhfsf2"),
+ // Only required if no CRT is present
+ FORCE_ADD_RUNTIME_ENTITIES(build_context.no_crt,
+ str_lit("memcpy"),
+ str_lit("memmove"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(is_arch_wasm() && !build_context.tilde_backend,
+ // // Extended data type internal procedures
+ // str_lit("umodti3"),
+ // str_lit("udivti3"),
+ // str_lit("modti3"),
+ // str_lit("divti3"),
+ // str_lit("fixdfti"),
+ // str_lit("fixunsdfti"),
+ // str_lit("fixunsdfdi"),
+ // str_lit("floattidf"),
+ // str_lit("floattidf_unsigned"),
+ // str_lit("truncsfhf2"),
+ // str_lit("truncdfhf2"),
+ // str_lit("gnu_h2f_ieee"),
+ // str_lit("gnu_f2h_ieee"),
+ // str_lit("extendhfsf2"),
// WASM Specific
str_lit("__ashlti3"),
@@ -2863,6 +2966,7 @@ gb_internal void init_core_type_info(Checker *c) {
t_type_info_relative_multi_pointer = find_core_type(c, str_lit("Type_Info_Relative_Multi_Pointer"));
t_type_info_matrix = find_core_type(c, str_lit("Type_Info_Matrix"));
t_type_info_soa_pointer = find_core_type(c, str_lit("Type_Info_Soa_Pointer"));
+ t_type_info_bit_field = find_core_type(c, str_lit("Type_Info_Bit_Field"));
t_type_info_named_ptr = alloc_type_pointer(t_type_info_named);
t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer);
@@ -2892,6 +2996,7 @@ gb_internal void init_core_type_info(Checker *c) {
t_type_info_relative_multi_pointer_ptr = alloc_type_pointer(t_type_info_relative_multi_pointer);
t_type_info_matrix_ptr = alloc_type_pointer(t_type_info_matrix);
t_type_info_soa_pointer_ptr = alloc_type_pointer(t_type_info_soa_pointer);
+ t_type_info_bit_field_ptr = alloc_type_pointer(t_type_info_bit_field);
}
gb_internal void init_mem_allocator(Checker *c) {
@@ -2919,6 +3024,16 @@ gb_internal void init_core_source_code_location(Checker *c) {
t_source_code_location_ptr = alloc_type_pointer(t_source_code_location);
}
+gb_internal void init_core_load_directory_file(Checker *c) {
+ if (t_load_directory_file != nullptr) {
+ return;
+ }
+ t_load_directory_file = find_core_type(c, str_lit("Load_Directory_File"));
+ t_load_directory_file_ptr = alloc_type_pointer(t_load_directory_file);
+ t_load_directory_file_slice = alloc_type_slice(t_load_directory_file);
+}
+
+
gb_internal void init_core_map_type(Checker *c) {
if (t_map_info != nullptr) {
return;
@@ -3107,6 +3222,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
linkage == "link_once") {
ac->linkage = linkage;
} else {
+ ERROR_BLOCK();
error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage));
error_line("\tinternal\n");
error_line("\tstrong\n");
@@ -3355,6 +3471,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
} else if (mode == "speed") {
ac->optimization_mode = ProcedureOptimizationMode_Speed;
} else {
+ ERROR_BLOCK();
error(elem, "Invalid optimization_mode for '%.*s'. Valid modes:", LIT(name));
error_line("\tnone\n");
error_line("\tminimal\n");
@@ -3485,6 +3602,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
model == "localexec") {
ac->thread_local_model = model;
} else {
+ ERROR_BLOCK();
error(elem, "Invalid thread local model '%.*s'. Valid models:", LIT(model));
error_line("\tdefault\n");
error_line("\tlocaldynamic\n");
@@ -3535,6 +3653,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
linkage == "link_once") {
ac->linkage = linkage;
} else {
+ ERROR_BLOCK();
error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage));
error_line("\tinternal\n");
error_line("\tstrong\n");
@@ -3589,6 +3708,15 @@ gb_internal DECL_ATTRIBUTE_PROC(const_decl_attribute) {
} else if (name == "private") {
// NOTE(bill): Handled elsewhere `check_collect_value_decl`
return true;
+ } else if (name == "static" ||
+ name == "thread_local" ||
+ name == "require" ||
+ name == "linkage" ||
+ name == "link_name" ||
+ name == "link_prefix" ||
+ false) {
+ error(elem, "@(%.*s) is not supported for compile time constant value declarations", LIT(name));
+ return true;
}
return false;
}
@@ -3689,6 +3817,7 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at
if (!proc(c, elem, name, value, ac)) {
if (!build_context.ignore_unknown_attributes) {
+ ERROR_BLOCK();
error(elem, "Unknown attribute element name '%.*s'", LIT(name));
error_line("\tDid you forget to use build flag '-ignore-unknown-attributes'?\n");
}
@@ -3758,6 +3887,8 @@ gb_internal bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_
gb_string_free(str);
return false;
} else if (is_global) {
+ ERROR_BLOCK();
+
Ast *n = vd->values[rhs-1];
error(n, "Expected %td expressions on the right hand side, got %td", lhs, rhs);
error_line("Note: Global declarations do not allow for multi-valued expressions");
@@ -3809,6 +3940,7 @@ gb_internal void check_builtin_attributes(CheckerContext *ctx, Entity *e, Array<
case Entity_ProcGroup:
case Entity_Procedure:
case Entity_TypeName:
+ case Entity_Constant:
// Okay
break;
default:
@@ -3976,6 +4108,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
Entity *e = alloc_entity_variable(c->scope, name->Ident.token, nullptr);
e->identifier = name;
e->file = c->file;
+ e->Variable.is_global = true;
if (entity_visibility_kind != EntityVisiblity_Public) {
e->flags |= EntityFlag_NotExported;
@@ -4340,6 +4473,10 @@ gb_internal void check_all_global_entities(Checker *c) {
DeclInfo *d = e->decl_info;
check_single_global_entity(c, e, d);
if (e->type != nullptr && is_type_typed(e->type)) {
+ for (Type *t = nullptr; mpsc_dequeue(&c->soa_types_to_complete, &t); /**/) {
+ complete_soa_type(c, t, false);
+ }
+
(void)type_size_of(e->type);
(void)type_align_of(e->type);
}
@@ -4430,7 +4567,7 @@ gb_internal void add_import_dependency_node(Checker *c, Ast *decl, PtrMap<AstPac
if (found == nullptr) {
Token token = ast_token(decl);
error(token, "Unable to find package: %.*s", LIT(path));
- gb_exit(1);
+ exit_with_errors();
}
AstPackage *pkg = *found;
GB_ASSERT(pkg->scope != nullptr);
@@ -4551,10 +4688,10 @@ gb_internal Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start
continue;
}
- if (pkg->kind == Package_Runtime) {
- // NOTE(bill): Allow cyclic imports within the runtime package for the time being
- continue;
- }
+ // if (pkg->kind == Package_Runtime) {
+ // // NOTE(bill): Allow cyclic imports within the runtime package for the time being
+ // continue;
+ // }
ImportPathItem item = {pkg, decl};
if (pkg == end) {
@@ -4732,6 +4869,22 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
return;
}
+ for (String const &path : fl->fullpaths) {
+ String ext = path_extension(path);
+ if (str_eq_ignore_case(ext, ".c") ||
+ str_eq_ignore_case(ext, ".cpp") ||
+ str_eq_ignore_case(ext, ".cxx") ||
+ str_eq_ignore_case(ext, ".h") ||
+ str_eq_ignore_case(ext, ".hpp") ||
+ str_eq_ignore_case(ext, ".hxx") ||
+ false
+ ) {
+ error(fl->token, "With 'foreign import', you cannot import a %.*s file directory, you must precompile the library and link against that", LIT(ext));
+ break;
+ }
+ }
+
+
// if (fl->collection_name != "system") {
// char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1);
// defer (gb_free(heap_allocator(), c_str));
@@ -4954,7 +5107,7 @@ gb_internal void check_create_file_scopes(Checker *c) {
for_array(i, c->parser->packages) {
AstPackage *pkg = c->parser->packages[i];
- gb_sort_array(pkg->files.data, pkg->files.count, sort_file_by_name);
+ array_sort(pkg->files, sort_file_by_name);
isize total_pkg_decl_count = 0;
for_array(j, pkg->files) {
@@ -5358,7 +5511,10 @@ gb_internal void check_procedure_later_from_entity(Checker *c, Entity *e, char c
return;
}
Type *type = base_type(e->type);
- GB_ASSERT(type->kind == Type_Proc);
+ if (type == t_invalid) {
+ return;
+ }
+ GB_ASSERT_MSG(type->kind == Type_Proc, "%s", type_to_string(e->type));
if (is_type_polymorphic(type) && !type->Proc.is_poly_specialized) {
return;
@@ -5583,7 +5739,7 @@ gb_internal void remove_neighbouring_duplicate_entires_from_sorted_array(Array<E
gb_internal void check_test_procedures(Checker *c) {
- gb_sort_array(c->info.testing_procedures.data, c->info.testing_procedures.count, init_procedures_cmp);
+ array_sort(c->info.testing_procedures, init_procedures_cmp);
remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.testing_procedures);
if (build_context.test_names.entries.count == 0) {
@@ -5961,11 +6117,14 @@ gb_internal void check_unique_package_names(Checker *c) {
continue;
}
+
+ begin_error_block();
error(curr, "Duplicate declaration of 'package %.*s'", LIT(name));
error_line("\tA package name must be unique\n"
"\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n"
"\tA package name is required for link name prefixing to have a consistent ABI\n");
- error(prev, "found at previous location");
+ error_line("%s found at previous location\n", token_pos_to_string(ast_token(prev).pos));
+ end_error_block();
}
}
@@ -5986,6 +6145,9 @@ gb_internal void check_add_definitions_from_queues(Checker *c) {
}
gb_internal void check_merge_queues_into_arrays(Checker *c) {
+ for (Type *t = nullptr; mpsc_dequeue(&c->soa_types_to_complete, &t); /**/) {
+ complete_soa_type(c, t, false);
+ }
check_add_entities_from_queues(c);
check_add_definitions_from_queues(c);
}
@@ -6032,8 +6194,8 @@ gb_internal GB_COMPARE_PROC(fini_procedures_cmp) {
}
gb_internal void check_sort_init_and_fini_procedures(Checker *c) {
- gb_sort_array(c->info.init_procedures.data, c->info.init_procedures.count, init_procedures_cmp);
- gb_sort_array(c->info.fini_procedures.data, c->info.fini_procedures.count, fini_procedures_cmp);
+ array_sort(c->info.init_procedures, init_procedures_cmp);
+ array_sort(c->info.fini_procedures, fini_procedures_cmp);
// NOTE(bill): remove possible duplicates from the init/fini lists
// NOTE(bill): because the arrays are sorted, you only need to check the previous element
@@ -6196,6 +6358,8 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("check bodies have all been checked");
check_unchecked_bodies(c);
+ TIME_SECTION("check #soa types");
+
check_merge_queues_into_arrays(c);
thread_pool_wait();
@@ -6230,6 +6394,8 @@ gb_internal void check_parsed_files(Checker *c) {
error(token, "Undefined entry point procedure 'main'");
}
+ } else if (build_context.build_mode == BuildMode_DynamicLibrary && build_context.no_entry_point) {
+ c->info.entry_point = nullptr;
}
thread_pool_wait();
diff --git a/src/checker.hpp b/src/checker.hpp
index 9da0f2950..1701da58d 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -340,6 +340,19 @@ struct LoadFileCache {
StringMap<u64> hashes;
};
+
+struct LoadDirectoryFile {
+ String file_name;
+ String data;
+};
+
+struct LoadDirectoryCache {
+ String path;
+ gbFileError file_error;
+ Array<LoadFileCache *> files;
+};
+
+
struct GenProcsData {
Array<Entity *> procs;
RwMutex mutex;
@@ -347,7 +360,7 @@ struct GenProcsData {
struct GenTypesData {
Array<Entity *> types;
- RwMutex mutex;
+ RecursiveMutex mutex;
};
// CheckerInfo stores all the symbol information for a type-checked program
@@ -387,8 +400,8 @@ struct CheckerInfo {
RecursiveMutex lazy_mutex; // Mutex required for lazy type checking of specific files
- RwMutex gen_types_mutex;
- PtrMap<Type *, GenTypesData > gen_types;
+ BlockingMutex gen_types_mutex;
+ PtrMap<Type *, GenTypesData *> gen_types;
BlockingMutex type_info_mutex; // NOT recursive
Array<Type *> type_info_types;
@@ -416,6 +429,11 @@ struct CheckerInfo {
BlockingMutex instrumentation_mutex;
Entity *instrumentation_enter_entity;
Entity *instrumentation_exit_entity;
+
+
+ BlockingMutex load_directory_mutex;
+ StringMap<LoadDirectoryCache *> load_directory_cache;
+ PtrMap<Ast *, LoadDirectoryCache *> load_directory_map; // Key: Ast_CallExpr *
};
struct CheckerContext {
@@ -433,6 +451,7 @@ struct CheckerContext {
u32 state_flags;
bool in_defer;
Type * type_hint;
+ Ast * type_hint_expr;
String proc_name;
DeclInfo * curr_proc_decl;
@@ -457,6 +476,7 @@ struct CheckerContext {
bool hide_polymorphic_errors;
bool in_polymorphic_specialization;
bool allow_arrow_right_selector_expr;
+ u8 bit_field_bit_size;
Scope * polymorphic_scope;
Ast *assignment_lhs_hint;
@@ -480,6 +500,7 @@ struct Checker {
MPSCQueue<UntypedExprInfo> global_untyped_queue;
+ MPSCQueue<Type *> soa_types_to_complete;
};
@@ -540,3 +561,6 @@ gb_internal void init_core_context(Checker *c);
gb_internal void init_mem_allocator(Checker *c);
gb_internal void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap *untyped);
+
+
+gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type); \ No newline at end of file
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index 3bab16293..c15ec7137 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -34,11 +34,6 @@ enum BuiltinProcId {
BuiltinProc_soa_zip,
BuiltinProc_soa_unzip,
-
- BuiltinProc_transpose,
- BuiltinProc_outer_product,
- BuiltinProc_hadamard_product,
- BuiltinProc_matrix_flatten,
BuiltinProc_unreachable,
@@ -48,6 +43,11 @@ enum BuiltinProcId {
// "Intrinsics"
BuiltinProc_is_package_imported,
+
+ BuiltinProc_transpose,
+ BuiltinProc_outer_product,
+ BuiltinProc_hadamard_product,
+ BuiltinProc_matrix_flatten,
BuiltinProc_soa_struct,
@@ -282,6 +282,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_field_index_of,
+ BuiltinProc_type_bit_set_backing_type,
+
BuiltinProc_type_equal_proc,
BuiltinProc_type_hasher_proc,
BuiltinProc_type_map_info,
@@ -341,11 +343,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("soa_zip"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("soa_unzip"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
-
- {STR_LIT("transpose"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
- {STR_LIT("outer_product"), 2, false, Expr_Expr, BuiltinProcPkg_builtin},
- {STR_LIT("hadamard_product"), 2, false, Expr_Expr, BuiltinProcPkg_builtin},
- {STR_LIT("matrix_flatten"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("unreachable"), 0, false, Expr_Expr, BuiltinProcPkg_builtin, /*diverging*/true},
@@ -356,6 +353,11 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
// "Intrinsics"
{STR_LIT("is_package_imported"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("transpose"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("outer_product"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("hadamard_product"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("matrix_flatten"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("soa_struct"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
@@ -586,6 +588,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_bit_set_backing_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
{STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_map_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
diff --git a/src/common.cpp b/src/common.cpp
index 90632def3..69426e2a6 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -353,6 +353,27 @@ gb_global bool global_module_path_set = false;
#include "thread_pool.cpp"
+gb_internal String obfuscate_string(String const &s, char const *prefix) {
+ if (s.len == 0) {
+ return s;
+ }
+ GB_ASSERT(prefix != nullptr);
+ u64 hash = gb_fnv64a(s.text, s.len);
+ gbString res = gb_string_make(permanent_allocator(), prefix);
+ res = gb_string_append_fmt(res, "x%llx", cast(long long unsigned)hash);
+ return make_string_c(res);
+}
+
+gb_internal i32 obfuscate_i32(i32 i) {
+ i32 x = cast(i32)gb_fnv64a(&i, sizeof(i));
+ if (x < 0) {
+ x = 1-x;
+ }
+ return cast(i32)x;
+}
+
+
+
struct StringIntern {
StringIntern *next;
isize len;
@@ -913,7 +934,7 @@ gb_internal void did_you_mean_append(DidYouMeanAnswers *d, String const &target)
array_add(&d->distances, dat);
}
gb_internal Slice<DistanceAndTarget> did_you_mean_results(DidYouMeanAnswers *d) {
- gb_sort_array(d->distances.data, d->distances.count, gb_isize_cmp(gb_offset_of(DistanceAndTarget, distance)));
+ array_sort(d->distances, gb_isize_cmp(gb_offset_of(DistanceAndTarget, distance)));
isize count = 0;
for (isize i = 0; i < d->distances.count; i++) {
isize distance = d->distances[i].distance;
diff --git a/src/docs.cpp b/src/docs.cpp
index f00d4e15a..004134a5c 100644
--- a/src/docs.cpp
+++ b/src/docs.cpp
@@ -237,7 +237,7 @@ gb_internal void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
}
array_add(&entities, e);
}
- gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
+ array_sort(entities, cmp_entities_for_printing);
bool show_docs = (build_context.cmd_doc_flags & CmdDocFlag_Short) == 0;
@@ -358,7 +358,7 @@ gb_internal void generate_documentation(Checker *c) {
}
}
- gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name);
+ array_sort(pkgs, cmp_ast_package_by_name);
for_array(i, pkgs) {
print_doc_package(info, pkgs[i]);
diff --git a/src/docs_format.cpp b/src/docs_format.cpp
index d0bca214b..ca6ecb5c2 100644
--- a/src/docs_format.cpp
+++ b/src/docs_format.cpp
@@ -14,8 +14,8 @@ struct OdinDocVersionType {
};
#define OdinDocVersionType_Major 0
-#define OdinDocVersionType_Minor 2
-#define OdinDocVersionType_Patch 4
+#define OdinDocVersionType_Minor 3
+#define OdinDocVersionType_Patch 1
struct OdinDocHeaderBase {
u8 magic[8];
@@ -84,6 +84,7 @@ enum OdinDocTypeKind : u32 {
OdinDocType_MultiPointer = 22,
OdinDocType_Matrix = 23,
OdinDocType_SoaPointer = 24,
+ OdinDocType_BitField = 25,
};
enum OdinDocTypeFlag_Basic : u32 {
@@ -162,13 +163,17 @@ enum OdinDocEntityFlag : u64 {
OdinDocEntityFlag_Foreign = 1ull<<0,
OdinDocEntityFlag_Export = 1ull<<1,
- OdinDocEntityFlag_Param_Using = 1ull<<2,
- OdinDocEntityFlag_Param_Const = 1ull<<3,
- OdinDocEntityFlag_Param_AutoCast = 1ull<<4,
- OdinDocEntityFlag_Param_Ellipsis = 1ull<<5,
- OdinDocEntityFlag_Param_CVararg = 1ull<<6,
- OdinDocEntityFlag_Param_NoAlias = 1ull<<7,
- OdinDocEntityFlag_Param_AnyInt = 1ull<<8,
+ OdinDocEntityFlag_Param_Using = 1ull<<2,
+ OdinDocEntityFlag_Param_Const = 1ull<<3,
+ OdinDocEntityFlag_Param_AutoCast = 1ull<<4,
+ OdinDocEntityFlag_Param_Ellipsis = 1ull<<5,
+ OdinDocEntityFlag_Param_CVararg = 1ull<<6,
+ OdinDocEntityFlag_Param_NoAlias = 1ull<<7,
+ OdinDocEntityFlag_Param_AnyInt = 1ull<<8,
+ OdinDocEntityFlag_Param_ByPtr = 1ull<<9,
+ OdinDocEntityFlag_Param_NoBroadcast = 1ull<<10,
+
+ OdinDocEntityFlag_BitField_Field = 1ull<<19,
OdinDocEntityFlag_Type_Alias = 1ull<<20,
@@ -192,7 +197,7 @@ struct OdinDocEntity {
u32 reserved_for_init;
OdinDocString comment; // line comment
OdinDocString docs; // preceding comment
- i32 field_group_index;
+ i32 field_group_index; // For `bit_field`s this is the "bit_size"
OdinDocEntityIndex foreign_library;
OdinDocString link_name;
OdinDocArray<OdinDocAttribute> attributes;
diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp
index 6816ae8eb..9ced78d33 100644
--- a/src/docs_writer.cpp
+++ b/src/docs_writer.cpp
@@ -615,6 +615,20 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
doc_type.types = odin_write_slice(w, types, gb_count_of(types));
}
break;
+ case Type_BitField:
+ doc_type.kind = OdinDocType_BitField;
+ {
+ auto fields = array_make<OdinDocEntityIndex>(heap_allocator(), type->BitField.fields.count);
+ defer (array_free(&fields));
+
+ for_array(i, type->BitField.fields) {
+ fields[i] = odin_doc_add_entity(w, type->BitField.fields[i]);
+ }
+ doc_type.entities = odin_write_slice(w, fields.data, fields.count);
+ doc_type.types = odin_doc_type_as_slice(w, type->BitField.backing_type);
+ }
+ break;
+
case Type_Struct:
doc_type.kind = OdinDocType_Struct;
if (type->Struct.soa_kind != StructSoa_None) {
@@ -863,6 +877,10 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e)
}
break;
case Entity_Variable:
+ if (e->flags & EntityFlag_BitFieldField) {
+ flags |= OdinDocEntityFlag_BitField_Field;
+ }
+
if (e->Variable.is_foreign) { flags |= OdinDocEntityFlag_Foreign; }
if (e->Variable.is_export) { flags |= OdinDocEntityFlag_Export; }
if (e->Variable.thread_local_model != "") {
@@ -873,7 +891,12 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e)
if (init_expr == nullptr) {
init_expr = e->Variable.init_expr;
}
- field_group_index = e->Variable.field_group_index;
+
+ if (e->flags & EntityFlag_BitFieldField) {
+ field_group_index = -cast(i32)e->Variable.bit_field_bit_size;
+ } else {
+ field_group_index = e->Variable.field_group_index;
+ }
break;
case Entity_Constant:
field_group_index = e->Constant.field_group_index;
@@ -902,11 +925,13 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e)
break;
}
- if (e->flags & EntityFlag_Using) { flags |= OdinDocEntityFlag_Param_Using; }
- if (e->flags & EntityFlag_ConstInput) { flags |= OdinDocEntityFlag_Param_Const; }
- if (e->flags & EntityFlag_Ellipsis) { flags |= OdinDocEntityFlag_Param_Ellipsis; }
- if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
- if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
+ if (e->flags & EntityFlag_Using) { flags |= OdinDocEntityFlag_Param_Using; }
+ if (e->flags & EntityFlag_ConstInput) { flags |= OdinDocEntityFlag_Param_Const; }
+ if (e->flags & EntityFlag_Ellipsis) { flags |= OdinDocEntityFlag_Param_Ellipsis; }
+ if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
+ if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
+ if (e->flags & EntityFlag_ByPtr) { flags |= OdinDocEntityFlag_Param_ByPtr; }
+ if (e->flags & EntityFlag_NoBroadcast) { flags |= OdinDocEntityFlag_Param_NoBroadcast; }
if (e->scope && (e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) && !is_entity_exported(e)) {
flags |= OdinDocEntityFlag_Private;
@@ -1084,7 +1109,7 @@ gb_internal void odin_doc_write_docs(OdinDocWriter *w) {
}
debugf("odin_doc_update_entities sort pkgs %s\n", w->state ? "preparing" : "writing");
- gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name);
+ array_sort(pkgs, cmp_ast_package_by_name);
for_array(i, pkgs) {
gbAllocator allocator = heap_allocator();
@@ -1147,7 +1172,7 @@ gb_internal void odin_doc_write_to_file(OdinDocWriter *w, char const *filename)
gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, filename);
if (err != gbFileError_None) {
gb_printf_err("Failed to write .odin-doc to: %s\n", filename);
- gb_exit(1);
+ exit_with_errors();
return;
}
defer (gb_file_close(&f));
diff --git a/src/entity.cpp b/src/entity.cpp
index e6c46d37e..6cea0930f 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -43,6 +43,7 @@ enum EntityFlag : u64 {
EntityFlag_NoAlias = 1ull<<9,
EntityFlag_TypeField = 1ull<<10,
EntityFlag_Value = 1ull<<11,
+ EntityFlag_BitFieldField = 1ull<<12,
@@ -59,7 +60,7 @@ enum EntityFlag : u64 {
EntityFlag_ProcBodyChecked = 1ull<<21,
EntityFlag_CVarArg = 1ull<<22,
-
+ EntityFlag_NoBroadcast = 1ull<<23,
EntityFlag_AnyInt = 1ull<<24,
EntityFlag_Disabled = 1ull<<25,
@@ -212,6 +213,7 @@ struct Entity {
Ast *init_expr; // only used for some variables within procedure bodies
i32 field_index;
i32 field_group_index;
+ u8 bit_field_bit_size;
ParameterValue param_value;
@@ -227,6 +229,7 @@ struct Entity {
CommentGroup *comment;
bool is_foreign;
bool is_export;
+ bool is_global;
} Variable;
struct {
Type * type_parameter_specialization;
@@ -478,3 +481,25 @@ gb_internal Entity *strip_entity_wrapping(Ast *expr) {
Entity *e = entity_from_expr(expr);
return strip_entity_wrapping(e);
}
+
+
+gb_internal bool is_entity_local_variable(Entity *e) {
+ if (e == nullptr) {
+ return false;
+ }
+ if (e->kind != Entity_Variable) {
+ return false;
+ }
+ if (e->Variable.is_global) {
+ return false;
+ }
+ if (e->scope == nullptr) {
+ return true;
+ }
+ if (e->flags & (EntityFlag_ForValue|EntityFlag_SwitchValue)) {
+ return false;
+ }
+
+ return ((e->scope->flags &~ ScopeFlag_ContextDefined) == 0) ||
+ (e->scope->flags & ScopeFlag_Proc) != 0;
+} \ No newline at end of file
diff --git a/src/error.cpp b/src/error.cpp
index e63682829..eb167d4c3 100644
--- a/src/error.cpp
+++ b/src/error.cpp
@@ -1,28 +1,74 @@
+enum ErrorValueKind : u32 {
+ ErrorValue_Error,
+ ErrorValue_Warning,
+};
+
+struct ErrorValue {
+ ErrorValueKind kind;
+ TokenPos pos;
+ TokenPos end;
+ Array<String> msgs;
+};
+
struct ErrorCollector {
TokenPos prev;
std::atomic<i64> count;
std::atomic<i64> warning_count;
std::atomic<bool> in_block;
- BlockingMutex mutex;
- BlockingMutex error_out_mutex;
- BlockingMutex string_mutex;
- RecursiveMutex block_mutex;
-
- RecursiveMutex error_buffer_mutex;
- Array<u8> error_buffer;
- Array<String> errors;
+
+ RecursiveMutex mutex;
+ BlockingMutex path_mutex;
+
+ Array<ErrorValue> error_values;
+ ErrorValue curr_error_value;
+ std::atomic<bool> curr_error_value_set;
};
gb_global ErrorCollector global_error_collector;
+gb_internal void push_error_value(TokenPos const &pos, ErrorValueKind kind = ErrorValue_Error) {
+ GB_ASSERT_MSG(global_error_collector.curr_error_value_set.load() == false, "Possible race condition in error handling system, please report this with an issue");
+ ErrorValue ev = {kind, pos};
+ ev.msgs.allocator = heap_allocator();
+
+ global_error_collector.curr_error_value = ev;
+ global_error_collector.curr_error_value_set.store(true);
+}
+
+gb_internal void pop_error_value(void) {
+ if (global_error_collector.curr_error_value_set.load()) {
+ array_add(&global_error_collector.error_values, global_error_collector.curr_error_value);
+
+ global_error_collector.curr_error_value = {};
+ global_error_collector.curr_error_value_set.store(false);
+ }
+}
+
+
+gb_internal void try_pop_error_value(void) {
+ if (!global_error_collector.in_block.load()) {
+ pop_error_value();
+ }
+}
+
+gb_internal ErrorValue *get_error_value(void) {
+ GB_ASSERT_MSG(global_error_collector.curr_error_value_set.load() == true, "Possible race condition in error handling system, please report this with an issue");
+ return &global_error_collector.curr_error_value;
+}
+
+
+
gb_internal bool any_errors(void) {
return global_error_collector.count.load() != 0;
}
+gb_internal bool any_warnings(void) {
+ return global_error_collector.warning_count.load() != 0;
+}
+
gb_internal void init_global_error_collector(void) {
- array_init(&global_error_collector.errors, heap_allocator());
- array_init(&global_error_collector.error_buffer, heap_allocator());
+ array_init(&global_error_collector.error_values, heap_allocator());
array_init(&global_file_path_strings, heap_allocator(), 1, 4096);
array_init(&global_files, heap_allocator(), 1, 4096);
}
@@ -37,7 +83,7 @@ gb_internal char *token_pos_to_string(TokenPos const &pos);
gb_internal bool set_file_path_string(i32 index, String const &path) {
bool ok = false;
GB_ASSERT(index >= 0);
- mutex_lock(&global_error_collector.string_mutex);
+ mutex_lock(&global_error_collector.path_mutex);
if (index >= global_file_path_strings.count) {
array_resize(&global_file_path_strings, index+1);
@@ -48,14 +94,14 @@ gb_internal bool set_file_path_string(i32 index, String const &path) {
ok = true;
}
- mutex_unlock(&global_error_collector.string_mutex);
+ mutex_unlock(&global_error_collector.path_mutex);
return ok;
}
gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
bool ok = false;
GB_ASSERT(index >= 0);
- mutex_lock(&global_error_collector.string_mutex);
+ mutex_lock(&global_error_collector.path_mutex);
if (index >= global_files.count) {
array_resize(&global_files, index+1);
@@ -66,33 +112,33 @@ gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
ok = true;
}
- mutex_unlock(&global_error_collector.string_mutex);
+ mutex_unlock(&global_error_collector.path_mutex);
return ok;
}
gb_internal String get_file_path_string(i32 index) {
GB_ASSERT(index >= 0);
- mutex_lock(&global_error_collector.string_mutex);
+ mutex_lock(&global_error_collector.path_mutex);
String path = {};
if (index < global_file_path_strings.count) {
path = global_file_path_strings[index];
}
- mutex_unlock(&global_error_collector.string_mutex);
+ mutex_unlock(&global_error_collector.path_mutex);
return path;
}
gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) {
GB_ASSERT(index >= 0);
- mutex_lock(&global_error_collector.string_mutex);
+ mutex_lock(&global_error_collector.path_mutex);
AstFile *file = nullptr;
if (index < global_files.count) {
file = global_files[index];
}
- mutex_unlock(&global_error_collector.string_mutex);
+ mutex_unlock(&global_error_collector.path_mutex);
return file;
}
@@ -102,6 +148,8 @@ gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) {
gb_internal bool global_warnings_as_errors(void);
gb_internal bool global_ignore_warnings(void);
gb_internal bool show_error_line(void);
+gb_internal bool terse_errors(void);
+gb_internal bool json_errors(void);
gb_internal bool has_ansi_terminal_colours(void);
gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset);
@@ -113,97 +161,41 @@ gb_internal void syntax_error(Token const &token, char const *fmt, ...);
gb_internal void syntax_error(TokenPos pos, char const *fmt, ...);
gb_internal void syntax_warning(Token const &token, char const *fmt, ...);
gb_internal void compiler_error(char const *fmt, ...);
-
-gb_internal void begin_error_block(void) {
- mutex_lock(&global_error_collector.block_mutex);
- global_error_collector.in_block.store(true);
-}
-
-gb_internal void end_error_block(void) {
- mutex_lock(&global_error_collector.error_buffer_mutex);
- isize n = global_error_collector.error_buffer.count;
- if (n > 0) {
- u8 *text = global_error_collector.error_buffer.data;
-
- bool add_extra_newline = false;
-
- if (show_error_line()) {
- if (n >= 2 && !(text[n-2] == '\n' && text[n-1] == '\n')) {
- add_extra_newline = true;
- }
- } else {
- isize newline_count = 0;
- for (isize i = 0; i < n; i++) {
- if (text[i] == '\n') {
- newline_count += 1;
- }
- }
- if (newline_count > 1) {
- add_extra_newline = true;
- }
- }
-
- if (add_extra_newline) {
- // add an extra new line as padding when the error line is being shown
- error_line("\n");
- }
-
- n = global_error_collector.error_buffer.count;
- text = gb_alloc_array(permanent_allocator(), u8, n+1);
- gb_memmove(text, global_error_collector.error_buffer.data, n);
- text[n] = 0;
-
-
- mutex_lock(&global_error_collector.error_out_mutex);
- String s = {text, n};
- array_add(&global_error_collector.errors, s);
- mutex_unlock(&global_error_collector.error_out_mutex);
-
- global_error_collector.error_buffer.count = 0;
- }
- mutex_unlock(&global_error_collector.error_buffer_mutex);
- global_error_collector.in_block.store(false);
- mutex_unlock(&global_error_collector.block_mutex);
-}
-
-#define ERROR_BLOCK() begin_error_block(); defer (end_error_block())
+gb_internal void print_all_errors(void);
#define ERROR_OUT_PROC(name) void name(char const *fmt, va_list va)
typedef ERROR_OUT_PROC(ErrorOutProc);
gb_internal ERROR_OUT_PROC(default_error_out_va) {
- gbFile *f = gb_file_get_standard(gbFileStandard_Error);
-
char buf[4096] = {};
isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va);
isize n = len-1;
- if (global_error_collector.in_block) {
- mutex_lock(&global_error_collector.error_buffer_mutex);
-
- isize cap = global_error_collector.error_buffer.count + n;
- array_reserve(&global_error_collector.error_buffer, cap);
- u8 *data = global_error_collector.error_buffer.data + global_error_collector.error_buffer.count;
- gb_memmove(data, buf, n);
- global_error_collector.error_buffer.count += n;
-
- mutex_unlock(&global_error_collector.error_buffer_mutex);
- } else {
- mutex_lock(&global_error_collector.error_out_mutex);
- {
- u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1);
- gb_memmove(text, buf, n);
- text[n] = 0;
- array_add(&global_error_collector.errors, make_string(text, n));
- }
- mutex_unlock(&global_error_collector.error_out_mutex);
+ if (n > 0) {
+ String msg = copy_string(permanent_allocator(), {(u8 *)buf, n});
+ ErrorValue *ev = get_error_value();
+ array_add(&ev->msgs, msg);
}
- gb_file_write(f, buf, n);
}
gb_global ErrorOutProc *error_out_va = default_error_out_va;
+gb_internal void begin_error_block(void) {
+ mutex_lock(&global_error_collector.mutex);
+ global_error_collector.in_block.store(true);
+}
+
+gb_internal void end_error_block(void) {
+ pop_error_value();
+ global_error_collector.in_block.store(false);
+ mutex_unlock(&global_error_collector.mutex);
+}
+
+#define ERROR_BLOCK() begin_error_block(); defer (end_error_block())
+
+
+
gb_internal void error_out(char const *fmt, ...) {
va_list va;
va_start(va, fmt);
@@ -256,6 +248,7 @@ gb_internal void terminal_reset_colours(void) {
gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) {
+ get_error_value()->end = end;
if (!show_error_line()) {
return false;
}
@@ -340,6 +333,9 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) {
return false;
}
+gb_internal void error_out_empty(void) {
+ error_out("");
+}
gb_internal void error_out_pos(TokenPos pos) {
terminal_set_colours(TerminalStyle_Bold, TerminalColour_White);
error_out("%s ", token_pos_to_string(pos));
@@ -357,11 +353,15 @@ gb_internal void error_out_coloured(char const *str, TerminalStyle style, Termin
gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
global_error_collector.count.fetch_add(1);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) {
+ print_all_errors();
gb_exit(1);
}
mutex_lock(&global_error_collector.mutex);
+
+ push_error_value(pos, ErrorValue_Error);
// NOTE(bill): Duplicate error, skip it
if (pos.line == 0) {
+ error_out_empty();
error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
error_out_va(fmt, va);
error_out("\n");
@@ -377,6 +377,7 @@ gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va
} else {
global_error_collector.count.fetch_sub(1);
}
+ try_pop_error_value();
mutex_unlock(&global_error_collector.mutex);
}
@@ -387,9 +388,13 @@ gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt,
}
global_error_collector.warning_count.fetch_add(1);
mutex_lock(&global_error_collector.mutex);
+
+ push_error_value(pos, ErrorValue_Warning);
+
if (!global_ignore_warnings()) {
// NOTE(bill): Duplicate error, skip it
if (pos.line == 0) {
+ error_out_empty();
error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
error_out_va(fmt, va);
error_out("\n");
@@ -402,6 +407,7 @@ gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt,
show_error_on_line(pos, end);
}
}
+ try_pop_error_value();
mutex_unlock(&global_error_collector.mutex);
}
@@ -413,11 +419,16 @@ gb_internal void error_line_va(char const *fmt, va_list va) {
gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
global_error_collector.count.fetch_add(1);
if (global_error_collector.count.load() > MAX_ERROR_COLLECTOR_COUNT()) {
+ print_all_errors();
gb_exit(1);
}
mutex_lock(&global_error_collector.mutex);
+
+ push_error_value(pos, ErrorValue_Error);
+
// NOTE(bill): Duplicate error, skip it
if (pos.line == 0) {
+ error_out_empty();
error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
error_out_va(fmt, va);
} else if (global_error_collector.prev != pos) {
@@ -428,6 +439,8 @@ gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_li
}
error_out_va(fmt, va);
}
+
+ try_pop_error_value();
mutex_unlock(&global_error_collector.mutex);
}
@@ -435,9 +448,13 @@ gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_li
gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
global_error_collector.count.fetch_add(1);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) {
+ print_all_errors();
gb_exit(1);
}
mutex_lock(&global_error_collector.mutex);
+
+ push_error_value(pos, ErrorValue_Warning);
+
// NOTE(bill): Duplicate error, skip it
if (global_error_collector.prev != pos) {
global_error_collector.prev = pos;
@@ -445,23 +462,31 @@ gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const *
error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red);
error_out_va(fmt, va);
error_out("\n");
- // show_error_on_line(pos, end);
+ show_error_on_line(pos, end);
} else if (pos.line == 0) {
+ error_out_empty();
error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red);
error_out_va(fmt, va);
error_out("\n");
}
+
+ try_pop_error_value();
mutex_unlock(&global_error_collector.mutex);
}
gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
global_error_collector.count.fetch_add(1);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) {
+ print_all_errors();
gb_exit(1);
}
mutex_lock(&global_error_collector.mutex);
+
+ push_error_value(pos, ErrorValue_Warning);
+
// NOTE(bill): Duplicate error, skip it
if (pos.line == 0) {
+ error_out_empty();
error_out_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red);
error_out_va(fmt, va);
error_out("\n");
@@ -475,6 +500,8 @@ gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end,
error_out("\n");
show_error_on_line(pos, end);
}
+
+ try_pop_error_value();
mutex_unlock(&global_error_collector.mutex);
}
@@ -486,6 +513,10 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const
}
mutex_lock(&global_error_collector.mutex);
global_error_collector.warning_count++;
+
+
+ push_error_value(pos, ErrorValue_Warning);
+
if (!global_ignore_warnings()) {
// NOTE(bill): Duplicate error, skip it
if (global_error_collector.prev != pos) {
@@ -496,11 +527,14 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const
error_out("\n");
// show_error_on_line(pos, end);
} else if (pos.line == 0) {
+ error_out_empty();
error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
error_out_va(fmt, va);
error_out("\n");
}
}
+
+ try_pop_error_value();
mutex_unlock(&global_error_collector.mutex);
}
@@ -568,6 +602,10 @@ gb_internal void syntax_error_with_verbose(TokenPos pos, TokenPos end, char cons
gb_internal void compiler_error(char const *fmt, ...) {
+ if (any_errors() || any_warnings()) {
+ print_all_errors();
+ }
+
va_list va;
va_start(va, fmt);
@@ -577,3 +615,127 @@ gb_internal void compiler_error(char const *fmt, ...) {
GB_DEBUG_TRAP();
gb_exit(1);
}
+
+
+gb_internal void exit_with_errors(void) {
+ if (any_errors() || any_warnings()) {
+ print_all_errors();
+ }
+ gb_exit(1);
+}
+
+
+
+gb_internal int error_value_cmp(void const *a, void const *b) {
+ ErrorValue *x = cast(ErrorValue *)a;
+ ErrorValue *y = cast(ErrorValue *)b;
+ return token_pos_cmp(x->pos, y->pos);
+}
+
+gb_internal void print_all_errors(void) {
+ auto const &escape_char = [](gbFile *f, u8 c) {
+ switch (c) {
+ case '\n': gb_file_write(f, "\\n", 2); break;
+ case '"': gb_file_write(f, "\\\"", 2); break;
+ case '\\': gb_file_write(f, "\\\\", 2); break;
+ case '\b': gb_file_write(f, "\\b", 2); break;
+ case '\f': gb_file_write(f, "\\f", 2); break;
+ case '\r': gb_file_write(f, "\\r", 2); break;
+ case '\t': gb_file_write(f, "\\t", 2); break;
+ default:
+ if ('\x00' <= c && c <= '\x1f') {
+ gb_fprintf(f, "\\u%04x", c);
+ } else {
+ gb_file_write(f, &c, 1);
+ }
+ break;
+ }
+ };
+
+ GB_ASSERT(any_errors() || any_warnings());
+ gbFile *f = gb_file_get_standard(gbFileStandard_Error);
+
+ array_sort(global_error_collector.error_values, error_value_cmp);
+
+
+ if (json_errors()) {
+ gb_fprintf(f, "{\n");
+ gb_fprintf(f, "\t\"error_count\": %td,\n", global_error_collector.error_values.count);
+ gb_fprintf(f, "\t\"errors\": [\n");
+ for_array(i, global_error_collector.error_values) {
+ ErrorValue ev = global_error_collector.error_values[i];
+
+ gb_fprintf(f, "\t\t{\n");
+
+ gb_fprintf(f, "\t\t\t\"type\": \"");
+ if (ev.kind == ErrorValue_Warning) {
+ gb_fprintf(f, "warning");
+ } else {
+ gb_fprintf(f, "error");
+ }
+ gb_fprintf(f, "\",\n");
+
+ gb_fprintf(f, "\t\t\t\"pos\": {\n");
+
+ if (ev.pos.file_id) {
+ gb_fprintf(f, "\t\t\t\t\"file\": \"");
+ String file = get_file_path_string(ev.pos.file_id);
+ for (isize k = 0; k < file.len; k++) {
+ escape_char(f, file.text[k]);
+ }
+ gb_fprintf(f, "\",\n");
+ gb_fprintf(f, "\t\t\t\t\"offset\": %d,\n", ev.pos.offset);
+ gb_fprintf(f, "\t\t\t\t\"line\": %d,\n", ev.pos.line);
+ gb_fprintf(f, "\t\t\t\t\"column\": %d,\n", ev.pos.column);
+ i32 end_column = gb_max(ev.end.column, ev.pos.column);
+ gb_fprintf(f, "\t\t\t\t\"end_column\": %d\n", end_column);
+ gb_fprintf(f, "\t\t\t},\n");
+ }
+
+ gb_fprintf(f, "\t\t\t\"msgs\": [\n");
+
+ if (ev.msgs.count > 1) {
+ gb_fprintf(f, "\t\t\t\t\"");
+
+ for (isize j = 1; j < ev.msgs.count; j++) {
+ String msg = ev.msgs[j];
+ for (isize k = 0; k < msg.len; k++) {
+ u8 c = msg.text[k];
+ if (c == '\n') {
+ if (k+1 == msg.len && j+1 == ev.msgs.count) {
+ // don't do the last one
+ } else {
+ gb_fprintf(f, "\",\n");
+ gb_fprintf(f, "\t\t\t\t\"");
+ }
+ } else {
+ escape_char(f, c);
+ }
+ }
+ }
+ gb_fprintf(f, "\"\n");
+ }
+ gb_fprintf(f, "\t\t\t]\n");
+ gb_fprintf(f, "\t\t}");
+ if (i+1 != global_error_collector.error_values.count) {
+ gb_fprintf(f, ",");
+ }
+ gb_fprintf(f, "\n");
+ }
+
+ gb_fprintf(f, "\t]\n");
+ gb_fprintf(f, "}\n");
+ } else {
+ for_array(i, global_error_collector.error_values) {
+ ErrorValue ev = global_error_collector.error_values[i];
+ for (isize j = 0; j < ev.msgs.count; j++) {
+ String msg = ev.msgs[j];
+ gb_file_write(f, msg.text, msg.len);
+
+ if (terse_errors() && string_contains_char(msg, '\n')) {
+ break;
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/gb/gb.h b/src/gb/gb.h
index 93d250f21..868e11a16 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -83,6 +83,10 @@ extern "C" {
#ifndef GB_SYSTEM_OPENBSD
#define GB_SYSTEM_OPENBSD 1
#endif
+ #elif defined(__HAIKU__) || defined(__haiku__)
+ #ifndef GB_SYSTEM_HAIKU
+ #define GB_SYSTEM_HAIKU 1
+ #endif
#else
#error This UNIX operating system is not supported
#endif
@@ -140,8 +144,6 @@ extern "C" {
#error Unknown CPU Type
#endif
-
-
#ifndef GB_STATIC_ASSERT
#define GB_STATIC_ASSERT3(cond, msg) typedef char static_assertion_##msg[(!!(cond))*2-1]
// NOTE(bill): Token pasting madness!!
@@ -206,7 +208,7 @@ extern "C" {
#endif
#include <stdlib.h> // NOTE(bill): malloc on linux
#include <sys/mman.h>
- #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
+ #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__HAIKU__)
#include <sys/sendfile.h>
#endif
#include <sys/stat.h>
@@ -247,6 +249,13 @@ extern "C" {
#include <pthread_np.h>
#define lseek64 lseek
#endif
+
+#if defined(GB_SYSTEM_HAIKU)
+ #include <stdio.h>
+ #include <pthread.h>
+ #include <kernel/OS.h>
+ #define lseek64 lseek
+#endif
#if defined(GB_SYSTEM_UNIX)
#include <semaphore.h>
@@ -469,6 +478,13 @@ typedef i32 b32; // NOTE(bill): Prefer this!!!
#endif
#endif
+#if !defined(gb_no_asan)
+ #if defined(_MSC_VER)
+ #define gb_no_asan __declspec(no_sanitize_address)
+ #else
+ #define gb_no_asan __attribute__((disable_sanitizer_instrumentation))
+ #endif
+#endif
// NOTE(bill): Easy to grep
// NOTE(bill): Not needed in macros
@@ -801,6 +817,13 @@ typedef struct gbAffinity {
isize thread_count;
isize threads_per_core;
} gbAffinity;
+#elif defined(GB_SYSTEM_HAIKU)
+typedef struct gbAffinity {
+ b32 is_accurate;
+ isize core_count;
+ isize thread_count;
+ isize threads_per_core;
+} gbAffinity;
#else
#error TODO(bill): Unknown system
#endif
@@ -2984,6 +3007,8 @@ gb_inline u32 gb_thread_current_id(void) {
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
#elif defined(GB_SYSTEM_LINUX)
thread_id = gettid();
+#elif defined(GB_SYSTEM_HAIKU)
+ thread_id = find_thread(NULL);
#else
#error Unsupported architecture for gb_thread_current_id()
#endif
@@ -3184,7 +3209,9 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
//info.affinity_tag = cast(integer_t)index;
//result = thread_policy_set(thread, THREAD_AFFINITY_POLICY, cast(thread_policy_t)&info, THREAD_AFFINITY_POLICY_COUNT);
+#if !defined(GB_SYSTEM_HAIKU)
result = pthread_setaffinity_np(thread, sizeof(cpuset_t), &mn);
+#endif
return result == 0;
}
@@ -3240,6 +3267,29 @@ isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
GB_ASSERT(0 <= core && core < a->core_count);
return a->threads_per_core;
}
+#elif defined(GB_SYSTEM_HAIKU)
+#include <unistd.h>
+
+void gb_affinity_init(gbAffinity *a) {
+ a->core_count = sysconf(_SC_NPROCESSORS_ONLN);
+ a->threads_per_core = 1;
+ a->is_accurate = a->core_count > 0;
+ a->core_count = a->is_accurate ? a->core_count : 1;
+ a->thread_count = a->core_count;
+}
+
+void gb_affinity_destroy(gbAffinity *a) {
+ gb_unused(a);
+}
+
+b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
+ return true;
+}
+
+isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
+ GB_ASSERT(0 <= core && core < a->core_count);
+ return a->threads_per_core;
+}
#else
#error TODO(bill): Unknown system
#endif
@@ -3528,7 +3578,7 @@ gb_inline void gb_str_to_upper(char *str) {
}
-gb_inline isize gb_strlen(char const *str) {
+gb_no_asan isize gb_strlen(char const *str) {
char const *begin = str;
isize const *w;
if (str == NULL) {
@@ -5457,7 +5507,7 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena
}
}
- gb_free(buf);
+ gb_mfree(buf);
close(new_fd);
close(existing_fd);
@@ -5634,7 +5684,7 @@ char *gb_path_get_full_name(gbAllocator a, char const *path) {
isize path_len = gb_strlen(path);
isize cwd_len = gb_strlen(cwd);
len = cwd_len + 1 + path_len + 1;
- result = gb_alloc_array(a, char, len);
+ result = gb_alloc_array(a, char, len+1);
gb_memmove(result, (void *)cwd, cwd_len);
result[cwd_len] = '/';
diff --git a/src/linker.cpp b/src/linker.cpp
index c0952d0e0..498a96c5f 100644
--- a/src/linker.cpp
+++ b/src/linker.cpp
@@ -139,9 +139,11 @@ gb_internal i32 linker_stage(LinkerData *gen) {
}
- StringSet libs = {};
- string_set_init(&libs, 64);
- defer (string_set_destroy(&libs));
+ StringSet min_libs_set = {};
+ string_set_init(&min_libs_set, 64);
+ defer (string_set_destroy(&min_libs_set));
+
+ String prev_lib = {};
StringSet asm_files = {};
string_set_init(&asm_files, 64);
@@ -149,6 +151,11 @@ gb_internal i32 linker_stage(LinkerData *gen) {
for (Entity *e : gen->foreign_libraries) {
GB_ASSERT(e->kind == Entity_LibraryName);
+ // NOTE(bill): Add these before the linking values
+ String extra_linker_flags = string_trim_whitespace(e->LibraryName.extra_linker_flags);
+ if (extra_linker_flags.len != 0) {
+ lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags));
+ }
for_array(i, e->LibraryName.paths) {
String lib = string_trim_whitespace(e->LibraryName.paths[i]);
// IMPORTANT NOTE(bill): calling `string_to_lower` here is not an issue because
@@ -162,12 +169,11 @@ gb_internal i32 linker_stage(LinkerData *gen) {
if (!string_set_update(&asm_files, lib)) {
String asm_file = asm_files.entries[i].value;
String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
- String obj_format;
-#if defined(GB_ARCH_64_BIT)
- obj_format = str_lit("win64");
-#elif defined(GB_ARCH_32_BIT)
+ String obj_format = str_lit("win64");
+ #if defined(GB_ARCH_32_BIT)
obj_format = str_lit("win32");
-#endif // GB_ARCH_*_BIT
+ #endif
+
result = system_exec_command_line_app("nasm",
"\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" "
"-f \"%.*s\" "
@@ -185,21 +191,16 @@ gb_internal i32 linker_stage(LinkerData *gen) {
}
array_add(&gen->output_object_paths, obj_file);
}
- } else {
- if (!string_set_update(&libs, lib)) {
+ } else if (!string_set_update(&min_libs_set, lib) ||
+ !build_context.min_link_libs) {
+ if (prev_lib != lib) {
lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
}
+ prev_lib = lib;
}
}
}
- for (Entity *e : gen->foreign_libraries) {
- GB_ASSERT(e->kind == Entity_LibraryName);
- if (e->LibraryName.extra_linker_flags.len != 0) {
- lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags));
- }
- }
-
if (build_context.build_mode == BuildMode_DynamicLibrary) {
link_settings = gb_string_append_fmt(link_settings, " /DLL");
} else {
@@ -318,12 +319,19 @@ gb_internal i32 linker_stage(LinkerData *gen) {
string_set_init(&asm_files, 64);
defer (string_set_destroy(&asm_files));
- StringSet libs = {};
- string_set_init(&libs, 64);
- defer (string_set_destroy(&libs));
+ StringSet min_libs_set = {};
+ string_set_init(&min_libs_set, 64);
+ defer (string_set_destroy(&min_libs_set));
+
+ String prev_lib = {};
for (Entity *e : gen->foreign_libraries) {
GB_ASSERT(e->kind == Entity_LibraryName);
+ // NOTE(bill): Add these before the linking values
+ String extra_linker_flags = string_trim_whitespace(e->LibraryName.extra_linker_flags);
+ if (extra_linker_flags.len != 0) {
+ lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags));
+ }
for (String lib : e->LibraryName.paths) {
lib = string_trim_whitespace(lib);
if (lib.len == 0) {
@@ -336,19 +344,19 @@ gb_internal i32 linker_stage(LinkerData *gen) {
String asm_file = lib;
String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".o"));
String obj_format;
-#if defined(GB_ARCH_64_BIT)
+ #if defined(GB_ARCH_64_BIT)
if (is_osx) {
obj_format = str_lit("macho64");
} else {
obj_format = str_lit("elf64");
}
-#elif defined(GB_ARCH_32_BIT)
+ #elif defined(GB_ARCH_32_BIT)
if (is_osx) {
obj_format = str_lit("macho32");
} else {
obj_format = str_lit("elf32");
}
-#endif // GB_ARCH_*_BIT
+ #endif // GB_ARCH_*_BIT
if (is_osx) {
// `as` comes with MacOS.
@@ -376,16 +384,29 @@ gb_internal i32 linker_stage(LinkerData *gen) {
LIT(obj_file),
LIT(build_context.extra_assembler_flags)
);
+ if (result) {
+ gb_printf_err("executing `nasm` to assemble foreing import of %.*s failed.\n\tSuggestion: `nasm` does not ship with the compiler and should be installed with your system's package manager.\n", LIT(asm_file));
+ return result;
+ }
}
array_add(&gen->output_object_paths, obj_file);
} else {
- if (string_set_update(&libs, lib)) {
+ if (string_set_update(&min_libs_set, lib) && build_context.min_link_libs) {
+ continue;
+ }
+
+ if (prev_lib == lib) {
+ continue;
+ }
+ prev_lib = lib;
+
+ // Do not add libc again, this is added later already, and omitted with
+ // the `-no-crt` flag, not skipping here would cause duplicate library
+ // warnings when linking on darwin and might link libc silently even with `-no-crt`.
+ if (lib == str_lit("System.framework") || lib == str_lit("c")) {
continue;
}
- // NOTE(zangent): Sometimes, you have to use -framework on MacOS.
- // This allows you to specify '-f' in a #foreign_system_library,
- // without having to implement any new syntax specifically for MacOS.
if (build_context.metrics.os == TargetOs_darwin) {
if (string_ends_with(lib, str_lit(".framework"))) {
// framework thingie
@@ -425,13 +446,6 @@ gb_internal i32 linker_stage(LinkerData *gen) {
}
}
- for (Entity *e : gen->foreign_libraries) {
- GB_ASSERT(e->kind == Entity_LibraryName);
- if (e->LibraryName.extra_linker_flags.len != 0) {
- lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags));
- }
- }
-
gbString object_files = gb_string_make(heap_allocator(), "");
defer (gb_string_free(object_files));
for (String object_path : gen->output_object_paths) {
@@ -474,45 +488,47 @@ gb_internal i32 linker_stage(LinkerData *gen) {
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
}
- } else if (build_context.metrics.os != TargetOs_openbsd) {
- // OpenBSD defaults to PIE executable. do not pass -no-pie for it.
+ } else if (build_context.metrics.os != TargetOs_openbsd && build_context.metrics.os != TargetOs_haiku) {
+ // OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it.
link_settings = gb_string_appendc(link_settings, "-no-pie ");
}
gbString platform_lib_str = gb_string_make(heap_allocator(), "");
defer (gb_string_free(platform_lib_str));
if (build_context.metrics.os == TargetOs_darwin) {
- platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib ");
// Homebrew's default library path, checking if it exists to avoid linking warnings.
if (gb_file_exists("/opt/homebrew/lib")) {
- platform_lib_str = gb_string_appendc(platform_lib_str, " -L/opt/homebrew/lib");
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-L/opt/homebrew/lib ");
}
// MacPort's default library path, checking if it exists to avoid linking warnings.
if (gb_file_exists("/opt/local/lib")) {
- platform_lib_str = gb_string_appendc(platform_lib_str, " -L/opt/local/lib");
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-L/opt/local/lib ");
}
- #if defined(GB_SYSTEM_OSX)
- if(!build_context.no_crt) {
- platform_lib_str = gb_string_appendc(platform_lib_str, " -lm ");
- if(gen->needs_system_library_linked == 1) {
- platform_lib_str = gb_string_appendc(platform_lib_str, " -lSystem ");
- }
+ // Only specify this flag if the user has given a minimum version to target.
+ // This will cause warnings to show up for mismatched libraries.
+ if (build_context.minimum_os_version_string_given) {
+ link_settings = gb_string_append_fmt(link_settings, "-mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string));
+ }
+
+ if (build_context.build_mode != BuildMode_DynamicLibrary) {
+ // This points the linker to where the entry point is
+ link_settings = gb_string_appendc(link_settings, "-e _main ");
}
- #endif
- } else {
- platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
}
- if (build_context.metrics.os == TargetOs_darwin) {
- // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
- if (build_context.minimum_os_version_string.len) {
- link_settings = gb_string_append_fmt(link_settings, " -mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string));
+ if (!build_context.no_crt) {
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-lm ");
+ if (build_context.metrics.os == TargetOs_darwin) {
+ // NOTE: adding this causes a warning about duplicate libraries, I think it is
+ // automatically assumed/added by clang when you don't do `-nostdlib`.
+ // platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem ");
+ } else {
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-lc ");
}
- // This points the linker to where the entry point is
- link_settings = gb_string_appendc(link_settings, " -e _main ");
}
gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument ");
@@ -526,7 +542,12 @@ gb_internal i32 linker_stage(LinkerData *gen) {
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
- result = system_exec_command_line_app("ld-link", link_command_line);
+ if (build_context.use_lld) {
+ link_command_line = gb_string_append_fmt(link_command_line, " -fuse-ld=lld");
+ result = system_exec_command_line_app("lld-link", link_command_line);
+ } else {
+ result = system_exec_command_line_app("ld-link", link_command_line);
+ }
if (result) {
return result;
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp
index 857b255f3..88bb58c55 100644
--- a/src/llvm_abi.cpp
+++ b/src/llvm_abi.cpp
@@ -192,7 +192,7 @@ gb_internal void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType
// }
LLVMSetFunctionCallConv(fn, cc_kind);
if (calling_convention == ProcCC_Odin) {
- unsigned context_index = offset+arg_count;
+ unsigned context_index = arg_index;
LLVMAddAttributeAtIndex(fn, context_index, noalias_attr);
LLVMAddAttributeAtIndex(fn, context_index, nonnull_attr);
LLVMAddAttributeAtIndex(fn, context_index, nocapture_attr);
@@ -275,7 +275,7 @@ gb_internal i64 lb_alignof(LLVMTypeRef type) {
case LLVMIntegerTypeKind:
{
unsigned w = LLVMGetIntTypeWidth(type);
- return gb_clamp((w + 7)/8, 1, build_context.ptr_size);
+ return gb_clamp((w + 7)/8, 1, build_context.max_align);
}
case LLVMHalfTypeKind:
return 2;
@@ -558,7 +558,6 @@ namespace lbAbiAmd64SysV {
Amd64TypeAttribute_StructRect,
};
- gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
gb_internal void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off);
gb_internal void fixup(LLVMTypeRef t, Array<RegClass> *cls);
gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention);
@@ -796,10 +795,10 @@ namespace lbAbiAmd64SysV {
}
}
+ i64 sz = lb_sizeof(type);
if (all_ints) {
- i64 sz = lb_sizeof(type);
for_array(i, reg_classes) {
- GB_ASSERT(sz != 0);
+ GB_ASSERT(sz > 0);
// TODO(bill): is this even correct? BECAUSE LLVM DOES NOT DOCUMENT ANY OF THIS!!!
if (sz >= 8) {
array_add(&types, LLVMIntTypeInContext(c, 64));
@@ -811,12 +810,16 @@ namespace lbAbiAmd64SysV {
}
} else {
for (isize i = 0; i < reg_classes.count; /**/) {
+ GB_ASSERT(sz > 0);
RegClass reg_class = reg_classes[i];
switch (reg_class) {
case RegClass_Int:
- // TODO(bill): is this even correct? BECAUSE LLVM DOES NOT DOCUMENT ANY OF THIS!!!
- array_add(&types, LLVMIntTypeInContext(c, 64));
- break;
+ {
+ i64 rs = gb_min(sz, 8);
+ array_add(&types, LLVMIntTypeInContext(c, cast(unsigned)(rs*8)));
+ sz -= rs;
+ break;
+ }
case RegClass_SSEFv:
case RegClass_SSEDv:
case RegClass_SSEInt8:
@@ -856,15 +859,18 @@ namespace lbAbiAmd64SysV {
unsigned vec_len = llvec_len(reg_classes, i+1);
LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word);
array_add(&types, vec_type);
+ sz -= lb_sizeof(vec_type);
i += vec_len;
continue;
}
break;
case RegClass_SSEFs:
array_add(&types, LLVMFloatTypeInContext(c));
+ sz -= 4;
break;
case RegClass_SSEDs:
array_add(&types, LLVMDoubleTypeInContext(c));
+ sz -= 8;
break;
default:
GB_PANIC("Unhandled RegClass");
@@ -876,7 +882,8 @@ namespace lbAbiAmd64SysV {
if (types.count == 1) {
return types[0];
}
- return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false);
+
+ return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, sz == 0);
}
gb_internal void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off) {
@@ -979,28 +986,6 @@ namespace lbAbiAmd64SysV {
break;
}
}
-
- gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type) {
- if (!return_is_defined) {
- return lb_arg_type_direct(LLVMVoidTypeInContext(c));
- } else if (lb_is_type_kind(return_type, LLVMStructTypeKind)) {
- i64 sz = lb_sizeof(return_type);
- switch (sz) {
- case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr);
- case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr);
- case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
- case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
- }
-
- LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
-
- LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
- return lb_arg_type_indirect(return_type, attr);
- } else if (build_context.metrics.os == TargetOs_windows && lb_is_type_kind(return_type, LLVMIntegerTypeKind) && lb_sizeof(return_type) == 16) {
- return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 128), nullptr, nullptr);
- }
- return non_struct(c, return_type);
- }
};
@@ -1145,17 +1130,27 @@ namespace lbAbiArm64 {
i64 size = lb_sizeof(return_type);
if (size <= 16) {
LLVMTypeRef cast_type = nullptr;
- if (size <= 1) {
- cast_type = LLVMInt8TypeInContext(c);
- } else if (size <= 2) {
- cast_type = LLVMInt16TypeInContext(c);
- } else if (size <= 4) {
- cast_type = LLVMInt32TypeInContext(c);
- } else if (size <= 8) {
- cast_type = LLVMInt64TypeInContext(c);
+
+ GB_ASSERT(size > 0);
+ if (size <= 8) {
+ cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8));
} else {
unsigned count = cast(unsigned)((size+7)/8);
- cast_type = llvm_array_type(LLVMInt64TypeInContext(c), count);
+
+ LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64);
+ LLVMTypeRef *types = gb_alloc_array(temporary_allocator(), LLVMTypeRef, count);
+
+ i64 size_copy = size;
+ for (unsigned i = 0; i < count; i++) {
+ if (size_copy >= 8) {
+ types[i] = llvm_i64;
+ } else {
+ types[i] = LLVMIntTypeInContext(c, 8*cast(unsigned)size_copy);
+ }
+ size_copy -= 8;
+ }
+ GB_ASSERT(size_copy <= 0);
+ cast_type = LLVMStructTypeInContext(c, types, count, true);
}
return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr);
} else {
@@ -1188,17 +1183,27 @@ namespace lbAbiArm64 {
i64 size = lb_sizeof(type);
if (size <= 16) {
LLVMTypeRef cast_type = nullptr;
- if (size <= 1) {
- cast_type = LLVMIntTypeInContext(c, 8);
- } else if (size <= 2) {
- cast_type = LLVMIntTypeInContext(c, 16);
- } else if (size <= 4) {
- cast_type = LLVMIntTypeInContext(c, 32);
+ if (size == 0) {
+ cast_type = LLVMStructTypeInContext(c, nullptr, 0, false);
} else if (size <= 8) {
- cast_type = LLVMIntTypeInContext(c, 64);
+ cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8));
} else {
unsigned count = cast(unsigned)((size+7)/8);
- cast_type = llvm_array_type(LLVMIntTypeInContext(c, 64), count);
+
+ LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64);
+ LLVMTypeRef *types = gb_alloc_array(temporary_allocator(), LLVMTypeRef, count);
+
+ i64 size_copy = size;
+ for (unsigned i = 0; i < count; i++) {
+ if (size_copy >= 8) {
+ types[i] = llvm_i64;
+ } else {
+ types[i] = LLVMIntTypeInContext(c, 8*cast(unsigned)size_copy);
+ }
+ size_copy -= 8;
+ }
+ GB_ASSERT(size_copy <= 0);
+ cast_type = LLVMStructTypeInContext(c, types, count, true);
}
args[i] = lb_arg_type_direct(type, cast_type, nullptr, nullptr);
} else {
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index 003424e0a..645a091b0 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -334,7 +334,7 @@ gb_internal void lb_add_callsite_force_inline(lbProcedure *p, lbValue ret_value)
gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) {
type = core_type(type);
- GB_ASSERT_MSG(is_type_valid_for_keys(type), "%s", type_to_string(type));
+ GB_ASSERT_MSG(is_type_comparable(type), "%s", type_to_string(type));
Type *pt = alloc_type_pointer(type);
@@ -1053,36 +1053,6 @@ struct lbGlobalVariable {
bool is_initialized;
};
-gb_internal lbProcedure *lb_create_startup_type_info(lbModule *m) {
- if (build_context.no_rtti) {
- return nullptr;
- }
- Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
-
- lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_TYPE_INFO_PROC_NAME), proc_type);
- p->is_startup = true;
- LLVMSetLinkage(p->value, LLVMInternalLinkage);
-
- lb_add_attribute_to_proc(m, p->value, "nounwind");
- if (!LB_USE_GIANT_PACKED_STRUCT) {
- lb_add_attribute_to_proc(m, p->value, "optnone");
- lb_add_attribute_to_proc(m, p->value, "noinline");
- }
-
- lb_begin_procedure_body(p);
-
- lb_setup_type_info_data(p);
-
- lb_end_procedure_body(p);
-
- if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
- gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
- LLVMDumpValue(p->value);
- gb_printf_err("\n\n\n\n");
- LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
- }
- return p;
-}
gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) {
if (build_context.metrics.os != TargetOs_darwin) {
@@ -1124,7 +1094,7 @@ gb_internal void lb_finalize_objc_names(lbProcedure *p) {
lb_end_procedure_body(p);
}
-gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
+gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type);
@@ -1134,9 +1104,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
lb_begin_procedure_body(p);
- if (startup_type_info) {
- LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, startup_type_info->type), startup_type_info->value, nullptr, 0, "");
- }
+ lb_setup_type_info_data(main_module);
if (objc_names) {
LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, objc_names->type), objc_names->value, nullptr, 0, "");
@@ -1196,7 +1164,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
lbValue data = lb_emit_struct_ep(p, var.var, 0);
lbValue ti = lb_emit_struct_ep(p, var.var, 1);
lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr));
- lb_emit_store(p, ti, lb_type_info(main_module, var_type));
+ lb_emit_store(p, ti, lb_type_info(p, var_type));
} else {
LLVMTypeRef vt = llvm_addr_type(p->module, var.var);
lbValue src0 = lb_emit_conv(p, var.init, t);
@@ -1382,7 +1350,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_emit_worker_proc) {
if (LLVMTargetMachineEmitToFile(wd->target_machine, wd->m->mod, cast(char *)wd->filepath_obj.text, wd->code_gen_file_type, &llvm_error)) {
gb_printf_err("LLVM Error: %s\n", llvm_error);
- gb_exit(1);
+ exit_with_errors();
}
debugf("Generated File: %.*s\n", LIT(wd->filepath_obj));
return 0;
@@ -1421,7 +1389,6 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
}
if (m == &m->gen->default_module) {
- lb_llvm_function_pass_per_function_internal(m, m->gen->startup_type_info);
lb_llvm_function_pass_per_function_internal(m, m->gen->startup_runtime);
lb_llvm_function_pass_per_function_internal(m, m->gen->cleanup_runtime);
lb_llvm_function_pass_per_function_internal(m, m->gen->objc_names);
@@ -1952,7 +1919,7 @@ verify
gb_printf_err("LLVM Error: %s\n", llvm_error);
}
}
- gb_exit(1);
+ exit_with_errors();
return 1;
}
#endif
@@ -2011,12 +1978,6 @@ gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) {
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
if (m->debug_builder != nullptr) {
- lb_debug_complete_types(m);
- }
- }
- for (auto const &entry : gen->modules) {
- lbModule *m = entry.value;
- if (m->debug_builder != nullptr) {
LLVMDIBuilderFinalize(m->debug_builder);
}
}
@@ -2137,11 +2098,11 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) {
String filepath_ll = lb_filepath_ll_for_module(m);
if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) {
gb_printf_err("LLVM Error: %s\n", llvm_error);
- gb_exit(1);
+ exit_with_errors();
return false;
}
}
- gb_exit(1);
+ exit_with_errors();
return 1;
}
return 0;
@@ -2226,7 +2187,7 @@ gb_internal bool lb_llvm_object_generation(lbGenerator *gen, bool do_threading)
if (LLVMTargetMachineEmitToFile(m->target_machine, m->mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) {
gb_printf_err("LLVM Error: %s\n", llvm_error);
- gb_exit(1);
+ exit_with_errors();
return false;
}
debugf("Generated File: %.*s\n", LIT(filepath_obj));
@@ -2426,7 +2387,7 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) {
gb_printf_err("LLVM Error: %s\n", llvm_error);
}
LLVMVerifyFunction(p->value, LLVMPrintMessageAction);
- gb_exit(1);
+ exit_with_errors();
}
}
@@ -2597,8 +2558,8 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
switch (build_context.reloc_mode) {
case RelocMode_Default:
- if (build_context.metrics.os == TargetOs_openbsd) {
- // Always use PIC for OpenBSD: it defaults to PIE
+ if (build_context.metrics.os == TargetOs_openbsd || build_context.metrics.os == TargetOs_haiku) {
+ // Always use PIC for OpenBSD and Haiku: they default to PIE
reloc_mode = LLVMRelocPIC;
}
break;
@@ -2686,17 +2647,19 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
{ // Add type info data
isize max_type_info_count = info->minimum_dependency_type_info_set.count+1;
- Type *t = alloc_type_array(t_type_info, max_type_info_count);
+ Type *t = alloc_type_array(t_type_info_ptr, max_type_info_count);
// IMPORTANT NOTE(bill): As LLVM does not have a union type, an array of unions cannot be initialized
// at compile time without cheating in some way. This means to emulate an array of unions is to use
// a giant packed struct of "corrected" data types.
- LLVMTypeRef internal_llvm_type = lb_setup_type_info_data_internal_type(m, max_type_info_count);
+ LLVMTypeRef internal_llvm_type = lb_type(m, t);
LLVMValueRef g = LLVMAddGlobal(m->mod, internal_llvm_type, LB_TYPE_INFO_DATA_NAME);
LLVMSetInitializer(g, LLVMConstNull(internal_llvm_type));
LLVMSetLinkage(g, USE_SEPARATE_MODULES ? LLVMExternalLinkage : LLVMInternalLinkage);
+ LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr);
+ LLVMSetGlobalConstant(g, /*true*/false);
lbValue value = {};
value.value = g;
@@ -2705,15 +2668,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
lb_global_type_info_data_entity = alloc_entity_variable(nullptr, make_token_ident(LB_TYPE_INFO_DATA_NAME), t, EntityState_Resolved);
lb_add_entity(m, lb_global_type_info_data_entity, value);
- if (LB_USE_GIANT_PACKED_STRUCT) {
- LLVMSetLinkage(g, LLVMPrivateLinkage);
- LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr);
- LLVMSetGlobalConstant(g, /*true*/false);
- }
}
{ // Type info member buffer
// NOTE(bill): Removes need for heap allocation by making it global memory
isize count = 0;
+ isize offsets_extra = 0;
for (Type *t : m->info->type_info_types) {
isize index = lb_type_info_index(m->info, t, false);
@@ -2731,6 +2690,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
case Type_Tuple:
count += t->Tuple.variables.count;
break;
+ case Type_BitField:
+ count += t->BitField.fields.count;
+ // Twice is needed for the bit_offsets
+ offsets_extra += t->BitField.fields.count;
+ break;
}
}
@@ -2739,15 +2703,13 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name);
LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
LLVMSetLinkage(g, LLVMInternalLinkage);
- if (LB_USE_GIANT_PACKED_STRUCT) {
- lb_make_global_private_const(g);
- }
+ lb_make_global_private_const(g);
return lb_addr({g, alloc_type_pointer(t)});
};
lb_global_type_info_member_types = global_type_info_make(m, LB_TYPE_INFO_TYPES_NAME, t_type_info_ptr, count);
lb_global_type_info_member_names = global_type_info_make(m, LB_TYPE_INFO_NAMES_NAME, t_string, count);
- lb_global_type_info_member_offsets = global_type_info_make(m, LB_TYPE_INFO_OFFSETS_NAME, t_uintptr, count);
+ lb_global_type_info_member_offsets = global_type_info_make(m, LB_TYPE_INFO_OFFSETS_NAME, t_uintptr, count+offsets_extra);
lb_global_type_info_member_usings = global_type_info_make(m, LB_TYPE_INFO_USINGS_NAME, t_bool, count);
lb_global_type_info_member_tags = global_type_info_make(m, LB_TYPE_INFO_TAGS_NAME, t_string, count);
}
@@ -2910,12 +2872,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
}
}
- TIME_SECTION("LLVM Runtime Type Information Creation");
- gen->startup_type_info = lb_create_startup_type_info(default_module);
+ TIME_SECTION("LLVM Runtime Objective-C Names Creation");
gen->objc_names = lb_create_objc_names(default_module);
TIME_SECTION("LLVM Runtime Startup Creation (Global Variables & @(init))");
- gen->startup_runtime = lb_create_startup_runtime(default_module, gen->startup_type_info, gen->objc_names, global_variables);
+ gen->startup_runtime = lb_create_startup_runtime(default_module, gen->objc_names, global_variables);
TIME_SECTION("LLVM Runtime Cleanup Creation & @(fini)");
gen->cleanup_runtime = lb_create_cleanup_runtime(default_module);
@@ -2995,7 +2956,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
String filepath_ll = lb_filepath_ll_for_module(m);
if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) {
gb_printf_err("LLVM Error: %s\n", llvm_error);
- gb_exit(1);
+ exit_with_errors();
return false;
}
array_add(&gen->output_temp_paths, filepath_ll);
@@ -3054,7 +3015,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
}
}
- gb_sort_array(gen->foreign_libraries.data, gen->foreign_libraries.count, foreign_library_cmp);
+ array_sort(gen->foreign_libraries, foreign_library_cmp);
return true;
}
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index fe2c2deba..c4bf2691d 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -84,6 +84,8 @@ enum lbAddrKind {
lbAddr_Swizzle,
lbAddr_SwizzleLarge,
+
+ lbAddr_BitField,
};
struct lbAddr {
@@ -118,6 +120,12 @@ struct lbAddr {
Type *type;
Slice<i32> indices;
} swizzle_large;
+ struct {
+ Type *type;
+ i64 index;
+ i64 bit_offset;
+ i64 bit_size;
+ } bitfield;
};
};
@@ -191,8 +199,6 @@ struct lbModule {
RecursiveMutex debug_values_mutex;
PtrMap<void *, LLVMMetadataRef> debug_values;
- Array<lbIncompleteDebugType> debug_incomplete_types;
-
StringMap<lbAddr> objc_classes;
StringMap<lbAddr> objc_selectors;
@@ -217,7 +223,6 @@ struct lbGenerator : LinkerData {
std::atomic<u32> global_array_index;
std::atomic<u32> global_generated_index;
- lbProcedure *startup_type_info;
lbProcedure *startup_runtime;
lbProcedure *cleanup_runtime;
lbProcedure *objc_names;
@@ -478,7 +483,7 @@ gb_internal lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValu
gb_internal void lb_fill_slice(lbProcedure *p, lbAddr const &slice, lbValue base_elem, lbValue len);
-gb_internal lbValue lb_type_info(lbModule *m, Type *type);
+gb_internal lbValue lb_type_info(lbProcedure *p, Type *type);
gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str);
gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent = nullptr);
@@ -501,7 +506,7 @@ gb_internal lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_pt
gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e);
gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e);
-gb_internal void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value);
+gb_internal void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value, bool is_default_case);
gb_internal lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value);
gb_internal lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos);
gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String const &procedure, TokenPos const &pos);
@@ -567,6 +572,8 @@ gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *t
gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, String const &procedure, TokenPos const &pos);
+gb_internal LLVMMetadataRef lb_debug_location_from_token_pos(lbProcedure *p, TokenPos pos);
+
gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t ElementCount) {
#if LB_USE_NEW_PASS_SYSTEM
return LLVMArrayType2(ElementType, ElementCount);
@@ -577,7 +584,6 @@ gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t Elemen
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
#define LB_CLEANUP_RUNTIME_PROC_NAME "__$cleanup_runtime"
-#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
#define LB_TYPE_INFO_DATA_NAME "__$type_info_data"
#define LB_TYPE_INFO_TYPES_NAME "__$type_info_types_data"
#define LB_TYPE_INFO_NAMES_NAME "__$type_info_names_data"
diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp
index 2291f24ac..8035336d3 100644
--- a/src/llvm_backend_const.cpp
+++ b/src/llvm_backend_const.cpp
@@ -287,24 +287,6 @@ gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type
return lb_const_value(m, t, tv.value);
}
-gb_internal String lb_obfuscate_string(String const &s, char const *prefix) {
- if (s.len == 0) {
- return {};
- }
- GB_ASSERT(prefix != nullptr);
- u64 hash = gb_fnv64a(s.text, s.len);
- gbString res = gb_string_make(temporary_allocator(), prefix);
- res = gb_string_append_fmt(res, "x%llx", cast(long long unsigned)hash);
- return make_string_c(res);
-}
-
-gb_internal i32 lb_obfuscate_i32(i32 i) {
- i32 x = cast(i32)gb_fnv64a(&i, sizeof(i));
- if (x < 0) {
- x = 1-x;
- }
- return cast(i32)x;
-}
gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String const &procedure_, TokenPos const &pos) {
String file = get_file_path_string(pos.file_id);
@@ -314,11 +296,11 @@ gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String cons
i32 column = pos.column;
if (build_context.obfuscate_source_code_locations) {
- file = lb_obfuscate_string(file, "F");
- procedure = lb_obfuscate_string(procedure, "P");
+ file = obfuscate_string(file, "F");
+ procedure = obfuscate_string(procedure, "P");
- line = lb_obfuscate_i32(line);
- column = lb_obfuscate_i32(column);
+ line = obfuscate_i32(line);
+ column = obfuscate_i32(column);
}
LLVMValueRef fields[4] = {};
@@ -730,9 +712,21 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
return res;
case ExactValue_Float:
if (is_type_different_to_arch_endianness(type)) {
- u64 u = bit_cast<u64>(value.value_float);
- u = gb_endian_swap64(u);
- res.value = LLVMConstReal(lb_type(m, original_type), bit_cast<f64>(u));
+ if (type->Basic.kind == Basic_f32le || type->Basic.kind == Basic_f32be) {
+ f32 f = static_cast<float>(value.value_float);
+ u32 u = bit_cast<u32>(f);
+ u = gb_endian_swap32(u);
+ res.value = LLVMConstReal(lb_type(m, original_type), bit_cast<f32>(u));
+ } else if (type->Basic.kind == Basic_f16le || type->Basic.kind == Basic_f16be) {
+ f32 f = static_cast<float>(value.value_float);
+ u16 u = f32_to_f16(f);
+ u = gb_endian_swap16(u);
+ res.value = LLVMConstReal(lb_type(m, original_type), f16_to_f32(u));
+ } else {
+ u64 u = bit_cast<u64>(value.value_float);
+ u = gb_endian_swap64(u);
+ res.value = LLVMConstReal(lb_type(m, original_type), bit_cast<f64>(u));
+ }
} else {
res.value = LLVMConstReal(lb_type(m, original_type), value.value_float);
}
@@ -1302,11 +1296,11 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
GB_ASSERT_MSG(elem_count == max_count, "%td != %td", elem_count, max_count);
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)total_count);
-
for_array(i, cl->elems) {
TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid);
- i64 offset = matrix_row_major_index_to_offset(type, i);
+ i64 offset = 0;
+ offset = matrix_row_major_index_to_offset(type, i);
values[offset] = lb_const_value(m, elem_type, tav.value, allow_local).value;
}
for (isize i = 0; i < total_count; i++) {
diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp
index e053c5b40..511ff0475 100644
--- a/src/llvm_backend_debug.cpp
+++ b/src/llvm_backend_debug.cpp
@@ -114,6 +114,415 @@ gb_internal LLVMMetadataRef lb_debug_basic_struct(lbModule *m, String const &nam
return LLVMDIBuilderCreateStructType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, 1, size_in_bits, align_in_bits, LLVMDIFlagZero, nullptr, elements, element_count, 0, nullptr, "", 0);
}
+gb_internal LLVMMetadataRef lb_debug_struct(lbModule *m, Type *type, Type *bt, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
+ GB_ASSERT(bt->kind == Type_Struct);
+
+ unsigned tag = DW_TAG_structure_type;
+ if (is_type_raw_union(bt)) {
+ tag = DW_TAG_union_type;
+ }
+
+ u64 size_in_bits = 8*type_size_of(bt);
+ u32 align_in_bits = 8*cast(u32)type_align_of(bt);
+
+ LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
+ m->debug_builder, tag,
+ cast(char const *)name.text, cast(size_t)name.len,
+ scope, file, line, 0, size_in_bits, align_in_bits, LLVMDIFlagZero, "", 0
+ );
+
+ lb_set_llvm_metadata(m, type, temp_forward_decl);
+
+ type_set_offsets(bt);
+
+ unsigned element_count = cast(unsigned)(bt->Struct.fields.count);
+ LLVMMetadataRef *elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+
+ LLVMMetadataRef member_scope = lb_get_llvm_metadata(m, bt->Struct.scope);
+
+ for_array(j, bt->Struct.fields) {
+ Entity *f = bt->Struct.fields[j];
+ String fname = f->token.string;
+
+ unsigned field_line = 0;
+ LLVMDIFlags field_flags = LLVMDIFlagZero;
+ GB_ASSERT(bt->Struct.offsets != nullptr);
+ u64 offset_in_bits = 8*cast(u64)bt->Struct.offsets[j];
+
+ elements[j] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder,
+ member_scope,
+ cast(char const *)fname.text, cast(size_t)fname.len,
+ file, field_line,
+ 8*cast(u64)type_size_of(f->type), 8*cast(u32)type_align_of(f->type),
+ offset_in_bits,
+ field_flags,
+ lb_debug_type(m, f->type)
+ );
+ }
+
+ LLVMMetadataRef final_decl = nullptr;
+ if (tag == DW_TAG_union_type) {
+ final_decl = LLVMDIBuilderCreateUnionType(
+ m->debug_builder, scope,
+ cast(char const*)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ elements, element_count,
+ 0,
+ "", 0
+ );
+ } else {
+ final_decl = LLVMDIBuilderCreateStructType(
+ m->debug_builder, scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ nullptr,
+ elements, element_count,
+ 0,
+ nullptr,
+ "", 0
+ );
+ }
+
+ LLVMMetadataReplaceAllUsesWith(temp_forward_decl, final_decl);
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+}
+
+gb_internal LLVMMetadataRef lb_debug_slice(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_Slice);
+
+ unsigned const ptr_bits = cast(unsigned)(8*build_context.ptr_size);
+
+ u64 size_in_bits = 8*type_size_of(bt);
+ u32 align_in_bits = 8*cast(u32)type_align_of(bt);
+
+ LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
+ m->debug_builder, DW_TAG_structure_type,
+ cast(char const *)name.text, cast(size_t)name.len,
+ scope, file, line, 0, size_in_bits, align_in_bits, LLVMDIFlagZero, "", 0
+ );
+
+ lb_set_llvm_metadata(m, type, temp_forward_decl);
+
+ unsigned element_count = 2;
+ LLVMMetadataRef elements[2];
+
+ // LLVMMetadataRef member_scope = lb_get_llvm_metadata(m, bt->Slice.scope);
+ LLVMMetadataRef member_scope = nullptr;
+
+ Type *elem_type = alloc_type_pointer(bt->Slice.elem);
+ elements[0] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ "data", 4,
+ file, line,
+ 8*cast(u64)type_size_of(elem_type), 8*cast(u32)type_align_of(elem_type),
+ 0,
+ LLVMDIFlagZero, lb_debug_type(m, elem_type)
+ );
+
+ elements[1] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ "len", 3,
+ file, line,
+ 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
+ ptr_bits,
+ LLVMDIFlagZero, lb_debug_type(m, t_int)
+ );
+
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateStructType(
+ m->debug_builder, scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ nullptr,
+ elements, element_count,
+ 0,
+ nullptr,
+ "", 0
+ );
+
+ LLVMMetadataReplaceAllUsesWith(temp_forward_decl, final_decl);
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+}
+
+gb_internal LLVMMetadataRef lb_debug_dynamic_array(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_DynamicArray);
+
+ unsigned const ptr_bits = cast(unsigned)(8*build_context.ptr_size);
+ unsigned const int_bits = cast(unsigned)(8*build_context.int_size);
+
+ u64 size_in_bits = 8*type_size_of(bt);
+ u32 align_in_bits = 8*cast(u32)type_align_of(bt);
+
+ LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
+ m->debug_builder, DW_TAG_structure_type,
+ cast(char const *)name.text, cast(size_t)name.len,
+ scope, file, line, 0, size_in_bits, align_in_bits, LLVMDIFlagZero, "", 0
+ );
+
+ lb_set_llvm_metadata(m, type, temp_forward_decl);
+
+ unsigned element_count = 4;
+ LLVMMetadataRef elements[4];
+
+ // LLVMMetadataRef member_scope = lb_get_llvm_metadata(m, bt->DynamicArray.scope);
+ LLVMMetadataRef member_scope = nullptr;
+
+ Type *elem_type = alloc_type_pointer(bt->DynamicArray.elem);
+ elements[0] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ "data", 4,
+ file, line,
+ 8*cast(u64)type_size_of(elem_type), 8*cast(u32)type_align_of(elem_type),
+ 0,
+ LLVMDIFlagZero, lb_debug_type(m, elem_type)
+ );
+
+ elements[1] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ "len", 3,
+ file, line,
+ 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
+ ptr_bits,
+ LLVMDIFlagZero, lb_debug_type(m, t_int)
+ );
+
+ elements[2] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ "cap", 3,
+ file, line,
+ 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
+ ptr_bits+int_bits,
+ LLVMDIFlagZero, lb_debug_type(m, t_int)
+ );
+
+ elements[3] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ "allocator", 9,
+ file, line,
+ 8*cast(u64)type_size_of(t_allocator), 8*cast(u32)type_align_of(t_allocator),
+ ptr_bits+int_bits+int_bits,
+ LLVMDIFlagZero, lb_debug_type(m, t_allocator)
+ );
+
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateStructType(
+ m->debug_builder, scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ nullptr,
+ elements, element_count,
+ 0,
+ nullptr,
+ "", 0
+ );
+
+ LLVMMetadataReplaceAllUsesWith(temp_forward_decl, final_decl);
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+}
+
+gb_internal LLVMMetadataRef lb_debug_union(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_Union);
+
+ u64 size_in_bits = 8*type_size_of(bt);
+ u32 align_in_bits = 8*cast(u32)type_align_of(bt);
+
+ LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
+ m->debug_builder, DW_TAG_union_type,
+ cast(char const *)name.text, cast(size_t)name.len,
+ scope, file, line, 0, size_in_bits, align_in_bits, LLVMDIFlagZero, "", 0
+ );
+
+ lb_set_llvm_metadata(m, type, temp_forward_decl);
+
+ isize index_offset = 1;
+ if (is_type_union_maybe_pointer(bt)) {
+ index_offset = 0;
+ }
+
+ LLVMMetadataRef member_scope = lb_get_llvm_metadata(m, bt->Union.scope);
+ unsigned element_count = cast(unsigned)bt->Union.variants.count;
+ if (index_offset > 0) {
+ element_count += 1;
+ }
+
+ LLVMMetadataRef *elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+
+ if (index_offset > 0) {
+ Type *tag_type = union_tag_type(bt);
+ u64 offset_in_bits = 8*cast(u64)bt->Union.variant_block_size;
+
+ elements[0] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ "tag", 3,
+ file, line,
+ 8*cast(u64)type_size_of(tag_type), 8*cast(u32)type_align_of(tag_type),
+ offset_in_bits,
+ LLVMDIFlagZero, lb_debug_type(m, tag_type)
+ );
+ }
+
+ for_array(j, bt->Union.variants) {
+ Type *variant = bt->Union.variants[j];
+
+ unsigned field_index = cast(unsigned)(index_offset+j);
+
+ char name[16] = {};
+ gb_snprintf(name, gb_size_of(name), "v%u", field_index);
+ isize name_len = gb_strlen(name);
+
+ elements[field_index] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ name, name_len,
+ file, line,
+ 8*cast(u64)type_size_of(variant), 8*cast(u32)type_align_of(variant),
+ 0,
+ LLVMDIFlagZero, lb_debug_type(m, variant)
+ );
+ }
+
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateUnionType(
+ m->debug_builder,
+ scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ elements,
+ element_count,
+ 0,
+ "", 0
+ );
+
+ LLVMMetadataReplaceAllUsesWith(temp_forward_decl, final_decl);
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+}
+
+gb_internal LLVMMetadataRef lb_debug_bitset(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_BitSet);
+
+ u64 size_in_bits = 8*type_size_of(bt);
+ u32 align_in_bits = 8*cast(u32)type_align_of(bt);
+
+ LLVMMetadataRef bit_set_field_type = lb_debug_type(m, t_bool);
+
+ unsigned element_count = 0;
+ LLVMMetadataRef *elements = nullptr;
+
+ Type *elem = base_type(bt->BitSet.elem);
+ if (elem->kind == Type_Enum) {
+ element_count = cast(unsigned)elem->Enum.fields.count;
+ elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+
+ for_array(i, elem->Enum.fields) {
+ Entity *f = elem->Enum.fields[i];
+ GB_ASSERT(f->kind == Entity_Constant);
+ i64 val = exact_value_to_i64(f->Constant.value);
+ String field_name = f->token.string;
+ u64 offset_in_bits = cast(u64)(val - bt->BitSet.lower);
+ elements[i] = LLVMDIBuilderCreateBitFieldMemberType(
+ m->debug_builder,
+ scope,
+ cast(char const *)field_name.text, field_name.len,
+ file, line,
+ 1,
+ offset_in_bits,
+ 0,
+ LLVMDIFlagZero,
+ bit_set_field_type
+ );
+ }
+ } else {
+ char name[32] = {};
+
+ GB_ASSERT(is_type_integer(elem));
+ i64 count = bt->BitSet.upper - bt->BitSet.lower + 1;
+ GB_ASSERT(0 <= count);
+
+ element_count = cast(unsigned)count;
+ elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+
+ for (unsigned i = 0; i < element_count; i++) {
+ u64 offset_in_bits = i;
+ i64 val = bt->BitSet.lower + cast(i64)i;
+ gb_snprintf(name, gb_count_of(name), "%lld", cast(long long)val);
+ elements[i] = LLVMDIBuilderCreateBitFieldMemberType(
+ m->debug_builder,
+ scope,
+ name, gb_strlen(name),
+ file, line,
+ 1,
+ offset_in_bits,
+ 0,
+ LLVMDIFlagZero,
+ bit_set_field_type
+ );
+ }
+ }
+
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateUnionType(
+ m->debug_builder,
+ scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ elements,
+ element_count,
+ 0,
+ "", 0
+ );
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+}
+
+gb_internal LLVMMetadataRef lb_debug_enum(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_Enum);
+
+ u64 size_in_bits = 8*type_size_of(bt);
+ u32 align_in_bits = 8*cast(u32)type_align_of(bt);
+
+ unsigned element_count = cast(unsigned)bt->Enum.fields.count;
+ LLVMMetadataRef *elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+
+ Type *bt_enum = base_enum_type(bt);
+ LLVMBool is_unsigned = is_type_unsigned(bt_enum);
+ for (unsigned i = 0; i < element_count; i++) {
+ Entity *f = bt->Enum.fields[i];
+ GB_ASSERT(f->kind == Entity_Constant);
+ String enum_name = f->token.string;
+ i64 value = exact_value_to_i64(f->Constant.value);
+ elements[i] = LLVMDIBuilderCreateEnumerator(m->debug_builder, cast(char const *)enum_name.text, cast(size_t)enum_name.len, value, is_unsigned);
+ }
+
+ LLVMMetadataRef class_type = lb_debug_type(m, bt_enum);
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateEnumerationType(
+ m->debug_builder,
+ scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ elements, element_count,
+ class_type
+ );
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+}
gb_internal LLVMMetadataRef lb_debug_type_basic_type(lbModule *m, String const &name, u64 size_in_bits, LLVMDWARFTypeEncoding encoding, LLVMDIFlags flags = LLVMDIFlagZero) {
LLVMMetadataRef basic_type = LLVMDIBuilderCreateBasicType(m->debug_builder, cast(char const *)name.text, name.len, size_in_bits, encoding, flags);
@@ -329,53 +738,19 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
return LLVMDIBuilderCreateTypedef(m->debug_builder, array_type, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
}
+ case Type_Map: {
+ Type *bt = base_type(type->Map.debug_metadata_type);
+ GB_ASSERT(bt->kind == Type_Struct);
- case Type_Struct:
- case Type_Union:
- case Type_Slice:
- case Type_DynamicArray:
- case Type_Map:
- case Type_BitSet:
- {
- unsigned tag = DW_TAG_structure_type;
- if (is_type_raw_union(type) || is_type_union(type)) {
- tag = DW_TAG_union_type;
- }
- u64 size_in_bits = cast(u64)(8*type_size_of(type));
- u32 align_in_bits = cast(u32)(8*type_size_of(type));
- LLVMDIFlags flags = LLVMDIFlagZero;
-
- LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
- m->debug_builder, tag, "", 0, nullptr, nullptr, 0, 0, size_in_bits, align_in_bits, flags, "", 0
- );
- lbIncompleteDebugType idt = {};
- idt.type = type;
- idt.metadata = temp_forward_decl;
-
- array_add(&m->debug_incomplete_types, idt);
- lb_set_llvm_metadata(m, type, temp_forward_decl);
- return temp_forward_decl;
- }
+ return lb_debug_struct(m, type, bt, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
+ }
- case Type_Enum:
- {
- LLVMMetadataRef scope = nullptr;
- LLVMMetadataRef file = nullptr;
- unsigned line = 0;
- unsigned element_count = cast(unsigned)type->Enum.fields.count;
- LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
- Type *bt = base_enum_type(type);
- LLVMBool is_unsigned = is_type_unsigned(bt);
- for (unsigned i = 0; i < element_count; i++) {
- Entity *f = type->Enum.fields[i];
- GB_ASSERT(f->kind == Entity_Constant);
- String name = f->token.string;
- i64 value = exact_value_to_i64(f->Constant.value);
- elements[i] = LLVMDIBuilderCreateEnumerator(m->debug_builder, cast(char const *)name.text, cast(size_t)name.len, value, is_unsigned);
- }
- LLVMMetadataRef class_type = lb_debug_type(m, bt);
- return LLVMDIBuilderCreateEnumerationType(m->debug_builder, scope, "", 0, file, line, 8*type_size_of(type), 8*cast(unsigned)type_align_of(type), elements, element_count, class_type);
- }
+ case Type_Struct: return lb_debug_struct( m, type, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
+ case Type_Slice: return lb_debug_slice( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
+ case Type_DynamicArray: return lb_debug_dynamic_array(m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
+ case Type_Union: return lb_debug_union( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
+ case Type_BitSet: return lb_debug_bitset( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
+ case Type_Enum: return lb_debug_enum( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_Tuple:
if (type->Tuple.variables.count == 1) {
@@ -461,6 +836,42 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
lb_debug_type(m, type->Matrix.elem),
subscripts, gb_count_of(subscripts));
}
+
+ case Type_BitField: {
+ LLVMMetadataRef parent_scope = nullptr;
+ LLVMMetadataRef scope = nullptr;
+ LLVMMetadataRef file = nullptr;
+ unsigned line = 0;
+ u64 size_in_bits = 8*cast(u64)type_size_of(type);
+ u32 align_in_bits = 8*cast(u32)type_align_of(type);
+ LLVMDIFlags flags = LLVMDIFlagZero;
+
+ unsigned element_count = cast(unsigned)type->BitField.fields.count;
+ LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
+
+ u64 offset_in_bits = 0;
+ for (unsigned i = 0; i < element_count; i++) {
+ Entity *f = type->BitField.fields[i];
+ u8 bit_size = type->BitField.bit_sizes[i];
+ GB_ASSERT(f->kind == Entity_Variable);
+ String name = f->token.string;
+ unsigned field_line = 0;
+ LLVMDIFlags field_flags = LLVMDIFlagZero;
+ elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, field_line,
+ bit_size, offset_in_bits, offset_in_bits,
+ field_flags, lb_debug_type(m, f->type)
+ );
+
+ offset_in_bits += bit_size;
+ }
+
+
+ return LLVMDIBuilderCreateStructType(m->debug_builder, parent_scope, "", 0, file, line,
+ size_in_bits, align_in_bits, flags,
+ nullptr, elements, element_count, 0, nullptr,
+ "", 0
+ );
+ }
}
GB_PANIC("Invalid type %s", type_to_string(type));
@@ -503,7 +914,6 @@ gb_internal LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
unsigned line = 0;
LLVMMetadataRef scope = nullptr;
-
if (type->Named.type_name != nullptr) {
Entity *e = type->Named.type_name;
scope = lb_get_base_scope_metadata(m, e->scope);
@@ -512,452 +922,48 @@ gb_internal LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
}
line = cast(unsigned)e->token.pos.line;
}
- // TODO(bill): location data for Type_Named
- u64 size_in_bits = 8*type_size_of(type);
- u32 align_in_bits = 8*cast(u32)type_align_of(type);
String name = type->Named.name;
- char const *name_text = cast(char const *)name.text;
- size_t name_len = cast(size_t)name.len;
- unsigned tag = DW_TAG_structure_type;
- if (is_type_raw_union(type) || is_type_union(type)) {
- tag = DW_TAG_union_type;
+ if (type->Named.type_name && type->Named.type_name->pkg && type->Named.type_name->pkg->name.len != 0) {
+ name = concatenate3_strings(temporary_allocator(), type->Named.type_name->pkg->name, str_lit("."), type->Named.name);
}
- LLVMDIFlags flags = LLVMDIFlagZero;
Type *bt = base_type(type->Named.base);
- lbIncompleteDebugType idt = {};
- idt.type = type;
-
switch (bt->kind) {
- case Type_Enum:
- {
- unsigned line = 0;
- unsigned element_count = cast(unsigned)bt->Enum.fields.count;
- LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
- Type *ct = base_enum_type(type);
- LLVMBool is_unsigned = is_type_unsigned(ct);
- for (unsigned i = 0; i < element_count; i++) {
- Entity *f = bt->Enum.fields[i];
- GB_ASSERT(f->kind == Entity_Constant);
- String name = f->token.string;
- i64 value = exact_value_to_i64(f->Constant.value);
- elements[i] = LLVMDIBuilderCreateEnumerator(m->debug_builder, cast(char const *)name.text, cast(size_t)name.len, value, is_unsigned);
- }
- LLVMMetadataRef class_type = lb_debug_type(m, ct);
- return LLVMDIBuilderCreateEnumerationType(m->debug_builder, scope, name_text, name_len, file, line, 8*type_size_of(type), 8*cast(unsigned)type_align_of(type), elements, element_count, class_type);
- }
-
+ default: {
+ u32 align_in_bits = 8*cast(u32)type_align_of(type);
+ LLVMMetadataRef debug_bt = lb_debug_type(m, bt);
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateTypedef(
+ m->debug_builder,
+ debug_bt,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line, scope, align_in_bits
+ );
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+ }
- default:
- {
- LLVMMetadataRef debug_bt = lb_debug_type(m, bt);
- LLVMMetadataRef final_decl = LLVMDIBuilderCreateTypedef(m->debug_builder, debug_bt, name_text, name_len, file, line, scope, align_in_bits);
- lb_set_llvm_metadata(m, type, final_decl);
- return final_decl;
- }
+ case Type_Map: {
+ bt = base_type(bt->Map.debug_metadata_type);
+ GB_ASSERT(bt->kind == Type_Struct);
+ return lb_debug_struct(m, type, bt, name, scope, file, line);
+ }
- case Type_Slice:
- case Type_DynamicArray:
- case Type_Map:
- case Type_Struct:
- case Type_Union:
- case Type_BitSet:
- {
- LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
- m->debug_builder, tag, name_text, name_len, nullptr, nullptr, 0, 0, size_in_bits, align_in_bits, flags, "", 0
- );
- idt.metadata = temp_forward_decl;
-
- array_add(&m->debug_incomplete_types, idt);
- lb_set_llvm_metadata(m, type, temp_forward_decl);
-
- LLVMMetadataRef dummy = nullptr;
- switch (bt->kind) {
- case Type_Slice:
- dummy = lb_debug_type(m, bt->Slice.elem);
- dummy = lb_debug_type(m, alloc_type_pointer(bt->Slice.elem));
- dummy = lb_debug_type(m, t_int);
- break;
- case Type_DynamicArray:
- dummy = lb_debug_type(m, bt->DynamicArray.elem);
- dummy = lb_debug_type(m, alloc_type_pointer(bt->DynamicArray.elem));
- dummy = lb_debug_type(m, t_int);
- dummy = lb_debug_type(m, t_allocator);
- break;
- case Type_Map:
- dummy = lb_debug_type(m, bt->Map.key);
- dummy = lb_debug_type(m, bt->Map.value);
- dummy = lb_debug_type(m, t_int);
- dummy = lb_debug_type(m, t_allocator);
- dummy = lb_debug_type(m, t_uintptr);
- break;
- case Type_BitSet:
- if (bt->BitSet.elem) dummy = lb_debug_type(m, bt->BitSet.elem);
- if (bt->BitSet.underlying) dummy = lb_debug_type(m, bt->BitSet.underlying);
- break;
- }
-
- return temp_forward_decl;
- }
+ case Type_Struct: return lb_debug_struct(m, type, base_type(type), name, scope, file, line);
+ case Type_Slice: return lb_debug_slice(m, type, name, scope, file, line);
+ case Type_DynamicArray: return lb_debug_dynamic_array(m, type, name, scope, file, line);
+ case Type_Union: return lb_debug_union(m, type, name, scope, file, line);
+ case Type_BitSet: return lb_debug_bitset(m, type, name, scope, file, line);
+ case Type_Enum: return lb_debug_enum(m, type, name, scope, file, line);
}
}
-
LLVMMetadataRef dt = lb_debug_type_internal(m, type);
lb_set_llvm_metadata(m, type, dt);
return dt;
}
-gb_internal void lb_debug_complete_types(lbModule *m) {
- unsigned const int_bits = cast(unsigned)(8*build_context.int_size);
-
- for_array(debug_incomplete_type_index, m->debug_incomplete_types) {
- TEMPORARY_ALLOCATOR_GUARD();
-
- auto const &idt = m->debug_incomplete_types[debug_incomplete_type_index];
- GB_ASSERT(idt.type != nullptr);
- GB_ASSERT(idt.metadata != nullptr);
-
- Type *t = idt.type;
- Type *bt = base_type(t);
-
- LLVMMetadataRef parent_scope = nullptr;
- LLVMMetadataRef file = nullptr;
- unsigned line_number = 0;
- u64 size_in_bits = 8*type_size_of(t);
- u32 align_in_bits = cast(u32)(8*type_align_of(t));
- LLVMDIFlags flags = LLVMDIFlagZero;
-
- LLVMMetadataRef derived_from = nullptr;
-
- LLVMMetadataRef *elements = nullptr;
- unsigned element_count = 0;
-
-
- unsigned runtime_lang = 0; // Objective-C runtime version
- char const *unique_id = "";
- LLVMMetadataRef vtable_holder = nullptr;
- size_t unique_id_len = 0;
-
-
- LLVMMetadataRef record_scope = nullptr;
-
- switch (bt->kind) {
- case Type_Slice:
- case Type_DynamicArray:
- case Type_Map:
- case Type_Struct:
- case Type_Union:
- case Type_BitSet: {
- bool is_union = is_type_raw_union(bt) || is_type_union(bt);
-
- String name = str_lit("<anonymous-struct>");
- if (t->kind == Type_Named) {
- name = t->Named.name;
- if (t->Named.type_name && t->Named.type_name->pkg && t->Named.type_name->pkg->name.len != 0) {
- name = concatenate3_strings(temporary_allocator(), t->Named.type_name->pkg->name, str_lit("."), t->Named.name);
- }
-
- LLVMMetadataRef file = nullptr;
- unsigned line = 0;
- LLVMMetadataRef file_scope = nullptr;
-
- if (t->Named.type_name != nullptr) {
- Entity *e = t->Named.type_name;
- file_scope = lb_get_llvm_metadata(m, e->scope);
- if (file_scope != nullptr) {
- file = LLVMDIScopeGetFile(file_scope);
- }
- line = cast(unsigned)e->token.pos.line;
- }
- // TODO(bill): location data for Type_Named
-
- } else {
- name = make_string_c(type_to_string(t, temporary_allocator()));
- }
-
-
-
- switch (bt->kind) {
- case Type_Slice:
- element_count = 2;
- elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
- #if defined(GB_SYSTEM_WINDOWS)
- elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->Slice.elem), 0*int_bits);
- #else
- // FIX HACK TODO(bill): For some reason this causes a crash in *nix systems due to the reference counting
- // of the debug type information
- elements[0] = lb_debug_struct_field(m, str_lit("data"), t_rawptr, 0*int_bits);
- #endif
- elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, 1*int_bits);
- break;
- case Type_DynamicArray:
- element_count = 4;
- elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
- #if defined(GB_SYSTEM_WINDOWS)
- elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->DynamicArray.elem), 0*int_bits);
- #else
- // FIX HACK TODO(bill): For some reason this causes a crash in *nix systems due to the reference counting
- // of the debug type information
- elements[0] = lb_debug_struct_field(m, str_lit("data"), t_rawptr, 0*int_bits);
- #endif
- elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, 1*int_bits);
- elements[2] = lb_debug_struct_field(m, str_lit("cap"), t_int, 2*int_bits);
- elements[3] = lb_debug_struct_field(m, str_lit("allocator"), t_allocator, 3*int_bits);
- break;
-
- case Type_Map:
- GB_ASSERT(t_raw_map != nullptr);
- bt = base_type(t_raw_map);
- /*fallthrough*/
- case Type_Struct:
- if (file == nullptr) {
- if (bt->Struct.node) {
- file = lb_get_llvm_metadata(m, bt->Struct.node->file());
- line_number = cast(unsigned)ast_token(bt->Struct.node).pos.line;
- }
- }
-
- type_set_offsets(bt);
- {
- isize element_offset = 0;
- record_scope = lb_get_llvm_metadata(m, bt->Struct.scope);
- switch (bt->Struct.soa_kind) {
- case StructSoa_Slice: element_offset = 1; break;
- case StructSoa_Dynamic: element_offset = 3; break;
- }
- element_count = cast(unsigned)(bt->Struct.fields.count + element_offset);
- elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
-
- isize field_size_bits = 8*type_size_of(bt) - element_offset*int_bits;
-
- switch (bt->Struct.soa_kind) {
- case StructSoa_Slice:
- elements[0] = LLVMDIBuilderCreateMemberType(
- m->debug_builder, record_scope,
- ".len", 4,
- file, 0,
- 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
- field_size_bits,
- LLVMDIFlagZero, lb_debug_type(m, t_int)
- );
- break;
- case StructSoa_Dynamic:
- elements[0] = LLVMDIBuilderCreateMemberType(
- m->debug_builder, record_scope,
- ".len", 4,
- file, 0,
- 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
- field_size_bits + 0*int_bits,
- LLVMDIFlagZero, lb_debug_type(m, t_int)
- );
- elements[1] = LLVMDIBuilderCreateMemberType(
- m->debug_builder, record_scope,
- ".cap", 4,
- file, 0,
- 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
- field_size_bits + 1*int_bits,
- LLVMDIFlagZero, lb_debug_type(m, t_int)
- );
- elements[2] = LLVMDIBuilderCreateMemberType(
- m->debug_builder, record_scope,
- ".allocator", 10,
- file, 0,
- 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
- field_size_bits + 2*int_bits,
- LLVMDIFlagZero, lb_debug_type(m, t_allocator)
- );
- break;
- }
-
- for_array(j, bt->Struct.fields) {
- Entity *f = bt->Struct.fields[j];
- String fname = f->token.string;
-
- unsigned field_line = 0;
- LLVMDIFlags field_flags = LLVMDIFlagZero;
- GB_ASSERT(bt->Struct.offsets != nullptr);
- u64 offset_in_bits = 8*cast(u64)bt->Struct.offsets[j];
-
- elements[element_offset+j] = LLVMDIBuilderCreateMemberType(
- m->debug_builder, record_scope,
- cast(char const *)fname.text, cast(size_t)fname.len,
- file, field_line,
- 8*cast(u64)type_size_of(f->type), 8*cast(u32)type_align_of(f->type),
- offset_in_bits,
- field_flags, lb_debug_type(m, f->type)
- );
- }
- }
- break;
- case Type_Union:
- {
- if (file == nullptr) {
- GB_ASSERT(bt->Union.node != nullptr);
- file = lb_get_llvm_metadata(m, bt->Union.node->file());
- line_number = cast(unsigned)ast_token(bt->Union.node).pos.line;
- }
-
- isize index_offset = 1;
- if (is_type_union_maybe_pointer(bt)) {
- index_offset = 0;
- }
- record_scope = lb_get_llvm_metadata(m, bt->Union.scope);
- element_count = cast(unsigned)bt->Union.variants.count;
- if (index_offset > 0) {
- element_count += 1;
- }
-
- elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
- if (index_offset > 0) {
- Type *tag_type = union_tag_type(bt);
- unsigned field_line = 0;
- u64 offset_in_bits = 8*cast(u64)bt->Union.variant_block_size;
- LLVMDIFlags field_flags = LLVMDIFlagZero;
-
- elements[0] = LLVMDIBuilderCreateMemberType(
- m->debug_builder, record_scope,
- "tag", 3,
- file, field_line,
- 8*cast(u64)type_size_of(tag_type), 8*cast(u32)type_align_of(tag_type),
- offset_in_bits,
- field_flags, lb_debug_type(m, tag_type)
- );
- }
-
- for_array(j, bt->Union.variants) {
- Type *variant = bt->Union.variants[j];
-
- unsigned field_index = cast(unsigned)(index_offset+j);
-
- char name[16] = {};
- gb_snprintf(name, gb_size_of(name), "v%u", field_index);
- isize name_len = gb_strlen(name);
-
- unsigned field_line = 0;
- LLVMDIFlags field_flags = LLVMDIFlagZero;
- u64 offset_in_bits = 0;
-
- elements[field_index] = LLVMDIBuilderCreateMemberType(
- m->debug_builder, record_scope,
- name, name_len,
- file, field_line,
- 8*cast(u64)type_size_of(variant), 8*cast(u32)type_align_of(variant),
- offset_in_bits,
- field_flags, lb_debug_type(m, variant)
- );
- }
- }
- break;
-
- case Type_BitSet:
- {
- if (file == nullptr) {
- GB_ASSERT(bt->BitSet.node != nullptr);
- file = lb_get_llvm_metadata(m, bt->BitSet.node->file());
- line_number = cast(unsigned)ast_token(bt->BitSet.node).pos.line;
- }
-
- LLVMMetadataRef bit_set_field_type = lb_debug_type(m, t_bool);
- LLVMMetadataRef scope = file;
-
- Type *elem = base_type(bt->BitSet.elem);
- if (elem->kind == Type_Enum) {
- element_count = cast(unsigned)elem->Enum.fields.count;
- elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
- for_array(i, elem->Enum.fields) {
- Entity *f = elem->Enum.fields[i];
- GB_ASSERT(f->kind == Entity_Constant);
- i64 val = exact_value_to_i64(f->Constant.value);
- String name = f->token.string;
- u64 offset_in_bits = cast(u64)(val - bt->BitSet.lower);
- elements[i] = LLVMDIBuilderCreateBitFieldMemberType(
- m->debug_builder,
- scope,
- cast(char const *)name.text, name.len,
- file, line_number,
- 1,
- offset_in_bits,
- 0,
- LLVMDIFlagZero,
- bit_set_field_type
- );
- }
- } else {
-
- char name[32] = {};
-
- GB_ASSERT(is_type_integer(elem));
- i64 count = bt->BitSet.upper - bt->BitSet.lower + 1;
- GB_ASSERT(0 <= count);
-
- element_count = cast(unsigned)count;
- elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
- for (unsigned i = 0; i < element_count; i++) {
- u64 offset_in_bits = i;
- i64 val = bt->BitSet.lower + cast(i64)i;
- gb_snprintf(name, gb_count_of(name), "%lld", cast(long long)val);
- elements[i] = LLVMDIBuilderCreateBitFieldMemberType(
- m->debug_builder,
- scope,
- name, gb_strlen(name),
- file, line_number,
- 1,
- offset_in_bits,
- 0,
- LLVMDIFlagZero,
- bit_set_field_type
- );
- }
- }
- }
- }
-
-
- LLVMMetadataRef final_metadata = nullptr;
- if (is_union) {
- final_metadata = LLVMDIBuilderCreateUnionType(
- m->debug_builder,
- parent_scope,
- cast(char const *)name.text, cast(size_t)name.len,
- file, line_number,
- size_in_bits, align_in_bits,
- flags,
- elements, element_count,
- runtime_lang,
- unique_id, unique_id_len
- );
- } else {
- final_metadata = LLVMDIBuilderCreateStructType(
- m->debug_builder,
- parent_scope,
- cast(char const *)name.text, cast(size_t)name.len,
- file, line_number,
- size_in_bits, align_in_bits,
- flags,
- derived_from,
- elements, element_count,
- runtime_lang,
- vtable_holder,
- unique_id, unique_id_len
- );
- }
-
- LLVMMetadataReplaceAllUsesWith(idt.metadata, final_metadata);
- lb_set_llvm_metadata(m, idt.type, final_metadata);
- } break;
- default:
- GB_PANIC("invalid incomplete debug type");
- break;
- }
- }
- array_clear(&m->debug_incomplete_types);
-}
-
-
-
gb_internal void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token) {
if (p->debug_info == nullptr) {
return;
@@ -1227,4 +1233,4 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
}
-} \ No newline at end of file
+}
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp
index 4675e203b..fcec59968 100644
--- a/src/llvm_backend_expr.cpp
+++ b/src/llvm_backend_expr.cpp
@@ -684,12 +684,6 @@ gb_internal lbValue lb_emit_matrix_flatten(lbProcedure *p, lbValue m, Type *type
Type *mt = base_type(m.type);
GB_ASSERT(mt->kind == Type_Matrix);
- // TODO(bill): Determine why this fails on Windows sometimes
- if (false && lb_is_matrix_simdable(mt)) {
- LLVMValueRef vector = lb_matrix_to_trimmed_vector(p, m);
- return lb_matrix_cast_vector_to_type(p, vector, type);
- }
-
lbAddr res = lb_add_local_generated(p, type, true);
i64 row_count = mt->Matrix.row_count;
@@ -763,6 +757,7 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs,
GB_ASSERT(is_type_matrix(yt));
GB_ASSERT(xt->Matrix.column_count == yt->Matrix.row_count);
GB_ASSERT(are_types_identical(xt->Matrix.elem, yt->Matrix.elem));
+ GB_ASSERT(xt->Matrix.is_row_major == yt->Matrix.is_row_major);
Type *elem = xt->Matrix.elem;
@@ -770,7 +765,7 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs,
unsigned inner = cast(unsigned)xt->Matrix.column_count;
unsigned outer_columns = cast(unsigned)yt->Matrix.column_count;
- if (lb_is_matrix_simdable(xt)) {
+ if (!xt->Matrix.is_row_major && lb_is_matrix_simdable(xt)) {
unsigned x_stride = cast(unsigned)matrix_type_stride_in_elems(xt);
unsigned y_stride = cast(unsigned)matrix_type_stride_in_elems(yt);
@@ -812,7 +807,7 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs,
return lb_addr_load(p, res);
}
- {
+ if (!xt->Matrix.is_row_major) {
lbAddr res = lb_add_local_generated(p, type, true);
auto inners = slice_make<lbValue[2]>(permanent_allocator(), inner);
@@ -836,6 +831,30 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs,
}
return lb_addr_load(p, res);
+ } else {
+ lbAddr res = lb_add_local_generated(p, type, true);
+
+ auto inners = slice_make<lbValue[2]>(permanent_allocator(), inner);
+
+ for (unsigned i = 0; i < outer_rows; i++) {
+ for (unsigned j = 0; j < outer_columns; j++) {
+ lbValue dst = lb_emit_matrix_epi(p, res.addr, i, j);
+ for (unsigned k = 0; k < inner; k++) {
+ inners[k][0] = lb_emit_matrix_ev(p, lhs, i, k);
+ inners[k][1] = lb_emit_matrix_ev(p, rhs, k, j);
+ }
+
+ lbValue sum = lb_const_nil(p->module, elem);
+ for (unsigned k = 0; k < inner; k++) {
+ lbValue a = inners[k][0];
+ lbValue b = inners[k][1];
+ sum = lb_emit_mul_add(p, a, b, sum, elem);
+ }
+ lb_emit_store(p, dst, sum);
+ }
+ }
+
+ return lb_addr_load(p, res);
}
}
@@ -855,7 +874,7 @@ gb_internal lbValue lb_emit_matrix_mul_vector(lbProcedure *p, lbValue lhs, lbVal
Type *elem = mt->Matrix.elem;
- if (lb_is_matrix_simdable(mt)) {
+ if (!mt->Matrix.is_row_major && lb_is_matrix_simdable(mt)) {
unsigned stride = cast(unsigned)matrix_type_stride_in_elems(mt);
unsigned row_count = cast(unsigned)mt->Matrix.row_count;
@@ -924,7 +943,7 @@ gb_internal lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbVal
Type *elem = mt->Matrix.elem;
- if (lb_is_matrix_simdable(mt)) {
+ if (!mt->Matrix.is_row_major && lb_is_matrix_simdable(mt)) {
unsigned stride = cast(unsigned)matrix_type_stride_in_elems(mt);
unsigned row_count = cast(unsigned)mt->Matrix.row_count;
@@ -1354,6 +1373,57 @@ gb_internal bool lb_is_empty_string_constant(Ast *expr) {
return false;
}
+gb_internal lbValue lb_build_binary_in(lbProcedure *p, lbValue left, lbValue right, TokenKind op) {
+ Type *rt = base_type(right.type);
+ if (is_type_pointer(rt)) {
+ right = lb_emit_load(p, right);
+ rt = base_type(type_deref(rt));
+ }
+
+ switch (rt->kind) {
+ case Type_Map:
+ {
+ lbValue map_ptr = lb_address_from_load_or_generate_local(p, right);
+ lbValue key = left;
+ lbValue ptr = lb_internal_dynamic_map_get_ptr(p, map_ptr, key);
+ if (op == Token_in) {
+ return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, ptr), t_bool);
+ } else {
+ return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_CmpEq, ptr), t_bool);
+ }
+ }
+ break;
+ case Type_BitSet:
+ {
+ Type *key_type = rt->BitSet.elem;
+ GB_ASSERT(are_types_identical(left.type, key_type));
+
+ Type *it = bit_set_to_int(rt);
+ left = lb_emit_conv(p, left, it);
+ if (is_type_different_to_arch_endianness(it)) {
+ left = lb_emit_byte_swap(p, left, integer_endian_type_to_platform_type(it));
+ }
+
+ lbValue lower = lb_const_value(p->module, left.type, exact_value_i64(rt->BitSet.lower));
+ lbValue key = lb_emit_arith(p, Token_Sub, left, lower, left.type);
+ lbValue bit = lb_emit_arith(p, Token_Shl, lb_const_int(p->module, left.type, 1), key, left.type);
+ bit = lb_emit_conv(p, bit, it);
+
+ lbValue old_value = lb_emit_transmute(p, right, it);
+ lbValue new_value = lb_emit_arith(p, Token_And, old_value, bit, it);
+
+ if (op == Token_in) {
+ return lb_emit_conv(p, lb_emit_comp(p, Token_NotEq, new_value, lb_const_int(p->module, new_value.type, 0)), t_bool);
+ } else {
+ return lb_emit_conv(p, lb_emit_comp(p, Token_CmpEq, new_value, lb_const_int(p->module, new_value.type, 0)), t_bool);
+ }
+ }
+ break;
+ }
+ GB_PANIC("Invalid 'in' type");
+ return {};
+}
+
gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
ast_node(be, BinaryExpr, expr);
@@ -1461,57 +1531,8 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
{
lbValue left = lb_build_expr(p, be->left);
lbValue right = lb_build_expr(p, be->right);
- Type *rt = base_type(right.type);
- if (is_type_pointer(rt)) {
- right = lb_emit_load(p, right);
- rt = base_type(type_deref(rt));
- }
-
- switch (rt->kind) {
- case Type_Map:
- {
- lbValue map_ptr = lb_address_from_load_or_generate_local(p, right);
- lbValue key = left;
- lbValue ptr = lb_internal_dynamic_map_get_ptr(p, map_ptr, key);
- if (be->op.kind == Token_in) {
- return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, ptr), t_bool);
- } else {
- return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_CmpEq, ptr), t_bool);
- }
- }
- break;
- case Type_BitSet:
- {
- Type *key_type = rt->BitSet.elem;
- GB_ASSERT(are_types_identical(left.type, key_type));
-
- Type *it = bit_set_to_int(rt);
- left = lb_emit_conv(p, left, it);
- if (is_type_different_to_arch_endianness(it)) {
- left = lb_emit_byte_swap(p, left, integer_endian_type_to_platform_type(it));
- }
-
- lbValue lower = lb_const_value(p->module, left.type, exact_value_i64(rt->BitSet.lower));
- lbValue key = lb_emit_arith(p, Token_Sub, left, lower, left.type);
- lbValue bit = lb_emit_arith(p, Token_Shl, lb_const_int(p->module, left.type, 1), key, left.type);
- bit = lb_emit_conv(p, bit, it);
-
- lbValue old_value = lb_emit_transmute(p, right, it);
- lbValue new_value = lb_emit_arith(p, Token_And, old_value, bit, it);
-
- if (be->op.kind == Token_in) {
- return lb_emit_conv(p, lb_emit_comp(p, Token_NotEq, new_value, lb_const_int(p->module, new_value.type, 0)), t_bool);
- } else {
- return lb_emit_conv(p, lb_emit_comp(p, Token_CmpEq, new_value, lb_const_int(p->module, new_value.type, 0)), t_bool);
- }
- }
- break;
- default:
- GB_PANIC("Invalid 'in' type");
- }
- break;
+ return lb_build_binary_in(p, left, right, be->op.kind);
}
- break;
default:
GB_PANIC("Invalid binary expression");
break;
@@ -1946,6 +1967,24 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
}
+ // bit_field <-> backing type
+ if (is_type_bit_field(src)) {
+ if (are_types_identical(src->BitField.backing_type, dst)) {
+ lbValue res = {};
+ res.type = t;
+ res.value = value.value;
+ return res;
+ }
+ }
+ if (is_type_bit_field(dst)) {
+ if (are_types_identical(src, dst->BitField.backing_type)) {
+ lbValue res = {};
+ res.type = t;
+ res.value = value.value;
+ return res;
+ }
+ }
+
// Pointer <-> uintptr
if (is_type_pointer(src) && is_type_uintptr(dst)) {
@@ -3657,7 +3696,7 @@ gb_internal void lb_build_addr_compound_lit_populate(lbProcedure *p, Slice<Ast *
Ast *elem = elems[i];
if (elem->kind == Ast_FieldValue) {
ast_node(fv, FieldValue, elem);
- if (lb_is_elem_const(fv->value, et)) {
+ if (bt->kind != Type_DynamicArray && lb_is_elem_const(fv->value, et)) {
continue;
}
if (is_ast_range(fv->field)) {
@@ -3953,12 +3992,21 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
}
lbValue index = lb_build_expr(p, ie->index);
index = lb_emit_conv(p, index, t_int);
- lbValue elem = lb_emit_matrix_ep(p, matrix, lb_const_int(p->module, t_int, 0), index);
+
+ isize bounds_len = 0;
+ lbValue elem = {};
+ if (t->Matrix.is_row_major) {
+ bounds_len = t->Matrix.row_count;
+ elem = lb_emit_matrix_ep(p, matrix, index, lb_const_int(p->module, t_int, 0));
+ } else {
+ bounds_len = t->Matrix.column_count;
+ elem = lb_emit_matrix_ep(p, matrix, lb_const_int(p->module, t_int, 0), index);
+ }
elem = lb_emit_conv(p, elem, alloc_type_pointer(type_of_expr(expr)));
auto index_tv = type_and_value_of_expr(ie->index);
if (index_tv.mode != Addressing_Constant) {
- lbValue len = lb_const_int(p->module, t_int, t->Matrix.column_count);
+ lbValue len = lb_const_int(p->module, t_int, bounds_len);
lb_emit_bounds_check(p, ast_token(ie->index), index, len);
}
return lb_addr(elem);
@@ -4217,6 +4265,38 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
switch (bt->kind) {
default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break;
+ case Type_BitField:
+ for (Ast *elem : cl->elems) {
+ ast_node(fv, FieldValue, elem);
+ String name = fv->field->Ident.token.string;
+ Selection sel = lookup_field(bt, name, false);
+ GB_ASSERT(sel.is_bit_field);
+ GB_ASSERT(!sel.indirect);
+ GB_ASSERT(sel.index.count == 1);
+ GB_ASSERT(sel.entity != nullptr);
+
+ i64 index = sel.index[0];
+ i64 bit_offset = 0;
+ i64 bit_size = -1;
+ for_array(i, bt->BitField.fields) {
+ Entity *f = bt->BitField.fields[i];
+ if (f == sel.entity) {
+ bit_offset = bt->BitField.bit_offsets[i];
+ bit_size = bt->BitField.bit_sizes[i];
+ break;
+ }
+ }
+ GB_ASSERT(bit_size > 0);
+
+ Type *field_type = sel.entity->type;
+ lbValue field_expr = lb_build_expr(p, fv->value);
+ field_expr = lb_emit_conv(p, field_expr, field_type);
+
+ lbAddr field_addr = lb_addr_bit_field(v.addr, field_type, index, bit_offset, bit_size);
+ lb_addr_store(p, field_addr, field_expr);
+ }
+ return v;
+
case Type_Struct: {
// TODO(bill): "constant" '#raw_union's are not initialized constantly at the moment.
// NOTE(bill): This is due to the layout of the unions when printed to LLVM-IR
@@ -4597,15 +4677,17 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
if (tav.mode == Addressing_Type) { // Addressing_Type
Selection sel = lookup_field(tav.type, selector, true);
if (sel.pseudo_field) {
- GB_ASSERT(sel.entity->kind == Entity_Procedure);
- return lb_addr(lb_find_value_from_entity(p->module, sel.entity));
+ GB_ASSERT(sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup);
+ Entity *e = entity_of_node(sel_node);
+ GB_ASSERT(e->kind == Entity_Procedure);
+ return lb_addr(lb_find_value_from_entity(p->module, e));
}
GB_PANIC("Unreachable %.*s", LIT(selector));
}
if (se->swizzle_count > 0) {
Type *array_type = base_type(type_deref(tav.type));
- GB_ASSERT(array_type->kind == Type_Array);
+ GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector);
u8 swizzle_count = se->swizzle_count;
u8 swizzle_indices_raw = se->swizzle_indices;
u8 swizzle_indices[4] = {};
@@ -4621,7 +4703,7 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
a = lb_addr_get_ptr(p, addr);
}
- GB_ASSERT(is_type_array(expr->tav.type));
+ GB_ASSERT(is_type_array(expr->tav.type) || is_type_simd_vector(expr->tav.type));
return lb_addr_swizzle(a, expr->tav.type, swizzle_count, swizzle_indices);
}
@@ -4634,6 +4716,33 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
return lb_addr(lb_find_value_from_entity(p->module, e));
}
+ if (sel.is_bit_field) {
+ lbAddr addr = lb_build_addr(p, se->expr);
+
+ Selection sub_sel = sel;
+ sub_sel.index.count -= 1;
+
+ lbValue ptr = lb_addr_get_ptr(p, addr);
+ if (sub_sel.index.count > 0) {
+ ptr = lb_emit_deep_field_gep(p, ptr, sub_sel);
+ }
+ if (is_type_pointer(type_deref(ptr.type))) {
+ ptr = lb_emit_load(p, ptr);
+ }
+
+ Type *bf_type = type_deref(ptr.type);
+ bf_type = base_type(bf_type);
+ GB_ASSERT(bf_type->kind == Type_BitField);
+
+ i32 index = sel.index[sel.index.count-1];
+
+ Entity *f = bf_type->BitField.fields[index];
+ u8 bit_size = bf_type->BitField.bit_sizes[index];
+ i64 bit_offset = bf_type->BitField.bit_offsets[index];
+
+ return lb_addr_bit_field(ptr, f->type, index, bit_offset, bit_size);
+ }
+
{
lbAddr addr = lb_build_addr(p, se->expr);
if (addr.kind == lbAddr_Map) {
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index f0f5327c6..0d8d9258a 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -81,7 +81,6 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
array_init(&m->global_procedures_and_types_to_create, a, 0, 1024);
array_init(&m->missing_procedures_to_check, a, 0, 16);
map_init(&m->debug_values);
- array_init(&m->debug_incomplete_types, a, 0, 1024);
string_map_init(&m->objc_classes);
string_map_init(&m->objc_selectors);
@@ -434,7 +433,7 @@ gb_internal lbAddr lb_addr_soa_variable(lbValue addr, lbValue index, Ast *index_
}
gb_internal lbAddr lb_addr_swizzle(lbValue addr, Type *array_type, u8 swizzle_count, u8 swizzle_indices[4]) {
- GB_ASSERT(is_type_array(array_type));
+ GB_ASSERT(is_type_array(array_type) || is_type_simd_vector(array_type));
GB_ASSERT(1 < swizzle_count && swizzle_count <= 4);
lbAddr v = {lbAddr_Swizzle, addr};
v.swizzle.type = array_type;
@@ -451,6 +450,20 @@ gb_internal lbAddr lb_addr_swizzle_large(lbValue addr, Type *array_type, Slice<i
return v;
}
+gb_internal lbAddr lb_addr_bit_field(lbValue addr, Type *type, i64 index, i64 bit_offset, i64 bit_size) {
+ GB_ASSERT(is_type_pointer(addr.type));
+ Type *mt = type_deref(addr.type);
+ GB_ASSERT_MSG(is_type_bit_field(mt), "%s", type_to_string(mt));
+
+ lbAddr v = {lbAddr_BitField, addr};
+ v.bitfield.type = type;
+ v.bitfield.index = index;
+ v.bitfield.bit_offset = bit_offset;
+ v.bitfield.bit_size = bit_size;
+ return v;
+}
+
+
gb_internal Type *lb_addr_type(lbAddr const &addr) {
if (addr.addr.value == nullptr) {
return nullptr;
@@ -759,7 +772,17 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
addr = lb_addr(lb_address_from_load(p, lb_addr_load(p, addr)));
}
- if (addr.kind == lbAddr_RelativePointer) {
+ if (addr.kind == lbAddr_BitField) {
+ lbValue dst = addr.addr;
+
+ auto args = array_make<lbValue>(temporary_allocator(), 4);
+ args[0] = dst;
+ args[1] = lb_address_from_load_or_generate_local(p, value);
+ args[2] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset);
+ args[3] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size);
+ lb_emit_runtime_call(p, "__write_bits", args);
+ return;
+ } else if (addr.kind == lbAddr_RelativePointer) {
Type *rel_ptr = base_type(lb_addr_type(addr));
GB_ASSERT(rel_ptr->kind == Type_RelativePointer ||
rel_ptr->kind == Type_RelativeMultiPointer);
@@ -1074,8 +1097,31 @@ gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue value) {
gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
GB_ASSERT(addr.addr.value != nullptr);
+ if (addr.kind == lbAddr_BitField) {
+ lbAddr dst = lb_add_local_generated(p, addr.bitfield.type, true);
+ lbValue src = addr.addr;
- if (addr.kind == lbAddr_RelativePointer) {
+ auto args = array_make<lbValue>(temporary_allocator(), 4);
+ args[0] = dst.addr;
+ args[1] = src;
+ args[2] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset);
+ args[3] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size);
+ lb_emit_runtime_call(p, "__read_bits", args);
+
+ lbValue r = lb_addr_load(p, dst);
+
+ if (!is_type_unsigned(core_type(addr.bitfield.type))) {
+ // Sign extension
+ // m := 1<<(bit_size-1)
+ // r = (r XOR m) - m
+ Type *t = addr.bitfield.type;
+ lbValue m = lb_const_int(p->module, t, 1ull<<(addr.bitfield.bit_size-1));
+ r = lb_emit_arith(p, Token_Xor, r, m, t);
+ r = lb_emit_arith(p, Token_Sub, r, m, t);
+ }
+
+ return r;
+ } else if (addr.kind == lbAddr_RelativePointer) {
Type *rel_ptr = base_type(lb_addr_type(addr));
Type *base_integer = nullptr;
Type *pointer_type = nullptr;
@@ -1217,6 +1263,30 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
return lb_addr_load(p, res);
} else if (addr.kind == lbAddr_Swizzle) {
Type *array_type = base_type(addr.swizzle.type);
+ if (array_type->kind == Type_SimdVector) {
+ lbValue vec = lb_emit_load(p, addr.addr);
+ u8 index_count = addr.swizzle.count;
+ if (index_count == 0) {
+ return vec;
+ }
+
+ unsigned mask_len = cast(unsigned)index_count;
+ LLVMValueRef *mask_elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, index_count);
+ for (isize i = 0; i < index_count; i++) {
+ mask_elems[i] = LLVMConstInt(lb_type(p->module, t_u32), addr.swizzle.indices[i], false);
+ }
+
+ LLVMValueRef mask = LLVMConstVector(mask_elems, mask_len);
+
+ LLVMValueRef v1 = vec.value;
+ LLVMValueRef v2 = vec.value;
+
+ lbValue res = {};
+ res.type = addr.swizzle.type;
+ res.value = LLVMBuildShuffleVector(p->builder, v1, v2, mask, "");
+ return res;
+ }
+
GB_ASSERT(array_type->kind == Type_Array);
unsigned res_align = cast(unsigned)type_align_of(addr.swizzle.type);
@@ -1444,9 +1514,11 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur
GB_ASSERT(scope->flags & ScopeFlag_Proc);
proc = scope->procedure_entity;
}
- GB_ASSERT(proc->kind == Entity_Procedure);
- if (proc->code_gen_procedure != nullptr) {
- p = proc->code_gen_procedure;
+ if (proc != nullptr) {
+ GB_ASSERT(proc->kind == Entity_Procedure);
+ if (proc->code_gen_procedure != nullptr) {
+ p = proc->code_gen_procedure;
+ }
}
}
@@ -2048,16 +2120,18 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
array_add(&fields, padding_type);
}
- i64 padding_offset = 0;
+ i64 prev_offset = 0;
for (i32 field_index : struct_fields_index_by_increasing_offset(temporary_allocator(), type)) {
Entity *field = type->Struct.fields[field_index];
- i64 padding = type->Struct.offsets[field_index] - padding_offset;
+ i64 offset = type->Struct.offsets[field_index];
+ GB_ASSERT(offset >= prev_offset);
+ i64 padding = offset - prev_offset;
if (padding != 0) {
LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, type_align_of(field->type));
array_add(&fields, padding_type);
}
-
+
field_remapping[field_index] = cast(i32)fields.count;
Type *field_type = field->type;
@@ -2068,14 +2142,11 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
}
array_add(&fields, lb_type(m, field_type));
-
- if (!type->Struct.is_packed) {
- padding_offset = align_formula(padding_offset, type_align_of(field->type));
- }
- padding_offset += type_size_of(field->type);
+
+ prev_offset = offset + type_size_of(field->type);
}
- i64 end_padding = full_type_size-padding_offset;
+ i64 end_padding = full_type_size-prev_offset;
if (end_padding > 0) {
array_add(&fields, lb_type_padding_filler(m, end_padding, 1));
}
@@ -2216,7 +2287,9 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
}
return LLVMStructTypeInContext(ctx, fields, field_count, false);
}
-
+
+ case Type_BitField:
+ return lb_type_internal(m, type->BitField.backing_type);
}
GB_PANIC("Invalid type %s", type_to_string(type));
diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp
index b57e74799..6a6d2f802 100644
--- a/src/llvm_backend_opt.cpp
+++ b/src/llvm_backend_opt.cpp
@@ -380,9 +380,19 @@ gb_internal void lb_run_remove_dead_instruction_pass(lbProcedure *p) {
}
}
-gb_internal LLVMValueRef lb_run_instrumentation_pass_insert_call(lbProcedure *p, Entity *entity, LLVMBuilderRef dummy_builder) {
+gb_internal LLVMValueRef lb_run_instrumentation_pass_insert_call(lbProcedure *p, Entity *entity, LLVMBuilderRef dummy_builder, bool is_enter) {
lbModule *m = p->module;
+ if (p->debug_info != nullptr) {
+ TokenPos pos = {};
+ if (is_enter) {
+ pos = ast_token(p->body).pos;
+ } else {
+ pos = ast_end_token(p->body).pos;
+ }
+ LLVMSetCurrentDebugLocation2(dummy_builder, lb_debug_location_from_token_pos(p, pos));
+ }
+
lbValue cc = lb_find_procedure_value_from_entity(m, entity);
LLVMValueRef args[3] = {};
@@ -430,7 +440,7 @@ gb_internal void lb_run_instrumentation_pass(lbProcedure *p) {
LLVMBasicBlockRef entry_bb = p->entry_block->block;
LLVMPositionBuilder(dummy_builder, entry_bb, LLVMGetFirstInstruction(entry_bb));
- lb_run_instrumentation_pass_insert_call(p, enter, dummy_builder);
+ lb_run_instrumentation_pass_insert_call(p, enter, dummy_builder, true);
LLVMRemoveStringAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, LLVM_V_NAME("instrument-function-entry"));
unsigned bb_count = LLVMCountBasicBlocks(p->value);
@@ -451,7 +461,7 @@ gb_internal void lb_run_instrumentation_pass(lbProcedure *p) {
LLVMPositionBuilderBefore(dummy_builder, terminator);
- lb_run_instrumentation_pass_insert_call(p, exit, dummy_builder);
+ lb_run_instrumentation_pass_insert_call(p, exit, dummy_builder, false);
}
LLVMRemoveStringAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, LLVM_V_NAME("instrument-function-exit"));
@@ -471,6 +481,8 @@ gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedur
// are not removed
lb_run_remove_dead_instruction_pass(p);
+ lb_run_instrumentation_pass(p);
+
switch (pass_manager_kind) {
case lbFunctionPassManager_none:
return;
@@ -481,7 +493,6 @@ gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedur
}
break;
}
- lb_run_instrumentation_pass(p);
LLVMRunFunctionPassManager(fpm, p->value);
}
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index 09bebd0cf..bb4aed3f1 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -578,7 +578,10 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
defer (param_index += 1);
if (arg_type->kind == lbArg_Ignore) {
- continue;
+ // Even though it is an ignored argument, it might still be referenced in the
+ // body.
+ lbValue dummy = lb_add_local_generated(p, e->type, false).addr;
+ lb_add_entity(p->module, e, dummy);
} else if (arg_type->kind == lbArg_Direct) {
if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) {
LLVMTypeRef param_type = lb_type(p->module, e->type);
@@ -1051,6 +1054,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
Type *original_type = e->type;
lbArgType *arg = &ft->args[param_index];
if (arg->kind == lbArg_Ignore) {
+ param_index += 1;
continue;
}
@@ -1399,8 +1403,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
return res;
case BuiltinProc_simd_min:
if (is_float) {
- LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOLT, arg0.value, arg1.value, "");
- res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
+ return lb_emit_min(p, res.type, arg0, arg1);
} else {
LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSLT : LLVMIntULT, arg0.value, arg1.value, "");
res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
@@ -1408,8 +1411,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
return res;
case BuiltinProc_simd_max:
if (is_float) {
- LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOGT, arg0.value, arg1.value, "");
- res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
+ return lb_emit_max(p, res.type, arg0, arg1);
} else {
LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSGT : LLVMIntUGT, arg0.value, arg1.value, "");
res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
@@ -1693,24 +1695,61 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name.string;
- GB_ASSERT(name == "location");
- String procedure = p->entity->token.string;
- TokenPos pos = ast_token(ce->proc).pos;
- if (ce->args.count > 0) {
- Ast *ident = unselector_expr(ce->args[0]);
- GB_ASSERT(ident->kind == Ast_Ident);
- Entity *e = entity_of_node(ident);
- GB_ASSERT(e != nullptr);
-
- if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) {
- procedure = e->parent_proc_decl->entity->token.string;
- } else {
- procedure = str_lit("");
+ if (name == "location") {
+ String procedure = p->entity->token.string;
+ TokenPos pos = ast_token(ce->proc).pos;
+ if (ce->args.count > 0) {
+ Ast *ident = unselector_expr(ce->args[0]);
+ GB_ASSERT(ident->kind == Ast_Ident);
+ Entity *e = entity_of_node(ident);
+ GB_ASSERT(e != nullptr);
+
+ if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) {
+ procedure = e->parent_proc_decl->entity->token.string;
+ } else {
+ procedure = str_lit("");
+ }
+ pos = e->token.pos;
+
+ }
+ return lb_emit_source_code_location_as_global(p, procedure, pos);
+ } else if (name == "load_directory") {
+ lbModule *m = p->module;
+ TEMPORARY_ALLOCATOR_GUARD();
+ LoadDirectoryCache *cache = map_must_get(&m->info->load_directory_map, expr);
+ isize count = cache->files.count;
+
+ LLVMValueRef *elements = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
+ for_array(i, cache->files) {
+ LoadFileCache *file = cache->files[i];
+
+ String file_name = filename_without_directory(file->path);
+
+ LLVMValueRef values[2] = {};
+ values[0] = lb_const_string(m, file_name).value;
+ values[1] = lb_const_string(m, file->data).value;
+ LLVMValueRef element = llvm_const_named_struct(m, t_load_directory_file, values, gb_count_of(values));
+ elements[i] = element;
}
- pos = e->token.pos;
+ LLVMValueRef backing_array = llvm_const_array(lb_type(m, t_load_directory_file), elements, count);
+
+ Type *array_type = alloc_type_array(t_load_directory_file, count);
+ lbAddr backing_array_addr = lb_add_global_generated(m, array_type, {backing_array, array_type}, nullptr);
+ lb_make_global_private_const(backing_array_addr);
+
+ LLVMValueRef backing_array_ptr = backing_array_addr.addr.value;
+ backing_array_ptr = LLVMConstPointerCast(backing_array_ptr, lb_type(m, t_load_directory_file_ptr));
+
+ LLVMValueRef const_slice = llvm_const_slice_internal(m, backing_array_ptr, LLVMConstInt(lb_type(m, t_int), count, false));
+
+ lbAddr addr = lb_add_global_generated(p->module, tv.type, {const_slice, t_load_directory_file_slice}, nullptr);
+ lb_make_global_private_const(addr);
+
+ return lb_addr_load(p, addr);
+ } else {
+ GB_PANIC("UNKNOWN DIRECTIVE: %.*s", LIT(name));
}
- return lb_emit_source_code_location_as_global(p, procedure, pos);
}
case BuiltinProc_type_info_of: {
@@ -1718,7 +1757,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
TypeAndValue tav = type_and_value_of_expr(arg);
if (tav.mode == Addressing_Type) {
Type *t = default_type(type_of_expr(arg));
- return lb_type_info(p->module, t);
+ return lb_type_info(p, t);
}
GB_ASSERT(is_type_typeid(tav.type));
@@ -2033,9 +2072,9 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
case BuiltinProc_clamp:
return lb_emit_clamp(p, type_of_expr(expr),
- lb_build_expr(p, ce->args[0]),
- lb_build_expr(p, ce->args[1]),
- lb_build_expr(p, ce->args[2]));
+ lb_build_expr(p, ce->args[0]),
+ lb_build_expr(p, ce->args[1]),
+ lb_build_expr(p, ce->args[2]));
case BuiltinProc_soa_zip:
@@ -3020,9 +3059,6 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
case BuiltinProc_wasm_memory_atomic_wait32:
{
char const *name = "llvm.wasm.memory.atomic.wait32";
- LLVMTypeRef types[1] = {
- lb_type(p->module, t_u32),
- };
Type *t_u32_ptr = alloc_type_pointer(t_u32);
@@ -3033,26 +3069,24 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
lbValue res = {};
res.type = tv.type;
- res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), nullptr, 0);
return res;
}
case BuiltinProc_wasm_memory_atomic_notify32:
{
char const *name = "llvm.wasm.memory.atomic.notify";
- LLVMTypeRef types[1] = {
- lb_type(p->module, t_u32),
- };
Type *t_u32_ptr = alloc_type_pointer(t_u32);
LLVMValueRef args[2] = {
- lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value,
- lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value };
+ lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value,
+ lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value
+ };
lbValue res = {};
res.type = tv.type;
- res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), nullptr, 0);
return res;
}
@@ -3324,9 +3358,12 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
for (Ast *var_arg : variadic) {
lbValue arg = lb_build_expr(p, var_arg);
if (is_type_any(elem_type)) {
- array_add(&args, lb_emit_conv(p, arg, default_type(arg.type)));
+ if (is_type_untyped_nil(arg.type)) {
+ arg = lb_const_nil(p->module, t_rawptr);
+ }
+ array_add(&args, lb_emit_conv(p, arg, c_vararg_promote_type(default_type(arg.type))));
} else {
- array_add(&args, lb_emit_conv(p, arg, elem_type));
+ array_add(&args, lb_emit_conv(p, arg, c_vararg_promote_type(elem_type)));
}
}
break;
@@ -3388,6 +3425,30 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
if (e->kind == Entity_TypeName) {
lbValue value = lb_const_nil(p->module, e->type);
args[param_index] = value;
+ } else if (is_c_vararg && pt->variadic && pt->variadic_index == param_index) {
+ GB_ASSERT(param_index == pt->param_count-1);
+ Type *slice_type = e->type;
+ GB_ASSERT(slice_type->kind == Type_Slice);
+ Type *elem_type = slice_type->Slice.elem;
+
+ if (fv->value->kind == Ast_CompoundLit) {
+ ast_node(literal, CompoundLit, fv->value);
+ for (Ast *var_arg : literal->elems) {
+ lbValue arg = lb_build_expr(p, var_arg);
+ if (is_type_any(elem_type)) {
+ if (is_type_untyped_nil(arg.type)) {
+ arg = lb_const_nil(p->module, t_rawptr);
+ }
+ array_add(&args, lb_emit_conv(p, arg, c_vararg_promote_type(default_type(arg.type))));
+ } else {
+ array_add(&args, lb_emit_conv(p, arg, c_vararg_promote_type(elem_type)));
+ }
+ }
+ } else {
+ lbValue value = lb_build_expr(p, fv->value);
+ GB_ASSERT(!is_type_tuple(value.type));
+ array_add(&args, lb_emit_conv(p, value, c_vararg_promote_type(value.type)));
+ }
} else {
lbValue value = lb_build_expr(p, fv->value);
GB_ASSERT(!is_type_tuple(value.type));
diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp
index 002fef881..24dd321f6 100644
--- a/src/llvm_backend_stmt.cpp
+++ b/src/llvm_backend_stmt.cpp
@@ -737,6 +737,22 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
lb_start_block(p, done);
}
+gb_internal lbValue lb_enum_values_slice(lbProcedure *p, Type *enum_type, i64 *enum_count_) {
+ Type *t = enum_type;
+ GB_ASSERT(is_type_enum(t));
+ t = base_type(t);
+ GB_ASSERT(t->kind == Type_Enum);
+ i64 enum_count = t->Enum.fields.count;
+
+ if (enum_count_) *enum_count_ = enum_count;
+
+ lbValue ti = lb_type_info(p, t);
+ lbValue variant = lb_emit_struct_ep(p, ti, 4);
+ lbValue eti_ptr = lb_emit_conv(p, variant, t_type_info_enum_ptr);
+ lbValue values = lb_emit_load(p, lb_emit_struct_ep(p, eti_ptr, 2));
+ return values;
+}
+
gb_internal void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_type, lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
lbModule *m = p->module;
@@ -744,15 +760,11 @@ gb_internal void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_
GB_ASSERT(is_type_enum(t));
t = base_type(t);
Type *core_elem = core_type(t);
- GB_ASSERT(t->kind == Type_Enum);
- i64 enum_count = t->Enum.fields.count;
- lbValue max_count = lb_const_int(m, t_int, enum_count);
+ i64 enum_count = 0;
- lbValue ti = lb_type_info(m, t);
- lbValue variant = lb_emit_struct_ep(p, ti, 4);
- lbValue eti_ptr = lb_emit_conv(p, variant, t_type_info_enum_ptr);
- lbValue values = lb_emit_load(p, lb_emit_struct_ep(p, eti_ptr, 2));
+ lbValue values = lb_enum_values_slice(p, enum_type, &enum_count);
lbValue values_data = lb_slice_elem(p, values);
+ lbValue max_count = lb_const_int(m, t_int, enum_count);
lbAddr offset_ = lb_add_local_generated(p, t_int, false);
lb_addr_store(p, offset_, lb_const_int(m, t_int, 0));
@@ -1052,6 +1064,74 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
case Type_Tuple:
lb_build_range_tuple(p, expr, val0_type, val1_type, &val, &key, &loop, &done);
break;
+
+ case Type_BitSet: {
+ lbModule *m = p->module;
+
+ lbValue the_set = lb_build_expr(p, expr);
+ if (is_type_pointer(type_deref(the_set.type))) {
+ the_set = lb_emit_load(p, the_set);
+ }
+
+ Type *elem = et->BitSet.elem;
+ if (is_type_enum(elem)) {
+ i64 enum_count = 0;
+ lbValue values = lb_enum_values_slice(p, elem, &enum_count);
+ lbValue values_data = lb_slice_elem(p, values);
+ lbValue max_count = lb_const_int(m, t_int, enum_count);
+
+ lbAddr offset_ = lb_add_local_generated(p, t_int, false);
+ lb_addr_store(p, offset_, lb_const_int(m, t_int, 0));
+
+ loop = lb_create_block(p, "for.bit_set.enum.loop");
+ lb_emit_jump(p, loop);
+ lb_start_block(p, loop);
+
+ lbBlock *body_check = lb_create_block(p, "for.bit_set.enum.body-check");
+ lbBlock *body = lb_create_block(p, "for.bit_set.enum.body");
+ done = lb_create_block(p, "for.bit_set.enum.done");
+
+ lbValue offset = lb_addr_load(p, offset_);
+ lbValue cond = lb_emit_comp(p, Token_Lt, offset, max_count);
+ lb_emit_if(p, cond, body_check, done);
+ lb_start_block(p, body_check);
+
+ lbValue val_ptr = lb_emit_ptr_offset(p, values_data, offset);
+ lb_emit_increment(p, offset_.addr);
+ val = lb_emit_load(p, val_ptr);
+ val = lb_emit_conv(p, val, elem);
+
+ lbValue check = lb_build_binary_in(p, val, the_set, Token_in);
+ lb_emit_if(p, check, body, loop);
+ lb_start_block(p, body);
+ } else {
+ lbAddr offset_ = lb_add_local_generated(p, t_int, false);
+ lb_addr_store(p, offset_, lb_const_int(m, t_int, et->BitSet.lower));
+
+ lbValue max_count = lb_const_int(m, t_int, et->BitSet.upper);
+
+ loop = lb_create_block(p, "for.bit_set.range.loop");
+ lb_emit_jump(p, loop);
+ lb_start_block(p, loop);
+
+ lbBlock *body_check = lb_create_block(p, "for.bit_set.range.body-check");
+ lbBlock *body = lb_create_block(p, "for.bit_set.range.body");
+ done = lb_create_block(p, "for.bit_set.range.done");
+
+ lbValue offset = lb_addr_load(p, offset_);
+ lbValue cond = lb_emit_comp(p, Token_LtEq, offset, max_count);
+ lb_emit_if(p, cond, body_check, done);
+ lb_start_block(p, body_check);
+
+ val = lb_emit_conv(p, offset, elem);
+ lb_emit_increment(p, offset_.addr);
+
+ lbValue check = lb_build_binary_in(p, val, the_set, Token_in);
+ lb_emit_if(p, check, body, loop);
+ lb_start_block(p, body);
+ }
+ break;
+ }
default:
GB_PANIC("Cannot range over %s", type_to_string(expr_type));
break;
@@ -1454,7 +1534,7 @@ gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *
lb_close_scope(p, lbDeferExit_Default, done);
}
-gb_internal void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value) {
+gb_internal void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value, bool is_default_case) {
Entity *e = implicit_entity_of_node(clause);
GB_ASSERT(e != nullptr);
if (e->flags & EntityFlag_Value) {
@@ -1463,8 +1543,9 @@ gb_internal void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValu
lbAddr x = lb_add_local(p, e->type, e, false);
lb_addr_store(p, x, value);
} else {
- // by reference
- GB_ASSERT(are_types_identical(e->type, type_deref(value.type)));
+ if (!is_default_case) {
+ GB_ASSERT_MSG(are_types_identical(e->type, type_deref(value.type)), "%s %s", type_to_string(e->type), type_to_string(value.type));
+ }
lb_add_entity(p->module, e, value);
}
}
@@ -1622,7 +1703,7 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
lb_open_scope(p, cc->scope);
if (cc->list.count == 0) {
lb_start_block(p, default_block);
- lb_store_type_case_implicit(p, clause, parent_value);
+ lb_store_type_case_implicit(p, clause, parent_value, true);
lb_type_case_body(p, ss->label, clause, p->curr_block, done);
continue;
}
@@ -1688,7 +1769,7 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
lb_add_entity(p->module, case_entity, ptr);
lb_add_debug_local_variable(p, ptr.value, case_entity->type, case_entity->token);
} else {
- lb_store_type_case_implicit(p, clause, parent_value);
+ lb_store_type_case_implicit(p, clause, parent_value, false);
}
lb_type_case_body(p, ss->label, clause, body, done);
@@ -1843,7 +1924,11 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) {
lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
- LLVMBuildRetVoid(p->builder);
+ // Check for terminator in the defer stmts
+ LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
+ if (!lb_is_instr_terminating(instr)) {
+ LLVMBuildRetVoid(p->builder);
+ }
} else {
LLVMValueRef ret_val = res.value;
LLVMTypeRef ret_type = p->abi_function_type->ret.type;
@@ -1868,7 +1953,12 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) {
}
lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
- LLVMBuildRet(p->builder, ret_val);
+
+ // Check for terminator in the defer stmts
+ LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
+ if (!lb_is_instr_terminating(instr)) {
+ LLVMBuildRet(p->builder, ret_val);
+ }
}
}
gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
@@ -1887,8 +1977,12 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return
// No return values
lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
-
- LLVMBuildRetVoid(p->builder);
+
+ // Check for terminator in the defer stmts
+ LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
+ if (!lb_is_instr_terminating(instr)) {
+ LLVMBuildRetVoid(p->builder);
+ }
return;
} else if (return_count == 1) {
Entity *e = tuple->variables[0];
diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp
index e291e40a5..93e2874a5 100644
--- a/src/llvm_backend_type.cpp
+++ b/src/llvm_backend_type.cpp
@@ -62,6 +62,7 @@ gb_internal u64 lb_typeid_kind(lbModule *m, Type *type, u64 id=0) {
case Type_RelativePointer: kind = Typeid_Relative_Pointer; break;
case Type_RelativeMultiPointer: kind = Typeid_Relative_Multi_Pointer; break;
case Type_SoaPointer: kind = Typeid_SoaPointer; break;
+ case Type_BitField: kind = Typeid_Bit_Field; break;
}
return kind;
@@ -110,16 +111,19 @@ gb_internal lbValue lb_typeid(lbModule *m, Type *type) {
return res;
}
-gb_internal lbValue lb_type_info(lbModule *m, Type *type) {
+gb_internal lbValue lb_type_info(lbProcedure *p, Type *type) {
GB_ASSERT(!build_context.no_rtti);
type = default_type(type);
+ lbModule *m = p->module;
isize index = lb_type_info_index(m->info, type);
GB_ASSERT(index >= 0);
- lbValue data = lb_global_type_info_data_ptr(m);
- return lb_emit_array_epi(m, data, index);
+ lbValue global = lb_global_type_info_data_ptr(m);
+
+ lbValue ptr = lb_emit_array_epi(p, global, index);
+ return lb_emit_load(p, ptr);
}
gb_internal LLVMTypeRef lb_get_procedure_raw_type(lbModule *m, Type *type) {
@@ -180,16 +184,7 @@ gb_internal lbValue lb_type_info_member_tags_offset(lbModule *m, isize count, i6
return offset;
}
-// enum {LB_USE_GIANT_PACKED_STRUCT = LB_USE_NEW_PASS_SYSTEM};
-enum {LB_USE_GIANT_PACKED_STRUCT = 0};
-
-gb_internal LLVMTypeRef lb_setup_type_info_data_internal_type(lbModule *m, isize max_type_info_count) {
- if (!LB_USE_GIANT_PACKED_STRUCT) {
- Type *t = alloc_type_array(t_type_info, max_type_info_count);
- return lb_type(m, t);
- }
- CheckerInfo *info = m->gen->info;
-
+gb_internal LLVMTypeRef *lb_setup_modified_types_for_type_info(lbModule *m, isize max_type_info_count) {
LLVMTypeRef *element_types = gb_alloc_array(heap_allocator(), LLVMTypeRef, max_type_info_count);
defer (gb_free(heap_allocator(), element_types));
@@ -210,7 +205,7 @@ gb_internal LLVMTypeRef lb_setup_type_info_data_internal_type(lbModule *m, isize
stypes[1] = lb_type(m, tibt->Struct.fields[1]->type);
stypes[2] = lb_type(m, tibt->Struct.fields[2]->type);
isize variant_index = 0;
- if (build_context.int_size == 8) {
+ if (build_context.ptr_size == 8) {
stypes[3] = lb_type(m, t_i32); // padding
stypes[4] = lb_type(m, tibt->Struct.fields[3]->type);
variant_index = 5;
@@ -219,8 +214,8 @@ gb_internal LLVMTypeRef lb_setup_type_info_data_internal_type(lbModule *m, isize
variant_index = 4;
}
- LLVMTypeRef modified_types[32] = {};
- GB_ASSERT(gb_count_of(modified_types) >= ut->Union.variants.count);
+ LLVMTypeRef *modified_types = gb_alloc_array(heap_allocator(), LLVMTypeRef, Typeid__COUNT);
+ GB_ASSERT(Typeid__COUNT == ut->Union.variants.count);
modified_types[0] = element_types[0];
i64 tag_offset = ut->Union.variant_block_size;
@@ -242,40 +237,24 @@ gb_internal LLVMTypeRef lb_setup_type_info_data_internal_type(lbModule *m, isize
modified_types[i] = modified_type;
}
- for_array(type_info_type_index, info->type_info_types) {
- Type *t = info->type_info_types[type_info_type_index];
- if (t == nullptr || t == t_invalid) {
- continue;
- }
- isize entry_index = lb_type_info_index(info, t, false);
- if (entry_index <= 0) {
- continue;
- }
-
- if (entries_handled[entry_index]) {
- continue;
- }
- entries_handled[entry_index] = true;
-
-
- if (t->kind == Type_Named) {
- element_types[entry_index] = modified_types[0];
- } else {
- i64 variant_index = lb_typeid_kind(m, t);
- element_types[entry_index] = modified_types[variant_index];
- }
-
- GB_ASSERT(element_types[entry_index] != nullptr);
- }
-
- for_array(i, entries_handled) {
- GB_ASSERT(entries_handled[i]);
+ for (isize i = 0; i < Typeid__COUNT; i++) {
+ GB_ASSERT_MSG(modified_types[i] != nullptr, "%td", ut->Union.variants.count);
}
- return LLVMStructType(element_types, cast(unsigned)max_type_info_count, true);
+ return modified_types;
}
-gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 global_type_info_data_entity_count, lbProcedure *p) { // NOTE(bill): Setup type_info data
+gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_type_info_data_entity_count) { // NOTE(bill): Setup type_info data
+ auto const &ADD_GLOBAL_TYPE_INFO_ENTRY = [](lbModule *m, LLVMTypeRef type, isize index) -> LLVMValueRef {
+ char name[64] = {};
+ gb_snprintf(name, 63, "__$ti-%lld", cast(long long)index);
+ LLVMValueRef g = LLVMAddGlobal(m->mod, type, name);
+ LLVMSetLinkage(g, LLVMInternalLinkage);
+ LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr);
+ LLVMSetGlobalConstant(g, true);
+ return g;
+ };
+
CheckerInfo *info = m->info;
// Useful types
@@ -292,19 +271,49 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
defer (gb_free(heap_allocator(), entries_handled.data));
entries_handled[0] = true;
- LLVMValueRef giant_struct = lb_global_type_info_data_ptr(m).value;
- LLVMTypeRef giant_struct_type = LLVMGlobalGetValueType(giant_struct);
- GB_ASSERT(LLVMGetTypeKind(giant_struct_type) == LLVMStructTypeKind);
-
LLVMValueRef *giant_const_values = gb_alloc_array(heap_allocator(), LLVMValueRef, global_type_info_data_entity_count);
defer (gb_free(heap_allocator(), giant_const_values));
- giant_const_values[0] = LLVMConstNull(LLVMStructGetTypeAtIndex(giant_struct_type, 0));
+ // zero value is just zero data
+ giant_const_values[0] = ADD_GLOBAL_TYPE_INFO_ENTRY(m, lb_type(m, t_type_info), 0);
+ LLVMSetInitializer(giant_const_values[0], LLVMConstNull(lb_type(m, t_type_info)));
+
+
+ LLVMTypeRef *modified_types = lb_setup_modified_types_for_type_info(m, global_type_info_data_entity_count);
+ defer (gb_free(heap_allocator(), modified_types));
+ for_array(type_info_type_index, info->type_info_types) {
+ Type *t = info->type_info_types[type_info_type_index];
+ if (t == nullptr || t == t_invalid) {
+ continue;
+ }
+
+ isize entry_index = lb_type_info_index(info, t, false);
+ if (entry_index <= 0) {
+ continue;
+ }
+
+ if (entries_handled[entry_index]) {
+ continue;
+ }
+ entries_handled[entry_index] = true;
+
+
+ LLVMTypeRef stype = nullptr;
+ if (t->kind == Type_Named) {
+ stype = modified_types[0];
+ } else {
+ stype = modified_types[lb_typeid_kind(m, t)];
+ }
+ giant_const_values[entry_index] = ADD_GLOBAL_TYPE_INFO_ENTRY(m, stype, entry_index);
+ }
+ for (isize i = 1; i < global_type_info_data_entity_count; i++) {
+ entries_handled[i] = false;
+ }
+
LLVMValueRef *small_const_values = gb_alloc_array(heap_allocator(), LLVMValueRef, 6);
defer (gb_free(heap_allocator(), small_const_values));
-
#define type_info_allocate_values(name) \
LLVMValueRef *name##_values = gb_alloc_array(heap_allocator(), LLVMValueRef, type_deref(name.addr.type)->Array.count); \
defer (gb_free(heap_allocator(), name##_values)); \
@@ -316,7 +325,7 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
(name##_values)[i] = LLVMConstNull(elem); \
} \
} \
- LLVMSetInitializer(name.addr.value, llvm_const_array(elem, name##_values, at->Array.count)); \
+ LLVMSetInitializer(name.addr.value, llvm_const_array(elem, name##_values, at->Array.count)); \
})
type_info_allocate_values(lb_global_type_info_member_types);
@@ -326,27 +335,13 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
type_info_allocate_values(lb_global_type_info_member_tags);
- i64 const type_info_struct_size = type_size_of(t_type_info);
- LLVMTypeRef llvm_u8 = lb_type(m, t_u8);
- LLVMTypeRef llvm_int = lb_type(m, t_int);
- // LLVMTypeRef llvm_type_info_ptr = lb_type(m, t_type_info_ptr);
-
auto const get_type_info_ptr = [&](lbModule *m, Type *type) -> LLVMValueRef {
type = default_type(type);
isize index = lb_type_info_index(m->info, type);
GB_ASSERT(index >= 0);
- u64 offset = cast(u64)(index * type_info_struct_size);
-
- LLVMValueRef indices[1] = {
- LLVMConstInt(llvm_int, offset, false)
- };
-
- // LLVMValueRef ptr = LLVMConstInBoundsGEP2(llvm_u8, giant_struct, indices, gb_count_of(indices));
- LLVMValueRef ptr = LLVMConstGEP2(llvm_u8, giant_struct, indices, gb_count_of(indices));
- return ptr;
- // return LLVMConstPointerCast(ptr, llvm_type_info_ptr);
+ return giant_const_values[index];
};
for_array(type_info_type_index, info->type_info_types) {
@@ -366,7 +361,12 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
entries_handled[entry_index] = true;
- LLVMTypeRef stype = LLVMStructGetTypeAtIndex(giant_struct_type, cast(unsigned)entry_index);
+ LLVMTypeRef stype = nullptr;
+ if (t->kind == Type_Named) {
+ stype = modified_types[0];
+ } else {
+ stype = modified_types[lb_typeid_kind(m, t)];
+ }
i64 size = type_size_of(t);
i64 align = type_align_of(t);
@@ -376,12 +376,16 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
lbValue type_info_flags = lb_const_int(m, t_type_info_flags, flags);
+ for (isize i = 0; i < 6; i++) {
+ small_const_values[i] = nullptr;
+ }
+
small_const_values[0] = LLVMConstInt(lb_type(m, t_int), size, true);
small_const_values[1] = LLVMConstInt(lb_type(m, t_int), align, true);
small_const_values[2] = type_info_flags.value;
unsigned variant_index = 0;
- if (build_context.int_size == 8) {
+ if (build_context.ptr_size == 8) {
small_const_values[3] = LLVMConstNull(LLVMStructGetTypeAtIndex(stype, 3));
small_const_values[4] = id.value;
variant_index = 5;
@@ -856,7 +860,7 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
Entity *f = t->Struct.fields[source_index];
i64 foffset = 0;
if (!t->Struct.is_raw_union) {
- GB_ASSERT(t->Struct.offsets != nullptr);
+ GB_ASSERT_MSG(t->Struct.offsets != nullptr, "%s", type_to_string(t));
GB_ASSERT(0 <= f->Variable.field_index && f->Variable.field_index < count);
foffset = t->Struct.offsets[source_index];
}
@@ -975,17 +979,81 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
tag_type = t_type_info_matrix;
i64 ez = type_size_of(t->Matrix.elem);
- LLVMValueRef vals[5] = {
+ LLVMValueRef vals[6] = {
get_type_info_ptr(m, t->Matrix.elem),
lb_const_int(m, t_int, ez).value,
lb_const_int(m, t_int, matrix_type_stride_in_elems(t)).value,
lb_const_int(m, t_int, t->Matrix.row_count).value,
lb_const_int(m, t_int, t->Matrix.column_count).value,
+ lb_const_int(m, t_u8, cast(u8)t->Matrix.is_row_major).value,
};
variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
}
break;
+
+ case Type_BitField:
+ {
+ tag_type = t_type_info_bit_field;
+
+ LLVMValueRef vals[6] = {};
+ vals[0] = get_type_info_ptr(m, t->BitField.backing_type);
+ isize count = t->BitField.fields.count;
+ if (count > 0) {
+ i64 names_offset = 0;
+ i64 types_offset = 0;
+ i64 bit_sizes_offset = 0;
+ i64 bit_offsets_offset = 0;
+ i64 tags_offset = 0;
+ lbValue memory_names = lb_type_info_member_names_offset (m, count, &names_offset);
+ lbValue memory_types = lb_type_info_member_types_offset (m, count, &types_offset);
+ lbValue memory_bit_sizes = lb_type_info_member_offsets_offset(m, count, &bit_sizes_offset);
+ lbValue memory_bit_offsets = lb_type_info_member_offsets_offset(m, count, &bit_offsets_offset);
+ lbValue memory_tags = lb_type_info_member_tags_offset (m, count, &tags_offset);
+
+ u64 bit_offset = 0;
+ for (isize source_index = 0; source_index < count; source_index++) {
+ Entity *f = t->BitField.fields[source_index];
+ u64 bit_size = cast(u64)t->BitField.bit_sizes[source_index];
+
+ lbValue index = lb_const_int(m, t_int, source_index);
+ if (f->token.string.len > 0) {
+ lb_global_type_info_member_names_values[names_offset+source_index] = lb_const_string(m, f->token.string).value;
+ }
+
+ lb_global_type_info_member_types_values[types_offset+source_index] = get_type_info_ptr(m, f->type);
+
+ lb_global_type_info_member_offsets_values[bit_sizes_offset+source_index] = lb_const_int(m, t_uintptr, bit_size).value;
+ lb_global_type_info_member_offsets_values[bit_offsets_offset+source_index] = lb_const_int(m, t_uintptr, bit_offset).value;
+
+ if (t->BitField.tags) {
+ String tag = t->BitField.tags[source_index];
+ if (tag.len > 0) {
+ lb_global_type_info_member_tags_values[tags_offset+source_index] = lb_const_string(m, tag).value;
+ }
+ }
+
+ bit_offset += bit_size;
+ }
+
+ lbValue cv = lb_const_int(m, t_int, count);
+ vals[1] = llvm_const_slice(m, memory_names, cv);
+ vals[2] = llvm_const_slice(m, memory_types, cv);
+ vals[3] = llvm_const_slice(m, memory_bit_sizes, cv);
+ vals[4] = llvm_const_slice(m, memory_bit_offsets, cv);
+ vals[5] = llvm_const_slice(m, memory_tags, cv);
+ }
+
+
+ for (isize i = 0; i < gb_count_of(vals); i++) {
+ if (vals[i] == nullptr) {
+ vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag_type, i)));
+ }
+ }
+
+ variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
+ break;
+ }
}
@@ -994,6 +1062,7 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
if (tag_type != nullptr) {
tag_index = union_variant_index(ut, tag_type);
}
+ GB_ASSERT(tag_index <= Typeid__COUNT);
LLVMValueRef full_variant_values[3] = {};
@@ -1024,788 +1093,43 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
small_const_values[variant_index] = full_variant_value;
- giant_const_values[entry_index] = LLVMConstNamedStruct(stype, small_const_values, variant_index+1);
+ LLVMSetInitializer(giant_const_values[entry_index], LLVMConstNamedStruct(stype, small_const_values, variant_index+1));
}
+ for (isize i = 0; i < global_type_info_data_entity_count; i++) {
+ giant_const_values[i] = LLVMConstPointerCast(giant_const_values[i], lb_type(m, t_type_info_ptr));
+ }
+
- LLVMValueRef giant_const = LLVMConstNamedStruct(giant_struct_type, giant_const_values, cast(unsigned)global_type_info_data_entity_count);
- LLVMSetInitializer(giant_struct, giant_const);
+ LLVMValueRef giant_const = LLVMConstArray(lb_type(m, t_type_info_ptr), giant_const_values, cast(unsigned)global_type_info_data_entity_count);
+ LLVMValueRef giant_array = lb_global_type_info_data_ptr(m).value;
+ LLVMSetInitializer(giant_array, giant_const);
}
-gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data
+gb_internal void lb_setup_type_info_data(lbModule *m) { // NOTE(bill): Setup type_info data
if (build_context.no_rtti) {
return;
}
- lbModule *m = p->module;
- CheckerInfo *info = m->info;
i64 global_type_info_data_entity_count = 0;
- {
- // NOTE(bill): Set the type_table slice with the global backing array
- lbValue global_type_table = lb_find_runtime_value(m, str_lit("type_table"));
- Type *type = base_type(lb_global_type_info_data_entity->type);
- GB_ASSERT(type->kind == Type_Array);
- global_type_info_data_entity_count = type->Array.count;
-
- LLVMValueRef data = lb_global_type_info_data_ptr(m).value;
- data = LLVMConstPointerCast(data, lb_type(m, alloc_type_pointer(type->Array.elem)));
- LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), type->Array.count, true);
- Type *t = type_deref(global_type_table.type);
- GB_ASSERT(is_type_slice(t));
- LLVMValueRef slice = llvm_const_slice_internal(m, data, len);
-
- LLVMSetInitializer(global_type_table.value, slice);
- }
-
- if (LB_USE_GIANT_PACKED_STRUCT) {
- lb_setup_type_info_data_giant_packed_struct(m, global_type_info_data_entity_count, p);
- return;
- }
-
- // Useful types
- Entity *type_info_flags_entity = find_core_entity(info->checker, str_lit("Type_Info_Flags"));
- Type *t_type_info_flags = type_info_flags_entity->type;
-
-
- auto entries_handled = slice_make<bool>(heap_allocator(), cast(isize)global_type_info_data_entity_count);
- defer (gb_free(heap_allocator(), entries_handled.data));
- entries_handled[0] = true;
-
- for_array(type_info_type_index, info->type_info_types) {
- Type *t = info->type_info_types[type_info_type_index];
- if (t == nullptr || t == t_invalid) {
- continue;
- }
-
- isize entry_index = lb_type_info_index(info, t, false);
- if (entry_index <= 0) {
- continue;
- }
-
- if (entries_handled[entry_index]) {
- continue;
- }
- entries_handled[entry_index] = true;
-
- lbValue global_data_ptr = lb_global_type_info_data_ptr(m);
- lbValue tag = {};
- lbValue ti_ptr = lb_emit_array_epi(p, global_data_ptr, cast(i32)entry_index);
-
- i64 size = type_size_of(t);
- i64 align = type_align_of(t);
- u32 flags = type_info_flags_of_type(t);
- lbValue id = lb_typeid(m, t);
- GB_ASSERT_MSG(align != 0, "%lld %s", align, type_to_string(t));
-
- lbValue type_info_flags = lb_const_int(p->module, t_type_info_flags, flags);
-
- lbValue size_ptr = lb_emit_struct_ep(p, ti_ptr, 0);
- lbValue align_ptr = lb_emit_struct_ep(p, ti_ptr, 1);
- lbValue flags_ptr = lb_emit_struct_ep(p, ti_ptr, 2);
- lbValue id_ptr = lb_emit_struct_ep(p, ti_ptr, 3);
-
- lb_emit_store(p, size_ptr, lb_const_int(m, t_int, size));
- lb_emit_store(p, align_ptr, lb_const_int(m, t_int, align));
- lb_emit_store(p, flags_ptr, type_info_flags);
- lb_emit_store(p, id_ptr, id);
-
- lbValue variant_ptr = lb_emit_struct_ep(p, ti_ptr, 4);
-
- switch (t->kind) {
- case Type_Named: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_named_ptr);
-
- LLVMValueRef pkg_name = nullptr;
- if (t->Named.type_name->pkg) {
- pkg_name = lb_const_string(m, t->Named.type_name->pkg->name).value;
- } else {
- pkg_name = LLVMConstNull(lb_type(m, t_string));
- }
-
- String proc_name = {};
- if (t->Named.type_name->parent_proc_decl) {
- DeclInfo *decl = t->Named.type_name->parent_proc_decl;
- if (decl->entity && decl->entity->kind == Entity_Procedure) {
- proc_name = decl->entity->token.string;
- }
- }
- TokenPos pos = t->Named.type_name->token.pos;
-
- lbValue loc = lb_emit_source_code_location_const(p, proc_name, pos);
-
- LLVMValueRef vals[4] = {
- lb_const_string(p->module, t->Named.type_name->token.string).value,
- lb_type_info(m, t->Named.base).value,
- pkg_name,
- loc.value
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
-
- case Type_Basic:
- switch (t->Basic.kind) {
- case Basic_bool:
- case Basic_b8:
- case Basic_b16:
- case Basic_b32:
- case Basic_b64:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_boolean_ptr);
- break;
-
- case Basic_i8:
- case Basic_u8:
- case Basic_i16:
- case Basic_u16:
- case Basic_i32:
- case Basic_u32:
- case Basic_i64:
- case Basic_u64:
- case Basic_i128:
- case Basic_u128:
-
- case Basic_i16le:
- case Basic_u16le:
- case Basic_i32le:
- case Basic_u32le:
- case Basic_i64le:
- case Basic_u64le:
- case Basic_i128le:
- case Basic_u128le:
- case Basic_i16be:
- case Basic_u16be:
- case Basic_i32be:
- case Basic_u32be:
- case Basic_i64be:
- case Basic_u64be:
- case Basic_i128be:
- case Basic_u128be:
-
- case Basic_int:
- case Basic_uint:
- case Basic_uintptr: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_integer_ptr);
-
- lbValue is_signed = lb_const_bool(m, t_bool, (t->Basic.flags & BasicFlag_Unsigned) == 0);
- // NOTE(bill): This is matches the runtime layout
- u8 endianness_value = 0;
- if (t->Basic.flags & BasicFlag_EndianLittle) {
- endianness_value = 1;
- } else if (t->Basic.flags & BasicFlag_EndianBig) {
- endianness_value = 2;
- }
- lbValue endianness = lb_const_int(m, t_u8, endianness_value);
-
- LLVMValueRef vals[2] = {
- is_signed.value,
- endianness.value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
-
- case Basic_rune:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_rune_ptr);
- break;
-
- case Basic_f16:
- case Basic_f32:
- case Basic_f64:
- case Basic_f16le:
- case Basic_f32le:
- case Basic_f64le:
- case Basic_f16be:
- case Basic_f32be:
- case Basic_f64be:
- {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_float_ptr);
-
- // NOTE(bill): This is matches the runtime layout
- u8 endianness_value = 0;
- if (t->Basic.flags & BasicFlag_EndianLittle) {
- endianness_value = 1;
- } else if (t->Basic.flags & BasicFlag_EndianBig) {
- endianness_value = 2;
- }
- lbValue endianness = lb_const_int(m, t_u8, endianness_value);
-
- LLVMValueRef vals[1] = {
- endianness.value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
-
- case Basic_complex32:
- case Basic_complex64:
- case Basic_complex128:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_complex_ptr);
- break;
-
- case Basic_quaternion64:
- case Basic_quaternion128:
- case Basic_quaternion256:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_quaternion_ptr);
- break;
-
- case Basic_rawptr:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_pointer_ptr);
- break;
-
- case Basic_string:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_string_ptr);
- break;
-
- case Basic_cstring:
- {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_string_ptr);
- LLVMValueRef vals[1] = {
- lb_const_bool(m, t_bool, true).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
-
- case Basic_any:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_any_ptr);
- break;
-
- case Basic_typeid:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_typeid_ptr);
- break;
- }
- break;
-
- case Type_Pointer: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_pointer_ptr);
- lbValue gep = lb_type_info(m, t->Pointer.elem);
-
- LLVMValueRef vals[1] = {
- gep.value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
- case Type_MultiPointer: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_multi_pointer_ptr);
- lbValue gep = lb_type_info(m, t->MultiPointer.elem);
-
- LLVMValueRef vals[1] = {
- gep.value,
- };
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
- case Type_SoaPointer: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_soa_pointer_ptr);
- lbValue gep = lb_type_info(m, t->SoaPointer.elem);
-
- LLVMValueRef vals[1] = {
- gep.value,
- };
+ // NOTE(bill): Set the type_table slice with the global backing array
+ lbValue global_type_table = lb_find_runtime_value(m, str_lit("type_table"));
+ Type *type = base_type(lb_global_type_info_data_entity->type);
+ GB_ASSERT(type->kind == Type_Array);
+ global_type_info_data_entity_count = type->Array.count;
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
- case Type_Array: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_array_ptr);
- i64 ez = type_size_of(t->Array.elem);
-
- LLVMValueRef vals[3] = {
- lb_type_info(m, t->Array.elem).value,
- lb_const_int(m, t_int, ez).value,
- lb_const_int(m, t_int, t->Array.count).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
- case Type_EnumeratedArray: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr);
-
- LLVMValueRef vals[7] = {
- lb_type_info(m, t->EnumeratedArray.elem).value,
- lb_type_info(m, t->EnumeratedArray.index).value,
- lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value,
- lb_const_int(m, t_int, t->EnumeratedArray.count).value,
-
- // Unions
- LLVMConstNull(lb_type(m, t_type_info_enum_value)),
- LLVMConstNull(lb_type(m, t_type_info_enum_value)),
-
- lb_const_bool(m, t_bool, t->EnumeratedArray.is_sparse).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
-
- // NOTE(bill): Union assignment
- lbValue min_value = lb_emit_struct_ep(p, tag, 4);
- lbValue max_value = lb_emit_struct_ep(p, tag, 5);
-
- lbValue min_v = lb_const_value(m, t_i64, *t->EnumeratedArray.min_value);
- lbValue max_v = lb_const_value(m, t_i64, *t->EnumeratedArray.max_value);
-
- lb_emit_store(p, min_value, min_v);
- lb_emit_store(p, max_value, max_v);
- break;
- }
- case Type_DynamicArray: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_dynamic_array_ptr);
-
- LLVMValueRef vals[2] = {
- lb_type_info(m, t->DynamicArray.elem).value,
- lb_const_int(m, t_int, type_size_of(t->DynamicArray.elem)).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
- case Type_Slice: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_slice_ptr);
-
- LLVMValueRef vals[2] = {
- lb_type_info(m, t->Slice.elem).value,
- lb_const_int(m, t_int, type_size_of(t->Slice.elem)).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
- case Type_Proc: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_procedure_ptr);
-
- LLVMValueRef params = LLVMConstNull(lb_type(m, t_type_info_ptr));
- LLVMValueRef results = LLVMConstNull(lb_type(m, t_type_info_ptr));
- if (t->Proc.params != nullptr) {
- params = lb_type_info(m, t->Proc.params).value;
- }
- if (t->Proc.results != nullptr) {
- results = lb_type_info(m, t->Proc.results).value;
- }
-
- LLVMValueRef vals[4] = {
- params,
- results,
- lb_const_bool(m, t_bool, t->Proc.variadic).value,
- lb_const_int(m, t_u8, t->Proc.calling_convention).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
- case Type_Tuple: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_parameters_ptr);
-
- lbValue memory_types = lb_type_info_member_types_offset(m, t->Tuple.variables.count);
- lbValue memory_names = lb_type_info_member_names_offset(m, t->Tuple.variables.count);
-
-
- for_array(i, t->Tuple.variables) {
- // NOTE(bill): offset is not used for tuples
- Entity *f = t->Tuple.variables[i];
-
- lbValue index = lb_const_int(m, t_int, i);
- lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
-
- // TODO(bill): Make this constant if possible, 'lb_const_store' does not work
- lb_emit_store(p, type_info, lb_type_info(m, f->type));
- if (f->token.string.len > 0) {
- lbValue name = lb_emit_ptr_offset(p, memory_names, index);
- lb_emit_store(p, name, lb_const_string(m, f->token.string));
- }
- }
-
- lbValue count = lb_const_int(m, t_int, t->Tuple.variables.count);
-
- LLVMValueRef types_slice = llvm_const_slice(m, memory_types, count);
- LLVMValueRef names_slice = llvm_const_slice(m, memory_names, count);
-
- LLVMValueRef vals[2] = {
- types_slice,
- names_slice,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
-
- break;
- }
-
- case Type_Enum:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enum_ptr);
-
- {
- GB_ASSERT(t->Enum.base_type != nullptr);
- // GB_ASSERT_MSG(type_size_of(t_type_info_enum_value) == 16, "%lld == 16", cast(long long)type_size_of(t_type_info_enum_value));
-
-
- LLVMValueRef vals[3] = {};
- vals[0] = lb_type_info(m, t->Enum.base_type).value;
- if (t->Enum.fields.count > 0) {
- auto fields = t->Enum.fields;
- lbValue name_array = lb_generate_global_array(m, t_string, fields.count,
- str_lit("$enum_names"), cast(i64)entry_index);
- lbValue value_array = lb_generate_global_array(m, t_type_info_enum_value, fields.count,
- str_lit("$enum_values"), cast(i64)entry_index);
-
-
- LLVMValueRef *name_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count);
- LLVMValueRef *value_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count);
-
- GB_ASSERT(is_type_integer(t->Enum.base_type));
-
- for_array(i, fields) {
- name_values[i] = lb_const_string(m, fields[i]->token.string).value;
- value_values[i] = lb_const_value(m, t_i64, fields[i]->Constant.value).value;
- }
-
- LLVMValueRef name_init = llvm_const_array(lb_type(m, t_string), name_values, cast(unsigned)fields.count);
- LLVMValueRef value_init = llvm_const_array(lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count);
- LLVMSetInitializer(name_array.value, name_init);
- LLVMSetInitializer(value_array.value, value_init);
- LLVMSetGlobalConstant(name_array.value, true);
- LLVMSetGlobalConstant(value_array.value, true);
-
- lbValue v_count = lb_const_int(m, t_int, fields.count);
-
- vals[1] = llvm_const_slice(m, lb_array_elem(p, name_array), v_count);
- vals[2] = llvm_const_slice(m, lb_array_elem(p, value_array), v_count);
- } else {
- vals[1] = LLVMConstNull(lb_type(m, base_type(t_type_info_enum)->Struct.fields[1]->type));
- vals[2] = LLVMConstNull(lb_type(m, base_type(t_type_info_enum)->Struct.fields[2]->type));
- }
-
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
-
- case Type_Union: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr);
-
- {
- LLVMValueRef vals[7] = {};
-
- isize variant_count = gb_max(0, t->Union.variants.count);
- lbValue memory_types = lb_type_info_member_types_offset(m, variant_count);
-
- // NOTE(bill): Zeroth is nil so ignore it
- for (isize variant_index = 0; variant_index < variant_count; variant_index++) {
- Type *vt = t->Union.variants[variant_index];
- lbValue tip = lb_type_info(m, vt);
-
- lbValue index = lb_const_int(m, t_int, variant_index);
- lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
- lb_emit_store(p, type_info, lb_type_info(m, vt));
- }
-
- lbValue count = lb_const_int(m, t_int, variant_count);
- vals[0] = llvm_const_slice(m, memory_types, count);
-
- i64 tag_size = union_tag_size(t);
- if (tag_size > 0) {
- i64 tag_offset = align_formula(t->Union.variant_block_size, tag_size);
- vals[1] = lb_const_int(m, t_uintptr, tag_offset).value;
- vals[2] = lb_type_info(m, union_tag_type(t)).value;
- } else {
- vals[1] = lb_const_int(m, t_uintptr, 0).value;
- vals[2] = LLVMConstNull(lb_type(m, t_type_info_ptr));
- }
-
- if (is_type_comparable(t) && !is_type_simple_compare(t)) {
- vals[3] = lb_equal_proc_for_type(m, t).value;
- }
-
- vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
- vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value;
- vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value;
-
- for (isize i = 0; i < gb_count_of(vals); i++) {
- if (vals[i] == nullptr) {
- vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i)));
- }
- }
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
-
- break;
- }
-
- case Type_Struct: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_struct_ptr);
-
- LLVMValueRef vals[13] = {};
-
-
- {
- lbValue is_packed = lb_const_bool(m, t_bool, t->Struct.is_packed);
- lbValue is_raw_union = lb_const_bool(m, t_bool, t->Struct.is_raw_union);
- lbValue is_no_copy = lb_const_bool(m, t_bool, t->Struct.is_no_copy);
- lbValue is_custom_align = lb_const_bool(m, t_bool, t->Struct.custom_align != 0);
- vals[5] = is_packed.value;
- vals[6] = is_raw_union.value;
- vals[7] = is_no_copy.value;
- vals[8] = is_custom_align.value;
- if (is_type_comparable(t) && !is_type_simple_compare(t)) {
- vals[9] = lb_equal_proc_for_type(m, t).value;
- }
-
-
- if (t->Struct.soa_kind != StructSoa_None) {
- lbValue kind = lb_emit_struct_ep(p, tag, 10);
- Type *kind_type = type_deref(kind.type);
-
- lbValue soa_kind = lb_const_value(m, kind_type, exact_value_i64(t->Struct.soa_kind));
- lbValue soa_type = lb_type_info(m, t->Struct.soa_elem);
- lbValue soa_len = lb_const_int(m, t_int, t->Struct.soa_count);
-
- vals[10] = soa_kind.value;
- vals[11] = soa_type.value;
- vals[12] = soa_len.value;
- }
- }
-
- isize count = t->Struct.fields.count;
- if (count > 0) {
- lbValue memory_types = lb_type_info_member_types_offset (m, count);
- lbValue memory_names = lb_type_info_member_names_offset (m, count);
- lbValue memory_offsets = lb_type_info_member_offsets_offset(m, count);
- lbValue memory_usings = lb_type_info_member_usings_offset (m, count);
- lbValue memory_tags = lb_type_info_member_tags_offset (m, count);
-
- type_set_offsets(t); // NOTE(bill): Just incase the offsets have not been set yet
- for (isize source_index = 0; source_index < count; source_index++) {
- Entity *f = t->Struct.fields[source_index];
- lbValue tip = lb_type_info(m, f->type);
- i64 foffset = 0;
- if (!t->Struct.is_raw_union) {
- GB_ASSERT(t->Struct.offsets != nullptr);
- GB_ASSERT(0 <= f->Variable.field_index && f->Variable.field_index < count);
- foffset = t->Struct.offsets[source_index];
- }
- GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field);
-
- lbValue index = lb_const_int(m, t_int, source_index);
- lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
- lbValue offset = lb_emit_ptr_offset(p, memory_offsets, index);
- lbValue is_using = lb_emit_ptr_offset(p, memory_usings, index);
-
- lb_emit_store(p, type_info, lb_type_info(m, f->type));
- if (f->token.string.len > 0) {
- lbValue name = lb_emit_ptr_offset(p, memory_names, index);
- lb_emit_store(p, name, lb_const_string(m, f->token.string));
- }
- lb_emit_store(p, offset, lb_const_int(m, t_uintptr, foffset));
- lb_emit_store(p, is_using, lb_const_bool(m, t_bool, (f->flags&EntityFlag_Using) != 0));
-
- if (t->Struct.tags != nullptr) {
- String tag_string = t->Struct.tags[source_index];
- if (tag_string.len > 0) {
- lbValue tag_ptr = lb_emit_ptr_offset(p, memory_tags, index);
- lb_emit_store(p, tag_ptr, lb_const_string(m, tag_string));
- }
- }
-
- }
-
- lbValue cv = lb_const_int(m, t_int, count);
- vals[0] = llvm_const_slice(m, memory_types, cv);
- vals[1] = llvm_const_slice(m, memory_names, cv);
- vals[2] = llvm_const_slice(m, memory_offsets, cv);
- vals[3] = llvm_const_slice(m, memory_usings, cv);
- vals[4] = llvm_const_slice(m, memory_tags, cv);
- }
- for (isize i = 0; i < gb_count_of(vals); i++) {
- if (vals[i] == nullptr) {
- vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i)));
- }
- }
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
-
- break;
- }
-
- case Type_Map: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_map_ptr);
- init_map_internal_types(t);
-
- LLVMValueRef vals[3] = {
- lb_type_info(m, t->Map.key).value,
- lb_type_info(m, t->Map.value).value,
- lb_gen_map_info_ptr(p->module, t).value
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
-
- case Type_BitSet:
- {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_bit_set_ptr);
-
- GB_ASSERT(is_type_typed(t->BitSet.elem));
-
-
- LLVMValueRef vals[4] = {
- lb_type_info(m, t->BitSet.elem).value,
- LLVMConstNull(lb_type(m, t_type_info_ptr)),
- lb_const_int(m, t_i64, t->BitSet.lower).value,
- lb_const_int(m, t_i64, t->BitSet.upper).value,
- };
- if (t->BitSet.underlying != nullptr) {
- vals[1] =lb_type_info(m, t->BitSet.underlying).value;
- }
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
-
- case Type_SimdVector:
- {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_simd_vector_ptr);
-
- LLVMValueRef vals[3] = {};
-
- vals[0] = lb_type_info(m, t->SimdVector.elem).value;
- vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value;
- vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value;
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
-
- case Type_RelativePointer:
- {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_pointer_ptr);
- LLVMValueRef vals[2] = {
- lb_type_info(m, t->RelativePointer.pointer_type).value,
- lb_type_info(m, t->RelativePointer.base_integer).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
-
- case Type_RelativeMultiPointer:
- {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_multi_pointer_ptr);
- LLVMValueRef vals[2] = {
- lb_type_info(m, t->RelativeMultiPointer.pointer_type).value,
- lb_type_info(m, t->RelativeMultiPointer.base_integer).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
-
- case Type_Matrix:
- {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_matrix_ptr);
- i64 ez = type_size_of(t->Matrix.elem);
-
- LLVMValueRef vals[5] = {
- lb_type_info(m, t->Matrix.elem).value,
- lb_const_int(m, t_int, ez).value,
- lb_const_int(m, t_int, matrix_type_stride_in_elems(t)).value,
- lb_const_int(m, t_int, t->Matrix.row_count).value,
- lb_const_int(m, t_int, t->Matrix.column_count).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
- }
+ if (true) {
+ lb_setup_type_info_data_giant_array(m, global_type_info_data_entity_count);
+ }
+ LLVMValueRef data = lb_global_type_info_data_ptr(m).value;
+ data = LLVMConstPointerCast(data, lb_type(m, alloc_type_pointer(type->Array.elem)));
+ LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), type->Array.count, true);
+ Type *t = type_deref(global_type_table.type);
+ GB_ASSERT(is_type_slice(t));
+ LLVMValueRef slice = llvm_const_slice_internal(m, data, len);
- if (tag.value != nullptr) {
- Type *tag_type = type_deref(tag.type);
- GB_ASSERT(is_type_named(tag_type));
- // lb_emit_store_union_variant(p, variant_ptr, lb_emit_load(p, tag), tag_type);
- lb_emit_store_union_variant_tag(p, variant_ptr, tag_type);
- } else {
- if (t != t_llvm_bool) {
- GB_PANIC("Unhandled Type_Info variant: %s", type_to_string(t));
- }
- }
- }
-
- for_array(i, entries_handled) {
- if (!entries_handled[i]) {
- GB_PANIC("UNHANDLED ENTRY %td (%td)", i, entries_handled.count);
- }
- }
+ LLVMSetInitializer(global_type_table.value, slice);
}
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index be3ae9c8a..865c3f1ec 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -83,27 +83,13 @@ gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef p
lb_type(p->module, t_rawptr),
lb_type(p->module, t_int)
};
- if (true || is_inlinable) {
+ LLVMValueRef args[4] = {};
+ args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
+ args[1] = LLVMConstInt(LLVMInt8TypeInContext(p->module->ctx), 0, false);
+ args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
+ args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), is_volatile, false);
- LLVMValueRef args[4] = {};
- args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
- args[1] = LLVMConstInt(LLVMInt8TypeInContext(p->module->ctx), 0, false);
- args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
- args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), is_volatile, false);
-
- return lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
- } else {
- lbValue pr = lb_lookup_runtime_procedure(p->module, str_lit("memset"));
-
- LLVMValueRef args[3] = {};
- args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
- args[1] = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), 0, false);
- args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
-
- // We always get the function pointer type rather than the function and there is apparently no way around that?
- LLVMTypeRef type = lb_type_internal_for_procedures_raw(p->module, pr.type);
- return LLVMBuildCall2(p->builder, type, pr.value, args, gb_count_of(args), "");
- }
+ return lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
}
@@ -138,11 +124,33 @@ gb_internal lbValue lb_emit_select(lbProcedure *p, lbValue cond, lbValue x, lbVa
gb_internal lbValue lb_emit_min(lbProcedure *p, Type *t, lbValue x, lbValue y) {
x = lb_emit_conv(p, x, t);
y = lb_emit_conv(p, y, t);
+ bool use_llvm_intrinsic = !is_arch_wasm() && (is_type_float(t) || (is_type_simd_vector(t) && is_type_float(base_array_type(t))));
+ if (use_llvm_intrinsic) {
+ LLVMValueRef args[2] = {x.value, y.value};
+ LLVMTypeRef types[1] = {lb_type(p->module, t)};
+
+ // NOTE(bill): f either operand is a NaN, returns NaN. Otherwise returns the lesser of the two arguments.
+ // -0.0 is considered to be less than +0.0 for this intrinsic.
+ // These semantics are specified by IEEE 754-2008.
+ LLVMValueRef v = lb_call_intrinsic(p, "llvm.minnum", args, gb_count_of(args), types, gb_count_of(types));
+ return {v, t};
+ }
return lb_emit_select(p, lb_emit_comp(p, Token_Lt, x, y), x, y);
}
gb_internal lbValue lb_emit_max(lbProcedure *p, Type *t, lbValue x, lbValue y) {
x = lb_emit_conv(p, x, t);
y = lb_emit_conv(p, y, t);
+ bool use_llvm_intrinsic = !is_arch_wasm() && (is_type_float(t) || (is_type_simd_vector(t) && is_type_float(base_array_type(t))));
+ if (use_llvm_intrinsic) {
+ LLVMValueRef args[2] = {x.value, y.value};
+ LLVMTypeRef types[1] = {lb_type(p->module, t)};
+
+ // NOTE(bill): If either operand is a NaN, returns NaN. Otherwise returns the greater of the two arguments.
+ // -0.0 is considered to be less than +0.0 for this intrinsic.
+ // These semantics are specified by IEEE 754-2008.
+ LLVMValueRef v = lb_call_intrinsic(p, "llvm.maxnum", args, gb_count_of(args), types, gb_count_of(types));
+ return {v, t};
+ }
return lb_emit_select(p, lb_emit_comp(p, Token_Gt, x, y), x, y);
}
@@ -1346,7 +1354,7 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection
if (index == 0) {
type = t_rawptr;
} else if (index == 1) {
- type = t_type_info_ptr;
+ type = t_typeid;
}
e = lb_emit_struct_ep(p, e, index);
break;
@@ -1456,14 +1464,16 @@ gb_internal lbValue lb_emit_matrix_epi(lbProcedure *p, lbValue s, isize row, isi
Type *t = s.type;
GB_ASSERT(is_type_pointer(t));
Type *mt = base_type(type_deref(t));
- if (column == 0) {
- GB_ASSERT_MSG(is_type_matrix(mt) || is_type_array_like(mt), "%s", type_to_string(mt));
- return lb_emit_epi(p, s, row);
- } else if (row == 0 && is_type_array_like(mt)) {
- return lb_emit_epi(p, s, column);
+
+ if (!mt->Matrix.is_row_major) {
+ if (column == 0) {
+ GB_ASSERT_MSG(is_type_matrix(mt) || is_type_array_like(mt), "%s", type_to_string(mt));
+ return lb_emit_epi(p, s, row);
+ } else if (row == 0 && is_type_array_like(mt)) {
+ return lb_emit_epi(p, s, column);
+ }
}
-
GB_ASSERT_MSG(is_type_matrix(mt), "%s", type_to_string(mt));
isize offset = matrix_indices_to_offset(mt, row, column);
@@ -1483,7 +1493,13 @@ gb_internal lbValue lb_emit_matrix_ep(lbProcedure *p, lbValue s, lbValue row, lb
row = lb_emit_conv(p, row, t_int);
column = lb_emit_conv(p, column, t_int);
- LLVMValueRef index = LLVMBuildAdd(p->builder, row.value, LLVMBuildMul(p->builder, column.value, stride_elems, ""), "");
+ LLVMValueRef index = nullptr;
+
+ if (mt->Matrix.is_row_major) {
+ index = LLVMBuildAdd(p->builder, column.value, LLVMBuildMul(p->builder, row.value, stride_elems, ""), "");
+ } else {
+ index = LLVMBuildAdd(p->builder, row.value, LLVMBuildMul(p->builder, column.value, stride_elems, ""), "");
+ }
LLVMValueRef indices[2] = {
LLVMConstInt(lb_type(p->module, t_int), 0, false),
diff --git a/src/main.cpp b/src/main.cpp
index 19271d667..2dbb72ca2 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -273,6 +273,7 @@ enum BuildFlagKind {
BuildFlag_DisallowDo,
BuildFlag_DefaultToNilAllocator,
+ BuildFlag_DefaultToPanicAllocator,
BuildFlag_StrictStyle,
BuildFlag_ForeignErrorProcedures,
BuildFlag_NoRTTI,
@@ -291,9 +292,12 @@ enum BuildFlagKind {
BuildFlag_WarningsAsErrors,
BuildFlag_TerseErrors,
BuildFlag_VerboseErrors,
+ BuildFlag_JsonErrors,
BuildFlag_ErrorPosStyle,
BuildFlag_MaxErrorCount,
+ BuildFlag_MinLinkLibs,
+
// internal use only
BuildFlag_InternalIgnoreLazy,
BuildFlag_InternalIgnoreLLVMBuild,
@@ -460,6 +464,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_DefaultToPanicAllocator, str_lit("default-to-panic-allocator"),BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ForeignErrorProcedures, str_lit("foreign-error-procedures"), BuildFlagParam_None, Command__does_check);
@@ -471,16 +476,19 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_ObfuscateSourceCodeLocations, str_lit("obfuscate-source-code-locations"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_Short, str_lit("short"), BuildFlagParam_None, Command_doc);
- add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc);
+ add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc | Command_test);
add_flag(&build_flags, BuildFlag_DocFormat, str_lit("doc-format"), BuildFlagParam_None, Command_doc);
add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_TerseErrors, str_lit("terse-errors"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_JsonErrors, str_lit("json-errors"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_ErrorPosStyle, str_lit("error-pos-style"), BuildFlagParam_String, Command_all);
add_flag(&build_flags, BuildFlag_MaxErrorCount, str_lit("max-error-count"), BuildFlagParam_Integer, Command_all);
+ add_flag(&build_flags, BuildFlag_MinLinkLibs, str_lit("min-link-libs"), BuildFlagParam_None, Command__does_build);
+
add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_InternalIgnoreLLVMBuild, str_lit("internal-ignore-llvm-build"),BuildFlagParam_None, Command_all);
@@ -805,9 +813,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
}
gbAllocator a = heap_allocator();
- String fullpath = path_to_fullpath(a, path);
- if (!path_is_directory(fullpath)) {
- gb_printf_err("Library collection '%.*s' path must be a directory, got '%.*s'\n", LIT(name), LIT(fullpath));
+ bool path_ok = false;
+ String fullpath = path_to_fullpath(a, path, &path_ok);
+ if (!path_ok || !path_is_directory(fullpath)) {
+ gb_printf_err("Library collection '%.*s' path must be a directory, got '%.*s'\n", LIT(name), LIT(path_ok ? fullpath : path));
gb_free(a, fullpath.text);
bad_flags = true;
break;
@@ -1061,6 +1070,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_MinimumOSVersion: {
GB_ASSERT(value.kind == ExactValue_String);
build_context.minimum_os_version_string = value.value_string;
+ build_context.minimum_os_version_string_given = true;
break;
}
case BuildFlag_RelocMode: {
@@ -1122,8 +1132,20 @@ gb_internal bool parse_build_flags(Array<String> args) {
break;
case BuildFlag_DefaultToNilAllocator:
+ if (build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR) {
+ gb_printf_err("'-default-to-panic-allocator' cannot be used with '-default-to-nil-allocator'\n");
+ bad_flags = true;
+ }
build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true;
break;
+ case BuildFlag_DefaultToPanicAllocator:
+ if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR) {
+ gb_printf_err("'-default-to-nil-allocator' cannot be used with '-default-to-panic-allocator'\n");
+ bad_flags = true;
+ }
+ build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR = true;
+ break;
+
case BuildFlag_ForeignErrorProcedures:
build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true;
break;
@@ -1135,6 +1157,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
break;
case BuildFlag_AllPackages:
build_context.cmd_doc_flags |= CmdDocFlag_AllPackages;
+ build_context.test_all_packages = true;
break;
case BuildFlag_DocFormat:
build_context.cmd_doc_flags |= CmdDocFlag_DocFormat;
@@ -1168,6 +1191,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
build_context.terse_errors = false;
break;
+ case BuildFlag_JsonErrors:
+ build_context.json_errors = true;
+ break;
+
case BuildFlag_ErrorPosStyle:
GB_ASSERT(value.kind == ExactValue_String);
@@ -1192,6 +1219,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
break;
}
+ case BuildFlag_MinLinkLibs:
+ build_context.min_link_libs = true;
+ break;
+
case BuildFlag_InternalIgnoreLazy:
build_context.ignore_lazy = true;
break;
@@ -1382,7 +1413,7 @@ gb_internal void timings_export_all(Timings *t, Checker *c, bool timings_are_fin
gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, fileName);
if (err != gbFileError_None) {
gb_printf_err("Failed to export timings to: %s\n", fileName);
- gb_exit(1);
+ exit_with_errors();
return;
} else {
gb_printf("\nExporting timings to '%s'... ", fileName);
@@ -1894,13 +1925,17 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-test-name:<string>");
print_usage_line(2, "Runs specific test only by name.");
print_usage_line(0, "");
+
+ print_usage_line(1, "-all-packages");
+ print_usage_line(2, "Tests all packages imported into the given initial package.");
+ print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-minimum-os-version:<string>");
print_usage_line(2, "Sets the minimum OS version targeted by the application.");
- print_usage_line(2, "Example: -minimum-os-version:12.0.0");
- print_usage_line(2, "(Only used when target is Darwin.)");
+ print_usage_line(2, "Default: -minimum-os-version:11.0.0");
+ print_usage_line(2, "Only used when target is Darwin, if given, linking mismatched versions will emit a warning.");
print_usage_line(0, "");
print_usage_line(1, "-extra-linker-flags:<string>");
@@ -1964,6 +1999,10 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(2, "Prints a terse error message without showing the code on that line and the location in that line.");
print_usage_line(0, "");
+ print_usage_line(1, "-json-errors");
+ print_usage_line(2, "Prints the error messages as json to stderr.");
+ print_usage_line(0, "");
+
print_usage_line(1, "-error-pos-style:<string>");
print_usage_line(2, "Available options:");
print_usage_line(3, "-error-pos-style:unix file/path:45:3:");
@@ -1977,6 +2016,11 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(2, "If not set, the default max error count is %d.", DEFAULT_MAX_ERROR_COLLECTOR_COUNT);
print_usage_line(0, "");
+ print_usage_line(1, "-min-link-libs");
+ print_usage_line(2, "If set, the number of linked libraries will be minimized to prevent duplications.");
+ print_usage_line(2, "This is useful for so called \"dumb\" linkers compared to \"smart\" linkers.");
+ print_usage_line(0, "");
+
print_usage_line(1, "-foreign-error-procedures");
print_usage_line(2, "States that the error procedures used in the runtime are defined in a separate translation unit.");
print_usage_line(0, "");
@@ -2075,7 +2119,7 @@ gb_internal void print_show_unused(Checker *c) {
array_add(&unused, e);
}
- gb_sort_array(unused.data, unused.count, cmp_entities_for_printing);
+ array_sort(unused, cmp_entities_for_printing);
print_usage_line(0, "Unused Package Declarations");
@@ -2376,8 +2420,18 @@ int main(int arg_count, char const **arg_ptr) {
TIME_SECTION("init default library collections");
array_init(&library_collections, heap_allocator());
// NOTE(bill): 'core' cannot be (re)defined by the user
- add_library_collection(str_lit("core"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("core")));
- add_library_collection(str_lit("vendor"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("vendor")));
+
+ auto const &add_collection = [](String const &name) {
+ bool ok = false;
+ add_library_collection(name, get_fullpath_relative(heap_allocator(), odin_root_dir(), name, &ok));
+ if (!ok) {
+ compiler_error("Cannot find the library collection '%.*s'. Is the ODIN_ROOT set up correctly?", LIT(name));
+ }
+ };
+
+ add_collection(str_lit("base"));
+ add_collection(str_lit("core"));
+ add_collection(str_lit("vendor"));
TIME_SECTION("init args");
map_init(&build_context.defined_values);
@@ -2405,14 +2459,18 @@ int main(int arg_count, char const **arg_ptr) {
Array<String> run_args = array_make<String>(heap_allocator(), 0, arg_count);
defer (array_free(&run_args));
+ isize run_args_start_idx = -1;
for_array(i, args) {
if (args[i] == "--") {
- last_non_run_arg = i;
+ run_args_start_idx = i;
+ break;
}
- if (i <= last_non_run_arg) {
- continue;
+ }
+ if(run_args_start_idx != -1) {
+ last_non_run_arg = run_args_start_idx;
+ for(isize i = run_args_start_idx+1; i < args.count; ++i) {
+ array_add(&run_args, args[i]);
}
- array_add(&run_args, args[i]);
}
args = array_slice(args, 0, last_non_run_arg);
@@ -2557,7 +2615,7 @@ int main(int arg_count, char const **arg_ptr) {
// NOTE(bill): add 'shared' directory if it is not already set
if (!find_library_collection_path(str_lit("shared"), nullptr)) {
add_library_collection(str_lit("shared"),
- get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
+ get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared"), nullptr));
}
init_build_context(selected_target_metrics ? selected_target_metrics->metrics : nullptr, selected_subtarget);
@@ -2646,8 +2704,12 @@ int main(int arg_count, char const **arg_ptr) {
}
if (any_errors()) {
+ print_all_errors();
return 1;
}
+ if (any_warnings()) {
+ print_all_errors();
+ }
MAIN_TIME_SECTION("type check");
@@ -2657,8 +2719,13 @@ int main(int arg_count, char const **arg_ptr) {
check_parsed_files(checker);
if (any_errors()) {
+ print_all_errors();
return 1;
}
+ if (any_warnings()) {
+ print_all_errors();
+ }
+
if (build_context.command_kind == Command_strip_semicolon) {
return strip_semicolons(parser);
diff --git a/src/parser.cpp b/src/parser.cpp
index 2671054df..01a3069ff 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -230,6 +230,10 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
case Ast_OrReturnExpr:
n->OrReturnExpr.expr = clone_ast(n->OrReturnExpr.expr, f);
break;
+ case Ast_OrBranchExpr:
+ n->OrBranchExpr.label = clone_ast(n->OrBranchExpr.label, f);
+ n->OrBranchExpr.expr = clone_ast(n->OrBranchExpr.expr, f);
+ break;
case Ast_TypeAssertion:
n->TypeAssertion.expr = clone_ast(n->TypeAssertion.expr, f);
n->TypeAssertion.type = clone_ast(n->TypeAssertion.type, f);
@@ -346,6 +350,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
n->Field.names = clone_ast_array(n->Field.names, f);
n->Field.type = clone_ast(n->Field.type, f);
break;
+ case Ast_BitFieldField:
+ n->BitFieldField.name = clone_ast(n->BitFieldField.name, f);
+ n->BitFieldField.type = clone_ast(n->BitFieldField.type, f);
+ n->BitFieldField.bit_size = clone_ast(n->BitFieldField.bit_size, f);
+ break;
case Ast_FieldList:
n->FieldList.list = clone_ast_array(n->FieldList.list, f);
break;
@@ -383,10 +392,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
n->DynamicArrayType.elem = clone_ast(n->DynamicArrayType.elem, f);
break;
case Ast_StructType:
- n->StructType.fields = clone_ast_array(n->StructType.fields, f);
+ n->StructType.fields = clone_ast_array(n->StructType.fields, f);
n->StructType.polymorphic_params = clone_ast(n->StructType.polymorphic_params, f);
- n->StructType.align = clone_ast(n->StructType.align, f);
- n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses, f);
+ n->StructType.align = clone_ast(n->StructType.align, f);
+ n->StructType.field_align = clone_ast(n->StructType.field_align, f);
+ n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses, f);
break;
case Ast_UnionType:
n->UnionType.variants = clone_ast_array(n->UnionType.variants, f);
@@ -401,6 +411,10 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
n->BitSetType.elem = clone_ast(n->BitSetType.elem, f);
n->BitSetType.underlying = clone_ast(n->BitSetType.underlying, f);
break;
+ case Ast_BitFieldType:
+ n->BitFieldType.backing_type = clone_ast(n->BitFieldType.backing_type, f);
+ n->BitFieldType.fields = clone_ast_array(n->BitFieldType.fields, f);
+ break;
case Ast_MapType:
n->MapType.count = clone_ast(n->MapType.count, f);
n->MapType.key = clone_ast(n->MapType.key, f);
@@ -1040,6 +1054,18 @@ gb_internal Ast *ast_field(AstFile *f, Array<Ast *> const &names, Ast *type, Ast
return result;
}
+gb_internal Ast *ast_bit_field_field(AstFile *f, Ast *name, Ast *type, Ast *bit_size, Token tag,
+ CommentGroup *docs, CommentGroup *comment) {
+ Ast *result = alloc_ast_node(f, Ast_BitFieldField);
+ result->BitFieldField.name = name;
+ result->BitFieldField.type = type;
+ result->BitFieldField.bit_size = bit_size;
+ result->BitFieldField.tag = tag;
+ result->BitFieldField.docs = docs;
+ result->BitFieldField.comment = comment;
+ return result;
+}
+
gb_internal Ast *ast_field_list(AstFile *f, Token token, Array<Ast *> const &list) {
Ast *result = alloc_ast_node(f, Ast_FieldList);
result->FieldList.token = token;
@@ -1125,7 +1151,7 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) {
gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_count,
Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_no_copy,
- Ast *align,
+ Ast *align, Ast *field_align,
Token where_token, Array<Ast *> const &where_clauses) {
Ast *result = alloc_ast_node(f, Ast_StructType);
result->StructType.token = token;
@@ -1136,6 +1162,7 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, i
result->StructType.is_raw_union = is_raw_union;
result->StructType.is_no_copy = is_no_copy;
result->StructType.align = align;
+ result->StructType.field_align = field_align;
result->StructType.where_token = where_token;
result->StructType.where_clauses = slice_from_array(where_clauses);
return result;
@@ -1172,6 +1199,17 @@ gb_internal Ast *ast_bit_set_type(AstFile *f, Token token, Ast *elem, Ast *under
return result;
}
+gb_internal Ast *ast_bit_field_type(AstFile *f, Token token, Ast *backing_type, Token open, Array<Ast *> const &fields, Token close) {
+ Ast *result = alloc_ast_node(f, Ast_BitFieldType);
+ result->BitFieldType.token = token;
+ result->BitFieldType.backing_type = backing_type;
+ result->BitFieldType.open = open;
+ result->BitFieldType.fields = slice_from_array(fields);
+ result->BitFieldType.close = close;
+ return result;
+}
+
+
gb_internal Ast *ast_map_type(AstFile *f, Token token, Ast *key, Ast *value) {
Ast *result = alloc_ast_node(f, Ast_MapType);
result->MapType.token = token;
@@ -1444,9 +1482,26 @@ gb_internal Token expect_token(AstFile *f, TokenKind kind) {
if (prev.kind != kind) {
String c = token_strings[kind];
String p = token_to_string(prev);
+ begin_error_block();
syntax_error(f->curr_token, "Expected '%.*s', got '%.*s'", LIT(c), LIT(p));
+ if (kind == Token_Ident) switch (prev.kind) {
+ case Token_context:
+ error_line("\tSuggestion: '%.*s' is a keyword, would 'ctx' suffice?\n", LIT(prev.string));
+ break;
+ case Token_package:
+ error_line("\tSuggestion: '%.*s' is a keyword, would 'pkg' suffice?\n", LIT(prev.string));
+ break;
+ default:
+ if (token_is_keyword(prev.kind)) {
+ error_line("\tNote: '%.*s' is a keyword\n", LIT(prev.string));
+ }
+ break;
+ }
+
+ end_error_block();
+
if (prev.kind == Token_EOF) {
- gb_exit(1);
+ exit_with_errors();
}
}
@@ -2158,6 +2213,49 @@ gb_internal Array<Ast *> parse_union_variant_list(AstFile *f) {
return variants;
}
+gb_internal void parser_check_polymorphic_record_parameters(AstFile *f, Ast *polymorphic_params) {
+ if (polymorphic_params == nullptr) {
+ return;
+ }
+ if (polymorphic_params->kind != Ast_FieldList) {
+ return;
+ }
+
+
+ enum {Unknown, Dollar, Bare} prefix = Unknown;
+ gb_unused(prefix);
+
+ for (Ast *field : polymorphic_params->FieldList.list) {
+ if (field == nullptr || field->kind != Ast_Field) {
+ continue;
+ }
+ for (Ast *name : field->Field.names) {
+ if (name == nullptr) {
+ continue;
+ }
+ bool error = false;
+
+ if (name->kind == Ast_Ident) {
+ switch (prefix) {
+ case Unknown: prefix = Bare; break;
+ case Dollar: error = true; break;
+ case Bare: break;
+ }
+ } else if (name->kind == Ast_PolyType) {
+ switch (prefix) {
+ case Unknown: prefix = Dollar; break;
+ case Dollar: break;
+ case Bare: error = true; break;
+ }
+ }
+ if (error) {
+ syntax_error(name, "Mixture of polymorphic $ names and normal identifiers are not allowed within record parameters");
+ }
+ }
+ }
+}
+
+
gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
Ast *operand = nullptr; // Operand
switch (f->curr_token.kind) {
@@ -2248,6 +2346,19 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
break;
}
return original_type;
+ } else if (name.string == "row_major" ||
+ name.string == "column_major") {
+ Ast *original_type = parse_type(f);
+ Ast *type = unparen_expr(original_type);
+ switch (type->kind) {
+ case Ast_MatrixType:
+ type->MatrixType.is_row_major = (name.string == "row_major");
+ break;
+ default:
+ syntax_error(type, "Expected a matrix type after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[type->kind]));
+ break;
+ }
+ return original_type;
} else if (name.string == "partial") {
Ast *tag = ast_basic_directive(f, token, name);
Ast *original_expr = parse_expr(f, lhs);
@@ -2257,9 +2368,6 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
return ast_bad_expr(f, token, name);
}
switch (expr->kind) {
- case Ast_ArrayType:
- syntax_error(expr, "#partial has been replaced with #sparse for non-contiguous enumerated array types");
- break;
case Ast_CompoundLit:
expr->CompoundLit.tag = tag;
break;
@@ -2447,6 +2555,9 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
return ast_pointer_type(f, token, elem);
} break;
+ case Token_Mul:
+ return parse_unary_expr(f, true);
+
case Token_OpenBracket: {
Token token = expect_token(f, Token_OpenBracket);
Ast *count_expr = nullptr;
@@ -2500,6 +2611,66 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
return ast_matrix_type(f, token, row_count, column_count, type);
} break;
+ case Token_bit_field: {
+ Token token = expect_token(f, Token_bit_field);
+ isize prev_level;
+
+ prev_level = f->expr_level;
+ f->expr_level = -1;
+
+ Ast *backing_type = parse_type_or_ident(f);
+ if (backing_type == nullptr) {
+ Token token = advance_token(f);
+ syntax_error(token, "Expected a backing type for a 'bit_field'");
+ backing_type = ast_bad_expr(f, token, f->curr_token);
+ }
+
+ skip_possible_newline_for_literal(f);
+ Token open = expect_token_after(f, Token_OpenBrace, "bit_field");
+
+
+ auto fields = array_make<Ast *>(ast_allocator(f), 0, 0);
+
+ while (f->curr_token.kind != Token_CloseBrace &&
+ f->curr_token.kind != Token_EOF) {
+ CommentGroup *docs = nullptr;
+ CommentGroup *comment = nullptr;
+
+ Ast *name = parse_ident(f);
+ bool err_once = false;
+ while (allow_token(f, Token_Comma)) {
+ Ast *dummy_name = parse_ident(f);
+ if (!err_once) {
+ error(dummy_name, "'bit_field' fields do not support multiple names per field");
+ err_once = true;
+ }
+ }
+ expect_token(f, Token_Colon);
+ Ast *type = parse_type(f);
+ expect_token(f, Token_Or);
+ Ast *bit_size = parse_expr(f, true);
+
+ Token tag = {};
+ if (f->curr_token.kind == Token_String) {
+ tag = expect_token(f, Token_String);
+ }
+
+ Ast *bf_field = ast_bit_field_field(f, name, type, bit_size, tag, docs, comment);
+ array_add(&fields, bf_field);
+
+ if (!allow_field_separator(f)) {
+ break;
+ }
+ }
+
+ Token close = expect_closing_brace_of_field_list(f);
+
+ f->expr_level = prev_level;
+
+ return ast_bit_field_type(f, token, backing_type, open, fields, close);
+ }
+
+
case Token_struct: {
Token token = expect_token(f, Token_struct);
Ast *polymorphic_params = nullptr;
@@ -2507,6 +2678,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
bool is_raw_union = false;
bool no_copy = false;
Ast *align = nullptr;
+ Ast *field_align = nullptr;
if (allow_token(f, Token_OpenParen)) {
isize param_count = 0;
@@ -2543,6 +2715,18 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
error_line("\tSuggestion: #align(%s)", s);
gb_string_free(s);
}
+ } else if (tag.string == "field_align") {
+ if (field_align) {
+ syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
+ }
+ field_align = parse_expr(f, true);
+ if (field_align && field_align->kind != Ast_ParenExpr) {
+ ERROR_BLOCK();
+ gbString s = expr_to_string(field_align);
+ syntax_warning(tag, "#field_align requires parentheses around the expression");
+ error_line("\tSuggestion: #field_align(%s)", s);
+ gb_string_free(s);
+ }
} else if (tag.string == "raw_union") {
if (is_raw_union) {
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
@@ -2591,7 +2775,9 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
decls = fields->FieldList.list;
}
- return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, where_token, where_clauses);
+ parser_check_polymorphic_record_parameters(f, polymorphic_params);
+
+ return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, field_align, where_token, where_clauses);
} break;
case Token_union: {
@@ -2683,6 +2869,8 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
auto variants = parse_union_variant_list(f);
Token close = expect_closing_brace_of_field_list(f);
+ parser_check_polymorphic_record_parameters(f, polymorphic_params);
+
return ast_union_type(f, token, variants, polymorphic_params, align, union_kind, where_token, where_clauses);
} break;
@@ -2714,6 +2902,10 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
elem = parse_expr(f, true);
f->allow_range = prev_allow_range;
+ if (elem == nullptr) {
+ syntax_error(token, "Expected a type or range, got nothing");
+ }
+
if (allow_token(f, Token_Semicolon)) {
underlying = parse_type(f);
} else if (allow_token(f, Token_Comma)) {
@@ -2723,6 +2915,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
underlying = parse_type(f);
}
+
expect_token(f, Token_CloseBracket);
return ast_bit_set_type(f, token, elem, underlying);
}
@@ -3103,7 +3296,9 @@ gb_internal Ast *parse_unary_expr(AstFile *f, bool lhs) {
case Token_Sub:
case Token_Xor:
case Token_And:
- case Token_Not: {
+ case Token_Not:
+ case Token_Mul: // Used for error handling when people do C-like things
+ {
Token token = advance_token(f);
Ast *expr = parse_unary_expr(f, lhs);
return ast_unary_expr(f, token, expr);
@@ -3490,6 +3685,7 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) {
expect_token_after(f, Token_Colon, "identifier list");
if ((flags&StmtAllowFlag_Label) && lhs.count == 1) {
bool is_partial = false;
+ bool is_reverse = false;
Token partial_token = {};
if (f->curr_token.kind == Token_Hash) {
// NOTE(bill): This is purely for error messages
@@ -3499,6 +3695,11 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) {
partial_token = expect_token(f, Token_Hash);
expect_token(f, Token_Ident);
is_partial = true;
+ } else if (name.kind == Token_Ident && name.string == "reverse" &&
+ peek_token_n(f, 1).kind == Token_for) {
+ partial_token = expect_token(f, Token_Hash);
+ expect_token(f, Token_Ident);
+ is_reverse = true;
}
}
switch (f->curr_token.kind) {
@@ -3533,6 +3734,18 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) {
break;
}
syntax_error(partial_token, "Incorrect use of directive, use '#partial %.*s: switch'", LIT(ast_token(name).string));
+ } else if (is_reverse) {
+ switch (stmt->kind) {
+ case Ast_RangeStmt:
+ if (stmt->RangeStmt.reverse) {
+ syntax_error(token, "#reverse already applied to a 'for in' statement");
+ }
+ stmt->RangeStmt.reverse = true;
+ break;
+ default:
+ syntax_error(token, "#reverse can only be applied to a 'for in' statement");
+ break;
+ }
}
return stmt;
@@ -3727,14 +3940,15 @@ struct ParseFieldPrefixMapping {
FieldFlag flag;
};
-gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = {
- {str_lit("using"), Token_using, FieldFlag_using},
- {str_lit("no_alias"), Token_Hash, FieldFlag_no_alias},
- {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},
- {str_lit("subtype"), Token_Hash, FieldFlag_subtype},
- {str_lit("by_ptr"), Token_Hash, FieldFlag_by_ptr},
+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("c_vararg"), Token_Hash, FieldFlag_c_vararg},
+ {str_lit("const"), Token_Hash, FieldFlag_const},
+ {str_lit("any_int"), Token_Hash, FieldFlag_any_int},
+ {str_lit("subtype"), Token_Hash, FieldFlag_subtype},
+ {str_lit("by_ptr"), Token_Hash, FieldFlag_by_ptr},
+ {str_lit("no_broadcast"), Token_Hash, FieldFlag_no_broadcast},
};
@@ -3857,6 +4071,15 @@ gb_internal Array<Ast *> convert_to_ident_list(AstFile *f, Array<AstAndFlags> li
case Ast_Ident:
case Ast_BadExpr:
break;
+ case Ast_Implicit:
+ begin_error_block();
+ syntax_error(ident, "Expected an identifier, '%.*s' which is a keyword", LIT(ident->Implicit.string));
+ if (ident->Implicit.kind == Token_context) {
+ error_line("\tSuggestion: Would 'ctx' suffice as an alternative name?\n");
+ }
+ end_error_block();
+ ident = ast_ident(f, blank_token);
+ break;
case Ast_PolyType:
if (allow_poly_names) {
@@ -3870,6 +4093,7 @@ gb_internal Array<Ast *> convert_to_ident_list(AstFile *f, Array<AstAndFlags> li
}
/*fallthrough*/
+
default:
syntax_error(ident, "Expected an identifier");
ident = ast_ident(f, blank_token);
@@ -4812,6 +5036,7 @@ gb_internal Ast *parse_stmt(AstFile *f) {
case Token_Xor:
case Token_Not:
case Token_And:
+ case Token_Mul: // Used for error handling when people do C-like things
s = parse_simple_stmt(f, StmtAllowFlag_Label);
expect_semicolon(f);
return s;
@@ -5264,14 +5489,27 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
return nullptr;
}
+ isize files_with_ext = 0;
isize files_to_reserve = 1; // always reserve 1
for (FileInfo fi : list) {
String name = fi.name;
String ext = path_extension(name);
+ if (ext == FILE_EXT) {
+ files_with_ext += 1;
+ }
if (ext == FILE_EXT && !is_excluded_target_filename(name)) {
files_to_reserve += 1;
}
}
+ if (files_with_ext == 0 || files_to_reserve == 1) {
+ if (files_with_ext != 0) {
+ syntax_error(pos, "Directory contains no .odin files for the specified platform: %.*s", LIT(rel_path));
+ } else {
+ syntax_error(pos, "Empty directory that contains no .odin files: %.*s", LIT(rel_path));
+ }
+ return nullptr;
+ }
+
array_reserve(&pkg->files, files_to_reserve);
for (FileInfo fi : list) {
@@ -5445,6 +5683,11 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
if (collection_name.len > 0) {
+ // NOTE(bill): `base:runtime` == `core:runtime`
+ if (collection_name == "core" && string_starts_with(file_str, str_lit("runtime"))) {
+ collection_name = str_lit("base");
+ }
+
if (collection_name == "system") {
if (node->kind != Ast_ForeignImportDecl) {
syntax_error(node, "The library collection 'system' is restrict for 'foreign_library'");
@@ -5474,13 +5717,12 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
#endif
}
-
if (is_package_name_reserved(file_str)) {
*path = file_str;
- if (collection_name == "core") {
+ if (collection_name == "core" || collection_name == "base") {
return true;
} else {
- syntax_error(node, "The package '%.*s' must be imported with the core library collection: 'core:%.*s'", LIT(file_str), LIT(file_str));
+ syntax_error(node, "The package '%.*s' must be imported with the 'base' library collection: 'base:%.*s'", LIT(file_str), LIT(file_str));
return false;
}
}
@@ -5496,7 +5738,8 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
if (has_windows_drive) {
*path = file_str;
} else {
- String fullpath = string_trim_whitespace(get_fullpath_relative(permanent_allocator(), base_dir, file_str));
+ bool ok = false;
+ String fullpath = string_trim_whitespace(get_fullpath_relative(permanent_allocator(), base_dir, file_str, &ok));
*path = fullpath;
}
return true;
@@ -6009,7 +6252,7 @@ gb_internal ParseFileError process_imported_file(Parser *p, ImportedFile importe
if (err == ParseFile_EmptyFile) {
if (fi.fullpath == p->init_fullpath) {
syntax_error(pos, "Initial file is empty - %.*s\n", LIT(p->init_fullpath));
- gb_exit(1);
+ exit_with_errors();
}
} else {
switch (err) {
@@ -6095,7 +6338,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
if (!path_is_directory(init_fullpath)) {
String const ext = str_lit(".odin");
if (!string_ends_with(init_fullpath, ext)) {
- error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
+ error({}, "Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
return ParseFile_WrongExtension;
}
} else if (init_fullpath.len != 0) {
@@ -6108,7 +6351,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
String short_path = filename_from_path(path);
char *cpath = alloc_cstring(temporary_allocator(), short_path);
if (gb_file_exists(cpath)) {
- error_line("Please specify the executable name with -out:<string> as a directory exists with the same name in the current working directory");
+ error({}, "Please specify the executable name with -out:<string> as a directory exists with the same name in the current working directory");
return ParseFile_DirectoryAlreadyExists;
}
}
@@ -6118,7 +6361,11 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
{ // Add these packages serially and then process them parallel
TokenPos init_pos = {};
{
- String s = get_fullpath_core(permanent_allocator(), str_lit("runtime"));
+ bool ok = false;
+ String s = get_fullpath_base_collection(permanent_allocator(), str_lit("runtime"), &ok);
+ if (!ok) {
+ compiler_error("Unable to find The 'base:runtime' package. Is the ODIN_ROOT set up correctly?");
+ }
try_add_import_path(p, s, s, init_pos, Package_Runtime);
}
@@ -6126,7 +6373,11 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
p->init_fullpath = init_fullpath;
if (build_context.command_kind == Command_test) {
- String s = get_fullpath_core(permanent_allocator(), str_lit("testing"));
+ bool ok = false;
+ String s = get_fullpath_core_collection(permanent_allocator(), str_lit("testing"), &ok);
+ if (!ok) {
+ compiler_error("Unable to find The 'core:testing' package. Is the ODIN_ROOT set up correctly?");
+ }
try_add_import_path(p, s, s, init_pos, Package_Normal);
}
@@ -6136,7 +6387,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
if (!path_is_directory(fullpath)) {
String const ext = str_lit(".odin");
if (!string_ends_with(fullpath, ext)) {
- error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath));
+ error({}, "Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath));
return ParseFile_WrongExtension;
}
}
diff --git a/src/parser.hpp b/src/parser.hpp
index cc1836ef3..5820275c8 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -310,6 +310,8 @@ enum StateFlag : u8 {
enum ViralStateFlag : u8 {
ViralStateFlag_ContainsDeferredProcedure = 1<<0,
+ ViralStateFlag_ContainsOrBreak = 1<<1,
+ ViralStateFlag_ContainsOrReturn = 1<<2,
};
@@ -324,6 +326,7 @@ enum FieldFlag : u32 {
FieldFlag_any_int = 1<<6,
FieldFlag_subtype = 1<<7,
FieldFlag_by_ptr = 1<<8,
+ FieldFlag_no_broadcast = 1<<9, // disallow array programming
// Internal use by the parser only
FieldFlag_Tags = 1<<10,
@@ -334,7 +337,7 @@ 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_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast,
FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags,
};
@@ -429,6 +432,7 @@ AST_KIND(_ExprBegin, "", bool) \
Ast *expr, *selector; \
u8 swizzle_count; /*maximum of 4 components, if set, count >= 2*/ \
u8 swizzle_indices; /*2 bits per component*/ \
+ bool is_bit_field; \
}) \
AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \
AST_KIND(SelectorCallExpr, "selector call expression", struct { \
@@ -650,6 +654,14 @@ AST_KIND(_DeclEnd, "", bool) \
CommentGroup * docs; \
CommentGroup * comment; \
}) \
+ AST_KIND(BitFieldField, "bit field field", struct { \
+ Ast * name; \
+ Ast * type; \
+ Ast * bit_size; \
+ Token tag; \
+ CommentGroup *docs; \
+ CommentGroup *comment; \
+ }) \
AST_KIND(FieldList, "field list", struct { \
Token token; \
Slice<Ast *> list; \
@@ -713,6 +725,7 @@ AST_KIND(_TypeBegin, "", bool) \
isize field_count; \
Ast *polymorphic_params; \
Ast *align; \
+ Ast *field_align; \
Token where_token; \
Slice<Ast *> where_clauses; \
bool is_packed; \
@@ -741,6 +754,14 @@ AST_KIND(_TypeBegin, "", bool) \
Ast * elem; \
Ast * underlying; \
}) \
+ AST_KIND(BitFieldType, "bit field type", struct { \
+ Scope *scope; \
+ Token token; \
+ Ast * backing_type; \
+ Token open; \
+ Slice<Ast *> fields; /* BitFieldField */ \
+ Token close; \
+ }) \
AST_KIND(MapType, "map type", struct { \
Token token; \
Ast *count; \
@@ -752,6 +773,7 @@ AST_KIND(_TypeBegin, "", bool) \
Ast *row_count; \
Ast *column_count; \
Ast *elem; \
+ bool is_row_major; \
}) \
AST_KIND(_TypeEnd, "", bool)
diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp
index f49c40f16..b2e12999b 100644
--- a/src/parser_pos.cpp
+++ b/src/parser_pos.cpp
@@ -111,6 +111,7 @@ gb_internal Token ast_token(Ast *node) {
case Ast_UnionType: return node->UnionType.token;
case Ast_EnumType: return node->EnumType.token;
case Ast_BitSetType: return node->BitSetType.token;
+ case Ast_BitFieldType: return node->BitFieldType.token;
case Ast_MapType: return node->MapType.token;
case Ast_MatrixType: return node->MatrixType.token;
}
@@ -364,6 +365,8 @@ Token ast_end_token(Ast *node) {
return ast_end_token(node->BitSetType.underlying);
}
return ast_end_token(node->BitSetType.elem);
+ case Ast_BitFieldType:
+ return node->BitFieldType.close;
case Ast_MapType: return ast_end_token(node->MapType.value);
case Ast_MatrixType: return ast_end_token(node->MatrixType.elem);
}
diff --git a/src/path.cpp b/src/path.cpp
index de80c9def..b07f20870 100644
--- a/src/path.cpp
+++ b/src/path.cpp
@@ -1,461 +1,461 @@
-/*
- Path handling utilities.
-*/
-#if !defined(GB_SYSTEM_WINDOWS)
-#include <unistd.h>
-#endif
-
-gb_internal String remove_extension_from_path(String const &s) {
- if (s.len != 0 && s.text[s.len-1] == '.') {
- return s;
- }
- for (isize i = s.len-1; i >= 0; i--) {
- if (s[i] == '.') {
- return substring(s, 0, i);
- }
- }
- return s;
-}
-
-gb_internal String remove_directory_from_path(String const &s) {
- isize len = 0;
- for (isize i = s.len-1; i >= 0; i--) {
- if (s[i] == '/' ||
- s[i] == '\\') {
- break;
- }
- len += 1;
- }
- return substring(s, s.len-len, s.len);
-}
-
-
-// NOTE(Mark Naughton): getcwd as String
-#if !defined(GB_SYSTEM_WINDOWS)
-gb_internal String get_current_directory(void) {
- char cwd[256];
- getcwd(cwd, 256);
-
- return make_string_c(cwd);
-}
-
-#else
-gb_internal String get_current_directory(void) {
- gbAllocator a = heap_allocator();
-
- wchar_t cwd[256];
- GetCurrentDirectoryW(256, cwd);
-
- String16 wstr = make_string16_c(cwd);
-
- return string16_to_string(a, wstr);
-}
-#endif
-
-gb_internal bool path_is_directory(String path);
-
-gb_internal String directory_from_path(String const &s) {
- if (path_is_directory(s)) {
- return s;
- }
-
- isize i = s.len-1;
- for (; i >= 0; i--) {
- if (s[i] == '/' ||
- s[i] == '\\') {
- break;
- }
- }
- if (i >= 0) {
- return substring(s, 0, i);
- }
- return substring(s, 0, 0);
-}
-
-#if defined(GB_SYSTEM_WINDOWS)
- gb_internal bool path_is_directory(String path) {
- gbAllocator a = heap_allocator();
- String16 wstr = string_to_string16(a, path);
- defer (gb_free(a, wstr.text));
-
- i32 attribs = GetFileAttributesW(wstr.text);
- if (attribs < 0) return false;
-
- return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
- }
-
-#else
- gb_internal bool path_is_directory(String path) {
- gbAllocator a = heap_allocator();
- char *copy = cast(char *)copy_string(a, path).text;
- defer (gb_free(a, copy));
-
- struct stat s;
- if (stat(copy, &s) == 0) {
- return (s.st_mode & S_IFDIR) != 0;
- }
- return false;
- }
-#endif
-
-
-gb_internal String path_to_full_path(gbAllocator a, String path) {
- gbAllocator ha = heap_allocator();
- char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
- defer (gb_free(ha, path_c));
-
- char *fullpath = gb_path_get_full_name(a, path_c);
- String res = string_trim_whitespace(make_string_c(fullpath));
-#if defined(GB_SYSTEM_WINDOWS)
- for (isize i = 0; i < res.len; i++) {
- if (res.text[i] == '\\') {
- res.text[i] = '/';
- }
- }
-#endif
- return copy_string(a, res);
-}
-
-struct Path {
- String basename;
- String name;
- String ext;
-};
-
-// NOTE(Jeroen): Naively turns a Path into a string.
-gb_internal String path_to_string(gbAllocator a, Path path) {
- if (path.basename.len + path.name.len + path.ext.len == 0) {
- return make_string(nullptr, 0);
- }
-
- isize len = path.basename.len + 1 + path.name.len + 1;
- if (path.ext.len > 0) {
- len += path.ext.len + 1;
- }
-
- u8 *str = gb_alloc_array(a, u8, len);
-
- isize i = 0;
- gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
-
- gb_memmove(str+i, "/", 1); i += 1;
-
- gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
- if (path.ext.len > 0) {
- gb_memmove(str+i, ".", 1); i += 1;
- gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
- }
- str[i] = 0;
-
- String res = make_string(str, i);
- res = string_trim_whitespace(res);
- return res;
-}
-
-// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
-gb_internal String path_to_full_path(gbAllocator a, Path path) {
- String temp = path_to_string(heap_allocator(), path);
- defer (gb_free(heap_allocator(), temp.text));
-
- return path_to_full_path(a, temp);
-}
-
-// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
-// and then breaks it into its components to make a Path.
-gb_internal Path path_from_string(gbAllocator a, String const &path) {
- Path res = {};
-
- if (path.len == 0) return res;
-
- String fullpath = path_to_full_path(a, path);
- defer (gb_free(heap_allocator(), fullpath.text));
-
- res.basename = directory_from_path(fullpath);
- res.basename = copy_string(a, res.basename);
-
- if (path_is_directory(fullpath)) {
- // It's a directory. We don't need to tinker with the name and extension.
- // It could have a superfluous trailing `/`. Remove it if so.
- if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
- res.basename.len--;
- }
- return res;
- }
-
- // Note(Dragos): Is the copy_string required if it's a substring?
- isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
- res.name = substring(fullpath, name_start, fullpath.len);
- res.name = remove_extension_from_path(res.name);
- res.name = copy_string(a, res.name);
-
- res.ext = path_extension(fullpath, false); // false says not to include the dot.
- res.ext = copy_string(a, res.ext);
- return res;
-}
-
-// NOTE(Jeroen): Takes a path String and returns the last path element.
-gb_internal String last_path_element(String const &path) {
- isize count = 0;
- u8 * start = (u8 *)(&path.text[path.len - 1]);
- for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
- count++;
- start--;
- }
- if (count > 0) {
- start++; // Advance past the `/` and return the substring.
- String res = make_string(start, count);
- return res;
- }
- // Must be a root path like `/` or `C:/`, return empty String.
- return STR_LIT("");
-}
-
-gb_internal bool path_is_directory(Path path) {
- String path_string = path_to_full_path(heap_allocator(), path);
- defer (gb_free(heap_allocator(), path_string.text));
-
- return path_is_directory(path_string);
-}
-
-struct FileInfo {
- String name;
- String fullpath;
- i64 size;
- bool is_dir;
-};
-
-enum ReadDirectoryError {
- ReadDirectory_None,
-
- ReadDirectory_InvalidPath,
- ReadDirectory_NotExists,
- ReadDirectory_Permission,
- ReadDirectory_NotDir,
- ReadDirectory_Empty,
- ReadDirectory_Unknown,
-
- ReadDirectory_COUNT,
-};
-
-gb_internal i64 get_file_size(String path) {
- char *c_str = alloc_cstring(heap_allocator(), path);
- defer (gb_free(heap_allocator(), c_str));
-
- gbFile f = {};
- gbFileError err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
- if (err != gbFileError_None) {
- return -1;
- }
- return gb_file_size(&f);
-}
-
-
-#if defined(GB_SYSTEM_WINDOWS)
-gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
- GB_ASSERT(fi != nullptr);
-
-
- while (path.len > 0) {
- Rune end = path[path.len-1];
- if (end == '/') {
- path.len -= 1;
- } else if (end == '\\') {
- path.len -= 1;
- } else {
- break;
- }
- }
-
- if (path.len == 0) {
- return ReadDirectory_InvalidPath;
- }
- {
- char *c_str = alloc_cstring(temporary_allocator(), path);
- gbFile f = {};
- gbFileError file_err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
-
- switch (file_err) {
- case gbFileError_Invalid: return ReadDirectory_InvalidPath;
- case gbFileError_NotExists: return ReadDirectory_NotExists;
- // case gbFileError_Permission: return ReadDirectory_Permission;
- }
- }
-
- if (!path_is_directory(path)) {
- return ReadDirectory_NotDir;
- }
-
-
- gbAllocator a = heap_allocator();
- char *new_path = gb_alloc_array(a, char, path.len+3);
- defer (gb_free(a, new_path));
-
- gb_memmove(new_path, path.text, path.len);
- gb_memmove(new_path+path.len, "/*", 2);
- new_path[path.len+2] = 0;
-
- String np = make_string(cast(u8 *)new_path, path.len+2);
- String16 wstr = string_to_string16(a, np);
- defer (gb_free(a, wstr.text));
-
- WIN32_FIND_DATAW file_data = {};
- HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
- if (find_file == INVALID_HANDLE_VALUE) {
- return ReadDirectory_Unknown;
- }
- defer (FindClose(find_file));
-
- array_init(fi, a, 0, 100);
-
- do {
- wchar_t *filename_w = file_data.cFileName;
- u64 size = cast(u64)file_data.nFileSizeLow;
- size |= (cast(u64)file_data.nFileSizeHigh) << 32;
- String name = string16_to_string(a, make_string16_c(filename_w));
- if (name == "." || name == "..") {
- gb_free(a, name.text);
- continue;
- }
-
- String filepath = {};
- filepath.len = path.len+1+name.len;
- filepath.text = gb_alloc_array(a, u8, filepath.len+1);
- defer (gb_free(a, filepath.text));
- gb_memmove(filepath.text, path.text, path.len);
- gb_memmove(filepath.text+path.len, "/", 1);
- gb_memmove(filepath.text+path.len+1, name.text, name.len);
-
- FileInfo info = {};
- info.name = name;
- info.fullpath = path_to_full_path(a, filepath);
- info.size = cast(i64)size;
- info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- array_add(fi, info);
- } while (FindNextFileW(find_file, &file_data));
-
- if (fi->count == 0) {
- return ReadDirectory_Empty;
- }
-
- return ReadDirectory_None;
-}
-#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
-
-#include <dirent.h>
-
-gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
- GB_ASSERT(fi != nullptr);
-
- gbAllocator a = heap_allocator();
-
- char *c_path = alloc_cstring(a, path);
- defer (gb_free(a, c_path));
-
- DIR *dir = opendir(c_path);
- if (!dir) {
- switch (errno) {
- case ENOENT:
- return ReadDirectory_NotExists;
- case EACCES:
- return ReadDirectory_Permission;
- case ENOTDIR:
- return ReadDirectory_NotDir;
- default:
- // ENOMEM: out of memory
- // EMFILE: per-process limit on open fds reached
- // ENFILE: system-wide limit on total open files reached
- return ReadDirectory_Unknown;
- }
- GB_PANIC("unreachable");
- }
-
- array_init(fi, a, 0, 100);
-
- for (;;) {
- struct dirent *entry = readdir(dir);
- if (entry == nullptr) {
- break;
- }
-
- String name = make_string_c(entry->d_name);
- if (name == "." || name == "..") {
- continue;
- }
-
- String filepath = {};
- filepath.len = path.len+1+name.len;
- filepath.text = gb_alloc_array(a, u8, filepath.len+1);
- defer (gb_free(a, filepath.text));
- gb_memmove(filepath.text, path.text, path.len);
- gb_memmove(filepath.text+path.len, "/", 1);
- gb_memmove(filepath.text+path.len+1, name.text, name.len);
- filepath.text[filepath.len] = 0;
-
-
- struct stat dir_stat = {};
-
- if (stat((char *)filepath.text, &dir_stat)) {
- continue;
- }
-
- if (S_ISDIR(dir_stat.st_mode)) {
- continue;
- }
-
- i64 size = dir_stat.st_size;
-
- FileInfo info = {};
- info.name = name;
- info.fullpath = path_to_full_path(a, filepath);
- info.size = size;
- array_add(fi, info);
- }
-
- if (fi->count == 0) {
- return ReadDirectory_Empty;
- }
-
- return ReadDirectory_None;
-}
-
-
-#else
-#error Implement read_directory
-#endif
-
-#if !defined(GB_SYSTEM_WINDOWS)
-gb_internal bool write_directory(String path) {
- char const *pathname = (char *) path.text;
-
- if (access(pathname, W_OK) < 0) {
- return false;
- }
-
- return true;
-}
-#else
-gb_internal bool write_directory(String path) {
- String16 wstr = string_to_string16(heap_allocator(), path);
- LPCWSTR wdirectory_name = wstr.text;
-
- HANDLE directory = CreateFileW(wdirectory_name,
- GENERIC_WRITE,
- 0,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
-
- if (directory == INVALID_HANDLE_VALUE) {
- DWORD error_code = GetLastError();
- if (error_code == ERROR_ACCESS_DENIED) {
- return false;
- }
- }
-
- CloseHandle(directory);
- return true;
-}
-#endif
+/*
+ Path handling utilities.
+*/
+#if !defined(GB_SYSTEM_WINDOWS)
+#include <unistd.h>
+#endif
+
+gb_internal String remove_extension_from_path(String const &s) {
+ if (s.len != 0 && s.text[s.len-1] == '.') {
+ return s;
+ }
+ for (isize i = s.len-1; i >= 0; i--) {
+ if (s[i] == '.') {
+ return substring(s, 0, i);
+ }
+ }
+ return s;
+}
+
+gb_internal String remove_directory_from_path(String const &s) {
+ isize len = 0;
+ for (isize i = s.len-1; i >= 0; i--) {
+ if (s[i] == '/' ||
+ s[i] == '\\') {
+ break;
+ }
+ len += 1;
+ }
+ return substring(s, s.len-len, s.len);
+}
+
+
+// NOTE(Mark Naughton): getcwd as String
+#if !defined(GB_SYSTEM_WINDOWS)
+gb_internal String get_current_directory(void) {
+ char cwd[256];
+ getcwd(cwd, 256);
+
+ return make_string_c(cwd);
+}
+
+#else
+gb_internal String get_current_directory(void) {
+ gbAllocator a = heap_allocator();
+
+ wchar_t cwd[256];
+ GetCurrentDirectoryW(256, cwd);
+
+ String16 wstr = make_string16_c(cwd);
+
+ return string16_to_string(a, wstr);
+}
+#endif
+
+gb_internal bool path_is_directory(String path);
+
+gb_internal String directory_from_path(String const &s) {
+ if (path_is_directory(s)) {
+ return s;
+ }
+
+ isize i = s.len-1;
+ for (; i >= 0; i--) {
+ if (s[i] == '/' ||
+ s[i] == '\\') {
+ break;
+ }
+ }
+ if (i >= 0) {
+ return substring(s, 0, i);
+ }
+ return substring(s, 0, 0);
+}
+
+#if defined(GB_SYSTEM_WINDOWS)
+ gb_internal bool path_is_directory(String path) {
+ gbAllocator a = heap_allocator();
+ String16 wstr = string_to_string16(a, path);
+ defer (gb_free(a, wstr.text));
+
+ i32 attribs = GetFileAttributesW(wstr.text);
+ if (attribs < 0) return false;
+
+ return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ }
+
+#else
+ gb_internal bool path_is_directory(String path) {
+ gbAllocator a = heap_allocator();
+ char *copy = cast(char *)copy_string(a, path).text;
+ defer (gb_free(a, copy));
+
+ struct stat s;
+ if (stat(copy, &s) == 0) {
+ return (s.st_mode & S_IFDIR) != 0;
+ }
+ return false;
+ }
+#endif
+
+
+gb_internal String path_to_full_path(gbAllocator a, String path) {
+ gbAllocator ha = heap_allocator();
+ char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
+ defer (gb_free(ha, path_c));
+
+ char *fullpath = gb_path_get_full_name(a, path_c);
+ String res = string_trim_whitespace(make_string_c(fullpath));
+#if defined(GB_SYSTEM_WINDOWS)
+ for (isize i = 0; i < res.len; i++) {
+ if (res.text[i] == '\\') {
+ res.text[i] = '/';
+ }
+ }
+#endif
+ return copy_string(a, res);
+}
+
+struct Path {
+ String basename;
+ String name;
+ String ext;
+};
+
+// NOTE(Jeroen): Naively turns a Path into a string.
+gb_internal String path_to_string(gbAllocator a, Path path) {
+ if (path.basename.len + path.name.len + path.ext.len == 0) {
+ return make_string(nullptr, 0);
+ }
+
+ isize len = path.basename.len + 1 + path.name.len + 1;
+ if (path.ext.len > 0) {
+ len += path.ext.len + 1;
+ }
+
+ u8 *str = gb_alloc_array(a, u8, len);
+
+ isize i = 0;
+ gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
+
+ gb_memmove(str+i, "/", 1); i += 1;
+
+ gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
+ if (path.ext.len > 0) {
+ gb_memmove(str+i, ".", 1); i += 1;
+ gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
+ }
+ str[i] = 0;
+
+ String res = make_string(str, i);
+ res = string_trim_whitespace(res);
+ return res;
+}
+
+// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
+gb_internal String path_to_full_path(gbAllocator a, Path path) {
+ String temp = path_to_string(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), temp.text));
+
+ return path_to_full_path(a, temp);
+}
+
+// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
+// and then breaks it into its components to make a Path.
+gb_internal Path path_from_string(gbAllocator a, String const &path) {
+ Path res = {};
+
+ if (path.len == 0) return res;
+
+ String fullpath = path_to_full_path(a, path);
+ defer (gb_free(heap_allocator(), fullpath.text));
+
+ res.basename = directory_from_path(fullpath);
+ res.basename = copy_string(a, res.basename);
+
+ if (path_is_directory(fullpath)) {
+ // It's a directory. We don't need to tinker with the name and extension.
+ // It could have a superfluous trailing `/`. Remove it if so.
+ if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
+ res.basename.len--;
+ }
+ return res;
+ }
+
+ // Note(Dragos): Is the copy_string required if it's a substring?
+ isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
+ res.name = substring(fullpath, name_start, fullpath.len);
+ res.name = remove_extension_from_path(res.name);
+ res.name = copy_string(a, res.name);
+
+ res.ext = path_extension(fullpath, false); // false says not to include the dot.
+ res.ext = copy_string(a, res.ext);
+ return res;
+}
+
+// NOTE(Jeroen): Takes a path String and returns the last path element.
+gb_internal String last_path_element(String const &path) {
+ isize count = 0;
+ u8 * start = (u8 *)(&path.text[path.len - 1]);
+ for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
+ count++;
+ start--;
+ }
+ if (count > 0) {
+ start++; // Advance past the `/` and return the substring.
+ String res = make_string(start, count);
+ return res;
+ }
+ // Must be a root path like `/` or `C:/`, return empty String.
+ return STR_LIT("");
+}
+
+gb_internal bool path_is_directory(Path path) {
+ String path_string = path_to_full_path(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), path_string.text));
+
+ return path_is_directory(path_string);
+}
+
+struct FileInfo {
+ String name;
+ String fullpath;
+ i64 size;
+ bool is_dir;
+};
+
+enum ReadDirectoryError {
+ ReadDirectory_None,
+
+ ReadDirectory_InvalidPath,
+ ReadDirectory_NotExists,
+ ReadDirectory_Permission,
+ ReadDirectory_NotDir,
+ ReadDirectory_Empty,
+ ReadDirectory_Unknown,
+
+ ReadDirectory_COUNT,
+};
+
+gb_internal i64 get_file_size(String path) {
+ char *c_str = alloc_cstring(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), c_str));
+
+ gbFile f = {};
+ gbFileError err = gb_file_open(&f, c_str);
+ defer (gb_file_close(&f));
+ if (err != gbFileError_None) {
+ return -1;
+ }
+ return gb_file_size(&f);
+}
+
+
+#if defined(GB_SYSTEM_WINDOWS)
+gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+ GB_ASSERT(fi != nullptr);
+
+
+ while (path.len > 0) {
+ Rune end = path[path.len-1];
+ if (end == '/') {
+ path.len -= 1;
+ } else if (end == '\\') {
+ path.len -= 1;
+ } else {
+ break;
+ }
+ }
+
+ if (path.len == 0) {
+ return ReadDirectory_InvalidPath;
+ }
+ {
+ char *c_str = alloc_cstring(temporary_allocator(), path);
+ gbFile f = {};
+ gbFileError file_err = gb_file_open(&f, c_str);
+ defer (gb_file_close(&f));
+
+ switch (file_err) {
+ case gbFileError_Invalid: return ReadDirectory_InvalidPath;
+ case gbFileError_NotExists: return ReadDirectory_NotExists;
+ // case gbFileError_Permission: return ReadDirectory_Permission;
+ }
+ }
+
+ if (!path_is_directory(path)) {
+ return ReadDirectory_NotDir;
+ }
+
+
+ gbAllocator a = heap_allocator();
+ char *new_path = gb_alloc_array(a, char, path.len+3);
+ defer (gb_free(a, new_path));
+
+ gb_memmove(new_path, path.text, path.len);
+ gb_memmove(new_path+path.len, "/*", 2);
+ new_path[path.len+2] = 0;
+
+ String np = make_string(cast(u8 *)new_path, path.len+2);
+ String16 wstr = string_to_string16(a, np);
+ defer (gb_free(a, wstr.text));
+
+ WIN32_FIND_DATAW file_data = {};
+ HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
+ if (find_file == INVALID_HANDLE_VALUE) {
+ return ReadDirectory_Unknown;
+ }
+ defer (FindClose(find_file));
+
+ array_init(fi, a, 0, 100);
+
+ do {
+ wchar_t *filename_w = file_data.cFileName;
+ u64 size = cast(u64)file_data.nFileSizeLow;
+ size |= (cast(u64)file_data.nFileSizeHigh) << 32;
+ String name = string16_to_string(a, make_string16_c(filename_w));
+ if (name == "." || name == "..") {
+ gb_free(a, name.text);
+ continue;
+ }
+
+ String filepath = {};
+ filepath.len = path.len+1+name.len;
+ filepath.text = gb_alloc_array(a, u8, filepath.len+1);
+ defer (gb_free(a, filepath.text));
+ gb_memmove(filepath.text, path.text, path.len);
+ gb_memmove(filepath.text+path.len, "/", 1);
+ gb_memmove(filepath.text+path.len+1, name.text, name.len);
+
+ FileInfo info = {};
+ info.name = name;
+ info.fullpath = path_to_full_path(a, filepath);
+ info.size = cast(i64)size;
+ info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ array_add(fi, info);
+ } while (FindNextFileW(find_file, &file_data));
+
+ if (fi->count == 0) {
+ return ReadDirectory_Empty;
+ }
+
+ return ReadDirectory_None;
+}
+#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_HAIKU)
+
+#include <dirent.h>
+
+gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+ GB_ASSERT(fi != nullptr);
+
+ gbAllocator a = heap_allocator();
+
+ char *c_path = alloc_cstring(a, path);
+ defer (gb_free(a, c_path));
+
+ DIR *dir = opendir(c_path);
+ if (!dir) {
+ switch (errno) {
+ case ENOENT:
+ return ReadDirectory_NotExists;
+ case EACCES:
+ return ReadDirectory_Permission;
+ case ENOTDIR:
+ return ReadDirectory_NotDir;
+ default:
+ // ENOMEM: out of memory
+ // EMFILE: per-process limit on open fds reached
+ // ENFILE: system-wide limit on total open files reached
+ return ReadDirectory_Unknown;
+ }
+ GB_PANIC("unreachable");
+ }
+
+ array_init(fi, a, 0, 100);
+
+ for (;;) {
+ struct dirent *entry = readdir(dir);
+ if (entry == nullptr) {
+ break;
+ }
+
+ String name = make_string_c(entry->d_name);
+ if (name == "." || name == "..") {
+ continue;
+ }
+
+ String filepath = {};
+ filepath.len = path.len+1+name.len;
+ filepath.text = gb_alloc_array(a, u8, filepath.len+1);
+ defer (gb_free(a, filepath.text));
+ gb_memmove(filepath.text, path.text, path.len);
+ gb_memmove(filepath.text+path.len, "/", 1);
+ gb_memmove(filepath.text+path.len+1, name.text, name.len);
+ filepath.text[filepath.len] = 0;
+
+
+ struct stat dir_stat = {};
+
+ if (stat((char *)filepath.text, &dir_stat)) {
+ continue;
+ }
+
+ if (S_ISDIR(dir_stat.st_mode)) {
+ continue;
+ }
+
+ i64 size = dir_stat.st_size;
+
+ FileInfo info = {};
+ info.name = copy_string(a, name);
+ info.fullpath = path_to_full_path(a, filepath);
+ info.size = size;
+ array_add(fi, info);
+ }
+
+ if (fi->count == 0) {
+ return ReadDirectory_Empty;
+ }
+
+ return ReadDirectory_None;
+}
+
+
+#else
+#error Implement read_directory
+#endif
+
+#if !defined(GB_SYSTEM_WINDOWS)
+gb_internal bool write_directory(String path) {
+ char const *pathname = (char *) path.text;
+
+ if (access(pathname, W_OK) < 0) {
+ return false;
+ }
+
+ return true;
+}
+#else
+gb_internal bool write_directory(String path) {
+ String16 wstr = string_to_string16(heap_allocator(), path);
+ LPCWSTR wdirectory_name = wstr.text;
+
+ HANDLE directory = CreateFileW(wdirectory_name,
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+
+ if (directory == INVALID_HANDLE_VALUE) {
+ DWORD error_code = GetLastError();
+ if (error_code == ERROR_ACCESS_DENIED) {
+ return false;
+ }
+ }
+
+ CloseHandle(directory);
+ return true;
+}
+#endif
diff --git a/src/string.cpp b/src/string.cpp
index 9fb933b1b..7bfa52f33 100644
--- a/src/string.cpp
+++ b/src/string.cpp
@@ -89,7 +89,6 @@ gb_internal char *alloc_cstring(gbAllocator a, String s) {
}
-
gb_internal gb_inline bool str_eq_ignore_case(String const &a, String const &b) {
if (a.len == b.len) {
for (isize i = 0; i < a.len; i++) {
@@ -104,6 +103,16 @@ gb_internal gb_inline bool str_eq_ignore_case(String const &a, String const &b)
return false;
}
+template <isize N>
+gb_internal gb_inline bool str_eq_ignore_case(String const &a, char const (&b_)[N]) {
+ if (a.len != N-1) {
+ return false;
+ }
+ String b = {cast(u8 *)b_, N-1};
+ return str_eq_ignore_case(a, b);
+}
+
+
gb_internal void string_to_lower(String *s) {
for (isize i = 0; i < s->len; i++) {
s->text[i] = gb_char_to_lower(s->text[i]);
@@ -293,6 +302,18 @@ gb_internal String filename_from_path(String s) {
return make_string(nullptr, 0);
}
+
+gb_internal String filename_without_directory(String s) {
+ isize j = 0;
+ for (j = s.len-1; j >= 0; j--) {
+ if (s[j] == '/' ||
+ s[j] == '\\') {
+ break;
+ }
+ }
+ return substring(s, gb_max(j+1, 0), s.len);
+}
+
gb_internal String concatenate_strings(gbAllocator a, String const &x, String const &y) {
isize len = x.len+y.len;
u8 *data = gb_alloc_array(a, u8, len+1);
diff --git a/src/threading.cpp b/src/threading.cpp
index c283da425..fbe8997d1 100644
--- a/src/threading.cpp
+++ b/src/threading.cpp
@@ -107,6 +107,20 @@ gb_internal void thread_set_name (Thread *t, char const *name);
gb_internal void yield_thread(void);
gb_internal void yield_process(void);
+struct Wait_Signal {
+ Futex futex;
+};
+
+gb_internal void wait_signal_until_available(Wait_Signal *ws) {
+ if (ws->futex.load() == 0) {
+ futex_wait(&ws->futex, 0);
+ }
+}
+
+gb_internal void wait_signal_set(Wait_Signal *ws) {
+ ws->futex.store(1);
+ futex_broadcast(&ws->futex);
+}
struct MutexGuard {
MutexGuard() = delete;
@@ -119,17 +133,25 @@ struct MutexGuard {
explicit MutexGuard(RecursiveMutex *rm) noexcept : rm{rm} {
mutex_lock(this->rm);
}
+ explicit MutexGuard(RwMutex *rwm) noexcept : rwm{rwm} {
+ rw_mutex_lock(this->rwm);
+ }
explicit MutexGuard(BlockingMutex &bm) noexcept : bm{&bm} {
mutex_lock(this->bm);
}
explicit MutexGuard(RecursiveMutex &rm) noexcept : rm{&rm} {
mutex_lock(this->rm);
}
+ explicit MutexGuard(RwMutex &rwm) noexcept : rwm{&rwm} {
+ rw_mutex_lock(this->rwm);
+ }
~MutexGuard() noexcept {
if (this->bm) {
mutex_unlock(this->bm);
} else if (this->rm) {
mutex_unlock(this->rm);
+ } else if (this->rwm) {
+ rw_mutex_unlock(this->rwm);
}
}
@@ -137,10 +159,12 @@ struct MutexGuard {
BlockingMutex *bm;
RecursiveMutex *rm;
+ RwMutex *rwm;
};
#define MUTEX_GUARD_BLOCK(m) if (MutexGuard GB_DEFER_3(_mutex_guard_){m})
#define MUTEX_GUARD(m) mutex_lock(m); defer (mutex_unlock(m))
+#define RW_MUTEX_GUARD(m) rw_mutex_lock(m); defer (rw_mutex_unlock(m))
struct RecursiveMutex {
@@ -466,6 +490,8 @@ gb_internal u32 thread_current_id(void) {
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
#elif defined(GB_SYSTEM_LINUX)
thread_id = gettid();
+#elif defined(GB_SYSTEM_HAIKU)
+ thread_id = find_thread(NULL);
#else
#error Unsupported architecture for thread_current_id()
#endif
@@ -732,6 +758,11 @@ gb_internal void futex_wait(Futex *f, Footex val) {
#elif defined(GB_SYSTEM_OSX)
+#if __has_include(<os/os_sync_wait_on_address.h>)
+ #define DARWIN_WAIT_ON_ADDRESS_AVAILABLE
+ #include <os/os_sync_wait_on_address.h>
+#endif
+
#define UL_COMPARE_AND_WAIT 0x00000001
#define ULF_NO_ERRNO 0x01000000
@@ -739,6 +770,23 @@ extern "C" int __ulock_wait(uint32_t operation, void *addr, uint64_t value, uint
extern "C" int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
gb_internal void futex_signal(Futex *f) {
+ #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
+ if (__builtin_available(macOS 14.4, *)) {
+ for (;;) {
+ int ret = os_sync_wake_by_address_any(f, sizeof(Futex), OS_SYNC_WAKE_BY_ADDRESS_NONE);
+ if (ret >= 0) {
+ return;
+ }
+ if (errno == EINTR || errno == EFAULT) {
+ continue;
+ }
+ if (errno == ENOENT) {
+ return;
+ }
+ GB_PANIC("Failed in futex wake %d %d!\n", ret, errno);
+ }
+ } else {
+ #endif
for (;;) {
int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0);
if (ret >= 0) {
@@ -752,9 +800,29 @@ gb_internal void futex_signal(Futex *f) {
}
GB_PANIC("Failed in futex wake!\n");
}
+ #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
+ }
+ #endif
}
gb_internal void futex_broadcast(Futex *f) {
+ #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
+ if (__builtin_available(macOS 14.4, *)) {
+ for (;;) {
+ int ret = os_sync_wake_by_address_all(f, sizeof(Footex), OS_SYNC_WAKE_BY_ADDRESS_NONE);
+ if (ret >= 0) {
+ return;
+ }
+ if (errno == EINTR || errno == EFAULT) {
+ continue;
+ }
+ if (errno == ENOENT) {
+ return;
+ }
+ GB_PANIC("Failed in futext wake %d %d!\n", ret, errno);
+ }
+ } else {
+ #endif
for (;;) {
enum { ULF_WAKE_ALL = 0x00000100 };
int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0);
@@ -769,9 +837,32 @@ gb_internal void futex_broadcast(Futex *f) {
}
GB_PANIC("Failed in futex wake!\n");
}
+ #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
+ }
+ #endif
}
gb_internal void futex_wait(Futex *f, Footex val) {
+ #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
+ if (__builtin_available(macOS 14.4, *)) {
+ for (;;) {
+ int ret = os_sync_wait_on_address(f, cast(uint64_t)(val), sizeof(Footex), OS_SYNC_WAIT_ON_ADDRESS_NONE);
+ if (ret >= 0) {
+ if (*f != val) {
+ return;
+ }
+ continue;
+ }
+ if (errno == EINTR || errno == EFAULT) {
+ continue;
+ }
+ if (errno == ENOENT) {
+ return;
+ }
+ GB_PANIC("Failed in futex wait %d %d!\n", ret, errno);
+ }
+ } else {
+ #endif
for (;;) {
int ret = __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, val, 0);
if (ret >= 0) {
@@ -789,7 +880,11 @@ gb_internal void futex_wait(Futex *f, Footex val) {
GB_PANIC("Failed in futex wait!\n");
}
+ #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
+ }
+ #endif
}
+
#elif defined(GB_SYSTEM_WINDOWS)
gb_internal void futex_signal(Futex *f) {
@@ -805,8 +900,178 @@ gb_internal void futex_wait(Futex *f, Footex val) {
WaitOnAddress(f, (void *)&val, sizeof(val), INFINITE);
} while (f->load() == val);
}
+
+#elif defined(GB_SYSTEM_HAIKU)
+
+// Futex implementation taken from https://tavianator.com/2023/futex.html
+
+#include <pthread.h>
+#include <atomic>
+
+struct _Spinlock {
+ std::atomic_flag state;
+
+ void init() {
+ state.clear();
+ }
+
+ void lock() {
+ while (state.test_and_set(std::memory_order_acquire)) {
+ #if defined(GB_CPU_X86)
+ _mm_pause();
+ #else
+ (void)0; // spin...
+ #endif
+ }
+ }
+
+ void unlock() {
+ state.clear(std::memory_order_release);
+ }
+};
+
+struct Futex_Waitq;
+
+struct Futex_Waiter {
+ _Spinlock lock;
+ pthread_t thread;
+ Futex *futex;
+ Futex_Waitq *waitq;
+ Futex_Waiter *prev, *next;
+};
+
+struct Futex_Waitq {
+ _Spinlock lock;
+ Futex_Waiter list;
+
+ void init() {
+ auto head = &list;
+ head->prev = head->next = head;
+ }
+};
+
+// FIXME: This approach may scale badly in the future,
+// possible solution - hash map (leads to deadlocks now).
+
+Futex_Waitq g_waitq = {
+ .lock = ATOMIC_FLAG_INIT,
+ .list = {
+ .prev = &g_waitq.list,
+ .next = &g_waitq.list,
+ },
+};
+
+Futex_Waitq *get_waitq(Futex *f) {
+ // Future hash map method...
+ return &g_waitq;
+}
+
+void futex_signal(Futex *f) {
+ auto waitq = get_waitq(f);
+
+ waitq->lock.lock();
+
+ auto head = &waitq->list;
+ for (auto waiter = head->next; waiter != head; waiter = waiter->next) {
+ if (waiter->futex != f) {
+ continue;
+ }
+ waitq->lock.unlock();
+ pthread_kill(waiter->thread, SIGCONT);
+ return;
+ }
+
+ waitq->lock.unlock();
+}
+
+void futex_broadcast(Futex *f) {
+ auto waitq = get_waitq(f);
+
+ waitq->lock.lock();
+
+ auto head = &waitq->list;
+ for (auto waiter = head->next; waiter != head; waiter = waiter->next) {
+ if (waiter->futex != f) {
+ continue;
+ }
+ if (waiter->next == head) {
+ waitq->lock.unlock();
+ pthread_kill(waiter->thread, SIGCONT);
+ return;
+ } else {
+ pthread_kill(waiter->thread, SIGCONT);
+ }
+ }
+
+ waitq->lock.unlock();
+}
+
+void futex_wait(Futex *f, Footex val) {
+ Futex_Waiter waiter;
+ waiter.thread = pthread_self();
+ waiter.futex = f;
+
+ auto waitq = get_waitq(f);
+ while (waitq->lock.state.test_and_set(std::memory_order_acquire)) {
+ if (f->load(std::memory_order_relaxed) != val) {
+ return;
+ }
+ #if defined(GB_CPU_X86)
+ _mm_pause();
+ #else
+ (void)0; // spin...
+ #endif
+ }
+
+ waiter.waitq = waitq;
+ waiter.lock.init();
+ waiter.lock.lock();
+
+ auto head = &waitq->list;
+ waiter.prev = head->prev;
+ waiter.next = head;
+ waiter.prev->next = &waiter;
+ waiter.next->prev = &waiter;
+
+ waiter.prev->next = &waiter;
+ waiter.next->prev = &waiter;
+
+ sigset_t old_mask, mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCONT);
+ pthread_sigmask(SIG_BLOCK, &mask, &old_mask);
+
+ if (f->load(std::memory_order_relaxed) == val) {
+ waiter.lock.unlock();
+ waitq->lock.unlock();
+
+ int sig;
+ sigwait(&mask, &sig);
+
+ waitq->lock.lock();
+ waiter.lock.lock();
+
+ while (waitq != waiter.waitq) {
+ auto req = waiter.waitq;
+ waiter.lock.unlock();
+ waitq->lock.unlock();
+ waitq = req;
+ waitq->lock.lock();
+ waiter.lock.lock();
+ }
+ }
+
+ waiter.prev->next = waiter.next;
+ waiter.next->prev = waiter.prev;
+
+ pthread_sigmask(SIG_SETMASK, &old_mask, NULL);
+
+ waiter.lock.unlock();
+ waitq->lock.unlock();
+}
+
#endif
#if defined(GB_SYSTEM_WINDOWS)
#pragma warning(pop)
-#endif \ No newline at end of file
+#endif
diff --git a/src/tilde.cpp b/src/tilde.cpp
index 06428f317..4fc7d1c9b 100644
--- a/src/tilde.cpp
+++ b/src/tilde.cpp
@@ -825,6 +825,7 @@ gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) {
case TargetOs_essence:
case TargetOs_freebsd:
case TargetOs_openbsd:
+ case TargetOs_haiku:
debug_format = TB_DEBUGFMT_DWARF;
break;
}
diff --git a/src/timings.cpp b/src/timings.cpp
index baa8b80da..712e804cb 100644
--- a/src/timings.cpp
+++ b/src/timings.cpp
@@ -33,22 +33,23 @@ gb_internal u64 win32_time_stamp__freq(void) {
#include <mach/mach_time.h>
+gb_internal mach_timebase_info_data_t osx_init_timebase_info(void) {
+ mach_timebase_info_data_t data;
+ data.numer = 0;
+ data.denom = 0;
+ kern_return_t r = mach_timebase_info(&data);
+ GB_ASSERT(r == KERN_SUCCESS);
+
+ return data;
+}
+
gb_internal u64 osx_time_stamp_time_now(void) {
return mach_absolute_time();
}
gb_internal u64 osx_time_stamp__freq(void) {
- mach_timebase_info_data_t data;
- data.numer = 0;
- data.denom = 0;
- mach_timebase_info(&data);
-#if defined(GB_CPU_ARM)
- // NOTE(bill, 2021-02-25): M1 Chip seems to have a different freq count
- // TODO(bill): Is this truly correct?
- return (1000000llu * cast(u64)data.numer) / cast(u64)data.denom;
-#else
- return (1000000000llu * cast(u64)data.numer) / cast(u64)data.denom;
-#endif
+ gb_local_persist mach_timebase_info_data_t data = osx_init_timebase_info();
+ return 1000000000ull * cast(u64)data.denom / cast(u64)data.numer;
}
#elif defined(GB_SYSTEM_UNIX)
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index dd9908be5..3d5348074 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -106,6 +106,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \
TOKEN_KIND(Token_union, "union"), \
TOKEN_KIND(Token_enum, "enum"), \
TOKEN_KIND(Token_bit_set, "bit_set"), \
+ TOKEN_KIND(Token_bit_field, "bit_field"), \
TOKEN_KIND(Token_map, "map"), \
TOKEN_KIND(Token_dynamic, "dynamic"), \
TOKEN_KIND(Token_auto_cast, "auto_cast"), \
diff --git a/src/types.cpp b/src/types.cpp
index 574e628c5..97512d29b 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -137,13 +137,15 @@ struct TypeStruct {
Scope * scope;
i64 custom_align;
+ i64 custom_field_align;
Type * polymorphic_params; // Type_Tuple
Type * polymorphic_parent;
Type * soa_elem;
i32 soa_count;
StructSoaKind soa_kind;
- RwMutex fields_mutex;
+ Wait_Signal fields_wait_signal;
+ BlockingMutex soa_mutex;
BlockingMutex offset_mutex; // for settings offsets
bool is_polymorphic;
@@ -230,6 +232,7 @@ struct TypeProc {
Type *key; \
Type *value; \
Type *lookup_result_type; \
+ Type *debug_metadata_type; \
}) \
TYPE_KIND(Struct, TypeStruct) \
TYPE_KIND(Union, TypeUnion) \
@@ -279,6 +282,16 @@ struct TypeProc {
Type *generic_row_count; \
Type *generic_column_count; \
i64 stride_in_bytes; \
+ bool is_row_major; \
+ }) \
+ TYPE_KIND(BitField, struct { \
+ Scope * scope; \
+ Type * backing_type; \
+ Slice<Entity *> fields; \
+ String * tags; /*count == fields.count*/ \
+ Slice<u8> bit_sizes; \
+ Slice<i64> bit_offsets; \
+ Ast * node; \
}) \
TYPE_KIND(SoaPointer, struct { Type *elem; })
@@ -353,6 +366,10 @@ enum Typeid_Kind : u8 {
Typeid_Relative_Multi_Pointer,
Typeid_Matrix,
Typeid_SoaPointer,
+ Typeid_Bit_Field,
+
+ Typeid__COUNT
+
};
// IMPORTANT NOTE(bill): This must match the same as the in core.odin
@@ -374,6 +391,9 @@ enum : int {
gb_internal bool is_type_comparable(Type *t);
gb_internal bool is_type_simple_compare(Type *t);
+gb_internal Type *type_deref(Type *t, bool allow_multi_pointer=false);
+gb_internal Type *base_type(Type *t);
+gb_internal Type *alloc_type_multi_pointer(Type *elem);
gb_internal u32 type_info_flags_of_type(Type *type) {
if (type == nullptr) {
@@ -398,6 +418,7 @@ struct Selection {
bool indirect; // Set if there was a pointer deref anywhere down the line
u8 swizzle_count; // maximum components = 4
u8 swizzle_indices; // 2 bits per component, representing which swizzle index
+ bool is_bit_field;
bool pseudo_field;
};
gb_global Selection const empty_selection = {0};
@@ -546,6 +567,14 @@ gb_global Type *t_f16 = &basic_types[Basic_f16];
gb_global Type *t_f32 = &basic_types[Basic_f32];
gb_global Type *t_f64 = &basic_types[Basic_f64];
+gb_global Type *t_f16be = &basic_types[Basic_f16be];
+gb_global Type *t_f32be = &basic_types[Basic_f32be];
+gb_global Type *t_f64be = &basic_types[Basic_f64be];
+
+gb_global Type *t_f16le = &basic_types[Basic_f16le];
+gb_global Type *t_f32le = &basic_types[Basic_f32le];
+gb_global Type *t_f64le = &basic_types[Basic_f64le];
+
gb_global Type *t_complex32 = &basic_types[Basic_complex32];
gb_global Type *t_complex64 = &basic_types[Basic_complex64];
gb_global Type *t_complex128 = &basic_types[Basic_complex128];
@@ -639,6 +668,7 @@ gb_global Type *t_type_info_relative_pointer = nullptr;
gb_global Type *t_type_info_relative_multi_pointer = nullptr;
gb_global Type *t_type_info_matrix = nullptr;
gb_global Type *t_type_info_soa_pointer = nullptr;
+gb_global Type *t_type_info_bit_field = nullptr;
gb_global Type *t_type_info_named_ptr = nullptr;
gb_global Type *t_type_info_integer_ptr = nullptr;
@@ -668,6 +698,7 @@ gb_global Type *t_type_info_relative_pointer_ptr = nullptr;
gb_global Type *t_type_info_relative_multi_pointer_ptr = nullptr;
gb_global Type *t_type_info_matrix_ptr = nullptr;
gb_global Type *t_type_info_soa_pointer_ptr = nullptr;
+gb_global Type *t_type_info_bit_field_ptr = nullptr;
gb_global Type *t_allocator = nullptr;
gb_global Type *t_allocator_ptr = nullptr;
@@ -678,6 +709,10 @@ gb_global Type *t_allocator_error = nullptr;
gb_global Type *t_source_code_location = nullptr;
gb_global Type *t_source_code_location_ptr = nullptr;
+gb_global Type *t_load_directory_file = nullptr;
+gb_global Type *t_load_directory_file_ptr = nullptr;
+gb_global Type *t_load_directory_file_slice = nullptr;
+
gb_global Type *t_map_info = nullptr;
gb_global Type *t_map_cell_info = nullptr;
gb_global Type *t_raw_map = nullptr;
@@ -733,6 +768,7 @@ gb_internal i64 type_offset_of (Type *t, i64 index, Type **field_type_=null
gb_internal gbString type_to_string (Type *type, bool shorthand=true);
gb_internal gbString type_to_string (Type *type, gbAllocator allocator, bool shorthand=true);
gb_internal i64 type_size_of_internal(Type *t, TypePath *path);
+gb_internal i64 type_align_of_internal(Type *t, TypePath *path);
gb_internal void init_map_internal_types(Type *type);
gb_internal Type * bit_set_to_int(Type *t);
gb_internal bool are_types_identical(Type *x, Type *y);
@@ -744,10 +780,6 @@ gb_internal bool is_type_proc(Type *t);
gb_internal bool is_type_slice(Type *t);
gb_internal bool is_type_integer(Type *t);
gb_internal bool type_set_offsets(Type *t);
-gb_internal Type *base_type(Type *t);
-
-gb_internal i64 type_size_of_internal(Type *t, TypePath *path);
-gb_internal i64 type_align_of_internal(Type *t, TypePath *path);
// IMPORTANT TODO(bill): SHould this TypePath code be removed since type cycle checking is handled much earlier on?
@@ -825,11 +857,13 @@ gb_internal void type_path_pop(TypePath *tp) {
#define FAILURE_SIZE 0
#define FAILURE_ALIGNMENT 0
+gb_internal bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t);
+
gb_internal bool type_ptr_set_update(PtrSet<Type *> *s, Type *t) {
if (t == nullptr) {
return true;
}
- if (ptr_set_exists(s, t)) {
+ if (type_ptr_set_exists(s, t)) {
return true;
}
ptr_set_add(s, t);
@@ -898,6 +932,9 @@ gb_internal Type *core_type(Type *t) {
case Type_Enum:
t = t->Enum.base_type;
continue;
+ case Type_BitField:
+ t = t->BitField.backing_type;
+ continue;
}
break;
}
@@ -965,7 +1002,7 @@ gb_internal Type *alloc_type_array(Type *elem, i64 count, Type *generic_count =
return t;
}
-gb_internal Type *alloc_type_matrix(Type *elem, i64 row_count, i64 column_count, Type *generic_row_count = nullptr, Type *generic_column_count = nullptr) {
+gb_internal Type *alloc_type_matrix(Type *elem, i64 row_count, i64 column_count, Type *generic_row_count, Type *generic_column_count, bool is_row_major) {
if (generic_row_count != nullptr || generic_column_count != nullptr) {
Type *t = alloc_type(Type_Matrix);
t->Matrix.elem = elem;
@@ -973,12 +1010,14 @@ gb_internal Type *alloc_type_matrix(Type *elem, i64 row_count, i64 column_count,
t->Matrix.column_count = column_count;
t->Matrix.generic_row_count = generic_row_count;
t->Matrix.generic_column_count = generic_column_count;
+ t->Matrix.is_row_major = is_row_major;
return t;
}
Type *t = alloc_type(Type_Matrix);
t->Matrix.elem = elem;
t->Matrix.row_count = row_count;
t->Matrix.column_count = column_count;
+ t->Matrix.is_row_major = is_row_major;
return t;
}
@@ -1020,6 +1059,13 @@ gb_internal Type *alloc_type_struct() {
return t;
}
+gb_internal Type *alloc_type_struct_complete() {
+ Type *t = alloc_type(Type_Struct);
+ wait_signal_set(&t->Struct.fields_wait_signal);
+ return t;
+}
+
+
gb_internal Type *alloc_type_union() {
Type *t = alloc_type(Type_Union);
return t;
@@ -1032,6 +1078,11 @@ gb_internal Type *alloc_type_enum() {
return t;
}
+gb_internal Type *alloc_type_bit_field() {
+ Type *t = alloc_type(Type_BitField);
+ return t;
+}
+
gb_internal Type *alloc_type_relative_pointer(Type *pointer_type, Type *base_integer) {
GB_ASSERT(is_type_pointer(pointer_type));
GB_ASSERT(is_type_integer(base_integer));
@@ -1132,7 +1183,7 @@ gb_internal Type *alloc_type_simd_vector(i64 count, Type *elem, Type *generic_co
////////////////////////////////////////////////////////////////
-gb_internal Type *type_deref(Type *t, bool allow_multi_pointer=false) {
+gb_internal Type *type_deref(Type *t, bool allow_multi_pointer) {
if (t != nullptr) {
Type *bt = base_type(t);
if (bt == nullptr) {
@@ -1470,14 +1521,18 @@ gb_internal i64 matrix_indices_to_offset(Type *t, i64 row_index, i64 column_inde
GB_ASSERT(0 <= row_index && row_index < t->Matrix.row_count);
GB_ASSERT(0 <= column_index && column_index < t->Matrix.column_count);
i64 stride_elems = matrix_type_stride_in_elems(t);
- // NOTE(bill): Column-major layout internally
- return row_index + stride_elems*column_index;
+ if (t->Matrix.is_row_major) {
+ return column_index + stride_elems*row_index;
+ } else {
+ // NOTE(bill): Column-major layout internally
+ return row_index + stride_elems*column_index;
+ }
}
gb_internal i64 matrix_row_major_index_to_offset(Type *t, i64 index) {
t = base_type(t);
GB_ASSERT(t->kind == Type_Matrix);
-
+
i64 row_index = index/t->Matrix.column_count;
i64 column_index = index%t->Matrix.column_count;
return matrix_indices_to_offset(t, row_index, column_index);
@@ -1699,6 +1754,10 @@ gb_internal bool is_type_bit_set(Type *t) {
t = base_type(t);
return (t->kind == Type_BitSet);
}
+gb_internal bool is_type_bit_field(Type *t) {
+ t = base_type(t);
+ return (t->kind == Type_BitField);
+}
gb_internal bool is_type_map(Type *t) {
t = base_type(t);
return t->kind == Type_Map;
@@ -2357,6 +2416,9 @@ gb_internal bool is_type_comparable(Type *t) {
case Type_SimdVector:
return true;
+
+ case Type_BitField:
+ return is_type_comparable(t->BitField.backing_type);
}
return false;
}
@@ -2641,6 +2703,7 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
case Type_Matrix:
return x->Matrix.row_count == y->Matrix.row_count &&
x->Matrix.column_count == y->Matrix.column_count &&
+ x->Matrix.is_row_major == y->Matrix.is_row_major &&
are_types_identical(x->Matrix.elem, y->Matrix.elem);
case Type_DynamicArray:
@@ -2764,6 +2827,29 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
return are_types_identical(x->SimdVector.elem, y->SimdVector.elem);
}
break;
+
+ case Type_BitField:
+ if (are_types_identical(x->BitField.backing_type, y->BitField.backing_type) &&
+ x->BitField.fields.count == y->BitField.fields.count) {
+ for_array(i, x->BitField.fields) {
+ Entity *a = x->BitField.fields[i];
+ Entity *b = y->BitField.fields[i];
+ if (!are_types_identical(a->type, b->type)) {
+ return false;
+ }
+ if (a->token.string != b->token.string) {
+ return false;
+ }
+ if (x->BitField.bit_sizes[i] != y->BitField.bit_sizes[i]) {
+ return false;
+ }
+ if (x->BitField.bit_offsets[i] != y->BitField.bit_offsets[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ break;
}
return false;
@@ -2787,6 +2873,49 @@ gb_internal Type *default_type(Type *type) {
return type;
}
+// See https://en.cppreference.com/w/c/language/conversion#Default_argument_promotions
+gb_internal Type *c_vararg_promote_type(Type *type) {
+ GB_ASSERT(type != nullptr);
+
+ Type *core = core_type(type);
+
+ if (core->kind == Type_BitSet) {
+ core = core_type(bit_set_to_int(core));
+ }
+
+ if (core->kind == Type_Basic) {
+ switch (core->Basic.kind) {
+ case Basic_f32:
+ case Basic_UntypedFloat:
+ return t_f64;
+ case Basic_f32le:
+ return t_f64le;
+ case Basic_f32be:
+ return t_f64be;
+
+ case Basic_UntypedBool:
+ case Basic_bool:
+ case Basic_b8:
+ case Basic_b16:
+ case Basic_i8:
+ case Basic_i16:
+ case Basic_u8:
+ case Basic_u16:
+ return t_i32;
+
+ case Basic_i16le:
+ case Basic_u16le:
+ return t_i32le;
+
+ case Basic_i16be:
+ case Basic_u16be:
+ return t_i32be;
+ }
+ }
+
+ return type;
+}
+
gb_internal bool union_variant_index_types_equal(Type *v, Type *vt) {
if (are_types_identical(v, vt)) {
return true;
@@ -2961,9 +3090,8 @@ gb_internal Selection lookup_field_from_index(Type *type, i64 index) {
isize max_count = 0;
switch (type->kind) {
case Type_Struct:
- rw_mutex_shared_lock(&type->Struct.fields_mutex);
+ wait_signal_until_available(&type->Struct.fields_wait_signal);
max_count = type->Struct.fields.count;
- rw_mutex_shared_unlock(&type->Struct.fields_mutex);
break;
case Type_Tuple: max_count = type->Tuple.variables.count; break;
}
@@ -2974,8 +3102,7 @@ gb_internal Selection lookup_field_from_index(Type *type, i64 index) {
switch (type->kind) {
case Type_Struct: {
- rw_mutex_shared_lock(&type->Struct.fields_mutex);
- defer (rw_mutex_shared_unlock(&type->Struct.fields_mutex));
+ wait_signal_until_available(&type->Struct.fields_wait_signal);
for (isize i = 0; i < max_count; i++) {
Entity *f = type->Struct.fields[i];
if (f->kind == Entity_Variable) {
@@ -3031,7 +3158,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
mutex_lock(md->mutex);
defer (mutex_unlock(md->mutex));
for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
- GB_ASSERT(entry.entity->kind == Entity_Procedure);
+ GB_ASSERT(entry.entity->kind == Entity_Procedure || entry.entity->kind == Entity_ProcGroup);
if (entry.name == field_name) {
sel.entity = entry.entity;
sel.pseudo_field = true;
@@ -3040,9 +3167,8 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
}
}
if (type->kind == Type_Struct) {
- rw_mutex_shared_lock(&type->Struct.fields_mutex);
+ wait_signal_until_available(&type->Struct.fields_wait_signal);
isize field_count = type->Struct.fields.count;
- rw_mutex_shared_unlock(&type->Struct.fields_mutex);
if (field_count != 0) for_array(i, type->Struct.fields) {
Entity *f = type->Struct.fields[i];
if (f->flags&EntityFlag_Using) {
@@ -3071,9 +3197,8 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
}
if (type->kind == Type_Struct) {
- rw_mutex_shared_lock(&type->Struct.fields_mutex);
+ wait_signal_until_available(&type->Struct.fields_wait_signal);
Scope *s = type->Struct.scope;
- rw_mutex_shared_unlock(&type->Struct.fields_mutex);
if (s != nullptr) {
Entity *found = scope_lookup_current(s, field_name);
if (found != nullptr && found->kind != Entity_Variable) {
@@ -3121,9 +3246,8 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
}
}
- rw_mutex_shared_lock(&type->Struct.fields_mutex);
+ wait_signal_until_available(&type->Struct.fields_wait_signal);
isize field_count = type->Struct.fields.count;
- rw_mutex_shared_unlock(&type->Struct.fields_mutex);
if (field_count != 0) for_array(i, type->Struct.fields) {
Entity *f = type->Struct.fields[i];
if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) {
@@ -3165,6 +3289,21 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
else if (field_name == "a") mapped_field_name = str_lit("w");
return lookup_field_with_selection(type, mapped_field_name, is_type, sel, allow_blank_ident);
}
+ } else if (type->kind == Type_BitField) {
+ for_array(i, type->BitField.fields) {
+ Entity *f = type->BitField.fields[i];
+ if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) {
+ continue;
+ }
+ String str = f->token.string;
+ if (field_name == str) {
+ selection_add_index(&sel, i); // HACK(bill): Leaky memory
+ sel.entity = f;
+ sel.is_bit_field = true;
+ return sel;
+ }
+ }
+
} else if (type->kind == Type_Basic) {
switch (type->Basic.kind) {
case Basic_any: {
@@ -3305,31 +3444,6 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
}
return sel;
- } else if (type->kind == Type_Array) {
- if (type->Array.count <= 4) {
- // HACK(bill): Memory leak
- switch (type->Array.count) {
- #define _ARRAY_FIELD_CASE_IF(_length, _name) \
- if (field_name == (_name)) { \
- selection_add_index(&sel, (_length)-1); \
- sel.entity = alloc_entity_array_elem(nullptr, make_token_ident(str_lit(_name)), type->Array.elem, (_length)-1); \
- return sel; \
- }
- #define _ARRAY_FIELD_CASE(_length, _name0, _name1) \
- case (_length): \
- _ARRAY_FIELD_CASE_IF(_length, _name0); \
- _ARRAY_FIELD_CASE_IF(_length, _name1); \
- /*fallthrough*/
-
- _ARRAY_FIELD_CASE(4, "w", "a");
- _ARRAY_FIELD_CASE(3, "z", "b");
- _ARRAY_FIELD_CASE(2, "y", "g");
- _ARRAY_FIELD_CASE(1, "x", "r");
- default: break;
-
- #undef _ARRAY_FIELD_CASE
- }
- }
} else if (type->kind == Type_DynamicArray) {
GB_ASSERT(t_allocator != nullptr);
String allocator_str = str_lit("allocator");
@@ -3350,8 +3464,54 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
sel.entity = entity__allocator;
return sel;
}
+
+
+#define _ARRAY_FIELD_CASE_IF(_length, _name) \
+ if (field_name == (_name)) { \
+ selection_add_index(&sel, (_length)-1); \
+ sel.entity = alloc_entity_array_elem(nullptr, make_token_ident(str_lit(_name)), elem, (_length)-1); \
+ return sel; \
+ }
+#define _ARRAY_FIELD_CASE(_length, _name0, _name1) \
+case (_length): \
+ _ARRAY_FIELD_CASE_IF(_length, _name0); \
+ _ARRAY_FIELD_CASE_IF(_length, _name1); \
+ /*fallthrough*/
+
+
+ } else if (type->kind == Type_Array) {
+
+ Type *elem = type->Array.elem;
+
+ if (type->Array.count <= 4) {
+ // HACK(bill): Memory leak
+ switch (type->Array.count) {
+
+ _ARRAY_FIELD_CASE(4, "w", "a");
+ _ARRAY_FIELD_CASE(3, "z", "b");
+ _ARRAY_FIELD_CASE(2, "y", "g");
+ _ARRAY_FIELD_CASE(1, "x", "r");
+ default: break;
+ }
+ }
+ } else if (type->kind == Type_SimdVector) {
+
+ Type *elem = type->SimdVector.elem;
+ if (type->SimdVector.count <= 4) {
+ // HACK(bill): Memory leak
+ switch (type->SimdVector.count) {
+ _ARRAY_FIELD_CASE(4, "w", "a");
+ _ARRAY_FIELD_CASE(3, "z", "b");
+ _ARRAY_FIELD_CASE(2, "y", "g");
+ _ARRAY_FIELD_CASE(1, "x", "r");
+ default: break;
+ }
+ }
}
+#undef _ARRAY_FIELD_CASE
+#undef _ARRAY_FIELD_CASE
+
return sel;
}
@@ -3414,8 +3574,6 @@ gb_internal Slice<i32> struct_fields_index_by_increasing_offset(gbAllocator allo
-gb_internal i64 type_size_of_internal (Type *t, TypePath *path);
-gb_internal i64 type_align_of_internal(Type *t, TypePath *path);
gb_internal i64 type_size_of(Type *t);
gb_internal i64 type_align_of(Type *t);
@@ -3565,6 +3723,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
case Type_Slice:
return build_context.int_size;
+ case Type_BitField:
+ return type_align_of_internal(t->BitField.backing_type, path);
case Type_Tuple: {
i64 max = 1;
@@ -3615,6 +3775,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
return 1;
}
+ type_set_offsets(t);
+
i64 max = 1;
for_array(i, t->Struct.fields) {
Type *field_type = t->Struct.fields[i]->type;
@@ -3666,10 +3828,15 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align);
}
-gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union) {
+gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union, i64 min_field_align) {
gbAllocator a = permanent_allocator();
auto offsets = gb_alloc_array(a, i64, fields.count);
i64 curr_offset = 0;
+
+ if (min_field_align == 0) {
+ min_field_align = 1;
+ }
+
if (is_raw_union) {
for_array(i, fields) {
offsets[i] = 0;
@@ -3690,7 +3857,7 @@ gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_pack
offsets[i] = -1;
} else {
Type *t = fields[i]->type;
- i64 align = gb_max(type_align_of(t), 1);
+ i64 align = gb_max(type_align_of(t), min_field_align);
i64 size = gb_max(type_size_of( t), 0);
curr_offset = align_formula(curr_offset, align);
offsets[i] = curr_offset;
@@ -3707,7 +3874,7 @@ gb_internal bool type_set_offsets(Type *t) {
MUTEX_GUARD(&t->Struct.offset_mutex);
if (!t->Struct.are_offsets_set) {
t->Struct.are_offsets_being_processed = true;
- t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union);
+ t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union, t->Struct.custom_field_align);
t->Struct.are_offsets_being_processed = false;
t->Struct.are_offsets_set = true;
return true;
@@ -3716,7 +3883,7 @@ gb_internal bool type_set_offsets(Type *t) {
MUTEX_GUARD(&t->Tuple.mutex);
if (!t->Tuple.are_offsets_set) {
t->Tuple.are_offsets_being_processed = true;
- t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false);
+ t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false, 1);
t->Tuple.are_offsets_being_processed = false;
t->Tuple.are_offsets_set = true;
return true;
@@ -3935,6 +4102,9 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
return stride_in_bytes * t->Matrix.column_count;
}
+ case Type_BitField:
+ return type_size_of_internal(t->BitField.backing_type, path);
+
case Type_RelativePointer:
return type_size_of_internal(t->RelativePointer.base_integer, path);
case Type_RelativeMultiPointer:
@@ -4085,7 +4255,7 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) {
return offset;
}
-gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) {
+gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false, bool allow_polymorphic=false) {
Type *prev_src = src;
src = type_deref(src);
if (!src_is_ptr) {
@@ -4097,11 +4267,21 @@ gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isi
return 0;
}
+ bool dst_is_polymorphic = is_type_polymorphic(dst);
+
for_array(i, src->Struct.fields) {
Entity *f = src->Struct.fields[i];
if (f->kind != Entity_Variable || (f->flags&EntityFlags_IsSubtype) == 0) {
continue;
}
+ if (allow_polymorphic && dst_is_polymorphic) {
+ Type *fb = base_type(type_deref(f->type));
+ if (fb->kind == Type_Struct) {
+ if (fb->Struct.polymorphic_parent == dst) {
+ return true;
+ }
+ }
+ }
if (are_types_identical(f->type, dst)) {
return level+1;
@@ -4111,7 +4291,7 @@ gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isi
return level+1;
}
}
- isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr);
+ isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr, allow_polymorphic);
if (nested_level > 0) {
return nested_level;
}
@@ -4127,6 +4307,13 @@ gb_internal bool is_type_subtype_of(Type *src, Type *dst) {
return 0 < check_is_assignable_to_using_subtype(src, dst, 0, is_type_pointer(src));
}
+gb_internal bool is_type_subtype_of_and_allow_polymorphic(Type *src, Type *dst) {
+ if (are_types_identical(src, dst)) {
+ return true;
+ }
+
+ return 0 < check_is_assignable_to_using_subtype(src, dst, 0, is_type_pointer(src), true);
+}
gb_internal bool has_type_got_objc_class_attribute(Type *t) {
@@ -4194,7 +4381,70 @@ gb_internal Type *alloc_type_proc_from_types(Type **param_types, unsigned param_
return t;
}
-
+// gb_internal Type *type_from_selection(Type *type, Selection const &sel) {
+// for (i32 index : sel.index) {
+// Type *bt = base_type(type_deref(type));
+// switch (bt->kind) {
+// case Type_Struct:
+// type = bt->Struct.fields[index]->type;
+// break;
+// case Type_Tuple:
+// type = bt->Tuple.variables[index]->type;
+// break;
+// case Type_BitField:
+// type = bt->BitField.fields[index]->type;
+// break;
+// case Type_Array:
+// type = bt->Array.elem;
+// break;
+// case Type_EnumeratedArray:
+// type = bt->Array.elem;
+// break;
+// case Type_Slice:
+// switch (index) {
+// case 0: type = alloc_type_multi_pointer(bt->Slice.elem); break;
+// case 1: type = t_int; break;
+// }
+// break;
+// case Type_DynamicArray:
+// switch (index) {
+// case 0: type = alloc_type_multi_pointer(bt->DynamicArray.elem); break;
+// case 1: type = t_int; break;
+// case 2: type = t_int; break;
+// case 3: type = t_allocator; break;
+// }
+// break;
+// case Type_Map:
+// switch (index) {
+// case 0: type = t_uintptr; break;
+// case 1: type = t_int; break;
+// case 2: type = t_allocator; break;
+// }
+// break;
+// case Type_Basic:
+// if (is_type_complex_or_quaternion(bt)) {
+// type = base_complex_elem_type(bt);
+// } else {
+// switch (type->Basic.kind) {
+// case Basic_any:
+// switch (index) {
+// case 0: type = t_rawptr; break;
+// case 1: type = t_typeid; break;
+// }
+// break;
+// case Basic_string:
+// switch (index) {
+// case 0: type = t_u8_multi_ptr; break;
+// case 1: type = t_int; break;
+// }
+// break;
+// }
+// }
+// break;
+// }
+// }
+// return type;
+// }
gb_internal gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
if (type == nullptr) {
@@ -4499,9 +4749,29 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
break;
case Type_Matrix:
+ if (type->Matrix.is_row_major) {
+ str = gb_string_appendc(str, "#row_major ");
+ }
str = gb_string_appendc(str, gb_bprintf("matrix[%d, %d]", cast(int)type->Matrix.row_count, cast(int)type->Matrix.column_count));
str = write_type_to_string(str, type->Matrix.elem);
break;
+
+ case Type_BitField:
+ str = gb_string_appendc(str, "bit_field ");
+ str = write_type_to_string(str, type->BitField.backing_type);
+ str = gb_string_appendc(str, " {");
+ for (isize i = 0; i < type->BitField.fields.count; i++) {
+ Entity *f = type->BitField.fields[i];
+ if (i > 0) {
+ str = gb_string_appendc(str, ", ");
+ }
+ str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
+ str = gb_string_appendc(str, ": ");
+ str = write_type_to_string(str, f->type);
+ str = gb_string_append_fmt(str, " | %u", type->BitField.bit_sizes[i]);
+ }
+ str = gb_string_appendc(str, " }");
+ break;
}
return str;