浏览代码

Merge branch 'master' into new-io

gingerBill 2 年之前
父节点
当前提交
08e466938f

+ 11 - 9
build.bat

@@ -3,18 +3,20 @@
 setlocal EnableDelayedExpansion
 
 where /Q cl.exe || (
-  set __VSCMD_ARG_NO_LOGO=1
-  for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i
-  if "!VS!" equ "" (
-    echo ERROR: Visual Studio installation not found
-    exit /b 1
-  )  
-  call "!VS!\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /b 1
+	set __VSCMD_ARG_NO_LOGO=1
+	for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i
+	if "!VS!" equ "" (
+		echo ERROR: Visual Studio installation not found
+		exit /b 1
+	)
+	call "!VS!\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /b 1
 )
 
 if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
-  echo ERROR: please run this from MSVC x64 native tools command prompt, 32-bit target is not supported!
-  exit /b 1
+	if "%ODIN_IGNORE_MSVC_CHECK%" == "" (
+		echo ERROR: please run this from MSVC x64 native tools command prompt, 32-bit target is not supported!
+		exit /b 1
+	)
 )
 
 for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (

+ 1 - 4
core/net/dns_unix.odin

@@ -44,9 +44,6 @@ _get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator :
 	if !hosts_ok {
 		return nil, .Invalid_Hosts_Config_Error
 	}
-	if len(hosts) == 0 {
-		return
-	}
 
 	host_overrides := make([dynamic]DNS_Record)
 	for host in hosts {
@@ -80,4 +77,4 @@ _get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator :
 	}
 
 	return get_dns_records_from_nameservers(hostname, type, name_servers, host_overrides[:])
-}
+}

+ 1 - 1
core/os/os2/file_windows.odin

@@ -294,7 +294,7 @@ _read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
 		}
 	}
 
-	return int(total_read), nil
+	return int(total_read), err
 }
 
 _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {

+ 7 - 7
core/os/os_darwin.odin

@@ -440,7 +440,7 @@ foreign libc {
 	@(link_name="closedir")         _unix_closedir      :: proc(dirp: Dir) -> c.int ---
 	@(link_name="rewinddir")        _unix_rewinddir     :: proc(dirp: Dir) ---
 
-	@(link_name="__fcntl")          _unix__fcntl        :: proc(fd: Handle, cmd: c.int, #c_vararg args: ..any) -> c.int ---
+	@(link_name="__fcntl")          _unix__fcntl        :: proc(fd: Handle, cmd: c.int, arg: uintptr) -> c.int ---
 
 	@(link_name="rename") _unix_rename :: proc(old: cstring, new: cstring) -> c.int ---
 	@(link_name="remove") _unix_remove :: proc(path: cstring) -> c.int ---
@@ -794,14 +794,14 @@ _readlink :: proc(path: string) -> (string, Errno) {
 }
 
 absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
-	buf : [256]byte
-	res  := _unix__fcntl(fd, F_GETPATH, &buf[0])
-	if	res != 0 {
-		return "", Errno(get_last_error())
+	buf: [DARWIN_MAXPATHLEN]byte
+	_, err := fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0])))
+	if err != ERROR_NONE {
+		return "", err
 	}
 
 	path := strings.clone_from_cstring(cstring(&buf[0]))
-	return path, ERROR_NONE
+	return path, err
 }
 
 absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
