aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeroen van Rijn <Kelimion@users.noreply.github.com>2022-04-27 14:37:15 +0200
committerJeroen van Rijn <Kelimion@users.noreply.github.com>2022-04-27 14:37:15 +0200
commitc4e0d1efa1ec655bae9134b95a0fcd060cc7bbea (patch)
treec29bd0b78138e8d67aebe34ac689d13e32d9d15f /src
parent6e61abc7d06f22129f93110a9f652c3eec21f0c6 (diff)
parent9349dfba8fec53f52f77a0c8928e115ec93ff447 (diff)
Merge branch 'master' into xml
Diffstat (limited to 'src')
-rw-r--r--src/array.cpp6
-rw-r--r--src/bug_report.cpp53
-rw-r--r--src/build_settings.cpp451
-rw-r--r--src/check_builtin.cpp872
-rw-r--r--src/check_decl.cpp216
-rw-r--r--src/check_expr.cpp3761
-rw-r--r--src/check_stmt.cpp122
-rw-r--r--src/check_type.cpp165
-rw-r--r--src/checker.cpp599
-rw-r--r--src/checker.hpp22
-rw-r--r--src/checker_builtin_procs.hpp217
-rw-r--r--src/common.cpp259
-rw-r--r--src/common_memory.cpp53
-rw-r--r--src/docs.cpp28
-rw-r--r--src/docs_format.cpp25
-rw-r--r--src/docs_writer.cpp146
-rw-r--r--src/entity.cpp53
-rw-r--r--src/error.cpp416
-rw-r--r--src/exact_value.cpp24
-rw-r--r--src/gb/gb.h137
-rw-r--r--src/llvm_abi.cpp39
-rw-r--r--src/llvm_backend.cpp185
-rw-r--r--src/llvm_backend.hpp15
-rw-r--r--src/llvm_backend_const.cpp8
-rw-r--r--src/llvm_backend_debug.cpp77
-rw-r--r--src/llvm_backend_expr.cpp186
-rw-r--r--src/llvm_backend_general.cpp68
-rw-r--r--src/llvm_backend_opt.cpp40
-rw-r--r--src/llvm_backend_proc.cpp484
-rw-r--r--src/llvm_backend_stmt.cpp69
-rw-r--r--src/llvm_backend_type.cpp32
-rw-r--r--src/llvm_backend_utility.cpp257
-rw-r--r--src/main.cpp707
-rw-r--r--src/microsoft_craziness.h10
-rw-r--r--src/parser.cpp365
-rw-r--r--src/parser.hpp57
-rw-r--r--src/parser_pos.cpp6
-rw-r--r--src/path.cpp394
-rw-r--r--src/ptr_set.cpp9
-rw-r--r--src/queue.cpp45
-rw-r--r--src/string.cpp88
-rw-r--r--src/threading.cpp40
-rw-r--r--src/tokenizer.cpp423
-rw-r--r--src/types.cpp289
44 files changed, 7565 insertions, 3953 deletions
diff --git a/src/array.cpp b/src/array.cpp
index c41125c6d..d08bd647f 100644
--- a/src/array.cpp
+++ b/src/array.cpp
@@ -77,15 +77,21 @@ template <typename T> Slice<T> slice_from_array(Array<T> const &a);
template <typename T>
Slice<T> slice_make(gbAllocator const &allocator, isize count) {
+ GB_ASSERT(count >= 0);
Slice<T> s = {};
s.data = gb_alloc_array(allocator, T, count);
+ GB_ASSERT(s.data != nullptr);
s.count = count;
return s;
}
template <typename T>
void slice_init(Slice<T> *s, gbAllocator const &allocator, isize count) {
+ GB_ASSERT(count >= 0);
s->data = gb_alloc_array(allocator, T, count);
+ if (count > 0) {
+ GB_ASSERT(s->data != nullptr);
+ }
s->count = count;
}
diff --git a/src/bug_report.cpp b/src/bug_report.cpp
index 27e7fcf9a..02a2b1ba2 100644
--- a/src/bug_report.cpp
+++ b/src/bug_report.cpp
@@ -17,6 +17,11 @@
#include <sys/sysctl.h>
#endif
+#if defined(GB_SYSTEM_OPENBSD)
+ #include <sys/sysctl.h>
+ #include <sys/utsname.h>
+#endif
+
/*
NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
*/
@@ -140,7 +145,7 @@ void report_windows_product_type(DWORD ProductType) {
break;
default:
- gb_printf("Unknown Edition (%08x)", ProductType);
+ gb_printf("Unknown Edition (%08x)", cast(unsigned)ProductType);
}
}
#endif
@@ -242,6 +247,14 @@ void report_ram_info() {
if (sysctl(sysctls, 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 sysctls[] = { CTL_HW, HW_PHYSMEM64 };
+ if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+ gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
+ }
#else
gb_printf("Unknown.\n");
#endif
@@ -316,14 +329,14 @@ void print_bug_report_help() {
}
if (false) {
- gb_printf("dwMajorVersion: %d\n", osvi.dwMajorVersion);
- gb_printf("dwMinorVersion: %d\n", osvi.dwMinorVersion);
- gb_printf("dwBuildNumber: %d\n", osvi.dwBuildNumber);
- gb_printf("dwPlatformId: %d\n", osvi.dwPlatformId);
- gb_printf("wServicePackMajor: %d\n", osvi.wServicePackMajor);
- gb_printf("wServicePackMinor: %d\n", osvi.wServicePackMinor);
- gb_printf("wSuiteMask: %d\n", osvi.wSuiteMask);
- gb_printf("wProductType: %d\n", osvi.wProductType);
+ 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 ");
@@ -441,18 +454,18 @@ void print_bug_report_help() {
TEXT("DisplayVersion"),
RRF_RT_REG_SZ,
ValueType,
- &DisplayVersion,
+ DisplayVersion,
&ValueSize
);
if (status == 0x0) {
- gb_printf(" (version: %s)", &DisplayVersion);
+ gb_printf(" (version: %s)", DisplayVersion);
}
/*
Now print build number.
*/
- gb_printf(", build %d", osvi.dwBuildNumber);
+ gb_printf(", build %u", cast(unsigned)osvi.dwBuildNumber);
ValueSize = sizeof(UBR);
status = RegGetValue(
@@ -466,18 +479,18 @@ void print_bug_report_help() {
);
if (status == 0x0) {
- gb_printf(".%d", UBR);
+ gb_printf(".%u", cast(unsigned)UBR);
}
gb_printf("\n");
}
#elif defined(GB_SYSTEM_LINUX)
/*
- Try to parse `/usr/lib/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
+ 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, "/usr/lib/os-release");
+ gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release");
defer (gb_file_free_contents(&release));
b32 found = 0;
@@ -643,6 +656,14 @@ void print_bug_report_help() {
} else {
gb_printf("macOS: Unknown\n");
}
+ #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");
+ }
#else
gb_printf("Unknown\n");
@@ -657,4 +678,4 @@ void print_bug_report_help() {
And RAM info.
*/
report_ram_info();
-} \ No newline at end of file
+}
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 29abd441c..89d370144 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -1,14 +1,13 @@
-#if defined(GB_SYSTEM_FREEBSD)
+#if defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
-
// #if defined(GB_SYSTEM_WINDOWS)
-#define DEFAULT_TO_THREADED_CHECKER
+// #define DEFAULT_TO_THREADED_CHECKER
// #endif
-enum TargetOsKind {
+enum TargetOsKind : u16 {
TargetOs_Invalid,
TargetOs_windows,
@@ -16,6 +15,7 @@ enum TargetOsKind {
TargetOs_linux,
TargetOs_essence,
TargetOs_freebsd,
+ TargetOs_openbsd,
TargetOs_wasi,
TargetOs_js,
@@ -25,11 +25,11 @@ enum TargetOsKind {
TargetOs_COUNT,
};
-enum TargetArchKind {
+enum TargetArchKind : u16 {
TargetArch_Invalid,
TargetArch_amd64,
- TargetArch_386,
+ TargetArch_i386,
TargetArch_arm64,
TargetArch_wasm32,
TargetArch_wasm64,
@@ -37,7 +37,7 @@ enum TargetArchKind {
TargetArch_COUNT,
};
-enum TargetEndianKind {
+enum TargetEndianKind : u8 {
TargetEndian_Invalid,
TargetEndian_Little,
@@ -46,6 +46,16 @@ enum TargetEndianKind {
TargetEndian_COUNT,
};
+enum TargetABIKind : u16 {
+ TargetABI_Default,
+
+ TargetABI_Win64,
+ TargetABI_SysV,
+
+ TargetABI_COUNT,
+};
+
+
String target_os_names[TargetOs_COUNT] = {
str_lit(""),
str_lit("windows"),
@@ -53,6 +63,7 @@ String target_os_names[TargetOs_COUNT] = {
str_lit("linux"),
str_lit("essence"),
str_lit("freebsd"),
+ str_lit("openbsd"),
str_lit("wasi"),
str_lit("js"),
@@ -63,7 +74,7 @@ String target_os_names[TargetOs_COUNT] = {
String target_arch_names[TargetArch_COUNT] = {
str_lit(""),
str_lit("amd64"),
- str_lit("386"),
+ str_lit("i386"),
str_lit("arm64"),
str_lit("wasm32"),
str_lit("wasm64"),
@@ -75,6 +86,12 @@ String target_endian_names[TargetEndian_COUNT] = {
str_lit("big"),
};
+String target_abi_names[TargetABI_COUNT] = {
+ str_lit(""),
+ str_lit("win64"),
+ str_lit("sysv"),
+};
+
TargetEndianKind target_endians[TargetArch_COUNT] = {
TargetEndian_Invalid,
TargetEndian_Little,
@@ -98,6 +115,7 @@ struct TargetMetrics {
isize max_align;
String target_triplet;
String target_data_layout;
+ TargetABIKind abi;
};
@@ -119,6 +137,8 @@ enum BuildModeKind {
BuildMode_Object,
BuildMode_Assembly,
BuildMode_LLVM_IR,
+
+ BuildMode_COUNT,
};
enum CommandKind : u32 {
@@ -163,19 +183,50 @@ enum TimingsExportFormat : i32 {
TimingsExportCSV = 2,
};
+enum ErrorPosStyle {
+ ErrorPosStyle_Default, // path(line:column) msg
+ ErrorPosStyle_Unix, // path:line:column: msg
+
+ ErrorPosStyle_COUNT
+};
+
+enum RelocMode : u8 {
+ RelocMode_Default,
+ RelocMode_Static,
+ RelocMode_PIC,
+ RelocMode_DynamicNoPIC,
+};
+
+enum BuildPath : u8 {
+ BuildPath_Main_Package, // Input Path to the package directory (or file) we're building.
+ BuildPath_RC, // Input Path for .rc file, can be set with `-resource:`.
+ BuildPath_RES, // Output Path for .res file, generated from previous.
+ BuildPath_Win_SDK_Root, // windows_sdk_root
+ BuildPath_Win_SDK_UM_Lib, // windows_sdk_um_library_path
+ BuildPath_Win_SDK_UCRT_Lib, // windows_sdk_ucrt_library_path
+ BuildPath_VS_EXE, // vs_exe_path
+ BuildPath_VS_LIB, // vs_library_path
+
+ BuildPath_Output, // Output Path for .exe, .dll, .so, etc. Can be overridden with `-out:`.
+ BuildPath_PDB, // Output Path for .pdb file, can be overridden with `-pdb-name:`.
+
+ BuildPathCOUNT,
+};
+
// This stores the information for the specify architecture of this build
struct BuildContext {
// Constants
String ODIN_OS; // target operating system
String ODIN_ARCH; // target architecture
- String ODIN_ENDIAN; // target endian
String ODIN_VENDOR; // compiler vendor
String ODIN_VERSION; // compiler version
String ODIN_ROOT; // Odin ROOT
- String ODIN_BUILD_MODE;
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_FOREIGN_ERROR_PROCEDURES;
+
+ ErrorPosStyle ODIN_ERROR_POS_STYLE;
TargetEndianKind endian_kind;
@@ -190,14 +241,19 @@ struct BuildContext {
bool show_help;
+ Array<Path> build_paths; // Contains `Path` objects to output filename, pdb, resource and intermediate files.
+ // BuildPath enum contains the indices of paths we know *before* the work starts.
+
String out_filepath;
String resource_filepath;
String pdb_filepath;
+
bool has_resource;
String link_flags;
String extra_linker_flags;
String extra_assembler_flags;
String microarch;
+ String target_features;
BuildModeKind build_mode;
bool generate_docs;
i32 optimization_level;
@@ -243,6 +299,12 @@ struct BuildContext {
bool copy_file_contents;
+ bool disallow_rtti;
+
+ RelocMode reloc_mode;
+ bool disable_red_zone;
+
+
u32 cmd_doc_flags;
Array<String> extra_packages;
@@ -254,9 +316,8 @@ struct BuildContext {
isize thread_count;
PtrMap<char const *, ExactValue> defined_values;
-};
-
+};
gb_global BuildContext build_context = {0};
@@ -268,9 +329,9 @@ bool global_ignore_warnings(void) {
}
-gb_global TargetMetrics target_windows_386 = {
+gb_global TargetMetrics target_windows_i386 = {
TargetOs_windows,
- TargetArch_386,
+ TargetArch_i386,
4,
8,
str_lit("i386-pc-windows-msvc"),
@@ -284,9 +345,9 @@ gb_global TargetMetrics target_windows_amd64 = {
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
-gb_global TargetMetrics target_linux_386 = {
+gb_global TargetMetrics target_linux_i386 = {
TargetOs_linux,
- TargetArch_386,
+ TargetArch_i386,
4,
8,
str_lit("i386-pc-linux-gnu"),
@@ -300,6 +361,14 @@ gb_global TargetMetrics target_linux_amd64 = {
str_lit("x86_64-pc-linux-gnu"),
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
+gb_global TargetMetrics target_linux_arm64 = {
+ TargetOs_linux,
+ TargetArch_arm64,
+ 8,
+ 16,
+ str_lit("aarch64-linux-elf"),
+ str_lit("e-m:e-i8:8:32-i16:32-i64:64-i128:128-n32:64-S128"),
+};
gb_global TargetMetrics target_darwin_amd64 = {
TargetOs_darwin,
@@ -319,9 +388,9 @@ gb_global TargetMetrics target_darwin_arm64 = {
str_lit("e-m:o-i64:64-i128:128-n32:64-S128"), // TODO(bill): Is this correct?
};
-gb_global TargetMetrics target_freebsd_386 = {
+gb_global TargetMetrics target_freebsd_i386 = {
TargetOs_freebsd,
- TargetArch_386,
+ TargetArch_i386,
4,
8,
str_lit("i386-unknown-freebsd-elf"),
@@ -336,6 +405,15 @@ gb_global TargetMetrics target_freebsd_amd64 = {
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
+gb_global TargetMetrics target_openbsd_amd64 = {
+ TargetOs_openbsd,
+ TargetArch_amd64,
+ 8,
+ 16,
+ str_lit("x86_64-unknown-openbsd-elf"),
+ 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_essence_amd64 = {
TargetOs_essence,
TargetArch_amd64,
@@ -381,6 +459,16 @@ gb_global TargetMetrics target_wasi_wasm32 = {
// str_lit(""),
// };
+gb_global TargetMetrics target_freestanding_amd64_sysv = {
+ TargetOs_freestanding,
+ TargetArch_amd64,
+ 8,
+ 16,
+ str_lit("x86_64-pc-none-gnu"),
+ str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
+ TargetABI_SysV,
+};
+
struct NamedTargetMetrics {
@@ -392,16 +480,19 @@ gb_global NamedTargetMetrics named_targets[] = {
{ str_lit("darwin_amd64"), &target_darwin_amd64 },
{ str_lit("darwin_arm64"), &target_darwin_arm64 },
{ str_lit("essence_amd64"), &target_essence_amd64 },
- { str_lit("linux_386"), &target_linux_386 },
+ { str_lit("linux_i386"), &target_linux_i386 },
{ str_lit("linux_amd64"), &target_linux_amd64 },
- { str_lit("windows_386"), &target_windows_386 },
+ { str_lit("linux_arm64"), &target_linux_arm64 },
+ { str_lit("windows_i386"), &target_windows_i386 },
{ str_lit("windows_amd64"), &target_windows_amd64 },
- { str_lit("freebsd_386"), &target_freebsd_386 },
+ { str_lit("freebsd_i386"), &target_freebsd_i386 },
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
+ { str_lit("openbsd_amd64"), &target_openbsd_amd64 },
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
{ str_lit("js_wasm32"), &target_js_wasm32 },
- // { str_lit("freestanding_wasm64"), &target_freestanding_wasm64 },
+
+ { str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv },
};
NamedTargetMetrics *selected_target_metrics;
@@ -524,7 +615,6 @@ bool allow_check_foreign_filepath(void) {
return true;
}
-
// TODO(bill): OS dependent versions for the BuildContext
// join_path
// is_dir
@@ -703,10 +793,38 @@ String internal_odin_root_dir(void) {
len = readlink("/proc/curproc/exe", &path_buf[0], path_buf.count);
#elif defined(GB_SYSTEM_DRAGONFLYBSD)
len = readlink("/proc/curproc/file", &path_buf[0], path_buf.count);
-#else
+#elif defined(GB_SYSTEM_LINUX)
len = readlink("/proc/self/exe", &path_buf[0], path_buf.count);
+#elif defined(GB_SYSTEM_OPENBSD)
+ int error;
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC_ARGS,
+ getpid(),
+ KERN_PROC_ARGV,
+ };
+ // get argv size
+ error = sysctl(mib, 4, NULL, (size_t *) &len, NULL, 0);
+ if (error == -1) {
+ // sysctl error
+ return make_string(nullptr, 0);
+ }
+ // get argv
+ char **argv = (char **)gb_malloc(len);
+ error = sysctl(mib, 4, argv, (size_t *) &len, NULL, 0);
+ if (error == -1) {
+ // sysctl error
+ gb_mfree(argv);
+ return make_string(nullptr, 0);
+ }
+ // copy argv[0] to path_buf
+ len = gb_strlen(argv[0]);
+ if(len < path_buf.count) {
+ gb_memmove(&path_buf[0], argv[0], len);
+ }
+ gb_mfree(argv);
#endif
- if(len == 0) {
+ if(len == 0 || len == -1) {
return make_string(nullptr, 0);
}
if (len < path_buf.count) {
@@ -834,6 +952,21 @@ bool has_asm_extension(String const &path) {
return false;
}
+// temporary
+char *token_pos_to_string(TokenPos const &pos) {
+ gbString s = gb_string_make_reserve(temporary_allocator(), 128);
+ String file = get_file_path_string(pos.file_id);
+ switch (build_context.ODIN_ERROR_POS_STYLE) {
+ default: /*fallthrough*/
+ case ErrorPosStyle_Default:
+ s = gb_string_append_fmt(s, "%.*s(%d:%d)", LIT(file), pos.line, pos.column);
+ break;
+ case ErrorPosStyle_Unix:
+ s = gb_string_append_fmt(s, "%.*s:%d:%d:", LIT(file), pos.line, pos.column);
+ break;
+ }
+ return s;
+}
void init_build_context(TargetMetrics *cross_target) {
BuildContext *bc = &build_context;
@@ -846,25 +979,31 @@ void init_build_context(TargetMetrics *cross_target) {
bc->ODIN_VENDOR = str_lit("odin");
bc->ODIN_VERSION = ODIN_VERSION;
bc->ODIN_ROOT = odin_root_dir();
- switch (bc->build_mode) {
- default:
- case BuildMode_Executable:
- bc->ODIN_BUILD_MODE = str_lit("executable");
- break;
- case BuildMode_DynamicLibrary:
- bc->ODIN_BUILD_MODE = str_lit("dynamic");
- break;
- case BuildMode_Object:
- bc->ODIN_BUILD_MODE = str_lit("object");
- break;
- case BuildMode_Assembly:
- bc->ODIN_BUILD_MODE = str_lit("assembly");
- break;
- case BuildMode_LLVM_IR:
- bc->ODIN_BUILD_MODE = str_lit("llvm-ir");
- break;
+
+ {
+ char const *found = gb_get_env("ODIN_ERROR_POS_STYLE", permanent_allocator());
+ if (found) {
+ ErrorPosStyle kind = ErrorPosStyle_Default;
+ String style = make_string_c(found);
+ style = string_trim_whitespace(style);
+ if (style == "" || style == "default" || style == "odin") {
+ kind = ErrorPosStyle_Default;
+ } else if (style == "unix" || style == "gcc" || style == "clang" || style == "llvm") {
+ kind = ErrorPosStyle_Unix;
+ } else {
+ gb_printf_err("Invalid ODIN_ERROR_POS_STYLE: got %.*s\n", LIT(style));
+ gb_printf_err("Valid formats:\n");
+ gb_printf_err("\t\"default\" or \"odin\"\n");
+ gb_printf_err("\t\tpath(line:column) message\n");
+ gb_printf_err("\t\"unix\"\n");
+ gb_printf_err("\t\tpath:line:column: message\n");
+ gb_exit(1);
+ }
+
+ build_context.ODIN_ERROR_POS_STYLE = kind;
+ }
}
-
+
bc->copy_file_contents = true;
TargetMetrics *metrics = nullptr;
@@ -880,18 +1019,22 @@ void init_build_context(TargetMetrics *cross_target) {
#endif
#elif defined(GB_SYSTEM_FREEBSD)
metrics = &target_freebsd_amd64;
+ #elif defined(GB_SYSTEM_OPENBSD)
+ metrics = &target_openbsd_amd64;
+ #elif defined(GB_CPU_ARM)
+ metrics = &target_linux_arm64;
#else
metrics = &target_linux_amd64;
#endif
#else
#if defined(GB_SYSTEM_WINDOWS)
- metrics = &target_windows_386;
+ metrics = &target_windows_i386;
#elif defined(GB_SYSTEM_OSX)
#error "Build Error: Unsupported architecture"
#elif defined(GB_SYSTEM_FREEBSD)
- metrics = &target_freebsd_386;
+ metrics = &target_freebsd_i386;
#else
- metrics = &target_linux_386;
+ metrics = &target_linux_i386;
#endif
#endif
@@ -910,7 +1053,6 @@ void init_build_context(TargetMetrics *cross_target) {
bc->metrics = *metrics;
bc->ODIN_OS = target_os_names[metrics->os];
bc->ODIN_ARCH = target_arch_names[metrics->arch];
- bc->ODIN_ENDIAN = target_endian_names[target_endians[metrics->arch]];
bc->endian_kind = target_endians[metrics->arch];
bc->word_size = metrics->word_size;
bc->max_align = metrics->max_align;
@@ -920,6 +1062,21 @@ void init_build_context(TargetMetrics *cross_target) {
bc->threaded_checker = true;
#endif
+ if (bc->disable_red_zone) {
+ if (!!is_arch_wasm() && bc->metrics.os == TargetOs_freestanding) {
+ gb_printf_err("-disable-red-zone is not support for this target");
+ gb_exit(1);
+ }
+ }
+
+ if (bc->metrics.os == TargetOs_freestanding) {
+ bc->no_entry_point = true;
+ } else {
+ if (bc->disallow_rtti) {
+ gb_printf_err("-disallow-rtti is only allowed on freestanding targets\n");
+ gb_exit(1);
+ }
+ }
// NOTE(zangent): The linker flags to set the build architecture are different
// across OSs. It doesn't make sense to allocate extra data on the heap
@@ -937,8 +1094,11 @@ void init_build_context(TargetMetrics *cross_target) {
case TargetOs_freebsd:
bc->link_flags = str_lit("-arch x86-64 ");
break;
+ case TargetOs_openbsd:
+ bc->link_flags = str_lit("-arch x86-64 ");
+ break;
}
- } else if (bc->metrics.arch == TargetArch_386) {
+ } else if (bc->metrics.arch == TargetArch_i386) {
switch (bc->metrics.os) {
case TargetOs_windows:
bc->link_flags = str_lit("/machine:x86 ");
@@ -959,6 +1119,9 @@ void init_build_context(TargetMetrics *cross_target) {
case TargetOs_darwin:
bc->link_flags = str_lit("-arch arm64 ");
break;
+ case TargetOs_linux:
+ bc->link_flags = str_lit("-arch aarch64 ");
+ break;
}
} else if (is_arch_wasm()) {
gbString link_flags = gb_string_make(heap_allocator(), " ");
@@ -968,14 +1131,14 @@ void init_build_context(TargetMetrics *cross_target) {
if (bc->metrics.arch == TargetArch_wasm64) {
link_flags = gb_string_appendc(link_flags, "-mwas64 ");
}
- if (bc->metrics.os == TargetOs_freestanding) {
+ if (bc->no_entry_point) {
link_flags = gb_string_appendc(link_flags, "--no-entry ");
}
bc->link_flags = make_string_c(link_flags);
// Disallow on wasm
- build_context.use_separate_modules = false;
+ bc->use_separate_modules = false;
} else {
gb_printf_err("Compiler Error: Unsupported architecture\n");
gb_exit(1);
@@ -986,3 +1149,191 @@ void init_build_context(TargetMetrics *cross_target) {
#undef LINK_FLAG_X64
#undef LINK_FLAG_386
}
+
+#if defined(GB_SYSTEM_WINDOWS)
+// NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
+// NOTE(Jeroen): No longer needed in `main.cpp -> linker_stage`. We now resolve those paths in `init_build_paths`.
+#include "microsoft_craziness.h"
+#endif
+
+// NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate.
+// We've previously called `parse_build_flags`, so `out_filepath` should be set.
+bool init_build_paths(String init_filename) {
+ gbAllocator ha = heap_allocator();
+ BuildContext *bc = &build_context;
+
+ // NOTE(Jeroen): We're pre-allocating BuildPathCOUNT slots so that certain paths are always at the same enumerated index.
+ array_init(&bc->build_paths, permanent_allocator(), BuildPathCOUNT);
+
+ // [BuildPathMainPackage] Turn given init path into a `Path`, which includes normalizing it into a full path.
+ bc->build_paths[BuildPath_Main_Package] = path_from_string(ha, init_filename);
+
+ bool produces_output_file = false;
+ if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) {
+ produces_output_file = true;
+ } else if (bc->command_kind & Command__does_build) {
+ produces_output_file = true;
+ }
+
+ if (!produces_output_file) {
+ // Command doesn't produce output files. We're done.
+ return true;
+ }
+
+ #if defined(GB_SYSTEM_WINDOWS)
+ if (bc->resource_filepath.len > 0) {
+ bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath);
+ bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath);
+ bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc"));
+ bc->build_paths[BuildPath_RES].ext = copy_string(ha, STR_LIT("res"));
+ }
+
+ if (bc->pdb_filepath.len > 0) {
+ bc->build_paths[BuildPath_PDB] = path_from_string(ha, bc->pdb_filepath);
+ }
+
+ if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) {
+ // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
+ Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
+
+ if (find_result.windows_sdk_version == 0) {
+ gb_printf_err("Windows SDK not found.\n");
+ return false;
+ }
+
+ GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
+ GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
+
+ if (find_result.windows_sdk_root.len > 0) {
+ bc->build_paths[BuildPath_Win_SDK_Root] = path_from_string(ha, find_result.windows_sdk_root);
+ }
+
+ if (find_result.windows_sdk_um_library_path.len > 0) {
+ bc->build_paths[BuildPath_Win_SDK_UM_Lib] = path_from_string(ha, find_result.windows_sdk_um_library_path);
+ }
+
+ if (find_result.windows_sdk_ucrt_library_path.len > 0) {
+ bc->build_paths[BuildPath_Win_SDK_UCRT_Lib] = path_from_string(ha, find_result.windows_sdk_ucrt_library_path);
+ }
+
+ if (find_result.vs_exe_path.len > 0) {
+ bc->build_paths[BuildPath_VS_EXE] = path_from_string(ha, find_result.vs_exe_path);
+ }
+
+ if (find_result.vs_library_path.len > 0) {
+ bc->build_paths[BuildPath_VS_LIB] = path_from_string(ha, find_result.vs_library_path);
+ }
+
+ gb_free(ha, find_result.windows_sdk_root.text);
+ gb_free(ha, find_result.windows_sdk_um_library_path.text);
+ gb_free(ha, find_result.windows_sdk_ucrt_library_path.text);
+ gb_free(ha, find_result.vs_exe_path.text);
+ gb_free(ha, find_result.vs_library_path.text);
+
+ }
+ #endif
+
+ // All the build targets and OSes.
+ String output_extension;
+
+ if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) {
+ output_extension = STR_LIT("odin-doc");
+ } else if (is_arch_wasm()) {
+ output_extension = STR_LIT("wasm");
+ } else if (build_context.build_mode == BuildMode_Executable) {
+ // By default use a .bin executable extension.
+ output_extension = STR_LIT("bin");
+
+ if (build_context.metrics.os == TargetOs_windows) {
+ output_extension = STR_LIT("exe");
+ } else if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
+ output_extension = make_string(nullptr, 0);
+ }
+ } else if (build_context.build_mode == BuildMode_DynamicLibrary) {
+ // By default use a .so shared library extension.
+ output_extension = STR_LIT("so");
+
+ if (build_context.metrics.os == TargetOs_windows) {
+ output_extension = STR_LIT("dll");
+ } else if (build_context.metrics.os == TargetOs_darwin) {
+ output_extension = STR_LIT("dylib");
+ }
+ } else if (build_context.build_mode == BuildMode_Object) {
+ // By default use a .o object extension.
+ output_extension = STR_LIT("o");
+
+ if (build_context.metrics.os == TargetOs_windows) {
+ output_extension = STR_LIT("obj");
+ }
+ } else if (build_context.build_mode == BuildMode_Assembly) {
+ // By default use a .S asm extension.
+ output_extension = STR_LIT("S");
+ } else if (build_context.build_mode == BuildMode_LLVM_IR) {
+ output_extension = STR_LIT("ll");
+ } else {
+ GB_PANIC("Unhandled build mode/target combination.\n");
+ }
+
+ if (bc->out_filepath.len > 0) {
+ bc->build_paths[BuildPath_Output] = path_from_string(ha, bc->out_filepath);
+ if (build_context.metrics.os == TargetOs_windows) {
+ String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
+ defer (gb_free(ha, output_file.text));
+ if (path_is_directory(bc->build_paths[BuildPath_Output])) {
+ gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
+ return false;
+ } else if (bc->build_paths[BuildPath_Output].ext.len == 0) {
+ gb_printf_err("Output path %.*s must have an appropriate extension.\n", LIT(output_file));
+ return false;
+ }
+ }
+ } else {
+ Path output_path;
+
+ if (str_eq(init_filename, str_lit("."))) {
+ // We must name the output file after the current directory.
+ debugf("Output name will be created from current base name %.*s.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename));
+ String last_element = last_path_element(bc->build_paths[BuildPath_Main_Package].basename);
+
+ if (last_element.len == 0) {
+ gb_printf_err("The output name is created from the last path element. `%.*s` has none. Use `-out:output_name.ext` to set it.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename));
+ return false;
+ }
+ output_path.basename = copy_string(ha, bc->build_paths[BuildPath_Main_Package].basename);
+ output_path.name = copy_string(ha, last_element);
+
+ } else {
+ // Init filename was not 'current path'.
+ // Contruct the output name from the path elements as usual.
+ String output_name = remove_directory_from_path(init_filename);
+ output_name = remove_extension_from_path(output_name);
+ output_name = copy_string(ha, string_trim_whitespace(output_name));
+ output_path = path_from_string(ha, output_name);
+
+ // Replace extension.
+ if (output_path.ext.len > 0) {
+ gb_free(ha, output_path.ext.text);
+ }
+ }
+ output_path.ext = copy_string(ha, output_extension);
+
+ bc->build_paths[BuildPath_Output] = output_path;
+ }
+
+ // Do we have an extension? We might not if the output filename was supplied.
+ if (bc->build_paths[BuildPath_Output].ext.len == 0) {
+ if (build_context.metrics.os == TargetOs_windows || build_context.build_mode != BuildMode_Executable) {
+ bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension);
+ }
+ }
+
+ // Check if output path is a directory.
+ if (path_is_directory(bc->build_paths[BuildPath_Output])) {
+ String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
+ defer (gb_free(ha, output_file.text));
+ gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
+ return false;
+ }
+
+ return true;
+} \ No newline at end of file
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index f93cf9886..6c7972d45 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -143,6 +143,270 @@ void check_or_return_split_types(CheckerContext *c, Operand *x, String const &na
}
+bool does_require_msgSend_stret(Type *return_type) {
+ if (return_type == nullptr) {
+ return false;
+ }
+ if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) {
+ i64 struct_limit = type_size_of(t_uintptr) << 1;
+ return type_size_of(return_type) > struct_limit;
+ }
+ if (build_context.metrics.arch == TargetArch_arm64) {
+ return false;
+ }
+
+ // if (build_context.metrics.arch == TargetArch_arm32) {
+ // i64 struct_limit = type_size_of(t_uintptr);
+ // // NOTE(bill): This is technically wrong
+ // return is_type_struct(return_type) && !is_type_raw_union(return_type) && type_size_of(return_type) > struct_limit;
+ // }
+ GB_PANIC("unsupported architecture");
+ return false;
+}
+
+ObjcMsgKind get_objc_proc_kind(Type *return_type) {
+ if (return_type == nullptr) {
+ return ObjcMsg_normal;
+ }
+
+ if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) {
+ if (is_type_float(return_type)) {
+ return ObjcMsg_fpret;
+ }
+ if (build_context.metrics.arch == TargetArch_amd64) {
+ if (is_type_complex(return_type)) {
+ // URL: https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/message.h#L143-L159
+ return ObjcMsg_fpret;
+ }
+ }
+ }
+ if (build_context.metrics.arch != TargetArch_arm64) {
+ if (does_require_msgSend_stret(return_type)) {
+ return ObjcMsg_stret;
+ }
+ }
+ return ObjcMsg_normal;
+}
+
+void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types) {
+ ObjcMsgKind kind = get_objc_proc_kind(return_type);
+
+ Scope *scope = create_scope(c->info, nullptr);
+
+ // NOTE(bill, 2022-02-08): the backend's ABI handling should handle this correctly, I hope
+ Type *params = alloc_type_tuple();
+ {
+ auto variables = array_make<Entity *>(permanent_allocator(), 0, param_types.count);
+
+ for_array(i, param_types) {
+ Type *type = param_types[i];
+ Entity *param = alloc_entity_param(scope, blank_token, type, false, true);
+ array_add(&variables, param);
+ }
+ params->Tuple.variables = slice_from_array(variables);
+ }
+
+ Type *results = alloc_type_tuple();
+ if (return_type) {
+ auto variables = array_make<Entity *>(permanent_allocator(), 1);
+ results->Tuple.variables = slice_from_array(variables);
+ Entity *param = alloc_entity_param(scope, blank_token, return_type, false, true);
+ results->Tuple.variables[0] = param;
+ }
+
+
+ ObjcMsgData data = {};
+ data.kind = kind;
+ data.proc_type = alloc_type_proc(scope, params, param_types.count, results, results->Tuple.variables.count, false, ProcCC_CDecl);
+
+ mutex_lock(&c->info->objc_types_mutex);
+ map_set(&c->info->objc_msgSend_types, call, data);
+ mutex_unlock(&c->info->objc_types_mutex);
+
+ try_to_add_package_dependency(c, "runtime", "objc_msgSend");
+ try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret");
+ try_to_add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
+ try_to_add_package_dependency(c, "runtime", "objc_msgSend_stret");
+}
+
+bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) {
+ Operand op = {};
+ check_expr(c, &op, expr);
+ if (op.mode == Addressing_Constant && op.value.kind == ExactValue_String) {
+ if (name_) *name_ = op.value.value_string;
+ return true;
+ }
+ gbString e = expr_to_string(op.expr);
+ gbString t = type_to_string(op.type);
+ error(op.expr, "'%.*s' expected a constant string value, got %s of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+}
+
+bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
+ String builtin_name = builtin_procs[id].name;
+
+ if (build_context.metrics.os != TargetOs_darwin) {
+ // allow on doc generation (e.g. Metal stuff)
+ if (build_context.command_kind != Command_doc && build_context.command_kind != Command_check) {
+ error(call, "'%.*s' only works on darwin", LIT(builtin_name));
+ }
+ }
+
+
+ ast_node(ce, CallExpr, call);
+ switch (id) {
+ default:
+ GB_PANIC("Implement objective built-in procedure: %.*s", LIT(builtin_name));
+ return false;
+
+ case BuiltinProc_objc_send: {
+ Type *return_type = nullptr;
+
+ Operand rt = {};
+ check_expr_or_type(c, &rt, ce->args[0]);
+ if (rt.mode == Addressing_Type) {
+ return_type = rt.type;
+ } else if (is_operand_nil(rt)) {
+ return_type = nullptr;
+ } else {
+ gbString e = expr_to_string(rt.expr);
+ error(rt.expr, "'%.*s' expected a type or nil to define the return type of the Objective-C call, got %s", LIT(builtin_name), e);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->type = return_type;
+ operand->mode = return_type ? Addressing_Value : Addressing_NoValue;
+
+ String class_name = {};
+ String sel_name = {};
+
+ Type *sel_type = t_objc_SEL;
+ Operand self = {};
+ check_expr_or_type(c, &self, ce->args[1]);
+ if (self.mode == Addressing_Type) {
+ if (!is_type_objc_object(self.type)) {
+ gbString t = type_to_string(self.type);
+ error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got type %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+ if (!has_type_got_objc_class_attribute(self.type)) {
+ gbString t = type_to_string(self.type);
+ error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=<string>) , got type %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ sel_type = t_objc_Class;
+ } else if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) {
+ gbString e = expr_to_string(self.expr);
+ gbString t = type_to_string(self.type);
+ error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ } else if (!is_type_pointer(self.type)) {
+ gbString e = expr_to_string(self.expr);
+ gbString t = type_to_string(self.type);
+ error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ } else {
+ Type *type = type_deref(self.type);
+ if (!(type->kind == Type_Named &&
+ type->Named.type_name != nullptr &&
+ type->Named.type_name->TypeName.objc_class_name != "")) {
+ gbString t = type_to_string(type);
+ error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=<string>) , got type %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+ }
+
+
+ if (!is_constant_string(c, builtin_name, ce->args[2], &sel_name)) {
+ return false;
+ }
+
+ isize const arg_offset = 1;
+ auto param_types = slice_make<Type *>(permanent_allocator(), ce->args.count-arg_offset);
+ param_types[0] = t_objc_id;
+ param_types[1] = sel_type;
+
+ for (isize i = 2+arg_offset; i < ce->args.count; i++) {
+ Operand x = {};
+ check_expr(c, &x, ce->args[i]);
+ param_types[i-arg_offset] = x.type;
+ }
+
+ add_objc_proc_type(c, call, return_type, param_types);
+
+ return true;
+ } break;
+
+ case BuiltinProc_objc_find_selector:
+ case BuiltinProc_objc_find_class:
+ case BuiltinProc_objc_register_selector:
+ case BuiltinProc_objc_register_class:
+ {
+ String sel_name = {};
+ if (!is_constant_string(c, builtin_name, ce->args[0], &sel_name)) {
+ return false;
+ }
+
+ switch (id) {
+ case BuiltinProc_objc_find_selector:
+ case BuiltinProc_objc_register_selector:
+ operand->type = t_objc_SEL;
+ break;
+ case BuiltinProc_objc_find_class:
+ case BuiltinProc_objc_register_class:
+ operand->type = t_objc_Class;
+ break;
+
+ }
+ operand->mode = Addressing_Value;
+
+ try_to_add_package_dependency(c, "runtime", "objc_lookUpClass");
+ try_to_add_package_dependency(c, "runtime", "sel_registerName");
+ try_to_add_package_dependency(c, "runtime", "objc_allocateClassPair");
+ return true;
+ } break;
+ }
+}
+
+bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String const &builtin_name, OdinAtomicMemoryOrder *memory_order_, char const *extra_message = nullptr) {
+ Operand x = {};
+ check_expr_with_type_hint(c, &x, expr, t_atomic_memory_order);
+ if (x.mode == Addressing_Invalid) {
+ return false;
+ }
+ if (!are_types_identical(x.type, t_atomic_memory_order) || x.mode != Addressing_Constant) {
+ gbString str = type_to_string(x.type);
+ if (extra_message) {
+ error(x.expr, "Expected a constant Atomic_Memory_Order value for the %s of '%.*s', got %s", extra_message, LIT(builtin_name), str);
+ } else {
+ error(x.expr, "Expected a constant Atomic_Memory_Order value for '%.*s', got %s", LIT(builtin_name), str);
+ }
+ gb_string_free(str);
+ return false;
+ }
+ i64 value = exact_value_to_i64(x.value);
+ if (value < 0 || value >= OdinAtomicMemoryOrder_COUNT) {
+ error(x.expr, "Illegal Atomic_Memory_Order value, got %lld", cast(long long)value);
+ return false;
+ }
+ if (memory_order_) {
+ *memory_order_ = cast(OdinAtomicMemoryOrder)value;
+ }
+
+ return true;
+
+}
bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
ast_node(ce, CallExpr, call);
@@ -179,9 +443,21 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_len:
case BuiltinProc_min:
case BuiltinProc_max:
+ case BuiltinProc_type_is_subtype_of:
+ case BuiltinProc_objc_send:
+ case BuiltinProc_objc_find_selector:
+ case BuiltinProc_objc_find_class:
+ case BuiltinProc_objc_register_selector:
+ case BuiltinProc_objc_register_class:
+ case BuiltinProc_atomic_type_is_lock_free:
// NOTE(bill): The first arg may be a Type, this will be checked case by case
break;
+ case BuiltinProc_atomic_thread_fence:
+ case BuiltinProc_atomic_signal_fence:
+ // NOTE(bill): first type will require a type hint
+ break;
+
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name.string;
@@ -202,7 +478,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
- String builtin_name = builtin_procs[id].name;;
+ String builtin_name = builtin_procs[id].name;
if (ce->args.count > 0) {
@@ -219,6 +495,19 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name));
break;
+ case BuiltinProc_objc_send:
+ case BuiltinProc_objc_find_selector:
+ case BuiltinProc_objc_find_class:
+ case BuiltinProc_objc_register_selector:
+ case BuiltinProc_objc_register_class:
+ return check_builtin_objc_procedure(c, operand, call, id, type_hint);
+
+ case BuiltinProc___entry_point:
+ operand->mode = Addressing_NoValue;
+ operand->type = nullptr;
+ mpmc_enqueue(&c->info->intrinsics_entry_point_usage, call);
+ break;
+
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name.string;
@@ -542,8 +831,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
} else if (name == "assert") {
- if (ce->args.count != 1) {
- error(call, "'#assert' expects 1 argument, got %td", ce->args.count);
+ if (ce->args.count != 1 && ce->args.count != 2) {
+ error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count);
return false;
}
if (!is_type_boolean(operand->type) || operand->mode != Addressing_Constant) {
@@ -552,15 +841,37 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
gb_string_free(str);
return false;
}
+ if (ce->args.count == 2) {
+ Ast *arg = unparen_expr(ce->args[1]);
+ if (arg == nullptr || arg->kind != Ast_BasicLit || arg->BasicLit.token.kind != Token_String) {
+ gbString str = expr_to_string(arg);
+ error(call, "'%s' is not a constant string", str);
+ gb_string_free(str);
+ return false;
+ }
+ }
+
if (!operand->value.value_bool) {
- gbString arg = expr_to_string(ce->args[0]);
- error(call, "Compile time assertion: %s", arg);
+ gbString arg1 = expr_to_string(ce->args[0]);
+ gbString arg2 = {};
+
+ if (ce->args.count == 1) {
+ error(call, "Compile time assertion: %s", arg1);
+ } else {
+ arg2 = expr_to_string(ce->args[1]);
+ error(call, "Compile time assertion: %s (%s)", arg1, arg2);
+ }
+
if (c->proc_name != "") {
gbString str = type_to_string(c->curr_proc_sig);
error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str);
gb_string_free(str);
}
- gb_string_free(arg);
+
+ gb_string_free(arg1);
+ if (ce->args.count == 2) {
+ gb_string_free(arg2);
+ }
}
operand->type = t_untyped_bool;
@@ -698,7 +1009,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
mode = Addressing_Constant;
value = exact_value_i64(at->EnumeratedArray.count);
type = t_untyped_integer;
- } else if (is_type_slice(op_type) && id == BuiltinProc_len) {
+ } else if ((is_type_slice(op_type) || is_type_relative_slice(op_type)) && id == BuiltinProc_len) {
mode = Addressing_Value;
} else if (is_type_dynamic_array(op_type)) {
mode = Addressing_Value;
@@ -852,7 +1163,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
Selection sel = lookup_field(type, field_name, false);
if (sel.entity == nullptr) {
- gbString type_str = type_to_string(type);
+ gbString type_str = type_to_string_shorthand(type);
error(ce->args[0],
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
gb_string_free(type_str);
@@ -864,7 +1175,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return false;
}
if (sel.indirect) {
- gbString type_str = type_to_string(type);
+ gbString type_str = type_to_string_shorthand(type);
error(ce->args[0],
"Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
gb_string_free(type_str);
@@ -925,7 +1236,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
Selection sel = lookup_field(type, field_name, false);
if (sel.entity == nullptr) {
- gbString type_str = type_to_string(type);
+ gbString type_str = type_to_string_shorthand(type);
error(ce->args[0],
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
gb_string_free(type_str);
@@ -937,7 +1248,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return false;
}
if (sel.indirect) {
- gbString type_str = type_to_string(type);
+ gbString type_str = type_to_string_shorthand(type);
error(ce->args[0],
"Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
gb_string_free(type_str);
@@ -987,6 +1298,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (c->scope->flags&ScopeFlag_Global) {
compiler_error("'type_info_of' Cannot be declared within the runtime package due to how the internals of the compiler works");
}
+ if (build_context.disallow_rtti) {
+ error(call, "'%.*s' has been disallowed", LIT(builtin_name));
+ return false;
+ }
// NOTE(bill): The type information may not be setup yet
init_core_type_info(c->checker);
@@ -999,9 +1314,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
Type *t = o.type;
if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(t)) {
if (is_type_polymorphic(t)) {
- error(ce->args[0], "Invalid argument for 'type_info_of', unspecialized polymorphic type");
+ error(ce->args[0], "Invalid argument for '%.*s', unspecialized polymorphic type", LIT(builtin_name));
} else {
- error(ce->args[0], "Invalid argument for 'type_info_of'");
+ error(ce->args[0], "Invalid argument for '%.*s'", LIT(builtin_name));
}
return false;
}
@@ -1012,7 +1327,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (is_operand_value(o) && is_type_typeid(t)) {
add_package_dependency(c, "runtime", "__type_info_of");
} else if (o.mode != Addressing_Type) {
- error(expr, "Expected a type or typeid for 'type_info_of'");
+ error(expr, "Expected a type or typeid for '%.*s'", LIT(builtin_name));
return false;
}
@@ -1026,6 +1341,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (c->scope->flags&ScopeFlag_Global) {
compiler_error("'typeid_of' Cannot be declared within the runtime package due to how the internals of the compiler works");
}
+ if (build_context.disallow_rtti) {
+ error(call, "'%.*s' has been disallowed", LIT(builtin_name));
+ return false;
+ }
// NOTE(bill): The type information may not be setup yet
init_core_type_info(c->checker);
@@ -1037,7 +1356,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
Type *t = o.type;
if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(operand->type)) {
- error(ce->args[0], "Invalid argument for 'typeid_of'");
+ error(ce->args[0], "Invalid argument for '%.*s'", LIT(builtin_name));
return false;
}
t = default_type(t);
@@ -1045,7 +1364,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
add_type_info_type(c, t);
if (o.mode != Addressing_Type) {
- error(expr, "Expected a type for 'typeid_of'");
+ error(expr, "Expected a type for '%.*s'", LIT(builtin_name));
return false;
}
@@ -1858,7 +2177,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
f64 r = operand->value.value_complex->real;
f64 i = operand->value.value_complex->imag;
operand->value = exact_value_float(gb_sqrt(r*r + i*i));
-
+ break;
+ }
+ case ExactValue_Quaternion: {
+ f64 r = operand->value.value_quaternion->real;
+ f64 i = operand->value.value_quaternion->imag;
+ f64 j = operand->value.value_quaternion->jmag;
+ f64 k = operand->value.value_quaternion->kmag;
+ operand->value = exact_value_float(gb_sqrt(r*r + i*i + j*j + k*k));
break;
}
default:
@@ -1877,10 +2203,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
}
- if (is_type_complex(operand->type)) {
+ if (is_type_complex_or_quaternion(operand->type)) {
operand->type = base_complex_elem_type(operand->type);
}
- GB_ASSERT(!is_type_complex(operand->type));
+ GB_ASSERT(!is_type_complex_or_quaternion(operand->type));
break;
}
@@ -2170,9 +2496,43 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
operand->mode = Addressing_Value;
- if (is_type_array(t)) {
+ if (t->kind == Type_Array) {
+ i32 rank = type_math_rank(t);
// Do nothing
- operand->type = x.type;
+ operand->type = x.type;
+ if (rank > 2) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with a rank of 2, got %s of rank %d", LIT(builtin_name), s, rank);
+ gb_string_free(s);
+ return false;
+ } else if (rank == 2) {
+ Type *inner = base_type(t->Array.elem);
+ GB_ASSERT(inner->kind == Type_Array);
+ Type *elem = inner->Array.elem;
+ Type *array_inner = alloc_type_array(elem, t->Array.count);
+ Type *array_outer = alloc_type_array(array_inner, inner->Array.count);
+ operand->type = array_outer;
+
+ i64 elements = t->Array.count*inner->Array.count;
+ i64 size = type_size_of(operand->type);
+ if (!is_type_valid_for_matrix_elems(elem)) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with a base element type of an integer, float, or complex number, got %s", LIT(builtin_name), s);
+ gb_string_free(s);
+ } else if (elements > MATRIX_ELEMENT_COUNT_MAX) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with a maximum of %d elements, got %s with %lld elements", LIT(builtin_name), MATRIX_ELEMENT_COUNT_MAX, s, elements);
+ gb_string_free(s);
+ } else if (elements > MATRIX_ELEMENT_COUNT_MAX) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with non-zero elements, got %s", LIT(builtin_name), MATRIX_ELEMENT_COUNT_MAX, s);
+ gb_string_free(s);
+ } else if (size > MATRIX_ELEMENT_MAX_SIZE) {
+ gbString s = type_to_string(x.type);
+ error(call, "Too large of a type for '%.*s', got %s of size %lld, maximum size %d", LIT(builtin_name), s, cast(long long)size, MATRIX_ELEMENT_MAX_SIZE);
+ gb_string_free(s);
+ }
+ }
} else {
GB_ASSERT(t->kind == Type_Matrix);
operand->type = alloc_type_matrix(t->Matrix.elem, t->Matrix.column_count, t->Matrix.row_count);
@@ -2895,11 +3255,56 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
- case BuiltinProc_atomic_fence:
- case BuiltinProc_atomic_fence_acq:
- case BuiltinProc_atomic_fence_rel:
- case BuiltinProc_atomic_fence_acqrel:
- operand->mode = Addressing_NoValue;
+ case BuiltinProc_atomic_type_is_lock_free:
+ {
+ Ast *expr = ce->args[0];
+ Operand o = {};
+ check_expr_or_type(c, &o, expr);
+
+ if (o.mode == Addressing_Invalid || o.mode == Addressing_Builtin) {
+ return false;
+ }
+ if (o.type == nullptr || o.type == t_invalid || is_type_asm_proc(o.type)) {
+ error(o.expr, "Invalid argument to '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ if (is_type_polymorphic(o.type)) {
+ error(o.expr, "'%.*s' of polymorphic type cannot be determined", LIT(builtin_name));
+ return false;
+ }
+ if (is_type_untyped(o.type)) {
+ error(o.expr, "'%.*s' of untyped type is not allowed", LIT(builtin_name));
+ return false;
+ }
+ Type *t = o.type;
+ bool is_lock_free = is_type_lock_free(t);
+
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_bool;
+ operand->value = exact_value_bool(is_lock_free);
+ break;
+ }
+
+ case BuiltinProc_atomic_thread_fence:
+ case BuiltinProc_atomic_signal_fence:
+ {
+ OdinAtomicMemoryOrder memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[0], builtin_name, &memory_order)) {
+ return false;
+ }
+ switch (memory_order) {
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_release:
+ case OdinAtomicMemoryOrder_acq_rel:
+ case OdinAtomicMemoryOrder_seq_cst:
+ break;
+ default:
+ error(ce->args[0], "Illegal memory ordering for '%.*s', got .%s", LIT(builtin_name), OdinAtomicMemoryOrder_strings[memory_order]);
+ break;
+ }
+
+ operand->mode = Addressing_NoValue;
+ }
break;
case BuiltinProc_volatile_store:
@@ -2907,9 +3312,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_unaligned_store:
/*fallthrough*/
case BuiltinProc_atomic_store:
- case BuiltinProc_atomic_store_rel:
- case BuiltinProc_atomic_store_relaxed:
- case BuiltinProc_atomic_store_unordered:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -2925,14 +3327,40 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
+ case BuiltinProc_atomic_store_explicit:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ check_expr_with_type_hint(c, &x, ce->args[1], elem);
+ check_assignment(c, &x, elem, builtin_name);
+
+ OdinAtomicMemoryOrder memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, &memory_order)) {
+ return false;
+ }
+ switch (memory_order) {
+ case OdinAtomicMemoryOrder_consume:
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_acq_rel:
+ error(ce->args[2], "Illegal memory order .%s for '%.*s'", OdinAtomicMemoryOrder_strings[memory_order], LIT(builtin_name));
+ break;
+ }
+
+ operand->type = nullptr;
+ operand->mode = Addressing_NoValue;
+ break;
+ }
+
+
case BuiltinProc_volatile_load:
/*fallthrough*/
case BuiltinProc_unaligned_load:
/*fallthrough*/
case BuiltinProc_atomic_load:
- case BuiltinProc_atomic_load_acq:
- case BuiltinProc_atomic_load_relaxed:
- case BuiltinProc_atomic_load_unordered:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -2944,41 +3372,75 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
+ case BuiltinProc_atomic_load_explicit:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+
+ OdinAtomicMemoryOrder memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[1], builtin_name, &memory_order)) {
+ return false;
+ }
+
+ switch (memory_order) {
+ case OdinAtomicMemoryOrder_release:
+ case OdinAtomicMemoryOrder_acq_rel:
+ error(ce->args[1], "Illegal memory order .%s for '%.*s'", OdinAtomicMemoryOrder_strings[memory_order], LIT(builtin_name));
+ break;
+ }
+
+ operand->type = elem;
+ operand->mode = Addressing_Value;
+ break;
+ }
+
case BuiltinProc_atomic_add:
- case BuiltinProc_atomic_add_acq:
- case BuiltinProc_atomic_add_rel:
- case BuiltinProc_atomic_add_acqrel:
- case BuiltinProc_atomic_add_relaxed:
case BuiltinProc_atomic_sub:
- case BuiltinProc_atomic_sub_acq:
- case BuiltinProc_atomic_sub_rel:
- case BuiltinProc_atomic_sub_acqrel:
- case BuiltinProc_atomic_sub_relaxed:
case BuiltinProc_atomic_and:
- case BuiltinProc_atomic_and_acq:
- case BuiltinProc_atomic_and_rel:
- case BuiltinProc_atomic_and_acqrel:
- case BuiltinProc_atomic_and_relaxed:
case BuiltinProc_atomic_nand:
- case BuiltinProc_atomic_nand_acq:
- case BuiltinProc_atomic_nand_rel:
- case BuiltinProc_atomic_nand_acqrel:
- case BuiltinProc_atomic_nand_relaxed:
case BuiltinProc_atomic_or:
- case BuiltinProc_atomic_or_acq:
- case BuiltinProc_atomic_or_rel:
- case BuiltinProc_atomic_or_acqrel:
- case BuiltinProc_atomic_or_relaxed:
case BuiltinProc_atomic_xor:
- case BuiltinProc_atomic_xor_acq:
- case BuiltinProc_atomic_xor_rel:
- case BuiltinProc_atomic_xor_acqrel:
- case BuiltinProc_atomic_xor_relaxed:
- case BuiltinProc_atomic_xchg:
- case BuiltinProc_atomic_xchg_acq:
- case BuiltinProc_atomic_xchg_rel:
- case BuiltinProc_atomic_xchg_acqrel:
- case BuiltinProc_atomic_xchg_relaxed:
+ case BuiltinProc_atomic_exchange:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ check_expr_with_type_hint(c, &x, ce->args[1], elem);
+ check_assignment(c, &x, elem, builtin_name);
+
+ Type *t = type_deref(operand->type);
+ switch (id) {
+ case BuiltinProc_atomic_add:
+ case BuiltinProc_atomic_sub:
+ if (!is_type_numeric(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ } else if (is_type_different_to_arch_endianness(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ }
+ }
+
+ operand->type = elem;
+ operand->mode = Addressing_Value;
+ break;
+ }
+
+ case BuiltinProc_atomic_add_explicit:
+ case BuiltinProc_atomic_sub_explicit:
+ case BuiltinProc_atomic_and_explicit:
+ case BuiltinProc_atomic_nand_explicit:
+ case BuiltinProc_atomic_or_explicit:
+ case BuiltinProc_atomic_xor_explicit:
+ case BuiltinProc_atomic_exchange_explicit:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -2989,30 +3451,34 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
check_expr_with_type_hint(c, &x, ce->args[1], elem);
check_assignment(c, &x, elem, builtin_name);
+
+ if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, nullptr)) {
+ return false;
+ }
+
+ Type *t = type_deref(operand->type);
+ switch (id) {
+ case BuiltinProc_atomic_add_explicit:
+ case BuiltinProc_atomic_sub_explicit:
+ if (!is_type_numeric(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ } else if (is_type_different_to_arch_endianness(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ }
+ break;
+ }
+
operand->type = elem;
operand->mode = Addressing_Value;
break;
}
- case BuiltinProc_atomic_cxchg:
- case BuiltinProc_atomic_cxchg_acq:
- case BuiltinProc_atomic_cxchg_rel:
- case BuiltinProc_atomic_cxchg_acqrel:
- case BuiltinProc_atomic_cxchg_relaxed:
- case BuiltinProc_atomic_cxchg_failrelaxed:
- case BuiltinProc_atomic_cxchg_failacq:
- case BuiltinProc_atomic_cxchg_acq_failrelaxed:
- case BuiltinProc_atomic_cxchg_acqrel_failrelaxed:
-
- case BuiltinProc_atomic_cxchgweak:
- case BuiltinProc_atomic_cxchgweak_acq:
- case BuiltinProc_atomic_cxchgweak_rel:
- case BuiltinProc_atomic_cxchgweak_acqrel:
- case BuiltinProc_atomic_cxchgweak_relaxed:
- case BuiltinProc_atomic_cxchgweak_failrelaxed:
- case BuiltinProc_atomic_cxchgweak_failacq:
- case BuiltinProc_atomic_cxchgweak_acq_failrelaxed:
- case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed:
+ case BuiltinProc_atomic_compare_exchange_strong:
+ case BuiltinProc_atomic_compare_exchange_weak:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -3026,11 +3492,110 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
check_assignment(c, &x, elem, builtin_name);
check_assignment(c, &y, elem, builtin_name);
+ Type *t = type_deref(operand->type);
+ if (!is_type_comparable(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a comparable type for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ }
+
+ operand->mode = Addressing_OptionalOk;
+ operand->type = elem;
+ break;
+ }
+
+ case BuiltinProc_atomic_compare_exchange_strong_explicit:
+ case BuiltinProc_atomic_compare_exchange_weak_explicit:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ Operand y = {};
+ check_expr_with_type_hint(c, &x, ce->args[1], elem);
+ check_expr_with_type_hint(c, &y, ce->args[2], elem);
+ check_assignment(c, &x, elem, builtin_name);
+ check_assignment(c, &y, elem, builtin_name);
+
+ OdinAtomicMemoryOrder success_memory_order = {};
+ OdinAtomicMemoryOrder failure_memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[3], builtin_name, &success_memory_order, "success ordering")) {
+ return false;
+ }
+ if (!check_atomic_memory_order_argument(c, ce->args[4], builtin_name, &failure_memory_order, "failure ordering")) {
+ return false;
+ }
+
+ Type *t = type_deref(operand->type);
+ if (!is_type_comparable(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a comparable type for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ }
+
+ bool invalid_combination = false;
+
+ switch (success_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_release:
+ if (failure_memory_order != OdinAtomicMemoryOrder_relaxed) {
+ invalid_combination = true;
+ }
+ break;
+ case OdinAtomicMemoryOrder_consume:
+ switch (failure_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_consume:
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+ break;
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_acq_rel:
+ switch (failure_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_consume:
+ case OdinAtomicMemoryOrder_acquire:
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+ break;
+ case OdinAtomicMemoryOrder_seq_cst:
+ switch (failure_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_consume:
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_seq_cst:
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+
+
+ if (invalid_combination) {
+ error(ce->args[3], "Illegal memory order pairing for '%.*s', success = .%s, failure = .%s",
+ LIT(builtin_name),
+ OdinAtomicMemoryOrder_strings[success_memory_order],
+ OdinAtomicMemoryOrder_strings[failure_memory_order]
+ );
+ }
+
operand->mode = Addressing_OptionalOk;
operand->type = elem;
break;
}
- break;
case BuiltinProc_fixed_point_mul:
case BuiltinProc_fixed_point_div:
@@ -3211,8 +3776,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case TargetOs_linux:
case TargetOs_essence:
case TargetOs_freebsd:
+ case TargetOs_openbsd:
switch (build_context.metrics.arch) {
- case TargetArch_386:
+ case TargetArch_i386:
case TargetArch_amd64:
case TargetArch_arm64:
max_arg_count = 7;
@@ -3297,9 +3863,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_type_is_simple_compare:
case BuiltinProc_type_is_dereferenceable:
case BuiltinProc_type_is_valid_map_key:
+ case BuiltinProc_type_is_valid_matrix_elements:
case BuiltinProc_type_is_named:
case BuiltinProc_type_is_pointer:
case BuiltinProc_type_is_array:
+ case BuiltinProc_type_is_enumerated_array:
case BuiltinProc_type_is_slice:
case BuiltinProc_type_is_dynamic_array:
case BuiltinProc_type_is_map:
@@ -3307,10 +3875,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_type_is_union:
case BuiltinProc_type_is_enum:
case BuiltinProc_type_is_proc:
- case BuiltinProc_type_is_bit_field:
- case BuiltinProc_type_is_bit_field_value:
case BuiltinProc_type_is_bit_set:
case BuiltinProc_type_is_simd_vector:
+ case BuiltinProc_type_is_matrix:
case BuiltinProc_type_is_specialized_polymorphic_record:
case BuiltinProc_type_is_unspecialized_polymorphic_record:
case BuiltinProc_type_has_nil:
@@ -3359,6 +3926,37 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
break;
+ case BuiltinProc_type_field_type:
+ {
+ Operand op = {};
+ Type *bt = check_type(c, ce->args[0]);
+ Type *type = base_type(bt);
+ if (type == nullptr || type == t_invalid) {
+ error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ check_expr(c, &x, ce->args[1]);
+
+ if (!is_type_string(x.type) || x.mode != Addressing_Constant || x.value.kind != ExactValue_String) {
+ error(ce->args[1], "Expected a const string for field argument");
+ return false;
+ }
+
+ String field_name = x.value.value_string;
+
+ Selection sel = lookup_field(type, field_name, false);
+ if (sel.index.count == 0) {
+ gbString t = type_to_string(type);
+ error(ce->args[1], "'%.*s' is not a field of type %s", LIT(field_name), t);
+ gb_string_free(t);
+ return false;
+ }
+ operand->mode = Addressing_Type;
+ operand->type = sel.entity->type;
+ break;
+ }
+ break;
case BuiltinProc_type_is_specialization_of:
{
@@ -3678,6 +4276,31 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
+ case BuiltinProc_type_is_subtype_of:
+ {
+ Operand op_src = {};
+ Operand op_dst = {};
+
+ check_expr_or_type(c, &op_src, ce->args[0]);
+ if (op_src.mode != Addressing_Type) {
+ gbString e = expr_to_string(op_src.expr);
+ error(op_src.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e);
+ gb_string_free(e);
+ return false;
+ }
+ check_expr_or_type(c, &op_dst, ce->args[1]);
+ if (op_dst.mode != Addressing_Type) {
+ gbString e = expr_to_string(op_dst.expr);
+ error(op_dst.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->value = exact_value_bool(is_type_subtype_of(op_src.type, op_dst.type));
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_bool;
+ } break;
+
case BuiltinProc_type_field_index_of:
{
Operand op = {};
@@ -3767,6 +4390,87 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->type = t_hasher_proc;
break;
}
+
+ case BuiltinProc_constant_utf16_cstring:
+ {
+ String value = {};
+ if (!is_constant_string(c, builtin_name, ce->args[0], &value)) {
+ return false;
+ }
+ operand->mode = Addressing_Value;
+ operand->type = alloc_type_multi_pointer(t_u16);
+ operand->value = {};
+ break;
+ }
+
+
+ case BuiltinProc_wasm_memory_grow:
+ {
+ if (!is_arch_wasm()) {
+ error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name));
+ return false;
+ }
+
+ Operand index = {};
+ Operand delta = {};
+ check_expr(c, &index, ce->args[0]); if (index.mode == Addressing_Invalid) return false;
+ check_expr(c, &delta, ce->args[1]); if (delta.mode == Addressing_Invalid) return false;
+
+ convert_to_typed(c, &index, t_uintptr); if (index.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &delta, t_uintptr); if (delta.mode == Addressing_Invalid) return false;
+
+ if (!is_operand_value(index) || !check_is_assignable_to(c, &index, t_uintptr)) {
+ gbString e = expr_to_string(index.expr);
+ gbString t = type_to_string(index.type);
+ error(index.expr, "'%.*s' expected a uintptr for the memory index, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ if (!is_operand_value(delta) || !check_is_assignable_to(c, &delta, t_uintptr)) {
+ gbString e = expr_to_string(delta.expr);
+ gbString t = type_to_string(delta.type);
+ error(delta.expr, "'%.*s' expected a uintptr for the memory delta, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = t_int;
+ operand->value = {};
+ break;
+ }
+ break;
+ case BuiltinProc_wasm_memory_size:
+ {
+ if (!is_arch_wasm()) {
+ error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name));
+ return false;
+ }
+
+ Operand index = {};
+ check_expr(c, &index, ce->args[0]); if (index.mode == Addressing_Invalid) return false;
+
+ convert_to_typed(c, &index, t_uintptr); if (index.mode == Addressing_Invalid) return false;
+
+ if (!is_operand_value(index) || !check_is_assignable_to(c, &index, t_uintptr)) {
+ gbString e = expr_to_string(index.expr);
+ gbString t = type_to_string(index.type);
+ error(index.expr, "'%.*s' expected a uintptr for the memory index, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = t_int;
+ operand->value = {};
+ break;
+ }
+ break;
+
}
return true;
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 42f68203c..5acd56097 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -174,6 +174,10 @@ void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
return;
}
+ if (is_type_proc(e->type)) {
+ error(e->token, "Illegal declaration of a constant procedure value");
+ }
+
e->parent_proc_decl = ctx->curr_proc_decl;
e->Constant.value = operand->value;
@@ -238,6 +242,51 @@ isize total_attribute_count(DeclInfo *decl) {
return attribute_count;
}
+Type *clone_enum_type(CheckerContext *ctx, Type *original_enum_type, Type *named_type) {
+ // NOTE(bill, 2022-02-05): Stupid edge case for `distinct` declarations
+ //
+ // X :: enum {A, B, C}
+ // Y :: distinct X
+ //
+ // To make Y be just like X, it will need to copy the elements of X and change their type
+ // so that they match Y rather than X.
+ GB_ASSERT(original_enum_type != nullptr);
+ GB_ASSERT(named_type != nullptr);
+ GB_ASSERT(original_enum_type->kind == Type_Enum);
+ GB_ASSERT(named_type->kind == Type_Named);
+
+ Scope *parent = original_enum_type->Enum.scope->parent;
+ Scope *scope = create_scope(nullptr, parent);
+
+
+ Type *et = alloc_type_enum();
+ et->Enum.base_type = original_enum_type->Enum.base_type;
+ et->Enum.min_value = original_enum_type->Enum.min_value;
+ et->Enum.max_value = original_enum_type->Enum.max_value;
+ et->Enum.min_value_index = original_enum_type->Enum.min_value_index;
+ et->Enum.max_value_index = original_enum_type->Enum.max_value_index;
+ et->Enum.scope = scope;
+
+ auto fields = array_make<Entity *>(permanent_allocator(), original_enum_type->Enum.fields.count);
+ for_array(i, fields) {
+ Entity *old = original_enum_type->Enum.fields[i];
+
+ Entity *e = alloc_entity_constant(scope, old->token, named_type, old->Constant.value);
+ e->file = old->file;
+ e->identifier = clone_ast(old->identifier);
+ e->flags |= EntityFlag_Visited;
+ e->state = EntityState_Resolved;
+ e->Constant.flags = old->Constant.flags;
+ e->Constant.docs = old->Constant.docs;
+ e->Constant.comment = old->Constant.comment;
+
+ fields[i] = e;
+ add_entity(ctx, scope, nullptr, e);
+ add_entity_use(ctx, e->identifier, e);
+ }
+ et->Enum.fields = fields;
+ return et;
+}
void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) {
GB_ASSERT(e->type == nullptr);
@@ -258,7 +307,11 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
Type *bt = check_type_expr(ctx, te, named);
check_type_path_pop(ctx);
- named->Named.base = base_type(bt);
+ Type *base = base_type(bt);
+ if (is_distinct && bt->kind == Type_Named && base->kind == Type_Enum) {
+ base = clone_enum_type(ctx, base, named);
+ }
+ named->Named.base = base;
if (is_distinct && is_type_typeid(e->type)) {
error(init_expr, "'distinct' cannot be applied to 'typeid'");
@@ -289,6 +342,13 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
if (decl != nullptr) {
AttributeContext ac = {};
check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
+ if (e->kind == Entity_TypeName && ac.objc_class != "") {
+ e->TypeName.objc_class_name = ac.objc_class;
+
+ if (type_size_of(e->type) > 0) {
+ error(e->token, "@(objc_class) marked type must be of zero size");
+ }
+ }
}
@@ -380,12 +440,56 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
if (type_expr) {
e->type = check_type(ctx, type_expr);
+ if (are_types_identical(e->type, t_typeid)) {
+ e->type = nullptr;
+ e->kind = Entity_TypeName;
+ check_type_decl(ctx, e, init, named_type);
+ return;
+ }
}
Operand operand = {};
if (init != nullptr) {
- Entity *entity = nullptr;
+ Entity *entity = check_entity_from_ident_or_selector(ctx, init, false);
+ if (entity != nullptr && entity->kind == Entity_TypeName) {
+ // @TypeAliasingProblem
+ // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases
+ // being "confused" as constants
+ //
+ // A :: B
+ // C :: proc "c" (^A)
+ // B :: struct {x: C}
+ //
+ // A gets evaluated first, and then checks B.
+ // B then checks C.
+ // C then tries to check A which is unresolved but thought to be a constant.
+ // Therefore within C's check, A errs as "not a type".
+ //
+ // This is because a const declaration may or may not be a type and this cannot
+ // be determined from a syntactical standpoint.
+ // This check allows the compiler to override the entity to be checked as a type.
+ //
+ // There is no problem if B is prefixed with the `#type` helper enforcing at
+ // both a syntax and semantic level that B must be a type.
+ //
+ // A :: #type B
+ //
+ // This approach is not fool proof and can fail in case such as:
+ //
+ // X :: type_of(x)
+ // X :: Foo(int).Type
+ //
+ // Since even these kind of declarations may cause weird checking cycles.
+ // For the time being, these are going to be treated as an unfortunate error
+ // until there is a proper delaying system to try declaration again if they
+ // have failed.
+
+ e->kind = Entity_TypeName;
+ check_type_decl(ctx, e, init, named_type);
+ return;
+ }
+ entity = nullptr;
if (init->kind == Ast_Ident) {
entity = check_ident(ctx, &operand, init, nullptr, e->type, true);
} else if (init->kind == Ast_SelectorExpr) {
@@ -732,6 +836,63 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
+ if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) {
+ if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
+ error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
+ } else if (ac.objc_type == nullptr) {
+ error(e->token, "@(objc_name) requires that @(objc_type) to be set");
+ } else if (ac.objc_name.len == 0 && ac.objc_type) {
+ error(e->token, "@(objc_name) is required with @(objc_type)");
+ } else {
+ Type *t = ac.objc_type;
+ if (t->kind == Type_Named) {
+ Entity *tn = t->Named.type_name;
+
+ GB_ASSERT(tn->kind == Entity_TypeName);
+
+ if (tn->scope != e->scope) {
+ error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
+ } else {
+ mutex_lock(&global_type_name_objc_metadata_mutex);
+ defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
+
+ if (!tn->TypeName.objc_metadata) {
+ tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
+ }
+ auto *md = tn->TypeName.objc_metadata;
+ mutex_lock(md->mutex);
+ defer (mutex_unlock(md->mutex));
+
+ if (!ac.objc_is_class_method) {
+ bool ok = true;
+ for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
+ if (entry.name == ac.objc_name) {
+ error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+ }
+ } else {
+ bool ok = true;
+ for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
+ if (entry.name == ac.objc_name) {
+ error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+ }
+ }
+ }
+ }
+ }
+ }
+
switch (e->Procedure.optimization_mode) {
case ProcedureOptimizationMode_None:
@@ -777,21 +938,23 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
if (e->pkg != nullptr && e->token.string == "main") {
- if (pt->param_count != 0 ||
- pt->result_count != 0) {
- gbString str = type_to_string(proc_type);
- error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str);
- gb_string_free(str);
- }
- if (pt->calling_convention != default_calling_convention()) {
- error(e->token, "Procedure 'main' cannot have a custom calling convention");
- }
- pt->calling_convention = default_calling_convention();
- if (e->pkg->kind == Package_Init) {
- if (ctx->info->entry_point != nullptr) {
- error(e->token, "Redeclaration of the entry pointer procedure 'main'");
- } else {
- ctx->info->entry_point = e;
+ if (e->pkg->kind != Package_Runtime) {
+ if (pt->param_count != 0 ||
+ pt->result_count != 0) {
+ gbString str = type_to_string(proc_type);
+ error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str);
+ gb_string_free(str);
+ }
+ if (pt->calling_convention != default_calling_convention()) {
+ error(e->token, "Procedure 'main' cannot have a custom calling convention");
+ }
+ pt->calling_convention = default_calling_convention();
+ if (e->pkg->kind == Package_Init) {
+ if (ctx->info->entry_point != nullptr) {
+ error(e->token, "Redeclaration of the entry pointer procedure 'main'");
+ } else {
+ ctx->info->entry_point = e;
+ }
}
}
}
@@ -924,7 +1087,9 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
"\tother at %s",
LIT(name), token_pos_to_string(pos));
} else if (name == "main") {
- error(d->proc_lit, "The link name 'main' is reserved for internal use");
+ if (d->entity->pkg->kind != Package_Runtime) {
+ error(d->proc_lit, "The link name 'main' is reserved for internal use");
+ }
} else {
string_map_set(fp, key, e);
}
@@ -971,6 +1136,12 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
}
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
+ if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) {
+ e->Variable.thread_local_model.len = 0;
+ // NOTE(bill): ignore this message for the time begin
+ // error(e->token, "@(thread_local) is not supported for this target platform");
+ }
+
String context_name = str_lit("variable declaration");
if (type_expr != nullptr) {
@@ -1046,6 +1217,8 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
Operand o = {};
check_expr_with_type_hint(ctx, &o, init_expr, e->type);
check_init_variable(ctx, e, &o, str_lit("variable declaration"));
+
+ check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
}
void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) {
@@ -1303,7 +1476,7 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
if (t->kind == Type_Struct) {
Scope *scope = t->Struct.scope;
GB_ASSERT(scope != nullptr);
- for_array(i, scope->elements.entries) {
+ MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
Entity *f = scope->elements.entries[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr);
@@ -1321,11 +1494,10 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
}
}
-
- for_array(i, using_entities) {
+ MUTEX_GUARD_BLOCK(ctx->scope->mutex) for_array(i, using_entities) {
Entity *e = using_entities[i].e;
Entity *uvar = using_entities[i].uvar;
- Entity *prev = scope_insert(ctx->scope, uvar);
+ Entity *prev = scope_insert(ctx->scope, uvar, false);
if (prev != nullptr) {
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));
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 560e5e607..336a711d4 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -119,6 +119,115 @@ void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name
void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint);
void check_or_return_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_);
+
+void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") {
+ auto results = did_you_mean_results(d);
+ if (results.count != 0) {
+ error_line("\tSuggestion: Did you mean?\n");
+ for_array(i, results) {
+ String const &target = results[i].target;
+ error_line("\t\t%s%.*s\n", prefix, LIT(target));
+ // error_line("\t\t%.*s %td\n", LIT(target), results[i].distance);
+ }
+ }
+}
+
+void populate_check_did_you_mean_objc_entity(StringSet *set, Entity *e, bool is_type) {
+ if (e->kind != Entity_TypeName) {
+ return;
+ }
+ if (e->TypeName.objc_metadata == nullptr) {
+ return;
+ }
+ TypeNameObjCMetadata *objc_metadata = e->TypeName.objc_metadata;
+ Type *t = base_type(e->type);
+ GB_ASSERT(t->kind == Type_Struct);
+
+ if (is_type) {
+ for_array(i, objc_metadata->type_entries) {
+ String name = objc_metadata->type_entries[i].name;
+ string_set_add(set, name);
+ }
+ } else {
+ for_array(i, objc_metadata->value_entries) {
+ String name = objc_metadata->value_entries[i].name;
+ string_set_add(set, name);
+ }
+ }
+
+ for_array(i, t->Struct.fields) {
+ Entity *f = t->Struct.fields[i];
+ if (f->flags & EntityFlag_Using && f->type != nullptr) {
+ if (f->type->kind == Type_Named && f->type->Named.type_name) {
+ populate_check_did_you_mean_objc_entity(set, f->type->Named.type_name, is_type);
+ }
+ }
+ }
+}
+
+
+void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") {
+ ERROR_BLOCK();
+ GB_ASSERT(e->kind == Entity_TypeName);
+ GB_ASSERT(e->TypeName.objc_metadata != nullptr);
+ auto *objc_metadata = e->TypeName.objc_metadata;
+ mutex_lock(objc_metadata->mutex);
+ defer (mutex_unlock(objc_metadata->mutex));
+
+ StringSet set = {};
+ string_set_init(&set, heap_allocator());
+ defer (string_set_destroy(&set));
+ populate_check_did_you_mean_objc_entity(&set, e, is_type);
+
+
+ DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), set.entries.count, name);
+ defer (did_you_mean_destroy(&d));
+ for_array(i, set.entries) {
+ did_you_mean_append(&d, set.entries[i].value);
+ }
+ check_did_you_mean_print(&d, prefix);
+}
+
+void check_did_you_mean_type(String const &name, Array<Entity *> const &fields, char const *prefix = "") {
+ ERROR_BLOCK();
+
+ DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
+ defer (did_you_mean_destroy(&d));
+
+ for_array(i, fields) {
+ did_you_mean_append(&d, fields[i]->token.string);
+ }
+ check_did_you_mean_print(&d, prefix);
+}
+
+
+void check_did_you_mean_type(String const &name, Slice<Entity *> const &fields, char const *prefix = "") {
+ ERROR_BLOCK();
+
+ DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
+ defer (did_you_mean_destroy(&d));
+
+ for_array(i, fields) {
+ did_you_mean_append(&d, fields[i]->token.string);
+ }
+ check_did_you_mean_print(&d, prefix);
+}
+
+void check_did_you_mean_scope(String const &name, Scope *scope, char const *prefix = "") {
+ ERROR_BLOCK();
+
+ DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), scope->elements.entries.count, name);
+ defer (did_you_mean_destroy(&d));
+
+ mutex_lock(&scope->mutex);
+ for_array(i, scope->elements.entries) {
+ Entity *e = scope->elements.entries[i].value;
+ did_you_mean_append(&d, e->token.string);
+ }
+ mutex_unlock(&scope->mutex);
+ check_did_you_mean_print(&d, prefix);
+}
+
Entity *entity_from_expr(Ast *expr) {
expr = unparen_expr(expr);
switch (expr->kind) {
@@ -176,42 +285,6 @@ void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes, isize reser
}
}
-
-isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) {
- Type *prev_src = src;
- src = type_deref(src);
- if (!src_is_ptr) {
- src_is_ptr = src != prev_src;
- }
- src = base_type(src);
-
- if (!is_type_struct(src)) {
- return 0;
- }
-
- for_array(i, src->Struct.fields) {
- Entity *f = src->Struct.fields[i];
- if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) {
- continue;
- }
-
- if (are_types_identical(f->type, dst)) {
- return level+1;
- }
- if (src_is_ptr && is_type_pointer(dst)) {
- if (are_types_identical(f->type, type_deref(dst))) {
- return level+1;
- }
- }
- isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr);
- if (nested_level > 0) {
- return nested_level;
- }
- }
-
- return 0;
-}
-
bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_entity, Type *type,
Array<Operand> *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) {
///////////////////////////////////////////////////////////////////////////////
@@ -456,6 +529,10 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type);
#define MAXIMUM_TYPE_DISTANCE 10
i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) {
+ if (c == nullptr) {
+ GB_ASSERT(operand->mode == Addressing_Value);
+ GB_ASSERT(is_type_typed(operand->type));
+ }
if (operand->mode == Addressing_Invalid ||
type == t_invalid) {
return -1;
@@ -621,6 +698,42 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
return 1;
}
}
+
+ // TODO(bill): Determine which rule is a better on in practice
+ #if 1
+ if (dst->Union.variants.count == 1) {
+ Type *vt = dst->Union.variants[0];
+ i64 score = check_distance_between_types(c, operand, vt);
+ if (score >= 0) {
+ return score+2;
+ }
+ }
+ #else
+ // NOTE(bill): check to see you can assign to it with one of the variants?
+ i64 prev_lowest_score = -1;
+ i64 lowest_score = -1;
+ for_array(i, dst->Union.variants) {
+ Type *vt = dst->Union.variants[i];
+ i64 score = check_distance_between_types(c, operand, vt);
+ if (score >= 0) {
+ if (lowest_score < 0) {
+ lowest_score = score;
+ } else {
+ if (prev_lowest_score < 0) {
+ prev_lowest_score = lowest_score;
+ } else {
+ prev_lowest_score = gb_min(prev_lowest_score, lowest_score);
+ }
+ lowest_score = gb_min(lowest_score, score);
+ }
+ }
+ }
+ if (lowest_score >= 0) {
+ if (prev_lowest_score != lowest_score) { // remove possible ambiguities
+ return lowest_score+2;
+ }
+ }
+ #endif
}
if (is_type_relative_pointer(dst)) {
@@ -649,6 +762,13 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
return 4;
}
}
+
+ if (is_type_complex_or_quaternion(dst)) {
+ Type *elem = base_complex_elem_type(dst);
+ if (are_types_identical(elem, base_type(src))) {
+ return 5;
+ }
+ }
if (is_type_array(dst)) {
Type *elem = base_array_type(dst);
@@ -723,6 +843,13 @@ bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) {
return check_is_assignable_to_with_score(c, operand, type, &score);
}
+bool internal_check_is_assignable_to(Type *src, Type *dst) {
+ Operand x = {};
+ x.type = src;
+ x.mode = Addressing_Value;
+ return check_is_assignable_to(nullptr, &x, dst);
+}
+
AstPackage *get_package_of_type(Type *type) {
for (;;) {
if (type == nullptr) {
@@ -1227,7 +1354,6 @@ bool check_cycle(CheckerContext *c, Entity *curr, bool report) {
return false;
}
-
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;
@@ -1363,8 +1489,12 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ
case Entity_TypeName:
o->mode = Addressing_Type;
if (check_cycle(c, e, true)) {
- type = t_invalid;
+ o->type = t_invalid;
+ }
+ if (o->type != nullptr && type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
+ o->type = base_type(o->type);
}
+
break;
case Entity_ImportName:
@@ -3354,7 +3484,16 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ
}
}
}
+ 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) {
+ String key = operand->value.value_string;
+ if (is_type_string(operand->type) && is_type_enum(target_type)) {
+ Type *et = base_type(target_type);
+ check_did_you_mean_type(key, et->Enum.fields, ".");
+ }
+ }
gb_string_free(from_type_str);
gb_string_free(type_str);
@@ -3972,54 +4111,6 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti
if (success_) *success_ = true;
return empty_exact_value;
}
-void check_did_you_mean_print(DidYouMeanAnswers *d) {
- auto results = did_you_mean_results(d);
- if (results.count != 0) {
- error_line("\tSuggestion: Did you mean?\n");
- for_array(i, results) {
- String const &target = results[i].target;
- error_line("\t\t%.*s\n", LIT(target));
- // error_line("\t\t%.*s %td\n", LIT(target), results[i].distance);
- }
- }
-}
-
-void check_did_you_mean_type(String const &name, Array<Entity *> const &fields) {
- ERROR_BLOCK();
-
- DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
- defer (did_you_mean_destroy(&d));
-
- for_array(i, fields) {
- did_you_mean_append(&d, fields[i]->token.string);
- }
- check_did_you_mean_print(&d);
-}
-
-void check_did_you_mean_type(String const &name, Slice<Entity *> const &fields) {
- ERROR_BLOCK();
-
- DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
- defer (did_you_mean_destroy(&d));
-
- for_array(i, fields) {
- did_you_mean_append(&d, fields[i]->token.string);
- }
- check_did_you_mean_print(&d);
-}
-
-void check_did_you_mean_scope(String const &name, Scope *scope) {
- ERROR_BLOCK();
-
- DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), scope->elements.entries.count, name);
- defer (did_you_mean_destroy(&d));
-
- for_array(i, scope->elements.entries) {
- Entity *e = scope->elements.entries[i].value;
- did_you_mean_append(&d, e->token.string);
- }
- check_did_you_mean_print(&d);
-}
Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize new_count) {
Type *array_type = base_type(type_deref(original_type));
@@ -4044,6 +4135,101 @@ Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize n
}
+bool is_entity_declared_for_selector(Entity *entity, Scope *import_scope, bool *allow_builtin) {
+ bool is_declared = entity != nullptr;
+ if (is_declared) {
+ 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;
+ } else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) {
+ is_declared = false;
+ }
+ }
+ return is_declared;
+}
+
+// NOTE(bill, 2022-02-03): see `check_const_decl` for why it exists reasoning
+Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node, bool ident_only) {
+ if (node->kind == Ast_Ident) {
+ String name = node->Ident.token.string;
+ return scope_lookup(c->scope, name);
+ } else if (!ident_only) if (node->kind == Ast_SelectorExpr) {
+ ast_node(se, SelectorExpr, node);
+ if (se->token.kind == Token_ArrowRight) {
+ return nullptr;
+ }
+
+ Ast *op_expr = se->expr;
+ Ast *selector = unparen_expr(se->selector);
+ if (selector == nullptr) {
+ return nullptr;
+ }
+ if (selector->kind != Ast_Ident) {
+ return nullptr;
+ }
+
+ Entity *entity = nullptr;
+ Entity *expr_entity = nullptr;
+ bool check_op_expr = true;
+
+ if (op_expr->kind == Ast_Ident) {
+ String op_name = op_expr->Ident.token.string;
+ Entity *e = scope_lookup(c->scope, op_name);
+ if (e == nullptr) {
+ return nullptr;
+ }
+ add_entity_use(c, op_expr, e);
+ expr_entity = e;
+
+ if (e != nullptr && e->kind == Entity_ImportName && selector->kind == Ast_Ident) {
+ // IMPORTANT NOTE(bill): This is very sloppy code but it's also very fragile
+ // It pretty much needs to be in this order and this way
+ // If you can clean this up, please do but be really careful
+ String import_name = op_name;
+ Scope *import_scope = e->ImportName.scope;
+ String entity_name = selector->Ident.token.string;
+
+ check_op_expr = false;
+ entity = scope_lookup_current(import_scope, entity_name);
+ bool allow_builtin = false;
+ if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) {
+ return nullptr;
+ }
+
+ check_entity_decl(c, entity, nullptr, nullptr);
+ if (entity->kind == Entity_ProcGroup) {
+ return entity;
+ }
+ GB_ASSERT_MSG(entity->type != nullptr, "%.*s (%.*s)", LIT(entity->token.string), LIT(entity_strings[entity->kind]));
+ }
+ }
+
+ Operand operand = {};
+ if (check_op_expr) {
+ check_expr_base(c, &operand, op_expr, nullptr);
+ if (operand.mode == Addressing_Invalid) {
+ return nullptr;
+ }
+ }
+
+ if (entity == nullptr && selector->kind == Ast_Ident) {
+ String field_name = selector->Ident.token.string;
+ if (is_type_dynamic_array(type_deref(operand.type))) {
+ init_mem_allocator(c->checker);
+ }
+ auto sel = lookup_field(operand.type, field_name, operand.mode == Addressing_Type);
+ entity = sel.entity;
+ }
+
+ if (entity != nullptr) {
+ return entity;
+ }
+ }
+ return nullptr;
+}
+
+
Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *type_hint) {
ast_node(se, SelectorExpr, node);
@@ -4092,18 +4278,8 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
check_op_expr = false;
entity = scope_lookup_current(import_scope, entity_name);
- bool is_declared = entity != nullptr;
bool allow_builtin = false;
- if (is_declared) {
- 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;
- } else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) {
- is_declared = false;
- }
- }
- if (!is_declared) {
+ if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) {
error(op_expr, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name));
operand->mode = Addressing_Invalid;
operand->expr = node;
@@ -4193,7 +4369,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
}
}
- 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))) {
// TODO(bill): Simd_Vector swizzling
String field_name = selector->Ident.token.string;
@@ -4294,14 +4470,19 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
if (entity == nullptr) {
gbString op_str = expr_to_string(op_expr);
- gbString type_str = type_to_string(operand->type);
+ gbString type_str = type_to_string_shorthand(operand->type);
gbString sel_str = expr_to_string(selector);
error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
if (operand->type != nullptr && selector->kind == Ast_Ident) {
String const &name = selector->Ident.token.string;
Type *bt = base_type(operand->type);
- if (bt->kind == Type_Struct) {
+ if (operand->type->kind == Type_Named &&
+ operand->type->Named.type_name &&
+ operand->type->Named.type_name->kind == Entity_TypeName &&
+ operand->type->Named.type_name->TypeName.objc_metadata) {
+ check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type);
+ } else if (bt->kind == Type_Struct) {
check_did_you_mean_type(name, bt->Struct.fields);
} else if (bt->kind == Type_Enum) {
check_did_you_mean_type(name, bt->Enum.fields);
@@ -4330,7 +4511,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
}
gbString op_str = expr_to_string(op_expr);
- gbString type_str = type_to_string(operand->type);
+ gbString type_str = type_to_string_shorthand(operand->type);
gbString sel_str = expr_to_string(selector);
error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
gb_string_free(sel_str);
@@ -4355,7 +4536,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
}
gbString op_str = expr_to_string(op_expr);
- gbString type_str = type_to_string(operand->type);
+ gbString type_str = type_to_string_shorthand(operand->type);
gbString sel_str = expr_to_string(selector);
error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
gb_string_free(sel_str);
@@ -4368,7 +4549,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
if (expr_entity != nullptr && is_type_polymorphic(expr_entity->type)) {
gbString op_str = expr_to_string(op_expr);
- gbString type_str = type_to_string(operand->type);
+ gbString type_str = type_to_string_shorthand(operand->type);
gbString sel_str = expr_to_string(selector);
error(op_expr, "Cannot access field '%s' from non-specialized polymorphic type '%s'", sel_str, op_str);
gb_string_free(sel_str);
@@ -4691,25 +4872,16 @@ bool is_expr_constant_zero(Ast *expr) {
return false;
}
-
-CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
- ast_node(ce, CallExpr, call);
- GB_ASSERT(is_type_proc(proc_type));
- proc_type = base_type(proc_type);
- TypeProc *pt = &proc_type->Proc;
-
+isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_) {
+ GB_ASSERT(pt != nullptr);
+ GB_ASSERT(pt->kind == Type_Proc);
isize param_count = 0;
isize param_count_excluding_defaults = 0;
- bool variadic = pt->variadic;
- bool vari_expand = (ce->ellipsis.pos.line != 0);
- i64 score = 0;
- bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
-
-
+ bool variadic = pt->Proc.variadic;
TypeTuple *param_tuple = nullptr;
- if (pt->params != nullptr) {
- param_tuple = &pt->params->Tuple;
+ if (pt->Proc.params != nullptr) {
+ param_tuple = &pt->Proc.params->Tuple;
param_count = param_tuple->variables.count;
if (variadic) {
@@ -4749,6 +4921,31 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
}
}
+ if (param_count_) *param_count_ = param_count;
+ return param_count_excluding_defaults;
+}
+
+
+CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
+ ast_node(ce, CallExpr, call);
+ GB_ASSERT(is_type_proc(proc_type));
+ proc_type = base_type(proc_type);
+ TypeProc *pt = &proc_type->Proc;
+
+ isize param_count = 0;
+ isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, &param_count);
+ bool variadic = pt->variadic;
+ bool vari_expand = (ce->ellipsis.pos.line != 0);
+ i64 score = 0;
+ bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
+
+
+ TypeTuple *param_tuple = nullptr;
+ if (pt->params != nullptr) {
+ param_tuple = &pt->params->Tuple;
+ }
+
+
CallArgumentError err = CallArgumentError_None;
Type *final_proc_type = proc_type;
Entity *gen_entity = nullptr;
@@ -5421,7 +5618,37 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
if (operand->mode == Addressing_ProcGroup) {
check_entity_decl(c, operand->proc_group, nullptr, nullptr);
- Array<Entity *> procs = proc_group_entities(c, *operand);
+ auto procs = proc_group_entities_cloned(c, *operand);
+
+ if (procs.count > 1) {
+ isize max_arg_count = args.count;
+ for_array(i, args) {
+ // NOTE(bill): The only thing that may have multiple values
+ // will be a call expression (assuming `or_return` and `()` will be stripped)
+ Ast *arg = strip_or_return_expr(args[i]);
+ if (arg && arg->kind == Ast_CallExpr) {
+ max_arg_count = ISIZE_MAX;
+ break;
+ }
+ }
+
+ for (isize proc_index = 0; proc_index < procs.count; /**/) {
+ Entity *proc = procs[proc_index];
+ Type *pt = base_type(proc->type);
+ if (!(pt != nullptr && is_type_proc(pt))) {
+ continue;
+ }
+
+ isize param_count = 0;
+ isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, &param_count);
+
+ if (param_count_excluding_defaults > max_arg_count) {
+ array_unordered_remove(&procs, proc_index);
+ } else {
+ proc_index++;
+ }
+ }
+ }
if (procs.count == 1) {
Ast *ident = operand->expr;
@@ -5451,6 +5678,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
return data;
}
+
Entity **lhs = nullptr;
isize lhs_count = -1;
@@ -5735,8 +5963,12 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
ctx.curr_proc_sig = e->type;
GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
+ bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
decl->where_clauses_evaluated = true;
+
+ if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+ check_procedure_later(c, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags);
+ }
}
return data;
}
@@ -5749,6 +5981,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
Entity *e = entity_of_node(ident);
+
CallArgumentData data = {};
CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
gb_unused(err);
@@ -5757,7 +5990,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
if (entity_to_use != nullptr) {
update_untyped_expr_type(c, operand->expr, entity_to_use->type, true);
}
-
if (data.gen_entity != nullptr) {
Entity *e = data.gen_entity;
DeclInfo *decl = data.gen_entity->decl_info;
@@ -5769,8 +6001,12 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
ctx.curr_proc_sig = e->type;
GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
+ bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
decl->where_clauses_evaluated = true;
+
+ if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+ check_procedure_later(c, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags);
+ }
}
return data;
}
@@ -6064,7 +6300,8 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
}
// NOTE(bill): Add type info the parameters
- add_type_info_type(c, o->type);
+ // TODO(bill, 2022-01-23): why was this line added in the first place? I'm commenting it out for the time being
+ // add_type_info_type(c, o->type);
}
{
@@ -6268,10 +6505,10 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
return builtin_procs[id].kind;
}
- Entity *e = entity_of_node(operand->expr);
+ Entity *initial_entity = entity_of_node(operand->expr);
- if (e != nullptr && e->kind == Entity_Procedure) {
- if (e->Procedure.deferred_procedure.entity != nullptr) {
+ if (initial_entity != nullptr && initial_entity->kind == Entity_Procedure) {
+ if (initial_entity->Procedure.deferred_procedure.entity != nullptr) {
call->viral_state_flags |= ViralStateFlag_ContainsDeferredProcedure;
}
}
@@ -6839,433 +7076,395 @@ void check_matrix_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *typ
}
-ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
- u32 prev_state_flags = c->state_flags;
- defer (c->state_flags = prev_state_flags);
- if (node->state_flags != 0) {
- u32 in = node->state_flags;
- u32 out = c->state_flags;
+struct TypeAndToken {
+ Type *type;
+ Token token;
+};
- if (in & StateFlag_no_bounds_check) {
- out |= StateFlag_no_bounds_check;
- out &= ~StateFlag_bounds_check;
- } else if (in & StateFlag_bounds_check) {
- out |= StateFlag_bounds_check;
- out &= ~StateFlag_no_bounds_check;
- }
+typedef PtrMap<uintptr, TypeAndToken> SeenMap;
- c->state_flags = out;
+void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Operand operand, bool use_expr = true) {
+ if (operand.mode != Addressing_Constant) {
+ return;
+ }
+ if (operand.value.kind == ExactValue_Invalid) {
+ return;
}
- ExprKind kind = Expr_Stmt;
-
- o->mode = Addressing_Invalid;
- o->type = t_invalid;
-
- switch (node->kind) {
- default:
- return kind;
+ uintptr key = hash_exact_value(operand.value);
+ TypeAndToken *found = map_get(seen, key);
+ if (found != nullptr) {
+ isize count = multi_map_count(seen, key);
+ TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
- case_ast_node(be, BadExpr, node)
- return kind;
- case_end;
+ multi_map_get_all(seen, key, taps);
+ for (isize i = 0; i < count; i++) {
+ TypeAndToken tap = taps[i];
+ if (!are_types_identical(operand.type, tap.type)) {
+ continue;
+ }
- case_ast_node(i, Implicit, node)
- switch (i->kind) {
- case Token_context:
- {
- if (c->proc_name.len == 0 && c->curr_proc_sig == nullptr) {
- error(node, "'context' is only allowed within procedures %p", c->curr_proc_decl);
- return kind;
- }
- if (unparen_expr(c->assignment_lhs_hint) == node) {
- c->scope->flags |= ScopeFlag_ContextDefined;
- }
+ TokenPos pos = tap.token.pos;
+ if (use_expr) {
+ gbString expr_str = expr_to_string(operand.expr);
+ error(operand.expr,
+ "Duplicate case '%s'\n"
+ "\tprevious case at %s",
+ expr_str,
+ token_pos_to_string(pos));
+ gb_string_free(expr_str);
+ } else {
+ error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos));
+ }
+ return;
+ }
+ }
- if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
- error(node, "'context' has not been defined within this scope");
- // Continue with value
- }
+ TypeAndToken tap = {operand.type, ast_token(operand.expr)};
+ multi_map_insert(seen, key, tap);
+}
- init_core_context(c->checker);
- o->mode = Addressing_Context;
- o->type = t_context;
- }
- break;
- default:
- error(node, "Illegal implicit name '%.*s'", LIT(i->string));
- return kind;
- }
- case_end;
+void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, TokenKind upper_op, Operand const &x, Operand const &lhs, Operand const &rhs) {
+ if (is_type_enum(x.type)) {
+ // TODO(bill): Fix this logic so it's fast!!!
- case_ast_node(i, Ident, node);
- check_ident(c, o, node, nullptr, type_hint, false);
- case_end;
+ i64 v0 = exact_value_to_i64(lhs.value);
+ i64 v1 = exact_value_to_i64(rhs.value);
+ Operand v = {};
+ v.mode = Addressing_Constant;
+ v.type = x.type;
+ v.expr = x.expr;
- case_ast_node(u, Undef, node);
- o->mode = Addressing_Value;
- o->type = t_untyped_undef;
- case_end;
+ Type *bt = base_type(x.type);
+ GB_ASSERT(bt->kind == Type_Enum);
+ for (i64 vi = v0; vi <= v1; vi++) {
+ if (upper_op != Token_LtEq && vi == v1) {
+ break;
+ }
+ bool found = false;
+ for_array(j, bt->Enum.fields) {
+ Entity *f = bt->Enum.fields[j];
+ GB_ASSERT(f->kind == Entity_Constant);
- case_ast_node(bl, BasicLit, node);
- Type *t = t_invalid;
- switch (node->tav.value.kind) {
- case ExactValue_String: t = t_untyped_string; break;
- case ExactValue_Float: t = t_untyped_float; break;
- case ExactValue_Complex: t = t_untyped_complex; break;
- case ExactValue_Quaternion: t = t_untyped_quaternion; break;
- case ExactValue_Integer:
- t = t_untyped_integer;
- if (bl->token.kind == Token_Rune) {
- t = t_untyped_rune;
+ i64 fv = exact_value_to_i64(f->Constant.value);
+ if (fv == vi) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ v.value = exact_value_i64(vi);
+ add_constant_switch_case(ctx, seen, v);
}
- break;
- default:
- GB_PANIC("Unhandled value type for basic literal");
- break;
}
+ } else {
+ add_constant_switch_case(ctx, seen, lhs);
+ if (upper_op == Token_LtEq) {
+ add_constant_switch_case(ctx, seen, rhs);
+ }
+ }
+}
+void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, Operand const &x) {
+ add_constant_switch_case(ctx, seen, x);
+}
- o->mode = Addressing_Constant;
- o->type = t;
- o->value = node->tav.value;
- case_end;
+ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(bd, BasicDirective, node);
- case_ast_node(bd, BasicDirective, node);
- o->mode = Addressing_Constant;
- String name = bd->name.string;
- if (name == "file") {
+ ExprKind kind = Expr_Expr;
+
+ o->mode = Addressing_Constant;
+ String name = bd->name.string;
+ if (name == "file") {
+ o->type = t_untyped_string;
+ o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id));
+ } else if (name == "line") {
+ o->type = t_untyped_integer;
+ o->value = exact_value_i64(bd->token.pos.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(get_file_path_string(bd->token.pos.file_id));
- } else if (name == "line") {
- o->type = t_untyped_integer;
- o->value = exact_value_i64(bd->token.pos.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 {
- o->type = t_untyped_string;
- o->value = exact_value_string(c->proc_name);
- }
- } else if (name == "caller_location") {
+ o->value = exact_value_string(str_lit(""));
+ } else {
+ o->type = t_untyped_string;
+ o->value = exact_value_string(c->proc_name);
+ }
+ } else if (name == "caller_location") {
+ init_core_source_code_location(c->checker);
+ error(node, "#caller_location may only be used as a default argument parameter");
+ o->type = t_source_code_location;
+ o->mode = Addressing_Value;
+ } else {
+ if (name == "location") {
init_core_source_code_location(c->checker);
- error(node, "#caller_location may only be used as a default argument parameter");
+ error(node, "'#%.*s' must be used in a call expression", LIT(name));
o->type = t_source_code_location;
o->mode = Addressing_Value;
+ } else if (
+ name == "assert" ||
+ name == "defined" ||
+ name == "config" ||
+ name == "load" ||
+ name == "load_hash" ||
+ name == "load_or"
+ ) {
+ error(node, "'#%.*s' must be used as a call", LIT(name));
+ o->type = t_invalid;
+ o->mode = Addressing_Invalid;
} else {
- if (name == "location") {
- init_core_source_code_location(c->checker);
- error(node, "'#%.*s' must be used in a call expression", LIT(name));
- o->type = t_source_code_location;
- o->mode = Addressing_Value;
- } else if (
- name == "assert" ||
- name == "defined" ||
- name == "config" ||
- name == "load" ||
- name == "load_hash" ||
- name == "load_or"
- ) {
- error(node, "'#%.*s' must be used as a call", LIT(name));
- o->type = t_invalid;
- o->mode = Addressing_Invalid;
- } else {
- error(node, "Unknown directive: #%.*s", LIT(name));
- o->type = t_invalid;
- o->mode = Addressing_Invalid;
- }
-
+ error(node, "Unknown directive: #%.*s", LIT(name));
+ o->type = t_invalid;
+ o->mode = Addressing_Invalid;
}
- case_end;
- case_ast_node(pg, ProcGroup, node);
- error(node, "Illegal use of a procedure group");
- o->mode = Addressing_Invalid;
- case_end;
+ }
+ return kind;
+}
- case_ast_node(pl, ProcLit, node);
- CheckerContext ctx = *c;
+ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ Operand cond = {Addressing_Invalid};
+ ast_node(te, TernaryIfExpr, node);
+ check_expr(c, &cond, te->cond);
+ node->viral_state_flags |= te->cond->viral_state_flags;
- DeclInfo *decl = nullptr;
- Type *type = alloc_type(Type_Proc);
- check_open_scope(&ctx, pl->type);
- {
- decl = make_decl_info(ctx.scope, ctx.decl);
- decl->proc_lit = node;
- ctx.decl = decl;
- defer (ctx.decl = ctx.decl->parent);
+ if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) {
+ error(te->cond, "Non-boolean condition in ternary if expression");
+ }
- if (pl->tags != 0) {
- error(node, "A procedure literal cannot have tags");
- pl->tags = 0; // TODO(bill): Should I zero this?!
- }
+ Operand x = {Addressing_Invalid};
+ Operand y = {Addressing_Invalid};
+ check_expr_or_type(c, &x, te->x, type_hint);
+ node->viral_state_flags |= te->x->viral_state_flags;
- check_procedure_type(&ctx, type, pl->type);
- if (!is_type_proc(type)) {
- gbString str = expr_to_string(node);
- error(node, "Invalid procedure literal '%s'", str);
- gb_string_free(str);
- check_close_scope(&ctx);
- return kind;
- }
+ if (te->y != nullptr) {
+ check_expr_or_type(c, &y, te->y, type_hint);
+ node->viral_state_flags |= te->y->viral_state_flags;
+ } else {
+ error(node, "A ternary expression must have an else clause");
+ return kind;
+ }
- if (pl->body == nullptr) {
- error(node, "A procedure literal must have a body");
- return kind;
- }
+ if (x.type == nullptr || x.type == t_invalid ||
+ y.type == nullptr || y.type == t_invalid) {
+ return kind;
+ }
- pl->decl = decl;
- check_procedure_later(&ctx, ctx.file, empty_token, decl, type, pl->body, pl->tags);
- }
- check_close_scope(&ctx);
+ convert_to_typed(c, &x, y.type);
+ if (x.mode == Addressing_Invalid) {
+ return kind;
+ }
+ convert_to_typed(c, &y, x.type);
+ if (y.mode == Addressing_Invalid) {
+ x.mode = Addressing_Invalid;
+ return kind;
+ }
- o->mode = Addressing_Value;
- o->type = type;
- case_end;
+ if (!ternary_compare_types(x.type, y.type)) {
+ gbString its = type_to_string(x.type);
+ gbString ets = type_to_string(y.type);
+ error(node, "Mismatched types in ternary if expression, %s vs %s", its, ets);
+ gb_string_free(ets);
+ gb_string_free(its);
+ return kind;
+ }
- case_ast_node(te, TernaryIfExpr, node);
- Operand cond = {Addressing_Invalid};
- check_expr(c, &cond, te->cond);
- node->viral_state_flags |= te->cond->viral_state_flags;
+ o->type = x.type;
+ if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) {
+ o->type = y.type;
+ }
- if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) {
- error(te->cond, "Non-boolean condition in ternary if expression");
+ o->mode = Addressing_Value;
+ o->expr = node;
+ if (type_hint != nullptr && is_type_untyped(o->type)) {
+ if (check_cast_internal(c, &x, type_hint) &&
+ check_cast_internal(c, &y, type_hint)) {
+ convert_to_typed(c, o, type_hint);
+ update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint));
}
+ }
+ return kind;
+}
- Operand x = {Addressing_Invalid};
- Operand y = {Addressing_Invalid};
- check_expr_or_type(c, &x, te->x, type_hint);
- node->viral_state_flags |= te->x->viral_state_flags;
+ExprKind check_ternary_when_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ Operand cond = {};
+ ast_node(te, TernaryWhenExpr, node);
+ check_expr(c, &cond, te->cond);
+ node->viral_state_flags |= te->cond->viral_state_flags;
+
+ if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type)) {
+ error(te->cond, "Expected a constant boolean condition in ternary when expression");
+ return kind;
+ }
+ if (cond.value.value_bool) {
+ check_expr_or_type(c, o, te->x, type_hint);
+ node->viral_state_flags |= te->x->viral_state_flags;
+ } else {
if (te->y != nullptr) {
- check_expr_or_type(c, &y, te->y, type_hint);
+ check_expr_or_type(c, o, te->y, type_hint);
node->viral_state_flags |= te->y->viral_state_flags;
} else {
- error(node, "A ternary expression must have an else clause");
- return kind;
- }
-
- if (x.type == nullptr || x.type == t_invalid ||
- y.type == nullptr || y.type == t_invalid) {
- return kind;
- }
-
- convert_to_typed(c, &x, y.type);
- if (x.mode == Addressing_Invalid) {
- return kind;
- }
- convert_to_typed(c, &y, x.type);
- if (y.mode == Addressing_Invalid) {
- x.mode = Addressing_Invalid;
+ error(node, "A ternary when expression must have an else clause");
return kind;
}
+ }
+ return kind;
+}
- if (!ternary_compare_types(x.type, y.type)) {
- gbString its = type_to_string(x.type);
- gbString ets = type_to_string(y.type);
- error(node, "Mismatched types in ternary if expression, %s vs %s", its, ets);
- gb_string_free(ets);
- gb_string_free(its);
- return kind;
- }
+ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(oe, OrElseExpr, node);
- o->type = x.type;
- if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) {
- o->type = y.type;
- }
+ String name = oe->token.string;
+ Ast *arg = oe->x;
+ Ast *default_value = oe->y;
+ Operand x = {};
+ Operand y = {};
+ check_multi_expr_with_type_hint(c, &x, arg, type_hint);
+ if (x.mode == Addressing_Invalid) {
o->mode = Addressing_Value;
+ o->type = t_invalid;
o->expr = node;
- if (type_hint != nullptr && is_type_untyped(o->type)) {
- if (check_cast_internal(c, &x, type_hint) &&
- check_cast_internal(c, &y, type_hint)) {
- convert_to_typed(c, o, type_hint);
- update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint));
- }
- }
- case_end;
-
- case_ast_node(te, TernaryWhenExpr, node);
- Operand cond = {};
- check_expr(c, &cond, te->cond);
- node->viral_state_flags |= te->cond->viral_state_flags;
-
- if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type)) {
- error(te->cond, "Expected a constant boolean condition in ternary when expression");
- return kind;
- }
-
- if (cond.value.value_bool) {
- check_expr_or_type(c, o, te->x, type_hint);
- node->viral_state_flags |= te->x->viral_state_flags;
- } else {
- if (te->y != nullptr) {
- check_expr_or_type(c, o, te->y, type_hint);
- node->viral_state_flags |= te->y->viral_state_flags;
- } else {
- error(node, "A ternary when expression must have an else clause");
- return kind;
- }
- }
- case_end;
+ return Expr_Expr;
+ }
- case_ast_node(oe, OrElseExpr, node);
- String name = oe->token.string;
- Ast *arg = oe->x;
- Ast *default_value = oe->y;
+ check_multi_expr_with_type_hint(c, &y, default_value, x.type);
+ error_operand_no_value(&y);
+ if (y.mode == Addressing_Invalid) {
+ o->mode = Addressing_Value;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Expr;
+ }
- Operand x = {};
- Operand y = {};
- check_multi_expr_with_type_hint(c, &x, arg, type_hint);
- if (x.mode == Addressing_Invalid) {
- o->mode = Addressing_Value;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Expr;
- }
+ Type *left_type = nullptr;
+ Type *right_type = nullptr;
+ check_or_else_split_types(c, &x, name, &left_type, &right_type);
+ add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value);
- check_multi_expr_with_type_hint(c, &y, default_value, x.type);
- error_operand_no_value(&y);
- if (y.mode == Addressing_Invalid) {
- o->mode = Addressing_Value;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Expr;
- }
+ if (left_type != nullptr) {
+ check_assignment(c, &y, left_type, name);
+ } else {
+ check_or_else_expr_no_value_error(c, name, x, type_hint);
+ }
- Type *left_type = nullptr;
- Type *right_type = nullptr;
- check_or_else_split_types(c, &x, name, &left_type, &right_type);
- add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value);
+ if (left_type == nullptr) {
+ left_type = t_invalid;
+ }
+ o->mode = Addressing_Value;
+ o->type = left_type;
+ o->expr = node;
+ return Expr_Expr;
+}
- if (left_type != nullptr) {
- check_assignment(c, &y, left_type, name);
- } else {
- check_or_else_expr_no_value_error(c, name, x, type_hint);
- }
+ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(re, OrReturnExpr, node);
- if (left_type == nullptr) {
- left_type = t_invalid;
- }
+ String name = re->token.string;
+ Operand x = {};
+ check_multi_expr_with_type_hint(c, &x, re->expr, type_hint);
+ if (x.mode == Addressing_Invalid) {
o->mode = Addressing_Value;
- o->type = left_type;
+ o->type = t_invalid;
o->expr = node;
return Expr_Expr;
- case_end;
-
- case_ast_node(re, OrReturnExpr, node);
- String name = re->token.string;
- Operand x = {};
- check_multi_expr_with_type_hint(c, &x, re->expr, type_hint);
- if (x.mode == Addressing_Invalid) {
- o->mode = Addressing_Value;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Expr;
- }
+ }
- Type *left_type = nullptr;
- Type *right_type = nullptr;
- check_or_return_split_types(c, &x, name, &left_type, &right_type);
- add_type_and_value(&c->checker->info, re->expr, x.mode, x.type, x.value);
+ Type *left_type = nullptr;
+ Type *right_type = nullptr;
+ check_or_return_split_types(c, &x, name, &left_type, &right_type);
+ add_type_and_value(&c->checker->info, re->expr, x.mode, x.type, x.value);
- if (right_type == nullptr) {
- check_or_else_expr_no_value_error(c, name, x, type_hint);
+ if (right_type == nullptr) {
+ check_or_else_expr_no_value_error(c, name, x, type_hint);
+ } else {
+ Type *proc_type = base_type(c->curr_proc_sig);
+ GB_ASSERT(proc_type->kind == Type_Proc);
+ Type *result_type = proc_type->Proc.results;
+ if (result_type == nullptr) {
+ error(node, "'%.*s' requires the current procedure to have at least one return value", LIT(name));
} else {
- Type *proc_type = base_type(c->curr_proc_sig);
- GB_ASSERT(proc_type->kind == Type_Proc);
- Type *result_type = proc_type->Proc.results;
- if (result_type == nullptr) {
- error(node, "'%.*s' requires the current procedure to have at least one return value", LIT(name));
- } else {
- GB_ASSERT(result_type->kind == Type_Tuple);
+ GB_ASSERT(result_type->kind == Type_Tuple);
- auto const &vars = result_type->Tuple.variables;
- Type *end_type = vars[vars.count-1]->type;
+ auto const &vars = result_type->Tuple.variables;
+ Type *end_type = vars[vars.count-1]->type;
- if (vars.count > 1) {
- if (!proc_type->Proc.has_named_results) {
- error(node, "'%.*s' within a procedure with more than 1 return value requires that the return values are named, allowing for early return", LIT(name));
- }
+ if (vars.count > 1) {
+ if (!proc_type->Proc.has_named_results) {
+ error(node, "'%.*s' within a procedure with more than 1 return value requires that the return values are named, allowing for early return", LIT(name));
}
+ }
- Operand rhs = {};
- rhs.type = right_type;
- rhs.mode = Addressing_Value;
+ Operand rhs = {};
+ rhs.type = right_type;
+ rhs.mode = Addressing_Value;
- // TODO(bill): better error message
- if (!check_is_assignable_to(c, &rhs, end_type)) {
- gbString a = type_to_string(right_type);
- gbString b = type_to_string(end_type);
- gbString ret_type = type_to_string(result_type);
- error(node, "Cannot assign end value of type '%s' to '%s' in '%.*s'", a, b, LIT(name));
- if (vars.count == 1) {
- error_line("\tProcedure return value type: %s\n", ret_type);
- } else {
- error_line("\tProcedure return value types: (%s)\n", ret_type);
- }
- gb_string_free(ret_type);
- gb_string_free(b);
- gb_string_free(a);
+ // TODO(bill): better error message
+ if (!check_is_assignable_to(c, &rhs, end_type)) {
+ gbString a = type_to_string(right_type);
+ gbString b = type_to_string(end_type);
+ gbString ret_type = type_to_string(result_type);
+ error(node, "Cannot assign end value of type '%s' to '%s' in '%.*s'", a, b, LIT(name));
+ if (vars.count == 1) {
+ error_line("\tProcedure return value type: %s\n", ret_type);
+ } else {
+ error_line("\tProcedure return value types: (%s)\n", ret_type);
}
+ gb_string_free(ret_type);
+ gb_string_free(b);
+ gb_string_free(a);
}
}
+ }
- o->expr = node;
- o->type = left_type;
- if (left_type != nullptr) {
- o->mode = Addressing_Value;
- } else {
- o->mode = Addressing_NoValue;
- }
+ o->expr = node;
+ o->type = left_type;
+ if (left_type != nullptr) {
+ o->mode = Addressing_Value;
+ } else {
+ o->mode = Addressing_NoValue;
+ }
- if (c->curr_proc_sig == nullptr) {
- error(node, "'%.*s' can only be used within a procedure", LIT(name));
- }
-
- if (c->in_defer) {
- error(node, "'or_return' cannot be used within a defer statement");
- }
+ if (c->curr_proc_sig == nullptr) {
+ error(node, "'%.*s' can only be used within a procedure", LIT(name));
+ }
- return Expr_Expr;
- case_end;
+ if (c->in_defer) {
+ error(node, "'or_return' cannot be used within a defer statement");
+ }
- case_ast_node(cl, CompoundLit, node);
- Type *type = type_hint;
- if (type != nullptr && is_type_untyped(type)) {
- type = nullptr;
- }
- bool is_to_be_determined_array_count = false;
- bool is_constant = true;
- if (cl->type != nullptr) {
- type = nullptr;
-
- // [?]Type
- if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) {
- Ast *count = cl->type->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);
- is_to_be_determined_array_count = true;
- }
- if (cl->elems.count > 0) {
- if (cl->type->ArrayType.tag != nullptr) {
- Ast *tag = cl->type->ArrayType.tag;
- GB_ASSERT(tag->kind == Ast_BasicDirective);
- String name = tag->BasicDirective.name.string;
- if (name == "soa") {
- error(node, "#soa arrays are not supported for compound literals");
- return kind;
- }
- }
- }
- }
- if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) {
- if (cl->elems.count > 0) {
- Ast *tag = cl->type->DynamicArrayType.tag;
+ return Expr_Expr;
+}
+
+ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ ast_node(cl, CompoundLit, node);
+
+ Type *type = type_hint;
+ if (type != nullptr && is_type_untyped(type)) {
+ type = nullptr;
+ }
+ bool is_to_be_determined_array_count = false;
+ bool is_constant = true;
+ if (cl->type != nullptr) {
+ type = nullptr;
+
+ // [?]Type
+ if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) {
+ Ast *count = cl->type->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);
+ is_to_be_determined_array_count = true;
+ }
+ if (cl->elems.count > 0) {
+ if (cl->type->ArrayType.tag != nullptr) {
+ Ast *tag = cl->type->ArrayType.tag;
GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name.string;
if (name == "soa") {
@@ -7274,464 +7473,268 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
}
}
}
-
- if (type == nullptr) {
- type = check_type(c, cl->type);
+ }
+ if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) {
+ if (cl->elems.count > 0) {
+ Ast *tag = cl->type->DynamicArrayType.tag;
+ GB_ASSERT(tag->kind == Ast_BasicDirective);
+ String name = tag->BasicDirective.name.string;
+ if (name == "soa") {
+ error(node, "#soa arrays are not supported for compound literals");
+ return kind;
+ }
}
}
if (type == nullptr) {
- error(node, "Missing type in compound literal");
- return kind;
+ type = check_type(c, cl->type);
}
+ }
+ if (type == nullptr) {
+ error(node, "Missing type in compound literal");
+ return kind;
+ }
- Type *t = base_type(type);
- if (is_type_polymorphic(t)) {
- gbString str = type_to_string(type);
- error(node, "Cannot use a polymorphic type for a compound literal, got '%s'", str);
- o->expr = node;
- o->type = type;
- gb_string_free(str);
- return kind;
- }
+ Type *t = base_type(type);
+ if (is_type_polymorphic(t)) {
+ gbString str = type_to_string(type);
+ error(node, "Cannot use a polymorphic type for a compound literal, got '%s'", str);
+ o->expr = node;
+ o->type = type;
+ gb_string_free(str);
+ return kind;
+ }
- switch (t->kind) {
- case Type_Struct: {
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
- }
- if (t->Struct.is_raw_union) {
- if (cl->elems.count > 0) {
- // NOTE: unions cannot be constant
- is_constant = false;
- if (cl->elems[0]->kind != Ast_FieldValue) {
+ switch (t->kind) {
+ case Type_Struct: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+ if (t->Struct.is_raw_union) {
+ if (cl->elems.count > 0) {
+ // NOTE: unions cannot be constant
+ is_constant = false;
+
+ if (cl->elems[0]->kind != Ast_FieldValue) {
+ gbString type_str = type_to_string(type);
+ error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str);
+ gb_string_free(type_str);
+ } else {
+ if (cl->elems.count != 1) {
gbString type_str = type_to_string(type);
- error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str);
+ error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
gb_string_free(type_str);
} else {
- if (cl->elems.count != 1) {
- gbString type_str = type_to_string(type);
- error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
- gb_string_free(type_str);
- } else {
- Ast *elem = cl->elems[0];
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in structure literal", expr_str);
- gb_string_free(expr_str);
- break;
- }
-
- String name = fv->field->Ident.token.string;
-
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- bool is_unknown = sel.entity == nullptr;
- if (is_unknown) {
- error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
- break;
- }
-
- if (sel.index.count > 1) {
- error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
- break;
- }
-
- Entity *field = t->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
-
- Operand o = {};
- check_expr_or_type(c, &o, fv->value, field->type);
-
-
- check_assignment(c, &o, field->type, str_lit("structure literal"));
+ Ast *elem = cl->elems[0];
+ ast_node(fv, FieldValue, elem);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(elem, "Invalid field name '%s' in structure literal", expr_str);
+ gb_string_free(expr_str);
+ break;
}
- }
- }
- break;
- }
-
-
- 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--) {
- Entity *e = t->Struct.fields[i];
- GB_ASSERT(e->kind == Entity_Variable);
- if (e->Variable.param_value.kind != ParameterValue_Invalid) {
- min_field_count--;
- } else {
- break;
- }
- }
-
- if (cl->elems[0]->kind == Ast_FieldValue) {
- bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count);
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- 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);
- error(elem, "Invalid field name '%s' in structure literal", expr_str);
- gb_string_free(expr_str);
- continue;
- }
- String name = fv->field->Ident.token.string;
-
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- bool is_unknown = sel.entity == nullptr;
- if (is_unknown) {
- error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
- continue;
- }
-
- if (sel.index.count > 1) {
- error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
- continue;
- }
-
- Entity *field = t->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
-
- if (fields_visited[sel.index[0]]) {
- error(elem, "Duplicate field '%.*s' in structure literal", LIT(name));
- continue;
- }
-
- fields_visited[sel.index[0]] = true;
-
- Operand o = {};
- check_expr_or_type(c, &o, fv->value, field->type);
+ String name = fv->field->Ident.token.string;
- if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
- is_constant = false;
- }
- if (is_constant) {
- is_constant = check_is_operand_compound_lit_constant(c, &o);
- }
+ Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
+ bool is_unknown = sel.entity == nullptr;
+ if (is_unknown) {
+ error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
+ break;
+ }
- check_assignment(c, &o, field->type, str_lit("structure literal"));
- }
- } else {
- bool seen_field_value = false;
+ if (sel.index.count > 1) {
+ error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
+ break;
+ }
- for_array(index, cl->elems) {
- Entity *field = nullptr;
- Ast *elem = cl->elems[index];
- if (elem->kind == Ast_FieldValue) {
- seen_field_value = true;
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- } else if (seen_field_value) {
- error(elem, "Value elements cannot be used after a 'field = value'");
- continue;
- }
- if (index >= field_count) {
- error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count);
- break;
- }
+ Entity *field = t->Struct.fields[sel.index[0]];
+ add_entity_use(c, fv->field, field);
- if (field == nullptr) {
- field = t->Struct.fields[index];
- }
+ Operand o = {};
+ check_expr_or_type(c, &o, fv->value, field->type);
- Operand o = {};
- check_expr_or_type(c, &o, elem, field->type);
- if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
- is_constant = false;
- }
- if (is_constant) {
- is_constant = check_is_operand_compound_lit_constant(c, &o);
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
}
- check_assignment(c, &o, field->type, str_lit("structure literal"));
- }
- if (cl->elems.count < field_count) {
- if (min_field_count < field_count) {
- if (cl->elems.count < min_field_count) {
- error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count);
- }
- } else {
- error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count);
- }
}
}
-
break;
}
- case Type_Slice:
- case Type_Array:
- case Type_DynamicArray:
- case Type_SimdVector:
- case Type_Matrix:
- {
- Type *elem_type = nullptr;
- String context_name = {};
- i64 max_type_count = -1;
- if (t->kind == Type_Slice) {
- elem_type = t->Slice.elem;
- context_name = str_lit("slice literal");
- } else if (t->kind == Type_Array) {
- elem_type = t->Array.elem;
- context_name = str_lit("array literal");
- if (!is_to_be_determined_array_count) {
- max_type_count = t->Array.count;
- }
- } else if (t->kind == Type_DynamicArray) {
- elem_type = t->DynamicArray.elem;
- context_name = str_lit("dynamic array literal");
- is_constant = false;
- if (!build_context.no_dynamic_literals) {
- add_package_dependency(c, "runtime", "__dynamic_array_reserve");
- add_package_dependency(c, "runtime", "__dynamic_array_append");
- }
- } else if (t->kind == Type_SimdVector) {
- elem_type = t->SimdVector.elem;
- context_name = str_lit("simd vector literal");
- max_type_count = t->SimdVector.count;
- } else if (t->kind == Type_Matrix) {
- elem_type = t->Matrix.elem;
- context_name = str_lit("matrix literal");
- max_type_count = t->Matrix.row_count*t->Matrix.column_count;
+ 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--) {
+ Entity *e = t->Struct.fields[i];
+ GB_ASSERT(e->kind == Entity_Variable);
+ if (e->Variable.param_value.kind != ParameterValue_Invalid) {
+ min_field_count--;
} else {
- GB_PANIC("unreachable");
- }
-
-
- i64 max = 0;
-
- Type *bet = base_type(elem_type);
- if (!elem_type_can_be_constant(bet)) {
- is_constant = false;
- }
-
- if (bet == t_invalid) {
break;
}
+ }
- if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
- if (is_type_simd_vector(t)) {
- error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals");
- } else {
- RangeCache rc = range_cache_make(heap_allocator());
- defer (range_cache_destroy(&rc));
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- 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 (is_ast_range(fv->field)) {
- Token op = fv->field->BinaryExpr.op;
-
- Operand x = {};
- Operand y = {};
- bool ok = check_range(c, fv->field, &x, &y, nullptr);
- if (!ok) {
- continue;
- }
- if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) {
- error(x.expr, "Expected a constant integer as an array field");
- continue;
- }
-
- if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) {
- error(y.expr, "Expected a constant integer as an array field");
- continue;
- }
-
- i64 lo = exact_value_to_i64(x.value);
- i64 hi = exact_value_to_i64(y.value);
- i64 max_index = hi;
- if (op.kind == Token_RangeHalf) { // ..< (exclusive)
- hi -= 1;
- } else { // .. (inclusive)
- max_index += 1;
- }
-
- bool new_range = range_cache_add_range(&rc, lo, hi);
- if (!new_range) {
- error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name));
- continue;
- }
-
-
- if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) {
- error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name));
- continue;
- }
- if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) {
- error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name));
- continue;
- }
-
- if (max < hi) {
- max = max_index;
- }
-
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, fv->value, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
-
- is_constant = is_constant && operand.mode == Addressing_Constant;
- } else {
- Operand op_index = {};
- check_expr(c, &op_index, fv->field);
-
- if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) {
- error(elem, "Expected a constant integer as an array field");
- continue;
- }
- // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value);
-
- i64 index = exact_value_to_i64(op_index.value);
-
- if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) {
- error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name));
- continue;
- }
-
- bool new_index = range_cache_add_index(&rc, index);
- if (!new_index) {
- error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name));
- continue;
- }
-
- if (max < index+1) {
- max = index+1;
- }
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count);
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, fv->value, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ 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);
+ error(elem, "Invalid field name '%s' in structure literal", expr_str);
+ gb_string_free(expr_str);
+ continue;
+ }
+ String name = fv->field->Ident.token.string;
- is_constant = is_constant && operand.mode == Addressing_Constant;
- }
- }
+ Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
+ bool is_unknown = sel.entity == nullptr;
+ if (is_unknown) {
+ error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
+ continue;
+ }
- cl->max_count = max;
+ if (sel.index.count > 1) {
+ error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
+ continue;
}
- } else {
- isize index = 0;
- for (; index < cl->elems.count; index++) {
- Ast *e = cl->elems[index];
- if (e == nullptr) {
- error(node, "Invalid literal element");
- continue;
- }
+ Entity *field = t->Struct.fields[sel.index[0]];
+ add_entity_use(c, fv->field, field);
- if (e->kind == Ast_FieldValue) {
- error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
+ if (fields_visited[sel.index[0]]) {
+ error(elem, "Duplicate field '%.*s' in structure literal", LIT(name));
+ continue;
+ }
- if (0 <= max_type_count && max_type_count <= index) {
- error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
- }
+ fields_visited[sel.index[0]] = true;
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, e, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
+ Operand o = {};
+ check_expr_or_type(c, &o, fv->value, field->type);
- is_constant = is_constant && operand.mode == Addressing_Constant;
+ if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
+ is_constant = false;
}
-
- if (max < index) {
- max = index;
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &o);
}
- }
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
+ }
+ } else {
+ bool seen_field_value = false;
+
+ for_array(index, cl->elems) {
+ Entity *field = nullptr;
+ Ast *elem = cl->elems[index];
+ if (elem->kind == Ast_FieldValue) {
+ seen_field_value = true;
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ } else if (seen_field_value) {
+ error(elem, "Value elements cannot be used after a 'field = value'");
+ continue;
+ }
+ if (index >= field_count) {
+ error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count);
+ break;
+ }
- if (t->kind == Type_Array) {
- if (is_to_be_determined_array_count) {
- t->Array.count = max;
- } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
- if (0 < max && max < t->Array.count) {
- error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max);
- }
+ if (field == nullptr) {
+ field = t->Struct.fields[index];
}
- }
+ Operand o = {};
+ check_expr_or_type(c, &o, elem, field->type);
- if (t->kind == Type_SimdVector) {
- if (!is_constant) {
- error(node, "Expected all constant elements for a simd vector");
+ if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
+ is_constant = false;
+ }
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &o);
}
- }
-
- if (t->kind == Type_DynamicArray) {
- if (build_context.no_dynamic_literals && cl->elems.count) {
- error(node, "Compound literals of dynamic types have been disabled");
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
+ }
+ if (cl->elems.count < field_count) {
+ if (min_field_count < field_count) {
+ if (cl->elems.count < min_field_count) {
+ error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count);
+ }
+ } else {
+ error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count);
}
}
-
- break;
}
- case Type_EnumeratedArray:
- {
- Type *elem_type = t->EnumeratedArray.elem;
- Type *index_type = t->EnumeratedArray.index;
- String context_name = str_lit("enumerated array literal");
- i64 max_type_count = t->EnumeratedArray.count;
+ break;
+ }
- gbString index_type_str = type_to_string(index_type);
- defer (gb_string_free(index_type_str));
+ case Type_Slice:
+ case Type_Array:
+ case Type_DynamicArray:
+ case Type_SimdVector:
+ case Type_Matrix:
+ {
+ Type *elem_type = nullptr;
+ String context_name = {};
+ i64 max_type_count = -1;
+ if (t->kind == Type_Slice) {
+ elem_type = t->Slice.elem;
+ context_name = str_lit("slice literal");
+ } else if (t->kind == Type_Array) {
+ elem_type = t->Array.elem;
+ context_name = str_lit("array literal");
+ if (!is_to_be_determined_array_count) {
+ max_type_count = t->Array.count;
+ }
+ } else if (t->kind == Type_DynamicArray) {
+ elem_type = t->DynamicArray.elem;
+ context_name = str_lit("dynamic array literal");
+ is_constant = false;
- i64 total_lo = exact_value_to_i64(*t->EnumeratedArray.min_value);
- i64 total_hi = exact_value_to_i64(*t->EnumeratedArray.max_value);
+ if (!build_context.no_dynamic_literals) {
+ add_package_dependency(c, "runtime", "__dynamic_array_reserve");
+ add_package_dependency(c, "runtime", "__dynamic_array_append");
+ }
+ } else if (t->kind == Type_SimdVector) {
+ elem_type = t->SimdVector.elem;
+ context_name = str_lit("simd vector literal");
+ max_type_count = t->SimdVector.count;
+ } else if (t->kind == Type_Matrix) {
+ elem_type = t->Matrix.elem;
+ context_name = str_lit("matrix literal");
+ max_type_count = t->Matrix.row_count*t->Matrix.column_count;
+ } else {
+ GB_PANIC("unreachable");
+ }
- String total_lo_string = {};
- String total_hi_string = {};
- GB_ASSERT(is_type_enum(index_type));
- {
- Type *bt = base_type(index_type);
- GB_ASSERT(bt->kind == Type_Enum);
- for_array(i, bt->Enum.fields) {
- Entity *f = bt->Enum.fields[i];
- if (f->kind != Entity_Constant) {
- continue;
- }
- if (total_lo_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.min_value)) {
- total_lo_string = f->token.string;
- }
- if (total_hi_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.max_value)) {
- total_hi_string = f->token.string;
- }
- if (total_lo_string.len != 0 && total_hi_string.len != 0) {
- break;
- }
- }
- }
- i64 max = 0;
+ i64 max = 0;
- Type *bet = base_type(elem_type);
- if (!elem_type_can_be_constant(bet)) {
- is_constant = false;
- }
+ Type *bet = base_type(elem_type);
+ if (!elem_type_can_be_constant(bet)) {
+ is_constant = false;
+ }
- if (bet == t_invalid) {
- break;
- }
+ if (bet == t_invalid) {
+ break;
+ }
- if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
+ if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
+ if (is_type_simd_vector(t)) {
+ error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals");
+ } else {
RangeCache rc = range_cache_make(heap_allocator());
defer (range_cache_destroy(&rc));
@@ -7748,49 +7751,42 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
Operand x = {};
Operand y = {};
- bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type);
+ bool ok = check_range(c, fv->field, &x, &y, nullptr);
if (!ok) {
continue;
}
- if (x.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
- error(x.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) {
+ error(x.expr, "Expected a constant integer as an array field");
continue;
}
- if (y.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
- error(y.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) {
+ error(y.expr, "Expected a constant integer as an array field");
continue;
}
i64 lo = exact_value_to_i64(x.value);
i64 hi = exact_value_to_i64(y.value);
i64 max_index = hi;
- if (op.kind == Token_RangeHalf) {
+ if (op.kind == Token_RangeHalf) { // ..< (exclusive)
hi -= 1;
+ } else { // .. (inclusive)
+ max_index += 1;
}
bool new_range = range_cache_add_range(&rc, lo, hi);
if (!new_range) {
- gbString lo_str = expr_to_string(x.expr);
- gbString hi_str = expr_to_string(y.expr);
- error(elem, "Overlapping field range index %s %.*s %s for %.*s", lo_str, LIT(op.string), hi_str, LIT(context_name));
- gb_string_free(hi_str);
- gb_string_free(lo_str);
+ error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name));
continue;
}
- // NOTE(bill): These are sanity checks for invalid enum values
- if (max_type_count >= 0 && (lo < total_lo || lo > total_hi)) {
- gbString lo_str = expr_to_string(x.expr);
- error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", lo_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
- gb_string_free(lo_str);
+ if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) {
+ error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name));
continue;
}
- if (max_type_count >= 0 && (hi < 0 || hi > total_hi)) {
- gbString hi_str = expr_to_string(y.expr);
- error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", hi_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
- gb_string_free(hi_str);
+ if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) {
+ error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name));
continue;
}
@@ -7805,27 +7801,24 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
is_constant = is_constant && operand.mode == Addressing_Constant;
} else {
Operand op_index = {};
- check_expr_with_type_hint(c, &op_index, fv->field, index_type);
+ check_expr(c, &op_index, fv->field);
- if (op_index.mode != Addressing_Constant || !are_types_identical(op_index.type, index_type)) {
- error(op_index.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) {
+ error(elem, "Expected a constant integer as an array field");
continue;
}
+ // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value);
i64 index = exact_value_to_i64(op_index.value);
- if (max_type_count >= 0 && (index < total_lo || index > total_hi)) {
- gbString idx_str = expr_to_string(op_index.expr);
- error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", idx_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
- gb_string_free(idx_str);
+ if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) {
+ error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name));
continue;
}
bool new_index = range_cache_add_index(&rc, index);
if (!new_index) {
- gbString idx_str = expr_to_string(op_index.expr);
- error(elem, "Duplicate field index %s for %.*s", idx_str, LIT(context_name));
- gb_string_free(idx_str);
+ error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name));
continue;
}
@@ -7842,951 +7835,1433 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
}
cl->max_count = max;
+ }
- } else {
- isize index = 0;
- for (; index < cl->elems.count; index++) {
- Ast *e = cl->elems[index];
- if (e == nullptr) {
- error(node, "Invalid literal element");
- continue;
- }
+ } else {
+ isize index = 0;
+ for (; index < cl->elems.count; index++) {
+ Ast *e = cl->elems[index];
+ if (e == nullptr) {
+ error(node, "Invalid literal element");
+ continue;
+ }
- if (e->kind == Ast_FieldValue) {
- error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
+ if (e->kind == Ast_FieldValue) {
+ error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
- if (0 <= max_type_count && max_type_count <= index) {
- error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
- }
+ if (0 <= max_type_count && max_type_count <= index) {
+ error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
+ }
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, e, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, e, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
- is_constant = is_constant && operand.mode == Addressing_Constant;
- }
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+ }
+
+ if (max < index) {
+ max = index;
+ }
+ }
- if (max < index) {
- max = index;
+
+ if (t->kind == Type_Array) {
+ if (is_to_be_determined_array_count) {
+ t->Array.count = max;
+ } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
+ if (0 < max && max < t->Array.count) {
+ error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max);
}
}
+ }
+
+ if (t->kind == Type_SimdVector) {
+ if (!is_constant) {
+ error(node, "Expected all constant elements for a simd vector");
+ }
+ }
+
+
+ if (t->kind == Type_DynamicArray) {
+ if (build_context.no_dynamic_literals && cl->elems.count) {
+ error(node, "Compound literals of dynamic types have been disabled");
+ }
+ }
+
+ if (t->kind == Type_Matrix) {
if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
- if (0 < max && max < t->EnumeratedArray.count) {
- error(node, "Expected %lld values for this enumerated array literal, got %lld", cast(long long)t->EnumeratedArray.count, cast(long long)max);
- } else {
- error(node, "Enumerated array literals must only have 'field = value' elements, bare elements are not allowed");
+ if (0 < max && max < max_type_count) {
+ error(node, "Expected %lld values for this matrix literal, got %lld", cast(long long)max_type_count, cast(long long)max);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case Type_EnumeratedArray:
+ {
+ Type *elem_type = t->EnumeratedArray.elem;
+ Type *index_type = t->EnumeratedArray.index;
+ String context_name = str_lit("enumerated array literal");
+ i64 max_type_count = t->EnumeratedArray.count;
+
+ gbString index_type_str = type_to_string(index_type);
+ defer (gb_string_free(index_type_str));
+
+ i64 total_lo = exact_value_to_i64(*t->EnumeratedArray.min_value);
+ i64 total_hi = exact_value_to_i64(*t->EnumeratedArray.max_value);
+
+ String total_lo_string = {};
+ String total_hi_string = {};
+ GB_ASSERT(is_type_enum(index_type));
+ {
+ Type *bt = base_type(index_type);
+ GB_ASSERT(bt->kind == Type_Enum);
+ for_array(i, bt->Enum.fields) {
+ Entity *f = bt->Enum.fields[i];
+ if (f->kind != Entity_Constant) {
+ continue;
+ }
+ if (total_lo_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.min_value)) {
+ total_lo_string = f->token.string;
+ }
+ if (total_hi_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.max_value)) {
+ total_hi_string = f->token.string;
+ }
+ if (total_lo_string.len != 0 && total_hi_string.len != 0) {
+ break;
}
}
+ }
+
+ i64 max = 0;
+ Type *bet = base_type(elem_type);
+ if (!elem_type_can_be_constant(bet)) {
+ is_constant = false;
+ }
+
+ if (bet == t_invalid) {
break;
}
+ bool is_partial = cl->tag && (cl->tag->BasicDirective.name.string == "partial");
+
+ SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue
+ map_init(&seen, heap_allocator());
+ defer (map_destroy(&seen));
+
+ if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
+ RangeCache rc = range_cache_make(heap_allocator());
+ defer (range_cache_destroy(&rc));
- case Type_Basic: {
- if (!is_type_any(t)) {
- if (cl->elems.count != 0) {
- error(node, "Illegal compound literal");
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
}
- break;
- }
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
- }
- { // Checker values
- Type *field_types[2] = {t_rawptr, t_typeid};
- isize field_count = 2;
- if (cl->elems[0]->kind == Ast_FieldValue) {
- bool fields_visited[2] = {};
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
- continue;
- }
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in 'any' literal", expr_str);
- gb_string_free(expr_str);
- continue;
- }
- String name = fv->field->Ident.token.string;
+ ast_node(fv, FieldValue, elem);
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- if (sel.entity == nullptr) {
- error(elem, "Unknown field '%.*s' in 'any' literal", LIT(name));
- continue;
- }
+ if (is_ast_range(fv->field)) {
+ Token op = fv->field->BinaryExpr.op;
- isize index = sel.index[0];
+ Operand x = {};
+ Operand y = {};
+ bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type);
+ if (!ok) {
+ continue;
+ }
+ if (x.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
+ error(x.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ continue;
+ }
- if (fields_visited[index]) {
- error(elem, "Duplicate field '%.*s' in 'any' literal", LIT(name));
- continue;
- }
+ if (y.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
+ error(y.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ continue;
+ }
- fields_visited[index] = true;
- check_expr(c, o, fv->value);
+ i64 lo = exact_value_to_i64(x.value);
+ i64 hi = exact_value_to_i64(y.value);
+ i64 max_index = hi;
+ if (op.kind == Token_RangeHalf) {
+ hi -= 1;
+ }
+
+ bool new_range = range_cache_add_range(&rc, lo, hi);
+ if (!new_range) {
+ gbString lo_str = expr_to_string(x.expr);
+ gbString hi_str = expr_to_string(y.expr);
+ error(elem, "Overlapping field range index %s %.*s %s for %.*s", lo_str, LIT(op.string), hi_str, LIT(context_name));
+ gb_string_free(hi_str);
+ gb_string_free(lo_str);
+ continue;
+ }
- // NOTE(bill): 'any' literals can never be constant
- is_constant = false;
- check_assignment(c, o, field_types[index], str_lit("'any' literal"));
+ // NOTE(bill): These are sanity checks for invalid enum values
+ if (max_type_count >= 0 && (lo < total_lo || lo > total_hi)) {
+ gbString lo_str = expr_to_string(x.expr);
+ error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", lo_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
+ gb_string_free(lo_str);
+ continue;
}
+ if (max_type_count >= 0 && (hi < 0 || hi > total_hi)) {
+ gbString hi_str = expr_to_string(y.expr);
+ error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", hi_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
+ gb_string_free(hi_str);
+ continue;
+ }
+
+ if (max < hi) {
+ max = max_index;
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, fv->value, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+
+ TokenKind upper_op = Token_LtEq;
+ if (op.kind == Token_RangeHalf) {
+ upper_op = Token_Lt;
+ }
+ add_to_seen_map(c, &seen, upper_op, x, x, y);
} else {
- for_array(index, cl->elems) {
- Ast *elem = cl->elems[index];
- if (elem->kind == Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
- continue;
- }
+ Operand op_index = {};
+ check_expr_with_type_hint(c, &op_index, fv->field, index_type);
+ if (op_index.mode != Addressing_Constant || !are_types_identical(op_index.type, index_type)) {
+ error(op_index.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ continue;
+ }
- check_expr(c, o, elem);
- if (index >= field_count) {
- error(o->expr, "Too many values in 'any' literal, expected %td", field_count);
- break;
- }
+ i64 index = exact_value_to_i64(op_index.value);
- // NOTE(bill): 'any' literals can never be constant
- is_constant = false;
+ if (max_type_count >= 0 && (index < total_lo || index > total_hi)) {
+ gbString idx_str = expr_to_string(op_index.expr);
+ error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", idx_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
+ gb_string_free(idx_str);
+ continue;
+ }
- check_assignment(c, o, field_types[index], str_lit("'any' literal"));
+ bool new_index = range_cache_add_index(&rc, index);
+ if (!new_index) {
+ gbString idx_str = expr_to_string(op_index.expr);
+ error(elem, "Duplicate field index %s for %.*s", idx_str, LIT(context_name));
+ gb_string_free(idx_str);
+ continue;
}
- if (cl->elems.count < field_count) {
- error(cl->close, "Too few values in 'any' literal, expected %td, got %td", field_count, cl->elems.count);
+
+ if (max < index+1) {
+ max = index+1;
}
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, fv->value, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+
+ add_to_seen_map(c, &seen, op_index);
}
}
- break;
+ cl->max_count = max;
+
+ } else {
+ isize index = 0;
+ for (; index < cl->elems.count; index++) {
+ Ast *e = cl->elems[index];
+ if (e == nullptr) {
+ error(node, "Invalid literal element");
+ continue;
+ }
+
+ if (e->kind == Ast_FieldValue) {
+ error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+
+ if (0 <= max_type_count && max_type_count <= index) {
+ error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, e, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+ }
+
+ if (max < index) {
+ max = index;
+ }
}
- case Type_Map: {
- if (cl->elems.count == 0) {
- break;
+ bool was_error = false;
+ if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
+ if (0 < max && max < t->EnumeratedArray.count) {
+ error(node, "Expected %lld values for this enumerated array literal, got %lld", cast(long long)t->EnumeratedArray.count, cast(long long)max);
+ was_error = true;
+ } else {
+ error(node, "Enumerated array literals must only have 'field = value' elements, bare elements are not allowed");
+ was_error = true;
+ }
+ }
+
+ // NOTE(bill): Check for missing cases when `#partial literal` is not present
+ if (cl->elems.count > 0 && !was_error && !is_partial) {
+ Type *et = base_type(index_type);
+ GB_ASSERT(et->kind == Type_Enum);
+ auto fields = et->Enum.fields;
+
+ auto unhandled = array_make<Entity *>(temporary_allocator(), 0, fields.count);
+
+ for_array(i, fields) {
+ Entity *f = fields[i];
+ if (f->kind != Entity_Constant) {
+ continue;
+ }
+ ExactValue v = f->Constant.value;
+ auto found = map_get(&seen, hash_exact_value(v));
+ if (!found) {
+ array_add(&unhandled, f);
+ }
}
- is_constant = false;
- { // Checker values
- bool key_is_typeid = is_type_typeid(t->Map.key);
- bool value_is_typeid = is_type_typeid(t->Map.value);
+
+ if (unhandled.count > 0) {
+ begin_error_block();
+ defer (end_error_block());
+
+ if (unhandled.count == 1) {
+ error_no_newline(node, "Unhandled enumerated array case: %.*s", LIT(unhandled[0]->token.string));
+ } else {
+ error(node, "Unhandled enumerated array cases:");
+ for_array(i, unhandled) {
+ Entity *f = unhandled[i];
+ error_line("\t%.*s\n", LIT(f->token.string));
+ }
+ }
+ error_line("\n");
+
+ error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type));
+ }
+ }
+
+ break;
+ }
+
+ case Type_Basic: {
+ if (!is_type_any(t)) {
+ if (cl->elems.count != 0) {
+ gbString s = type_to_string(t);
+ error(node, "Illegal compound literal, %s cannot be used as a compound literal with fields", s);
+ gb_string_free(s);
+ is_constant = false;
+ }
+ break;
+ }
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+ { // Checker values
+ Type *field_types[2] = {t_rawptr, t_typeid};
+ isize field_count = 2;
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ bool fields_visited[2] = {};
for_array(i, cl->elems) {
Ast *elem = cl->elems[i];
if (elem->kind != Ast_FieldValue) {
- error(elem, "Only 'field = value' elements are allowed in a map literal");
+ error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
continue;
}
ast_node(fv, FieldValue, elem);
-
- if (key_is_typeid) {
- check_expr_or_type(c, o, fv->field, t->Map.key);
- } else {
- check_expr_with_type_hint(c, o, fv->field, t->Map.key);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(elem, "Invalid field name '%s' in 'any' literal", expr_str);
+ gb_string_free(expr_str);
+ continue;
}
- check_assignment(c, o, t->Map.key, str_lit("map literal"));
- if (o->mode == Addressing_Invalid) {
+ String name = fv->field->Ident.token.string;
+
+ Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
+ if (sel.entity == nullptr) {
+ error(elem, "Unknown field '%.*s' in 'any' literal", LIT(name));
continue;
}
- if (value_is_typeid) {
- check_expr_or_type(c, o, fv->value, t->Map.value);
- } else {
- check_expr_with_type_hint(c, o, fv->value, t->Map.value);
+ isize index = sel.index[0];
+
+ if (fields_visited[index]) {
+ error(elem, "Duplicate field '%.*s' in 'any' literal", LIT(name));
+ continue;
}
- check_assignment(c, o, t->Map.value, str_lit("map literal"));
- }
- }
- if (build_context.no_dynamic_literals && cl->elems.count) {
- error(node, "Compound literals of dynamic types have been disabled");
- } else {
- add_package_dependency(c, "runtime", "__dynamic_map_reserve");
- add_package_dependency(c, "runtime", "__dynamic_map_set");
- }
- break;
- }
+ fields_visited[index] = true;
+ check_expr(c, o, fv->value);
- case Type_BitSet: {
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
- }
- Type *et = base_type(t->BitSet.elem);
- isize field_count = 0;
- if (et->kind == Type_Enum) {
- field_count = et->Enum.fields.count;
- }
+ // NOTE(bill): 'any' literals can never be constant
+ is_constant = false;
- if (cl->elems[0]->kind == Ast_FieldValue) {
- error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed");
- is_constant = false;
+ check_assignment(c, o, field_types[index], str_lit("'any' literal"));
+ }
} else {
for_array(index, cl->elems) {
Ast *elem = cl->elems[index];
if (elem->kind == Ast_FieldValue) {
- error(elem, "'field = value' in a bit_set a literal is not allowed");
+ error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
continue;
}
- check_expr_with_type_hint(c, o, elem, et);
- if (is_constant) {
- is_constant = o->mode == Addressing_Constant;
+ check_expr(c, o, elem);
+ if (index >= field_count) {
+ error(o->expr, "Too many values in 'any' literal, expected %td", field_count);
+ break;
}
- check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
- if (o->mode == Addressing_Constant) {
- i64 lower = t->BitSet.lower;
- i64 upper = t->BitSet.upper;
- i64 v = exact_value_to_i64(o->value);
- if (lower <= v && v <= upper) {
- // okay
- } else {
- error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper);
- continue;
- }
- }
+ // NOTE(bill): 'any' literals can never be constant
+ is_constant = false;
+
+ check_assignment(c, o, field_types[index], str_lit("'any' literal"));
+ }
+ if (cl->elems.count < field_count) {
+ error(cl->close, "Too few values in 'any' literal, expected %td, got %td", field_count, cl->elems.count);
}
}
+ }
+
+ break;
+ }
+
+ case Type_Map: {
+ if (cl->elems.count == 0) {
break;
}
+ is_constant = false;
+ { // Checker values
+ bool key_is_typeid = is_type_typeid(t->Map.key);
+ bool value_is_typeid = is_type_typeid(t->Map.value);
- default: {
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Only 'field = value' elements are allowed in a map literal");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+
+ if (key_is_typeid) {
+ check_expr_or_type(c, o, fv->field, t->Map.key);
+ } else {
+ check_expr_with_type_hint(c, o, fv->field, t->Map.key);
+ }
+ check_assignment(c, o, t->Map.key, str_lit("map literal"));
+ if (o->mode == Addressing_Invalid) {
+ continue;
+ }
+
+ if (value_is_typeid) {
+ check_expr_or_type(c, o, fv->value, t->Map.value);
+ } else {
+ check_expr_with_type_hint(c, o, fv->value, t->Map.value);
+ }
+ check_assignment(c, o, t->Map.value, str_lit("map literal"));
}
+ }
- gbString str = type_to_string(type);
- error(node, "Invalid compound literal type '%s'", str);
- gb_string_free(str);
- return kind;
+ if (build_context.no_dynamic_literals && cl->elems.count) {
+ error(node, "Compound literals of dynamic types have been disabled");
+ } else {
+ add_package_dependency(c, "runtime", "__dynamic_map_reserve");
+ add_package_dependency(c, "runtime", "__dynamic_map_set");
}
+ break;
+ }
+
+ case Type_BitSet: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+ Type *et = base_type(t->BitSet.elem);
+ isize field_count = 0;
+ if (et->kind == Type_Enum) {
+ field_count = et->Enum.fields.count;
}
- if (is_constant) {
- o->mode = Addressing_Constant;
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed");
+ is_constant = false;
+ } else {
+ for_array(index, cl->elems) {
+ Ast *elem = cl->elems[index];
+ if (elem->kind == Ast_FieldValue) {
+ error(elem, "'field = value' in a bit_set a literal is not allowed");
+ continue;
+ }
- if (is_type_bit_set(type)) {
- // NOTE(bill): Encode as an integer
+ check_expr_with_type_hint(c, o, elem, et);
- i64 lower = base_type(type)->BitSet.lower;
+ if (is_constant) {
+ is_constant = o->mode == Addressing_Constant;
+ }
- u64 bits = 0;
- for_array(index, cl->elems) {
- Ast *elem = cl->elems[index];
- GB_ASSERT(elem->kind != Ast_FieldValue);
- TypeAndValue tav = elem->tav;
- ExactValue i = exact_value_to_integer(tav.value);
- if (i.kind != ExactValue_Integer) {
+ check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
+ if (o->mode == Addressing_Constant) {
+ i64 lower = t->BitSet.lower;
+ i64 upper = t->BitSet.upper;
+ i64 v = exact_value_to_i64(o->value);
+ if (lower <= v && v <= upper) {
+ // okay
+ } else {
+ error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper);
continue;
}
- i64 val = big_int_to_i64(&i.value_integer);
- val -= lower;
- u64 bit = u64(1ll<<val);
- bits |= bit;
- }
- o->value = exact_value_u64(bits);
- } else if (is_type_constant_type(type) && cl->elems.count == 0) {
- ExactValue value = exact_value_compound(node);
- Type *bt = core_type(type);
- if (bt->kind == Type_Basic) {
- if (bt->Basic.flags & BasicFlag_Boolean) {
- value = exact_value_bool(false);
- } else if (bt->Basic.flags & BasicFlag_Integer) {
- value = exact_value_i64(0);
- } else if (bt->Basic.flags & BasicFlag_Unsigned) {
- value = exact_value_i64(0);
- } else if (bt->Basic.flags & BasicFlag_Float) {
- value = exact_value_float(0);
- } else if (bt->Basic.flags & BasicFlag_Complex) {
- value = exact_value_complex(0, 0);
- } else if (bt->Basic.flags & BasicFlag_Quaternion) {
- value = exact_value_quaternion(0, 0, 0, 0);
- } else if (bt->Basic.flags & BasicFlag_Pointer) {
- value = exact_value_pointer(0);
- } else if (bt->Basic.flags & BasicFlag_String) {
- String empty_string = {};
- value = exact_value_string(empty_string);
- } else if (bt->Basic.flags & BasicFlag_Rune) {
- value = exact_value_i64(0);
- }
}
-
- o->value = value;
- } else {
- o->value = exact_value_compound(node);
}
+ }
+ break;
+ }
+
+ default: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+
+ gbString str = type_to_string(type);
+ error(node, "Invalid compound literal type '%s'", str);
+ gb_string_free(str);
+ return kind;
+ }
+ }
+
+ if (is_constant) {
+ o->mode = Addressing_Constant;
+
+ if (is_type_bit_set(type)) {
+ // NOTE(bill): Encode as an integer
+
+ i64 lower = base_type(type)->BitSet.lower;
+
+ u64 bits = 0;
+ for_array(index, cl->elems) {
+ Ast *elem = cl->elems[index];
+ GB_ASSERT(elem->kind != Ast_FieldValue);
+ TypeAndValue tav = elem->tav;
+ ExactValue i = exact_value_to_integer(tav.value);
+ if (i.kind != ExactValue_Integer) {
+ continue;
+ }
+ i64 val = big_int_to_i64(&i.value_integer);
+ val -= lower;
+ u64 bit = u64(1ll<<val);
+ bits |= bit;
+ }
+ o->value = exact_value_u64(bits);
+ } else if (is_type_constant_type(type) && cl->elems.count == 0) {
+ ExactValue value = exact_value_compound(node);
+ Type *bt = core_type(type);
+ if (bt->kind == Type_Basic) {
+ if (bt->Basic.flags & BasicFlag_Boolean) {
+ value = exact_value_bool(false);
+ } else if (bt->Basic.flags & BasicFlag_Integer) {
+ value = exact_value_i64(0);
+ } else if (bt->Basic.flags & BasicFlag_Unsigned) {
+ value = exact_value_i64(0);
+ } else if (bt->Basic.flags & BasicFlag_Float) {
+ value = exact_value_float(0);
+ } else if (bt->Basic.flags & BasicFlag_Complex) {
+ value = exact_value_complex(0, 0);
+ } else if (bt->Basic.flags & BasicFlag_Quaternion) {
+ value = exact_value_quaternion(0, 0, 0, 0);
+ } else if (bt->Basic.flags & BasicFlag_Pointer) {
+ value = exact_value_pointer(0);
+ } else if (bt->Basic.flags & BasicFlag_String) {
+ String empty_string = {};
+ value = exact_value_string(empty_string);
+ } else if (bt->Basic.flags & BasicFlag_Rune) {
+ value = exact_value_i64(0);
+ }
+ }
+
+ o->value = value;
} else {
- o->mode = Addressing_Value;
+ o->value = exact_value_compound(node);
}
- o->type = type;
- case_end;
+ } else {
+ o->mode = Addressing_Value;
+ }
+ o->type = type;
+ return kind;
+}
- case_ast_node(pe, ParenExpr, node);
- kind = check_expr_base(c, o, pe->expr, type_hint);
- node->viral_state_flags |= pe->expr->viral_state_flags;
+ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ ast_node(ta, TypeAssertion, node);
+ check_expr(c, o, ta->expr);
+ node->viral_state_flags |= ta->expr->viral_state_flags;
+
+ if (o->mode == Addressing_Invalid) {
o->expr = node;
- case_end;
+ return kind;
+ }
+ if (o->mode == Addressing_Constant) {
+ gbString expr_str = expr_to_string(o->expr);
+ error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str);
+ gb_string_free(expr_str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
- case_ast_node(te, TagExpr, node);
- String name = te->name.string;
- error(node, "Unknown tag expression, #%.*s", LIT(name));
- if (te->expr) {
- kind = check_expr_base(c, o, te->expr, type_hint);
- node->viral_state_flags |= te->expr->viral_state_flags;
- }
+ if (is_type_untyped(o->type)) {
+ gbString expr_str = expr_to_string(o->expr);
+ error(o->expr, "A type assertion cannot be applied to an untyped expression: '%s'", expr_str);
+ gb_string_free(expr_str);
+ o->mode = Addressing_Invalid;
o->expr = node;
- case_end;
+ return kind;
+ }
- case_ast_node(ta, TypeAssertion, node);
- check_expr(c, o, ta->expr);
- node->viral_state_flags |= ta->expr->viral_state_flags;
+ Type *src = type_deref(o->type);
+ Type *bsrc = base_type(src);
- if (o->mode == Addressing_Invalid) {
- o->expr = node;
- return kind;
- }
- if (o->mode == Addressing_Constant) {
- gbString expr_str = expr_to_string(o->expr);
- error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str);
- gb_string_free(expr_str);
+
+ if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) {
+ if (!is_type_union(src)) {
+ gbString str = type_to_string(o->type);
+ error(o->expr, "Type assertions with .? can only operate on unions, got %s", str);
+ gb_string_free(str);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
- if (is_type_untyped(o->type)) {
- gbString expr_str = expr_to_string(o->expr);
- error(o->expr, "A type assertion cannot be applied to an untyped expression: '%s'", expr_str);
- gb_string_free(expr_str);
+ if (bsrc->Union.variants.count != 1 && type_hint != nullptr) {
+ bool allowed = false;
+ for_array(i, bsrc->Union.variants) {
+ Type *vt = bsrc->Union.variants[i];
+ if (are_types_identical(vt, type_hint)) {
+ allowed = true;
+ add_type_info_type(c, vt);
+ break;
+ }
+ }
+ if (allowed) {
+ add_type_info_type(c, o->type);
+ o->type = type_hint;
+ o->mode = Addressing_OptionalOk;
+ return kind;
+ }
+ }
+
+ if (bsrc->Union.variants.count != 1) {
+ error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
- Type *src = type_deref(o->type);
- Type *bsrc = base_type(src);
-
-
- if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) {
- if (!is_type_union(src)) {
- gbString str = type_to_string(o->type);
- error(o->expr, "Type assertions with .? can only operate on unions, got %s", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
+ add_type_info_type(c, o->type);
+ add_type_info_type(c, bsrc->Union.variants[0]);
- if (bsrc->Union.variants.count != 1 && type_hint != nullptr) {
- bool allowed = false;
- for_array(i, bsrc->Union.variants) {
- Type *vt = bsrc->Union.variants[i];
- if (are_types_identical(vt, type_hint)) {
- allowed = true;
- add_type_info_type(c, vt);
- break;
- }
- }
- if (allowed) {
- add_type_info_type(c, o->type);
- o->type = type_hint;
- o->mode = Addressing_OptionalOk;
- return kind;
+ o->type = bsrc->Union.variants[0];
+ o->mode = Addressing_OptionalOk;
+ } else {
+ Type *t = check_type(c, ta->type);
+ Type *dst = t;
+
+ if (is_type_union(src)) {
+ bool ok = false;
+ for_array(i, bsrc->Union.variants) {
+ Type *vt = bsrc->Union.variants[i];
+ if (are_types_identical(vt, dst)) {
+ ok = true;
+ break;
}
}
- if (bsrc->Union.variants.count != 1) {
- error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count);
+ if (!ok) {
+ gbString expr_str = expr_to_string(o->expr);
+ gbString dst_type_str = type_to_string(t);
+ defer (gb_string_free(expr_str));
+ defer (gb_string_free(dst_type_str));
+ if (bsrc->Union.variants.count == 0) {
+ error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str);
+ } else {
+ error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str);
+ }
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
add_type_info_type(c, o->type);
- add_type_info_type(c, bsrc->Union.variants[0]);
+ add_type_info_type(c, t);
- o->type = bsrc->Union.variants[0];
+ o->type = t;
+ o->mode = Addressing_OptionalOk;
+ } else if (is_type_any(src)) {
+ o->type = t;
o->mode = Addressing_OptionalOk;
+
+ add_type_info_type(c, o->type);
+ add_type_info_type(c, t);
} else {
- Type *t = check_type(c, ta->type);
- Type *dst = t;
+ gbString str = type_to_string(o->type);
+ error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+ }
- if (is_type_union(src)) {
- bool ok = false;
- for_array(i, bsrc->Union.variants) {
- Type *vt = bsrc->Union.variants[i];
- if (are_types_identical(vt, dst)) {
- ok = true;
- break;
- }
- }
+ if ((c->state_flags & StateFlag_no_type_assert) == 0) {
+ add_package_dependency(c, "runtime", "type_assertion_check");
+ add_package_dependency(c, "runtime", "type_assertion_check2");
+ }
+ return kind;
+}
- if (!ok) {
- gbString expr_str = expr_to_string(o->expr);
- gbString dst_type_str = type_to_string(t);
- defer (gb_string_free(expr_str));
- defer (gb_string_free(dst_type_str));
- if (bsrc->Union.variants.count == 0) {
- error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str);
- } else {
- error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str);
- }
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
+ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(se, SelectorCallExpr, node);
+ // IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables
+ // COM APIs is a great example of where this kind of thing is extremely useful
+ // General idea:
+ //
+ // x->y(123) == x.y(x, 123)
+ //
+ // How this has been implemented at the moment is quite hacky but it's done so to reduce need for huge backend changes
+ // Just regenerating a new AST aids things
+ //
+ // TODO(bill): Is this a good hack or not?
+ //
+ // NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I?
+
+
+ if (se->modified_call) {
+ // Prevent double evaluation
+ o->expr = node;
+ o->type = node->tav.type;
+ o->value = node->tav.value;
+ o->mode = node->tav.mode;
+ return Expr_Expr;
+ }
- add_type_info_type(c, o->type);
- add_type_info_type(c, t);
+ bool allow_arrow_right_selector_expr;
+ allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
+ c->allow_arrow_right_selector_expr = true;
+ Operand x = {};
+ ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
+ c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
- o->type = t;
- o->mode = Addressing_OptionalOk;
- } else if (is_type_any(src)) {
- o->type = t;
- o->mode = Addressing_OptionalOk;
+ if (x.mode == Addressing_Invalid || x.type == t_invalid) {
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
+ o->expr = node;
+ return kind;
+ }
+ if (!is_type_proc(x.type)) {
+ gbString type_str = type_to_string(x.type);
+ error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
+ gb_string_free(type_str);
- add_type_info_type(c, o->type);
- add_type_info_type(c, t);
- } else {
- gbString str = type_to_string(o->type);
- error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- }
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Stmt;
+ }
- add_package_dependency(c, "runtime", "type_assertion_check");
- add_package_dependency(c, "runtime", "type_assertion_check2");
- case_end;
+ ast_node(ce, CallExpr, se->call);
- case_ast_node(tc, TypeCast, node);
- check_expr_or_type(c, o, tc->type);
- if (o->mode != Addressing_Type) {
- gbString str = expr_to_string(tc->type);
- error(tc->type, "Expected a type, got %s", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- }
- if (o->mode == Addressing_Invalid) {
- o->expr = node;
- return kind;
+ GB_ASSERT(x.expr->kind == Ast_SelectorExpr);
+
+ Ast *first_arg = x.expr->SelectorExpr.expr;
+ GB_ASSERT(first_arg != nullptr);
+
+ Type *pt = base_type(x.type);
+ GB_ASSERT(pt->kind == Type_Proc);
+ Type *first_type = nullptr;
+ String first_arg_name = {};
+ if (pt->Proc.param_count > 0) {
+ Entity *f = pt->Proc.params->Tuple.variables[0];
+ first_type = f->type;
+ first_arg_name = f->token.string;
+ }
+ if (first_arg_name.len == 0) {
+ first_arg_name = str_lit("_");
+ }
+
+ if (first_type == nullptr) {
+ error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Stmt;
+ }
+
+ Operand y = {};
+ y.mode = first_arg->tav.mode;
+ y.type = first_arg->tav.type;
+ y.value = first_arg->tav.value;
+ if (check_is_assignable_to(c, &y, first_type)) {
+ // Do nothing, it's valid
+ } else {
+ Operand z = y;
+ z.type = type_deref(y.type);
+ if (check_is_assignable_to(c, &z, first_type)) {
+ // NOTE(bill): AST GENERATION HACK!
+ Token op = {Token_Pointer};
+ first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
+ } else if (y.mode == Addressing_Variable) {
+ Operand w = y;
+ w.type = alloc_type_pointer(y.type);
+ if (check_is_assignable_to(c, &w, first_type)) {
+ // NOTE(bill): AST GENERATION HACK!
+ Token op = {Token_And};
+ first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
+ }
}
- Type *type = o->type;
- check_expr_base(c, o, tc->expr, type);
- node->viral_state_flags |= tc->expr->viral_state_flags;
+ }
- if (o->mode != Addressing_Invalid) {
- switch (tc->token.kind) {
- case Token_transmute:
- check_transmute(c, node, o, type);
- break;
- case Token_cast:
- check_cast(c, o, type);
- break;
- default:
- error(node, "Invalid AST: Invalid casting expression");
- o->mode = Addressing_Invalid;
+ if (ce->args.count > 0) {
+ bool fail = false;
+ bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
+ for_array(i, ce->args) {
+ Ast *arg = ce->args[i];
+ bool mix = false;
+ if (first_is_field_value) {
+ mix = arg->kind != Ast_FieldValue;
+ } else {
+ mix = arg->kind == Ast_FieldValue;
+ }
+ if (mix) {
+ fail = true;
break;
}
}
- return Expr_Expr;
- case_end;
+ if (!fail && first_is_field_value) {
+ Token op = {Token_Eq};
+ AstFile *f = first_arg->file();
+ first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
+ }
+ }
- case_ast_node(ac, AutoCast, node);
- check_expr_base(c, o, ac->expr, type_hint);
- node->viral_state_flags |= ac->expr->viral_state_flags;
- if (o->mode == Addressing_Invalid) {
+
+ auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
+ modified_args[0] = first_arg;
+ slice_copy(&modified_args, ce->args, 1);
+ ce->args = modified_args;
+ se->modified_call = true;
+
+ allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
+ c->allow_arrow_right_selector_expr = true;
+ check_expr_base(c, o, se->call, type_hint);
+ c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
+
+ o->expr = node;
+ return Expr_Expr;
+}
+
+
+ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ ast_node(ie, IndexExpr, node);
+ check_expr(c, o, ie->expr);
+ node->viral_state_flags |= ie->expr->viral_state_flags;
+ if (o->mode == Addressing_Invalid) {
+ o->expr = node;
+ return kind;
+ }
+
+ Type *t = base_type(type_deref(o->type));
+ bool is_ptr = is_type_pointer(o->type);
+ bool is_const = o->mode == Addressing_Constant;
+
+ if (is_type_map(t)) {
+ Operand key = {};
+ if (is_type_typeid(t->Map.key)) {
+ check_expr_or_type(c, &key, ie->index, t->Map.key);
+ } else {
+ check_expr_with_type_hint(c, &key, ie->index, t->Map.key);
+ }
+ check_assignment(c, &key, t->Map.key, str_lit("map index"));
+ if (key.mode == Addressing_Invalid) {
+ o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
- if (type_hint) {
- Type *type = type_of_expr(ac->expr);
- check_cast(c, o, type_hint);
- if (is_type_typed(type) && are_types_identical(type, type_hint)) {
- if (build_context.vet_extra) {
- error(node, "Redundant 'auto_cast' applied to expression");
- }
- }
-
- }
+ o->mode = Addressing_MapIndex;
+ o->type = t->Map.value;
o->expr = node;
+
+ add_package_dependency(c, "runtime", "__dynamic_map_get");
+ add_package_dependency(c, "runtime", "__dynamic_map_set");
return Expr_Expr;
- case_end;
+ }
- case_ast_node(ue, UnaryExpr, node);
- Type *th = type_hint;
- if (ue->op.kind == Token_And) {
- th = type_deref(th);
+ i64 max_count = -1;
+ bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
+
+ if (is_const) {
+ if (is_type_array(t)) {
+ // OKay
+ } else if (is_type_slice(t)) {
+ // Okay
+ } else if (is_type_enumerated_array(t)) {
+ // Okay
+ } else if (is_type_string(t)) {
+ // Okay
+ } else if (is_type_relative_slice(t)) {
+ // Okay
+ } else if (is_type_matrix(t)) {
+ // Okay
+ } else {
+ valid = false;
}
- check_expr_base(c, o, ue->expr, th);
- node->viral_state_flags |= ue->expr->viral_state_flags;
+ }
- if (o->mode != Addressing_Invalid) {
- check_unary_expr(c, o, ue->op, node);
+ if (!valid) {
+ gbString str = expr_to_string(o->expr);
+ gbString type_str = type_to_string(o->type);
+ defer (gb_string_free(str));
+ defer (gb_string_free(type_str));
+ if (is_const) {
+ error(o->expr, "Cannot index constant '%s' of type '%s'", str, type_str);
+ } else {
+ error(o->expr, "Cannot index '%s' of type '%s'", str, type_str);
}
+ o->mode = Addressing_Invalid;
o->expr = node;
return kind;
- case_end;
+ }
+ if (ie->index == nullptr) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Missing index for '%s'", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
- case_ast_node(be, BinaryExpr, node);
- check_binary_expr(c, o, node, type_hint, true);
- if (o->mode == Addressing_Invalid) {
+ Type *index_type_hint = nullptr;
+ if (is_type_enumerated_array(t)) {
+ Type *bt = base_type(t);
+ GB_ASSERT(bt->kind == Type_EnumeratedArray);
+ index_type_hint = bt->EnumeratedArray.index;
+ }
+
+ i64 index = 0;
+ bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint);
+ if (is_const) {
+ if (index < 0) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Cannot index a constant '%s'", str);
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
o->expr = node;
return kind;
+ } else if (ok) {
+ ExactValue value = type_and_value_of_expr(ie->expr).value;
+ o->mode = Addressing_Constant;
+ bool success = false;
+ bool finish = false;
+ o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish);
+ if (!success) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index);
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
}
- case_end;
+ }
- case_ast_node(se, SelectorExpr, node);
- check_selector(c, o, node, type_hint);
- node->viral_state_flags |= se->expr->viral_state_flags;
- case_end;
+ if (type_hint != nullptr && is_type_matrix(t)) {
+ // TODO(bill): allow matrix columns to be assignable to other types which are the same internally
+ // if a type hint exists
+ }
+ return kind;
+}
- case_ast_node(se, SelectorCallExpr, node);
- // IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables
- // COM APIs is a great example of where this kind of thing is extremely useful
- // General idea:
- //
- // x->y(123) == x.y(x, 123)
- //
- // How this has been implemented at the moment is quite hacky but it's done so to reduce need for huge backend changes
- // Just regenerating a new AST aids things
- //
- // TODO(bill): Is this a good hack or not?
- //
- // NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I?
-
-
- if (se->modified_call) {
- // Prevent double evaluation
- o->expr = node;
- o->type = node->tav.type;
- o->value = node->tav.value;
- o->mode = node->tav.mode;
- return Expr_Expr;
- }
+ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Stmt;
+ ast_node(se, SliceExpr, node);
+ check_expr(c, o, se->expr);
+ node->viral_state_flags |= se->expr->viral_state_flags;
- bool allow_arrow_right_selector_expr;
- allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
- c->allow_arrow_right_selector_expr = true;
- Operand x = {};
- ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
- c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
+ if (o->mode == Addressing_Invalid) {
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
- if (x.mode == Addressing_Invalid || x.type == t_invalid) {
- o->mode = Addressing_Invalid;
- o->type = t_invalid;
- o->expr = node;
- return kind;
+ bool valid = false;
+ i64 max_count = -1;
+ Type *t = base_type(type_deref(o->type));
+ switch (t->kind) {
+ case Type_Basic:
+ if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
+ valid = true;
+ if (o->mode == Addressing_Constant) {
+ max_count = o->value.value_string.len;
+ }
+ o->type = type_deref(o->type);
}
- if (!is_type_proc(x.type)) {
- gbString type_str = type_to_string(x.type);
- error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
- gb_string_free(type_str);
+ break;
+ case Type_Array:
+ valid = true;
+ max_count = t->Array.count;
+ if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) {
+ gbString str = expr_to_string(node);
+ error(node, "Cannot slice array '%s', value is not addressable", str);
+ gb_string_free(str);
o->mode = Addressing_Invalid;
- o->type = t_invalid;
o->expr = node;
- return Expr_Stmt;
+ return kind;
}
+ o->type = alloc_type_slice(t->Array.elem);
+ break;
- ast_node(ce, CallExpr, se->call);
+ case Type_MultiPointer:
+ valid = true;
+ o->type = type_deref(o->type);
+ break;
- GB_ASSERT(x.expr->kind == Ast_SelectorExpr);
+ case Type_Slice:
+ valid = true;
+ o->type = type_deref(o->type);
+ break;
- Ast *first_arg = x.expr->SelectorExpr.expr;
- GB_ASSERT(first_arg != nullptr);
+ case Type_DynamicArray:
+ valid = true;
+ o->type = alloc_type_slice(t->DynamicArray.elem);
+ break;
- Type *pt = base_type(x.type);
- GB_ASSERT(pt->kind == Type_Proc);
- Type *first_type = nullptr;
- String first_arg_name = {};
- if (pt->Proc.param_count > 0) {
- Entity *f = pt->Proc.params->Tuple.variables[0];
- first_type = f->type;
- first_arg_name = f->token.string;
- }
- if (first_arg_name.len == 0) {
- first_arg_name = str_lit("_");
+ case Type_Struct:
+ if (is_type_soa_struct(t)) {
+ valid = true;
+ o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
}
+ break;
- if (first_type == nullptr) {
- error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
+ case Type_RelativeSlice:
+ valid = true;
+ o->type = t->RelativeSlice.slice_type;
+ if (o->mode != Addressing_Variable) {
+ gbString str = expr_to_string(node);
+ error(node, "Cannot relative slice '%s', value is not addressable", str);
+ gb_string_free(str);
o->mode = Addressing_Invalid;
- o->type = t_invalid;
o->expr = node;
- return Expr_Stmt;
+ return kind;
}
+ break;
+ }
- Operand y = {};
- y.mode = first_arg->tav.mode;
- y.type = first_arg->tav.type;
- y.value = first_arg->tav.value;
- if (check_is_assignable_to(c, &y, first_type)) {
- // Do nothing, it's valid
- } else {
- Operand z = y;
- z.type = type_deref(y.type);
- if (check_is_assignable_to(c, &z, first_type)) {
- // NOTE(bill): AST GENERATION HACK!
- Token op = {Token_Pointer};
- first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
- } else if (y.mode == Addressing_Variable) {
- Operand w = y;
- w.type = alloc_type_pointer(y.type);
- if (check_is_assignable_to(c, &w, first_type)) {
- // NOTE(bill): AST GENERATION HACK!
- Token op = {Token_And};
- first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
- }
+ if (!valid) {
+ gbString str = expr_to_string(o->expr);
+ gbString type_str = type_to_string(o->type);
+ error(o->expr, "Cannot slice '%s' of type '%s'", str, type_str);
+ gb_string_free(type_str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ if (se->low == nullptr && se->high != nullptr) {
+ // It is okay to continue as it will assume the 1st index is zero
+ }
+
+ i64 indices[2] = {};
+ Ast *nodes[2] = {se->low, se->high};
+ for (isize i = 0; i < gb_count_of(nodes); i++) {
+ i64 index = max_count;
+ if (nodes[i] != nullptr) {
+ i64 capacity = -1;
+ if (max_count >= 0) {
+ capacity = max_count;
+ }
+ i64 j = 0;
+ if (check_index_value(c, t, true, nodes[i], capacity, &j)) {
+ index = j;
}
+
+ node->viral_state_flags |= nodes[i]->viral_state_flags;
+ } else if (i == 0) {
+ index = 0;
}
+ indices[i] = index;
+ }
- if (ce->args.count > 0) {
- bool fail = false;
- bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
- for_array(i, ce->args) {
- Ast *arg = ce->args[i];
- bool mix = false;
- if (first_is_field_value) {
- mix = arg->kind != Ast_FieldValue;
- } else {
- mix = arg->kind == Ast_FieldValue;
- }
- if (mix) {
- fail = true;
+ for (isize i = 0; i < gb_count_of(indices); i++) {
+ i64 a = indices[i];
+ for (isize j = i+1; j < gb_count_of(indices); j++) {
+ i64 b = indices[j];
+ if (a > b && b >= 0) {
+ error(se->close, "Invalid slice indices: [%td > %td]", a, b);
+ }
+ }
+ }
+
+ if (max_count < 0) {
+ if (o->mode == Addressing_Constant) {
+ gbString s = expr_to_string(se->expr);
+ error(se->expr, "Cannot slice constant value '%s'", s);
+ gb_string_free(s);
+ }
+ }
+
+ if (t->kind == Type_MultiPointer && se->high != nullptr) {
+ /*
+ x[:] -> [^]T
+ x[i:] -> [^]T
+ x[:n] -> []T
+ x[i:n] -> []T
+ */
+ o->type = alloc_type_slice(t->MultiPointer.elem);
+ }
+
+ o->mode = Addressing_Value;
+
+ if (is_type_string(t) && max_count >= 0) {
+ bool all_constant = true;
+ for (isize i = 0; i < gb_count_of(nodes); i++) {
+ if (nodes[i] != nullptr) {
+ TypeAndValue tav = type_and_value_of_expr(nodes[i]);
+ if (tav.mode != Addressing_Constant) {
+ all_constant = false;
break;
}
}
- if (!fail && first_is_field_value) {
- Token op = {Token_Eq};
- AstFile *f = first_arg->file();
- first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
- }
+ }
+ if (!all_constant) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Cannot slice '%s' with non-constant indices", str);
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ gb_string_free(str);
+ o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring
+ o->expr = node;
+ return kind;
}
+ String s = {};
+ if (o->value.kind == ExactValue_String) {
+ s = o->value.value_string;
+ }
+ o->mode = Addressing_Constant;
+ o->type = t;
+ o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
+ }
+ return kind;
+}
- auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
- modified_args[0] = first_arg;
- slice_copy(&modified_args, ce->args, 1);
- ce->args = modified_args;
- se->modified_call = true;
+ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ u32 prev_state_flags = c->state_flags;
+ defer (c->state_flags = prev_state_flags);
+ if (node->state_flags != 0) {
+ u32 in = node->state_flags;
+ u32 out = c->state_flags;
- allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
- c->allow_arrow_right_selector_expr = true;
- check_expr_base(c, o, se->call, type_hint);
- c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
+ if (in & StateFlag_no_bounds_check) {
+ out |= StateFlag_no_bounds_check;
+ out &= ~StateFlag_bounds_check;
+ } else if (in & StateFlag_bounds_check) {
+ out |= StateFlag_bounds_check;
+ out &= ~StateFlag_no_bounds_check;
+ }
- o->expr = node;
- return Expr_Expr;
- case_end;
+ if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ } else if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ }
+ c->state_flags = out;
+ }
- case_ast_node(ise, ImplicitSelectorExpr, node);
- return check_implicit_selector_expr(c, o, node, type_hint);
- case_end;
+ ExprKind kind = Expr_Stmt;
- case_ast_node(ie, IndexExpr, node);
- check_expr(c, o, ie->expr);
- node->viral_state_flags |= ie->expr->viral_state_flags;
- if (o->mode == Addressing_Invalid) {
- o->expr = node;
- return kind;
- }
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
+ o->value = {ExactValue_Invalid};
- Type *t = base_type(type_deref(o->type));
- bool is_ptr = is_type_pointer(o->type);
- bool is_const = o->mode == Addressing_Constant;
+ switch (node->kind) {
+ default:
+ return kind;
- if (is_type_map(t)) {
- Operand key = {};
- if (is_type_typeid(t->Map.key)) {
- check_expr_or_type(c, &key, ie->index, t->Map.key);
- } else {
- check_expr_with_type_hint(c, &key, ie->index, t->Map.key);
- }
- check_assignment(c, &key, t->Map.key, str_lit("map index"));
- if (key.mode == Addressing_Invalid) {
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- o->mode = Addressing_MapIndex;
- o->type = t->Map.value;
- o->expr = node;
+ case_ast_node(be, BadExpr, node)
+ return kind;
+ case_end;
- add_package_dependency(c, "runtime", "__dynamic_map_get");
- add_package_dependency(c, "runtime", "__dynamic_map_set");
- return Expr_Expr;
- }
+ case_ast_node(i, Implicit, node)
+ switch (i->kind) {
+ case Token_context:
+ {
+ if (c->proc_name.len == 0 && c->curr_proc_sig == nullptr) {
+ error(node, "'context' is only allowed within procedures %p", c->curr_proc_decl);
+ return kind;
+ }
+ if (unparen_expr(c->assignment_lhs_hint) == node) {
+ c->scope->flags |= ScopeFlag_ContextDefined;
+ }
- i64 max_count = -1;
- bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
+ if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
+ error(node, "'context' has not been defined within this scope");
+ // Continue with value
+ }
- if (is_const) {
- if (is_type_array(t)) {
- // OKay
- } else if (is_type_slice(t)) {
- // Okay
- } else if (is_type_enumerated_array(t)) {
- // Okay
- } else if (is_type_string(t)) {
- // Okay
- } else if (is_type_relative_slice(t)) {
- // Okay
- } else if (is_type_matrix(t)) {
- // Okay
- } else {
- valid = false;
+ init_core_context(c->checker);
+ o->mode = Addressing_Context;
+ o->type = t_context;
}
- }
+ break;
- if (!valid) {
- gbString str = expr_to_string(o->expr);
- gbString type_str = type_to_string(o->type);
- defer (gb_string_free(str));
- defer (gb_string_free(type_str));
- if (is_const) {
- error(o->expr, "Cannot index constant '%s' of type '%s'", str, type_str);
- } else {
- error(o->expr, "Cannot index '%s' of type '%s'", str, type_str);
- }
- o->mode = Addressing_Invalid;
- o->expr = node;
+ default:
+ error(node, "Illegal implicit name '%.*s'", LIT(i->string));
return kind;
}
+ case_end;
- if (ie->index == nullptr) {
- gbString str = expr_to_string(o->expr);
- error(o->expr, "Missing index for '%s'", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
+ case_ast_node(i, Ident, node);
+ check_ident(c, o, node, nullptr, type_hint, false);
+ case_end;
- Type *index_type_hint = nullptr;
- if (is_type_enumerated_array(t)) {
- Type *bt = base_type(t);
- GB_ASSERT(bt->kind == Type_EnumeratedArray);
- index_type_hint = bt->EnumeratedArray.index;
- }
+ case_ast_node(u, Undef, node);
+ o->mode = Addressing_Value;
+ o->type = t_untyped_undef;
+ case_end;
- i64 index = 0;
- bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint);
- if (is_const) {
- if (index < 0) {
- gbString str = expr_to_string(o->expr);
- error(o->expr, "Cannot index a constant '%s'", str);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- } else if (ok) {
- ExactValue value = type_and_value_of_expr(ie->expr).value;
- o->mode = Addressing_Constant;
- bool success = false;
- bool finish = false;
- o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish);
- if (!success) {
- gbString str = expr_to_string(o->expr);
- error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
+
+ case_ast_node(bl, BasicLit, node);
+ Type *t = t_invalid;
+ switch (node->tav.value.kind) {
+ case ExactValue_String: t = t_untyped_string; break;
+ case ExactValue_Float: t = t_untyped_float; break;
+ case ExactValue_Complex: t = t_untyped_complex; break;
+ case ExactValue_Quaternion: t = t_untyped_quaternion; break;
+ case ExactValue_Integer:
+ t = t_untyped_integer;
+ if (bl->token.kind == Token_Rune) {
+ t = t_untyped_rune;
}
+ break;
+ default:
+ GB_PANIC("Unhandled value type for basic literal");
+ break;
}
-
- if (type_hint != nullptr && is_type_matrix(t)) {
- // TODO(bill): allow matrix columns to be assignable to other types which are the same internally
- // if a type hint exists
- }
-
+
+ o->mode = Addressing_Constant;
+ o->type = t;
+ o->value = node->tav.value;
case_end;
- case_ast_node(se, SliceExpr, node);
- check_expr(c, o, se->expr);
- node->viral_state_flags |= se->expr->viral_state_flags;
+ case_ast_node(bd, BasicDirective, node);
+ kind = check_basic_directive_expr(c, o, node, type_hint);
+ case_end;
- if (o->mode == Addressing_Invalid) {
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
+ case_ast_node(pg, ProcGroup, node);
+ error(node, "Illegal use of a procedure group");
+ o->mode = Addressing_Invalid;
+ case_end;
- bool valid = false;
- i64 max_count = -1;
- Type *t = base_type(type_deref(o->type));
- switch (t->kind) {
- case Type_Basic:
- if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
- valid = true;
- if (o->mode == Addressing_Constant) {
- max_count = o->value.value_string.len;
- }
- o->type = type_deref(o->type);
+ case_ast_node(pl, ProcLit, node);
+ CheckerContext ctx = *c;
+
+ DeclInfo *decl = nullptr;
+ Type *type = alloc_type(Type_Proc);
+ check_open_scope(&ctx, pl->type);
+ {
+ decl = make_decl_info(ctx.scope, ctx.decl);
+ decl->proc_lit = node;
+ ctx.decl = decl;
+ defer (ctx.decl = ctx.decl->parent);
+
+ if (pl->tags != 0) {
+ error(node, "A procedure literal cannot have tags");
+ pl->tags = 0; // TODO(bill): Should I zero this?!
}
- break;
- case Type_Array:
- valid = true;
- max_count = t->Array.count;
- if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) {
+ check_procedure_type(&ctx, type, pl->type);
+ if (!is_type_proc(type)) {
gbString str = expr_to_string(node);
- error(node, "Cannot slice array '%s', value is not addressable", str);
+ error(node, "Invalid procedure literal '%s'", str);
gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
+ check_close_scope(&ctx);
return kind;
}
- o->type = alloc_type_slice(t->Array.elem);
- break;
- case Type_MultiPointer:
- valid = true;
- o->type = type_deref(o->type);
- break;
+ if (pl->body == nullptr) {
+ error(node, "A procedure literal must have a body");
+ return kind;
+ }
- case Type_Slice:
- valid = true;
- o->type = type_deref(o->type);
- break;
+ pl->decl = decl;
+ check_procedure_later(&ctx, ctx.file, empty_token, decl, type, pl->body, pl->tags);
+ }
+ check_close_scope(&ctx);
- case Type_DynamicArray:
- valid = true;
- o->type = alloc_type_slice(t->DynamicArray.elem);
- break;
+ o->mode = Addressing_Value;
+ o->type = type;
+ case_end;
- case Type_Struct:
- if (is_type_soa_struct(t)) {
- valid = true;
- o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
- }
- break;
+ case_ast_node(te, TernaryIfExpr, node);
+ kind = check_ternary_if_expr(c, o, node, type_hint);
+ case_end;
- case Type_RelativeSlice:
- valid = true;
- o->type = t->RelativeSlice.slice_type;
- if (o->mode != Addressing_Variable) {
- gbString str = expr_to_string(node);
- error(node, "Cannot relative slice '%s', value is not addressable", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- break;
+ case_ast_node(te, TernaryWhenExpr, node);
+ kind = check_ternary_when_expr(c, o, node, type_hint);
+ case_end;
+
+ case_ast_node(oe, OrElseExpr, node);
+ return check_or_else_expr(c, o, node, type_hint);
+ case_end;
+
+ case_ast_node(re, OrReturnExpr, node);
+ return check_or_return_expr(c, o, node, type_hint);
+ case_end;
+
+ case_ast_node(cl, CompoundLit, node);
+ kind = check_compound_literal(c, o, node, type_hint);
+ case_end;
+
+ case_ast_node(pe, ParenExpr, node);
+ kind = check_expr_base(c, o, pe->expr, type_hint);
+ node->viral_state_flags |= pe->expr->viral_state_flags;
+ o->expr = node;
+ case_end;
+
+ case_ast_node(te, TagExpr, node);
+ String name = te->name.string;
+ error(node, "Unknown tag expression, #%.*s", LIT(name));
+ if (te->expr) {
+ kind = check_expr_base(c, o, te->expr, type_hint);
+ node->viral_state_flags |= te->expr->viral_state_flags;
}
+ o->expr = node;
+ case_end;
- if (!valid) {
- gbString str = expr_to_string(o->expr);
- gbString type_str = type_to_string(o->type);
- error(o->expr, "Cannot slice '%s' of type '%s'", str, type_str);
- gb_string_free(type_str);
+ case_ast_node(ta, TypeAssertion, node);
+ kind = check_type_assertion(c, o, node, type_hint);
+ case_end;
+
+ case_ast_node(tc, TypeCast, node);
+ check_expr_or_type(c, o, tc->type);
+ if (o->mode != Addressing_Type) {
+ gbString str = expr_to_string(tc->type);
+ error(tc->type, "Expected a type, got %s", str);
gb_string_free(str);
o->mode = Addressing_Invalid;
+ }
+ if (o->mode == Addressing_Invalid) {
o->expr = node;
return kind;
}
+ Type *type = o->type;
+ check_expr_base(c, o, tc->expr, type);
+ node->viral_state_flags |= tc->expr->viral_state_flags;
- if (se->low == nullptr && se->high != nullptr) {
- // It is okay to continue as it will assume the 1st index is zero
+ if (o->mode != Addressing_Invalid) {
+ switch (tc->token.kind) {
+ case Token_transmute:
+ check_transmute(c, node, o, type);
+ break;
+ case Token_cast:
+ check_cast(c, o, type);
+ break;
+ default:
+ error(node, "Invalid AST: Invalid casting expression");
+ o->mode = Addressing_Invalid;
+ break;
+ }
}
+ return Expr_Expr;
+ case_end;
- i64 indices[2] = {};
- Ast *nodes[2] = {se->low, se->high};
- for (isize i = 0; i < gb_count_of(nodes); i++) {
- i64 index = max_count;
- if (nodes[i] != nullptr) {
- i64 capacity = -1;
- if (max_count >= 0) {
- capacity = max_count;
- }
- i64 j = 0;
- if (check_index_value(c, t, true, nodes[i], capacity, &j)) {
- index = j;
- }
+ case_ast_node(ac, AutoCast, node);
+ check_expr_base(c, o, ac->expr, type_hint);
+ node->viral_state_flags |= ac->expr->viral_state_flags;
- node->viral_state_flags |= nodes[i]->viral_state_flags;
- } else if (i == 0) {
- index = 0;
- }
- indices[i] = index;
+ if (o->mode == Addressing_Invalid) {
+ o->expr = node;
+ return kind;
}
-
- for (isize i = 0; i < gb_count_of(indices); i++) {
- i64 a = indices[i];
- for (isize j = i+1; j < gb_count_of(indices); j++) {
- i64 b = indices[j];
- if (a > b && b >= 0) {
- error(se->close, "Invalid slice indices: [%td > %td]", a, b);
+ if (type_hint) {
+ Type *type = type_of_expr(ac->expr);
+ check_cast(c, o, type_hint);
+ if (is_type_typed(type) && are_types_identical(type, type_hint)) {
+ if (build_context.vet_extra) {
+ error(node, "Redundant 'auto_cast' applied to expression");
}
}
+
}
+ o->expr = node;
+ return Expr_Expr;
+ case_end;
- if (max_count < 0) {
- if (o->mode == Addressing_Constant) {
- gbString s = expr_to_string(se->expr);
- error(se->expr, "Cannot slice constant value '%s'", s);
- gb_string_free(s);
- }
+ case_ast_node(ue, UnaryExpr, node);
+ Type *th = type_hint;
+ if (ue->op.kind == Token_And) {
+ th = type_deref(th);
}
+ check_expr_base(c, o, ue->expr, th);
+ node->viral_state_flags |= ue->expr->viral_state_flags;
- if (t->kind == Type_MultiPointer && se->high != nullptr) {
- /*
- x[:] -> [^]T
- x[i:] -> [^]T
- x[:n] -> []T
- x[i:n] -> []T
- */
- o->type = alloc_type_slice(t->MultiPointer.elem);
+ if (o->mode != Addressing_Invalid) {
+ check_unary_expr(c, o, ue->op, node);
}
+ o->expr = node;
+ return kind;
+ case_end;
- o->mode = Addressing_Value;
- if (is_type_string(t) && max_count >= 0) {
- bool all_constant = true;
- for (isize i = 0; i < gb_count_of(nodes); i++) {
- if (nodes[i] != nullptr) {
- TypeAndValue tav = type_and_value_of_expr(nodes[i]);
- if (tav.mode != Addressing_Constant) {
- all_constant = false;
- break;
- }
- }
- }
- if (!all_constant) {
- gbString str = expr_to_string(o->expr);
- error(o->expr, "Cannot slice '%s' with non-constant indices", str);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
- gb_string_free(str);
- o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring
- o->expr = node;
- return kind;
- }
+ case_ast_node(be, BinaryExpr, node);
+ check_binary_expr(c, o, node, type_hint, true);
+ if (o->mode == Addressing_Invalid) {
+ o->expr = node;
+ return kind;
+ }
+ case_end;
- String s = {};
- if (o->value.kind == ExactValue_String) {
- s = o->value.value_string;
- }
+ case_ast_node(se, SelectorExpr, node);
+ check_selector(c, o, node, type_hint);
+ node->viral_state_flags |= se->expr->viral_state_flags;
+ case_end;
- o->mode = Addressing_Constant;
- o->type = t;
- o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
- }
+ case_ast_node(se, SelectorCallExpr, node);
+ return check_selector_call_expr(c, o, node, type_hint);
+ case_end;
+ case_ast_node(ise, ImplicitSelectorExpr, node);
+ return check_implicit_selector_expr(c, o, node, type_hint);
+ case_end;
+
+ case_ast_node(ie, IndexExpr, node);
+ kind = check_index_expr(c, o, node, type_hint);
+ case_end;
+
+ case_ast_node(se, SliceExpr, node);
+ kind = check_slice_expr(c, o, node, type_hint);
case_end;
case_ast_node(mie, MatrixIndexExpr, node);
@@ -8911,6 +9386,8 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
return kind;
}
+
+
ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = check_expr_base_internal(c, o, node, type_hint);
if (o->type != nullptr && core_type(o->type) == nullptr) {
@@ -8926,6 +9403,8 @@ ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hi
if (o->type != nullptr && is_type_untyped(o->type)) {
add_untyped(c, node, o->mode, o->type, o->value);
}
+ check_rtti_type_disallowed(node, o->type, "An expression is using a type, %s, which has been disallowed");
+
add_type_and_value(c->info, node, o->mode, o->type, o->value);
return kind;
}
@@ -9085,18 +9564,7 @@ gbString string_append_string(gbString str, String string) {
gbString string_append_token(gbString str, Token token) {
- if (token.kind == Token_String) {
- str = gb_string_append_rune(str, '"');
- } else if (token.kind == Token_Rune) {
- str = gb_string_append_rune(str, '\'');
- }
str = string_append_string(str, token.string);
- if (token.kind == Token_String) {
- str = gb_string_append_rune(str, '"');
- } else if (token.kind == Token_Rune) {
- str = gb_string_append_rune(str, '\'');
- }
-
return str;
}
@@ -9323,6 +9791,13 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
str = gb_string_appendc(str, " = ");
str = write_expr_to_string(str, fv->value, shorthand);
case_end;
+ case_ast_node(fv, EnumFieldValue, node);
+ str = write_expr_to_string(str, fv->name, shorthand);
+ if (fv->value) {
+ str = gb_string_appendc(str, " = ");
+ str = write_expr_to_string(str, fv->value, shorthand);
+ }
+ case_end;
case_ast_node(ht, HelperType, node);
str = gb_string_appendc(str, "#type ");
@@ -9414,6 +9889,9 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
if (f->flags&FieldFlag_const) {
str = gb_string_appendc(str, "#const ");
}
+ if (f->flags&FieldFlag_subtype) {
+ str = gb_string_appendc(str, "#subtype ");
+ }
for_array(i, f->names) {
Ast *name = f->names[i];
@@ -9572,8 +10050,11 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
str = write_expr_to_string(str, st->polymorphic_params, shorthand);
str = gb_string_appendc(str, ") ");
}
- if (st->no_nil) str = gb_string_appendc(str, "#no_nil ");
- if (st->maybe) str = gb_string_appendc(str, "#maybe ");
+ switch (st->kind) {
+ case UnionType_maybe: str = gb_string_appendc(str, "#maybe "); break;
+ case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break;
+ case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break;
+ }
if (st->align) {
str = gb_string_appendc(str, "#align ");
str = write_expr_to_string(str, st->align, shorthand);
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 1a424240c..f2c830c1b 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -490,6 +490,14 @@ void check_stmt(CheckerContext *ctx, Ast *node, u32 flags) {
out &= ~StateFlag_no_bounds_check;
}
+ if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ } else if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ }
+
ctx->state_flags = out;
}
@@ -607,7 +615,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
case Entity_ImportName: {
Scope *scope = e->ImportName.scope;
- for_array(i, scope->elements.entries) {
+ MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
String name = scope->elements.entries[i].key.string;
Entity *decl = scope->elements.entries[i].value;
if (!is_entity_exported(decl)) continue;
@@ -689,54 +697,6 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
return true;
}
-
-struct TypeAndToken {
- Type *type;
- Token token;
-};
-
-
-void add_constant_switch_case(CheckerContext *ctx, PtrMap<uintptr, TypeAndToken> *seen, Operand operand, bool use_expr = true) {
- if (operand.mode != Addressing_Constant) {
- return;
- }
- if (operand.value.kind == ExactValue_Invalid) {
- return;
- }
-
- uintptr key = hash_exact_value(operand.value);
- TypeAndToken *found = map_get(seen, key);
- if (found != nullptr) {
- isize count = multi_map_count(seen, key);
- TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
-
- multi_map_get_all(seen, key, taps);
- for (isize i = 0; i < count; i++) {
- TypeAndToken tap = taps[i];
- if (!are_types_identical(operand.type, tap.type)) {
- continue;
- }
-
- TokenPos pos = tap.token.pos;
- if (use_expr) {
- gbString expr_str = expr_to_string(operand.expr);
- error(operand.expr,
- "Duplicate case '%s'\n"
- "\tprevious case at %s",
- expr_str,
- token_pos_to_string(pos));
- gb_string_free(expr_str);
- } else {
- error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos));
- }
- return;
- }
- }
-
- TypeAndToken tap = {operand.type, ast_token(operand.expr)};
- multi_map_insert(seen, key, tap);
-}
-
void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
ast_node(irs, UnrollRangeStmt, node);
check_open_scope(ctx, node);
@@ -961,7 +921,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
}
}
- PtrMap<uintptr, TypeAndToken> seen = {}; // NOTE(bill): Multimap, Key: ExactValue
+ SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue
map_init(&seen, heap_allocator());
defer (map_destroy(&seen));
@@ -1001,9 +961,9 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
TokenKind upper_op = Token_Invalid;
switch (be->op.kind) {
- case Token_Ellipsis: upper_op = Token_GtEq; break;
- case Token_RangeFull: upper_op = Token_GtEq; break;
- case Token_RangeHalf: upper_op = Token_Gt; break;
+ case Token_Ellipsis: upper_op = Token_LtEq; break;
+ case Token_RangeFull: upper_op = Token_LtEq; break;
+ case Token_RangeHalf: upper_op = Token_Lt; break;
default: GB_PANIC("Invalid range operator"); break;
}
@@ -1024,45 +984,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
Operand b1 = rhs;
check_comparison(ctx, &a1, &b1, Token_LtEq);
- if (is_type_enum(x.type)) {
- // TODO(bill): Fix this logic so it's fast!!!
-
- i64 v0 = exact_value_to_i64(lhs.value);
- i64 v1 = exact_value_to_i64(rhs.value);
- Operand v = {};
- v.mode = Addressing_Constant;
- v.type = x.type;
- v.expr = x.expr;
-
- Type *bt = base_type(x.type);
- GB_ASSERT(bt->kind == Type_Enum);
- for (i64 vi = v0; vi <= v1; vi++) {
- if (upper_op != Token_GtEq && vi == v1) {
- break;
- }
-
- bool found = false;
- for_array(j, bt->Enum.fields) {
- Entity *f = bt->Enum.fields[j];
- GB_ASSERT(f->kind == Entity_Constant);
-
- i64 fv = exact_value_to_i64(f->Constant.value);
- if (fv == vi) {
- found = true;
- break;
- }
- }
- if (found) {
- v.value = exact_value_i64(vi);
- add_constant_switch_case(ctx, &seen, v);
- }
- }
- } else {
- add_constant_switch_case(ctx, &seen, lhs);
- if (upper_op == Token_GtEq) {
- add_constant_switch_case(ctx, &seen, rhs);
- }
- }
+ add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs);
if (is_type_string(x.type)) {
// NOTE(bill): Force dependency for strings here
@@ -1107,7 +1029,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
continue;
}
update_untyped_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type));
- add_constant_switch_case(ctx, &seen, y);
+ add_to_seen_map(ctx, &seen, y);
}
}
}
@@ -1143,7 +1065,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
if (unhandled.count == 1) {
error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string));
} else {
- error_no_newline(node, "Unhandled switch cases: ");
+ error(node, "Unhandled switch cases:");
for_array(i, unhandled) {
Entity *f = unhandled[i];
error_line("\t%.*s\n", LIT(f->token.string));
@@ -2230,7 +2152,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
e->state = EntityState_Resolved;
}
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
- e->Variable.thread_local_model = ac.thread_local_model;
if (ac.link_name.len > 0) {
e->Variable.link_name = ac.link_name;
@@ -2243,6 +2164,9 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
error(e->token, "The 'static' attribute is not allowed to be applied to '_'");
} else {
e->flags |= EntityFlag_Static;
+ if (ctx->in_defer) {
+ error(e->token, "'static' variables cannot be declared within a defer statement");
+ }
}
}
if (ac.thread_local_model != "") {
@@ -2251,10 +2175,18 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
error(e->token, "The 'thread_local' attribute is not allowed to be applied to '_'");
} else {
e->flags |= EntityFlag_Static;
+ if (ctx->in_defer) {
+ error(e->token, "'thread_local' variables cannot be declared within a defer statement");
+ }
}
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");
}
diff --git a/src/check_type.cpp b/src/check_type.cpp
index b4f30d2f0..51f472961 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -109,14 +109,19 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
}
i32 field_src_index = 0;
+ 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;
Type *type = nullptr;
+ CommentGroup *docs = p->docs;
+ CommentGroup *comment = p->comment;
if (type_expr != nullptr) {
type = check_type_expr(ctx, type_expr, nullptr);
@@ -139,6 +144,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
}
bool is_using = (p->flags&FieldFlag_using) != 0;
+ bool is_subtype = (p->flags&FieldFlag_subtype) != 0;
for_array(j, p->names) {
Ast *name = p->names[j];
@@ -152,6 +158,18 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index);
add_entity(ctx, ctx->scope, name, field);
+ field->Variable.field_group_index = field_group_index;
+ if (is_subtype) {
+ field->flags |= EntityFlag_Subtype;
+ }
+
+ if (j == 0) {
+ field->Variable.docs = docs;
+ }
+ if (j+1 == p->names.count) {
+ field->Variable.comment = comment;
+ }
+
array_add(&fields_array, field);
String tag = p->tag.string;
if (tag.len != 0 && !unquote_string(permanent_allocator(), &tag, 0, tag.text[0] == '`')) {
@@ -180,6 +198,20 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
populate_using_entity_scope(ctx, node, p, type);
}
+
+ if (is_subtype && p->names.count > 0) {
+ Type *first_type = fields_array[fields_array.count-1]->type;
+ Type *t = base_type(type_deref(first_type));
+
+ if (!does_field_type_allow_using(t) &&
+ p->names.count >= 1 &&
+ p->names[0]->kind == Ast_Ident) {
+ Token name_token = p->names[0]->Ident.token;
+ gbString type_str = type_to_string(first_type);
+ error(name_token, "'subtype' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str);
+ gb_string_free(type_str);
+ }
+ }
}
*fields = slice_from_array(fields_array);
@@ -309,6 +341,10 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t
}
named_type->Named.type_name = e;
+ GB_ASSERT(original_type->kind == Type_Named);
+ e->TypeName.objc_class_name = original_type->Named.type_name->TypeName.objc_class_name;
+ // TODO(bill): Is this even correct? Or should the metadata be copied?
+ e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata;
mutex_lock(&ctx->info->gen_types_mutex);
auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
@@ -639,22 +675,31 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
}
if (ok) {
array_add(&variants, t);
+
+ if (ut->kind == UnionType_shared_nil) {
+ if (!type_has_nil(t)) {
+ gbString s = type_to_string(t);
+ error(node, "Each variant of a union with #shared_nil must have a 'nil' value, got %s", s);
+ gb_string_free(s);
+ }
+ }
}
}
}
union_type->Union.variants = slice_from_array(variants);
- union_type->Union.no_nil = ut->no_nil;
- union_type->Union.maybe = ut->maybe;
- if (union_type->Union.no_nil) {
+ union_type->Union.kind = ut->kind;
+ switch (ut->kind) {
+ case UnionType_no_nil:
if (variants.count < 2) {
error(ut->align, "A union with #no_nil must have at least 2 variants");
}
- }
- if (union_type->Union.maybe) {
+ break;
+ case UnionType_maybe:
if (variants.count != 1) {
error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count);
}
+ break;
}
if (ut->align != nullptr) {
@@ -718,20 +763,19 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
Ast *ident = nullptr;
Ast *init = nullptr;
u32 entity_flags = 0;
- if (field->kind == Ast_FieldValue) {
- ast_node(fv, FieldValue, field);
- if (fv->field == nullptr || fv->field->kind != Ast_Ident) {
- error(field, "An enum field's name must be an identifier");
- continue;
- }
- ident = fv->field;
- init = fv->value;
- } else if (field->kind == Ast_Ident) {
- ident = field;
- } else {
+ if (field->kind != Ast_EnumFieldValue) {
+ error(field, "An enum field's name must be an identifier");
+ continue;
+ }
+ ident = field->EnumFieldValue.name;
+ init = field->EnumFieldValue.value;
+ if (ident == nullptr || ident->kind != Ast_Ident) {
error(field, "An enum field's name must be an identifier");
continue;
}
+ CommentGroup *docs = field->EnumFieldValue.docs;
+ CommentGroup *comment = field->EnumFieldValue.comment;
+
String name = ident->Ident.token.string;
if (init != nullptr) {
@@ -789,6 +833,8 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
e->flags |= EntityFlag_Visited;
e->state = EntityState_Resolved;
e->Constant.flags |= entity_flags;
+ e->Constant.docs = docs;
+ e->Constant.comment = comment;
if (scope_lookup_current(ctx->scope, name) != nullptr) {
error(ident, "'%.*s' is already declared in this enumeration", LIT(name));
@@ -922,20 +968,19 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
i64 lower = big_int_to_i64(&i);
i64 upper = big_int_to_i64(&j);
- bool lower_changed = false;
+ i64 actual_lower = lower;
i64 bits = MAX_BITS;
if (type->BitSet.underlying != nullptr) {
bits = 8*type_size_of(type->BitSet.underlying);
if (lower > 0) {
- lower = 0;
- lower_changed = true;
+ actual_lower = 0;
} else if (lower < 0) {
error(bs->elem, "bit_set does not allow a negative lower bound (%lld) when an underlying type is set", lower);
}
}
- i64 bits_required = upper-lower;
+ i64 bits_required = upper-actual_lower;
switch (be->op.kind) {
case Token_Ellipsis:
case Token_RangeFull:
@@ -959,7 +1004,7 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
break;
}
if (!is_valid) {
- if (lower_changed) {
+ 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);
} else {
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, bits_required);
@@ -1367,11 +1412,13 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
isize variadic_index = -1;
bool is_c_vararg = false;
auto variables = 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 = unparen_expr(p->type);
Type *type = nullptr;
@@ -1672,9 +1719,11 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
}
param = alloc_entity_const_param(scope, name->Ident.token, type, poly_const, is_type_polymorphic(type));
+ param->Constant.field_group_index = field_group_index;
} else {
param = alloc_entity_param(scope, name->Ident.token, type, is_using, true);
param->Variable.param_value = param_value;
+ param->Variable.field_group_index = field_group_index;
}
}
if (p->flags&FieldFlag_no_alias) {
@@ -1768,7 +1817,10 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
}
auto variables = array_make<Entity *>(permanent_allocator(), 0, variable_count);
+ i32 field_group_index = -1;
for_array(i, results) {
+ field_group_index += 1;
+
ast_node(field, Field, results[i]);
Ast *default_value = unparen_expr(field->default_value);
ParameterValue param_value = {};
@@ -1799,6 +1851,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
token.string = str_lit("");
Entity *param = alloc_entity_param(scope, token, type, false, false);
param->Variable.param_value = param_value;
+ param->Variable.field_group_index = -1;
array_add(&variables, param);
} else {
for_array(j, field->names) {
@@ -1822,6 +1875,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
Entity *param = alloc_entity_param(scope, token, type, false, false);
param->flags |= EntityFlag_Result;
param->Variable.param_value = param_value;
+ param->Variable.field_group_index = field_group_index;
array_add(&variables, param);
add_entity(ctx, scope, name, param);
// NOTE(bill): Removes `declared but not used` when using -vet
@@ -1883,6 +1937,25 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
c->scope->flags &= ~ScopeFlag_ContextDefined;
}
+ TargetArchKind arch = build_context.metrics.arch;
+ switch (cc) {
+ case ProcCC_StdCall:
+ case ProcCC_FastCall:
+ if (arch != TargetArch_i386 && arch != TargetArch_amd64) {
+ error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected either i386 or amd64, got %.*s",
+ proc_calling_convention_strings[cc], LIT(target_arch_names[arch]));
+ }
+ break;
+ case ProcCC_Win64:
+ case ProcCC_SysV:
+ if (arch != TargetArch_amd64) {
+ error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected amd64, got %.*s",
+ proc_calling_convention_strings[cc], LIT(target_arch_names[arch]));
+ }
+ break;
+ }
+
+
bool variadic = false;
isize variadic_index = -1;
bool success = true;
@@ -1896,20 +1969,6 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
if (params) param_count = params ->Tuple.variables.count;
if (results) result_count = results->Tuple.variables.count;
- if (param_count > 0) {
- for_array(i, params->Tuple.variables) {
- Entity *param = params->Tuple.variables[i];
- if (param->kind == Entity_Variable) {
- ParameterValue pv = param->Variable.param_value;
- if (pv.kind == ParameterValue_Constant &&
- pv.value.kind == ExactValue_Procedure) {
- type->Proc.has_proc_default_values = true;
- break;
- }
- }
- }
- }
-
if (result_count > 0) {
Entity *first = results->Tuple.variables[0];
type->Proc.has_named_results = first->token.string != "";
@@ -1967,10 +2026,14 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
if (param_count > 0) {
Entity *end = params->Tuple.variables[param_count-1];
if (end->flags&EntityFlag_CVarArg) {
- if (cc == ProcCC_StdCall || cc == ProcCC_CDecl) {
+ switch (cc) {
+ default:
type->Proc.c_vararg = true;
- } else {
+ break;
+ case ProcCC_Odin:
+ case ProcCC_Contextless:
error(end->token, "Calling convention does not support #c_vararg");
+ break;
}
}
}
@@ -2106,7 +2169,7 @@ void init_map_entry_type(Type *type) {
/*
struct {
- hash: runtime.Map_Hash,
+ hash: uintptr,
next: int,
key: Key,
value: Value,
@@ -2285,10 +2348,21 @@ void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) {
}
if (!is_type_valid_for_matrix_elems(elem)) {
+ if (elem == t_typeid) {
+ Entity *e = entity_of_node(mt->elem);
+ if (e && e->kind == Entity_TypeName && e->TypeName.is_type_alias) {
+ // HACK TODO(bill): This is to allow polymorphic parameters for matrix elements
+ // proc($T: typeid) -> matrix[2, 2]T
+ //
+ // THIS IS NEEDS TO BE FIXED AND NOT USE THIS HACK
+ goto type_assign;
+ }
+ }
gbString s = type_to_string(elem);
error(column.expr, "Matrix elements types are limited to integers, floats, and complex, got %s", s);
gb_string_free(s);
}
+type_assign:;
*type = alloc_type_matrix(elem, row_count, column_count, generic_row, generic_column);
@@ -2679,29 +2753,30 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, Token_Invalid);
- bool is_partial = false;
+ bool is_sparse = false;
if (at->tag != nullptr) {
GB_ASSERT(at->tag->kind == Ast_BasicDirective);
String name = at->tag->BasicDirective.name.string;
- if (name == "partial") {
- is_partial = true;
+ if (name == "sparse") {
+ is_sparse = true;
} else {
error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name));
}
}
- if (!is_partial && t->EnumeratedArray.count > bt->Enum.fields.count) {
+ 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 #partial to the enumerated array to allow for non-named elements\n");
+ 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 #partial is applied\n");
+ error_line("\t this warning will be removed if #sparse is applied\n");
}
}
+ t->EnumeratedArray.is_sparse = is_sparse;
*type = t;
@@ -2951,5 +3026,7 @@ Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) {
}
set_base_type(named_type, type);
+ check_rtti_type_disallowed(e, type, "Use of a type, %s, which has been disallowed");
+
return type;
}
diff --git a/src/checker.cpp b/src/checker.cpp
index 667146eda..1bb786ea1 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -4,7 +4,7 @@
void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr);
void add_comparison_procedures_for_fields(CheckerContext *c, Type *t);
-
+Type *check_type(CheckerContext *ctx, Ast *e);
bool is_operand_value(Operand o) {
switch (o.mode) {
@@ -29,6 +29,23 @@ bool is_operand_undef(Operand o) {
return o.mode == Addressing_Value && o.type == t_untyped_undef;
}
+bool check_rtti_type_disallowed(Token const &token, Type *type, char const *format) {
+ if (build_context.disallow_rtti && type) {
+ if (is_type_any(type)) {
+ gbString t = type_to_string(type);
+ error(token, format, t);
+ gb_string_free(t);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool check_rtti_type_disallowed(Ast *expr, Type *type, char const *format) {
+ GB_ASSERT(expr != nullptr);
+ return check_rtti_type_disallowed(ast_token(expr), type, format);
+}
+
void scope_reset(Scope *scope) {
if (scope == nullptr) return;
@@ -225,8 +242,8 @@ bool decl_info_has_init(DeclInfo *d) {
Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
Scope *s = gb_alloc_item(permanent_allocator(), Scope);
s->parent = parent;
- string_map_init(&s->elements, permanent_allocator(), init_elements_capacity);
- ptr_set_init(&s->imported, permanent_allocator(), 0);
+ string_map_init(&s->elements, heap_allocator(), init_elements_capacity);
+ ptr_set_init(&s->imported, heap_allocator(), 0);
mutex_init(&s->mutex);
if (parent != nullptr && parent != builtin_pkg->scope) {
@@ -446,7 +463,7 @@ Entity *scope_lookup(Scope *s, String const &name) {
-Entity *scope_insert_with_name(Scope *s, String const &name, Entity *entity) {
+Entity *scope_insert_with_name(Scope *s, String const &name, Entity *entity, bool use_mutex=true) {
if (name == "") {
return nullptr;
}
@@ -454,8 +471,8 @@ Entity *scope_insert_with_name(Scope *s, String const &name, Entity *entity) {
Entity **found = nullptr;
Entity *result = nullptr;
- mutex_lock(&s->mutex);
- defer (mutex_unlock(&s->mutex));
+ if (use_mutex) mutex_lock(&s->mutex);
+ defer (if (use_mutex) mutex_unlock(&s->mutex));
found = string_map_get(&s->elements, key);
@@ -485,9 +502,9 @@ end:;
return result;
}
-Entity *scope_insert(Scope *s, Entity *entity) {
+Entity *scope_insert(Scope *s, Entity *entity, bool use_mutex) {
String name = entity->token.string;
- return scope_insert_with_name(s, name, entity);
+ return scope_insert_with_name(s, name, entity, use_mutex);
}
@@ -504,6 +521,7 @@ enum VettedEntityKind {
VettedEntity_Unused,
VettedEntity_Shadowed,
+ VettedEntity_Shadowed_And_Unused,
};
struct VettedEntity {
VettedEntityKind kind;
@@ -622,15 +640,21 @@ void check_scope_usage(Checker *c, Scope *scope) {
Array<VettedEntity> vetted_entities = {};
array_init(&vetted_entities, heap_allocator());
- for_array(i, scope->elements.entries) {
+ MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
Entity *e = scope->elements.entries[i].value;
if (e == nullptr) continue;
- VettedEntity ve = {};
- if (vet_unused && check_vet_unused(c, e, &ve)) {
- array_add(&vetted_entities, ve);
- }
- if (vet_shadowing && check_vet_shadowing(c, e, &ve)) {
- array_add(&vetted_entities, ve);
+ VettedEntity ve_unused = {};
+ VettedEntity ve_shadowed = {};
+ bool is_unused = vet_unused && check_vet_unused(c, e, &ve_unused);
+ bool is_shadowed = vet_shadowing && check_vet_shadowing(c, e, &ve_shadowed);
+ if (is_unused && is_shadowed) {
+ VettedEntity ve_both = ve_shadowed;
+ ve_both.kind = VettedEntity_Shadowed_And_Unused;
+ array_add(&vetted_entities, ve_both);
+ } else if (is_unused) {
+ array_add(&vetted_entities, ve_unused);
+ } else if (is_shadowed) {
+ array_add(&vetted_entities, ve_shadowed);
}
}
@@ -642,16 +666,18 @@ void check_scope_usage(Checker *c, Scope *scope) {
Entity *other = ve.other;
String name = e->token.string;
- if (build_context.vet) {
+ if (ve.kind == VettedEntity_Shadowed_And_Unused) {
+ error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line);
+ } else if (build_context.vet) {
switch (ve.kind) {
case VettedEntity_Unused:
error(e->token, "'%.*s' declared but not used", LIT(name));
break;
case VettedEntity_Shadowed:
if (e->flags&EntityFlag_Using) {
- error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line);
+ error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %d", LIT(name), other->token.pos.line);
} else {
- error(e->token, "Declaration of '%.*s' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line);
+ error(e->token, "Declaration of '%.*s' shadows declaration at line %d", LIT(name), other->token.pos.line);
}
break;
default:
@@ -688,12 +714,17 @@ void add_dependency(CheckerInfo *info, DeclInfo *d, Entity *e) {
ptr_set_add(&d->deps, e);
mutex_unlock(&info->deps_mutex);
}
-void add_type_info_dependency(DeclInfo *d, Type *type) {
+void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *type, bool require_mutex) {
if (d == nullptr) {
return;
}
- // NOTE(bill): no mutex is required here because the only procedure calling it is wrapped in a mutex already
+ if (require_mutex) {
+ mutex_lock(&info->deps_mutex);
+ }
ptr_set_add(&d->type_info_deps, type);
+ if (require_mutex) {
+ mutex_unlock(&info->deps_mutex);
+ }
}
AstPackage *get_core_package(CheckerInfo *info, String name) {
@@ -719,12 +750,25 @@ void add_package_dependency(CheckerContext *c, char const *package_name, char co
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);
- e->flags |= EntityFlag_Used;
GB_ASSERT_MSG(e != nullptr, "%s", name);
GB_ASSERT(c->decl != nullptr);
+ e->flags |= EntityFlag_Used;
+ add_dependency(c->info, c->decl, e);
+}
+
+void try_to_add_package_dependency(CheckerContext *c, char const *package_name, char const *name) {
+ 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);
+ if (e == nullptr) {
+ return;
+ }
+ GB_ASSERT(c->decl != nullptr);
+ e->flags |= EntityFlag_Used;
add_dependency(c->info, c->decl, e);
}
+
void add_declaration_dependency(CheckerContext *c, Entity *e) {
if (e == nullptr) {
return;
@@ -780,6 +824,69 @@ AstPackage *create_builtin_package(char const *name) {
return pkg;
}
+struct GlobalEnumValue {
+ char const *name;
+ i64 value;
+};
+
+Slice<Entity *> add_global_enum_type(String const &type_name, GlobalEnumValue *values, isize value_count, Type **enum_type_ = nullptr) {
+ Scope *scope = create_scope(nullptr, builtin_pkg->scope);
+ Entity *entity = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved);
+
+ Type *enum_type = alloc_type_enum();
+ Type *named_type = alloc_type_named(type_name, enum_type, entity);
+ set_base_type(named_type, enum_type);
+ enum_type->Enum.base_type = t_int;
+ enum_type->Enum.scope = scope;
+ entity->type = named_type;
+
+ auto fields = array_make<Entity *>(permanent_allocator(), value_count);
+ for (isize i = 0; i < value_count; i++) {
+ i64 value = values[i].value;
+ Entity *e = alloc_entity_constant(scope, make_token_ident(values[i].name), named_type, exact_value_i64(value));
+ e->flags |= EntityFlag_Visited;
+ e->state = EntityState_Resolved;
+ fields[i] = e;
+
+ Entity *ie = scope_insert(scope, e);
+ GB_ASSERT(ie == nullptr);
+ }
+
+
+ enum_type->Enum.fields = fields;
+ enum_type->Enum.min_value_index = 0;
+ enum_type->Enum.max_value_index = value_count-1;
+ enum_type->Enum.min_value = &enum_type->Enum.fields[enum_type->Enum.min_value_index]->Constant.value;
+ enum_type->Enum.max_value = &enum_type->Enum.fields[enum_type->Enum.max_value_index]->Constant.value;
+
+
+ if (enum_type_) *enum_type_ = named_type;
+
+ return slice_from_array(fields);
+}
+void add_global_enum_constant(Slice<Entity *> const &fields, char const *name, i64 value) {
+ for (Entity *field : fields) {
+ GB_ASSERT(field->kind == Entity_Constant);
+ if (value == exact_value_to_i64(field->Constant.value)) {
+ add_global_constant(name, field->type, field->Constant.value);
+ return;
+ }
+ }
+ GB_PANIC("Unfound enum value for global constant: %s %lld", name, cast(long long)value);
+}
+
+Type *add_global_type_name(Scope *scope, String const &type_name, Type *backing_type) {
+ Entity *e = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved);
+ Type *named_type = alloc_type_named(type_name, backing_type, e);
+ e->type = named_type;
+ set_base_type(named_type, backing_type);
+ if (scope_insert(scope, e)) {
+ compiler_error("double declaration of %.*s", LIT(e->token.string));
+ }
+ return named_type;
+}
+
+
void init_universal(void) {
BuildContext *bc = &build_context;
@@ -789,7 +896,8 @@ void init_universal(void) {
// Types
for (isize i = 0; i < gb_count_of(basic_types); i++) {
- add_global_type_entity(basic_types[i].Basic.name, &basic_types[i]);
+ String const &name = basic_types[i].Basic.name;
+ add_global_type_entity(name, &basic_types[i]);
}
add_global_type_entity(str_lit("byte"), &basic_types[Basic_u8]);
@@ -808,14 +916,96 @@ void init_universal(void) {
add_global_bool_constant("false", false);
// TODO(bill): Set through flags in the compiler
- add_global_string_constant("ODIN_OS", bc->ODIN_OS);
- add_global_string_constant("ODIN_ARCH", bc->ODIN_ARCH);
- add_global_string_constant("ODIN_ENDIAN", bc->ODIN_ENDIAN);
add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR);
add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT);
+
+ {
+ GlobalEnumValue values[TargetOs_COUNT] = {
+ {"Unknown", TargetOs_Invalid},
+ {"Windows", TargetOs_windows},
+ {"Darwin", TargetOs_darwin},
+ {"Linux", TargetOs_linux},
+ {"Essence", TargetOs_essence},
+ {"FreeBSD", TargetOs_freebsd},
+ {"OpenBSD", TargetOs_openbsd},
+ {"WASI", TargetOs_wasi},
+ {"JS", TargetOs_js},
+ {"Freestanding", TargetOs_freestanding},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_OS_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_OS", bc->metrics.os);
+ add_global_string_constant("ODIN_OS_STRING", target_os_names[bc->metrics.os]);
+ }
+
+ {
+ GlobalEnumValue values[TargetArch_COUNT] = {
+ {"Unknown", TargetArch_Invalid},
+ {"amd64", TargetArch_amd64},
+ {"i386", TargetArch_i386},
+ {"arm64", TargetArch_arm64},
+ {"wasm32", TargetArch_wasm32},
+ {"wasm64", TargetArch_wasm64},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Arch_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_ARCH", bc->metrics.arch);
+ add_global_string_constant("ODIN_ARCH_STRING", target_arch_names[bc->metrics.arch]);
+ }
- add_global_string_constant("ODIN_BUILD_MODE", bc->ODIN_BUILD_MODE);
+ {
+ GlobalEnumValue values[BuildMode_COUNT] = {
+ {"Executable", BuildMode_Executable},
+ {"Dynamic", BuildMode_DynamicLibrary},
+ {"Object", BuildMode_Object},
+ {"Assembly", BuildMode_Assembly},
+ {"LLVM_IR", BuildMode_LLVM_IR},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Build_Mode_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_BUILD_MODE", bc->build_mode);
+ }
+
+ {
+ GlobalEnumValue values[TargetEndian_COUNT] = {
+ {"Unknown", TargetEndian_Invalid},
+
+ {"Little", TargetEndian_Little},
+ {"Big", TargetEndian_Big},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Endian_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_ENDIAN", target_endians[bc->metrics.arch]);
+ add_global_string_constant("ODIN_ENDIAN_STRING", target_endian_names[target_endians[bc->metrics.arch]]);
+ }
+
+ {
+ GlobalEnumValue values[ErrorPosStyle_COUNT] = {
+ {"Default", ErrorPosStyle_Default},
+ {"Unix", ErrorPosStyle_Unix},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Error_Pos_Style_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_ERROR_POS_STYLE", build_context.ODIN_ERROR_POS_STYLE);
+ }
+
+ {
+ GlobalEnumValue values[OdinAtomicMemoryOrder_COUNT] = {
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_relaxed], OdinAtomicMemoryOrder_relaxed},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_consume], OdinAtomicMemoryOrder_consume},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_acquire], OdinAtomicMemoryOrder_acquire},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_release], OdinAtomicMemoryOrder_release},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_acq_rel], OdinAtomicMemoryOrder_acq_rel},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_seq_cst], OdinAtomicMemoryOrder_seq_cst},
+ };
+
+ add_global_enum_type(str_lit("Atomic_Memory_Order"), values, gb_count_of(values), &t_atomic_memory_order);
+ GB_ASSERT(t_atomic_memory_order->kind == Type_Named);
+ 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);
@@ -823,6 +1013,9 @@ void init_universal(void) {
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_DISALLOW_RTTI", bc->disallow_rtti);
// Builtin Procedures
@@ -887,6 +1080,17 @@ void init_universal(void) {
t_f64_ptr = alloc_type_pointer(t_f64);
t_u8_slice = alloc_type_slice(t_u8);
t_string_slice = alloc_type_slice(t_string);
+
+ // 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_id = alloc_type_pointer(t_objc_object);
+ t_objc_SEL = alloc_type_pointer(t_objc_selector);
+ t_objc_Class = alloc_type_pointer(t_objc_class);
+ }
}
@@ -941,6 +1145,11 @@ void init_checker_info(CheckerInfo *i) {
mutex_init(&i->foreign_mutex);
semaphore_init(&i->collect_semaphore);
+
+ mpmc_init(&i->intrinsics_entry_point_usage, a, 1<<10); // just waste some memory here, even if it probably never used
+
+ mutex_init(&i->objc_types_mutex);
+ map_init(&i->objc_msgSend_types, a);
}
void destroy_checker_info(CheckerInfo *i) {
@@ -973,6 +1182,9 @@ void destroy_checker_info(CheckerInfo *i) {
mutex_destroy(&i->type_and_value_mutex);
mutex_destroy(&i->identifier_uses_mutex);
mutex_destroy(&i->foreign_mutex);
+
+ mutex_destroy(&i->objc_types_mutex);
+ map_destroy(&i->objc_msgSend_types);
}
CheckerContext make_checker_context(Checker *c) {
@@ -1177,7 +1389,7 @@ isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) {
// TODO(bill): This is O(n) and can be very slow
for_array(i, info->type_info_map.entries){
auto *e = &info->type_info_map.entries[i];
- if (are_types_identical(e->key, type)) {
+ if (are_types_identical_unique_tuples(e->key, type)) {
entry_index = e->value;
// NOTE(bill): Add it to the search map
map_set(&info->type_info_map, type, entry_index);
@@ -1228,7 +1440,7 @@ void add_type_and_value(CheckerInfo *i, Ast *expr, AddressingMode mode, Type *ty
while (prev_expr != expr) {
prev_expr = expr;
expr->tav.mode = mode;
- if (type != nullptr && expr->tav.type != nullptr &&
+ if (type != nullptr && expr->tav.type != nullptr &&
is_type_any(type) && is_type_untyped(expr->tav.type)) {
// ignore
} else {
@@ -1424,7 +1636,7 @@ bool could_entity_be_lazy(Entity *e, DeclInfo *d) {
return false;
} else if (name == "linkage") {
return false;
- }
+ }
}
}
}
@@ -1496,6 +1708,10 @@ void add_implicit_entity(CheckerContext *c, Ast *clause, Entity *e) {
void add_type_info_type(CheckerContext *c, Type *t) {
void add_type_info_type_internal(CheckerContext *c, Type *t);
+ if (build_context.disallow_rtti) {
+ return;
+ }
+
mutex_lock(&c->info->type_info_mutex);
add_type_info_type_internal(c, t);
mutex_unlock(&c->info->type_info_mutex);
@@ -1513,7 +1729,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
return;
}
- add_type_info_dependency(c->decl, t);
+ add_type_info_dependency(c->info, c->decl, t, false);
auto found = map_get(&c->info->type_info_map, t);
if (found != nullptr) {
@@ -1525,7 +1741,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
isize ti_index = -1;
for_array(i, c->info->type_info_map.entries) {
auto *e = &c->info->type_info_map.entries[i];
- if (are_types_identical(t, e->key)) {
+ if (are_types_identical_unique_tuples(t, e->key)) {
// Duplicate entry
ti_index = e->value;
prev = true;
@@ -1642,6 +1858,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
} else {
add_type_info_type_internal(c, t_type_info_ptr);
}
+ add_type_info_type_internal(c, bt->Union.polymorphic_params);
for_array(i, bt->Union.variants) {
add_type_info_type_internal(c, bt->Union.variants[i]);
}
@@ -1665,6 +1882,7 @@ 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);
@@ -1704,7 +1922,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->RelativeSlice.slice_type);
add_type_info_type_internal(c, bt->RelativeSlice.base_integer);
break;
-
+
case Type_Matrix:
add_type_info_type_internal(c, bt->Matrix.elem);
break;
@@ -1858,6 +2076,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
} else {
add_min_dep_type_info(c, t_type_info_ptr);
}
+ add_min_dep_type_info(c, bt->Union.polymorphic_params);
for_array(i, bt->Union.variants) {
add_min_dep_type_info(c, bt->Union.variants[i]);
}
@@ -1881,6 +2100,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
}
}
}
+ add_min_dep_type_info(c, bt->Struct.polymorphic_params);
for_array(i, bt->Struct.fields) {
Entity *f = bt->Struct.fields[i];
add_min_dep_type_info(c, f->type);
@@ -1919,7 +2139,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
add_min_dep_type_info(c, bt->RelativeSlice.slice_type);
add_min_dep_type_info(c, bt->RelativeSlice.base_integer);
break;
-
+
case Type_Matrix:
add_min_dep_type_info(c, bt->Matrix.elem);
break;
@@ -2004,23 +2224,27 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
ptr_set_init(&c->info.minimum_dependency_set, heap_allocator(), min_dep_set_cap);
ptr_set_init(&c->info.minimum_dependency_type_info_set, heap_allocator());
- String required_runtime_entities[] = {
+#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \
+ if (condition) { \
+ String entities[] = {__VA_ARGS__}; \
+ for (isize i = 0; i < gb_count_of(entities); i++) { \
+ force_add_dependency_entity(c, c->info.runtime_package->scope, entities[i]); \
+ } \
+ } \
+} while (0)
+
+ // required runtime entities
+ FORCE_ADD_RUNTIME_ENTITIES(true,
// Odin types
- str_lit("Type_Info"),
str_lit("Source_Code_Location"),
str_lit("Context"),
str_lit("Allocator"),
str_lit("Logger"),
- // Global variables
- str_lit("args__"),
- str_lit("type_table"),
-
// Odin internal procedures
str_lit("__init_context"),
- str_lit("__type_info_of"),
str_lit("cstring_to_string"),
- str_lit("_cleanup_runtime"),
+ str_lit("_cleanup_runtime"),
// Pseudo-CRT required procedures
str_lit("memset"),
@@ -2047,39 +2271,40 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
str_lit("gnu_h2f_ieee"),
str_lit("gnu_f2h_ieee"),
str_lit("extendhfsf2"),
-
+
// WASM Specific
str_lit("__ashlti3"),
str_lit("__multi3"),
- };
- for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) {
- force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]);
- }
+ );
- if (build_context.no_crt) {
- String required_no_crt_entities[] = {
- // NOTE(bill): Only if these exist
- str_lit("_tls_index"),
- str_lit("_fltused"),
- };
- for (isize i = 0; i < gb_count_of(required_no_crt_entities); i++) {
- force_add_dependency_entity(c, c->info.runtime_package->scope, required_no_crt_entities[i]);
- }
- }
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.disallow_rtti,
+ // Odin types
+ str_lit("Type_Info"),
- if (!build_context.no_bounds_check) {
- String bounds_check_entities[] = {
- // Bounds checking related procedures
- str_lit("bounds_check_error"),
- str_lit("matrix_bounds_check_error"),
- str_lit("slice_expr_error_hi"),
- str_lit("slice_expr_error_lo_hi"),
- str_lit("multi_pointer_slice_expr_error"),
- };
- for (isize i = 0; i < gb_count_of(bounds_check_entities); i++) {
- force_add_dependency_entity(c, c->info.runtime_package->scope, bounds_check_entities[i]);
- }
- }
+ // Global variables
+ str_lit("type_table"),
+ str_lit("__type_info_of"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_entry_point,
+ // Global variables
+ str_lit("args__"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES((build_context.no_crt && !is_arch_wasm()),
+ // NOTE(bill): Only if these exist
+ str_lit("_tls_index"),
+ str_lit("_fltused"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_bounds_check,
+ // Bounds checking related procedures
+ str_lit("bounds_check_error"),
+ str_lit("matrix_bounds_check_error"),
+ str_lit("slice_expr_error_hi"),
+ str_lit("slice_expr_error_lo_hi"),
+ str_lit("multi_pointer_slice_expr_error"),
+ );
for_array(i, c->info.definitions) {
Entity *e = c->info.definitions[i];
@@ -2110,34 +2335,38 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
case Entity_Variable:
if (e->Variable.is_export) {
add_dependency_to_set(c, e);
+ } else if (e->flags & EntityFlag_Require) {
+ add_dependency_to_set(c, e);
}
break;
case Entity_Procedure:
if (e->Procedure.is_export) {
add_dependency_to_set(c, e);
+ } else if (e->flags & EntityFlag_Require) {
+ add_dependency_to_set(c, e);
}
if (e->flags & EntityFlag_Init) {
Type *t = base_type(e->type);
GB_ASSERT(t->kind == Type_Proc);
-
+
bool is_init = true;
-
+
if (t->Proc.param_count != 0 || t->Proc.result_count != 0) {
gbString str = type_to_string(t);
error(e->token, "@(init) procedures must have a signature type with no parameters nor results, got %s", str);
gb_string_free(str);
is_init = false;
}
-
+
if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
error(e->token, "@(init) procedures must be declared at the file scope");
is_init = false;
}
-
+
if (is_init) {
add_dependency_to_set(c, e);
array_add(&c->info.init_procedures, e);
- }
+ }
}
break;
}
@@ -2197,6 +2426,8 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
start->flags |= EntityFlag_Used;
add_dependency_to_set(c, start);
}
+
+#undef FORCE_ADD_RUNTIME_ENTITIES
}
bool is_entity_a_dependency(Entity *e) {
@@ -2445,6 +2676,15 @@ Array<Entity *> proc_group_entities(CheckerContext *c, Operand o) {
return procs;
}
+Array<Entity *> proc_group_entities_cloned(CheckerContext *c, Operand o) {
+ auto entities = proc_group_entities(c, o);
+ if (entities.count == 0) {
+ return {};
+ }
+ return array_clone(permanent_allocator(), entities);
+}
+
+
void init_core_type_info(Checker *c) {
@@ -2604,6 +2844,14 @@ ExactValue check_decl_attribute_value(CheckerContext *c, Ast *value) {
return ev;
}
+Type *check_decl_attribute_type(CheckerContext *c, Ast *value) {
+ if (value != nullptr) {
+ return check_type(c, value);
+ }
+ return nullptr;
+}
+
+
#define ATTRIBUTE_USER_TAG_NAME "tag"
@@ -2903,6 +3151,42 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
error(elem, "Expected a string for '%.*s'", LIT(name));
}
return true;
+ } else if (name == "objc_name") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_String) {
+ if (string_is_valid_identifier(ev.value_string)) {
+ ac->objc_name = ev.value_string;
+ } else {
+ error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
+ }
+ } else {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_is_class_method") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_Bool) {
+ ac->objc_is_class_method = ev.value_bool;
+ } else {
+ error(elem, "Expected a boolean value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_type") {
+ if (value == nullptr) {
+ error(elem, "Expected a type for '%.*s'", LIT(name));
+ } else {
+ Type *objc_type = check_type(c, value);
+ if (objc_type != nullptr) {
+ if (!has_type_got_objc_class_attribute(objc_type)) {
+ gbString t = type_to_string(objc_type);
+ error(value, "'%.*s' expected a named type with the attribute @(obj_class=<string>), got type %s", LIT(name), t);
+ gb_string_free(t);
+ } else {
+ ac->objc_type = objc_type;
+ }
+ }
+ }
+ return true;
}
return false;
}
@@ -3053,6 +3337,14 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
} else if (name == "private") {
// NOTE(bill): Handled elsewhere `check_collect_value_decl`
return true;
+ } else if (name == "objc_class") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind != ExactValue_String || ev.value_string == "") {
+ error(elem, "Expected a non-empty string value for '%.*s'", LIT(name));
+ } else {
+ ac->objc_class = ev.value_string;
+ }
+ return true;
}
return false;
}
@@ -3366,6 +3658,16 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
}
}
+ if (entity_visibility_kind == EntityVisiblity_Public &&
+ (c->scope->flags&ScopeFlag_File) &&
+ c->scope->file) {
+ if (c->scope->file->flags & AstFile_IsPrivateFile) {
+ entity_visibility_kind = EntityVisiblity_PrivateToFile;
+ } else if (c->scope->file->flags & AstFile_IsPrivatePkg) {
+ entity_visibility_kind = EntityVisiblity_PrivateToPackage;
+ }
+ }
+
if (entity_visibility_kind != EntityVisiblity_Public && !(c->scope->flags&ScopeFlag_File)) {
error(decl, "Attribute 'private' is not allowed on a non file scope entity");
}
@@ -3454,9 +3756,6 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
if (is_ast_type(init)) {
e = alloc_entity_type_name(d->scope, token, nullptr);
- // if (vd->type != nullptr) {
- // error(name, "A type declaration cannot have an type parameter");
- // }
} else if (init->kind == Ast_ProcLit) {
if (c->scope->flags&ScopeFlag_Type) {
error(name, "Procedure declarations are not allowed within a struct");
@@ -3559,6 +3858,59 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
check_collect_entities(&c, block->stmts);
}
+bool correct_single_type_alias(CheckerContext *c, Entity *e) {
+ if (e->kind == Entity_Constant) {
+ DeclInfo *d = e->decl_info;
+ if (d != nullptr && d->init_expr != nullptr) {
+ Ast *init = d->init_expr;
+ Entity *alias_of = check_entity_from_ident_or_selector(c, init, true);
+ if (alias_of != nullptr && alias_of->kind == Entity_TypeName) {
+ e->kind = Entity_TypeName;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool correct_type_alias_in_scope_backwards(CheckerContext *c, Scope *s) {
+ isize n = s->elements.entries.count;
+ bool correction = false;
+ for (isize i = n-1; i >= 0; i--) {
+ correction |= correct_single_type_alias(c, s->elements.entries[i].value);
+ }
+ return correction;
+}
+bool correct_type_alias_in_scope_forwards(CheckerContext *c, Scope *s) {
+ isize n = s->elements.entries.count;
+ bool correction = false;
+ for (isize i = 0; i < n; i++) {
+ correction |= correct_single_type_alias(c, s->elements.entries[i].value);
+ }
+ return correction;
+}
+
+
+void correct_type_aliases_in_scope(CheckerContext *c, Scope *s) {
+ // NOTE(bill, 2022-02-04): This is used to solve the problem caused by type aliases
+ // of type aliases being "confused" as constants
+ //
+ // A :: C
+ // B :: A
+ // C :: struct {b: ^B}
+ //
+ // See @TypeAliasingProblem for more information
+ for (;;) {
+ bool corrections = false;
+ corrections |= correct_type_alias_in_scope_backwards(c, s);
+ corrections |= correct_type_alias_in_scope_forwards(c, s);
+ if (!corrections) {
+ return;
+ }
+ }
+}
+
+
// NOTE(bill): If file_scopes == nullptr, this will act like a local scope
void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes) {
AstFile *curr_file = nullptr;
@@ -3630,6 +3982,7 @@ void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes) {
}
}
+ // correct_type_aliases(c);
// NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something
// declared after this stmt in source
@@ -3677,11 +4030,6 @@ void check_single_global_entity(Checker *c, Entity *e, DeclInfo *d) {
error(e->token, "'main' is reserved as the entry point procedure in the initial scope");
return;
}
- } else if (pkg->kind == Package_Runtime) {
- if (e->token.string == "main") {
- error(e->token, "'main' is reserved as the entry point procedure in the initial scope");
- return;
- }
}
check_entity_decl(ctx, e, d, nullptr);
@@ -3841,7 +4189,7 @@ void add_import_dependency_node(Checker *c, Ast *decl, PtrMap<AstPackage *, Impo
}
Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c) {
- PtrMap<AstPackage *, ImportGraphNode *> M = {};
+ PtrMap<AstPackage *, ImportGraphNode *> M = {};
map_init(&M, heap_allocator(), 2*c->parser->packages.count);
defer (map_destroy(&M));
@@ -4121,11 +4469,11 @@ void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
mpmc_enqueue(&ctx->info->required_foreign_imports_through_force_queue, e);
add_entity_use(ctx, nullptr, e);
}
-
+
if (has_asm_extension(fullpath)) {
if (build_context.metrics.arch != TargetArch_amd64 ||
build_context.metrics.os != TargetOs_windows) {
- error(decl, "Assembly files are not yet supported on this platform: %.*s_%.*s",
+ error(decl, "Assembly files are not yet supported on this platform: %.*s_%.*s",
LIT(target_os_names[build_context.metrics.os]), LIT(target_arch_names[build_context.metrics.arch]));
}
}
@@ -4280,10 +4628,11 @@ bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls) {
for_array(i, decls) {
if (collect_file_decl(ctx, decls[i])) {
+ correct_type_aliases_in_scope(ctx, ctx->scope);
return true;
}
}
-
+ correct_type_aliases_in_scope(ctx, ctx->scope);
return false;
}
@@ -4327,7 +4676,7 @@ void check_with_workers(Checker *c, WorkerTaskProc *proc, isize total_count) {
if (!build_context.threaded_checker) {
worker_count = 0;
}
-
+
semaphore_post(&c->info.collect_semaphore, cast(i32)thread_count);
if (worker_count == 0) {
ThreadProcCheckerSection section_all = {};
@@ -4351,7 +4700,7 @@ void check_with_workers(Checker *c, WorkerTaskProc *proc, isize total_count) {
}
GB_ASSERT(remaining_count <= 0);
-
+
for (isize i = 0; i < thread_count; i++) {
global_thread_pool_add_task(proc, thread_data+i);
}
@@ -4553,6 +4902,15 @@ void check_import_entities(Checker *c) {
}
add_untyped_expressions(ctx.info, &untyped);
}
+
+ for_array(i, pkg->files) {
+ AstFile *f = pkg->files[i];
+ reset_checker_context(&ctx, f, &untyped);
+ ctx.collect_delayed_decls = false;
+
+ correct_type_aliases_in_scope(&ctx, pkg->scope);
+ }
+
for_array(i, pkg->files) {
AstFile *f = pkg->files[i];
reset_checker_context(&ctx, f, &untyped);
@@ -4750,11 +5108,11 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
ctx.decl = pi->decl;
ctx.procs_to_check_queue = procs_to_check_queue;
GB_ASSERT(procs_to_check_queue != nullptr);
-
+
GB_ASSERT(pi->type->kind == Type_Proc);
TypeProc *pt = &pi->type->Proc;
String name = pi->token.string;
-
+
if (pt->is_polymorphic && !pt->is_poly_specialized) {
Token token = pi->token;
if (pi->poly_def_node != nullptr) {
@@ -4774,6 +5132,9 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0;
bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0;
+ bool type_assert = (pi->tags & ProcTag_type_assert) != 0;
+ bool no_type_assert = (pi->tags & ProcTag_no_type_assert) != 0;
+
if (bounds_check) {
ctx.state_flags |= StateFlag_bounds_check;
ctx.state_flags &= ~StateFlag_no_bounds_check;
@@ -4781,6 +5142,15 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
ctx.state_flags |= StateFlag_no_bounds_check;
ctx.state_flags &= ~StateFlag_bounds_check;
}
+
+ if (type_assert) {
+ ctx.state_flags |= StateFlag_type_assert;
+ ctx.state_flags &= ~StateFlag_no_type_assert;
+ } else if (no_type_assert) {
+ ctx.state_flags |= StateFlag_no_type_assert;
+ ctx.state_flags &= ~StateFlag_type_assert;
+ }
+
if (pi->body != nullptr && e != nullptr) {
GB_ASSERT((e->flags & EntityFlag_ProcBodyChecked) == 0);
}
@@ -4832,7 +5202,7 @@ void check_unchecked_bodies(Checker *c) {
if (pi->body == nullptr) {
continue;
}
-
+
debugf("unchecked: %.*s\n", LIT(e->token.string));
mpmc_enqueue(&c->procs_to_check_queue, pi);
}
@@ -4941,17 +5311,16 @@ void check_procedure_bodies(Checker *c) {
if (!build_context.threaded_checker) {
worker_count = 0;
}
- worker_count = 0;
if (worker_count == 0) {
auto *this_queue = &c->procs_to_check_queue;
-
+
UntypedExprInfoMap untyped = {};
map_init(&untyped, heap_allocator());
-
+
for (ProcInfo *pi = nullptr; mpmc_dequeue(this_queue, &pi); /**/) {
consume_proc_info_queue(c, pi, this_queue, &untyped);
}
-
+
map_destroy(&untyped);
debugf("Total Procedure Bodies Checked: %td\n", total_bodies_checked.load(std::memory_order_relaxed));
@@ -4995,7 +5364,7 @@ void check_procedure_bodies(Checker *c) {
GB_ASSERT(total_queued == original_queue_count);
semaphore_post(&c->procs_to_check_semaphore, cast(i32)thread_count);
-
+
for (isize i = 0; i < thread_count; i++) {
global_thread_pool_add_task(thread_proc_body, thread_data+i);
}
@@ -5032,7 +5401,7 @@ void check_deferred_procedures(Checker *c) {
Entity *dst = src->Procedure.deferred_procedure.entity;
GB_ASSERT(dst != nullptr);
GB_ASSERT(dst->kind == Entity_Procedure);
-
+
char const *attribute = "deferred_none";
switch (dst_kind) {
case DeferredProcedure_none:
@@ -5195,12 +5564,18 @@ void check_unique_package_names(Checker *c) {
string_map_set(&pkgs, key, pkg);
continue;
}
+ auto *curr = pkg->files[0]->pkg_decl;
+ auto *prev = (*found)->files[0]->pkg_decl;
+ if (curr == prev) {
+ // NOTE(bill): A false positive was found, ignore it
+ continue;
+ }
- error(pkg->files[0]->pkg_decl, "Duplicate declaration of 'package %.*s'", LIT(name));
+ 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((*found)->files[0]->pkg_decl, "found at previous location");
+ error(prev, "found at previous location");
}
}
@@ -5233,7 +5608,7 @@ GB_COMPARE_PROC(init_procedures_cmp) {
cmp = 0;
return cmp;
}
-
+
if (x->pkg != y->pkg) {
isize order_x = x->pkg ? x->pkg->order : 0;
isize order_y = y->pkg ? y->pkg->order : 0;
@@ -5247,14 +5622,14 @@ GB_COMPARE_PROC(init_procedures_cmp) {
String fullpath_y = y->file ? y->file->fullpath : (String{});
String file_x = filename_from_path(fullpath_x);
String file_y = filename_from_path(fullpath_y);
-
+
cmp = string_compare(file_x, file_y);
if (cmp) {
return cmp;
}
}
-
+
cmp = u64_cmp(x->order_in_src, y->order_in_src);
if (cmp) {
return cmp;
@@ -5392,9 +5767,6 @@ void check_parsed_files(Checker *c) {
TIME_SECTION("calculate global init order");
calculate_global_init_order(c);
- TIME_SECTION("generate minimum dependency set");
- generate_minimum_dependency_set(c, c->info.entry_point);
-
TIME_SECTION("check test procedures");
check_test_procedures(c);
@@ -5405,6 +5777,9 @@ void check_parsed_files(Checker *c) {
add_type_info_for_type_definitions(c);
check_merge_queues_into_arrays(c);
+ TIME_SECTION("generate minimum dependency set");
+ generate_minimum_dependency_set(c, c->info.entry_point);
+
TIME_SECTION("check entry point");
if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) {
Scope *s = c->info.init_scope;
@@ -5434,9 +5809,21 @@ void check_parsed_files(Checker *c) {
TIME_SECTION("sanity checks");
GB_ASSERT(c->info.entity_queue.count.load(std::memory_order_relaxed) == 0);
GB_ASSERT(c->info.definition_queue.count.load(std::memory_order_relaxed) == 0);
-
+
TIME_SECTION("sort init procedures");
check_sort_init_procedures(c);
+ if (c->info.intrinsics_entry_point_usage.count > 0) {
+ TIME_SECTION("check intrinsics.__entry_point usage");
+ Ast *node = nullptr;
+ while (mpmc_dequeue(&c->info.intrinsics_entry_point_usage, &node)) {
+ if (c->info.entry_point == nullptr && node != nullptr) {
+ if (node->file()->pkg->kind != Package_Runtime) {
+ warning(node, "usage of intrinsics.__entry_point will be a no-op");
+ }
+ }
+ }
+ }
+
TIME_SECTION("type check finish");
}
diff --git a/src/checker.hpp b/src/checker.hpp
index 74435c1d4..552e6aca7 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -118,6 +118,11 @@ struct AttributeContext {
bool init : 1;
bool set_cold : 1;
u32 optimization_mode; // ProcedureOptimizationMode
+
+ String objc_class;
+ String objc_name;
+ bool objc_is_class_method;
+ Type * objc_type;
};
AttributeContext make_attribute_context(String link_prefix) {
@@ -267,6 +272,17 @@ struct UntypedExprInfo {
typedef PtrMap<Ast *, ExprInfo *> UntypedExprInfoMap;
typedef MPMCQueue<ProcInfo *> ProcBodyQueue;
+enum ObjcMsgKind : u32 {
+ ObjcMsg_normal,
+ ObjcMsg_fpret,
+ ObjcMsg_fp2ret,
+ ObjcMsg_stret,
+};
+struct ObjcMsgData {
+ ObjcMsgKind kind;
+ Type *proc_type;
+};
+
// CheckerInfo stores all the symbol information for a type-checked program
struct CheckerInfo {
Checker *checker;
@@ -338,6 +354,10 @@ struct CheckerInfo {
MPMCQueue<Entity *> required_global_variable_queue;
MPMCQueue<Entity *> required_foreign_imports_through_force_queue;
+ MPMCQueue<Ast *> intrinsics_entry_point_usage;
+
+ BlockingMutex objc_types_mutex;
+ PtrMap<Ast *, ObjcMsgData> objc_msgSend_types;
};
struct CheckerContext {
@@ -423,7 +443,7 @@ Entity *entity_of_node(Ast *expr);
Entity *scope_lookup_current(Scope *s, String const &name);
Entity *scope_lookup (Scope *s, String const &name);
void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_);
-Entity *scope_insert (Scope *s, Entity *entity);
+Entity *scope_insert (Scope *s, Entity *entity, bool use_mutex=true);
void add_type_and_value (CheckerInfo *i, Ast *expression, AddressingMode mode, Type *type, ExactValue value);
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index abd9fc6ca..0f72f01f7 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -86,77 +86,31 @@ enum BuiltinProcId {
BuiltinProc_prefetch_write_instruction,
BuiltinProc_prefetch_write_data,
- BuiltinProc_atomic_fence,
- BuiltinProc_atomic_fence_acq,
- BuiltinProc_atomic_fence_rel,
- BuiltinProc_atomic_fence_acqrel,
-
+ BuiltinProc_atomic_type_is_lock_free,
+ BuiltinProc_atomic_thread_fence,
+ BuiltinProc_atomic_signal_fence,
BuiltinProc_atomic_store,
- BuiltinProc_atomic_store_rel,
- BuiltinProc_atomic_store_relaxed,
- BuiltinProc_atomic_store_unordered,
-
+ BuiltinProc_atomic_store_explicit,
BuiltinProc_atomic_load,
- BuiltinProc_atomic_load_acq,
- BuiltinProc_atomic_load_relaxed,
- BuiltinProc_atomic_load_unordered,
-
+ BuiltinProc_atomic_load_explicit,
BuiltinProc_atomic_add,
- BuiltinProc_atomic_add_acq,
- BuiltinProc_atomic_add_rel,
- BuiltinProc_atomic_add_acqrel,
- BuiltinProc_atomic_add_relaxed,
+ BuiltinProc_atomic_add_explicit,
BuiltinProc_atomic_sub,
- BuiltinProc_atomic_sub_acq,
- BuiltinProc_atomic_sub_rel,
- BuiltinProc_atomic_sub_acqrel,
- BuiltinProc_atomic_sub_relaxed,
+ BuiltinProc_atomic_sub_explicit,
BuiltinProc_atomic_and,
- BuiltinProc_atomic_and_acq,
- BuiltinProc_atomic_and_rel,
- BuiltinProc_atomic_and_acqrel,
- BuiltinProc_atomic_and_relaxed,
+ BuiltinProc_atomic_and_explicit,
BuiltinProc_atomic_nand,
- BuiltinProc_atomic_nand_acq,
- BuiltinProc_atomic_nand_rel,
- BuiltinProc_atomic_nand_acqrel,
- BuiltinProc_atomic_nand_relaxed,
+ BuiltinProc_atomic_nand_explicit,
BuiltinProc_atomic_or,
- BuiltinProc_atomic_or_acq,
- BuiltinProc_atomic_or_rel,
- BuiltinProc_atomic_or_acqrel,
- BuiltinProc_atomic_or_relaxed,
+ BuiltinProc_atomic_or_explicit,
BuiltinProc_atomic_xor,
- BuiltinProc_atomic_xor_acq,
- BuiltinProc_atomic_xor_rel,
- BuiltinProc_atomic_xor_acqrel,
- BuiltinProc_atomic_xor_relaxed,
-
- BuiltinProc_atomic_xchg,
- BuiltinProc_atomic_xchg_acq,
- BuiltinProc_atomic_xchg_rel,
- BuiltinProc_atomic_xchg_acqrel,
- BuiltinProc_atomic_xchg_relaxed,
-
- BuiltinProc_atomic_cxchg,
- BuiltinProc_atomic_cxchg_acq,
- BuiltinProc_atomic_cxchg_rel,
- BuiltinProc_atomic_cxchg_acqrel,
- BuiltinProc_atomic_cxchg_relaxed,
- BuiltinProc_atomic_cxchg_failrelaxed,
- BuiltinProc_atomic_cxchg_failacq,
- BuiltinProc_atomic_cxchg_acq_failrelaxed,
- BuiltinProc_atomic_cxchg_acqrel_failrelaxed,
-
- BuiltinProc_atomic_cxchgweak,
- BuiltinProc_atomic_cxchgweak_acq,
- BuiltinProc_atomic_cxchgweak_rel,
- BuiltinProc_atomic_cxchgweak_acqrel,
- BuiltinProc_atomic_cxchgweak_relaxed,
- BuiltinProc_atomic_cxchgweak_failrelaxed,
- BuiltinProc_atomic_cxchgweak_failacq,
- BuiltinProc_atomic_cxchgweak_acq_failrelaxed,
- BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed,
+ BuiltinProc_atomic_xor_explicit,
+ BuiltinProc_atomic_exchange,
+ BuiltinProc_atomic_exchange_explicit,
+ BuiltinProc_atomic_compare_exchange_strong,
+ BuiltinProc_atomic_compare_exchange_strong_explicit,
+ BuiltinProc_atomic_compare_exchange_weak,
+ BuiltinProc_atomic_compare_exchange_weak_explicit,
BuiltinProc_fixed_point_mul,
BuiltinProc_fixed_point_div,
@@ -213,8 +167,6 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_union,
BuiltinProc_type_is_enum,
BuiltinProc_type_is_proc,
- BuiltinProc_type_is_bit_field,
- BuiltinProc_type_is_bit_field_value,
BuiltinProc_type_is_bit_set,
BuiltinProc_type_is_simd_vector,
BuiltinProc_type_is_matrix,
@@ -227,6 +179,7 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_has_field,
+ BuiltinProc_type_field_type,
BuiltinProc_type_is_specialization_of,
@@ -243,6 +196,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_polymorphic_record_parameter_count,
BuiltinProc_type_polymorphic_record_parameter_value,
+ BuiltinProc_type_is_subtype_of,
+
BuiltinProc_type_field_index_of,
BuiltinProc_type_equal_proc,
@@ -250,6 +205,19 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc__type_end,
+ BuiltinProc___entry_point,
+
+ BuiltinProc_objc_send,
+ BuiltinProc_objc_find_selector,
+ BuiltinProc_objc_find_class,
+ BuiltinProc_objc_register_selector,
+ BuiltinProc_objc_register_class,
+
+ BuiltinProc_constant_utf16_cstring,
+
+ BuiltinProc_wasm_memory_grow,
+ BuiltinProc_wasm_memory_size,
+
BuiltinProc_COUNT,
};
gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
@@ -339,78 +307,31 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("prefetch_write_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_write_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
+ {STR_LIT("atomic_type_is_lock_free"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_thread_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_signal_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_store_explicit"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_load_explicit"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_add_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_sub_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_and_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_nand_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_or_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_xor_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_exchange"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_exchange_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_compare_exchange_strong"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_compare_exchange_strong_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_compare_exchange_weak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_compare_exchange_weak_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_mul"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_div"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -464,8 +385,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_union"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_enum"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("type_is_bit_field"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("type_is_bit_field_value"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_bit_set"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_matrix"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -477,6 +396,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("type_has_field"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_field_type"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_specialization_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -493,10 +413,27 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_polymorphic_record_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_polymorphic_record_parameter_value"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_is_subtype_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
{STR_LIT("type_field_index_of"), 2, 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(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("objc_find_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("wasm_memory_grow"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("wasm_memory_size"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
};
diff --git a/src/common.cpp b/src/common.cpp
index ab2a46118..94248fb62 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -675,262 +675,7 @@ wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
#endif
-
-#if defined(GB_SYSTEM_WINDOWS)
- 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
- 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
-
-
-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 res;
-}
-
-
-
-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,
-};
-
-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)
-ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
- GB_ASSERT(fi != nullptr);
-
- gbAllocator a = heap_allocator();
-
- 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(a, path);
- defer (gb_free(a, c_str));
-
- 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;
- }
-
-
- 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;
- i64 size = cast(i64)file_data.nFileSizeLow;
- size |= (cast(i64)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 = 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)
-
-#include <dirent.h>
-
-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
-
-
+#include "path.cpp"
struct LoadedFile {
void *handle;
@@ -1021,7 +766,7 @@ LoadedFileError load_file_32(char const *fullpath, LoadedFile *memory_mapped_fil
#endif
}
- gbFileContents fc = gb_file_read_contents(heap_allocator(), true, fullpath);
+ gbFileContents fc = gb_file_read_contents(permanent_allocator(), true, fullpath);
if (fc.size > I32_MAX) {
err = LoadedFile_FileTooLarge;
diff --git a/src/common_memory.cpp b/src/common_memory.cpp
index 2d7a7a246..953462077 100644
--- a/src/common_memory.cpp
+++ b/src/common_memory.cpp
@@ -139,6 +139,7 @@ struct PlatformMemoryBlock {
};
+gb_global std::atomic<isize> global_platform_memory_total_usage;
gb_global PlatformMemoryBlock global_platform_memory_block_sentinel;
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size);
@@ -158,10 +159,17 @@ void platform_virtual_memory_protect(void *memory, isize size);
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)VirtualAlloc(0, total_size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
- GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
+ if (pmblock == nullptr) {
+ gb_printf_err("Out of Virtual memory, oh no...\n");
+ gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size);
+ gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage);
+ GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
+ }
+ global_platform_memory_total_usage += total_size;
return pmblock;
}
void platform_virtual_memory_free(PlatformMemoryBlock *block) {
+ global_platform_memory_total_usage -= block->total_size;
GB_ASSERT(VirtualFree(block, 0, MEM_RELEASE));
}
void platform_virtual_memory_protect(void *memory, isize size) {
@@ -180,11 +188,18 @@ void platform_virtual_memory_protect(void *memory, isize size);
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)mmap(nullptr, total_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
- GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
+ if (pmblock == nullptr) {
+ gb_printf_err("Out of Virtual memory, oh no...\n");
+ gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size);
+ gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage);
+ GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
+ }
+ global_platform_memory_total_usage += total_size;
return pmblock;
}
void platform_virtual_memory_free(PlatformMemoryBlock *block) {
isize size = block->total_size;
+ global_platform_memory_total_usage -= size;
munmap(block, size);
}
void platform_virtual_memory_protect(void *memory, isize size) {
@@ -325,18 +340,32 @@ GB_ALLOCATOR_PROC(heap_allocator_proc) {
// TODO(bill): Throughly test!
switch (type) {
#if defined(GB_COMPILER_MSVC)
- case gbAllocation_Alloc: {
- isize aligned_size = align_formula_isize(size, alignment);
- // TODO(bill): Make sure this is aligned correctly
- ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, aligned_size);
- } break;
+ case gbAllocation_Alloc:
+ if (size == 0) {
+ return NULL;
+ } else {
+ isize aligned_size = align_formula_isize(size, alignment);
+ // TODO(bill): Make sure this is aligned correctly
+ ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, aligned_size);
+ }
+ break;
case gbAllocation_Free:
- HeapFree(GetProcessHeap(), 0, old_memory);
+ if (old_memory != nullptr) {
+ HeapFree(GetProcessHeap(), 0, old_memory);
+ }
+ break;
+ case gbAllocation_Resize:
+ if (old_memory != nullptr && size > 0) {
+ isize aligned_size = align_formula_isize(size, alignment);
+ ptr = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, old_memory, aligned_size);
+ } else if (old_memory != nullptr) {
+ HeapFree(GetProcessHeap(), 0, old_memory);
+ } else if (size != 0) {
+ isize aligned_size = align_formula_isize(size, alignment);
+ // TODO(bill): Make sure this is aligned correctly
+ ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, aligned_size);
+ }
break;
- case gbAllocation_Resize: {
- isize aligned_size = align_formula_isize(size, alignment);
- ptr = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, old_memory, aligned_size);
- } break;
#elif defined(GB_SYSTEM_LINUX)
// TODO(bill): *nix version that's decent
case gbAllocation_Alloc: {
diff --git a/src/docs.cpp b/src/docs.cpp
index 8d65cb83a..3ea3cce1b 100644
--- a/src/docs.cpp
+++ b/src/docs.cpp
@@ -67,6 +67,14 @@ GB_COMPARE_PROC(cmp_ast_package_by_name) {
#include "docs_format.cpp"
#include "docs_writer.cpp"
+void print_doc_line(i32 indent, String const &data) {
+ while (indent --> 0) {
+ gb_printf("\t");
+ }
+ gb_file_write(gb_file_get_standard(gbFileStandard_Output), data.text, data.len);
+ gb_printf("\n");
+}
+
void print_doc_line(i32 indent, char const *fmt, ...) {
while (indent --> 0) {
gb_printf("\t");
@@ -86,6 +94,13 @@ void print_doc_line_no_newline(i32 indent, char const *fmt, ...) {
gb_printf_va(fmt, va);
va_end(va);
}
+void print_doc_line_no_newline(i32 indent, String const &data) {
+ while (indent --> 0) {
+ gb_printf("\t");
+ }
+ gb_file_write(gb_file_get_standard(gbFileStandard_Output), data.text, data.len);
+}
+
bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
if (g == nullptr) {
@@ -106,8 +121,9 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
String comment = g->list[i].string;
String original_comment = comment;
- bool slash_slash = comment[1] == '/';
+ bool slash_slash = false;
if (comment[1] == '/') {
+ slash_slash = true;
comment.text += 2;
comment.len -= 2;
} else if (comment[1] == '*') {
@@ -131,7 +147,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
}
if (slash_slash) {
- print_doc_line(indent, "%.*s", LIT(comment));
+ print_doc_line(indent, comment);
count += 1;
} else {
isize pos = 0;
@@ -143,7 +159,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
}
}
String line = substring(comment, pos, end);
- pos = end+1;
+ pos = end;
String trimmed_line = string_trim_whitespace(line);
if (trimmed_line.len == 0) {
if (count == 0) {
@@ -159,7 +175,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
line = substring(line, 2, line.len);
}
- print_doc_line(indent, "%.*s", LIT(line));
+ print_doc_line(indent, line);
count += 1;
}
}
@@ -263,7 +279,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
}
GB_ASSERT(type_expr != nullptr || init_expr != nullptr);
- print_doc_line_no_newline(2, "%.*s", LIT(e->token.string));
+ print_doc_line_no_newline(2, e->token.string);
if (type_expr != nullptr) {
gbString t = expr_to_string(type_expr);
gb_printf(": %s ", t);
@@ -298,7 +314,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
for_array(i, pkg->files) {
AstFile *f = pkg->files[i];
String filename = remove_directory_from_path(f->fullpath);
- print_doc_line(2, "%.*s", LIT(filename));
+ print_doc_line(2, filename);
}
}
diff --git a/src/docs_format.cpp b/src/docs_format.cpp
index 1c3af6257..ee32d0e05 100644
--- a/src/docs_format.cpp
+++ b/src/docs_format.cpp
@@ -15,7 +15,7 @@ struct OdinDocVersionType {
#define OdinDocVersionType_Major 0
#define OdinDocVersionType_Minor 2
-#define OdinDocVersionType_Patch 1
+#define OdinDocVersionType_Patch 4
struct OdinDocHeaderBase {
u8 magic[8];
@@ -99,6 +99,7 @@ enum OdinDocTypeFlag_Union : u32 {
OdinDocTypeFlag_Union_polymorphic = 1<<0,
OdinDocTypeFlag_Union_no_nil = 1<<1,
OdinDocTypeFlag_Union_maybe = 1<<2,
+ OdinDocTypeFlag_Union_shared_nil = 1<<3,
};
enum OdinDocTypeFlag_Proc : u32 {
@@ -137,6 +138,7 @@ struct OdinDocType {
OdinDocArray<OdinDocEntityIndex> entities;
OdinDocTypeIndex polmorphic_params;
OdinDocArray<OdinDocString> where_clauses;
+ OdinDocArray<OdinDocString> tags; // struct field tags
};
struct OdinDocAttribute {
@@ -153,6 +155,7 @@ enum OdinDocEntityKind : u32 {
OdinDocEntity_ProcGroup = 5,
OdinDocEntity_ImportName = 6,
OdinDocEntity_LibraryName = 7,
+ OdinDocEntity_Builtin = 8,
};
enum OdinDocEntityFlag : u64 {
@@ -169,8 +172,13 @@ enum OdinDocEntityFlag : u64 {
OdinDocEntityFlag_Type_Alias = 1ull<<20,
+ OdinDocEntityFlag_Builtin_Pkg_Builtin = 1ull<<30,
+ OdinDocEntityFlag_Builtin_Pkg_Intrinsics = 1ull<<31,
+
OdinDocEntityFlag_Var_Thread_Local = 1ull<<40,
OdinDocEntityFlag_Var_Static = 1ull<<41,
+
+ OdinDocEntityFlag_Private = 1ull<<50,
};
struct OdinDocEntity {
@@ -182,8 +190,9 @@ struct OdinDocEntity {
OdinDocTypeIndex type;
OdinDocString init_string;
u32 reserved_for_init;
- OdinDocString comment;
- OdinDocString docs;
+ OdinDocString comment; // line comment
+ OdinDocString docs; // preceding comment
+ i32 field_group_index;
OdinDocEntityIndex foreign_library;
OdinDocString link_name;
OdinDocArray<OdinDocAttribute> attributes;
@@ -197,15 +206,21 @@ enum OdinDocPkgFlags : u32 {
OdinDocPkgFlag_Init = 1<<2,
};
+struct OdinDocScopeEntry {
+ OdinDocString name;
+ OdinDocEntityIndex entity;
+};
+
struct OdinDocPkg {
OdinDocString fullpath;
OdinDocString name;
u32 flags;
OdinDocString docs;
- OdinDocArray<OdinDocFileIndex> files;
- OdinDocArray<OdinDocEntityIndex> entities;
+ OdinDocArray<OdinDocFileIndex> files;
+ OdinDocArray<OdinDocScopeEntry> entries;
};
+
struct OdinDocHeader {
OdinDocHeaderBase base;
diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp
index e8e8892ec..0ad10ac49 100644
--- a/src/docs_writer.cpp
+++ b/src/docs_writer.cpp
@@ -292,8 +292,9 @@ bool odin_doc_append_comment_group_string(Array<u8> *buf, CommentGroup *g) {
String comment = g->list[i].string;
String original_comment = comment;
- bool slash_slash = comment[1] == '/';
+ bool slash_slash = false;
if (comment[1] == '/') {
+ slash_slash = true;
comment.text += 2;
comment.len -= 2;
} else if (comment[1] == '*') {
@@ -330,7 +331,7 @@ bool odin_doc_append_comment_group_string(Array<u8> *buf, CommentGroup *g) {
}
}
String line = substring(comment, pos, end);
- pos = end+1;
+ pos = end;
String trimmed_line = string_trim_whitespace(line);
if (trimmed_line.len == 0) {
if (count == 0) {
@@ -482,7 +483,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
for_array(i, w->type_cache.entries) {
// NOTE(bill): THIS IS SLOW
Type *other = w->type_cache.entries[i].key;
- if (are_types_identical(type, other)) {
+ if (are_types_identical_unique_tuples(type, other)) {
OdinDocTypeIndex index = w->type_cache.entries[i].value;
map_set(&w->type_cache, type, index);
return index;
@@ -511,10 +512,16 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
doc_type.entities = odin_doc_add_entity_as_slice(w, type->Named.type_name);
break;
case Type_Generic:
- doc_type.kind = OdinDocType_Generic;
- doc_type.name = odin_doc_write_string(w, type->Generic.name);
- if (type->Generic.specialized) {
- doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized);
+ {
+ String name = type->Generic.name;
+ if (type->Generic.entity) {
+ name = type->Generic.entity->token.string;
+ }
+ doc_type.kind = OdinDocType_Generic;
+ doc_type.name = odin_doc_write_string(w, name);
+ if (type->Generic.specialized) {
+ doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized);
+ }
}
break;
case Type_Pointer:
@@ -598,14 +605,25 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
}
doc_type.where_clauses = odin_doc_where_clauses(w, st->where_clauses);
}
+
+ auto tags = array_make<OdinDocString>(heap_allocator(), type->Struct.fields.count);
+ defer (array_free(&tags));
+
+ for_array(i, type->Struct.fields) {
+ tags[i] = odin_doc_write_string(w, type->Struct.tags[i]);
+ }
+
+ doc_type.tags = odin_write_slice(w, tags.data, tags.count);
}
break;
case Type_Union:
doc_type.kind = OdinDocType_Union;
if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; }
- if (type->Union.no_nil) { doc_type.flags |= OdinDocTypeFlag_Union_no_nil; }
- if (type->Union.maybe) { doc_type.flags |= OdinDocTypeFlag_Union_maybe; }
-
+ switch (type->Union.kind) {
+ case UnionType_maybe: doc_type.flags |= OdinDocTypeFlag_Union_maybe; break;
+ case UnionType_no_nil: doc_type.flags |= OdinDocTypeFlag_Union_no_nil; break;
+ case UnionType_shared_nil: doc_type.flags |= OdinDocTypeFlag_Union_shared_nil; break;
+ }
{
auto variants = array_make<OdinDocTypeIndex>(heap_allocator(), type->Union.variants.count);
defer (array_free(&variants));
@@ -667,40 +685,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
types[1] = odin_doc_type(w, type->Proc.results);
doc_type.types = odin_write_slice(w, types, gb_count_of(types));
- String calling_convention = {};
- switch (type->Proc.calling_convention) {
- case ProcCC_Invalid:
- // no need
- break;
- case ProcCC_Odin:
- if (default_calling_convention() != ProcCC_Odin) {
- calling_convention = str_lit("odin");
- }
- break;
- case ProcCC_Contextless:
- if (default_calling_convention() != ProcCC_Contextless) {
- calling_convention = str_lit("contextless");
- }
- break;
- case ProcCC_CDecl:
- calling_convention = str_lit("cdecl");
- break;
- case ProcCC_StdCall:
- calling_convention = str_lit("stdcall");
- break;
- case ProcCC_FastCall:
- calling_convention = str_lit("fastcall");
- break;
- case ProcCC_None:
- calling_convention = str_lit("none");
- break;
- case ProcCC_Naked:
- calling_convention = str_lit("naked");
- break;
- case ProcCC_InlineAsm:
- calling_convention = str_lit("inline-assembly");
- break;
- }
+ String calling_convention = make_string_c(proc_calling_convention_strings[type->Proc.calling_convention]);
doc_type.calling_convention = odin_doc_write_string(w, calling_convention);
}
break;
@@ -795,11 +780,21 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
comment = e->decl_info->comment;
docs = e->decl_info->docs;
}
+ if (e->kind == Entity_Variable) {
+ if (!comment) { comment = e->Variable.comment; }
+ if (!docs) { docs = e->Variable.docs; }
+ } else if (e->kind == Entity_Constant) {
+ if (!comment) { comment = e->Constant.comment; }
+ if (!docs) { docs = e->Constant.docs; }
+ }
+ String name = e->token.string;
String link_name = {};
+ TokenPos pos = e->token.pos;
OdinDocEntityKind kind = OdinDocEntity_Invalid;
- u32 flags = 0;
+ u64 flags = 0;
+ i32 field_group_index = -1;
switch (e->kind) {
case Entity_Invalid: kind = OdinDocEntity_Invalid; break;
@@ -810,6 +805,7 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
case Entity_ProcGroup: kind = OdinDocEntity_ProcGroup; break;
case Entity_ImportName: kind = OdinDocEntity_ImportName; break;
case Entity_LibraryName: kind = OdinDocEntity_LibraryName; break;
+ case Entity_Builtin: kind = OdinDocEntity_Builtin; break;
}
switch (e->kind) {
@@ -829,12 +825,33 @@ 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;
+ break;
+ case Entity_Constant:
+ field_group_index = e->Constant.field_group_index;
break;
case Entity_Procedure:
if (e->Procedure.is_foreign) { flags |= OdinDocEntityFlag_Foreign; }
if (e->Procedure.is_export) { flags |= OdinDocEntityFlag_Export; }
link_name = e->Procedure.link_name;
break;
+ case Entity_Builtin:
+ {
+ auto bp = builtin_procs[e->Builtin.id];
+ pos = {};
+ name = bp.name;
+ switch (bp.pkg) {
+ case BuiltinProcPkg_builtin:
+ flags |= OdinDocEntityFlag_Builtin_Pkg_Builtin;
+ break;
+ case BuiltinProcPkg_intrinsics:
+ flags |= OdinDocEntityFlag_Builtin_Pkg_Intrinsics;
+ break;
+ default:
+ GB_PANIC("Unhandled BuiltinProcPkg");
+ }
+ }
+ break;
}
if (e->flags & EntityFlag_Param) {
@@ -845,6 +862,9 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
}
+ if (e->scope && (e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) && !is_entity_exported(e)) {
+ flags |= OdinDocEntityFlag_Private;
+ }
OdinDocString init_string = {};
if (init_expr) {
@@ -867,12 +887,13 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
doc_entity.kind = kind;
doc_entity.flags = flags;
- doc_entity.pos = odin_doc_token_pos_cast(w, e->token.pos);
- doc_entity.name = odin_doc_write_string(w, e->token.string);
+ doc_entity.pos = odin_doc_token_pos_cast(w, pos);
+ doc_entity.name = odin_doc_write_string(w, name);
doc_entity.type = 0; // Set later
doc_entity.init_string = init_string;
doc_entity.comment = odin_doc_comment_group_string(w, comment);
doc_entity.docs = odin_doc_comment_group_string(w, docs);
+ doc_entity.field_group_index = field_group_index;
doc_entity.foreign_library = 0; // Set later
doc_entity.link_name = odin_doc_write_string(w, link_name);
if (e->decl_info != nullptr) {
@@ -944,7 +965,7 @@ void odin_doc_update_entities(OdinDocWriter *w) {
-OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, AstPackage *pkg) {
+OdinDocArray<OdinDocScopeEntry> odin_doc_add_pkg_entries(OdinDocWriter *w, AstPackage *pkg) {
if (pkg->scope == nullptr) {
return {};
}
@@ -952,14 +973,14 @@ OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, Ast
return {};
}
- auto entities = array_make<Entity *>(heap_allocator(), 0, pkg->scope->elements.entries.count);
- defer (array_free(&entities));
+ auto entries = array_make<OdinDocScopeEntry>(heap_allocator(), 0, w->entity_cache.entries.count);
+ defer (array_free(&entries));
for_array(i, pkg->scope->elements.entries) {
+ String name = pkg->scope->elements.entries[i].key.string;
Entity *e = pkg->scope->elements.entries[i].value;
switch (e->kind) {
case Entity_Invalid:
- case Entity_Builtin:
case Entity_Nil:
case Entity_Label:
continue;
@@ -970,34 +991,27 @@ OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, Ast
case Entity_ProcGroup:
case Entity_ImportName:
case Entity_LibraryName:
+ case Entity_Builtin:
// Fine
break;
}
- array_add(&entities, e);
- }
- gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
-
- auto entity_indices = array_make<OdinDocEntityIndex>(heap_allocator(), 0, w->entity_cache.entries.count);
- defer (array_free(&entity_indices));
-
- for_array(i, entities) {
- Entity *e = entities[i];
if (e->pkg != pkg) {
continue;
}
- if (!is_entity_exported(e)) {
+ if (!is_entity_exported(e, true)) {
continue;
}
if (e->token.string.len == 0) {
continue;
}
- OdinDocEntityIndex doc_entity_index = 0;
- doc_entity_index = odin_doc_add_entity(w, e);
- array_add(&entity_indices, doc_entity_index);
+ OdinDocScopeEntry entry = {};
+ entry.name = odin_doc_write_string(w, name);
+ entry.entity = odin_doc_add_entity(w, e);
+ array_add(&entries, entry);
}
- return odin_write_slice(w, entity_indices.data, entity_indices.count);
+ return odin_write_slice(w, entries.data, entries.count);
}
@@ -1065,7 +1079,7 @@ void odin_doc_write_docs(OdinDocWriter *w) {
}
doc_pkg.files = odin_write_slice(w, file_indices.data, file_indices.count);
- doc_pkg.entities = odin_doc_add_pkg_entities(w, pkg);
+ doc_pkg.entries = odin_doc_add_pkg_entries(w, pkg);
if (dst) {
*dst = doc_pkg;
diff --git a/src/entity.cpp b/src/entity.cpp
index b39ffc63a..1f87f7af6 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -45,9 +45,9 @@ enum EntityFlag : u64 {
EntityFlag_NoAlias = 1ull<<9,
EntityFlag_TypeField = 1ull<<10,
EntityFlag_Value = 1ull<<11,
- EntityFlag_Sret = 1ull<<12,
- EntityFlag_ByVal = 1ull<<13,
- EntityFlag_BitFieldValue = 1ull<<14,
+
+
+
EntityFlag_PolyConst = 1ull<<15,
EntityFlag_NotExported = 1ull<<16,
EntityFlag_ConstInput = 1ull<<17,
@@ -74,6 +74,7 @@ enum EntityFlag : u64 {
EntityFlag_Test = 1ull<<30,
EntityFlag_Init = 1ull<<31,
+ EntityFlag_Subtype = 1ull<<32,
EntityFlag_CustomLinkName = 1ull<<40,
EntityFlag_CustomLinkage_Internal = 1ull<<41,
@@ -86,6 +87,10 @@ enum EntityFlag : u64 {
EntityFlag_Overridden = 1ull<<63,
};
+enum : u64 {
+ EntityFlags_IsSubtype = EntityFlag_Using|EntityFlag_Subtype,
+};
+
enum EntityState : u32 {
EntityState_Unresolved = 0,
EntityState_InProgress = 1,
@@ -110,6 +115,16 @@ struct ParameterValue {
};
};
+bool has_parameter_value(ParameterValue const &param_value) {
+ if (param_value.kind != ParameterValue_Invalid) {
+ return true;
+ }
+ if (param_value.original_ast_expr != nullptr) {
+ return true;
+ }
+ return false;
+}
+
enum EntityConstantFlags : u32 {
EntityConstantFlag_ImplicitEnumValue = 1<<0,
};
@@ -122,6 +137,28 @@ enum ProcedureOptimizationMode : u32 {
ProcedureOptimizationMode_Speed,
};
+
+BlockingMutex global_type_name_objc_metadata_mutex;
+
+struct TypeNameObjCMetadataEntry {
+ String name;
+ Entity *entity;
+};
+struct TypeNameObjCMetadata {
+ BlockingMutex *mutex;
+ Array<TypeNameObjCMetadataEntry> type_entries;
+ Array<TypeNameObjCMetadataEntry> value_entries;
+};
+
+TypeNameObjCMetadata *create_type_name_obj_c_metadata() {
+ TypeNameObjCMetadata *md = gb_alloc_item(permanent_allocator(), TypeNameObjCMetadata);
+ md->mutex = gb_alloc_item(permanent_allocator(), BlockingMutex);
+ mutex_init(md->mutex);
+ array_init(&md->type_entries, heap_allocator());
+ array_init(&md->value_entries, heap_allocator());
+ return md;
+}
+
// An Entity is a named "thing" in the language
struct Entity {
EntityKind kind;
@@ -160,10 +197,14 @@ struct Entity {
ExactValue value;
ParameterValue param_value;
u32 flags;
+ i32 field_group_index;
+ CommentGroup *docs;
+ CommentGroup *comment;
} Constant;
struct {
Ast *init_expr; // only used for some variables within procedure bodies
i32 field_index;
+ i32 field_group_index;
ParameterValue param_value;
@@ -173,6 +214,8 @@ struct Entity {
String link_name;
String link_prefix;
String link_section;
+ CommentGroup *docs;
+ CommentGroup *comment;
bool is_foreign;
bool is_export;
} Variable;
@@ -180,6 +223,8 @@ struct Entity {
Type * type_parameter_specialization;
String ir_mangled_name;
bool is_type_alias;
+ String objc_class_name;
+ TypeNameObjCMetadata *objc_metadata;
} TypeName;
struct {
u64 tags;
@@ -239,7 +284,7 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) {
if (e->flags & EntityFlag_NotExported) {
return false;
}
- if (e->file != nullptr && (e->file->flags & AstFile_IsPrivate) != 0) {
+ if (e->file != nullptr && (e->file->flags & (AstFile_IsPrivatePkg|AstFile_IsPrivateFile)) != 0) {
return false;
}
diff --git a/src/error.cpp b/src/error.cpp
new file mode 100644
index 000000000..faf4d11fb
--- /dev/null
+++ b/src/error.cpp
@@ -0,0 +1,416 @@
+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;
+
+ Array<u8> error_buffer;
+ Array<String> errors;
+};
+
+gb_global ErrorCollector global_error_collector;
+
+#define MAX_ERROR_COLLECTOR_COUNT (36)
+
+
+bool any_errors(void) {
+ return global_error_collector.count.load() != 0;
+}
+
+void init_global_error_collector(void) {
+ mutex_init(&global_error_collector.mutex);
+ mutex_init(&global_error_collector.block_mutex);
+ mutex_init(&global_error_collector.error_out_mutex);
+ mutex_init(&global_error_collector.string_mutex);
+ array_init(&global_error_collector.errors, heap_allocator());
+ array_init(&global_error_collector.error_buffer, heap_allocator());
+ array_init(&global_file_path_strings, heap_allocator(), 1, 4096);
+ array_init(&global_files, heap_allocator(), 1, 4096);
+}
+
+
+// temporary
+// defined in build_settings.cpp
+char *token_pos_to_string(TokenPos const &pos);
+
+bool set_file_path_string(i32 index, String const &path) {
+ bool ok = false;
+ GB_ASSERT(index >= 0);
+ mutex_lock(&global_error_collector.string_mutex);
+
+ if (index >= global_file_path_strings.count) {
+ array_resize(&global_file_path_strings, index+1);
+ }
+ String prev = global_file_path_strings[index];
+ if (prev.len == 0) {
+ global_file_path_strings[index] = path;
+ ok = true;
+ }
+
+ mutex_unlock(&global_error_collector.string_mutex);
+ return ok;
+}
+
+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);
+
+ if (index >= global_files.count) {
+ array_resize(&global_files, index+1);
+ }
+ AstFile *prev = global_files[index];
+ if (prev == nullptr) {
+ global_files[index] = file;
+ ok = true;
+ }
+
+ mutex_unlock(&global_error_collector.string_mutex);
+ return ok;
+}
+
+String get_file_path_string(i32 index) {
+ GB_ASSERT(index >= 0);
+ mutex_lock(&global_error_collector.string_mutex);
+
+ String path = {};
+ if (index < global_file_path_strings.count) {
+ path = global_file_path_strings[index];
+ }
+
+ mutex_unlock(&global_error_collector.string_mutex);
+ return path;
+}
+
+AstFile *thread_safe_get_ast_file_from_id(i32 index) {
+ GB_ASSERT(index >= 0);
+ mutex_lock(&global_error_collector.string_mutex);
+
+ AstFile *file = nullptr;
+ if (index < global_files.count) {
+ file = global_files[index];
+ }
+
+ mutex_unlock(&global_error_collector.string_mutex);
+ return file;
+}
+
+
+
+void begin_error_block(void) {
+ mutex_lock(&global_error_collector.block_mutex);
+ global_error_collector.in_block.store(true);
+}
+
+void end_error_block(void) {
+ if (global_error_collector.error_buffer.count > 0) {
+ isize n = global_error_collector.error_buffer.count;
+ u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1);
+ gb_memmove(text, global_error_collector.error_buffer.data, n);
+ text[n] = 0;
+ String s = {text, n};
+ array_add(&global_error_collector.errors, s);
+ global_error_collector.error_buffer.count = 0;
+ }
+
+ global_error_collector.in_block.store(false);
+ mutex_unlock(&global_error_collector.block_mutex);
+}
+
+#define ERROR_BLOCK() begin_error_block(); defer (end_error_block())
+
+
+#define ERROR_OUT_PROC(name) void name(char const *fmt, va_list va)
+typedef ERROR_OUT_PROC(ErrorOutProc);
+
+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) {
+ 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;
+ } 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);
+
+ }
+ gb_file_write(f, buf, n);
+}
+
+
+ErrorOutProc *error_out_va = default_error_out_va;
+
+// NOTE: defined in build_settings.cpp
+bool global_warnings_as_errors(void);
+bool global_ignore_warnings(void);
+bool show_error_line(void);
+gbString get_file_line_as_string(TokenPos const &pos, i32 *offset);
+
+void error_out(char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ error_out_va(fmt, va);
+ va_end(va);
+}
+
+
+bool show_error_on_line(TokenPos const &pos, TokenPos end) {
+ if (!show_error_line()) {
+ return false;
+ }
+
+ i32 offset = 0;
+ gbString the_line = get_file_line_as_string(pos, &offset);
+ defer (gb_string_free(the_line));
+
+ if (the_line != nullptr) {
+ String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line));
+
+ // TODO(bill): This assumes ASCII
+
+ enum {
+ MAX_LINE_LENGTH = 76,
+ MAX_TAB_WIDTH = 8,
+ ELLIPSIS_PADDING = 8
+ };
+
+ error_out("\n\t");
+ if (line.len+MAX_TAB_WIDTH+ELLIPSIS_PADDING > MAX_LINE_LENGTH) {
+ i32 const half_width = MAX_LINE_LENGTH/2;
+ i32 left = cast(i32)(offset);
+ i32 right = cast(i32)(line.len - offset);
+ left = gb_min(left, half_width);
+ right = gb_min(right, half_width);
+
+ line.text += offset-left;
+ line.len -= offset+right-left;
+
+ line = string_trim_whitespace(line);
+
+ offset = left + ELLIPSIS_PADDING/2;
+
+ error_out("... %.*s ...", LIT(line));
+ } else {
+ error_out("%.*s", LIT(line));
+ }
+ error_out("\n\t");
+
+ for (i32 i = 0; i < offset; i++) {
+ error_out(" ");
+ }
+ error_out("^");
+ if (end.file_id == pos.file_id) {
+ if (end.line > pos.line) {
+ for (i32 i = offset; i < line.len; i++) {
+ error_out("~");
+ }
+ } else if (end.line == pos.line && end.column > pos.column) {
+ i32 length = gb_min(end.offset - pos.offset, cast(i32)(line.len-offset));
+ for (i32 i = 1; i < length-1; i++) {
+ error_out("~");
+ }
+ if (length > 1) {
+ error_out("^");
+ }
+ }
+ }
+
+ error_out("\n\n");
+ return true;
+ }
+ return false;
+}
+
+void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+ global_error_collector.count.fetch_add(1);
+
+ mutex_lock(&global_error_collector.mutex);
+ // NOTE(bill): Duplicate error, skip it
+ if (pos.line == 0) {
+ error_out("Error: %s\n", gb_bprintf_va(fmt, va));
+ } else if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s %s\n",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ show_error_on_line(pos, end);
+ }
+ mutex_unlock(&global_error_collector.mutex);
+ if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
+ gb_exit(1);
+ }
+}
+
+void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+ if (global_warnings_as_errors()) {
+ error_va(pos, end, fmt, va);
+ return;
+ }
+ global_error_collector.warning_count.fetch_add(1);
+ mutex_lock(&global_error_collector.mutex);
+ if (!global_ignore_warnings()) {
+ // NOTE(bill): Duplicate error, skip it
+ if (pos.line == 0) {
+ error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
+ } else if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s Warning: %s\n",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ show_error_on_line(pos, end);
+ }
+ }
+ mutex_unlock(&global_error_collector.mutex);
+}
+
+
+void error_line_va(char const *fmt, va_list va) {
+ error_out_va(fmt, va);
+}
+
+void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
+ mutex_lock(&global_error_collector.mutex);
+ global_error_collector.count++;
+ // NOTE(bill): Duplicate error, skip it
+ if (pos.line == 0) {
+ error_out("Error: %s", gb_bprintf_va(fmt, va));
+ } else if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s %s",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ }
+ mutex_unlock(&global_error_collector.mutex);
+ if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
+ gb_exit(1);
+ }
+}
+
+
+void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+ mutex_lock(&global_error_collector.mutex);
+ global_error_collector.count++;
+ // NOTE(bill): Duplicate error, skip it
+ if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s Syntax Error: %s\n",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ show_error_on_line(pos, end);
+ } else if (pos.line == 0) {
+ error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va));
+ }
+
+ mutex_unlock(&global_error_collector.mutex);
+ if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
+ gb_exit(1);
+ }
+}
+
+void syntax_warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+ if (global_warnings_as_errors()) {
+ syntax_error_va(pos, end, fmt, va);
+ return;
+ }
+ mutex_lock(&global_error_collector.mutex);
+ global_error_collector.warning_count++;
+ if (!global_ignore_warnings()) {
+ // NOTE(bill): Duplicate error, skip it
+ if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s Syntax Warning: %s\n",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ show_error_on_line(pos, end);
+ } else if (pos.line == 0) {
+ error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
+ }
+ }
+ mutex_unlock(&global_error_collector.mutex);
+}
+
+
+
+void warning(Token const &token, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ warning_va(token.pos, {}, fmt, va);
+ va_end(va);
+}
+
+void error(Token const &token, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ error_va(token.pos, {}, fmt, va);
+ va_end(va);
+}
+
+void error(TokenPos pos, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ Token token = {};
+ token.pos = pos;
+ error_va(pos, {}, fmt, va);
+ va_end(va);
+}
+
+void error_line(char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ error_line_va(fmt, va);
+ va_end(va);
+}
+
+
+void syntax_error(Token const &token, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ syntax_error_va(token.pos, {}, fmt, va);
+ va_end(va);
+}
+
+void syntax_error(TokenPos pos, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ syntax_error_va(pos, {}, fmt, va);
+ va_end(va);
+}
+
+void syntax_warning(Token const &token, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ syntax_warning_va(token.pos, {}, fmt, va);
+ va_end(va);
+}
+
+
+void compiler_error(char const *fmt, ...) {
+ va_list va;
+
+ va_start(va, fmt);
+ gb_printf_err("Internal Compiler Error: %s\n",
+ gb_bprintf_va(fmt, va));
+ va_end(va);
+ GB_DEBUG_TRAP();
+ gb_exit(1);
+}
+
+
+
+
diff --git a/src/exact_value.cpp b/src/exact_value.cpp
index fd90278e5..cedef48c4 100644
--- a/src/exact_value.cpp
+++ b/src/exact_value.cpp
@@ -50,9 +50,9 @@ struct ExactValue {
union {
bool value_bool;
String value_string;
- BigInt value_integer; // NOTE(bill): This must be an integer and not a pointer
+ BigInt value_integer;
f64 value_float;
- i64 value_pointer;
+ i64 value_pointer; // NOTE(bill): This must be an integer and not a pointer
Complex128 *value_complex;
Quaternion256 *value_quaternion;
Ast * value_compound;
@@ -591,6 +591,7 @@ failure:
i32 exact_value_order(ExactValue const &v) {
switch (v.kind) {
case ExactValue_Invalid:
+ case ExactValue_Compound:
return 0;
case ExactValue_Bool:
case ExactValue_String:
@@ -607,8 +608,6 @@ i32 exact_value_order(ExactValue const &v) {
return 6;
case ExactValue_Procedure:
return 7;
- // case ExactValue_Compound:
- // return 8;
default:
GB_PANIC("How'd you get here? Invalid Value.kind %d", v.kind);
@@ -630,6 +629,9 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
case ExactValue_Bool:
case ExactValue_String:
case ExactValue_Quaternion:
+ case ExactValue_Pointer:
+ case ExactValue_Procedure:
+ case ExactValue_Typeid:
return;
case ExactValue_Integer:
@@ -671,9 +673,6 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
return;
}
break;
-
- case ExactValue_Procedure:
- return;
}
compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
@@ -932,6 +931,17 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
break;
}
+ case ExactValue_Pointer: {
+ switch (op) {
+ case Token_CmpEq: return x.value_pointer == y.value_pointer;
+ case Token_NotEq: return x.value_pointer != y.value_pointer;
+ case Token_Lt: return x.value_pointer < y.value_pointer;
+ case Token_LtEq: return x.value_pointer <= y.value_pointer;
+ case Token_Gt: return x.value_pointer > y.value_pointer;
+ case Token_GtEq: return x.value_pointer >= y.value_pointer;
+ }
+ }
+
case ExactValue_Typeid:
switch (op) {
case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid);
diff --git a/src/gb/gb.h b/src/gb/gb.h
index f716b0840..3b2d6434c 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -79,6 +79,10 @@ extern "C" {
#ifndef GB_SYSTEM_FREEBSD
#define GB_SYSTEM_FREEBSD 1
#endif
+ #elif defined(__OpenBSD__)
+ #ifndef GB_SYSTEM_OPENBSD
+ #define GB_SYSTEM_OPENBSD 1
+ #endif
#else
#error This UNIX operating system is not supported
#endif
@@ -199,7 +203,7 @@ extern "C" {
#endif
#include <stdlib.h> // NOTE(bill): malloc on linux
#include <sys/mman.h>
- #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__)
+ #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
#include <sys/sendfile.h>
#endif
#include <sys/stat.h>
@@ -235,6 +239,12 @@ extern "C" {
#define sendfile(out, in, offset, count) sendfile(out, in, offset, count, NULL, NULL, 0)
#endif
+#if defined(GB_SYSTEM_OPENBSD)
+ #include <stdio.h>
+ #include <pthread_np.h>
+ #define lseek64 lseek
+#endif
+
#if defined(GB_SYSTEM_UNIX)
#include <semaphore.h>
#endif
@@ -783,6 +793,13 @@ typedef struct gbAffinity {
isize thread_count;
isize threads_per_core;
} gbAffinity;
+#elif defined(GB_SYSTEM_OPENBSD)
+typedef struct gbAffinity {
+ b32 is_accurate;
+ isize core_count;
+ isize thread_count;
+ isize threads_per_core;
+} gbAffinity;
#else
#error TODO(bill): Unknown system
#endif
@@ -3355,6 +3372,8 @@ gb_inline u32 gb_thread_current_id(void) {
__asm__("mov %%gs:0x08,%0" : "=r"(thread_id));
#elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
+#elif defined(GB_SYSTEM_LINUX)
+ thread_id = gettid();
#else
#error Unsupported architecture for gb_thread_current_id()
#endif
@@ -3680,6 +3699,30 @@ 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_OPENBSD)
+#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
@@ -6023,7 +6066,7 @@ gbFileTime gb_file_last_write_time(char const *filepath) {
gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists) {
#if defined(GB_SYSTEM_OSX)
return copyfile(existing_filename, new_filename, NULL, COPYFILE_DATA) == 0;
-#else
+#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_FREEBSD)
isize size;
int existing_fd = open(existing_filename, O_RDONLY, 0);
int new_fd = open(new_filename, O_WRONLY|O_CREAT, 0666);
@@ -6040,6 +6083,49 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena
close(existing_fd);
return size == stat_existing.st_size;
+#else
+ int new_flags = O_WRONLY | O_CREAT;
+ if (fail_if_exists) {
+ new_flags |= O_EXCL;
+ }
+ int existing_fd = open(existing_filename, O_RDONLY, 0);
+ int new_fd = open(new_filename, new_flags, 0666);
+
+ struct stat stat_existing;
+ if (fstat(existing_fd, &stat_existing) == -1) {
+ return 0;
+ }
+
+ size_t bsize = stat_existing.st_blksize > BUFSIZ ? stat_existing.st_blksize : BUFSIZ;
+ char *buf = (char *)malloc(bsize);
+ if (buf == NULL) {
+ close(new_fd);
+ close(existing_fd);
+ return 0;
+ }
+
+ isize size = 0;
+ ssize_t nread, nwrite, offset;
+ while ((nread = read(existing_fd, buf, bsize)) != -1 && nread != 0) {
+ for (offset = 0; nread; nread -= nwrite, offset += nwrite) {
+ if ((nwrite = write(new_fd, buf + offset, nread)) == -1 || nwrite == 0) {
+ free(buf);
+ close(new_fd);
+ close(existing_fd);
+ return 0;
+ }
+ size += nwrite;
+ }
+ }
+
+ free(buf);
+ close(new_fd);
+ close(existing_fd);
+
+ if (nread == -1) {
+ return 0;
+ }
+ return size == stat_existing.st_size;
#endif
}
@@ -6091,6 +6177,7 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con
}
void gb_file_free_contents(gbFileContents *fc) {
+ if (fc == NULL || fc->size == 0) return;
GB_ASSERT_NOT_NULL(fc->data);
gb_free(fc->allocator, fc->data);
fc->data = NULL;
@@ -6186,20 +6273,44 @@ char *gb_path_get_full_name(gbAllocator a, char const *path) {
#else
char *p, *result, *fullpath = NULL;
isize len;
- p = realpath(path, NULL);
- fullpath = p;
- if (p == NULL) {
- // NOTE(bill): File does not exist
- fullpath = cast(char *)path;
- }
+ fullpath = realpath(path, NULL);
+
+ if (fullpath == NULL) {
+ // NOTE(Jeroen): Path doesn't exist.
+ if (gb_strlen(path) > 0 && path[0] == '/') {
+ // But it is an absolute path, so return as-is.
- len = gb_strlen(fullpath);
+ fullpath = (char *)path;
+ len = gb_strlen(fullpath) + 1;
+ result = gb_alloc_array(a, char, len + 1);
- result = gb_alloc_array(a, char, len + 1);
- gb_memmove(result, fullpath, len);
- result[len] = 0;
- free(p);
+ gb_memmove(result, fullpath, len);
+ result[len] = 0;
+ } else {
+ // Appears to be a relative path, so construct an absolute one relative to <cwd>.
+ char cwd[4096];
+ getcwd(&cwd[0], 4096);
+
+ 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);
+
+ gb_memmove(result, (void *)cwd, cwd_len);
+ result[cwd_len] = '/';
+
+ gb_memmove(result + cwd_len + 1, (void *)path, gb_strlen(path));
+ result[len] = 0;
+
+ }
+ } else {
+ len = gb_strlen(fullpath) + 1;
+ result = gb_alloc_array(a, char, len + 1);
+ gb_memmove(result, fullpath, len);
+ result[len] = 0;
+ free(fullpath);
+ }
return result;
#endif
}
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp
index e18dc344b..07d2dd6e3 100644
--- a/src/llvm_abi.cpp
+++ b/src/llvm_abi.cpp
@@ -965,6 +965,10 @@ namespace lbAbiArm64 {
}
return false;
}
+
+ unsigned is_homogenous_aggregate_small_enough(LLVMTypeRef *base_type_, unsigned member_count_) {
+ return (member_count_ <= 4);
+ }
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef type, bool return_is_defined) {
LLVMTypeRef homo_base_type = {};
@@ -975,22 +979,31 @@ namespace lbAbiArm64 {
} else if (is_register(type)) {
return non_struct(c, type);
} else if (is_homogenous_aggregate(c, type, &homo_base_type, &homo_member_count)) {
- return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
+ if(is_homogenous_aggregate_small_enough(&homo_base_type, homo_member_count)) {
+ return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
+ } else {
+ //TODO(Platin): do i need to create stuff that can handle the diffrent return type?
+ // else this needs a fix in llvm_backend_proc as we would need to cast it to the correct array type
+
+ //LLVMTypeRef array_type = LLVMArrayType(homo_base_type, homo_member_count);
+ LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", type);
+ return lb_arg_type_indirect(type, attr);
+ }
} else {
i64 size = lb_sizeof(type);
if (size <= 16) {
LLVMTypeRef cast_type = nullptr;
if (size <= 1) {
- cast_type = LLVMIntTypeInContext(c, 8);
+ cast_type = LLVMInt8TypeInContext(c);
} else if (size <= 2) {
- cast_type = LLVMIntTypeInContext(c, 16);
+ cast_type = LLVMInt16TypeInContext(c);
} else if (size <= 4) {
- cast_type = LLVMIntTypeInContext(c, 32);
+ cast_type = LLVMInt32TypeInContext(c);
} else if (size <= 8) {
- cast_type = LLVMIntTypeInContext(c, 64);
+ cast_type = LLVMInt64TypeInContext(c);
} else {
unsigned count = cast(unsigned)((size+7)/8);
- cast_type = LLVMArrayType(LLVMIntTypeInContext(c, 64), count);
+ cast_type = LLVMArrayType(LLVMInt64TypeInContext(c), count);
}
return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
} else {
@@ -999,7 +1012,7 @@ namespace lbAbiArm64 {
}
}
}
-
+
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
auto args = array_make<lbArgType>(heap_allocator(), arg_count);
@@ -1171,16 +1184,24 @@ LB_ABI_INFO(lb_get_abi_info) {
ft->calling_convention = calling_convention;
return ft;
}
+ case ProcCC_Win64:
+ GB_ASSERT(build_context.metrics.arch == TargetArch_amd64);
+ return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ case ProcCC_SysV:
+ GB_ASSERT(build_context.metrics.arch == TargetArch_amd64);
+ return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
}
switch (build_context.metrics.arch) {
case TargetArch_amd64:
- if (build_context.metrics.os == TargetOs_windows) {
+ if (build_context.metrics.os == TargetOs_windows || build_context.metrics.abi == TargetABI_Win64) {
return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ } else if (build_context.metrics.abi == TargetABI_SysV) {
+ return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
} else {
return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
}
- case TargetArch_386:
+ case TargetArch_i386:
return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
case TargetArch_arm64:
return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index 5acd2a80f..7781997f7 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -624,6 +624,9 @@ struct lbGlobalVariable {
};
lbProcedure *lb_create_startup_type_info(lbModule *m) {
+ if (build_context.disallow_rtti) {
+ return nullptr;
+ }
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod);
lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level);
LLVMFinalizeFunctionPassManager(default_function_pass_manager);
@@ -652,7 +655,54 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) {
return p;
}
-lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
+lbProcedure *lb_create_objc_names(lbModule *main_module) {
+ if (build_context.metrics.os != TargetOs_darwin) {
+ return nullptr;
+ }
+ Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
+ lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit("__$init_objc_names"), proc_type);
+ p->is_startup = true;
+ return p;
+}
+
+void lb_finalize_objc_names(lbProcedure *p) {
+ if (p == nullptr) {
+ return;
+ }
+ lbModule *m = p->module;
+
+ LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod);
+ lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level);
+ LLVMFinalizeFunctionPassManager(default_function_pass_manager);
+
+
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
+
+ LLVMSetLinkage(p->value, LLVMInternalLinkage);
+ lb_begin_procedure_body(p);
+ for_array(i, m->objc_classes.entries) {
+ auto const &entry = m->objc_classes.entries[i];
+ String name = entry.key.string;
+ args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
+ lbValue ptr = lb_emit_runtime_call(p, "objc_lookUpClass", args);
+ lb_addr_store(p, entry.value, ptr);
+ }
+
+ for_array(i, m->objc_selectors.entries) {
+ auto const &entry = m->objc_selectors.entries[i];
+ String name = entry.key.string;
+ args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
+ lbValue ptr = lb_emit_runtime_call(p, "sel_registerName", args);
+ lb_addr_store(p, entry.value, ptr);
+ }
+
+ lb_end_procedure_body(p);
+
+ lb_run_function_pass_manager(default_function_pass_manager, p);
+
+}
+
+lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(main_module->mod);
lb_populate_function_pass_manager(main_module, default_function_pass_manager, false, build_context.optimization_level);
LLVMFinalizeFunctionPassManager(default_function_pass_manager);
@@ -664,7 +714,13 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start
lb_begin_procedure_body(p);
- LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, "");
+ if (startup_type_info) {
+ LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, "");
+ }
+
+ if (objc_names) {
+ LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, objc_names->type)), objc_names->value, nullptr, 0, "");
+ }
for_array(i, global_variables) {
auto *var = &global_variables[i];
@@ -783,7 +839,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("fdwReason"), t_u32, false, true);
params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true);
call_cleanup = false;
- } else if (build_context.metrics.os == TargetOs_windows && (build_context.metrics.arch == TargetArch_386 || build_context.no_crt)) {
+ } else if (build_context.metrics.os == TargetOs_windows && (build_context.metrics.arch == TargetArch_i386 || build_context.no_crt)) {
name = str_lit("mainCRTStartup");
} else if (is_arch_wasm()) {
name = str_lit("_start");
@@ -873,7 +929,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
} else {
if (m->info->entry_point != nullptr) {
lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point);
- lb_emit_call(p, entry_point, {});
+ lb_emit_call(p, entry_point, {}, ProcInlining_no_inline);
}
}
@@ -911,7 +967,12 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
}
String lb_filepath_ll_for_module(lbModule *m) {
- String path = m->gen->output_base;
+ String path = concatenate3_strings(permanent_allocator(),
+ build_context.build_paths[BuildPath_Output].basename,
+ STR_LIT("/"),
+ build_context.build_paths[BuildPath_Output].name
+ );
+
if (m->pkg) {
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
} else if (USE_SEPARATE_MODULES) {
@@ -922,7 +983,12 @@ String lb_filepath_ll_for_module(lbModule *m) {
return path;
}
String lb_filepath_obj_for_module(lbModule *m) {
- String path = m->gen->output_base;
+ String path = concatenate3_strings(permanent_allocator(),
+ build_context.build_paths[BuildPath_Output].basename,
+ STR_LIT("/"),
+ build_context.build_paths[BuildPath_Output].name
+ );
+
if (m->pkg) {
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
}
@@ -945,6 +1011,19 @@ String lb_filepath_obj_for_module(lbModule *m) {
case TargetOs_essence:
ext = STR_LIT(".o");
break;
+
+ case TargetOs_freestanding:
+ switch (build_context.metrics.abi) {
+ default:
+ case TargetABI_Default:
+ case TargetABI_SysV:
+ ext = STR_LIT(".o");
+ break;
+ case TargetABI_Win64:
+ ext = STR_LIT(".obj");
+ break;
+ }
+ break;
}
}
}
@@ -1140,7 +1219,7 @@ void lb_generate_code(lbGenerator *gen) {
switch (build_context.metrics.arch) {
case TargetArch_amd64:
- case TargetArch_386:
+ case TargetArch_i386:
LLVMInitializeX86TargetInfo();
LLVMInitializeX86Target();
LLVMInitializeX86TargetMC();
@@ -1197,6 +1276,8 @@ void lb_generate_code(lbGenerator *gen) {
LLVMCodeModel code_mode = LLVMCodeModelDefault;
if (is_arch_wasm()) {
code_mode = LLVMCodeModelJITDefault;
+ } else if (build_context.metrics.os == TargetOs_freestanding) {
+ code_mode = LLVMCodeModelKernel;
}
char const *host_cpu_name = LLVMGetHostCPUName();
@@ -1219,10 +1300,18 @@ void lb_generate_code(lbGenerator *gen) {
// x86-64-v3: (close to Haswell) AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE
// x86-64-v4: AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL
if (ODIN_LLVM_MINIMUM_VERSION_12) {
- llvm_cpu = "x86-64-v2";
+ if (build_context.metrics.os == TargetOs_freestanding) {
+ llvm_cpu = "x86-64";
+ } else {
+ llvm_cpu = "x86-64-v2";
+ }
}
}
+ if (build_context.target_features.len != 0) {
+ llvm_features = alloc_cstring(permanent_allocator(), build_context.target_features);
+ }
+
// GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target));
LLVMCodeGenOptLevel code_gen_level = LLVMCodeGenLevelNone;
@@ -1238,12 +1327,36 @@ void lb_generate_code(lbGenerator *gen) {
// NOTE(bill, 2021-05-04): Target machines must be unique to each module because they are not thread safe
auto target_machines = array_make<LLVMTargetMachineRef>(permanent_allocator(), gen->modules.entries.count);
+ // NOTE(dweiler): Dynamic libraries require position-independent code.
+ LLVMRelocMode reloc_mode = LLVMRelocDefault;
+ if (build_context.build_mode == BuildMode_DynamicLibrary) {
+ reloc_mode = LLVMRelocPIC;
+ }
+
+ switch (build_context.reloc_mode) {
+ case RelocMode_Default:
+ if (build_context.metrics.os == TargetOs_openbsd) {
+ // Always use PIC for OpenBSD: it defaults to PIE
+ reloc_mode = LLVMRelocPIC;
+ }
+ break;
+ case RelocMode_Static:
+ reloc_mode = LLVMRelocStatic;
+ break;
+ case RelocMode_PIC:
+ reloc_mode = LLVMRelocPIC;
+ break;
+ case RelocMode_DynamicNoPIC:
+ reloc_mode = LLVMRelocDynamicNoPic;
+ break;
+ }
+
for_array(i, gen->modules.entries) {
target_machines[i] = LLVMCreateTargetMachine(
target, target_triple, llvm_cpu,
llvm_features,
code_gen_level,
- LLVMRelocDefault,
+ reloc_mode,
code_mode);
LLVMSetModuleDataLayout(gen->modules.entries[i].value->mod, LLVMCreateTargetDataLayout(target_machines[i]));
}
@@ -1304,7 +1417,7 @@ void lb_generate_code(lbGenerator *gen) {
TIME_SECTION("LLVM Global Variables");
- {
+ if (!build_context.disallow_rtti) {
lbModule *m = default_module;
{ // Add type info data
@@ -1399,30 +1512,30 @@ void lb_generate_code(lbGenerator *gen) {
isize global_variable_max_count = 0;
- Entity *entry_point = info->entry_point;
- bool has_dll_main = false;
- bool has_win_main = false;
+ bool already_has_entry_point = false;
for_array(i, info->entities) {
Entity *e = info->entities[i];
String name = e->token.string;
- bool is_global = e->pkg != nullptr;
-
if (e->kind == Entity_Variable) {
global_variable_max_count++;
- } else if (e->kind == Entity_Procedure && !is_global) {
+ } else if (e->kind == Entity_Procedure) {
if ((e->scope->flags&ScopeFlag_Init) && name == "main") {
- GB_ASSERT(e == entry_point);
- // entry_point = e;
+ GB_ASSERT(e == info->entry_point);
}
- if (e->Procedure.is_export ||
- (e->Procedure.link_name.len > 0) ||
- ((e->scope->flags&ScopeFlag_File) && e->Procedure.link_name.len > 0)) {
- if (!has_dll_main && name == "DllMain") {
- has_dll_main = true;
- } else if (!has_win_main && name == "WinMain") {
- has_win_main = true;
+ if (build_context.command_kind == Command_test &&
+ (e->Procedure.is_export || e->Procedure.link_name.len > 0)) {
+ String link_name = e->Procedure.link_name;
+ if (e->pkg->kind == Package_Runtime) {
+ if (link_name == "main" ||
+ link_name == "DllMain" ||
+ link_name == "WinMain" ||
+ link_name == "wWinMain" ||
+ link_name == "mainCRTStartup" ||
+ link_name == "_start") {
+ already_has_entry_point = true;
+ }
}
}
}
@@ -1560,6 +1673,14 @@ void lb_generate_code(lbGenerator *gen) {
}
}
+ TIME_SECTION("LLVM Runtime Type Information Creation");
+ lbProcedure *startup_type_info = lb_create_startup_type_info(default_module);
+
+ lbProcedure *objc_names = lb_create_objc_names(default_module);
+
+ TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)");
+ lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, objc_names, global_variables);
+ gb_unused(startup_runtime);
TIME_SECTION("LLVM Global Procedures and Types");
for_array(i, info->entities) {
@@ -1619,14 +1740,6 @@ void lb_generate_code(lbGenerator *gen) {
}
}
-
- TIME_SECTION("LLVM Runtime Type Information Creation");
- lbProcedure *startup_type_info = lb_create_startup_type_info(default_module);
-
- TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)");
- lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, global_variables);
-
-
TIME_SECTION("LLVM Procedure Generation");
for_array(j, gen->modules.entries) {
lbModule *m = gen->modules.entries[j].value;
@@ -1636,8 +1749,7 @@ void lb_generate_code(lbGenerator *gen) {
}
}
-
- if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) {
+ if (build_context.command_kind == Command_test && !already_has_entry_point) {
TIME_SECTION("LLVM main");
lb_create_main_procedure(default_module, startup_runtime);
}
@@ -1651,6 +1763,8 @@ void lb_generate_code(lbGenerator *gen) {
}
}
+ lb_finalize_objc_names(objc_names);
+
if (build_context.ODIN_DEBUG) {
TIME_SECTION("LLVM Debug Info Complete Types and Finalize");
for_array(j, gen->modules.entries) {
@@ -1663,6 +1777,7 @@ void lb_generate_code(lbGenerator *gen) {
}
+
TIME_SECTION("LLVM Function Pass");
for_array(i, gen->modules.entries) {
lbModule *m = gen->modules.entries[i].value;
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index e70b1f84c..f2bcfaff6 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -144,6 +144,9 @@ struct lbModule {
PtrMap<void *, LLVMMetadataRef> debug_values;
Array<lbIncompleteDebugType> debug_incomplete_types;
+
+ StringMap<lbAddr> objc_classes;
+ StringMap<lbAddr> objc_selectors;
};
struct lbGenerator {
@@ -204,7 +207,6 @@ enum lbDeferExitKind {
enum lbDeferKind {
lbDefer_Node,
- lbDefer_Instr,
lbDefer_Proc,
};
@@ -215,8 +217,6 @@ struct lbDefer {
lbBlock * block;
union {
Ast *stmt;
- // NOTE(bill): 'instr' will be copied every time to create a new one
- lbValue instr;
struct {
lbValue deferred;
Array<lbValue> result_as_args;
@@ -235,6 +235,7 @@ struct lbTargetList {
enum lbProcedureFlag : u32 {
lbProcedureFlag_WithoutMemcpyPass = 1<<0,
+ lbProcedureFlag_DebugAllocaCopy = 1<<1,
};
struct lbCopyElisionHint {
@@ -270,7 +271,6 @@ struct lbProcedure {
bool is_done;
lbAddr return_ptr;
- Array<lbValue> params;
Array<lbDefer> defer_stmts;
Array<lbBlock *> blocks;
Array<lbBranchBlocks> branch_blocks;
@@ -296,7 +296,6 @@ struct lbProcedure {
bool lb_init_generator(lbGenerator *gen, Checker *c);
-void lb_generate_module(lbGenerator *gen);
String lb_mangle_name(lbModule *m, Entity *e);
String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
@@ -369,7 +368,7 @@ lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx);
lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p);
-lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={});
+lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={}, Entity **entity_=nullptr);
lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_init=true, i32 param_index=0, bool force_no_init=false);
void lb_add_foreign_library_path(lbModule *m, Entity *e);
@@ -549,6 +548,10 @@ lbCallingConventionKind const lb_calling_convention_map[ProcCC_MAX] = {
lbCallingConvention_C, // ProcCC_None,
lbCallingConvention_C, // ProcCC_Naked,
lbCallingConvention_C, // ProcCC_InlineAsm,
+
+ lbCallingConvention_Win64, // ProcCC_Win64,
+ lbCallingConvention_X86_64_SysV, // ProcCC_SysV,
+
};
enum : LLVMDWARFTypeEncoding {
diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp
index 5862a7add..8f17a1cfb 100644
--- a/src/llvm_backend_const.cpp
+++ b/src/llvm_backend_const.cpp
@@ -115,8 +115,8 @@ LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) {
- GB_ASSERT(is_type_pointer(value.type));
- GB_ASSERT(is_type_pointer(t));
+ GB_ASSERT(is_type_internally_pointer_like(value.type));
+ GB_ASSERT(is_type_internally_pointer_like(t));
GB_ASSERT(lb_is_const(value));
lbValue res = {};
@@ -175,7 +175,7 @@ LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize
}
LLVMValueRef llvm_const_slice(lbModule *m, lbValue data, lbValue len) {
- GB_ASSERT(is_type_pointer(data.type));
+ GB_ASSERT(is_type_pointer(data.type) || is_type_multi_pointer(data.type));
GB_ASSERT(are_types_identical(len.type, t_int));
LLVMValueRef vals[2] = {
data.value,
@@ -568,7 +568,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
}
case ExactValue_Integer:
- if (is_type_pointer(type)) {
+ if (is_type_pointer(type) || is_type_multi_pointer(type)) {
LLVMTypeRef t = lb_type(m, original_type);
LLVMValueRef i = lb_big_int_to_llvm(m, t_uintptr, &value.value_integer);
res.value = LLVMConstIntToPtr(i, t);
diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp
index 7a2b00fe9..b91f32bfc 100644
--- a/src/llvm_backend_debug.cpp
+++ b/src/llvm_backend_debug.cpp
@@ -958,13 +958,79 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T
);
LLVMValueRef storage = ptr;
- LLVMValueRef instr = ptr;
+ LLVMBasicBlockRef block = p->decl_block->block;
LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
lb_set_llvm_metadata(m, ptr, llvm_expr);
- LLVMDIBuilderInsertDeclareBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr);
+ LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block);
}
+
+void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token, unsigned arg_number, lbBlock *block) {
+ if (p->debug_info == nullptr) {
+ return;
+ }
+ if (type == nullptr) {
+ return;
+ }
+ if (type == t_invalid) {
+ return;
+ }
+ if (p->body == nullptr) {
+ return;
+ }
+
+ lbModule *m = p->module;
+ String const &name = token.string;
+ if (name == "" || name == "_") {
+ return;
+ }
+
+ if (lb_get_llvm_metadata(m, ptr) != nullptr) {
+ // Already been set
+ return;
+ }
+
+
+ AstFile *file = p->body->file();
+
+ LLVMMetadataRef llvm_scope = lb_get_current_debug_scope(p);
+ LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
+ GB_ASSERT(llvm_scope != nullptr);
+ if (llvm_file == nullptr) {
+ llvm_file = LLVMDIScopeGetFile(llvm_scope);
+ }
+
+ if (llvm_file == nullptr) {
+ return;
+ }
+
+ LLVMDIFlags flags = LLVMDIFlagZero;
+ LLVMBool always_preserve = build_context.optimization_level == 0;
+
+ LLVMMetadataRef debug_type = lb_debug_type(m, type);
+
+ LLVMMetadataRef var_info = LLVMDIBuilderCreateParameterVariable(
+ m->debug_builder, llvm_scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ arg_number,
+ llvm_file, token.pos.line,
+ debug_type,
+ always_preserve, flags
+ );
+
+ LLVMValueRef storage = ptr;
+ LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
+ LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
+ lb_set_llvm_metadata(m, ptr, llvm_expr);
+
+ // NOTE(bill, 2022-02-01): For parameter values, you must insert them at the end of the decl block
+ // The reason is that if the parameter is at index 0 and a pointer, there is not such things as an
+ // instruction "before" it.
+ LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block);
+}
+
+
void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
if (!p->debug_info || !p->body) {
return;
@@ -984,5 +1050,10 @@ void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
token.string = str_lit("context");
token.pos = pos;
- lb_add_debug_local_variable(p, ctx.addr.value, t_context, token);
+ LLVMValueRef ptr = ctx.addr.value;
+ while (LLVMIsABitCastInst(ptr)) {
+ ptr = LLVMGetOperand(ptr, 0);
+ }
+
+ lb_add_debug_local_variable(p, ptr, t_context, token);
}
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp
index edda3a267..133df4d41 100644
--- a/src/llvm_backend_expr.cpp
+++ b/src/llvm_backend_expr.cpp
@@ -508,7 +508,7 @@ bool lb_is_matrix_simdable(Type *t) {
case TargetArch_arm64:
// TODO(bill): determine when this is fine
return true;
- case TargetArch_386:
+ case TargetArch_i386:
case TargetArch_wasm32:
case TargetArch_wasm64:
return false;
@@ -580,6 +580,27 @@ LLVMValueRef lb_matrix_to_trimmed_vector(lbProcedure *p, lbValue m) {
lbValue lb_emit_matrix_tranpose(lbProcedure *p, lbValue m, Type *type) {
if (is_type_array(m.type)) {
+ i32 rank = type_math_rank(m.type);
+ if (rank == 2) {
+ lbAddr addr = lb_add_local_generated(p, type, false);
+ lbValue dst = addr.addr;
+ lbValue src = m;
+ i32 n = cast(i32)get_array_type_count(m.type);
+ i32 m = cast(i32)get_array_type_count(type);
+ // m.type == [n][m]T
+ // type == [m][n]T
+
+ for (i32 j = 0; j < m; j++) {
+ lbValue dst_col = lb_emit_struct_ep(p, dst, j);
+ for (i32 i = 0; i < n; i++) {
+ lbValue dst_row = lb_emit_struct_ep(p, dst_col, i);
+ lbValue src_col = lb_emit_struct_ev(p, src, i);
+ lbValue src_row = lb_emit_struct_ev(p, src_col, j);
+ lb_emit_store(p, dst_row, src_row);
+ }
+ }
+ return lb_addr_load(p, addr);
+ }
// no-op
m.type = type;
return m;
@@ -1655,26 +1676,10 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return res;
}
- if (is_type_float(src) && is_type_complex(dst)) {
- Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, false);
- lbValue gp = lb_addr_get_ptr(p, gen);
- lbValue real = lb_emit_conv(p, value, ft);
- lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
- return lb_addr_load(p, gen);
- }
- if (is_type_float(src) && is_type_quaternion(dst)) {
- Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, false);
- lbValue gp = lb_addr_get_ptr(p, gen);
- lbValue real = lb_emit_conv(p, value, ft);
- lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
- return lb_addr_load(p, gen);
- }
if (is_type_complex(src) && is_type_complex(dst)) {
Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, false);
+ lbAddr gen = lb_add_local_generated(p, t, false);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
lbValue imag = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
@@ -1686,7 +1691,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
if (is_type_quaternion(src) && is_type_quaternion(dst)) {
// @QuaternionLayout
Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, false);
+ lbAddr gen = lb_add_local_generated(p, t, false);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue q0 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
lbValue q1 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
@@ -1701,7 +1706,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
if (is_type_integer(src) && is_type_complex(dst)) {
Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, true);
+ lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
@@ -1709,7 +1714,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
if (is_type_float(src) && is_type_complex(dst)) {
Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, true);
+ lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
@@ -1719,7 +1724,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
if (is_type_integer(src) && is_type_quaternion(dst)) {
Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, true);
+ lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
// @QuaternionLayout
@@ -1728,7 +1733,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
if (is_type_float(src) && is_type_quaternion(dst)) {
Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, true);
+ lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
// @QuaternionLayout
@@ -1737,7 +1742,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
if (is_type_complex(src) && is_type_quaternion(dst)) {
Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, true);
+ lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
lbValue imag = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
@@ -1850,6 +1855,15 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return lb_addr_load(p, parent);
}
}
+ if (dst->Union.variants.count == 1) {
+ Type *vt = dst->Union.variants[0];
+ if (internal_check_is_assignable_to(src, vt)) {
+ value = lb_emit_conv(p, value, vt);
+ lbAddr parent = lb_add_local_generated(p, t, true);
+ lb_emit_store_union_variant(p, parent.addr, value, vt);
+ return lb_addr_load(p, parent);
+ }
+ }
}
// NOTE(bill): This has to be done before 'Pointer <-> Pointer' as it's
@@ -2188,6 +2202,21 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
}
}
+ if (is_type_matrix(a) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
+ Type *tl = base_type(a);
+ lbValue lhs = lb_address_from_load_or_generate_local(p, left);
+ lbValue rhs = lb_address_from_load_or_generate_local(p, right);
+
+
+ // TODO(bill): Test to see if this is actually faster!!!!
+ auto args = array_make<lbValue>(permanent_allocator(), 3);
+ args[0] = lb_emit_conv(p, lhs, t_rawptr);
+ args[1] = lb_emit_conv(p, rhs, t_rawptr);
+ args[2] = lb_const_int(p->module, t_int, type_size_of(tl));
+ lbValue val = lb_emit_runtime_call(p, "memory_compare", args);
+ lbValue res = lb_emit_comp(p, op_kind, val, lb_const_nil(p->module, val.type));
+ return lb_emit_conv(p, res, t_bool);
+ }
if (is_type_array(a) || is_type_enumerated_array(a)) {
Type *tl = base_type(a);
lbValue lhs = lb_address_from_load_or_generate_local(p, left);
@@ -2784,27 +2813,38 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
Type *src_type = type_deref(v.type);
Type *dst_type = type;
- lbValue src_tag = {};
- lbValue dst_tag = {};
- if (is_type_union_maybe_pointer(src_type)) {
- src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
- dst_tag = lb_const_bool(p->module, t_bool, true);
- } else {
- src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
- dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
- }
- lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
- auto args = array_make<lbValue>(permanent_allocator(), 6);
- args[0] = ok;
+ if ((p->state_flags & StateFlag_no_type_assert) == 0) {
+ lbValue src_tag = {};
+ lbValue dst_tag = {};
+ if (is_type_union_maybe_pointer(src_type)) {
+ src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
+ dst_tag = lb_const_bool(p->module, t_bool, true);
+ } else {
+ src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
+ dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
+ }
+
- args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
- args[2] = lb_const_int(p->module, t_i32, pos.line);
- args[3] = lb_const_int(p->module, t_i32, pos.column);
+ isize arg_count = 6;
+ if (build_context.disallow_rtti) {
+ arg_count = 4;
+ }
+
+ lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
+ auto args = array_make<lbValue>(permanent_allocator(), arg_count);
+ args[0] = ok;
- args[4] = lb_typeid(p->module, src_type);
- args[5] = lb_typeid(p->module, dst_type);
- lb_emit_runtime_call(p, "type_assertion_check", args);
+ args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
+ args[2] = lb_const_int(p->module, t_i32, pos.line);
+ args[3] = lb_const_int(p->module, t_i32, pos.column);
+
+ if (!build_context.disallow_rtti) {
+ args[4] = lb_typeid(p->module, src_type);
+ args[5] = lb_typeid(p->module, dst_type);
+ }
+ lb_emit_runtime_call(p, "type_assertion_check", args);
+ }
lbValue data_ptr = v;
return lb_emit_conv(p, data_ptr, tv.type);
@@ -2813,23 +2853,25 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
if (is_type_pointer(v.type)) {
v = lb_emit_load(p, v);
}
-
lbValue data_ptr = lb_emit_struct_ev(p, v, 0);
- lbValue any_id = lb_emit_struct_ev(p, v, 1);
- lbValue id = lb_typeid(p->module, type);
+ if ((p->state_flags & StateFlag_no_type_assert) == 0) {
+ GB_ASSERT(!build_context.disallow_rtti);
+ lbValue any_id = lb_emit_struct_ev(p, v, 1);
- lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
- auto args = array_make<lbValue>(permanent_allocator(), 6);
- args[0] = ok;
+ lbValue id = lb_typeid(p->module, type);
+ lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
+ auto args = array_make<lbValue>(permanent_allocator(), 6);
+ args[0] = ok;
- args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
- args[2] = lb_const_int(p->module, t_i32, pos.line);
- args[3] = lb_const_int(p->module, t_i32, pos.column);
+ args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
+ args[2] = lb_const_int(p->module, t_i32, pos.line);
+ args[3] = lb_const_int(p->module, t_i32, pos.column);
- args[4] = any_id;
- args[5] = id;
- lb_emit_runtime_call(p, "type_assertion_check", args);
+ args[4] = any_id;
+ args[5] = id;
+ lb_emit_runtime_call(p, "type_assertion_check", args);
+ }
return lb_emit_conv(p, data_ptr, tv.type);
} else {
@@ -2859,6 +2901,14 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
out &= ~StateFlag_bounds_check;
}
+ if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ } else if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ }
+
p->state_flags = out;
}
@@ -2978,7 +3028,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later
lbBlock *else_ = lb_create_block(p, "if.else");
- lbValue cond = lb_build_cond(p, te->cond, then, else_);
+ lb_build_cond(p, te->cond, then, else_);
lb_start_block(p, then);
Type *type = default_type(type_of_expr(expr));
@@ -3279,9 +3329,9 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
case_end;
case_ast_node(se, SelectorExpr, expr);
- Ast *sel = unparen_expr(se->selector);
- if (sel->kind == Ast_Ident) {
- String selector = sel->Ident.token.string;
+ Ast *sel_node = unparen_expr(se->selector);
+ if (sel_node->kind == Ast_Ident) {
+ String selector = sel_node->Ident.token.string;
TypeAndValue tav = type_and_value_of_expr(se->expr);
if (tav.mode == Addressing_Invalid) {
@@ -3296,7 +3346,12 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
Type *type = base_type(tav.type);
if (tav.mode == Addressing_Type) { // Addressing_Type
- GB_PANIC("Unreachable");
+ 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_PANIC("Unreachable %.*s", LIT(selector));
}
if (se->swizzle_count > 0) {
@@ -3323,6 +3378,11 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
Selection sel = lookup_field(type, selector, false);
GB_ASSERT(sel.entity != nullptr);
+ if (sel.pseudo_field) {
+ GB_ASSERT(sel.entity->kind == Entity_Procedure);
+ Entity *e = entity_of_node(sel_node);
+ return lb_addr(lb_find_value_from_entity(p->module, e));
+ }
{
lbAddr addr = lb_build_addr(p, se->expr);
@@ -3476,7 +3536,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
if (is_type_map(t)) {
- lbValue map_val = lb_build_addr_ptr(p, ie->expr);
+ lbAddr map_addr = lb_build_addr(p, ie->expr);
+ lbValue map_val = lb_addr_load(p, map_addr);
if (deref) {
map_val = lb_emit_load(p, map_val);
}
@@ -3485,7 +3546,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
key = lb_emit_conv(p, key, t->Map.key);
Type *result_type = type_of_expr(expr);
- return lb_addr_map(map_val, key, t, result_type);
+ lbValue map_ptr = lb_address_from_load_or_generate_local(p, map_val);
+ return lb_addr_map(map_ptr, key, t, result_type);
}
switch (t->kind) {
@@ -4584,7 +4646,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later
lbBlock *else_ = lb_create_block(p, "if.else");
- lbValue cond = lb_build_cond(p, te->cond, then, else_);
+ lb_build_cond(p, te->cond, then, else_);
lb_start_block(p, then);
Type *ptr_type = alloc_type_pointer(default_type(type_of_expr(expr)));
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index 17eeb0bea..1a431a4ac 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -72,6 +72,9 @@ void lb_init_module(lbModule *m, Checker *c) {
map_init(&m->debug_values, a);
array_init(&m->debug_incomplete_types, a, 0, 1024);
+
+ string_map_init(&m->objc_classes, a);
+ string_map_init(&m->objc_selectors, a);
}
bool lb_init_generator(lbGenerator *gen, Checker *c) {
@@ -84,7 +87,6 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) {
return false;
}
-
String init_fullpath = c->parser->init_fullpath;
if (build_context.out_filepath.len == 0) {
@@ -271,6 +273,10 @@ lbAddr lb_addr(lbValue addr) {
lbAddr lb_addr_map(lbValue addr, lbValue map_key, Type *map_type, Type *map_result) {
+ GB_ASSERT(is_type_pointer(addr.type));
+ Type *mt = type_deref(addr.type);
+ GB_ASSERT(is_type_map(mt));
+
lbAddr v = {lbAddr_Map, addr};
v.map.key = map_key;
v.map.type = map_type;
@@ -1169,10 +1175,35 @@ void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *varia
}
void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) {
- lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
+ Type *pt = base_type(type_deref(parent.type));
+ GB_ASSERT(pt->kind == Type_Union);
+ if (pt->Union.kind == UnionType_shared_nil) {
+ lbBlock *if_nil = lb_create_block(p, "shared_nil.if_nil");
+ lbBlock *if_not_nil = lb_create_block(p, "shared_nil.if_not_nil");
+ lbBlock *done = lb_create_block(p, "shared_nil.done");
+
+ lbValue cond_is_nil = lb_emit_comp_against_nil(p, Token_CmpEq, variant);
+ lb_emit_if(p, cond_is_nil, if_nil, if_not_nil);
+
+ lb_start_block(p, if_nil);
+ lb_emit_store(p, parent, lb_const_nil(p->module, type_deref(parent.type)));
+ lb_emit_jump(p, done);
+
+ lb_start_block(p, if_not_nil);
+ lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
+ lb_emit_store(p, underlying, variant);
+ lb_emit_store_union_variant_tag(p, parent, variant_type);
+ lb_emit_jump(p, done);
+
+ lb_start_block(p, done);
- lb_emit_store(p, underlying, variant);
- lb_emit_store_union_variant_tag(p, parent, variant_type);
+
+ } else {
+ lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
+
+ lb_emit_store(p, underlying, variant);
+ lb_emit_store_union_variant_tag(p, parent, variant_type);
+ }
}
@@ -1598,8 +1629,9 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
return llvm_type;
}
llvm_type = LLVMStructCreateNamed(ctx, name);
+ LLVMTypeRef found_val = *found;
map_set(&m->types, type, llvm_type);
- lb_clone_struct_type(llvm_type, *found);
+ lb_clone_struct_type(llvm_type, found_val);
return llvm_type;
}
}
@@ -2304,7 +2336,7 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) {
LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
LLVMSetInitializer(global_data, data);
- LLVMSetLinkage(global_data, LLVMInternalLinkage);
+ LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2);
string_map_set(&m->const_strings, key, ptr);
@@ -2346,7 +2378,7 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str)
}
LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
LLVMSetInitializer(global_data, data);
- LLVMSetLinkage(global_data, LLVMInternalLinkage);
+ LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMValueRef ptr = nullptr;
if (str.len != 0) {
@@ -2445,7 +2477,7 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) {
return {};
}
-lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) {
+lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value, Entity **entity_) {
GB_ASSERT(type != nullptr);
type = default_type(type);
@@ -2471,6 +2503,9 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) {
lb_add_entity(m, e, g);
lb_add_member(m, name, g);
+
+ if (entity_) *entity_ = e;
+
return lb_addr(g);
}
@@ -2579,7 +2614,7 @@ lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String
g.value = LLVMAddGlobal(m->mod, lb_type(m, t), text);
g.type = alloc_type_pointer(t);
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, t)));
- LLVMSetLinkage(g.value, LLVMInternalLinkage);
+ LLVMSetLinkage(g.value, LLVMPrivateLinkage);
string_map_set(&m->members, s, g);
return g;
}
@@ -2591,6 +2626,9 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
GB_ASSERT(true_block != nullptr);
GB_ASSERT(false_block != nullptr);
+ // Use to signal not to do compile time short circuit for consts
+ lbValue no_comptime_short_circuit = {};
+
switch (cond->kind) {
case_ast_node(pe, ParenExpr, cond);
return lb_build_cond(p, pe->expr, true_block, false_block);
@@ -2598,7 +2636,11 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
case_ast_node(ue, UnaryExpr, cond);
if (ue->op.kind == Token_Not) {
- return lb_build_cond(p, ue->expr, false_block, true_block);
+ lbValue cond_val = lb_build_cond(p, ue->expr, false_block, true_block);
+ if (cond_val.value && LLVMIsConstant(cond_val.value)) {
+ return lb_const_bool(p->module, cond_val.type, LLVMConstIntGetZExtValue(cond_val.value) == 0);
+ }
+ return no_comptime_short_circuit;
}
case_end;
@@ -2607,12 +2649,14 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
lbBlock *block = lb_create_block(p, "cmp.and");
lb_build_cond(p, be->left, block, false_block);
lb_start_block(p, block);
- return lb_build_cond(p, be->right, true_block, false_block);
+ lb_build_cond(p, be->right, true_block, false_block);
+ return no_comptime_short_circuit;
} else if (be->op.kind == Token_CmpOr) {
lbBlock *block = lb_create_block(p, "cmp.or");
lb_build_cond(p, be->left, true_block, block);
lb_start_block(p, block);
- return lb_build_cond(p, be->right, true_block, false_block);
+ lb_build_cond(p, be->right, true_block, false_block);
+ return no_comptime_short_circuit;
}
case_end;
}
diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp
index de925655f..d36bdec0b 100644
--- a/src/llvm_backend_opt.cpp
+++ b/src/llvm_backend_opt.cpp
@@ -48,12 +48,6 @@ LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data
return LLVMIsAAllocaInst(value) != nullptr;
}
-void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) {
- if (false && optimization_level == 0 && m->debug_builder) {
- // LLVMAddInternalizePassWithMustPreservePredicate(fpm, m, lb_must_preserve_predicate_callback);
- }
-}
-
#if LLVM_VERSION_MAJOR < 12
#define LLVM_ADD_CONSTANT_VALUE_PASS(fpm) LLVMAddConstantPropagationPass(fpm)
@@ -61,16 +55,15 @@ void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i3
#define LLVM_ADD_CONSTANT_VALUE_PASS(fpm)
#endif
-void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) {
- LLVMAddPromoteMemoryToRegisterPass(fpm);
- LLVMAddMergedLoadStoreMotionPass(fpm);
- LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
- LLVMAddEarlyCSEPass(fpm);
-
- // LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
- // LLVMAddMergedLoadStoreMotionPass(fpm);
- // LLVMAddPromoteMemoryToRegisterPass(fpm);
- // LLVMAddCFGSimplificationPass(fpm);
+void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm, i32 optimization_level) {
+ if (false && optimization_level == 0 && build_context.ODIN_DEBUG) {
+ LLVMAddMergedLoadStoreMotionPass(fpm);
+ } else {
+ LLVMAddPromoteMemoryToRegisterPass(fpm);
+ LLVMAddMergedLoadStoreMotionPass(fpm);
+ LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
+ LLVMAddEarlyCSEPass(fpm);
+ }
}
void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) {
@@ -78,14 +71,12 @@ void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool
// TODO(bill): Determine which opt definitions should exist in the first place
optimization_level = gb_clamp(optimization_level, 0, 2);
- lb_add_must_preserve_predicate_pass(m, fpm, optimization_level);
-
if (ignore_memcpy_pass) {
- lb_basic_populate_function_pass_manager(fpm);
+ lb_basic_populate_function_pass_manager(fpm, optimization_level);
return;
} else if (optimization_level == 0) {
LLVMAddMemCpyOptPass(fpm);
- lb_basic_populate_function_pass_manager(fpm);
+ lb_basic_populate_function_pass_manager(fpm, optimization_level);
return;
}
@@ -96,7 +87,7 @@ void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool
LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm);
#else
LLVMAddMemCpyOptPass(fpm);
- lb_basic_populate_function_pass_manager(fpm);
+ lb_basic_populate_function_pass_manager(fpm, optimization_level);
LLVMAddSCCPPass(fpm);
@@ -114,11 +105,9 @@ void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef
// TODO(bill): Determine which opt definitions should exist in the first place
optimization_level = gb_clamp(optimization_level, 0, 2);
- lb_add_must_preserve_predicate_pass(m, fpm, optimization_level);
-
if (optimization_level == 0) {
LLVMAddMemCpyOptPass(fpm);
- lb_basic_populate_function_pass_manager(fpm);
+ lb_basic_populate_function_pass_manager(fpm, optimization_level);
return;
}
@@ -191,6 +180,9 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa
// NOTE(bill): Treat -opt:3 as if it was -opt:2
// TODO(bill): Determine which opt definitions should exist in the first place
optimization_level = gb_clamp(optimization_level, 0, 2);
+ if (optimization_level == 0 && build_context.ODIN_DEBUG) {
+ return;
+ }
LLVMAddAlwaysInlinerPass(mpm);
LLVMAddStripDeadPrototypesPass(mpm);
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index 25b27ee47..96ff19d10 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -57,11 +57,12 @@ void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbVal
LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
}
+
lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) {
GB_ASSERT(entity != nullptr);
GB_ASSERT(entity->kind == Entity_Procedure);
if (!entity->Procedure.is_foreign) {
- GB_ASSERT(entity->flags & EntityFlag_ProcBodyChecked);
+ GB_ASSERT_MSG(entity->flags & EntityFlag_ProcBodyChecked, "%.*s :: %s", LIT(entity->token.string), type_to_string(entity->type));
}
String link_name = {};
@@ -107,7 +108,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
gbAllocator a = heap_allocator();
p->children.allocator = a;
- p->params.allocator = a;
p->defer_stmts.allocator = a;
p->blocks.allocator = a;
p->branch_blocks.allocator = a;
@@ -135,6 +135,10 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
lb_add_attribute_to_proc(m, p->value, "naked");
}
+ if (!entity->Procedure.is_foreign && build_context.disable_red_zone) {
+ lb_add_attribute_to_proc(m, p->value, "noredzone");
+ }
+
switch (p->inlining) {
case ProcInlining_inline:
lb_add_attribute_to_proc(m, p->value, "alwaysinline");
@@ -164,14 +168,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
break;
}
-
-
- // lbCallingConventionKind cc_kind = lbCallingConvention_C;
- // // TODO(bill): Clean up this logic
- // if (build_context.metrics.os != TargetOs_js) {
- // cc_kind = lb_calling_convention_map[pt->Proc.calling_convention];
- // }
- // LLVMSetFunctionCallConv(p->value, cc_kind);
lbValue proc_value = {p->value, p->type};
lb_add_entity(m, entity, proc_value);
lb_add_member(m, p->name, proc_value);
@@ -304,7 +300,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type) {
{
lbValue *found = string_map_get(&m->members, link_name);
- GB_ASSERT(found == nullptr);
+ GB_ASSERT_MSG(found == nullptr, "failed to create dummy procedure for: %.*s", LIT(link_name));
}
lbProcedure *p = gb_alloc_item(permanent_allocator(), lbProcedure);
@@ -323,7 +319,6 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type
gbAllocator a = permanent_allocator();
p->children.allocator = a;
- p->params.allocator = a;
p->defer_stmts.allocator = a;
p->blocks.allocator = a;
p->branch_blocks.allocator = a;
@@ -452,7 +447,7 @@ void lb_begin_procedure_body(lbProcedure *p) {
Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results));
Entity *e = alloc_entity_param(nullptr, make_token_ident(name), ptr_type, false, false);
- e->flags |= EntityFlag_Sret | EntityFlag_NoAlias;
+ e->flags |= EntityFlag_NoAlias;
return_ptr_value.value = LLVMGetParam(p->value, 0);
LLVMSetValueName2(return_ptr_value.value, cast(char const *)name.text, name.len);
@@ -475,44 +470,42 @@ void lb_begin_procedure_body(lbProcedure *p) {
}
lbArgType *arg_type = &ft->args[param_index];
+ defer (param_index += 1);
+
if (arg_type->kind == lbArg_Ignore) {
continue;
} else if (arg_type->kind == lbArg_Direct) {
- lbParamPasskind kind = lbParamPass_Value;
- LLVMTypeRef param_type = lb_type(p->module, e->type);
- if (param_type != arg_type->type) {
- kind = lbParamPass_BitCast;
- }
- LLVMValueRef value = LLVMGetParam(p->value, param_offset+param_index);
+ if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) {
+ LLVMTypeRef param_type = lb_type(p->module, e->type);
+ LLVMValueRef original_value = LLVMGetParam(p->value, param_offset+param_index);
+ LLVMValueRef value = OdinLLVMBuildTransmute(p, original_value, param_type);
- value = OdinLLVMBuildTransmute(p, value, param_type);
+ lbValue param = {};
+ param.value = value;
+ param.type = e->type;
- lbValue param = {};
- param.value = value;
- param.type = e->type;
- array_add(&p->params, param);
+ lbValue ptr = lb_address_from_load_or_generate_local(p, param);
+ GB_ASSERT(LLVMIsAAllocaInst(ptr.value));
+ lb_add_entity(p->module, e, ptr);
- if (e->token.string.len != 0) {
- lbAddr l = lb_add_local(p, e->type, e, false, param_index);
- lb_addr_store(p, l, param);
+ lbBlock *block = p->decl_block;
+ if (original_value != value) {
+ block = p->curr_block;
+ }
+ LLVMValueRef debug_storage_value = value;
+ if (original_value != value && LLVMIsALoadInst(value)) {
+ debug_storage_value = LLVMGetOperand(value, 0);
+ }
+ lb_add_debug_param_variable(p, debug_storage_value, e->type, e->token, param_index+1, block);
}
-
- param_index += 1;
} else if (arg_type->kind == lbArg_Indirect) {
- LLVMValueRef value_ptr = LLVMGetParam(p->value, param_offset+param_index);
- LLVMValueRef value = LLVMBuildLoad(p->builder, value_ptr, "");
-
- lbValue param = {};
- param.value = value;
- param.type = e->type;
- array_add(&p->params, param);
-
- lbValue ptr = {};
- ptr.value = value_ptr;
- ptr.type = alloc_type_pointer(e->type);
-
- lb_add_entity(p->module, e, ptr);
- param_index += 1;
+ if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) {
+ lbValue ptr = {};
+ ptr.value = LLVMGetParam(p->value, param_offset+param_index);
+ ptr.type = alloc_type_pointer(e->type);
+ lb_add_entity(p->module, e, ptr);
+ lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1, p->decl_block);
+ }
}
}
}
@@ -736,6 +729,10 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
LLVMValueRef ret = LLVMBuildCall2(p->builder, fnp, fn, args, arg_count, "");
+ if (return_ptr.value != nullptr) {
+ LLVMAddCallSiteAttribute(ret, 1, lb_create_enum_attribute_with_type(p->module->ctx, "sret", LLVMTypeOf(args[0])));
+ }
+
switch (inlining) {
case ProcInlining_none:
break;
@@ -1049,7 +1046,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return lb_string_len(p, v);
} else if (is_type_array(t)) {
GB_PANIC("Array lengths are constant");
- } else if (is_type_slice(t)) {
+ } else if (is_type_slice(t) || is_type_relative_slice(t)) {
return lb_slice_len(p, v);
} else if (is_type_dynamic_array(t)) {
return lb_dynamic_array_len(p, v);
@@ -1075,7 +1072,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
GB_PANIC("Unreachable");
} else if (is_type_array(t)) {
GB_PANIC("Array lengths are constant");
- } else if (is_type_slice(t)) {
+ } else if (is_type_slice(t) || is_type_relative_slice(t)) {
return lb_slice_len(p, v);
} else if (is_type_dynamic_array(t)) {
return lb_dynamic_array_cap(p, v);
@@ -1372,15 +1369,23 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
case BuiltinProc_cpu_relax:
- if (build_context.metrics.arch == TargetArch_386 ||
+ if (build_context.metrics.arch == TargetArch_i386 ||
build_context.metrics.arch == TargetArch_amd64) {
LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
- LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {});
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {}, true);
GB_ASSERT(the_asm != nullptr);
LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
} else if (build_context.metrics.arch == TargetArch_arm64) {
LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
- LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("yield"), {});
+ // NOTE(bill, 2022-03-30): `isb` appears to a better option that `yield`
+ // See: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8258604
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("isb"), {}, true);
+ GB_ASSERT(the_asm != nullptr);
+ LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
+ } else {
+ // NOTE: default to something to prevent optimization
+ LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit(""), {}, true);
GB_ASSERT(the_asm != nullptr);
LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
}
@@ -1409,14 +1414,23 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_read_cycle_counter:
{
- char const *name = "llvm.readcyclecounter";
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
-
lbValue res = {};
- res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, "");
res.type = tv.type;
+
+ if (build_context.metrics.arch == TargetArch_arm64) {
+ LLVMTypeRef func_type = LLVMFunctionType(LLVMInt64TypeInContext(p->module->ctx), nullptr, 0, false);
+ bool has_side_effects = false;
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("mrs x9, cntvct_el0"), str_lit("=r"), has_side_effects);
+ GB_ASSERT(the_asm != nullptr);
+ res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
+ } else {
+ char const *name = "llvm.readcyclecounter";
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
+
+ res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, "");
+ }
return res;
}
@@ -1592,36 +1606,26 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
-
- case BuiltinProc_atomic_fence:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, "");
- return {};
- case BuiltinProc_atomic_fence_acq:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquire, false, "");
+ // TODO(bill): Which is correct?
+ case BuiltinProc_atomic_thread_fence:
+ LLVMBuildFence(p->builder, llvm_atomic_ordering_from_odin(ce->args[0]), false, "");
return {};
- case BuiltinProc_atomic_fence_rel:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingRelease, false, "");
- return {};
- case BuiltinProc_atomic_fence_acqrel:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquireRelease, false, "");
+ case BuiltinProc_atomic_signal_fence:
+ LLVMBuildFence(p->builder, llvm_atomic_ordering_from_odin(ce->args[0]), true, "");
return {};
case BuiltinProc_volatile_store:
case BuiltinProc_atomic_store:
- case BuiltinProc_atomic_store_rel:
- case BuiltinProc_atomic_store_relaxed:
- case BuiltinProc_atomic_store_unordered: {
+ case BuiltinProc_atomic_store_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
lbValue val = lb_build_expr(p, ce->args[1]);
val = lb_emit_conv(p, val, type_deref(dst.type));
LLVMValueRef instr = LLVMBuildStore(p->builder, val.value, dst.value);
switch (id) {
- case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break;
- case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
- case BuiltinProc_atomic_store_rel: LLVMSetOrdering(instr, LLVMAtomicOrderingRelease); break;
- case BuiltinProc_atomic_store_relaxed: LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic); break;
- case BuiltinProc_atomic_store_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered); break;
+ case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break;
+ case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
+ case BuiltinProc_atomic_store_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[2])); break;
}
LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type)));
@@ -1631,18 +1635,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_volatile_load:
case BuiltinProc_atomic_load:
- case BuiltinProc_atomic_load_acq:
- case BuiltinProc_atomic_load_relaxed:
- case BuiltinProc_atomic_load_unordered: {
+ case BuiltinProc_atomic_load_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
LLVMValueRef instr = LLVMBuildLoad(p->builder, dst.value, "");
switch (id) {
- case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break;
- case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
- case BuiltinProc_atomic_load_acq: LLVMSetOrdering(instr, LLVMAtomicOrderingAcquire); break;
- case BuiltinProc_atomic_load_relaxed: LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic); break;
- case BuiltinProc_atomic_load_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered); break;
+ case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break;
+ case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
+ case BuiltinProc_atomic_load_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[1])); break;
}
LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type)));
@@ -1672,40 +1672,19 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
case BuiltinProc_atomic_add:
- case BuiltinProc_atomic_add_acq:
- case BuiltinProc_atomic_add_rel:
- case BuiltinProc_atomic_add_acqrel:
- case BuiltinProc_atomic_add_relaxed:
case BuiltinProc_atomic_sub:
- case BuiltinProc_atomic_sub_acq:
- case BuiltinProc_atomic_sub_rel:
- case BuiltinProc_atomic_sub_acqrel:
- case BuiltinProc_atomic_sub_relaxed:
case BuiltinProc_atomic_and:
- case BuiltinProc_atomic_and_acq:
- case BuiltinProc_atomic_and_rel:
- case BuiltinProc_atomic_and_acqrel:
- case BuiltinProc_atomic_and_relaxed:
case BuiltinProc_atomic_nand:
- case BuiltinProc_atomic_nand_acq:
- case BuiltinProc_atomic_nand_rel:
- case BuiltinProc_atomic_nand_acqrel:
- case BuiltinProc_atomic_nand_relaxed:
case BuiltinProc_atomic_or:
- case BuiltinProc_atomic_or_acq:
- case BuiltinProc_atomic_or_rel:
- case BuiltinProc_atomic_or_acqrel:
- case BuiltinProc_atomic_or_relaxed:
case BuiltinProc_atomic_xor:
- case BuiltinProc_atomic_xor_acq:
- case BuiltinProc_atomic_xor_rel:
- case BuiltinProc_atomic_xor_acqrel:
- case BuiltinProc_atomic_xor_relaxed:
- case BuiltinProc_atomic_xchg:
- case BuiltinProc_atomic_xchg_acq:
- case BuiltinProc_atomic_xchg_rel:
- case BuiltinProc_atomic_xchg_acqrel:
- case BuiltinProc_atomic_xchg_relaxed: {
+ case BuiltinProc_atomic_exchange:
+ case BuiltinProc_atomic_add_explicit:
+ case BuiltinProc_atomic_sub_explicit:
+ case BuiltinProc_atomic_and_explicit:
+ case BuiltinProc_atomic_nand_explicit:
+ case BuiltinProc_atomic_or_explicit:
+ case BuiltinProc_atomic_xor_explicit:
+ case BuiltinProc_atomic_exchange_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
lbValue val = lb_build_expr(p, ce->args[1]);
val = lb_emit_conv(p, val, type_deref(dst.type));
@@ -1714,41 +1693,20 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMAtomicOrdering ordering = {};
switch (id) {
- case BuiltinProc_atomic_add: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_add_acq: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_add_rel: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_add_acqrel: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_add_relaxed: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_sub: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_sub_acq: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_sub_rel: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_sub_acqrel: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_sub_relaxed: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_and: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_and_acq: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_and_rel: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_and_acqrel: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_and_relaxed: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_nand: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_nand_acq: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_nand_rel: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_nand_acqrel: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_nand_relaxed: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_or: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_or_acq: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_or_rel: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_or_acqrel: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_or_relaxed: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_xor: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_xor_acq: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_xor_rel: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_xor_acqrel: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_xor_relaxed: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_xchg: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_xchg_acq: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_xchg_rel: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_xchg_acqrel: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_xchg_relaxed: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingMonotonic; break;
+ case BuiltinProc_atomic_add: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_sub: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_and: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_nand: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_or: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_xor: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_exchange: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_add_explicit: op = LLVMAtomicRMWBinOpAdd; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_sub_explicit: op = LLVMAtomicRMWBinOpSub; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_and_explicit: op = LLVMAtomicRMWBinOpAnd; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_nand_explicit: op = LLVMAtomicRMWBinOpNand; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_or_explicit: op = LLVMAtomicRMWBinOpOr; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_xor_explicit: op = LLVMAtomicRMWBinOpXor; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_exchange_explicit: op = LLVMAtomicRMWBinOpXchg; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
}
lbValue res = {};
@@ -1757,24 +1715,10 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return res;
}
- case BuiltinProc_atomic_cxchg:
- case BuiltinProc_atomic_cxchg_acq:
- case BuiltinProc_atomic_cxchg_rel:
- case BuiltinProc_atomic_cxchg_acqrel:
- case BuiltinProc_atomic_cxchg_relaxed:
- case BuiltinProc_atomic_cxchg_failrelaxed:
- case BuiltinProc_atomic_cxchg_failacq:
- case BuiltinProc_atomic_cxchg_acq_failrelaxed:
- case BuiltinProc_atomic_cxchg_acqrel_failrelaxed:
- case BuiltinProc_atomic_cxchgweak:
- case BuiltinProc_atomic_cxchgweak_acq:
- case BuiltinProc_atomic_cxchgweak_rel:
- case BuiltinProc_atomic_cxchgweak_acqrel:
- case BuiltinProc_atomic_cxchgweak_relaxed:
- case BuiltinProc_atomic_cxchgweak_failrelaxed:
- case BuiltinProc_atomic_cxchgweak_failacq:
- case BuiltinProc_atomic_cxchgweak_acq_failrelaxed:
- case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: {
+ case BuiltinProc_atomic_compare_exchange_strong:
+ case BuiltinProc_atomic_compare_exchange_weak:
+ case BuiltinProc_atomic_compare_exchange_strong_explicit:
+ case BuiltinProc_atomic_compare_exchange_weak_explicit: {
lbValue address = lb_build_expr(p, ce->args[0]);
Type *elem = type_deref(address.type);
lbValue old_value = lb_build_expr(p, ce->args[1]);
@@ -1787,28 +1731,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMBool weak = false;
switch (id) {
- case BuiltinProc_atomic_cxchg: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_acq: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_relaxed: success_ordering = LLVMAtomicOrderingMonotonic; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchg_failrelaxed: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchg_failacq: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire; weak = false; break;
- case BuiltinProc_atomic_cxchg_acq_failrelaxed: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchg_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchgweak: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchgweak_acq: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_relaxed: success_ordering = LLVMAtomicOrderingMonotonic; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_failrelaxed: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_failacq: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_acq_failrelaxed: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
+ case BuiltinProc_atomic_compare_exchange_strong: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
+ case BuiltinProc_atomic_compare_exchange_weak: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
+ case BuiltinProc_atomic_compare_exchange_strong_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = false; break;
+ case BuiltinProc_atomic_compare_exchange_weak_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = true; break;
}
// TODO(bill): Figure out how to make it weak
- LLVMBool single_threaded = weak;
+ LLVMBool single_threaded = false;
LLVMValueRef value = LLVMBuildAtomicCmpXchg(
p->builder, address.value,
@@ -1817,6 +1747,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
failure_ordering,
single_threaded
);
+ LLVMSetWeak(value, weak);
if (tv.type->kind == Type_Tuple) {
Type *fix_typed = alloc_type_tuple();
@@ -1961,6 +1892,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return res;
}
+ case BuiltinProc___entry_point:
+ if (p->module->info->entry_point) {
+ lbValue entry_point = lb_find_procedure_value_from_entity(p->module, p->module->info->entry_point);
+ GB_ASSERT(entry_point.value != nullptr);
+ lb_emit_call(p, entry_point, {});
+ }
+ return {};
+
case BuiltinProc_syscall:
{
unsigned arg_count = cast(unsigned)ce->args.count;
@@ -2021,7 +1960,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
}
break;
- case TargetArch_386:
+ case TargetArch_i386:
{
GB_ASSERT(arg_count <= 7);
@@ -2054,26 +1993,47 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
break;
case TargetArch_arm64:
{
- GB_ASSERT(arg_count <= 7);
-
- char asm_string[] = "svc #0";
- gbString constraints = gb_string_make(heap_allocator(), "={x0}");
- for (unsigned i = 0; i < arg_count; i++) {
- constraints = gb_string_appendc(constraints, ",{");
- static char const *regs[] = {
- "x8",
- "x0",
- "x1",
- "x2",
- "x3",
- "x4",
- "x5",
- };
- constraints = gb_string_appendc(constraints, regs[i]);
- constraints = gb_string_appendc(constraints, "}");
- }
-
- inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
+ GB_ASSERT(arg_count <= 7);
+
+ if(build_context.metrics.os == TargetOs_darwin) {
+ char asm_string[] = "svc #0x80";
+ gbString constraints = gb_string_make(heap_allocator(), "={x0}");
+ for (unsigned i = 0; i < arg_count; i++) {
+ constraints = gb_string_appendc(constraints, ",{");
+ static char const *regs[] = {
+ "x16",
+ "x0",
+ "x1",
+ "x2",
+ "x3",
+ "x4",
+ "x5",
+ };
+ constraints = gb_string_appendc(constraints, regs[i]);
+ constraints = gb_string_appendc(constraints, "}");
+ }
+
+ inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
+ } else {
+ char asm_string[] = "svc #0";
+ gbString constraints = gb_string_make(heap_allocator(), "={x0}");
+ for (unsigned i = 0; i < arg_count; i++) {
+ constraints = gb_string_appendc(constraints, ",{");
+ static char const *regs[] = {
+ "x8",
+ "x0",
+ "x1",
+ "x2",
+ "x3",
+ "x4",
+ "x5",
+ };
+ constraints = gb_string_appendc(constraints, regs[i]);
+ constraints = gb_string_appendc(constraints, "}");
+ }
+
+ inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
+ }
}
break;
default:
@@ -2085,6 +2045,124 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
res.type = t_uintptr;
return res;
}
+
+ case BuiltinProc_objc_send:
+ return lb_handle_objc_send(p, expr);
+
+ case BuiltinProc_objc_find_selector: return lb_handle_objc_find_selector(p, expr);
+ case BuiltinProc_objc_find_class: return lb_handle_objc_find_class(p, expr);
+ case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr);
+ case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr);
+
+
+ case BuiltinProc_constant_utf16_cstring:
+ {
+ auto const encode_surrogate_pair = [](Rune r, u16 *r1, u16 *r2) {
+ if (r < 0x10000 || r > 0x10ffff) {
+ *r1 = 0xfffd;
+ *r2 = 0xfffd;
+ } else {
+ r -= 0x10000;
+ *r1 = 0xd800 + ((r>>10)&0x3ff);
+ *r2 = 0xdc00 + (r&0x3ff);
+ }
+ };
+
+ lbModule *m = p->module;
+
+ auto tav = type_and_value_of_expr(ce->args[0]);
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String value = tav.value.value_string;
+
+ LLVMTypeRef llvm_u16 = lb_type(m, t_u16);
+
+ isize max_len = value.len*2 + 1;
+ LLVMValueRef *buffer = gb_alloc_array(temporary_allocator(), LLVMValueRef, max_len);
+ isize n = 0;
+ while (value.len > 0) {
+ Rune r = 0;
+ isize w = gb_utf8_decode(value.text, value.len, &r);
+ value.text += w;
+ value.len -= w;
+ if ((0 <= r && r < 0xd800) || (0xe000 <= r && r < 0x10000)) {
+ buffer[n++] = LLVMConstInt(llvm_u16, cast(u16)r, false);
+ } else if (0x10000 <= r && r <= 0x10ffff) {
+ u16 r1, r2;
+ encode_surrogate_pair(r, &r1, &r2);
+ buffer[n++] = LLVMConstInt(llvm_u16, r1, false);
+ buffer[n++] = LLVMConstInt(llvm_u16, r2, false);
+ } else {
+ buffer[n++] = LLVMConstInt(llvm_u16, 0xfffd, false);
+ }
+ }
+
+ buffer[n++] = LLVMConstInt(llvm_u16, 0, false);
+
+ LLVMValueRef array = LLVMConstArray(llvm_u16, buffer, cast(unsigned int)n);
+
+ char *name = nullptr;
+ {
+ isize max_len = 7+8+1;
+ name = gb_alloc_array(permanent_allocator(), char, max_len);
+ u32 id = m->gen->global_array_index.fetch_add(1);
+ isize len = gb_snprintf(name, max_len, "csbs$%x", id);
+ len -= 1;
+ }
+ LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(array), name);
+ LLVMSetInitializer(global_data, array);
+ LLVMSetLinkage(global_data, LLVMInternalLinkage);
+
+
+
+ LLVMValueRef indices[] = {
+ LLVMConstInt(lb_type(m, t_u32), 0, false),
+ LLVMConstInt(lb_type(m, t_u32), 0, false),
+ };
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildInBoundsGEP(p->builder, global_data, indices, gb_count_of(indices), "");
+ return res;
+
+ }
+
+ case BuiltinProc_wasm_memory_grow:
+ {
+ char const *name = "llvm.wasm.memory.grow";
+ LLVMTypeRef types[1] = {
+ lb_type(p->module, t_uintptr),
+ };
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[2] = {};
+ args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value;
+ args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_uintptr).value;
+
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ return res;
+ }
+ case BuiltinProc_wasm_memory_size:
+ {
+ char const *name = "llvm.wasm.memory.size";
+ LLVMTypeRef types[1] = {
+ lb_type(p->module, t_uintptr),
+ };
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[1] = {};
+ args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value;
+
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ return res;
+ }
+
}
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp
index 016e464b8..2afb5300b 100644
--- a/src/llvm_backend_stmt.cpp
+++ b/src/llvm_backend_stmt.cpp
@@ -1652,13 +1652,16 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) {
}
lbValue cond = lb_build_cond(p, is->cond, then, else_);
+ // Note `cond.value` only set for non-and/or conditions and const negs so that the `LLVMIsConstant()`
+ // and `LLVMConstIntGetZExtValue()` calls below will be valid and `LLVMInstructionEraseFromParent()`
+ // will target the correct (& only) branch statement
if (is->label != nullptr) {
lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
tl->is_block = true;
}
- if (LLVMIsConstant(cond.value)) {
+ if (cond.value && LLVMIsConstant(cond.value)) {
// NOTE(bill): Do a compile time short circuit for when the condition is constantly known.
// This done manually rather than relying on the SSA passes because sometimes the SSA passes
// miss some even if they are constantly known, especially with few optimization passes.
@@ -1766,6 +1769,8 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) {
}
void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs, lbValue const &value) {
+ GB_ASSERT(op != Token_Eq);
+
Type *lhs_type = lb_addr_type(lhs);
Type *array_type = base_type(lhs_type);
GB_ASSERT(is_type_array_like(array_type));
@@ -1795,7 +1800,6 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
}
indices[index_count++] = index;
}
- gb_sort_array(indices, index_count, gb_i32_cmp(0));
lbValue lhs_ptrs[4] = {};
lbValue x_loads[4] = {};
@@ -1840,7 +1844,6 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
}
indices[index_count++] = index;
}
- gb_sort_array(indices.data, index_count, gb_i32_cmp(0));
lbValue lhs_ptrs[4] = {};
lbValue x_loads[4] = {};
@@ -1868,11 +1871,7 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
lbValue x = lb_addr_get_ptr(p, lhs);
-
-
if (inline_array_arith) {
- #if 1
- #if 1
unsigned n = cast(unsigned)count;
auto lhs_ptrs = slice_make<lbValue>(temporary_allocator(), n);
@@ -1896,50 +1895,6 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
for (unsigned i = 0; i < n; i++) {
lb_emit_store(p, lhs_ptrs[i], ops[i]);
}
-
- #else
- lbValue y = lb_address_from_load_or_generate_local(p, rhs);
-
- unsigned n = cast(unsigned)count;
-
- auto lhs_ptrs = slice_make<lbValue>(temporary_allocator(), n);
- auto rhs_ptrs = slice_make<lbValue>(temporary_allocator(), n);
- auto x_loads = slice_make<lbValue>(temporary_allocator(), n);
- auto y_loads = slice_make<lbValue>(temporary_allocator(), n);
- auto ops = slice_make<lbValue>(temporary_allocator(), n);
-
- for (unsigned i = 0; i < n; i++) {
- lhs_ptrs[i] = lb_emit_array_epi(p, x, i);
- }
- for (unsigned i = 0; i < n; i++) {
- rhs_ptrs[i] = lb_emit_array_epi(p, y, i);
- }
- for (unsigned i = 0; i < n; i++) {
- x_loads[i] = lb_emit_load(p, lhs_ptrs[i]);
- }
- for (unsigned i = 0; i < n; i++) {
- y_loads[i] = lb_emit_load(p, rhs_ptrs[i]);
- }
- for (unsigned i = 0; i < n; i++) {
- ops[i] = lb_emit_arith(p, op, x_loads[i], y_loads[i], elem_type);
- }
- for (unsigned i = 0; i < n; i++) {
- lb_emit_store(p, lhs_ptrs[i], ops[i]);
- }
- #endif
- #else
- lbValue y = lb_address_from_load_or_generate_local(p, rhs);
-
- for (i64 i = 0; i < count; i++) {
- lbValue a_ptr = lb_emit_array_epi(p, x, i);
- lbValue b_ptr = lb_emit_array_epi(p, y, i);
-
- lbValue a = lb_emit_load(p, a_ptr);
- lbValue b = lb_emit_load(p, b_ptr);
- lbValue c = lb_emit_arith(p, op, a, b, elem_type);
- lb_emit_store(p, a_ptr, c);
- }
- #endif
} else {
lbValue y = lb_address_from_load_or_generate_local(p, rhs);
@@ -2039,6 +1994,13 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
out |= StateFlag_no_bounds_check;
out &= ~StateFlag_bounds_check;
}
+ if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ } else if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ }
p->state_flags = out;
}
@@ -2188,6 +2150,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
lb_emit_defer_stmts(p, lbDeferExit_Branch, block);
}
lb_emit_jump(p, block);
+ lb_start_block(p, lb_create_block(p, "unreachable"));
case_end;
}
}
@@ -2219,10 +2182,6 @@ void lb_build_defer_stmt(lbProcedure *p, lbDefer const &d) {
lb_start_block(p, b);
if (d.kind == lbDefer_Node) {
lb_build_stmt(p, d.stmt);
- } else if (d.kind == lbDefer_Instr) {
- // NOTE(bill): Need to make a new copy
- LLVMValueRef instr = LLVMInstructionClone(d.instr.value);
- LLVMInsertIntoBuilder(p->builder, instr);
} else if (d.kind == lbDefer_Proc) {
lb_emit_call(p, d.proc.deferred, d.proc.result_as_args);
}
diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp
index decb57702..7d73956e8 100644
--- a/src/llvm_backend_type.cpp
+++ b/src/llvm_backend_type.cpp
@@ -1,11 +1,10 @@
isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true) {
- isize index = type_info_index(info, type, false);
+ auto *set = &info->minimum_dependency_type_info_set;
+ isize index = type_info_index(info, type, err_on_not_found);
if (index >= 0) {
- auto *set = &info->minimum_dependency_type_info_set;
- for_array(i, set->entries) {
- if (set->entries[i].ptr == index) {
- return i+1;
- }
+ isize i = ptr_entry_index(set, index);
+ if (i >= 0) {
+ return i+1;
}
}
if (err_on_not_found) {
@@ -15,6 +14,8 @@ isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=tr
}
lbValue lb_typeid(lbModule *m, Type *type) {
+ GB_ASSERT(!build_context.disallow_rtti);
+
type = default_type(type);
u64 id = cast(u64)lb_type_info_index(m->info, type);
@@ -89,6 +90,8 @@ lbValue lb_typeid(lbModule *m, Type *type) {
}
lbValue lb_type_info(lbModule *m, Type *type) {
+ GB_ASSERT(!build_context.disallow_rtti);
+
type = default_type(type);
isize index = lb_type_info_index(m->info, type);
@@ -107,6 +110,8 @@ lbValue lb_type_info(lbModule *m, Type *type) {
}
lbValue lb_get_type_info_ptr(lbModule *m, Type *type) {
+ GB_ASSERT(!build_context.disallow_rtti);
+
i32 index = cast(i32)lb_type_info_index(m->info, type);
GB_ASSERT(index >= 0);
// gb_printf_err("%d %s\n", index, type_to_string(type));
@@ -156,6 +161,10 @@ lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) {
void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data
+ if (build_context.disallow_rtti) {
+ return;
+ }
+
lbModule *m = p->module;
CheckerInfo *info = m->info;
@@ -455,7 +464,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
case Type_EnumeratedArray: {
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr);
- LLVMValueRef vals[6] = {
+ LLVMValueRef vals[7] = {
lb_get_type_info_ptr(m, t->EnumeratedArray.elem).value,
lb_get_type_info_ptr(m, t->EnumeratedArray.index).value,
lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value,
@@ -464,6 +473,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
// 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 = {};
@@ -630,7 +641,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr);
{
- LLVMValueRef vals[7] = {};
+ LLVMValueRef vals[8] = {};
isize variant_count = gb_max(0, t->Union.variants.count);
lbValue memory_types = lb_type_info_member_types_offset(p, variant_count);
@@ -664,8 +675,9 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
}
vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
- vals[5] = lb_const_bool(m, t_bool, t->Union.no_nil).value;
- vals[6] = lb_const_bool(m, t_bool, t->Union.maybe).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_maybe).value;
+ vals[7] = 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) {
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index 0350f7287..037171637 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -626,6 +626,12 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
lbValue value_ = lb_address_from_load_or_generate_local(p, value);
+ if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) {
+ // just do a bit cast of the data at the front
+ lbValue ptr = lb_emit_conv(p, value_, alloc_type_pointer(type));
+ return lb_emit_load(p, ptr);
+ }
+
lbValue tag = {};
lbValue dst_tag = {};
lbValue cond = {};
@@ -666,23 +672,29 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
lb_start_block(p, end_block);
if (!is_tuple) {
- {
- // NOTE(bill): Panic on invalid conversion
- Type *dst_type = tuple->Tuple.variables[0]->type;
+ GB_ASSERT((p->state_flags & StateFlag_no_type_assert) == 0);
+ // NOTE(bill): Panic on invalid conversion
+ Type *dst_type = tuple->Tuple.variables[0]->type;
+
+ isize arg_count = 7;
+ if (build_context.disallow_rtti) {
+ arg_count = 4;
+ }
- lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
- auto args = array_make<lbValue>(permanent_allocator(), 7);
- args[0] = ok;
+ lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
+ auto args = array_make<lbValue>(permanent_allocator(), arg_count);
+ args[0] = ok;
- args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
- args[2] = lb_const_int(m, t_i32, pos.line);
- args[3] = lb_const_int(m, t_i32, pos.column);
+ args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
+ args[2] = lb_const_int(m, t_i32, pos.line);
+ args[3] = lb_const_int(m, t_i32, pos.column);
+ if (!build_context.disallow_rtti) {
args[4] = lb_typeid(m, src_type);
args[5] = lb_typeid(m, dst_type);
args[6] = lb_emit_conv(p, value_, t_rawptr);
- lb_emit_runtime_call(p, "type_assertion_check2", args);
}
+ lb_emit_runtime_call(p, "type_assertion_check2", args);
return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0));
}
@@ -706,6 +718,13 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
}
Type *dst_type = tuple->Tuple.variables[0]->type;
+ if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) {
+ // just do a bit cast of the data at the front
+ lbValue ptr = lb_emit_struct_ev(p, value, 0);
+ ptr = lb_emit_conv(p, ptr, alloc_type_pointer(type));
+ return lb_addr(ptr);
+ }
+
lbAddr v = lb_add_local_generated(p, tuple, true);
lbValue dst_typeid = lb_typeid(m, dst_type);
@@ -731,18 +750,24 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
if (!is_tuple) {
// NOTE(bill): Panic on invalid conversion
-
lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
- auto args = array_make<lbValue>(permanent_allocator(), 7);
+
+ isize arg_count = 7;
+ if (build_context.disallow_rtti) {
+ arg_count = 4;
+ }
+ auto args = array_make<lbValue>(permanent_allocator(), arg_count);
args[0] = ok;
args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
args[2] = lb_const_int(m, t_i32, pos.line);
args[3] = lb_const_int(m, t_i32, pos.column);
- args[4] = any_typeid;
- args[5] = dst_typeid;
- args[6] = lb_emit_struct_ev(p, value, 0);;
+ if (!build_context.disallow_rtti) {
+ args[4] = any_typeid;
+ args[5] = dst_typeid;
+ args[6] = lb_emit_struct_ev(p, value, 0);
+ }
lb_emit_runtime_call(p, "type_assertion_check2", args);
return lb_addr(lb_emit_struct_ep(p, v.addr, 0));
@@ -1200,7 +1225,7 @@ lbValue lb_emit_array_epi(lbProcedure *p, lbValue s, isize index) {
}
lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index) {
- index = lb_correct_endianness(p, index);
+ index = lb_emit_conv(p, index, t_int);
LLVMValueRef indices[1] = {index.value};
lbValue res = {};
res.type = ptr.type;
@@ -1362,7 +1387,7 @@ lbValue lb_slice_elem(lbProcedure *p, lbValue slice) {
return lb_emit_struct_ev(p, slice, 0);
}
lbValue lb_slice_len(lbProcedure *p, lbValue slice) {
- GB_ASSERT(is_type_slice(slice.type));
+ GB_ASSERT(is_type_slice(slice.type) || is_type_relative_slice(slice.type));
return lb_emit_struct_ev(p, slice, 1);
}
lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {
@@ -1494,7 +1519,7 @@ lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValue c, Type *t
case TargetArch_arm64:
// possible
break;
- case TargetArch_386:
+ case TargetArch_i386:
case TargetArch_wasm32:
case TargetArch_wasm64:
is_possible = false;
@@ -1768,7 +1793,7 @@ LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const &str, Strin
return LLVMGetInlineAsm(func_type,
cast(char *)str.text, cast(size_t)str.len,
cast(char *)clobbers.text, cast(size_t)clobbers.len,
- /*HasSideEffects*/true, /*IsAlignStack*/false,
+ has_side_effects, is_align_stack,
dialect
#if LLVM_VERSION_MAJOR >= 13
, /*CanThrow*/false
@@ -1808,3 +1833,197 @@ void lb_set_wasm_export_attributes(LLVMValueRef value, String export_name) {
LLVMSetVisibility(value, LLVMDefaultVisibility);
LLVMAddTargetDependentFunctionAttr(value, "wasm-export-name", alloc_cstring(permanent_allocator(), export_name));
}
+
+
+lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name);
+
+
+lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, String const &name) {
+ lbAddr *found = string_map_get(&p->module->objc_selectors, name);
+ if (found) {
+ return *found;
+ } else {
+ lbModule *default_module = &p->module->gen->default_module;
+ Entity *e = nullptr;
+ lbAddr default_addr = lb_add_global_generated(default_module, t_objc_SEL, {}, &e);
+
+ lbValue ptr = lb_find_value_from_entity(p->module, e);
+ lbAddr local_addr = lb_addr(ptr);
+
+ string_map_set(&default_module->objc_selectors, name, default_addr);
+ if (default_module != p->module) {
+ string_map_set(&p->module->objc_selectors, name, local_addr);
+ }
+ return local_addr;
+ }
+}
+
+lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+
+ auto tav = ce->args[0]->tav;
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String name = tav.value.value_string;
+ return lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, name));
+}
+
+lbValue lb_handle_objc_register_selector(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+ lbModule *m = p->module;
+
+ auto tav = ce->args[0]->tav;
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String name = tav.value.value_string;
+ lbAddr dst = lb_handle_objc_find_or_register_selector(p, name);
+
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
+ args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
+ lbValue ptr = lb_emit_runtime_call(p, "sel_registerName", args);
+ lb_addr_store(p, dst, ptr);
+
+ return lb_addr_load(p, dst);
+}
+
+lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) {
+ lbAddr *found = string_map_get(&p->module->objc_classes, name);
+ if (found) {
+ return *found;
+ } else {
+ lbModule *default_module = &p->module->gen->default_module;
+ Entity *e = nullptr;
+ lbAddr default_addr = lb_add_global_generated(default_module, t_objc_SEL, {}, &e);
+
+ lbValue ptr = lb_find_value_from_entity(p->module, e);
+ lbAddr local_addr = lb_addr(ptr);
+
+ string_map_set(&default_module->objc_classes, name, default_addr);
+ if (default_module != p->module) {
+ string_map_set(&p->module->objc_classes, name, local_addr);
+ }
+ return local_addr;
+ }
+}
+
+lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+
+ auto tav = ce->args[0]->tav;
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String name = tav.value.value_string;
+ return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name));
+}
+
+lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+ lbModule *m = p->module;
+
+ auto tav = ce->args[0]->tav;
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String name = tav.value.value_string;
+ lbAddr dst = lb_handle_objc_find_or_register_class(p, name);
+
+ auto args = array_make<lbValue>(permanent_allocator(), 3);
+ args[0] = lb_const_nil(m, t_objc_Class);
+ args[1] = lb_const_nil(m, t_objc_Class);
+ args[2] = lb_const_int(m, t_uint, 0);
+ lbValue ptr = lb_emit_runtime_call(p, "objc_allocateClassPair", args);
+ lb_addr_store(p, dst, ptr);
+
+ return lb_addr_load(p, dst);
+}
+
+
+lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) {
+ TypeAndValue const &tav = type_and_value_of_expr(expr);
+ if (tav.mode == Addressing_Type) {
+ Type *type = tav.type;
+ GB_ASSERT_MSG(type->kind == Type_Named, "%s", type_to_string(type));
+ Entity *e = type->Named.type_name;
+ GB_ASSERT(e->kind == Entity_TypeName);
+ String name = e->TypeName.objc_class_name;
+
+ lbAddr *found = string_map_get(&p->module->objc_classes, name);
+ if (found) {
+ return lb_addr_load(p, *found);
+ } else {
+ lbModule *default_module = &p->module->gen->default_module;
+ Entity *e = nullptr;
+ lbAddr default_addr = lb_add_global_generated(default_module, t_objc_Class, {}, &e);
+
+ lbValue ptr = lb_find_value_from_entity(p->module, e);
+ lbAddr local_addr = lb_addr(ptr);
+
+ string_map_set(&default_module->objc_classes, name, default_addr);
+ if (default_module != p->module) {
+ string_map_set(&p->module->objc_classes, name, local_addr);
+ }
+ return lb_addr_load(p, local_addr);
+ }
+ }
+
+ return lb_build_expr(p, expr);
+}
+
+lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+
+ lbModule *m = p->module;
+ CheckerInfo *info = m->info;
+ ObjcMsgData data = map_must_get(&info->objc_msgSend_types, expr);
+ GB_ASSERT(data.proc_type != nullptr);
+
+ GB_ASSERT(ce->args.count >= 3);
+ auto args = array_make<lbValue>(permanent_allocator(), 0, ce->args.count-1);
+
+ lbValue id = lb_handle_objc_id(p, ce->args[1]);
+ Ast *sel_expr = ce->args[2];
+ GB_ASSERT(sel_expr->tav.value.kind == ExactValue_String);
+ lbValue sel = lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, sel_expr->tav.value.value_string));
+
+ array_add(&args, id);
+ array_add(&args, sel);
+ for (isize i = 3; i < ce->args.count; i++) {
+ lbValue arg = lb_build_expr(p, ce->args[i]);
+ array_add(&args, arg);
+ }
+
+
+ lbValue the_proc = {};
+ switch (data.kind) {
+ default:
+ GB_PANIC("unhandled ObjcMsgKind %u", data.kind);
+ break;
+ case ObjcMsg_normal: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend")); break;
+ case ObjcMsg_fpret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fpret")); break;
+ case ObjcMsg_fp2ret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fp2ret")); break;
+ case ObjcMsg_stret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_stret")); break;
+ }
+
+ the_proc = lb_emit_conv(p, the_proc, data.proc_type);
+
+ return lb_emit_call(p, the_proc, args);
+}
+
+
+
+
+LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
+ GB_ASSERT(value.kind == ExactValue_Integer);
+ i64 v = exact_value_to_i64(value);
+ switch (v) {
+ case OdinAtomicMemoryOrder_relaxed: return LLVMAtomicOrderingUnordered;
+ case OdinAtomicMemoryOrder_consume: return LLVMAtomicOrderingMonotonic;
+ case OdinAtomicMemoryOrder_acquire: return LLVMAtomicOrderingAcquire;
+ case OdinAtomicMemoryOrder_release: return LLVMAtomicOrderingRelease;
+ case OdinAtomicMemoryOrder_acq_rel: return LLVMAtomicOrderingAcquireRelease;
+ case OdinAtomicMemoryOrder_seq_cst: return LLVMAtomicOrderingSequentiallyConsistent;
+ }
+ GB_PANIC("Unknown atomic ordering");
+ return LLVMAtomicOrderingSequentiallyConsistent;
+}
+
+
+LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) {
+ ExactValue value = type_and_value_of_expr(expr).value;
+ return llvm_atomic_ordering_from_odin(value);
+}
diff --git a/src/main.cpp b/src/main.cpp
index 7b4bc92ee..7b0364149 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -46,7 +46,6 @@ gb_global Timings global_timings = {0};
#include "checker.cpp"
#include "docs.cpp"
-
#include "llvm_backend.cpp"
#if defined(GB_SYSTEM_OSX)
@@ -57,16 +56,8 @@ gb_global Timings global_timings = {0};
#endif
#include "query_data.cpp"
-
-
-#if defined(GB_SYSTEM_WINDOWS)
-// NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
-#include "microsoft_craziness.h"
-#endif
-
#include "bug_report.cpp"
-
// NOTE(bill): 'name' is used in debugging and profiling modes
i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
isize const cmd_cap = 64<<20; // 64 MiB should be more than enough
@@ -74,16 +65,16 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
isize cmd_len = 0;
va_list va;
i32 exit_code = 0;
-
+
va_start(va, fmt);
cmd_len = gb_snprintf_va(cmd_line, cmd_cap-1, fmt, va);
va_end(va);
-
+
#if defined(GB_SYSTEM_WINDOWS)
STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
PROCESS_INFORMATION pi = {0};
String16 wcmd = {};
-
+
start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
start_info.wShowWindow = SW_SHOW;
start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
@@ -117,44 +108,48 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
gb_printf_err("%s\n\n", cmd_line);
}
exit_code = system(cmd_line);
+ if (WIFEXITED(exit_code)) {
+ exit_code = WEXITSTATUS(exit_code);
+ }
#endif
-
+
if (exit_code) {
exit(exit_code);
}
-
+
return exit_code;
}
-
-
i32 linker_stage(lbGenerator *gen) {
i32 result = 0;
Timings *timings = &global_timings;
- String output_base = gen->output_base;
+ String output_filename = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
+ debugf("Linking %.*s\n", LIT(output_filename));
+
+ // TOOD(Jeroen): Make a `build_paths[BuildPath_Object] to avoid `%.*s.o`.
if (is_arch_wasm()) {
timings_start_section(timings, str_lit("wasm-ld"));
-
+
#if defined(GB_SYSTEM_WINDOWS)
result = system_exec_command_line_app("wasm-ld",
- "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
+ "\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
LIT(build_context.ODIN_ROOT),
- LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#else
result = system_exec_command_line_app("wasm-ld",
- "wasm-ld \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
- LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ "wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
+ LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#endif
return result;
}
if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
-#ifdef GB_SYSTEM_UNIX
+#if defined(GB_SYSTEM_UNIX)
result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
- LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#else
gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
LIT(target_os_names[build_context.metrics.os]),
@@ -178,28 +173,11 @@ i32 linker_stage(lbGenerator *gen) {
gbString lib_str = gb_string_make(heap_allocator(), "");
defer (gb_string_free(lib_str));
- char const *output_ext = "exe";
gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
defer (gb_string_free(link_settings));
-
- // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
- Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
-
- if (find_result.windows_sdk_version == 0) {
- gb_printf_err("Windows SDK not found.\n");
- exit(1);
- }
-
- if (build_context.ignore_microsoft_magic) {
- find_result = {};
- }
-
// Add library search paths.
- if (find_result.vs_library_path.len > 0) {
- GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
- GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
-
+ if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) {
String path = {};
auto add_path = [&](String path) {
if (path[path.len-1] == '\\') {
@@ -207,16 +185,16 @@ i32 linker_stage(lbGenerator *gen) {
}
link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
};
- add_path(find_result.windows_sdk_um_library_path);
- add_path(find_result.windows_sdk_ucrt_library_path);
- add_path(find_result.vs_library_path);
+ add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename);
+ add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename);
+ add_path(build_context.build_paths[BuildPath_VS_LIB].basename);
}
-
-
+
+
StringSet libs = {};
string_set_init(&libs, heap_allocator(), 64);
defer (string_set_destroy(&libs));
-
+
StringSet asm_files = {};
string_set_init(&asm_files, heap_allocator(), 64);
defer (string_set_destroy(&asm_files));
@@ -241,22 +219,22 @@ i32 linker_stage(lbGenerator *gen) {
string_set_add(&libs, lib);
}
}
-
+
for_array(i, libs.entries) {
String lib = libs.entries[i].value;
lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
}
-
-
+
+
if (build_context.build_mode == BuildMode_DynamicLibrary) {
- output_ext = "dll";
link_settings = gb_string_append_fmt(link_settings, " /DLL");
} else {
link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
}
if (build_context.pdb_filepath != "") {
- link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath));
+ String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]);
+ link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path));
}
if (build_context.no_crt) {
@@ -268,7 +246,7 @@ i32 linker_stage(lbGenerator *gen) {
if (build_context.ODIN_DEBUG) {
link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
}
-
+
for_array(i, asm_files.entries) {
String asm_file = asm_files.entries[i].value;
String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
@@ -283,7 +261,7 @@ i32 linker_stage(lbGenerator *gen) {
LIT(obj_file),
LIT(build_context.extra_assembler_flags)
);
-
+
if (result) {
return result;
}
@@ -297,27 +275,35 @@ i32 linker_stage(lbGenerator *gen) {
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
}
+ String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
+ defer (gb_free(heap_allocator(), vs_exe_path.text));
+
char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
if (!build_context.use_lld) { // msvc
if (build_context.has_resource) {
+ String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
+ String res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
+ defer (gb_free(heap_allocator(), rc_path.text));
+ defer (gb_free(heap_allocator(), res_path.text));
+
result = system_exec_command_line_app("msvc-link",
- "\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
- LIT(output_base),
- LIT(build_context.resource_filepath)
+ "\"rc.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
+ LIT(res_path),
+ LIT(rc_path)
);
-
+
if (result) {
return result;
}
result = system_exec_command_line_app("msvc-link",
- "\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
+ "\"%.*slink.exe\" %s \"%.*s\" -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
" %.*s "
" %s "
"",
- LIT(find_result.vs_exe_path), object_files, LIT(output_base), LIT(output_base), output_ext,
+ LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
link_settings,
subsystem_str,
LIT(build_context.link_flags),
@@ -326,13 +312,13 @@ i32 linker_stage(lbGenerator *gen) {
);
} else {
result = system_exec_command_line_app("msvc-link",
- "\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s "
+ "\"%.*slink.exe\" %s -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
" %.*s "
" %s "
"",
- LIT(find_result.vs_exe_path), object_files, LIT(output_base), output_ext,
+ LIT(vs_exe_path), object_files, LIT(output_filename),
link_settings,
subsystem_str,
LIT(build_context.link_flags),
@@ -340,27 +326,27 @@ i32 linker_stage(lbGenerator *gen) {
lib_str
);
}
-
+
if (result) {
return result;
}
-
+
} else { // lld
result = system_exec_command_line_app("msvc-lld-link",
- "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s.%s\" %s "
+ "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
" %.*s "
" %s "
"",
- LIT(build_context.ODIN_ROOT), object_files, LIT(output_base),output_ext,
+ LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
link_settings,
subsystem_str,
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
);
-
+
if (result) {
return result;
}
@@ -384,7 +370,7 @@ i32 linker_stage(lbGenerator *gen) {
// 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 defined(GB_SYSTEM_OSX)
+ if (build_context.metrics.os == TargetOs_darwin) {
if (string_ends_with(lib, str_lit(".framework"))) {
// framework thingie
String lib_name = lib;
@@ -400,25 +386,25 @@ i32 linker_stage(lbGenerator *gen) {
// dynamic or static system lib, just link regularly searching system library paths
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
}
- #else
+ } else {
// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
// since those are statically linked to at link time. shared libraries (.so) has to be
// available at runtime wherever the executable is run, so we make require those to be
// local to the executable (unless the system collection is used, in which case we search
// the system library paths for the library file).
- if (string_ends_with(lib, str_lit(".a"))) {
- // static libs, absolute full path relative to the file in which the lib was imported from
+ if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
+ // static libs and object files, absolute full path relative to the file in which the lib was imported from
lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
} else if (string_ends_with(lib, str_lit(".so"))) {
// dynamic lib, relative path to executable
// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
- // at runtimeto the executable
+ // at runtime to the executable
lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
} else {
// dynamic or static system lib, just link regularly searching system library paths
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
}
- #endif
+ }
}
gbString object_files = gb_string_make(heap_allocator(), "");
@@ -428,80 +414,74 @@ i32 linker_stage(lbGenerator *gen) {
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
}
- // Unlike the Win32 linker code, the output_ext includes the dot, because
- // typically executable files on *NIX systems don't have extensions.
- String output_ext = {};
gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
- char const *linker;
+
+ if (build_context.no_crt) {
+ link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
+ }
+
+ // NOTE(dweiler): We use clang as a frontend for the linker as there are
+ // other runtime and compiler support libraries that need to be linked in
+ // very specific orders such as libgcc_s, ld-linux-so, unwind, etc.
+ // These are not always typically inside /lib, /lib64, or /usr versions
+ // of that, e.g libgcc.a is in /usr/lib/gcc/{version}, and can vary on
+ // the distribution of Linux even. The gcc or clang specs is the only
+ // reliable way to query this information to call ld directly.
if (build_context.build_mode == BuildMode_DynamicLibrary) {
- // NOTE(tetra, 2020-11-06): __$startup_runtime must be called at DLL load time.
- // Clang, for some reason, won't let us pass the '-init' flag that lets us do this,
- // so use ld instead.
- // :UseLDForShared
- linker = "ld";
- link_settings = gb_string_appendc(link_settings, "-init '__$startup_runtime' ");
- // Shared libraries are .dylib on MacOS and .so on Linux.
- #if defined(GB_SYSTEM_OSX)
- output_ext = STR_LIT(".dylib");
- link_settings = gb_string_appendc(link_settings, "-dylib -dynamic ");
- #else
- output_ext = STR_LIT(".so");
- link_settings = gb_string_appendc(link_settings, "-shared ");
- #endif
- } else {
- #if defined(GB_SYSTEM_OSX)
- linker = "ld";
- #else
- // TODO(zangent): Figure out how to make ld work on Linux.
- // It probably has to do with including the entire CRT, but
- // that's quite a complicated issue to solve while remaining distro-agnostic.
- // Clang can figure out linker flags for us, and that's good enough _for now_.
- linker = "clang -Wno-unused-command-line-argument";
- #endif
- }
-
- if (build_context.metrics.os == TargetOs_linux) {
+ // NOTE(dweiler): Let the frontend know we're building a shared library
+ // so it doesn't generate symbols which cannot be relocated.
+ link_settings = gb_string_appendc(link_settings, "-shared ");
+
+ // NOTE(dweiler): _odin_entry_point must be called at initialization
+ // time of the shared object, similarly, _odin_exit_point must be called
+ // at deinitialization. We can pass both -init and -fini to the linker by
+ // using a comma separated list of arguments to -Wl.
+ //
+ // This previously used ld but ld cannot actually build a shared library
+ // correctly this way since all the other dependencies provided implicitly
+ // by the compiler frontend are still needed and most of the command
+ // line arguments prepared previously are incompatible with ld.
+ link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
+ 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.
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, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
+ } else {
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
+ }
- if (build_context.out_filepath.len > 0) {
- //NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that
- isize pos = string_extension_position(build_context.out_filepath);
- if (pos > 0) {
- output_ext = substring(build_context.out_filepath, pos, build_context.out_filepath.len);
+ 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.
+ // NOTE: If you change this (although this minimum is as low as you can go with Odin working)
+ // make sure to also change the 'mtriple' param passed to 'opt'
+ if (build_context.metrics.arch == TargetArch_arm64) {
+ link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0 ");
+ } else {
+ link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.8.0 ");
}
+ // This points the linker to where the entry point is
+ link_settings = gb_string_appendc(link_settings, " -e _main ");
}
- result = system_exec_command_line_app("ld-link",
- "%s %s -o \"%.*s%.*s\" %s "
- " %s "
- " %.*s "
- " %.*s "
- " %s "
- #if defined(GB_SYSTEM_OSX)
- // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
- // NOTE: If you change this (although this minimum is as low as you can go with Odin working)
- // make sure to also change the 'mtriple' param passed to 'opt'
- #if defined(GB_CPU_ARM)
- " -macosx_version_min 11.0.0 "
- #else
- " -macosx_version_min 10.8.0 "
- #endif
- // This points the linker to where the entry point is
- " -e _main "
- #endif
- , linker, object_files, LIT(output_base), LIT(output_ext),
- #if defined(GB_SYSTEM_OSX)
- "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib",
- #else
- "-lc -lm",
- #endif
- lib_str,
- LIT(build_context.link_flags),
- LIT(build_context.extra_linker_flags),
- link_settings);
-
+ gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument ");
+ defer (gb_string_free(link_command_line));
+
+ link_command_line = gb_string_appendc(link_command_line, object_files);
+ link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
+ link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
+ link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
+ link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
+ 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 (result) {
return result;
}
@@ -510,10 +490,8 @@ i32 linker_stage(lbGenerator *gen) {
if (build_context.ODIN_DEBUG) {
// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
// to the symbols in the object file
- result = system_exec_command_line_app("dsymutil",
- "dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
- );
-
+ result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename));
+
if (result) {
return result;
}
@@ -571,54 +549,26 @@ void usage(String argv0) {
print_usage_line(0, "Usage:");
print_usage_line(1, "%.*s command [arguments]", LIT(argv0));
print_usage_line(0, "Commands:");
- print_usage_line(1, "build compile .odin file, or directory of .odin files, as an executable.");
- print_usage_line(1, " one must contain the program's entry point, all must be in the same package.");
- print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
- print_usage_line(1, "check parse and type check .odin file");
- print_usage_line(1, "query parse, type check, and output a .json file containing information about the program");
- print_usage_line(1, "doc generate documentation .odin file, or directory of .odin files");
- print_usage_line(1, "version print version");
- print_usage_line(1, "report print information useful to reporting a bug");
+ print_usage_line(1, "build compile directory of .odin files, as an executable.");
+ print_usage_line(1, " one must contain the program's entry point, all must be in the same package.");
+ print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
+ print_usage_line(1, "check parse, and type check a directory of .odin files");
+ print_usage_line(1, "query parse, type check, and output a .json file containing information about the program");
+ print_usage_line(1, "strip-semicolon parse, type check, and remove unneeded semicolons from the entire program");
+ print_usage_line(1, "test build ands runs procedures with the attribute @(test) in the initial package");
+ print_usage_line(1, "doc generate documentation on a directory of .odin files");
+ print_usage_line(1, "version print version");
+ print_usage_line(1, "report print information useful to reporting a bug");
print_usage_line(0, "");
print_usage_line(0, "For further details on a command, use -help after the command name");
print_usage_line(1, "e.g. odin build -help");
}
-
-bool string_is_valid_identifier(String str) {
- if (str.len <= 0) return false;
-
- isize rune_count = 0;
-
- isize w = 0;
- isize offset = 0;
- while (offset < str.len) {
- Rune r = 0;
- w = utf8_decode(str.text, str.len, &r);
- if (r == GB_RUNE_INVALID) {
- return false;
- }
-
- if (rune_count == 0) {
- if (!rune_is_letter(r)) {
- return false;
- }
- } else {
- if (!rune_is_letter(r) && !rune_is_digit(r)) {
- return false;
- }
- }
- rune_count += 1;
- offset += w;
- }
-
- return true;
-}
-
enum BuildFlagKind {
BuildFlag_Invalid,
BuildFlag_Help,
+ BuildFlag_SingleFile,
BuildFlag_OutFile,
BuildFlag_OptimizationLevel,
@@ -654,6 +604,10 @@ enum BuildFlagKind {
BuildFlag_ExtraLinkerFlags,
BuildFlag_ExtraAssemblerFlags,
BuildFlag_Microarch,
+ BuildFlag_TargetFeatures,
+
+ BuildFlag_RelocMode,
+ BuildFlag_DisableRedZone,
BuildFlag_TestName,
@@ -662,6 +616,8 @@ enum BuildFlagKind {
BuildFlag_InsertSemicolon,
BuildFlag_StrictStyle,
BuildFlag_StrictStyleInitOnly,
+ BuildFlag_ForeignErrorProcedures,
+ BuildFlag_DisallowRTTI,
BuildFlag_Compact,
BuildFlag_GlobalDefinitions,
@@ -674,9 +630,9 @@ enum BuildFlagKind {
BuildFlag_IgnoreWarnings,
BuildFlag_WarningsAsErrors,
BuildFlag_VerboseErrors,
-
+
// internal use only
- BuildFlag_InternalIgnoreLazy,
+ BuildFlag_InternalIgnoreLazy,
#if defined(GB_SYSTEM_WINDOWS)
BuildFlag_IgnoreVsSearch,
@@ -771,69 +727,80 @@ ExactValue build_param_to_exact_value(String name, String param) {
bool parse_build_flags(Array<String> args) {
auto build_flags = array_make<BuildFlag>(heap_allocator(), 0, BuildFlag_COUNT);
- add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all);
- add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test);
- add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build);
- add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("O"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_ExportTimings, str_lit("export-timings"), BuildFlagParam_String, Command__does_check);
- add_flag(&build_flags, BuildFlag_ExportTimingsFile, str_lit("export-timings-file"), BuildFlagParam_String, Command__does_check);
- add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check);
- add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check);
- add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all);
- add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer, Command_all);
- add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None, Command__does_build|Command_strip_semicolon);
- add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String, Command__does_check);
- add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String, Command__does_check, true);
- add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String, Command__does_build); // Commands_build is not used to allow for a better error message
- add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String, Command__does_check);
- add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
- add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_UseSeparateModules,str_lit("use-separate-modules"),BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_ThreadedChecker, str_lit("threaded-checker"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all);
- add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build);
-
- add_flag(&build_flags, BuildFlag_TestName, str_lit("test-name"), BuildFlagParam_String, Command_test);
-
- 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_InsertSemicolon, str_lit("insert-semicolon"), 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_StrictStyleInitOnly, str_lit("strict-style-init-only"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None, Command_query);
- add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query);
- add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query);
-
- 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_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_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all);
-
- add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_SingleFile, str_lit("file"), BuildFlagParam_None, Command__does_build | Command__does_check);
+ add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test);
+ add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build);
+ add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("O"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ExportTimings, str_lit("export-timings"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ExportTimingsFile, str_lit("export-timings-file"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check);
+ add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check);
+ add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer, Command_all);
+ add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None, Command__does_build | Command_strip_semicolon);
+ add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String, Command__does_check, true);
+ add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String, Command__does_build); // Commands_build is not used to allow for a better error message
+ add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
+ add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_UseSeparateModules, str_lit("use-separate-modules"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ThreadedChecker, str_lit("threaded-checker"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_TargetFeatures, str_lit("target-features"), BuildFlagParam_String, Command__does_build);
+
+ add_flag(&build_flags, BuildFlag_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_DisableRedZone, str_lit("disable-red-zone"), BuildFlagParam_None, Command__does_build);
+
+ add_flag(&build_flags, BuildFlag_TestName, str_lit("test-name"), BuildFlagParam_String, Command_test);
+
+ 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_InsertSemicolon, str_lit("insert-semicolon"), 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_StrictStyleInitOnly, str_lit("strict-style-init-only"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ForeignErrorProcedures, str_lit("foreign-error-procedures"), BuildFlagParam_None, Command__does_check);
+
+ add_flag(&build_flags, BuildFlag_DisallowRTTI, str_lit("disallow-rtti"), BuildFlagParam_None, Command__does_check);
+
+
+ add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None, Command_query);
+ add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query);
+ add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query);
+
+
+ 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_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_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all);
+
+ add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
#if defined(GB_SYSTEM_WINDOWS)
- add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String, Command__does_build);
#endif
@@ -1280,7 +1247,7 @@ bool parse_build_flags(Array<String> args) {
} else {
gb_printf_err("Unknown build mode '%.*s'\n", LIT(str));
gb_printf_err("Valid build modes:\n");
- gb_printf_err("\tdll, shared\n");
+ gb_printf_err("\tdll, shared, dynamic\n");
gb_printf_err("\tobj, object\n");
gb_printf_err("\texe\n");
gb_printf_err("\tasm, assembly, assembler\n");
@@ -1355,7 +1322,7 @@ bool parse_build_flags(Array<String> args) {
GB_ASSERT(value.kind == ExactValue_String);
build_context.extra_linker_flags = value.value_string;
break;
- case BuildFlag_ExtraAssemblerFlags:
+ case BuildFlag_ExtraAssemblerFlags:
GB_ASSERT(value.kind == ExactValue_String);
build_context.extra_assembler_flags = value.value_string;
break;
@@ -1365,6 +1332,37 @@ bool parse_build_flags(Array<String> args) {
string_to_lower(&build_context.microarch);
break;
}
+ case BuildFlag_TargetFeatures: {
+ GB_ASSERT(value.kind == ExactValue_String);
+ build_context.target_features = value.value_string;
+ string_to_lower(&build_context.target_features);
+ break;
+ }
+ case BuildFlag_RelocMode: {
+ GB_ASSERT(value.kind == ExactValue_String);
+ String v = value.value_string;
+ if (v == "default") {
+ build_context.reloc_mode = RelocMode_Default;
+ } else if (v == "static") {
+ build_context.reloc_mode = RelocMode_Static;
+ } else if (v == "pic") {
+ build_context.reloc_mode = RelocMode_PIC;
+ } else if (v == "dynamic-no-pic") {
+ build_context.reloc_mode = RelocMode_DynamicNoPIC;
+ } else {
+ gb_printf_err("-reloc-mode flag expected one of the following\n");
+ gb_printf_err("\tdefault\n");
+ gb_printf_err("\tstatic\n");
+ gb_printf_err("\tpic\n");
+ gb_printf_err("\tdynamic-no-pic\n");
+ bad_flags = true;
+ }
+
+ break;
+ }
+ case BuildFlag_DisableRedZone:
+ build_context.disable_red_zone = true;
+ break;
case BuildFlag_TestName: {
GB_ASSERT(value.kind == ExactValue_String);
{
@@ -1383,9 +1381,15 @@ bool parse_build_flags(Array<String> args) {
case BuildFlag_DisallowDo:
build_context.disallow_do = true;
break;
+ case BuildFlag_DisallowRTTI:
+ build_context.disallow_rtti = true;
+ break;
case BuildFlag_DefaultToNilAllocator:
build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true;
break;
+ case BuildFlag_ForeignErrorProcedures:
+ build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true;
+ break;
case BuildFlag_InsertSemicolon: {
gb_printf_err("-insert-semicolon flag is not required any more\n");
bad_flags = true;
@@ -1486,6 +1490,10 @@ bool parse_build_flags(Array<String> args) {
gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
bad_flags = true;
break;
+ } else if (!gb_file_exists((const char *)path.text)) {
+ gb_printf_err("Invalid -resource path %.*s, file does not exist.\n", LIT(path));
+ bad_flags = true;
+ break;
}
build_context.resource_filepath = substring(path, 0, string_extension_position(path));
build_context.has_resource = true;
@@ -1500,6 +1508,11 @@ bool parse_build_flags(Array<String> args) {
String path = value.value_string;
path = string_trim_whitespace(path);
if (is_build_flag_path_valid(path)) {
+ if (path_is_directory(path)) {
+ gb_printf_err("Invalid -pdb-name path. %.*s, is a directory.\n", LIT(path));
+ bad_flags = true;
+ break;
+ }
// #if defined(GB_SYSTEM_WINDOWS)
// String ext = path_extension(path);
// if (ext != ".pdb") {
@@ -1817,7 +1830,7 @@ void show_timings(Checker *c, Timings *t) {
void remove_temp_files(lbGenerator *gen) {
if (build_context.keep_temp_files) return;
-
+
TIME_SECTION("remove keep temp files");
for_array(i, gen->output_temp_paths) {
@@ -1841,32 +1854,47 @@ void remove_temp_files(lbGenerator *gen) {
void print_show_help(String const arg0, String const &command) {
print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(arg0));
- print_usage_line(0, "Usage");
+ print_usage_line(0, "Usage:");
print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command));
print_usage_line(0, "");
if (command == "build") {
- print_usage_line(1, "build compile .odin file, or directory of .odin files, as an executable.");
- print_usage_line(1, " one must contain the program's entry point, all must be in the same package.");
+ print_usage_line(1, "build Compile directory of .odin files as an executable.");
+ print_usage_line(2, "One must contain the program's entry point, all must be in the same package.");
+ print_usage_line(2, "Use `-file` to build a single file instead.");
+ print_usage_line(2, "Examples:");
+ print_usage_line(3, "odin build . # Build package in current directory");
+ print_usage_line(3, "odin build <dir> # Build package in <dir>");
+ print_usage_line(3, "odin build filename.odin -file # Build single-file package, must contain entry point.");
} else if (command == "run") {
- print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
+ print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable.");
+ print_usage_line(2, "Append an empty flag and then the args, '-- <args>', to specify args for the output.");
+ print_usage_line(2, "Examples:");
+ print_usage_line(3, "odin run . # Build and run package in current directory");
+ print_usage_line(3, "odin run <dir> # Build and run package in <dir>");
+ print_usage_line(3, "odin run filename.odin -file # Build and run single-file package, must contain entry point.");
} else if (command == "check") {
- print_usage_line(1, "check parse and type check .odin file(s)");
+ print_usage_line(1, "check Parse and type check directory of .odin files");
+ print_usage_line(2, "Examples:");
+ print_usage_line(3, "odin check . # Type check package in current directory");
+ print_usage_line(3, "odin check <dir> # Type check package in <dir>");
+ print_usage_line(3, "odin check filename.odin -file # Type check single-file package, must contain entry point.");
} else if (command == "test") {
- print_usage_line(1, "test build ands runs procedures with the attribute @(test) in the initial package");
+ print_usage_line(1, "test Build ands runs procedures with the attribute @(test) in the initial package");
} else if (command == "query") {
- print_usage_line(1, "query [experimental] parse, type check, and output a .json file containing information about the program");
+ print_usage_line(1, "query [experimental] Parse, type check, and output a .json file containing information about the program");
} else if (command == "doc") {
- print_usage_line(1, "doc generate documentation from a .odin file, or directory of .odin files");
+ print_usage_line(1, "doc generate documentation from a directory of .odin files");
print_usage_line(2, "Examples:");
- print_usage_line(3, "odin doc core/path");
- print_usage_line(3, "odin doc core/path core/path/filepath");
+ print_usage_line(3, "odin doc . # Generate documentation on package in current directory");
+ print_usage_line(3, "odin doc <dir> # Generate documentation on package in <dir>");
+ print_usage_line(3, "odin doc filename.odin -file # Generate documentation on single-file package.");
} else if (command == "version") {
print_usage_line(1, "version print version");
} else if (command == "strip-semicolon") {
print_usage_line(1, "strip-semicolon");
- print_usage_line(2, "parse and type check .odin file(s) and then remove unneeded semicolons from the entire project");
- }
+ print_usage_line(2, "Parse and type check .odin file(s) and then remove unneeded semicolons from the entire project");
+ }
bool doc = command == "doc";
bool build = command == "build";
@@ -1880,6 +1908,13 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "Flags");
print_usage_line(0, "");
+ if (check) {
+ print_usage_line(1, "-file");
+ print_usage_line(2, "Tells `%.*s %.*s` to treat the given file as a self-contained package.", LIT(arg0), LIT(command));
+ print_usage_line(2, "This means that `<dir>/a.odin` won't have access to `<dir>/b.odin`'s contents.");
+ print_usage_line(0, "");
+ }
+
if (doc) {
print_usage_line(1, "-short");
print_usage_line(2, "Show shortened documentation for the packages");
@@ -1970,6 +2005,7 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-define:<name>=<expression>");
print_usage_line(2, "Defines a global constant with a value");
print_usage_line(2, "Example: -define:SPAM=123");
+ print_usage_line(2, "To use: #config(SPAM, default_value)");
print_usage_line(0, "");
}
@@ -2071,7 +2107,7 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-extra-linker-flags:<string>");
print_usage_line(2, "Adds extra linker specific flags in a string");
print_usage_line(0, "");
-
+
print_usage_line(1, "-extra-assembler-flags:<string>");
print_usage_line(2, "Adds extra assembler specific flags in a string");
print_usage_line(0, "");
@@ -2083,6 +2119,18 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(3, "-microarch:sandybridge");
print_usage_line(3, "-microarch:native");
print_usage_line(0, "");
+
+ print_usage_line(1, "-reloc-mode:<string>");
+ print_usage_line(2, "Specifies the reloc mode");
+ print_usage_line(2, "Options:");
+ print_usage_line(3, "default");
+ print_usage_line(3, "static");
+ print_usage_line(3, "pic");
+ print_usage_line(3, "dynamic-no-pic");
+ print_usage_line(0, "");
+
+ print_usage_line(1, "-disable-red-zone");
+ print_usage_line(2, "Disable red zone on a supported freestanding target");
}
if (check) {
@@ -2097,7 +2145,7 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-strict-style");
print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons");
print_usage_line(0, "");
-
+
print_usage_line(1, "-strict-style-init-only");
print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons, only on the initial project");
print_usage_line(0, "");
@@ -2113,6 +2161,11 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-verbose-errors");
print_usage_line(2, "Prints verbose error messages showing the code on that line and the location in that line");
print_usage_line(0, "");
+
+ print_usage_line(1, "-foreign-error-procedures");
+ print_usage_line(2, "States that the error procedues used in the runtime are defined in a separate translation unit");
+ print_usage_line(0, "");
+
}
if (run_or_build) {
@@ -2262,7 +2315,7 @@ gbFileError write_file_with_stripped_tokens(gbFile *f, AstFile *file, i64 *writt
}
written += to_write;
prev_offset = token_pos_end(*token).offset;
- }
+ }
if (token->flags & TokenFlag_Replace) {
if (token->kind == Token_Ellipsis) {
if (!gb_file_write(f, "..=", 3)) {
@@ -2281,7 +2334,7 @@ gbFileError write_file_with_stripped_tokens(gbFile *f, AstFile *file, i64 *writt
}
written += to_write;
}
-
+
if (written_) *written_ = written;
return err;
}
@@ -2292,14 +2345,14 @@ int strip_semicolons(Parser *parser) {
AstPackage *pkg = parser->packages[i];
file_count += pkg->files.count;
}
-
+
auto generated_files = array_make<StripSemicolonFile>(permanent_allocator(), 0, file_count);
-
+
for_array(i, parser->packages) {
AstPackage *pkg = parser->packages[i];
for_array(j, pkg->files) {
AstFile *file = pkg->files[j];
-
+
bool nothing_to_change = true;
for_array(i, file->tokens) {
Token *token = &file->tokens[i];
@@ -2308,29 +2361,29 @@ int strip_semicolons(Parser *parser) {
break;
}
}
-
+
if (nothing_to_change) {
continue;
}
-
+
String old_fullpath = copy_string(permanent_allocator(), file->fullpath);
-
+
// assumes .odin extension
String fullpath_base = substring(old_fullpath, 0, old_fullpath.len-5);
-
+
String old_fullpath_backup = concatenate_strings(permanent_allocator(), fullpath_base, str_lit("~backup.odin-temp"));
String new_fullpath = concatenate_strings(permanent_allocator(), fullpath_base, str_lit("~temp.odin-temp"));
-
+
array_add(&generated_files, StripSemicolonFile{old_fullpath, old_fullpath_backup, new_fullpath, file});
}
}
-
+
gb_printf_err("File count to be stripped of unneeded tokens: %td\n", generated_files.count);
-
-
+
+
isize generated_count = 0;
bool failed = false;
-
+
for_array(i, generated_files) {
auto *file = &generated_files[i];
char const *filename = cast(char const *)file->new_fullpath.text;
@@ -2338,15 +2391,15 @@ int strip_semicolons(Parser *parser) {
defer (if (err != gbFileError_None) {
failed = true;
});
-
- gbFile f = {};
+
+ gbFile f = {};
err = gb_file_create(&f, filename);
if (err) {
break;
}
defer (err = gb_file_close(&f));
generated_count += 1;
-
+
i64 written = 0;
defer (err = gb_file_truncate(&f, written));
@@ -2367,23 +2420,23 @@ int strip_semicolons(Parser *parser) {
}
return 1;
}
-
+
isize overwritten_files = 0;
-
+
for_array(i, generated_files) {
auto *file = &generated_files[i];
-
+
char const *old_fullpath = cast(char const *)file->old_fullpath.text;
char const *old_fullpath_backup = cast(char const *)file->old_fullpath_backup.text;
char const *new_fullpath = cast(char const *)file->new_fullpath.text;
-
+
debugf("Copy '%s' to '%s'\n", old_fullpath, old_fullpath_backup);
if (!gb_file_copy(old_fullpath, old_fullpath_backup, false)) {
gb_printf_err("failed to copy '%s' to '%s'\n", old_fullpath, old_fullpath_backup);
failed = true;
break;
}
-
+
debugf("Copy '%s' to '%s'\n", new_fullpath, old_fullpath);
if (!gb_file_copy(new_fullpath, old_fullpath, false)) {
@@ -2400,19 +2453,19 @@ int strip_semicolons(Parser *parser) {
if (!gb_file_remove(old_fullpath_backup)) {
gb_printf_err("failed to remove '%s'\n", old_fullpath_backup);
}
-
+
overwritten_files++;
}
-
+
if (!build_context.keep_temp_files) {
for_array(i, generated_files) {
auto *file = &generated_files[i];
char const *filename = nullptr;
filename = cast(char const *)file->new_fullpath.text;
-
+
debugf("Remove '%s'\n", filename);
GB_ASSERT_MSG(gb_file_remove(filename), "unable to delete file %s", filename);
-
+
filename = cast(char const *)file->old_fullpath_backup.text;
debugf("Remove '%s'\n", filename);
if (gb_file_exists(filename) && !gb_file_remove(filename)) {
@@ -2423,14 +2476,12 @@ int strip_semicolons(Parser *parser) {
}
}
}
-
+
gb_printf_err("Files stripped of unneeded token: %td\n", generated_files.count);
-
-
- return cast(int)failed;
-}
+ return cast(int)failed;
+}
int main(int arg_count, char const **arg_ptr) {
if (arg_count < 2) {
@@ -2442,10 +2493,11 @@ int main(int arg_count, char const **arg_ptr) {
defer (timings_destroy(&global_timings));
MAIN_TIME_SECTION("initialization");
-
+
virtual_memory_init();
mutex_init(&fullpath_mutex);
mutex_init(&hash_exact_value_mutex);
+ mutex_init(&global_type_name_objc_metadata_mutex);
init_string_buffer_memory();
init_string_interner();
@@ -2471,6 +2523,7 @@ int main(int arg_count, char const **arg_ptr) {
String command = args[1];
String init_filename = {};
String run_args_string = {};
+ isize last_non_run_arg = args.count;
bool run_output = false;
if (command == "run" || command == "test") {
@@ -2486,7 +2539,6 @@ 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 last_non_run_arg = args.count;
for_array(i, args) {
if (args[i] == "--") {
last_non_run_arg = i;
@@ -2499,6 +2551,7 @@ int main(int arg_count, char const **arg_ptr) {
args = array_slice(args, 0, last_non_run_arg);
run_args_string = string_join_and_quote(heap_allocator(), run_args);
+
init_filename = args[2];
run_output = true;
@@ -2586,11 +2639,40 @@ int main(int arg_count, char const **arg_ptr) {
return 1;
}
+ init_filename = copy_string(permanent_allocator(), init_filename);
+
if (init_filename == "-help" ||
init_filename == "--help") {
build_context.show_help = true;
}
+ if (init_filename.len > 0 && !build_context.show_help) {
+ // The command must be build, run, test, check, or another that takes a directory or filename.
+ if (!path_is_directory(init_filename)) {
+ // Input package is a filename. We allow this only if `-file` was given, otherwise we exit with an error message.
+ bool single_file_package = false;
+ for_array(i, args) {
+ if (i >= 3 && i <= last_non_run_arg && args[i] == "-file") {
+ single_file_package = true;
+ break;
+ }
+ }
+
+ if (!single_file_package) {
+ gb_printf_err("ERROR: `%.*s %.*s` takes a package as its first argument.\n", LIT(args[0]), LIT(command));
+ gb_printf_err("Did you mean `%.*s %.*s %.*s -file`?\n", LIT(args[0]), LIT(command), LIT(init_filename));
+ gb_printf_err("The `-file` flag tells it to treat a file as a self-contained package.\n");
+ return 1;
+ } else {
+ String const ext = str_lit(".odin");
+ if (!string_ends_with(init_filename, ext)) {
+ gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
+ return 1;
+ }
+ }
+ }
+ }
+
build_context.command = command;
if (!parse_build_flags(args)) {
@@ -2605,19 +2687,30 @@ 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")));
}
-
init_build_context(selected_target_metrics ? selected_target_metrics->metrics : nullptr);
// if (build_context.word_size == 4 && build_context.metrics.os != TargetOs_js) {
// print_usage_line(0, "%.*s 32-bit is not yet supported for this platform", LIT(args[0]));
// return 1;
// }
+ // Set and check build paths...
+ if (!init_build_paths(init_filename)) {
+ return 1;
+ }
+
+ if (build_context.show_debug_messages) {
+ for_array(i, build_context.build_paths) {
+ String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]);
+ debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path));
+ }
+ }
+
init_global_thread_pool();
defer (thread_pool_destroy(&global_thread_pool));
-
+
init_universal();
// TODO(bill): prevent compiling without a linker
@@ -2631,6 +2724,8 @@ int main(int arg_count, char const **arg_ptr) {
}
defer (destroy_parser(parser));
+ // TODO(jeroen): Remove the `init_filename` param.
+ // Let's put that on `build_context.build_paths[0]` instead.
if (parse_packages(parser, init_filename) != ParseFile_None) {
return 1;
}
@@ -2649,7 +2744,7 @@ int main(int arg_count, char const **arg_ptr) {
if (any_errors()) {
return 1;
}
-
+
if (build_context.command_kind == Command_strip_semicolon) {
return strip_semicolons(parser);
}
@@ -2703,22 +2798,20 @@ int main(int arg_count, char const **arg_ptr) {
}
remove_temp_files(gen);
-
+
if (build_context.show_timings) {
show_timings(checker, &global_timings);
}
if (run_output) {
+ String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
+ defer (gb_free(heap_allocator(), exe_name.text));
+
#if defined(GB_SYSTEM_WINDOWS)
- return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(gen->output_base), LIT(run_args_string));
+ return system_exec_command_line_app("odin run", "%.*s %.*s", LIT(exe_name), LIT(run_args_string));
#else
- //NOTE(thebirk): This whole thing is a little leaky
- String output_ext = {};
- String complete_path = concatenate_strings(permanent_allocator(), gen->output_base, output_ext);
- complete_path = path_to_full_path(permanent_allocator(), complete_path);
- return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
+ return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string));
#endif
}
-
return 0;
}
diff --git a/src/microsoft_craziness.h b/src/microsoft_craziness.h
index 02f14dda3..b4f815284 100644
--- a/src/microsoft_craziness.h
+++ b/src/microsoft_craziness.h
@@ -460,7 +460,7 @@ bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *res
wchar_t *library_path = nullptr;
if (build_context.metrics.arch == TargetArch_amd64) {
library_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\lib\\x64\\");
- } else if (build_context.metrics.arch == TargetArch_386) {
+ } else if (build_context.metrics.arch == TargetArch_i386) {
library_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\lib\\x86\\");
} else {
continue;
@@ -472,7 +472,7 @@ bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *res
wchar_t *link_exe_path = nullptr;
if (build_context.metrics.arch == TargetArch_amd64) {
link_exe_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\bin\\Hostx64\\x64\\");
- } else if (build_context.metrics.arch == TargetArch_386) {
+ } else if (build_context.metrics.arch == TargetArch_i386) {
link_exe_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\bin\\Hostx86\\x86\\");
} else {
continue;
@@ -529,7 +529,7 @@ bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *res
if (build_context.metrics.arch == TargetArch_amd64) {
lib_path = concat(buffer, L"VC\\Lib\\amd64\\");
- } else if (build_context.metrics.arch == TargetArch_386) {
+ } else if (build_context.metrics.arch == TargetArch_i386) {
lib_path = concat(buffer, L"VC\\Lib\\");
} else {
continue;
@@ -542,7 +542,7 @@ bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *res
if (os_file_exists(vcruntime_filename)) {
if (build_context.metrics.arch == TargetArch_amd64) {
result->vs_exe_path = concat(buffer, L"VC\\bin\\");
- } else if (build_context.metrics.arch == TargetArch_386) {
+ } else if (build_context.metrics.arch == TargetArch_i386) {
// result->vs_exe_path = concat(buffer, L"VC\\bin\\amd64_x86\\");
result->vs_exe_path = concat(buffer, L"VC\\bin\\x86_amd64\\");
} else {
@@ -573,7 +573,7 @@ Find_Result find_visual_studio_and_windows_sdk() {
if (build_context.metrics.arch == TargetArch_amd64) {
result.windows_sdk_um_library_path = concat(result.windows_sdk_root, L"um\\x64\\");
result.windows_sdk_ucrt_library_path = concat(result.windows_sdk_root, L"ucrt\\x64\\");
- } else if (build_context.metrics.arch == TargetArch_386) {
+ } else if (build_context.metrics.arch == TargetArch_i386) {
result.windows_sdk_um_library_path = concat(result.windows_sdk_root, L"um\\x86\\");
result.windows_sdk_ucrt_library_path = concat(result.windows_sdk_root, L"ucrt\\x86\\");
}
diff --git a/src/parser.cpp b/src/parser.cpp
index cbd4d61d5..df7f908a6 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -57,6 +57,9 @@ isize ast_node_size(AstKind kind) {
return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *));
}
+
+gb_global std::atomic<isize> global_total_node_memory_allocated;
+
// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
Ast *alloc_ast_node(AstFile *f, AstKind kind) {
gbAllocator a = ast_allocator(f);
@@ -66,6 +69,9 @@ Ast *alloc_ast_node(AstFile *f, AstKind kind) {
Ast *node = cast(Ast *)gb_alloc(a, size);
node->kind = kind;
node->file_id = f ? f->id : 0;
+
+ global_total_node_memory_allocated += size;
+
return node;
}
@@ -183,6 +189,11 @@ Ast *clone_ast(Ast *node) {
n->FieldValue.value = clone_ast(n->FieldValue.value);
break;
+ case Ast_EnumFieldValue:
+ n->EnumFieldValue.name = clone_ast(n->EnumFieldValue.name);
+ n->EnumFieldValue.value = clone_ast(n->EnumFieldValue.value);
+ break;
+
case Ast_TernaryIfExpr:
n->TernaryIfExpr.x = clone_ast(n->TernaryIfExpr.x);
n->TernaryIfExpr.cond = clone_ast(n->TernaryIfExpr.cond);
@@ -693,6 +704,16 @@ Ast *ast_field_value(AstFile *f, Ast *field, Ast *value, Token eq) {
return result;
}
+
+Ast *ast_enum_field_value(AstFile *f, Ast *name, Ast *value, CommentGroup *docs, CommentGroup *comment) {
+ Ast *result = alloc_ast_node(f, Ast_EnumFieldValue);
+ result->EnumFieldValue.name = name;
+ result->EnumFieldValue.value = value;
+ result->EnumFieldValue.docs = docs;
+ result->EnumFieldValue.comment = comment;
+ return result;
+}
+
Ast *ast_compound_lit(AstFile *f, Ast *type, Array<Ast *> const &elems, Token open, Token close) {
Ast *result = alloc_ast_node(f, Ast_CompoundLit);
result->CompoundLit.type = type;
@@ -944,7 +965,7 @@ Ast *ast_field(AstFile *f, Array<Ast *> const &names, Ast *type, Ast *default_va
result->Field.default_value = default_value;
result->Field.flags = flags;
result->Field.tag = tag;
- result->Field.docs = docs;
+ result->Field.docs = docs;
result->Field.comment = comment;
return result;
}
@@ -1050,15 +1071,14 @@ Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_c
}
-Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> const &variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe,
+Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> const &variants, Ast *polymorphic_params, Ast *align, UnionTypeKind kind,
Token where_token, Array<Ast *> const &where_clauses) {
Ast *result = alloc_ast_node(f, Ast_UnionType);
result->UnionType.token = token;
result->UnionType.variants = slice_from_array(variants);
result->UnionType.polymorphic_params = polymorphic_params;
result->UnionType.align = align;
- result->UnionType.no_nil = no_nil;
- result->UnionType.maybe = maybe;
+ result->UnionType.kind = kind;
result->UnionType.where_token = where_token;
result->UnionType.where_clauses = slice_from_array(where_clauses);
return result;
@@ -1234,7 +1254,7 @@ CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_line_) {
return comments;
}
-void comsume_comment_groups(AstFile *f, Token prev) {
+void consume_comment_groups(AstFile *f, Token prev) {
if (f->curr_token.kind == Token_Comment) {
CommentGroup *comment = nullptr;
isize end_line = 0;
@@ -1278,7 +1298,7 @@ Token advance_token(AstFile *f) {
if (ok) {
switch (f->curr_token.kind) {
case Token_Comment:
- comsume_comment_groups(f, prev);
+ consume_comment_groups(f, prev);
break;
case Token_Semicolon:
if (ignore_newlines(f) && f->curr_token.string == "\n") {
@@ -1517,7 +1537,7 @@ void fix_advance_to_next_stmt(AstFile *f) {
Token expect_closing(AstFile *f, TokenKind kind, String context) {
if (f->curr_token.kind != kind &&
f->curr_token.kind == Token_Semicolon &&
- f->curr_token.string == "\n") {
+ (f->curr_token.string == "\n" || f->curr_token.kind == Token_EOF)) {
Token tok = f->prev_token;
tok.pos.column += cast(i32)tok.string.len;
syntax_error(tok, "Missing ',' before newline in %.*s", LIT(context));
@@ -1539,6 +1559,7 @@ void assign_removal_flag_to_semicolon(AstFile *f) {
switch (curr_token->kind) {
case Token_CloseBrace:
case Token_CloseParen:
+ case Token_EOF:
ok = true;
break;
}
@@ -1555,7 +1576,7 @@ void assign_removal_flag_to_semicolon(AstFile *f) {
}
}
-void expect_semicolon(AstFile *f, Ast *s) {
+void expect_semicolon(AstFile *f) {
Token prev_token = {};
if (allow_token(f, Token_Semicolon)) {
@@ -1580,17 +1601,17 @@ void expect_semicolon(AstFile *f, Ast *s) {
if (f->curr_token.kind == Token_EOF) {
return;
}
-
- if (s != nullptr) {
- return;
- }
switch (f->curr_token.kind) {
case Token_EOF:
return;
}
- String p = token_to_string(f->curr_token);
- syntax_error(prev_token, "Expected ';', got %.*s", LIT(p));
- fix_advance_to_next_stmt(f);
+
+ if (f->curr_token.pos.line == f->prev_token.pos.line) {
+ String p = token_to_string(f->curr_token);
+ prev_token.pos = token_pos_end(prev_token);
+ syntax_error(prev_token, "Expected ';', got %.*s", LIT(p));
+ fix_advance_to_next_stmt(f);
+ }
}
@@ -1689,6 +1710,46 @@ Array<Ast *> parse_element_list(AstFile *f) {
return elems;
}
+CommentGroup *consume_line_comment(AstFile *f) {
+ CommentGroup *comment = f->line_comment;
+ if (f->line_comment == f->lead_comment) {
+ f->lead_comment = nullptr;
+ }
+ f->line_comment = nullptr;
+ return comment;
+
+}
+
+Array<Ast *> parse_enum_field_list(AstFile *f) {
+ auto elems = array_make<Ast *>(heap_allocator());
+
+ while (f->curr_token.kind != Token_CloseBrace &&
+ f->curr_token.kind != Token_EOF) {
+ CommentGroup *docs = f->lead_comment;
+ CommentGroup *comment = nullptr;
+ Ast *name = parse_value(f);
+ Ast *value = nullptr;
+ if (f->curr_token.kind == Token_Eq) {
+ Token eq = expect_token(f, Token_Eq);
+ value = parse_value(f);
+ }
+
+ comment = consume_line_comment(f);
+
+ Ast *elem = ast_enum_field_value(f, name, value, docs, comment);
+ array_add(&elems, elem);
+
+ if (!allow_token(f, Token_Comma)) {
+ break;
+ }
+
+ if (!elem->EnumFieldValue.comment) {
+ elem->EnumFieldValue.comment = consume_line_comment(f);
+ }
+ }
+
+ return elems;
+}
Ast *parse_literal_value(AstFile *f, Ast *type) {
Array<Ast *> elems = {};
@@ -1793,6 +1854,8 @@ void parse_proc_tags(AstFile *f, u64 *tags) {
ELSE_IF_ADD_TAG(require_results)
ELSE_IF_ADD_TAG(bounds_check)
ELSE_IF_ADD_TAG(no_bounds_check)
+ ELSE_IF_ADD_TAG(type_assert)
+ ELSE_IF_ADD_TAG(no_type_assert)
else {
syntax_error(tag_expr, "Unknown procedure type tag #%.*s", LIT(tag_name));
}
@@ -1803,6 +1866,10 @@ void parse_proc_tags(AstFile *f, u64 *tags) {
if ((*tags & ProcTag_bounds_check) && (*tags & ProcTag_no_bounds_check)) {
syntax_error(f->curr_token, "You cannot apply both #bounds_check and #no_bounds_check to a procedure");
}
+
+ if ((*tags & ProcTag_type_assert) && (*tags & ProcTag_no_type_assert)) {
+ syntax_error(f->curr_token, "You cannot apply both #type_assert and #no_type_assert to a procedure");
+ }
}
@@ -1950,11 +2017,23 @@ Ast *parse_check_directive_for_statement(Ast *s, Token const &tag_token, u16 sta
syntax_error(tag_token, "#bounds_check and #no_bounds_check cannot be applied together");
}
break;
+ case StateFlag_type_assert:
+ if ((s->state_flags & StateFlag_no_type_assert) != 0) {
+ syntax_error(tag_token, "#type_assert and #no_type_assert cannot be applied together");
+ }
+ break;
+ case StateFlag_no_type_assert:
+ if ((s->state_flags & StateFlag_type_assert) != 0) {
+ syntax_error(tag_token, "#type_assert and #no_type_assert cannot be applied together");
+ }
+ break;
}
switch (state_flag) {
case StateFlag_bounds_check:
case StateFlag_no_bounds_check:
+ case StateFlag_type_assert:
+ case StateFlag_no_type_assert:
switch (s->kind) {
case Ast_BlockStmt:
case Ast_IfStmt:
@@ -2063,6 +2142,22 @@ Ast *parse_operand(AstFile *f, bool lhs) {
return original_type;
} else if (name.string == "partial") {
Ast *tag = ast_basic_directive(f, token, name);
+ Ast *original_expr = parse_expr(f, lhs);
+ Ast *expr = unparen_expr(original_expr);
+ 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;
+ default:
+ syntax_error(expr, "Expected a compound literal after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[expr->kind]));
+ break;
+ }
+ return original_expr;
+ } else if (name.string == "sparse") {
+ Ast *tag = ast_basic_directive(f, token, name);
Ast *original_type = parse_type(f);
Ast *type = unparen_expr(original_type);
switch (type->kind) {
@@ -2078,6 +2173,12 @@ Ast *parse_operand(AstFile *f, bool lhs) {
} else if (name.string == "no_bounds_check") {
Ast *operand = parse_expr(f, lhs);
return parse_check_directive_for_statement(operand, name, StateFlag_no_bounds_check);
+ } else if (name.string == "type_assert") {
+ Ast *operand = parse_expr(f, lhs);
+ return parse_check_directive_for_statement(operand, name, StateFlag_type_assert);
+ } else if (name.string == "no_type_assert") {
+ Ast *operand = parse_expr(f, lhs);
+ return parse_check_directive_for_statement(operand, name, StateFlag_no_type_assert);
} else if (name.string == "relative") {
Ast *tag = ast_basic_directive(f, token, name);
tag = parse_call_expr(f, tag);
@@ -2174,6 +2275,12 @@ Ast *parse_operand(AstFile *f, bool lhs) {
if (tags & ProcTag_bounds_check) {
body->state_flags |= StateFlag_bounds_check;
}
+ if (tags & ProcTag_no_type_assert) {
+ body->state_flags |= StateFlag_no_type_assert;
+ }
+ if (tags & ProcTag_type_assert) {
+ body->state_flags |= StateFlag_type_assert;
+ }
return ast_proc_lit(f, type, body, tags, where_token, where_clauses);
} else if (allow_token(f, Token_do)) {
@@ -2367,6 +2474,9 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Ast *align = nullptr;
bool no_nil = false;
bool maybe = false;
+ bool shared_nil = false;
+
+ UnionTypeKind union_kind = UnionType_Normal;
Token start_token = f->curr_token;
@@ -2393,6 +2503,11 @@ Ast *parse_operand(AstFile *f, bool lhs) {
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
}
no_nil = true;
+ } else if (tag.string == "shared_nil") {
+ if (shared_nil) {
+ syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
+ }
+ shared_nil = true;
} else if (tag.string == "maybe") {
if (maybe) {
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
@@ -2405,6 +2520,21 @@ Ast *parse_operand(AstFile *f, bool lhs) {
if (no_nil && maybe) {
syntax_error(f->curr_token, "#maybe and #no_nil cannot be applied together");
}
+ if (no_nil && shared_nil) {
+ syntax_error(f->curr_token, "#shared_nil and #no_nil cannot be applied together");
+ }
+ if (shared_nil && maybe) {
+ syntax_error(f->curr_token, "#maybe and #shared_nil cannot be applied together");
+ }
+
+
+ if (maybe) {
+ union_kind = UnionType_maybe;
+ } else if (no_nil) {
+ union_kind = UnionType_no_nil;
+ } else if (shared_nil) {
+ union_kind = UnionType_shared_nil;
+ }
skip_possible_newline_for_literal(f);
@@ -2436,7 +2566,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Token close = expect_closing_brace_of_field_list(f);
- return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, maybe, where_token, where_clauses);
+ return ast_union_type(f, token, variants, polymorphic_params, align, union_kind, where_token, where_clauses);
} break;
case Token_enum: {
@@ -2449,7 +2579,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
skip_possible_newline_for_literal(f);
Token open = expect_token(f, Token_OpenBrace);
- Array<Ast *> values = parse_element_list(f);
+ Array<Ast *> values = parse_enum_field_list(f);
Token close = expect_closing_brace_of_field_list(f);
return ast_enum_type(f, token, base_type, values);
@@ -2908,64 +3038,62 @@ i32 token_precedence(AstFile *f, TokenKind t) {
Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
Ast *expr = parse_unary_expr(f, lhs);
- for (i32 prec = token_precedence(f, f->curr_token.kind); prec >= prec_in; prec--) {
- for (;;) {
- Token op = f->curr_token;
- i32 op_prec = token_precedence(f, op.kind);
- if (op_prec != prec) {
- // NOTE(bill): This will also catch operators that are not valid "binary" operators
- break;
+ for (;;) {
+ Token op = f->curr_token;
+ i32 op_prec = token_precedence(f, op.kind);
+ if (op_prec < prec_in) {
+ // NOTE(bill): This will also catch operators that are not valid "binary" operators
+ break;
+ }
+ Token prev = f->prev_token;
+ switch (op.kind) {
+ case Token_if:
+ case Token_when:
+ if (prev.pos.line < op.pos.line) {
+ // NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition
+ goto loop_end;
}
- Token prev = f->prev_token;
+ break;
+ }
+ expect_operator(f); // NOTE(bill): error checks too
+
+ if (op.kind == Token_Question) {
+ Ast *cond = expr;
+ // Token_Question
+ Ast *x = parse_expr(f, lhs);
+ Token token_c = expect_token(f, Token_Colon);
+ Ast *y = parse_expr(f, lhs);
+ expr = ast_ternary_if_expr(f, x, cond, y);
+ } else if (op.kind == Token_if || op.kind == Token_when) {
+ Ast *x = expr;
+ Ast *cond = parse_expr(f, lhs);
+ Token tok_else = expect_token(f, Token_else);
+ Ast *y = parse_expr(f, lhs);
+
switch (op.kind) {
case Token_if:
+ expr = ast_ternary_if_expr(f, x, cond, y);
+ break;
case Token_when:
- if (prev.pos.line < op.pos.line) {
- // NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition
- goto loop_end;
- }
+ expr = ast_ternary_when_expr(f, x, cond, y);
break;
}
- expect_operator(f); // NOTE(bill): error checks too
-
- if (op.kind == Token_Question) {
- Ast *cond = expr;
- // Token_Question
- Ast *x = parse_expr(f, lhs);
- Token token_c = expect_token(f, Token_Colon);
- Ast *y = parse_expr(f, lhs);
- expr = ast_ternary_if_expr(f, x, cond, y);
- } else if (op.kind == Token_if || op.kind == Token_when) {
- Ast *x = expr;
- Ast *cond = parse_expr(f, lhs);
- Token tok_else = expect_token(f, Token_else);
- Ast *y = parse_expr(f, lhs);
-
- switch (op.kind) {
- case Token_if:
- expr = ast_ternary_if_expr(f, x, cond, y);
- break;
- case Token_when:
- expr = ast_ternary_when_expr(f, x, cond, y);
- break;
- }
+ } else {
+ Ast *right = parse_binary_expr(f, false, op_prec+1);
+ if (right == nullptr) {
+ syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string));
+ }
+ if (op.kind == Token_or_else) {
+ // NOTE(bill): easier to handle its logic different with its own AST kind
+ expr = ast_or_else_expr(f, expr, op, right);
} else {
- Ast *right = parse_binary_expr(f, false, prec+1);
- if (right == nullptr) {
- syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string));
- }
- if (op.kind == Token_or_else) {
- // NOTE(bill): easier to handle its logic different with its own AST kind
- expr = ast_or_else_expr(f, expr, op, right);
- } else {
- expr = ast_binary_expr(f, op, expr, right);
- }
+ expr = ast_binary_expr(f, op, expr, right);
}
-
- lhs = false;
}
- loop_end:;
+
+ lhs = false;
}
+ loop_end:;
return expr;
}
@@ -3075,7 +3203,7 @@ Ast *parse_foreign_block(AstFile *f, Token token) {
Ast *body = ast_block_stmt(f, decls, open, close);
Ast *decl = ast_foreign_block_decl(f, token, foreign_library, body, docs);
- expect_semicolon(f, decl);
+ expect_semicolon(f);
return decl;
}
@@ -3121,15 +3249,11 @@ Ast *parse_value_decl(AstFile *f, Array<Ast *> names, CommentGroup *docs) {
}
if (f->expr_level >= 0) {
- Ast *end = nullptr;
- if (!is_mutable && values.count > 0) {
- end = values[values.count-1];
- }
if (f->curr_token.kind == Token_CloseBrace &&
f->curr_token.pos.line == f->prev_token.pos.line) {
} else {
- expect_semicolon(f, end);
+ expect_semicolon(f);
}
}
@@ -3311,12 +3435,18 @@ ProcCallingConvention string_to_calling_convention(String s) {
if (s == "fast") return ProcCC_FastCall;
if (s == "none") return ProcCC_None;
if (s == "naked") return ProcCC_Naked;
+
+ if (s == "win64") return ProcCC_Win64;
+ if (s == "sysv") return ProcCC_SysV;
+
if (s == "system") {
if (build_context.metrics.os == TargetOs_windows) {
return ProcCC_StdCall;
}
return ProcCC_CDecl;
}
+
+
return ProcCC_Invalid;
}
@@ -3403,12 +3533,13 @@ enum FieldPrefixKind : i32 {
FieldPrefix_Unknown = -1,
FieldPrefix_Invalid = 0,
- FieldPrefix_using,
+ FieldPrefix_using, // implies #subtype
FieldPrefix_const,
FieldPrefix_no_alias,
FieldPrefix_c_vararg,
FieldPrefix_auto_cast,
FieldPrefix_any_int,
+ FieldPrefix_subtype, // does not imply `using` semantics
};
struct ParseFieldPrefixMapping {
@@ -3425,6 +3556,7 @@ gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = {
{str_lit("c_vararg"), Token_Hash, FieldPrefix_c_vararg, FieldFlag_c_vararg},
{str_lit("const"), Token_Hash, FieldPrefix_const, FieldFlag_const},
{str_lit("any_int"), Token_Hash, FieldPrefix_any_int, FieldFlag_any_int},
+ {str_lit("subtype"), Token_Hash, FieldPrefix_subtype, FieldFlag_subtype},
};
@@ -4029,11 +4161,7 @@ Ast *parse_return_stmt(AstFile *f) {
advance_token(f);
}
- Ast *end = nullptr;
- if (results.count > 0) {
- end = results[results.count-1];
- }
- expect_semicolon(f, end);
+ expect_semicolon(f);
return ast_return_stmt(f, token, results);
}
@@ -4284,7 +4412,7 @@ Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) {
syntax_error(import_name, "'using import' is not allowed, please use the import name explicitly");
}
- expect_semicolon(f, s);
+ expect_semicolon(f);
return s;
}
@@ -4342,7 +4470,7 @@ Ast *parse_foreign_decl(AstFile *f) {
} else {
s = ast_foreign_import_decl(f, token, filepaths, lib_name, docs, f->line_comment);
}
- expect_semicolon(f, s);
+ expect_semicolon(f);
return s;
}
}
@@ -4481,7 +4609,7 @@ Ast *parse_stmt(AstFile *f) {
case Token_Not:
case Token_And:
s = parse_simple_stmt(f, StmtAllowFlag_Label);
- expect_semicolon(f, s);
+ expect_semicolon(f);
return s;
@@ -4509,7 +4637,7 @@ Ast *parse_stmt(AstFile *f) {
label = parse_ident(f);
}
s = ast_branch_stmt(f, token, label);
- expect_semicolon(f, s);
+ expect_semicolon(f);
return s;
}
@@ -4524,12 +4652,12 @@ Ast *parse_stmt(AstFile *f) {
Array<Ast *> list = parse_lhs_expr_list(f);
if (list.count == 0) {
syntax_error(token, "Illegal use of 'using' statement");
- expect_semicolon(f, nullptr);
+ expect_semicolon(f);
return ast_bad_stmt(f, token, f->curr_token);
}
if (f->curr_token.kind != Token_Colon) {
- expect_semicolon(f, list[list.count-1]);
+ expect_semicolon(f);
return ast_using_stmt(f, token, list);
}
expect_token_after(f, Token_Colon, "identifier list");
@@ -4561,6 +4689,12 @@ Ast *parse_stmt(AstFile *f) {
} else if (tag == "no_bounds_check") {
s = parse_stmt(f);
return parse_check_directive_for_statement(s, name, StateFlag_no_bounds_check);
+ } else if (tag == "type_assert") {
+ s = parse_stmt(f);
+ return parse_check_directive_for_statement(s, name, StateFlag_type_assert);
+ } else if (tag == "no_type_assert") {
+ s = parse_stmt(f);
+ return parse_check_directive_for_statement(s, name, StateFlag_no_type_assert);
} else if (tag == "partial") {
s = parse_stmt(f);
switch (s->kind) {
@@ -4580,13 +4714,13 @@ Ast *parse_stmt(AstFile *f) {
} else if (tag == "assert" || tag == "panic") {
Ast *t = ast_basic_directive(f, hash_token, name);
Ast *stmt = ast_expr_stmt(f, parse_call_expr(f, t));
- expect_semicolon(f, stmt);
+ expect_semicolon(f);
return stmt;
} else if (name.string == "force_inline" ||
name.string == "force_no_inline") {
Ast *expr = parse_force_inlining_operand(f, name);
Ast *stmt = ast_expr_stmt(f, expr);
- expect_semicolon(f, stmt);
+ expect_semicolon(f);
return stmt;
} else if (tag == "unroll") {
return parse_unrolled_for_loop(f, name);
@@ -4608,7 +4742,7 @@ Ast *parse_stmt(AstFile *f) {
case Token_Semicolon:
s = ast_empty_stmt(f, token);
- expect_semicolon(f, nullptr);
+ expect_semicolon(f);
return s;
}
@@ -4736,12 +4870,6 @@ ParseFileError init_ast_file(AstFile *f, String fullpath, TokenPos *err_pos) {
f->prev_token = f->tokens[f->prev_token_index];
f->curr_token = f->tokens[f->curr_token_index];
- isize const page_size = 4*1024;
- isize block_size = 2*f->tokens.count*gb_size_of(Ast);
- block_size = ((block_size + page_size-1)/page_size) * page_size;
- block_size = gb_clamp(block_size, page_size, DEFAULT_MINIMUM_BLOCK_SIZE);
- f->arena.minimum_block_size = block_size;
-
array_init(&f->comments, heap_allocator(), 0, 0);
array_init(&f->imports, heap_allocator(), 0, 0);
@@ -5362,6 +5490,15 @@ isize calc_decl_count(Ast *decl) {
count += calc_decl_count(decl->BlockStmt.stmts.data[i]);
}
break;
+ case Ast_WhenStmt:
+ {
+ isize inner_count = calc_decl_count(decl->WhenStmt.body);
+ if (decl->WhenStmt.else_stmt) {
+ inner_count = gb_max(inner_count, calc_decl_count(decl->WhenStmt.else_stmt));
+ }
+ count += inner_count;
+ }
+ break;
case Ast_ValueDecl:
count = decl->ValueDecl.names.count;
break;
@@ -5389,7 +5526,7 @@ bool parse_file(Parser *p, AstFile *f) {
String filepath = f->tokenizer.fullpath;
String base_dir = dir_from_path(filepath);
if (f->curr_token.kind == Token_Comment) {
- comsume_comment_groups(f, f->prev_token);
+ consume_comment_groups(f, f->prev_token);
}
CommentGroup *docs = f->lead_comment;
@@ -5403,6 +5540,15 @@ bool parse_file(Parser *p, AstFile *f) {
if (f->package_token.kind != Token_package) {
return false;
}
+ if (docs != nullptr) {
+ TokenPos end = token_pos_end(docs->list[docs->list.count-1]);
+ if (end.line == f->package_token.pos.line || end.line+1 == f->package_token.pos.line) {
+ // Okay
+ } else {
+ docs = nullptr;
+ }
+ }
+
Token package_name = expect_token_after(f, Token_Ident, "package");
if (package_name.kind == Token_Ident) {
if (package_name.string == "_") {
@@ -5426,8 +5572,17 @@ bool parse_file(Parser *p, AstFile *f) {
if (!parse_build_tag(tok, lc)) {
return false;
}
- } else if (lc == "+private") {
- f->flags |= AstFile_IsPrivate;
+ } else if (string_starts_with(lc, str_lit("+private"))) {
+ f->flags |= AstFile_IsPrivatePkg;
+ String command = string_trim_starts_with(lc, str_lit("+private "));
+ command = string_trim_whitespace(command);
+ if (lc == "+private") {
+ f->flags |= AstFile_IsPrivatePkg;
+ } else if (command == "package") {
+ f->flags |= AstFile_IsPrivatePkg;
+ } else if (command == "file") {
+ f->flags |= AstFile_IsPrivateFile;
+ }
} else if (lc == "+lazy") {
if (build_context.ignore_lazy) {
// Ignore
@@ -5445,7 +5600,7 @@ bool parse_file(Parser *p, AstFile *f) {
}
Ast *pd = ast_package_decl(f, f->package_token, package_name, docs, f->line_comment);
- expect_semicolon(f, pd);
+ expect_semicolon(f);
f->pkg_decl = pd;
if (f->error_count == 0) {
@@ -5579,8 +5734,24 @@ ParseFileError parse_packages(Parser *p, String init_filename) {
error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
return ParseFile_WrongExtension;
}
+ } else if (init_fullpath.len != 0) {
+ String path = init_fullpath;
+ if (path[path.len-1] == '/') {
+ path.len -= 1;
+ }
+ if ((build_context.command_kind & Command__does_build) &&
+ build_context.build_mode == BuildMode_Executable) {
+ String short_path = filename_from_path(path);
+ char *cpath = alloc_cstring(heap_allocator(), short_path);
+ defer (gb_free(heap_allocator(), cpath));
+
+ 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");
+ return ParseFile_DirectoryAlreadyExists;
+ }
+ }
}
-
+
{ // Add these packages serially and then process them parallel
mutex_lock(&p->wait_mutex);
diff --git a/src/parser.hpp b/src/parser.hpp
index b83822cbf..c7b4fd0d8 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -46,6 +46,7 @@ enum ParseFileError {
ParseFile_InvalidToken,
ParseFile_GeneralError,
ParseFile_FileTooLarge,
+ ParseFile_DirectoryAlreadyExists,
ParseFile_Count,
};
@@ -78,9 +79,11 @@ struct ImportedFile {
};
enum AstFileFlag : u32 {
- AstFile_IsPrivate = 1<<0,
- AstFile_IsTest = 1<<1,
- AstFile_IsLazy = 1<<2,
+ AstFile_IsPrivatePkg = 1<<0,
+ AstFile_IsPrivateFile = 1<<1,
+
+ AstFile_IsTest = 1<<3,
+ AstFile_IsLazy = 1<<4,
};
enum AstDelayQueueKind {
@@ -95,8 +98,6 @@ struct AstFile {
AstPackage * pkg;
Scope * scope;
- Arena arena;
-
Ast * pkg_decl;
String fullpath;
Tokenizer tokenizer;
@@ -226,6 +227,8 @@ enum ProcInlining {
enum ProcTag {
ProcTag_bounds_check = 1<<0,
ProcTag_no_bounds_check = 1<<1,
+ ProcTag_type_assert = 1<<2,
+ ProcTag_no_type_assert = 1<<3,
ProcTag_require_results = 1<<4,
ProcTag_optional_ok = 1<<5,
@@ -245,12 +248,30 @@ enum ProcCallingConvention : i32 {
ProcCC_InlineAsm = 8,
+ ProcCC_Win64 = 9,
+ ProcCC_SysV = 10,
+
+
ProcCC_MAX,
ProcCC_ForeignBlockDefault = -1,
};
+char const *proc_calling_convention_strings[ProcCC_MAX] = {
+ "",
+ "odin",
+ "contextless",
+ "cdecl",
+ "stdcall",
+ "fastcall",
+ "none",
+ "naked",
+ "inlineasm",
+ "win64",
+ "sysv",
+};
+
ProcCallingConvention default_calling_convention(void) {
return ProcCC_Odin;
}
@@ -258,6 +279,8 @@ ProcCallingConvention default_calling_convention(void) {
enum StateFlag : u8 {
StateFlag_bounds_check = 1<<0,
StateFlag_no_bounds_check = 1<<1,
+ StateFlag_type_assert = 1<<2,
+ StateFlag_no_type_assert = 1<<3,
StateFlag_BeenHandled = 1<<7,
};
@@ -276,6 +299,7 @@ enum FieldFlag : u32 {
FieldFlag_auto_cast = 1<<4,
FieldFlag_const = 1<<5,
FieldFlag_any_int = 1<<6,
+ FieldFlag_subtype = 1<<7,
// Internal use by the parser only
FieldFlag_Tags = 1<<10,
@@ -283,7 +307,7 @@ enum FieldFlag : u32 {
// Parameter List Restrictions
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int,
- FieldFlag_Struct = FieldFlag_using|FieldFlag_Tags,
+ FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags,
};
enum StmtAllowFlag {
@@ -306,6 +330,13 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
"intel",
};
+enum UnionTypeKind : u8 {
+ UnionType_Normal = 0,
+ UnionType_maybe = 1,
+ UnionType_no_nil = 2,
+ UnionType_shared_nil = 3,
+};
+
#define AST_KINDS \
AST_KIND(Ident, "identifier", struct { \
Token token; \
@@ -344,6 +375,7 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
Slice<Ast *> elems; \
Token open, close; \
i64 max_count; \
+ Ast *tag; \
}) \
AST_KIND(_ExprBegin, "", bool) \
AST_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \
@@ -383,6 +415,12 @@ AST_KIND(_ExprBegin, "", bool) \
void *sce_temp_data; \
}) \
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
+ AST_KIND(EnumFieldValue, "enum field value", struct { \
+ Ast *name; \
+ Ast *value; \
+ CommentGroup *docs; \
+ CommentGroup *comment; \
+ }) \
AST_KIND(TernaryIfExpr, "ternary if expression", struct { Ast *x, *cond, *y; }) \
AST_KIND(TernaryWhenExpr, "ternary when expression", struct { Ast *x, *cond, *y; }) \
AST_KIND(OrElseExpr, "or_else expression", struct { Ast *x; Token token; Ast *y; }) \
@@ -647,8 +685,7 @@ AST_KIND(_TypeBegin, "", bool) \
Slice<Ast *> variants; \
Ast *polymorphic_params; \
Ast * align; \
- bool maybe; \
- bool no_nil; \
+ UnionTypeKind kind; \
Token where_token; \
Slice<Ast *> where_clauses; \
}) \
@@ -769,10 +806,10 @@ gb_inline bool is_ast_when_stmt(Ast *node) {
return node->kind == Ast_WhenStmt;
}
-gb_global gb_thread_local Arena global_ast_arena = {};
+gb_global gb_thread_local Arena global_thread_local_ast_arena = {};
gbAllocator ast_allocator(AstFile *f) {
- Arena *arena = f ? &f->arena : &global_ast_arena;
+ Arena *arena = &global_thread_local_ast_arena;
return arena_allocator(arena);
}
diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp
index 6ef0db215..54c3ec1f1 100644
--- a/src/parser_pos.cpp
+++ b/src/parser_pos.cpp
@@ -39,6 +39,7 @@ Token ast_token(Ast *node) {
case Ast_SliceExpr: return node->SliceExpr.open;
case Ast_Ellipsis: return node->Ellipsis.token;
case Ast_FieldValue: return node->FieldValue.eq;
+ case Ast_EnumFieldValue: return ast_token(node->EnumFieldValue.name);
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x);
case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x);
@@ -178,6 +179,11 @@ Token ast_end_token(Ast *node) {
}
return node->Ellipsis.token;
case Ast_FieldValue: return ast_end_token(node->FieldValue.value);
+ case Ast_EnumFieldValue:
+ if (node->EnumFieldValue.value) {
+ return ast_end_token(node->EnumFieldValue.value);
+ }
+ return ast_end_token(node->EnumFieldValue.name);
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_end_token(node->TernaryIfExpr.y);
case Ast_TernaryWhenExpr: return ast_end_token(node->TernaryWhenExpr.y);
diff --git a/src/path.cpp b/src/path.cpp
new file mode 100644
index 000000000..6f83c39ea
--- /dev/null
+++ b/src/path.cpp
@@ -0,0 +1,394 @@
+/*
+ Path handling utilities.
+*/
+String remove_extension_from_path(String const &s) {
+ for (isize i = s.len-1; i >= 0; i--) {
+ if (s[i] == '.') {
+ return substring(s, 0, i);
+ }
+ }
+ return s;
+}
+
+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);
+}
+
+bool path_is_directory(String path);
+
+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)
+ 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
+ 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
+
+
+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.
+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`.
+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.
+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;
+ }
+
+ 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.
+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("");
+}
+
+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,
+};
+
+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)
+ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+ GB_ASSERT(fi != nullptr);
+
+ gbAllocator a = heap_allocator();
+
+ 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(a, path);
+ defer (gb_free(a, c_str));
+
+ 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;
+ }
+
+
+ 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;
+ i64 size = cast(i64)file_data.nFileSizeLow;
+ size |= (cast(i64)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 = 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>
+
+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
+
diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp
index ca7df3b53..b45997916 100644
--- a/src/ptr_set.cpp
+++ b/src/ptr_set.cpp
@@ -138,6 +138,15 @@ gb_inline bool ptr_set_exists(PtrSet<T> *s, T ptr) {
return index != MAP_SENTINEL;
}
+template <typename T>
+gb_inline isize ptr_entry_index(PtrSet<T> *s, T ptr) {
+ isize index = ptr_set__find(s, ptr).entry_index;
+ if (index != MAP_SENTINEL) {
+ return index;
+ }
+ return -1;
+}
+
// Returns true if it already exists
template <typename T>
T ptr_set_add(PtrSet<T> *s, T ptr) {
diff --git a/src/queue.cpp b/src/queue.cpp
index d69a2845c..ee8b1b086 100644
--- a/src/queue.cpp
+++ b/src/queue.cpp
@@ -72,14 +72,38 @@ void mpmc_destroy(MPMCQueue<T> *q) {
template <typename T>
+bool mpmc_internal_grow(MPMCQueue<T> *q) {
+ mutex_lock(&q->mutex);
+ i32 old_size = q->mask+1;
+ i32 new_size = old_size*2;
+ resize_array_raw(&q->nodes, q->allocator, old_size, new_size);
+ if (q->nodes == nullptr) {
+ GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
+ mutex_unlock(&q->mutex);
+ return false;
+ }
+ resize_array_raw(&q->indices, q->allocator, old_size, new_size);
+ if (q->indices == nullptr) {
+ GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
+ mutex_unlock(&q->mutex);
+ return false;
+ }
+ mpmc_internal_init_indices(q->indices, old_size, new_size);
+ q->mask = new_size-1;
+ mutex_unlock(&q->mutex);
+ return true;
+}
+
+template <typename T>
i32 mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
GB_ASSERT(q->mask != 0);
i32 head_idx = q->head_idx.load(std::memory_order_relaxed);
for (;;) {
- auto node = &q->nodes[head_idx & q->mask];
- auto node_idx_ptr = &q->indices[head_idx & q->mask];
+ i32 index = head_idx & q->mask;
+ auto node = &q->nodes[index];
+ auto node_idx_ptr = &q->indices[index];
i32 node_idx = node_idx_ptr->load(std::memory_order_acquire);
i32 diff = node_idx - head_idx;
@@ -91,24 +115,9 @@ i32 mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
return q->count.fetch_add(1, std::memory_order_release);
}
} else if (diff < 0) {
- mutex_lock(&q->mutex);
- i32 old_size = q->mask+1;
- i32 new_size = old_size*2;
- resize_array_raw(&q->nodes, q->allocator, old_size, new_size);
- if (q->nodes == nullptr) {
- GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
- mutex_unlock(&q->mutex);
- return -1;
- }
- resize_array_raw(&q->indices, q->allocator, old_size, new_size);
- if (q->indices == nullptr) {
- GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
- mutex_unlock(&q->mutex);
+ if (!mpmc_internal_grow(q)) {
return -1;
}
- mpmc_internal_init_indices(q->indices, old_size, new_size);
- q->mask = new_size-1;
- mutex_unlock(&q->mutex);
} else {
head_idx = q->head_idx.load(std::memory_order_relaxed);
}
diff --git a/src/string.cpp b/src/string.cpp
index 800378689..616761265 100644
--- a/src/string.cpp
+++ b/src/string.cpp
@@ -10,10 +10,6 @@ struct String {
u8 * text;
isize len;
- // u8 &operator[](isize i) {
- // GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i);
- // return text[i];
- // }
u8 const &operator[](isize i) const {
GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i);
return text[i];
@@ -33,10 +29,6 @@ struct String {
struct String16 {
wchar_t *text;
isize len;
- wchar_t &operator[](isize i) {
- GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i);
- return text[i];
- }
wchar_t const &operator[](isize i) const {
GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i);
return text[i];
@@ -195,8 +187,6 @@ template <isize N> bool operator > (String const &a, char const (&b)[N]) { retu
template <isize N> bool operator <= (String const &a, char const (&b)[N]) { return str_le(a, make_string(cast(u8 *)b, N-1)); }
template <isize N> bool operator >= (String const &a, char const (&b)[N]) { return str_ge(a, make_string(cast(u8 *)b, N-1)); }
-
-
gb_inline bool string_starts_with(String const &s, String const &prefix) {
if (prefix.len > s.len) {
return false;
@@ -230,6 +220,16 @@ gb_inline bool string_ends_with(String const &s, u8 suffix) {
return s[s.len-1] == suffix;
}
+
+
+gb_inline String string_trim_starts_with(String const &s, String const &prefix) {
+ if (string_starts_with(s, prefix)) {
+ return substring(s, prefix.len, s.len);
+ }
+ return s;
+}
+
+
gb_inline isize string_extension_position(String const &str) {
isize dot_pos = -1;
isize i = str.len;
@@ -245,15 +245,14 @@ gb_inline isize string_extension_position(String const &str) {
return dot_pos;
}
-String path_extension(String const &str) {
+String path_extension(String const &str, bool include_dot = true) {
isize pos = string_extension_position(str);
if (pos < 0) {
return make_string(nullptr, 0);
}
- return substring(str, pos, str.len);
+ return substring(str, include_dot ? pos : pos + 1, str.len);
}
-
String string_trim_whitespace(String str) {
while (str.len > 0 && rune_is_whitespace(str[str.len-1])) {
str.len--;
@@ -299,38 +298,6 @@ String filename_from_path(String s) {
return make_string(nullptr, 0);
}
-String remove_extension_from_path(String const &s) {
- for (isize i = s.len-1; i >= 0; i--) {
- if (s[i] == '.') {
- return substring(s, 0, i);
- }
- }
- return s;
-}
-
-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);
-}
-
-String directory_from_path(String const &s) {
- isize i = s.len-1;
- for (; i >= 0; i--) {
- if (s[i] == '/' ||
- s[i] == '\\') {
- break;
- }
- }
- return substring(s, 0, i);
-}
-
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);
@@ -773,3 +740,34 @@ i32 unquote_string(gbAllocator a, String *s_, u8 quote=0, bool has_carriage_retu
return 2;
}
+
+
+bool string_is_valid_identifier(String str) {
+ if (str.len <= 0) return false;
+
+ isize rune_count = 0;
+
+ isize w = 0;
+ isize offset = 0;
+ while (offset < str.len) {
+ Rune r = 0;
+ w = utf8_decode(str.text, str.len, &r);
+ if (r == GB_RUNE_INVALID) {
+ return false;
+ }
+
+ if (rune_count == 0) {
+ if (!rune_is_letter(r)) {
+ return false;
+ }
+ } else {
+ if (!rune_is_letter(r) && !rune_is_digit(r)) {
+ return false;
+ }
+ }
+ rune_count += 1;
+ offset += w;
+ }
+
+ return true;
+}
diff --git a/src/threading.cpp b/src/threading.cpp
index e9412b411..63e3415b2 100644
--- a/src/threading.cpp
+++ b/src/threading.cpp
@@ -68,6 +68,40 @@ void yield_thread(void);
void yield_process(void);
+struct MutexGuard {
+ MutexGuard() = delete;
+ MutexGuard(MutexGuard const &) = delete;
+
+ MutexGuard(BlockingMutex *bm) : bm{bm} {
+ mutex_lock(this->bm);
+ }
+ MutexGuard(RecursiveMutex *rm) : rm{rm} {
+ mutex_lock(this->rm);
+ }
+ MutexGuard(BlockingMutex &bm) : bm{&bm} {
+ mutex_lock(this->bm);
+ }
+ MutexGuard(RecursiveMutex &rm) : rm{&rm} {
+ mutex_lock(this->rm);
+ }
+ ~MutexGuard() {
+ if (this->bm) {
+ mutex_unlock(this->bm);
+ } else if (this->rm) {
+ mutex_unlock(this->rm);
+ }
+ }
+
+ operator bool() const { return true; }
+
+ BlockingMutex *bm;
+ RecursiveMutex *rm;
+};
+
+#define MUTEX_GUARD_BLOCK(m) if (MutexGuard GB_DEFER_3(_mutex_guard_){m})
+#define MUTEX_GUARD(m) MutexGuard GB_DEFER_3(_mutex_guard_){m}
+
+
#if defined(GB_SYSTEM_WINDOWS)
struct BlockingMutex {
SRWLOCK srwlock;
@@ -296,6 +330,8 @@ u32 thread_current_id(void) {
__asm__("mov %%gs:0x08,%0" : "=r"(thread_id));
#elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
+#elif defined(GB_SYSTEM_LINUX)
+ thread_id = gettid();
#else
#error Unsupported architecture for thread_current_id()
#endif
@@ -315,6 +351,8 @@ gb_inline void yield_thread(void) {
#endif
#elif defined(GB_CPU_X86)
_mm_pause();
+#elif defined(GB_CPU_ARM)
+ __asm__ volatile ("yield" : : : "memory");
#else
#error Unknown architecture
#endif
@@ -448,7 +486,7 @@ void thread_set_name(Thread *t, char const *name) {
#elif defined(GB_SYSTEM_OSX)
// TODO(bill): Test if this works
pthread_setname_np(name);
-#elif defined(GB_SYSTEM_FREEBSD)
+#elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
pthread_set_name_np(t->posix_handle, name);
#else
// TODO(bill): Test if this works
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index 624aea2aa..40bc5c220 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -201,14 +201,6 @@ struct TokenPos {
i32 column; // starting at 1
};
-// temporary
-char *token_pos_to_string(TokenPos const &pos) {
- gbString s = gb_string_make_reserve(temporary_allocator(), 128);
- String file = get_file_path_string(pos.file_id);
- s = gb_string_append_fmt(s, "%.*s(%d:%d)", LIT(file), pos.line, pos.column);
- return s;
-}
-
i32 token_pos_cmp(TokenPos const &a, TokenPos const &b) {
if (a.offset != b.offset) {
return (a.offset < b.offset) ? -1 : +1;
@@ -264,419 +256,6 @@ bool token_is_newline(Token const &tok) {
return tok.kind == Token_Semicolon && tok.string == "\n";
}
-
-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;
-
- Array<u8> error_buffer;
- Array<String> errors;
-};
-
-gb_global ErrorCollector global_error_collector;
-
-#define MAX_ERROR_COLLECTOR_COUNT (36)
-
-
-bool any_errors(void) {
- return global_error_collector.count.load() != 0;
-}
-
-void init_global_error_collector(void) {
- mutex_init(&global_error_collector.mutex);
- mutex_init(&global_error_collector.block_mutex);
- mutex_init(&global_error_collector.error_out_mutex);
- mutex_init(&global_error_collector.string_mutex);
- array_init(&global_error_collector.errors, heap_allocator());
- array_init(&global_error_collector.error_buffer, heap_allocator());
- array_init(&global_file_path_strings, heap_allocator(), 1, 4096);
- array_init(&global_files, heap_allocator(), 1, 4096);
-}
-
-
-bool set_file_path_string(i32 index, String const &path) {
- bool ok = false;
- GB_ASSERT(index >= 0);
- mutex_lock(&global_error_collector.string_mutex);
-
- if (index >= global_file_path_strings.count) {
- array_resize(&global_file_path_strings, index+1);
- }
- String prev = global_file_path_strings[index];
- if (prev.len == 0) {
- global_file_path_strings[index] = path;
- ok = true;
- }
-
- mutex_unlock(&global_error_collector.string_mutex);
- return ok;
-}
-
-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);
-
- if (index >= global_files.count) {
- array_resize(&global_files, index+1);
- }
- AstFile *prev = global_files[index];
- if (prev == nullptr) {
- global_files[index] = file;
- ok = true;
- }
-
- mutex_unlock(&global_error_collector.string_mutex);
- return ok;
-}
-
-String get_file_path_string(i32 index) {
- GB_ASSERT(index >= 0);
- mutex_lock(&global_error_collector.string_mutex);
-
- String path = {};
- if (index < global_file_path_strings.count) {
- path = global_file_path_strings[index];
- }
-
- mutex_unlock(&global_error_collector.string_mutex);
- return path;
-}
-
-AstFile *thread_safe_get_ast_file_from_id(i32 index) {
- GB_ASSERT(index >= 0);
- mutex_lock(&global_error_collector.string_mutex);
-
- AstFile *file = nullptr;
- if (index < global_files.count) {
- file = global_files[index];
- }
-
- mutex_unlock(&global_error_collector.string_mutex);
- return file;
-}
-
-
-
-void begin_error_block(void) {
- mutex_lock(&global_error_collector.block_mutex);
- global_error_collector.in_block.store(true);
-}
-
-void end_error_block(void) {
- if (global_error_collector.error_buffer.count > 0) {
- isize n = global_error_collector.error_buffer.count;
- u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1);
- gb_memmove(text, global_error_collector.error_buffer.data, n);
- text[n] = 0;
- String s = {text, n};
- array_add(&global_error_collector.errors, s);
- global_error_collector.error_buffer.count = 0;
- }
-
- global_error_collector.in_block.store(false);
- mutex_unlock(&global_error_collector.block_mutex);
-}
-
-#define ERROR_BLOCK() begin_error_block(); defer (end_error_block())
-
-
-#define ERROR_OUT_PROC(name) void name(char const *fmt, va_list va)
-typedef ERROR_OUT_PROC(ErrorOutProc);
-
-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) {
- 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;
- } 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);
-
- }
- gb_file_write(f, buf, n);
-}
-
-
-ErrorOutProc *error_out_va = default_error_out_va;
-
-// NOTE: defined in build_settings.cpp
-bool global_warnings_as_errors(void);
-bool global_ignore_warnings(void);
-bool show_error_line(void);
-gbString get_file_line_as_string(TokenPos const &pos, i32 *offset);
-
-void error_out(char const *fmt, ...) {
- va_list va;
- va_start(va, fmt);
- error_out_va(fmt, va);
- va_end(va);
-}
-
-
-bool show_error_on_line(TokenPos const &pos, TokenPos end) {
- if (!show_error_line()) {
- return false;
- }
-
- i32 offset = 0;
- gbString the_line = get_file_line_as_string(pos, &offset);
- defer (gb_string_free(the_line));
-
- if (the_line != nullptr) {
- String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line));
-
- // TODO(bill): This assumes ASCII
-
- enum {
- MAX_LINE_LENGTH = 76,
- MAX_TAB_WIDTH = 8,
- ELLIPSIS_PADDING = 8
- };
-
- error_out("\n\t");
- if (line.len+MAX_TAB_WIDTH+ELLIPSIS_PADDING > MAX_LINE_LENGTH) {
- i32 const half_width = MAX_LINE_LENGTH/2;
- i32 left = cast(i32)(offset);
- i32 right = cast(i32)(line.len - offset);
- left = gb_min(left, half_width);
- right = gb_min(right, half_width);
-
- line.text += offset-left;
- line.len -= offset+right-left;
-
- line = string_trim_whitespace(line);
-
- offset = left + ELLIPSIS_PADDING/2;
-
- error_out("... %.*s ...", LIT(line));
- } else {
- error_out("%.*s", LIT(line));
- }
- error_out("\n\t");
-
- for (i32 i = 0; i < offset; i++) {
- error_out(" ");
- }
- error_out("^");
- if (end.file_id == pos.file_id) {
- if (end.line > pos.line) {
- for (i32 i = offset; i < line.len; i++) {
- error_out("~");
- }
- } else if (end.line == pos.line && end.column > pos.column) {
- i32 length = gb_min(end.offset - pos.offset, cast(i32)(line.len-offset));
- for (i32 i = 1; i < length-1; i++) {
- error_out("~");
- }
- if (length > 1) {
- error_out("^");
- }
- }
- }
-
- error_out("\n\n");
- return true;
- }
- return false;
-}
-
-void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
- global_error_collector.count.fetch_add(1);
-
- mutex_lock(&global_error_collector.mutex);
- // NOTE(bill): Duplicate error, skip it
- if (pos.line == 0) {
- error_out("Error: %s\n", gb_bprintf_va(fmt, va));
- } else if (global_error_collector.prev != pos) {
- global_error_collector.prev = pos;
- error_out("%s %s\n",
- token_pos_to_string(pos),
- gb_bprintf_va(fmt, va));
- show_error_on_line(pos, end);
- }
- mutex_unlock(&global_error_collector.mutex);
- if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
- gb_exit(1);
- }
-}
-
-void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
- if (global_warnings_as_errors()) {
- error_va(pos, end, fmt, va);
- return;
- }
- global_error_collector.warning_count.fetch_add(1);
- mutex_lock(&global_error_collector.mutex);
- if (!global_ignore_warnings()) {
- // NOTE(bill): Duplicate error, skip it
- if (pos.line == 0) {
- error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
- } else if (global_error_collector.prev != pos) {
- global_error_collector.prev = pos;
- error_out("%s Warning: %s\n",
- token_pos_to_string(pos),
- gb_bprintf_va(fmt, va));
- show_error_on_line(pos, end);
- }
- }
- mutex_unlock(&global_error_collector.mutex);
-}
-
-
-void error_line_va(char const *fmt, va_list va) {
- error_out_va(fmt, va);
-}
-
-void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
- mutex_lock(&global_error_collector.mutex);
- global_error_collector.count++;
- // NOTE(bill): Duplicate error, skip it
- if (pos.line == 0) {
- error_out("Error: %s", gb_bprintf_va(fmt, va));
- } else if (global_error_collector.prev != pos) {
- global_error_collector.prev = pos;
- error_out("%s %s",
- token_pos_to_string(pos),
- gb_bprintf_va(fmt, va));
- }
- mutex_unlock(&global_error_collector.mutex);
- if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
- gb_exit(1);
- }
-}
-
-
-void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
- mutex_lock(&global_error_collector.mutex);
- global_error_collector.count++;
- // NOTE(bill): Duplicate error, skip it
- if (global_error_collector.prev != pos) {
- global_error_collector.prev = pos;
- error_out("%s Syntax Error: %s\n",
- token_pos_to_string(pos),
- gb_bprintf_va(fmt, va));
- show_error_on_line(pos, end);
- } else if (pos.line == 0) {
- error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va));
- }
-
- mutex_unlock(&global_error_collector.mutex);
- if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
- gb_exit(1);
- }
-}
-
-void syntax_warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
- if (global_warnings_as_errors()) {
- syntax_error_va(pos, end, fmt, va);
- return;
- }
- mutex_lock(&global_error_collector.mutex);
- global_error_collector.warning_count++;
- if (!global_ignore_warnings()) {
- // NOTE(bill): Duplicate error, skip it
- if (global_error_collector.prev != pos) {
- global_error_collector.prev = pos;
- error_out("%s Syntax Warning: %s\n",
- token_pos_to_string(pos),
- gb_bprintf_va(fmt, va));
- show_error_on_line(pos, end);
- } else if (pos.line == 0) {
- error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
- }
- }
- mutex_unlock(&global_error_collector.mutex);
-}
-
-
-
-void warning(Token const &token, char const *fmt, ...) {
- va_list va;
- va_start(va, fmt);
- warning_va(token.pos, {}, fmt, va);
- va_end(va);
-}
-
-void error(Token const &token, char const *fmt, ...) {
- va_list va;
- va_start(va, fmt);
- error_va(token.pos, {}, fmt, va);
- va_end(va);
-}
-
-void error(TokenPos pos, char const *fmt, ...) {
- va_list va;
- va_start(va, fmt);
- Token token = {};
- token.pos = pos;
- error_va(pos, {}, fmt, va);
- va_end(va);
-}
-
-void error_line(char const *fmt, ...) {
- va_list va;
- va_start(va, fmt);
- error_line_va(fmt, va);
- va_end(va);
-}
-
-
-void syntax_error(Token const &token, char const *fmt, ...) {
- va_list va;
- va_start(va, fmt);
- syntax_error_va(token.pos, {}, fmt, va);
- va_end(va);
-}
-
-void syntax_error(TokenPos pos, char const *fmt, ...) {
- va_list va;
- va_start(va, fmt);
- syntax_error_va(pos, {}, fmt, va);
- va_end(va);
-}
-
-void syntax_warning(Token const &token, char const *fmt, ...) {
- va_list va;
- va_start(va, fmt);
- syntax_warning_va(token.pos, {}, fmt, va);
- va_end(va);
-}
-
-
-void compiler_error(char const *fmt, ...) {
- va_list va;
-
- va_start(va, fmt);
- gb_printf_err("Internal Compiler Error: %s\n",
- gb_bprintf_va(fmt, va));
- va_end(va);
- gb_exit(1);
-}
-
-
-
-
-
gb_inline bool token_is_literal(TokenKind t) {
return gb_is_between(t, Token__LiteralBegin+1, Token__LiteralEnd-1);
}
@@ -695,6 +274,8 @@ gb_inline bool token_is_shift(TokenKind t) {
gb_inline void print_token(Token t) { gb_printf("%.*s\n", LIT(t.string)); }
+#include "error.cpp"
+
enum TokenizerInitError {
TokenizerInit_None,
diff --git a/src/types.cpp b/src/types.cpp
index 2b7ea93dc..b4dc17256 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -165,9 +165,8 @@ struct TypeUnion {
i16 tag_size;
bool is_polymorphic;
- bool is_poly_specialized : 1;
- bool no_nil : 1;
- bool maybe : 1;
+ bool is_poly_specialized;
+ UnionTypeKind kind;
};
struct TypeProc {
@@ -186,7 +185,6 @@ struct TypeProc {
bool c_vararg;
bool is_polymorphic;
bool is_poly_specialized;
- bool has_proc_default_values;
bool has_named_results;
bool diverging; // no return
bool return_by_pointer;
@@ -221,6 +219,7 @@ struct TypeProc {
ExactValue *max_value; \
i64 count; \
TokenKind op; \
+ bool is_sparse; \
}) \
TYPE_KIND(Slice, struct { Type *elem; }) \
TYPE_KIND(DynamicArray, struct { Type *elem; }) \
@@ -362,6 +361,7 @@ enum TypeInfoFlag : u32 {
enum : int {
MATRIX_ELEMENT_COUNT_MIN = 1,
MATRIX_ELEMENT_COUNT_MAX = 16,
+ MATRIX_ELEMENT_MAX_SIZE = MATRIX_ELEMENT_COUNT_MAX * (2 * 8), // complex128
};
@@ -391,6 +391,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 pseudo_field;
};
Selection empty_selection = {0};
@@ -683,6 +684,38 @@ gb_global Type *t_map_header = nullptr;
gb_global Type *t_equal_proc = nullptr;
gb_global Type *t_hasher_proc = nullptr;
+gb_global Type *t_objc_object = nullptr;
+gb_global Type *t_objc_selector = nullptr;
+gb_global Type *t_objc_class = nullptr;
+
+gb_global Type *t_objc_id = nullptr;
+gb_global Type *t_objc_SEL = nullptr;
+gb_global Type *t_objc_Class = nullptr;
+
+enum OdinAtomicMemoryOrder : i32 {
+ OdinAtomicMemoryOrder_relaxed = 0, // unordered
+ OdinAtomicMemoryOrder_consume = 1, // monotonic
+ OdinAtomicMemoryOrder_acquire = 2,
+ OdinAtomicMemoryOrder_release = 3,
+ OdinAtomicMemoryOrder_acq_rel = 4,
+ OdinAtomicMemoryOrder_seq_cst = 5,
+ OdinAtomicMemoryOrder_COUNT,
+};
+
+char const *OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_COUNT] = {
+ "Relaxed",
+ "Consume",
+ "Acquire",
+ "Release",
+ "Acq_Rel",
+ "Seq_Cst",
+};
+
+gb_global Type *t_atomic_memory_order = nullptr;
+
+
+
+
gb_global RecursiveMutex g_type_mutex;
struct TypePath;
@@ -690,7 +723,7 @@ struct TypePath;
i64 type_size_of (Type *t);
i64 type_align_of (Type *t);
i64 type_offset_of (Type *t, i32 index);
-gbString type_to_string (Type *type);
+gbString type_to_string (Type *type, bool shorthand=false);
i64 type_size_of_internal(Type *t, TypePath *path);
void init_map_internal_types(Type *type);
Type * bit_set_to_int(Type *t);
@@ -1253,6 +1286,13 @@ bool is_type_quaternion(Type *t) {
}
return false;
}
+bool is_type_complex_or_quaternion(Type *t) {
+ t = core_type(t);
+ if (t->kind == Type_Basic) {
+ return (t->Basic.flags & (BasicFlag_Complex|BasicFlag_Quaternion)) != 0;
+ }
+ return false;
+}
bool is_type_f16(Type *t) {
t = core_type(t);
if (t->kind == Type_Basic) {
@@ -1575,6 +1615,24 @@ Type *core_array_type(Type *t) {
}
}
+i32 type_math_rank(Type *t) {
+ i32 rank = 0;
+ for (;;) {
+ t = base_type(t);
+ switch (t->kind) {
+ case Type_Array:
+ rank += 1;
+ t = t->Array.elem;
+ break;
+ case Type_Matrix:
+ rank += 2;
+ t = t->Matrix.elem;
+ break;
+ default:
+ return rank;
+ }
+ }
+}
Type *base_complex_elem_type(Type *t) {
@@ -1627,7 +1685,7 @@ bool is_type_map(Type *t) {
bool is_type_union_maybe_pointer(Type *t) {
t = base_type(t);
- if (t->kind == Type_Union && t->Union.maybe) {
+ if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) {
if (t->Union.variants.count == 1) {
Type *v = t->Union.variants[0];
return is_type_pointer(v) || is_type_multi_pointer(v);
@@ -1639,7 +1697,7 @@ bool is_type_union_maybe_pointer(Type *t) {
bool is_type_union_maybe_pointer_original_alignment(Type *t) {
t = base_type(t);
- if (t->kind == Type_Union && t->Union.maybe) {
+ if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) {
if (t->Union.variants.count == 1) {
Type *v = t->Union.variants[0];
if (is_type_pointer(v) || is_type_multi_pointer(v)) {
@@ -2131,7 +2189,7 @@ bool type_has_nil(Type *t) {
case Type_Map:
return true;
case Type_Union:
- return !t->Union.no_nil;
+ return t->Union.kind != UnionType_no_nil;
case Type_Struct:
if (is_type_soa_struct(t)) {
switch (t->Struct.soa_kind) {
@@ -2160,6 +2218,17 @@ bool elem_type_can_be_constant(Type *t) {
return true;
}
+bool is_type_lock_free(Type *t) {
+ t = core_type(t);
+ if (t == t_invalid) {
+ return false;
+ }
+ i64 sz = type_size_of(t);
+ // TODO(bill): Figure this out correctly
+ return sz <= build_context.max_align;
+}
+
+
bool is_type_comparable(Type *t) {
t = base_type(t);
@@ -2296,7 +2365,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) {
GB_ASSERT(is_type_struct(src) || is_type_union(src));
for_array(i, src->Struct.fields) {
Entity *f = src->Struct.fields[i];
- if (f->kind == Entity_Variable && f->flags & EntityFlag_Using) {
+ if (f->kind == Entity_Variable && f->flags & EntityFlags_IsSubtype) {
if (are_types_identical(dst, f->type)) {
return f->token.string;
}
@@ -2305,7 +2374,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) {
return f->token.string;
}
}
- if (is_type_struct(f->type)) {
+ if ((f->flags & EntityFlag_Using) != 0 && is_type_struct(f->type)) {
String name = lookup_subtype_polymorphic_field(dst, f->type);
if (name.len > 0) {
return name;
@@ -2331,7 +2400,17 @@ Type *strip_type_aliasing(Type *x) {
return x;
}
+bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names);
+
bool are_types_identical(Type *x, Type *y) {
+ return are_types_identical_internal(x, y, false);
+}
+bool are_types_identical_unique_tuples(Type *x, Type *y) {
+ return are_types_identical_internal(x, y, true);
+}
+
+
+bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) {
if (x == y) {
return true;
}
@@ -2407,7 +2486,7 @@ bool are_types_identical(Type *x, Type *y) {
if (y->kind == Type_Union) {
if (x->Union.variants.count == y->Union.variants.count &&
x->Union.custom_align == y->Union.custom_align &&
- x->Union.no_nil == y->Union.no_nil) {
+ x->Union.kind == y->Union.kind) {
// NOTE(bill): zeroth variant is nullptr
for_array(i, x->Union.variants) {
if (!are_types_identical(x->Union.variants[i], y->Union.variants[i])) {
@@ -2441,9 +2520,9 @@ bool are_types_identical(Type *x, Type *y) {
if (xf->token.string != yf->token.string) {
return false;
}
- bool xf_is_using = (xf->flags&EntityFlag_Using) != 0;
- bool yf_is_using = (yf->flags&EntityFlag_Using) != 0;
- if (xf_is_using ^ yf_is_using) {
+ u64 xf_flags = (xf->flags&EntityFlags_IsSubtype);
+ u64 yf_flags = (yf->flags&EntityFlags_IsSubtype);
+ if (xf_flags != yf_flags) {
return false;
}
}
@@ -2480,6 +2559,11 @@ bool are_types_identical(Type *x, Type *y) {
if (xe->kind != ye->kind || !are_types_identical(xe->type, ye->type)) {
return false;
}
+ if (check_tuple_names) {
+ if (xe->token.string != ye->token.string) {
+ return false;
+ }
+ }
if (xe->kind == Entity_Constant && !compare_exact_values(Token_CmpEq, xe->Constant.value, ye->Constant.value)) {
// NOTE(bill): This is needed for polymorphic procedures
return false;
@@ -2546,7 +2630,7 @@ i64 union_variant_index(Type *u, Type *v) {
for_array(i, u->Union.variants) {
Type *vt = u->Union.variants[i];
if (are_types_identical(v, vt)) {
- if (u->Union.no_nil) {
+ if (u->Union.kind == UnionType_no_nil) {
return cast(i64)(i+0);
} else {
return cast(i64)(i+1);
@@ -2570,6 +2654,17 @@ i64 union_tag_size(Type *u) {
// TODO(bill): Is this an okay approach?
i64 max_align = 1;
+
+ if (u->Union.variants.count < 1ull<<8) {
+ max_align = 1;
+ } else if (u->Union.variants.count < 1ull<<16) {
+ max_align = 2;
+ } else if (u->Union.variants.count < 1ull<<32) {
+ max_align = 4;
+ } else {
+ GB_PANIC("how many variants do you have?!");
+ }
+
for_array(i, u->Union.variants) {
Type *variant_type = u->Union.variants[i];
i64 align = type_align_of(variant_type);
@@ -2730,6 +2825,7 @@ Selection lookup_field_from_index(Type *type, i64 index) {
}
Entity *scope_lookup_current(Scope *s, String const &name);
+bool has_type_got_objc_class_attribute(Type *t);
Selection lookup_field_with_selection(Type *type_, String field_name, bool is_type, Selection sel, bool allow_blank_ident) {
GB_ASSERT(type_ != nullptr);
@@ -2742,9 +2838,40 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
bool is_ptr = type != type_;
sel.indirect = sel.indirect || is_ptr;
+ Type *original_type = type;
+
type = base_type(type);
if (is_type) {
+ if (has_type_got_objc_class_attribute(original_type) && original_type->kind == Type_Named) {
+ Entity *e = original_type->Named.type_name;
+ GB_ASSERT(e->kind == Entity_TypeName);
+ if (e->TypeName.objc_metadata) {
+ auto *md = e->TypeName.objc_metadata;
+ mutex_lock(md->mutex);
+ defer (mutex_unlock(md->mutex));
+ for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
+ GB_ASSERT(entry.entity->kind == Entity_Procedure);
+ if (entry.name == field_name) {
+ sel.entity = entry.entity;
+ sel.pseudo_field = true;
+ return sel;
+ }
+ }
+ }
+ if (type->kind == Type_Struct) {
+ for_array(i, type->Struct.fields) {
+ Entity *f = type->Struct.fields[i];
+ if (f->flags&EntityFlag_Using) {
+ sel = lookup_field_with_selection(f->type, field_name, is_type, sel, allow_blank_ident);
+ if (sel.entity) {
+ return sel;
+ }
+ }
+ }
+ }
+ }
+
if (is_type_enum(type)) {
// NOTE(bill): These may not have been added yet, so check in case
for_array(i, type->Enum.fields) {
@@ -2791,6 +2918,24 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
} else if (type->kind == Type_Union) {
} else if (type->kind == Type_Struct) {
+ if (has_type_got_objc_class_attribute(original_type) && original_type->kind == Type_Named) {
+ Entity *e = original_type->Named.type_name;
+ GB_ASSERT(e->kind == Entity_TypeName);
+ if (e->TypeName.objc_metadata) {
+ auto *md = e->TypeName.objc_metadata;
+ mutex_lock(md->mutex);
+ defer (mutex_unlock(md->mutex));
+ for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
+ GB_ASSERT(entry.entity->kind == Entity_Procedure);
+ if (entry.name == field_name) {
+ sel.entity = entry.entity;
+ sel.pseudo_field = true;
+ return sel;
+ }
+ }
+ }
+ }
+
for_array(i, type->Struct.fields) {
Entity *f = type->Struct.fields[i];
if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) {
@@ -3696,6 +3841,61 @@ i64 type_offset_of_from_selection(Type *type, Selection sel) {
return offset;
}
+isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) {
+ Type *prev_src = src;
+ src = type_deref(src);
+ if (!src_is_ptr) {
+ src_is_ptr = src != prev_src;
+ }
+ src = base_type(src);
+
+ if (!is_type_struct(src)) {
+ return 0;
+ }
+
+ for_array(i, src->Struct.fields) {
+ Entity *f = src->Struct.fields[i];
+ if (f->kind != Entity_Variable || (f->flags&EntityFlags_IsSubtype) == 0) {
+ continue;
+ }
+
+ if (are_types_identical(f->type, dst)) {
+ return level+1;
+ }
+ if (src_is_ptr && is_type_pointer(dst)) {
+ if (are_types_identical(f->type, type_deref(dst))) {
+ return level+1;
+ }
+ }
+ isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr);
+ if (nested_level > 0) {
+ return nested_level;
+ }
+ }
+
+ return 0;
+}
+
+bool is_type_subtype_of(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));
+}
+
+
+bool has_type_got_objc_class_attribute(Type *t) {
+ return t->kind == Type_Named && t->Named.type_name != nullptr && t->Named.type_name->TypeName.objc_class_name != "";
+}
+
+
+
+bool is_type_objc_object(Type *t) {
+ bool internal_check_is_assignable_to(Type *src, Type *dst);
+
+ return internal_check_is_assignable_to(t, t_objc_object);
+}
Type *get_struct_field_type(Type *t, isize index) {
t = base_type(type_deref(t));
@@ -3767,7 +3967,7 @@ Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type
-gbString write_type_to_string(gbString str, Type *type) {
+gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
if (type == nullptr) {
return gb_string_appendc(str, "<no type>");
}
@@ -3808,6 +4008,9 @@ gbString write_type_to_string(gbString str, Type *type) {
break;
case Type_EnumeratedArray:
+ if (type->EnumeratedArray.is_sparse) {
+ str = gb_string_appendc(str, "#sparse");
+ }
str = gb_string_append_rune(str, '[');
str = write_type_to_string(str, type->EnumeratedArray.index);
str = gb_string_append_rune(str, ']');
@@ -3850,8 +4053,11 @@ gbString write_type_to_string(gbString str, Type *type) {
case Type_Union:
str = gb_string_appendc(str, "union");
- if (type->Union.no_nil != 0) str = gb_string_appendc(str, " #no_nil");
- if (type->Union.maybe != 0) str = gb_string_appendc(str, " #maybe");
+ switch (type->Union.kind) {
+ case UnionType_maybe: str = gb_string_appendc(str, " #maybe"); break;
+ case UnionType_no_nil: str = gb_string_appendc(str, " #no_nil"); break;
+ case UnionType_shared_nil: str = gb_string_appendc(str, " #shared_nil"); break;
+ }
if (type->Union.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Union.custom_align);
str = gb_string_appendc(str, " {");
for_array(i, type->Union.variants) {
@@ -3879,15 +4085,21 @@ gbString write_type_to_string(gbString str, Type *type) {
if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union");
if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align);
str = gb_string_appendc(str, " {");
- for_array(i, type->Struct.fields) {
- Entity *f = type->Struct.fields[i];
- GB_ASSERT(f->kind == Entity_Variable);
- if (i > 0) {
- str = gb_string_appendc(str, ", ");
+
+
+ if (shorthand && type->Struct.fields.count > 16) {
+ str = gb_string_append_fmt(str, "%lld fields...", cast(long long)type->Struct.fields.count);
+ } else {
+ for_array(i, type->Struct.fields) {
+ Entity *f = type->Struct.fields[i];
+ GB_ASSERT(f->kind == Entity_Variable);
+ 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_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_rune(str, '}');
} break;
@@ -3926,7 +4138,7 @@ gbString write_type_to_string(gbString str, Type *type) {
str = gb_string_appendc(str, " = ");
str = write_exact_value_to_string(str, var->Constant.value);
} else {
- str = gb_string_appendc(str, "=");
+ str = gb_string_appendc(str, " := ");
str = write_exact_value_to_string(str, var->Constant.value);
}
continue;
@@ -3954,14 +4166,10 @@ gbString write_type_to_string(gbString str, Type *type) {
str = gb_string_appendc(str, "typeid/");
str = write_type_to_string(str, var->type);
} else {
- if (var->kind == Entity_TypeName) {
- str = gb_string_appendc(str, "$");
- str = gb_string_append_length(str, name.text, name.len);
- str = gb_string_appendc(str, "=");
- str = write_type_to_string(str, var->type);
- } else {
- str = gb_string_appendc(str, "typeid");
- }
+ str = gb_string_appendc(str, "$");
+ str = gb_string_append_length(str, name.text, name.len);
+ str = gb_string_appendc(str, "=");
+ str = write_type_to_string(str, var->type);
}
}
}
@@ -4066,13 +4274,16 @@ gbString write_type_to_string(gbString str, Type *type) {
}
-gbString type_to_string(Type *type, gbAllocator allocator) {
- return write_type_to_string(gb_string_make(allocator, ""), type);
+gbString type_to_string(Type *type, gbAllocator allocator, bool shorthand=false) {
+ return write_type_to_string(gb_string_make(allocator, ""), type, shorthand);
}
-gbString type_to_string(Type *type) {
- return write_type_to_string(gb_string_make(heap_allocator(), ""), type);
+gbString type_to_string(Type *type, bool shorthand) {
+ return write_type_to_string(gb_string_make(heap_allocator(), ""), type, shorthand);
}
+gbString type_to_string_shorthand(Type *type) {
+ return type_to_string(type, true);
+}