@@ -1068,7 +1068,7 @@ shutdown :: proc(sd: Socket, how: int) -> (Errno) {
 }
 
 fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) {
-	result := _unix__fcntl(Handle(fd), c.int(cmd), c.int(arg))
+	result := _unix__fcntl(Handle(fd), c.int(cmd), uintptr(arg))
 	if result < 0 {
 		return 0, Errno(get_last_error())
 	}

+ 1 - 1
core/reflect/reflect.odin

@@ -132,7 +132,7 @@ type_info_core :: runtime.type_info_core
 type_info_base_without_enum :: type_info_core
 
 
-when !ODIN_DISALLOW_RTTI {
+when !ODIN_NO_RTTI {
 	typeid_base :: runtime.typeid_base
 	typeid_core :: runtime.typeid_core
 	typeid_base_without_enum :: typeid_core

+ 1 - 1
core/runtime/core.odin

@@ -566,7 +566,7 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check
 	return &type_table[n]
 }
 
-when !ODIN_DISALLOW_RTTI {
+when !ODIN_NO_RTTI {
 	typeid_base :: proc "contextless" (id: typeid) -> typeid {
 		ti := type_info_of(id)
 		ti = type_info_base(ti)

+ 1 - 1
core/runtime/error_checks.odin

@@ -122,7 +122,7 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
 }
 
 
-when ODIN_DISALLOW_RTTI {
+when ODIN_NO_RTTI {
 	type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32) {
 		if ok {
 			return

+ 2 - 2
core/runtime/print.odin

@@ -5,7 +5,7 @@ _INTEGER_DIGITS :: "0123456789abcdefghijklmnopqrstuvwxyz"
 @(private="file")
 _INTEGER_DIGITS_VAR := _INTEGER_DIGITS
 
-when !ODIN_DISALLOW_RTTI {
+when !ODIN_NO_RTTI {
 	print_any_single :: proc "contextless" (arg: any) {
 		x := arg
 		if loc, ok := x.(Source_Code_Location); ok {
@@ -234,7 +234,7 @@ print_caller_location :: proc "contextless" (using loc: Source_Code_Location) {
 	}
 }
 print_typeid :: proc "contextless" (id: typeid) {
-	when ODIN_DISALLOW_RTTI {
+	when ODIN_NO_RTTI {
 		if id == nil {
 			print_string("nil")
 		} else {

+ 1 - 1
examples/demo/demo.odin

@@ -352,7 +352,7 @@ control_flow :: proc() {
 
 		if false {
 			f, err := os.open("my_file.txt")
-			if err != 0 {
+			if err != os.ERROR_NONE {
 				// handle error
 			}
 			defer os.close(f)

+ 2 - 0
misc/shell.bat

@@ -7,5 +7,7 @@ rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxil
 rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 1> NUL
 set _NO_DEBUG_HEAP=1
 
+set ODIN_IGNORE_MSVC_CHECK=1
+
 rem set path=w:\Odin\misc;%path%
 cls

+ 3 - 3
src/build_settings.cpp

@@ -309,7 +309,7 @@ struct BuildContext {
 	
 	bool   copy_file_contents;
 
-	bool   disallow_rtti;
+	bool   no_rtti;
 
 	bool   dynamic_map_calls;
 
@@ -1227,8 +1227,8 @@ gb_internal void init_build_context(TargetMetrics *cross_target) {
 	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");
+		if (bc->no_rtti) {
+			gb_printf_err("-no-rtti is only allowed on freestanding targets\n");
 			gb_exit(1);
 		}
 	}

+ 2 - 2
src/check_builtin.cpp

@@ -2063,7 +2063,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 		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) {
+		if (build_context.no_rtti) {
 			error(call, "'%.*s' has been disallowed", LIT(builtin_name));
 			return false;
 		}
@@ -2106,7 +2106,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 		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) {
+		if (build_context.no_rtti) {
 			error(call, "'%.*s' has been disallowed", LIT(builtin_name));
 			return false;
 		}

+ 6 - 0
src/check_type.cpp

@@ -729,6 +729,12 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no
 	union_type->Union.kind = ut->kind;
 	switch (ut->kind) {
 	case UnionType_no_nil:
+		if (union_type->Union.is_polymorphic && poly_operands == nullptr) {
+			GB_ASSERT(variants.count == 0);
+			if (ut->variants.count != 1) {
+				break;
+			}
+		}
 		if (variants.count < 2) {
 			error(ut->align, "A union with #no_nil must have at least 2 variants");
 		}

+ 4 - 4
src/checker.cpp

@@ -32,7 +32,7 @@ gb_internal bool is_operand_uninit(Operand o) {
 }
 
 gb_internal bool check_rtti_type_disallowed(Token const &token, Type *type, char const *format) {
-	if (build_context.disallow_rtti && type) {
+	if (build_context.no_rtti && type) {
 		if (is_type_any(type)) {
 			gbString t = type_to_string(type);
 			error(token, format, t);
@@ -1054,7 +1054,7 @@ gb_internal void init_universal(void) {
 	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);
+	add_global_bool_constant("ODIN_NO_RTTI",            bc->no_rtti);
 
 	add_global_bool_constant("ODIN_VALGRIND_SUPPORT",         bc->ODIN_VALGRIND_SUPPORT);
 
@@ -1742,7 +1742,7 @@ gb_internal void add_implicit_entity(CheckerContext *c, Ast *clause, Entity *e)
 
 gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t);
 gb_internal void add_type_info_type(CheckerContext *c, Type *t) {
-	if (build_context.disallow_rtti) {
+	if (build_context.no_rtti) {
 		return;
 	}
 	if (t == nullptr) {
@@ -2343,7 +2343,7 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
 		str_lit("__multi3"),
 	);
 
-	FORCE_ADD_RUNTIME_ENTITIES(!build_context.disallow_rtti,
+	FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_rtti,
 		// Odin types
 		str_lit("Type_Info"),
 

+ 3 - 0
src/checker.hpp

@@ -199,6 +199,9 @@ struct DeclInfo {
 	BlockingMutex type_and_value_mutex;
 
 	Array<BlockLabel> labels;
+
+	// NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time
+	struct lbModule *code_gen_module;
 };
 
 // ProcInfo stores the information needed for checking a procedure

+ 7 - 58
src/llvm_backend.cpp

@@ -157,10 +157,10 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
 		return {compare_proc->value, compare_proc->type};
 	}
 
-	static u32 proc_index = 0;
+	static std::atomic<u32> proc_index;
 
 	char buf[32] = {};
-	isize n = gb_snprintf(buf, 32, "__$equal%u", ++proc_index);
+	isize n = gb_snprintf(buf, 32, "__$equal%u", 1+proc_index.fetch_add(1));
 	char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
 	String proc_name = make_string_c(str);
 
@@ -656,10 +656,10 @@ gb_internal lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) {
 		GB_ASSERT(*found != nullptr);
 		return {(*found)->value, (*found)->type};
 	}
-	static u32 proc_index = 0;
+	static std::atomic<u32> proc_index;
 
 	char buf[32] = {};
-	isize n = gb_snprintf(buf, 32, "__$map_set-%u", ++proc_index);
+	isize n = gb_snprintf(buf, 32, "__$map_set-%u", 1+proc_index.fetch_add(1));
 	char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
 	String proc_name = make_string_c(str);
 
@@ -772,56 +772,6 @@ gb_internal lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) {
 	return {p->value, p->type};
 }
 
-
-gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) {
-	MUTEX_GUARD(&m->gen->anonymous_proc_lits_mutex);
-
-	lbProcedure **found = map_get(&m->gen->anonymous_proc_lits, expr);
-	if (found) {
-		return lb_find_procedure_value_from_entity(m, (*found)->entity);
-	}
-
-	ast_node(pl, ProcLit, expr);
-
-	// NOTE(bill): Generate a new name
-	// parent$count
-	isize name_len = prefix_name.len + 1 + 8 + 1;
-	char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
-	i32 name_id = cast(i32)m->gen->anonymous_proc_lits.count;
-
-	name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), name_id);
-	String name = make_string((u8 *)name_text, name_len-1);
-
-	Type *type = type_of_expr(expr);
-
-	Token token = {};
-	token.pos = ast_token(expr).pos;
-	token.kind = Token_Ident;
-	token.string = name;
-	Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags);
-	e->file = expr->file();
-	e->decl_info = pl->decl;
-	e->code_gen_module = m;
-	e->flags |= EntityFlag_ProcBodyChecked;
-	lbProcedure *p = lb_create_procedure(m, e);
-
-	lbValue value = {};
-	value.value = p->value;
-	value.type = p->type;
-
-	array_add(&m->procedures_to_generate, p);
-	if (parent != nullptr) {
-		array_add(&parent->children, p);
-	} else {
-		string_map_set(&m->members, name, value);
-	}
-
-	map_set(&m->gen->anonymous_proc_lits, expr, p);
-
-	return value;
-}
-
-
 gb_internal lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type) {
 	lbAddr *found = map_get(&m->map_cell_info_map, type);
 	if (found) {
@@ -1048,7 +998,7 @@ struct lbGlobalVariable {
 };
 
 gb_internal lbProcedure *lb_create_startup_type_info(lbModule *m) {
-	if (build_context.disallow_rtti) {
+	if (build_context.no_rtti) {
 		return nullptr;
 	}
 	Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
@@ -1513,7 +1463,7 @@ gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc
 	lbModule *m = cast(lbModule *)data;
 	for (isize i = 0; i < m->missing_procedures_to_check.count; i++) {
 		lbProcedure *p = m->missing_procedures_to_check[i];
-		debugf("Generate missing procedure: %.*s\n", LIT(p->name));
+		debugf("Generate missing procedure: %.*s module %p\n", LIT(p->name), m);
 		lb_generate_procedure(m, p);
 	}
 	return 0;
@@ -1577,7 +1527,6 @@ gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) {
 	thread_pool_wait();
 }
 
-
 gb_internal String lb_filepath_ll_for_module(lbModule *m) {
 	String path = concatenate3_strings(permanent_allocator(),
 		build_context.build_paths[BuildPath_Output].basename,
@@ -2170,7 +2119,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 
 	TIME_SECTION("LLVM Global Variables");
 
-	if (!build_context.disallow_rtti) {
+	if (!build_context.no_rtti) {
 		lbModule *m = default_module;
 
 		{ // Add type info data

+ 4 - 2
src/llvm_backend.hpp

@@ -164,7 +164,7 @@ struct lbModule {
 	PtrMap<Type *, lbProcedure *> map_get_procs;
 	PtrMap<Type *, lbProcedure *> map_set_procs;
 
-	u32 nested_type_name_guid;
+	std::atomic<u32> nested_type_name_guid;
 
 	Array<lbProcedure *> procedures_to_generate;
 	Array<Entity *> global_procedures_and_types_to_create;
@@ -201,7 +201,7 @@ struct lbGenerator {
 	PtrMap<LLVMContextRef, lbModule *> modules_through_ctx; 
 	lbModule default_module;
 
-	BlockingMutex anonymous_proc_lits_mutex;
+	RecursiveMutex anonymous_proc_lits_mutex;
 	PtrMap<Ast *, lbProcedure *> anonymous_proc_lits; 
 
 	BlockingMutex foreign_mutex;
@@ -545,6 +545,8 @@ gb_internal gb_inline i64 lb_max_zero_init_size(void) {
 gb_internal LLVMTypeRef OdinLLVMGetArrayElementType(LLVMTypeRef type);
 gb_internal LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type);
 
+gb_internal String lb_filepath_ll_for_module(lbModule *m);
+
 #define LB_STARTUP_RUNTIME_PROC_NAME   "__$startup_runtime"
 #define LB_CLEANUP_RUNTIME_PROC_NAME   "__$cleanup_runtime"
 #define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"

+ 1 - 0
src/llvm_backend_const.cpp

@@ -473,6 +473,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
 	if (value.kind == ExactValue_Procedure) {
 		lbValue res = {};
 		Ast *expr = unparen_expr(value.value_procedure);
+		GB_ASSERT(expr != nullptr);
 		if (expr->kind == Ast_ProcLit) {
 			res = lb_generate_anonymous_proc_lit(m, str_lit("_proclit"), expr);
 		} else {

+ 3 - 3
src/llvm_backend_expr.cpp

@@ -2984,7 +2984,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
 
 
 					isize arg_count = 6;
-					if (build_context.disallow_rtti) {
+					if (build_context.no_rtti) {
 						arg_count = 4;
 					}
 
@@ -2996,7 +2996,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
 					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) {
+					if (!build_context.no_rtti) {
 						args[4] = lb_typeid(p->module, src_type);
 						args[5] = lb_typeid(p->module, dst_type);
 					}
@@ -3012,7 +3012,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
 				}
 				lbValue data_ptr = lb_emit_struct_ev(p, v, 0);
 				if ((p->state_flags & StateFlag_no_type_assert) == 0) {
-					GB_ASSERT(!build_context.disallow_rtti);
+					GB_ASSERT(!build_context.no_rtti);
 
 					lbValue any_id = lb_emit_struct_ev(p, v, 1);
 

+ 94 - 6
src/llvm_backend_general.cpp

@@ -334,10 +334,35 @@ gb_internal bool lb_is_instr_terminating(LLVMValueRef instr) {
 	return false;
 }
 
+gb_internal lbModule *lb_module_of_expr(lbGenerator *gen, Ast *expr) {
+	GB_ASSERT(expr != nullptr);
+	lbModule **found = nullptr;
+	AstFile *file = expr->file();
+	if (file) {
+		found = map_get(&gen->modules, cast(void *)file);
+		if (found) {
+			return *found;
+		}
+
+		if (file->pkg) {
+			found = map_get(&gen->modules, cast(void *)file->pkg);
+			if (found) {
+				return *found;
+			}
+		}
+	}
+
+	return &gen->default_module;
+}
 
 gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) {
 	GB_ASSERT(e != nullptr);
 	lbModule **found = nullptr;
+	if (e->kind == Entity_Procedure &&
+	    e->decl_info &&
+	    e->decl_info->code_gen_module) {
+		return e->decl_info->code_gen_module;
+	}
 	if (e->file) {
 		found = map_get(&gen->modules, cast(void *)e->file);
 		if (found) {
@@ -1426,7 +1451,7 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur
 	if (p != nullptr) {
 		isize name_len = p->name.len + 1 + ts_name.len + 1 + 10 + 1;
 		char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
-		u32 guid = ++p->module->nested_type_name_guid;
+		u32 guid = 1+p->module->nested_type_name_guid.fetch_add(1);
 		name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%u", LIT(p->name), LIT(ts_name), guid);
 
 		String name = make_string(cast(u8 *)name_text, name_len-1);
@@ -1436,9 +1461,8 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur
 		// NOTE(bill): a nested type be required before its parameter procedure exists. Just give it a temp name for now
 		isize name_len = 9 + 1 + ts_name.len + 1 + 10 + 1;
 		char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
-		static u32 guid = 0;
-		guid += 1;
-		name_len = gb_snprintf(name_text, name_len, "_internal.%.*s-%u", LIT(ts_name), guid);
+		static std::atomic<u32> guid;
+		name_len = gb_snprintf(name_text, name_len, "_internal.%.*s-%u", LIT(ts_name), 1+guid.fetch_add(1));
 
 		String name = make_string(cast(u8 *)name_text, name_len-1);
 		e->TypeName.ir_mangled_name = name;
@@ -2662,9 +2686,12 @@ gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *e
 
 
 gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) {
+	lbGenerator *gen = m->gen;
+
 	GB_ASSERT(is_type_proc(e->type));
 	e = strip_entity_wrapping(e);
 	GB_ASSERT(e != nullptr);
+	GB_ASSERT(e->kind == Entity_Procedure);
 
 	lbValue *found = nullptr;
 	rw_mutex_shared_lock(&m->values_mutex);
@@ -2678,20 +2705,24 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
 
 	lbModule *other_module = m;
 	if (USE_SEPARATE_MODULES) {
-		other_module = lb_module_of_entity(m->gen, e);
+		other_module = lb_module_of_entity(gen, e);
 	}
 	if (other_module == m) {
-		debugf("Missing Procedure (lb_find_procedure_value_from_entity): %.*s\n", LIT(e->token.string));
+		debugf("Missing Procedure (lb_find_procedure_value_from_entity): %.*s module %p\n", LIT(e->token.string), m);
 	}
 	ignore_body = other_module != m;
 
 	lbProcedure *missing_proc = lb_create_procedure(m, e, ignore_body);
 	if (ignore_body) {
+		mutex_lock(&gen->anonymous_proc_lits_mutex);
+		defer (mutex_unlock(&gen->anonymous_proc_lits_mutex));
+
 		GB_ASSERT(other_module != nullptr);
 		rw_mutex_shared_lock(&other_module->values_mutex);
 		auto *found = map_get(&other_module->values, e);
 		rw_mutex_shared_unlock(&other_module->values_mutex);
 		if (found == nullptr) {
+			// THIS IS THE RACE CONDITION
 			lbProcedure *missing_proc_in_other_module = lb_create_procedure(other_module, e, false);
 			array_add(&other_module->missing_procedures_to_check, missing_proc_in_other_module);
 		}
@@ -2708,6 +2739,63 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
 }
 
 
+
+gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) {
+	lbGenerator *gen = m->gen;
+
+	mutex_lock(&gen->anonymous_proc_lits_mutex);
+	defer (mutex_unlock(&gen->anonymous_proc_lits_mutex));
+
+	TokenPos pos = ast_token(expr).pos;
+	lbProcedure **found = map_get(&gen->anonymous_proc_lits, expr);
+	if (found) {
+		return lb_find_procedure_value_from_entity(m, (*found)->entity);
+	}
+
+	ast_node(pl, ProcLit, expr);
+
+	// NOTE(bill): Generate a new name
+	// parent$count
+	isize name_len = prefix_name.len + 6 + 11;
+	char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
+	static std::atomic<i32> name_id;
+	name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), 1+name_id.fetch_add(1));
+	String name = make_string((u8 *)name_text, name_len-1);
+
+	Type *type = type_of_expr(expr);
+
+	GB_ASSERT(pl->decl->entity == nullptr);
+	Token token = {};
+	token.pos = ast_token(expr).pos;
+	token.kind = Token_Ident;
+	token.string = name;
+	Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags);
+	e->file = expr->file();
+
+	// NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time
+	pl->decl->code_gen_module = m;
+	e->decl_info = pl->decl;
+	pl->decl->entity = e;
+	e->flags |= EntityFlag_ProcBodyChecked;
+
+	lbProcedure *p = lb_create_procedure(m, e);
+	GB_ASSERT(e->code_gen_module == m);
+
+	lbValue value = {};
+	value.value = p->value;
+	value.type = p->type;
+
+	map_set(&gen->anonymous_proc_lits, expr, p);
+	array_add(&m->procedures_to_generate, p);
+	if (parent != nullptr) {
+		array_add(&parent->children, p);
+	} else {
+		string_map_set(&m->members, name, value);
+	}
+	return value;
+}
+
+
 gb_internal lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value, Entity **entity_) {
 	GB_ASSERT(type != nullptr);
 	type = default_type(type);

+ 3 - 3
src/llvm_backend_type.cpp

@@ -15,7 +15,7 @@ gb_internal isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_
 }
 
 gb_internal lbValue lb_typeid(lbModule *m, Type *type) {
-	GB_ASSERT(!build_context.disallow_rtti);
+	GB_ASSERT(!build_context.no_rtti);
 
 	type = default_type(type);
 
@@ -92,7 +92,7 @@ gb_internal lbValue lb_typeid(lbModule *m, Type *type) {
 }
 
 gb_internal lbValue lb_type_info(lbModule *m, Type *type) {
-	GB_ASSERT(!build_context.disallow_rtti);
+	GB_ASSERT(!build_context.no_rtti);
 
 	type = default_type(type);
 
@@ -141,7 +141,7 @@ gb_internal lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count)
 
 
 gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data
-	if (build_context.disallow_rtti) {
+	if (build_context.no_rtti) {
 		return;
 	}
 

+ 4 - 4
src/llvm_backend_utility.cpp

@@ -721,7 +721,7 @@ gb_internal lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type
 		Type *dst_type = tuple->Tuple.variables[0]->type;
 
 		isize arg_count = 7;
-		if (build_context.disallow_rtti) {
+		if (build_context.no_rtti) {
 			arg_count = 4;
 		}
 
@@ -733,7 +733,7 @@ gb_internal lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type
 		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) {
+		if (!build_context.no_rtti) {
 			args[4] = lb_typeid(m, src_type);
 			args[5] = lb_typeid(m, dst_type);
 			args[6] = lb_emit_conv(p, value_, t_rawptr);
@@ -797,7 +797,7 @@ gb_internal lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *ty
 		lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
 
 		isize arg_count = 7;
-		if (build_context.disallow_rtti) {
+		if (build_context.no_rtti) {
 			arg_count = 4;
 		}
 		auto args = array_make<lbValue>(permanent_allocator(), arg_count);
@@ -807,7 +807,7 @@ gb_internal lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *ty
 		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) {
+		if (!build_context.no_rtti) {
 			args[4] = any_typeid;
 			args[5] = dst_typeid;
 			args[6] = lb_emit_struct_ev(p, value, 0);

+ 9 - 18
src/main.cpp

@@ -655,7 +655,6 @@ enum BuildFlagKind {
 	BuildFlag_ShowDebugMessages,
 	BuildFlag_Vet,
 	BuildFlag_VetExtra,
-	BuildFlag_UseLLVMApi,
 	BuildFlag_IgnoreUnknownAttributes,
 	BuildFlag_ExtraLinkerFlags,
 	BuildFlag_ExtraAssemblerFlags,
@@ -671,11 +670,10 @@ enum BuildFlagKind {
 
 	BuildFlag_DisallowDo,
 	BuildFlag_DefaultToNilAllocator,
-	BuildFlag_InsertSemicolon,
 	BuildFlag_StrictStyle,
 	BuildFlag_StrictStyleInitOnly,
 	BuildFlag_ForeignErrorProcedures,
-	BuildFlag_DisallowRTTI,
+	BuildFlag_NoRTTI,
 	BuildFlag_DynamicMapCalls,
 
 	BuildFlag_Compact,
@@ -834,7 +832,6 @@ gb_internal bool parse_build_flags(Array<String> args) {
 	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);
@@ -849,12 +846,12 @@ gb_internal bool parse_build_flags(Array<String> args) {
 
 	add_flag(&build_flags, BuildFlag_DisallowDo,              str_lit("disallow-do"),               BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_DefaultToNilAllocator,   str_lit("default-to-nil-allocator"),  BuildFlagParam_None,    Command__does_check);
-	add_flag(&build_flags, BuildFlag_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_NoRTTI,                  str_lit("no-rtti"),                   BuildFlagParam_None,    Command__does_check);
+	add_flag(&build_flags, BuildFlag_NoRTTI,                  str_lit("disallow-rtti"),             BuildFlagParam_None,    Command__does_check);
 
 	add_flag(&build_flags, BuildFlag_DynamicMapCalls,         str_lit("dynamic-map-calls"),         BuildFlagParam_None,    Command__does_check);
 
@@ -1372,11 +1369,6 @@ gb_internal bool parse_build_flags(Array<String> args) {
 							build_context.vet_extra = true;
 							break;
 						}
-						case BuildFlag_UseLLVMApi: {
-							gb_printf_err("-llvm-api flag is not required any more\n");
-							bad_flags = true;
-							break;
-						}
 						case BuildFlag_IgnoreUnknownAttributes:
 							build_context.ignore_unknown_attributes = true;
 							break;
@@ -1448,8 +1440,12 @@ gb_internal bool parse_build_flags(Array<String> args) {
 						case BuildFlag_DisallowDo:
 							build_context.disallow_do = true;
 							break;
-						case BuildFlag_DisallowRTTI:
-							build_context.disallow_rtti = true;
+						case BuildFlag_NoRTTI:
+							if (name == "disallow-rtti") {
+								gb_printf_err("'-disallow-rtti' has been replaced with '-no-rtti'\n");
+								bad_flags = true;
+							}
+							build_context.no_rtti = true;
 							break;
 						case BuildFlag_DynamicMapCalls:
 							build_context.dynamic_map_calls = true;
@@ -1460,11 +1456,6 @@ gb_internal bool parse_build_flags(Array<String> args) {
 						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;
-							break;
-						}
 						case BuildFlag_StrictStyle: {
 							if (build_context.strict_style_init_only) {
 								gb_printf_err("-strict-style and -strict-style-init-only cannot be used together\n");

+ 7 - 0
tests/issues/run.sh

@@ -6,6 +6,8 @@ pushd build
 ODIN=../../../odin
 COMMON="-collection:tests=../.."
 
+NO_NIL_ERR="Error: "
+
 set -x
 
 $ODIN test ../test_issue_829.odin  $COMMON -file
@@ -14,6 +16,11 @@ $ODIN test ../test_issue_2056.odin $COMMON -file
 $ODIN test ../test_issue_2087.odin $COMMON -file
 $ODIN build ../test_issue_2113.odin $COMMON -file -debug
 $ODIN test ../test_issue_2466.odin $COMMON -file
+if [[ $($ODIN build ../test_issue_2395.odin $COMMON -file 2>&1 >/dev/null | grep -c "$NO_NIL_ERR") -eq 2 ]] ; then
+	echo "SUCCESSFUL 1/1"
+else
+	echo "SUCCESSFUL 0/1"
+fi
 
 set +x
 

+ 29 - 0
tests/issues/test_issue_2395.odin

@@ -0,0 +1,29 @@
+// Tests issue #2395 https://github.com/odin-lang/Odin/issues/2395
+
+// Ensures that we no longer raise the faulty error for #no_nil unions when
+// then are 2 variants with the polymorphic type. Also ensure that we raise
+// exactly 2 errors from the invalid unions
+package test_issues
+
+import "core:testing"
+
+ValidUnion :: union($T: typeid) #no_nil {
+    T,
+    f32,
+}
+
+OtherValidUnion :: union($T: typeid, $S: typeid) #no_nil {
+    T,
+    S,
+}
+
+InvalidUnion :: union($T: typeid) #no_nil {
+    T,
+}
+
+OtherInvalidUnion :: union($T: typeid) #no_nil {
+    u8,
+}
+
+main :: proc() {
+}