Browse Source

`#optional_ok` tag for procedures

gingerBill 5 years ago
parent
commit
97f7a558fa
10 changed files with 417 additions and 308 deletions
  1. 9 1
      core/container/array.odin
  2. 11 4
      core/mem/mem.odin
  3. 51 1
      src/check_expr.cpp
  4. 17 0
      src/check_type.cpp
  5. 1 1
      src/common.cpp
  6. 311 297
      src/ir.cpp
  7. 7 1
      src/llvm_backend.cpp
  8. 1 0
      src/parser.cpp
  9. 3 0
      src/parser.hpp
  10. 6 3
      src/types.cpp

+ 9 - 1
core/container/array.odin

@@ -47,10 +47,16 @@ array_slice :: proc(a: $A/Array($T)) -> []T {
 
 
 array_get :: proc(a: $A/Array($T), index: int) -> T {
+	assert(uint(index) < a.len);
 	return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^;
 }
+array_get_ptr :: proc(a: $A/Array($T), index: int) -> ^T {
+	assert(uint(index) < a.len);
+	return (^T)(uintptr(a.data) + size_of(T)*uintptr(index));
+}
 
 array_set :: proc(a: ^$A/Array($T), index: int, item: T)  {
+	assert(uint(index) < a.len);
 	(^T)(uintptr(a.data) + size_of(T)*uintptr(index))^ = item;
 }
 
@@ -122,7 +128,7 @@ array_clear :: proc(q: ^$Q/Queue($T)) {
 }
 
 
-array_push :: proc(a: ^$A/Array($T), items: ..T) {
+array_push_back_elems :: proc(a: ^$A/Array($T), items: ..T) {
 	if array_space(a^) < len(items) {
 		array_grow(a, a.size + len(items));
 	}
@@ -133,6 +139,8 @@ array_push :: proc(a: ^$A/Array($T), items: ..T) {
 	a.len = offset + n;
 }
 
+array_push   :: proc{array_push_back, array_push_back_elems};
+array_append :: proc{array_push_back, array_push_back_elems};
 
 array_set_capacity :: proc(a: ^$A/Array($T), new_capacity: int) {
 	if new_capacity == a.cap {

+ 11 - 4
core/mem/mem.odin

@@ -107,8 +107,12 @@ ptr_sub :: inline proc "contextless" (a, b: $P/^$T) -> int {
 
 slice_ptr :: inline proc "contextless" (ptr: ^$T, len: int) -> []T {
 	assert(len >= 0);
-	slice := Raw_Slice{data = ptr, len = len};
-	return transmute([]T)slice;
+	return transmute([]T)Raw_Slice{data = ptr, len = len};
+}
+
+slice_ptr_to_bytes :: proc "contextless" (ptr: rawptr, len: int) -> []byte {
+	assert(len >= 0);
+	return transmute([]byte)Raw_Slice{data = ptr, len = len};
 }
 
 slice_to_bytes :: inline proc "contextless" (slice: $E/[]$T) -> []byte {
@@ -127,16 +131,19 @@ slice_data_cast :: inline proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -
 	}
 }
 
+slice_to_components :: proc "contextless" (slice: $E/[]$T) -> (data: ^T, len: int) {
+	s := transmute(Raw_Slice)slice;
+	return s.data, s.len;
+}
 
 buffer_from_slice :: inline proc(backing: $T/[]$E) -> [dynamic]E {
 	s := transmute(Raw_Slice)backing;
-	d := Raw_Dynamic_Array{
+	return transmute([dynamic]E)Raw_Dynamic_Array{
 		data      = s.data,
 		len       = 0,
 		cap       = s.len,
 		allocator = nil_allocator(),
 	};
-	return transmute([dynamic]E)d;
 }
 
 ptr_to_bytes :: inline proc "contextless" (ptr: ^$T, len := 1) -> []byte {

+ 51 - 1
src/check_expr.cpp

@@ -5750,12 +5750,41 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
 
 				optional_ok = true;
 				tuple_index += 2;
+			} else if (o.mode == Addressing_OptionalOk) {
+				Type *tuple = o.type;
+				GB_ASSERT(is_type_tuple(tuple));
+				GB_ASSERT(tuple->Tuple.variables.count == 2);
+				Ast *expr = unparen_expr(o.expr);
+				if (expr->kind == Ast_CallExpr) {
+					expr->CallExpr.optional_ok_one = true;
+				}
+				Operand val = o;
+				val.type = tuple->Tuple.variables[0]->type;
+				val.mode = Addressing_Value;
+				array_add(operands, val);
+				tuple_index += 1;
 			} else {
 				array_add(operands, o);
 				tuple_index += 1;
 			}
 		} else {
 			TypeTuple *tuple = &o.type->Tuple;
+			if (o.mode == Addressing_OptionalOk) {
+				GB_ASSERT(tuple->variables.count == 2);
+				if (lhs.count == 1) {
+					Ast *expr = unparen_expr(o.expr);
+					if (expr->kind == Ast_CallExpr) {
+						expr->CallExpr.optional_ok_one = true;
+					}
+					Operand val = o;
+					val.type = tuple->variables[0]->type;
+					val.mode = Addressing_Value;
+					array_add(operands, val);
+					tuple_index += 1;
+					continue;
+				}
+			}
+
 			for_array(j, tuple->variables) {
 				o.type = tuple->variables[j]->type;
 				array_add(operands, o);
@@ -5839,6 +5868,22 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
 			}
 		} else {
 			TypeTuple *tuple = &o.type->Tuple;
+			if (o.mode == Addressing_OptionalOk) {
+				GB_ASSERT(tuple->variables.count == 2);
+				if (lhs_count == 1) {
+					Ast *expr = unparen_expr(o.expr);
+					if (expr->kind == Ast_CallExpr) {
+						expr->CallExpr.optional_ok_one = true;
+					}
+					Operand val = o;
+					val.type = tuple->variables[0]->type;
+					val.mode = Addressing_Value;
+					array_add(operands, val);
+					tuple_index += 1;
+					continue;
+				}
+			}
+
 			for_array(j, tuple->variables) {
 				o.type = tuple->variables[j]->type;
 				array_add(operands, o);
@@ -7332,7 +7377,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t
 					if (pl->inlining == ProcInlining_no_inline) {
 						error(call, "'inline' cannot be applied to a procedure that has be marked as 'no_inline'");
 					}
-			}
+				}
 			}
 			break;
 		}
@@ -7342,6 +7387,11 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t
 	}
 
 	operand->expr = call;
+
+	if (pt->kind == Type_Proc && pt->Proc.optional_ok) {
+		operand->mode = Addressing_OptionalOk;
+	}
+
 	return Expr_Expr;
 }
 

+ 17 - 0
src/check_type.cpp

@@ -2530,6 +2530,22 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
 	}
 	GB_ASSERT(cc > 0);
 
+	bool optional_ok = (pt->tags & ProcTag_optional_ok) != 0;
+	if (optional_ok) {
+		if (result_count != 2) {
+			error(proc_type_node, "A procedure type with the #optional_ok tag requires 2 return values, got %td", result_count);
+		} else {
+			Entity *second = results->Tuple.variables[1];
+			if (is_type_polymorphic(second->type)) {
+				// ignore
+			} else if (is_type_boolean(second->type)) {
+				// GOOD
+			} else {
+				error(second->token, "Second return value of an #optional_ok procedure must be a boolean, got %s", type_to_string(second->type));
+			}
+		}
+	}
+
 	type->Proc.node                 = proc_type_node;
 	type->Proc.scope                = c->scope;
 	type->Proc.params               = params;
@@ -2542,6 +2558,7 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
 	type->Proc.is_polymorphic       = pt->generic;
 	type->Proc.specialization_count = specialization_count;
 	type->Proc.diverging            = pt->diverging;
+	type->Proc.optional_ok          = optional_ok;
 	type->Proc.tags                 = pt->tags;
 
 	if (param_count > 0) {

+ 1 - 1
src/common.cpp

@@ -468,8 +468,8 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) {
 
 
 struct StringIntern {
-	isize len;
 	StringIntern *next;
+	isize len;
 	char str[1];
 };
 

+ 311 - 297
src/ir.cpp

@@ -7081,6 +7081,311 @@ irValue *ir_build_expr(irProcedure *proc, Ast *expr) {
 	return v;
 }
 
+
+irValue *ir_build_call_expr(irProcedure *proc, Ast *expr) {
+	ast_node(ce, CallExpr, expr);
+	TypeAndValue tv = type_and_value_of_expr(expr);
+	TypeAndValue proc_tv = type_and_value_of_expr(ce->proc);
+	AddressingMode proc_mode = proc_tv.mode;
+	if (proc_mode == Addressing_Type) {
+		GB_ASSERT(ce->args.count == 1);
+		irValue *x = ir_build_expr(proc, ce->args[0]);
+		irValue *y = ir_emit_conv(proc, x, tv.type);
+		return y;
+	}
+
+	Ast *p = unparen_expr(ce->proc);
+	if (proc_mode == Addressing_Builtin) {
+		Entity *e = entity_of_node(p);
+		BuiltinProcId id = BuiltinProc_Invalid;
+		if (e != nullptr) {
+			id = cast(BuiltinProcId)e->Builtin.id;
+		} else {
+			id = BuiltinProc_DIRECTIVE;
+		}
+		return ir_build_builtin_proc(proc, expr, tv, id);
+	}
+
+	// NOTE(bill): Regular call
+	irValue *value = nullptr;
+	Ast *proc_expr = unparen_expr(ce->proc);
+	if (proc_expr->tav.mode == Addressing_Constant) {
+		ExactValue v = proc_expr->tav.value;
+		switch (v.kind) {
+		case ExactValue_Integer:
+			{
+				u64 u = big_int_to_u64(&v.value_integer);
+				irValue *x = ir_const_uintptr(u);
+				x = ir_emit_conv(proc, x, t_rawptr);
+				value = ir_emit_conv(proc, x, proc_expr->tav.type);
+				break;
+			}
+		case ExactValue_Pointer:
+			{
+				u64 u = cast(u64)v.value_pointer;
+				irValue *x = ir_const_uintptr(u);
+				x = ir_emit_conv(proc, x, t_rawptr);
+				value = ir_emit_conv(proc, x, proc_expr->tav.type);
+				break;
+			}
+		}
+	}
+
+	if (value == nullptr) {
+		value = ir_build_expr(proc, proc_expr);
+	}
+
+	GB_ASSERT(value != nullptr);
+	Type *proc_type_ = base_type(ir_type(value));
+	GB_ASSERT(proc_type_->kind == Type_Proc);
+	TypeProc *pt = &proc_type_->Proc;
+	set_procedure_abi_types(heap_allocator(), proc_type_);
+
+	if (is_call_expr_field_value(ce)) {
+		auto args = array_make<irValue *>(ir_allocator(), pt->param_count);
+
+		for_array(arg_index, ce->args) {
+			Ast *arg = ce->args[arg_index];
+			ast_node(fv, FieldValue, arg);
+			GB_ASSERT(fv->field->kind == Ast_Ident);
+			String name = fv->field->Ident.token.string;
+			isize index = lookup_procedure_parameter(pt, name);
+			GB_ASSERT(index >= 0);
+			TypeAndValue tav = type_and_value_of_expr(fv->value);
+			if (tav.mode == Addressing_Type) {
+				args[index] = ir_value_nil(tav.type);
+			} else {
+				args[index] = ir_build_expr(proc, fv->value);
+			}
+		}
+		TypeTuple *params = &pt->params->Tuple;
+		for (isize i = 0; i < args.count; i++) {
+			Entity *e = params->variables[i];
+			if (e->kind == Entity_TypeName) {
+				args[i] = ir_value_nil(e->type);
+			} else if (e->kind == Entity_Constant) {
+				continue;
+			} else {
+				GB_ASSERT(e->kind == Entity_Variable);
+				if (args[i] == nullptr) {
+					switch (e->Variable.param_value.kind) {
+					case ParameterValue_Constant:
+						args[i] = ir_value_constant(e->type, e->Variable.param_value.value);
+						break;
+					case ParameterValue_Nil:
+						args[i] = ir_value_nil(e->type);
+						break;
+					case ParameterValue_Location:
+						args[i] = ir_emit_source_code_location(proc, proc->entity->token.string, ast_token(expr).pos);
+						break;
+					case ParameterValue_Value:
+						args[i] = ir_build_expr(proc, e->Variable.param_value.ast_value);
+						break;
+					}
+				} else {
+					args[i] = ir_emit_conv(proc, args[i], e->type);
+				}
+			}
+		}
+
+		return ir_emit_call(proc, value, args, ce->inlining, proc->return_ptr_hint_ast == expr);
+	}
+
+	isize arg_index = 0;
+
+	isize arg_count = 0;
+	for_array(i, ce->args) {
+		Ast *arg = ce->args[i];
+		TypeAndValue tav = type_and_value_of_expr(arg);
+		GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s", expr_to_string(arg), expr_to_string(expr));
+		GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg));
+		Type *at = tav.type;
+		if (at->kind == Type_Tuple) {
+			arg_count += at->Tuple.variables.count;
+		} else {
+			arg_count++;
+		}
+	}
+
+	isize param_count = 0;
+	if (pt->params) {
+		GB_ASSERT(pt->params->kind == Type_Tuple);
+		param_count = pt->params->Tuple.variables.count;
+	}
+
+	auto args = array_make<irValue *>(ir_allocator(), cast(isize)gb_max(param_count, arg_count));
+	isize variadic_index = pt->variadic_index;
+	bool variadic = pt->variadic && variadic_index >= 0;
+	bool vari_expand = ce->ellipsis.pos.line != 0;
+	bool is_c_vararg = pt->c_vararg;
+
+	String proc_name = {};
+	if (proc->entity != nullptr) {
+		proc_name = proc->entity->token.string;
+	}
+	TokenPos pos = ast_token(ce->proc).pos;
+
+	TypeTuple *param_tuple = nullptr;
+	if (pt->params) {
+		GB_ASSERT(pt->params->kind == Type_Tuple);
+		param_tuple = &pt->params->Tuple;
+	}
+
+	for_array(i, ce->args) {
+		Ast *arg = ce->args[i];
+		TypeAndValue arg_tv = type_and_value_of_expr(arg);
+		if (arg_tv.mode == Addressing_Type) {
+			args[arg_index++] = ir_value_nil(arg_tv.type);
+		} else {
+			irValue *a = ir_build_expr(proc, arg);
+			Type *at = ir_type(a);
+			if (at->kind == Type_Tuple) {
+				for_array(i, at->Tuple.variables) {
+					Entity *e = at->Tuple.variables[i];
+					irValue *v = ir_emit_struct_ev(proc, a, cast(i32)i);
+					args[arg_index++] = v;
+				}
+			} else {
+				args[arg_index++] = a;
+			}
+		}
+	}
+
+
+	if (param_count > 0) {
+		GB_ASSERT_MSG(pt->params != nullptr, "%s %td", expr_to_string(expr), pt->param_count);
+		GB_ASSERT(param_count < 1000000);
+
+		if (arg_count < param_count) {
+			isize end = cast(isize)param_count;
+			if (variadic) {
+				end = variadic_index;
+			}
+			while (arg_index < end) {
+				Entity *e = param_tuple->variables[arg_index];
+				GB_ASSERT(e->kind == Entity_Variable);
+
+				switch (e->Variable.param_value.kind) {
+				case ParameterValue_Constant:
+					args[arg_index++] = ir_value_constant(e->type, e->Variable.param_value.value);
+					break;
+				case ParameterValue_Nil:
+					args[arg_index++] = ir_value_nil(e->type);
+					break;
+				case ParameterValue_Location:
+					args[arg_index++] = ir_emit_source_code_location(proc, proc_name, pos);
+					break;
+				case ParameterValue_Value:
+					args[arg_index++] = ir_build_expr(proc, e->Variable.param_value.ast_value);
+					break;
+				}
+			}
+		}
+
+		if (is_c_vararg) {
+			GB_ASSERT(variadic);
+			GB_ASSERT(!vari_expand);
+			isize i = 0;
+			for (; i < variadic_index; i++) {
+				Entity *e = param_tuple->variables[i];
+				if (e->kind == Entity_Variable) {
+					args[i] = ir_emit_conv(proc, args[i], e->type);
+				}
+			}
+			Type *variadic_type = param_tuple->variables[i]->type;
+			GB_ASSERT(is_type_slice(variadic_type));
+			variadic_type = base_type(variadic_type)->Slice.elem;
+			if (!is_type_any(variadic_type)) {
+				for (; i < arg_count; i++) {
+					args[i] = ir_emit_conv(proc, args[i], variadic_type);
+				}
+			} else {
+				for (; i < arg_count; i++) {
+					args[i] = ir_emit_conv(proc, args[i], default_type(ir_type(args[i])));
+				}
+			}
+		} else if (variadic) {
+			isize i = 0;
+			for (; i < variadic_index; i++) {
+				Entity *e = param_tuple->variables[i];
+				if (e->kind == Entity_Variable) {
+					args[i] = ir_emit_conv(proc, args[i], e->type);
+				}
+			}
+			if (!vari_expand) {
+				Type *variadic_type = param_tuple->variables[i]->type;
+				GB_ASSERT(is_type_slice(variadic_type));
+				variadic_type = base_type(variadic_type)->Slice.elem;
+				for (; i < arg_count; i++) {
+					args[i] = ir_emit_conv(proc, args[i], variadic_type);
+				}
+			}
+		} else {
+			for (isize i = 0; i < param_count; i++) {
+				Entity *e = param_tuple->variables[i];
+				if (e->kind == Entity_Variable) {
+					GB_ASSERT(args[i] != nullptr);
+					args[i] = ir_emit_conv(proc, args[i], e->type);
+				}
+			}
+		}
+
+		if (variadic && !vari_expand && !is_c_vararg) {
+			ir_emit_comment(proc, str_lit("variadic call argument generation"));
+			gbAllocator allocator = ir_allocator();
+			Type *slice_type = param_tuple->variables[variadic_index]->type;
+			Type *elem_type  = base_type(slice_type)->Slice.elem;
+			irValue *slice = ir_add_local_generated(proc, slice_type, true);
+			isize slice_len = arg_count+1 - (variadic_index+1);
+
+			if (slice_len > 0) {
+				irValue *base_array = ir_add_local_generated(proc, alloc_type_array(elem_type, slice_len), true);
+
+				for (isize i = variadic_index, j = 0; i < arg_count; i++, j++) {
+					irValue *addr = ir_emit_array_epi(proc, base_array, cast(i32)j);
+					ir_emit_store(proc, addr, args[i]);
+				}
+
+				irValue *base_elem = ir_emit_array_epi(proc, base_array, 0);
+				irValue *len = ir_const_int(slice_len);
+				ir_fill_slice(proc, slice, base_elem, len);
+			}
+
+			arg_count = param_count;
+			args[variadic_index] = ir_emit_load(proc, slice);
+		}
+	}
+
+	if (variadic && variadic_index+1 < param_count) {
+		for (isize i = variadic_index+1; i < param_count; i++) {
+			Entity *e = param_tuple->variables[i];
+			switch (e->Variable.param_value.kind) {
+			case ParameterValue_Constant:
+				args[i] = ir_value_constant(e->type, e->Variable.param_value.value);
+				break;
+			case ParameterValue_Nil:
+				args[i] = ir_value_nil(e->type);
+				break;
+			case ParameterValue_Location:
+				args[i] = ir_emit_source_code_location(proc, proc_name, pos);
+				break;
+			case ParameterValue_Value:
+				args[i] = ir_build_expr(proc, e->Variable.param_value.ast_value);
+				break;
+			}
+		}
+	}
+
+	isize final_count = param_count;
+	if (is_c_vararg) {
+		final_count = arg_count;
+	}
+
+	auto call_args = array_slice(args, 0, final_count);
+	return ir_emit_call(proc, value, call_args, ce->inlining, proc->return_ptr_hint_ast == expr);
+}
+
+
 irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
 	Ast *original_expr = expr;
 	expr = unparen_expr(expr);
@@ -7551,304 +7856,13 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
 
 
 	case_ast_node(ce, CallExpr, expr);
-		TypeAndValue proc_tv = type_and_value_of_expr(ce->proc);
-		AddressingMode proc_mode = proc_tv.mode;
-		if (proc_mode == Addressing_Type) {
-			GB_ASSERT(ce->args.count == 1);
-			irValue *x = ir_build_expr(proc, ce->args[0]);
-			irValue *y = ir_emit_conv(proc, x, tv.type);
-			return y;
-		}
-
-		Ast *p = unparen_expr(ce->proc);
-		if (proc_mode == Addressing_Builtin) {
-			Entity *e = entity_of_node(p);
-			BuiltinProcId id = BuiltinProc_Invalid;
-			if (e != nullptr) {
-				id = cast(BuiltinProcId)e->Builtin.id;
-			} else {
-				id = BuiltinProc_DIRECTIVE;
-			}
-			return ir_build_builtin_proc(proc, expr, tv, id);
-		}
-
-		// NOTE(bill): Regular call
-		irValue *value = nullptr;
-		Ast *proc_expr = unparen_expr(ce->proc);
-		if (proc_expr->tav.mode == Addressing_Constant) {
-			ExactValue v = proc_expr->tav.value;
-			switch (v.kind) {
-			case ExactValue_Integer:
-				{
-					u64 u = big_int_to_u64(&v.value_integer);
-					irValue *x = ir_const_uintptr(u);
-					x = ir_emit_conv(proc, x, t_rawptr);
-					value = ir_emit_conv(proc, x, proc_expr->tav.type);
-					break;
-				}
-			case ExactValue_Pointer:
-				{
-					u64 u = cast(u64)v.value_pointer;
-					irValue *x = ir_const_uintptr(u);
-					x = ir_emit_conv(proc, x, t_rawptr);
-					value = ir_emit_conv(proc, x, proc_expr->tav.type);
-					break;
-				}
-			}
-		}
-
-		if (value == nullptr) {
-			value = ir_build_expr(proc, proc_expr);
-		}
-
-		GB_ASSERT(value != nullptr);
-		Type *proc_type_ = base_type(ir_type(value));
-		GB_ASSERT(proc_type_->kind == Type_Proc);
-		TypeProc *pt = &proc_type_->Proc;
-		set_procedure_abi_types(heap_allocator(), proc_type_);
-
-		if (is_call_expr_field_value(ce)) {
-			auto args = array_make<irValue *>(ir_allocator(), pt->param_count);
-
-			for_array(arg_index, ce->args) {
-				Ast *arg = ce->args[arg_index];
-				ast_node(fv, FieldValue, arg);
-				GB_ASSERT(fv->field->kind == Ast_Ident);
-				String name = fv->field->Ident.token.string;
-				isize index = lookup_procedure_parameter(pt, name);
-				GB_ASSERT(index >= 0);
-				TypeAndValue tav = type_and_value_of_expr(fv->value);
-				if (tav.mode == Addressing_Type) {
-					args[index] = ir_value_nil(tav.type);
-				} else {
-					args[index] = ir_build_expr(proc, fv->value);
-				}
-			}
-			TypeTuple *params = &pt->params->Tuple;
-			for (isize i = 0; i < args.count; i++) {
-				Entity *e = params->variables[i];
-				if (e->kind == Entity_TypeName) {
-					args[i] = ir_value_nil(e->type);
-				} else if (e->kind == Entity_Constant) {
-					continue;
-				} else {
-					GB_ASSERT(e->kind == Entity_Variable);
-					if (args[i] == nullptr) {
-						switch (e->Variable.param_value.kind) {
-						case ParameterValue_Constant:
-							args[i] = ir_value_constant(e->type, e->Variable.param_value.value);
-							break;
-						case ParameterValue_Nil:
-							args[i] = ir_value_nil(e->type);
-							break;
-						case ParameterValue_Location:
-							args[i] = ir_emit_source_code_location(proc, proc->entity->token.string, ast_token(expr).pos);
-							break;
-						case ParameterValue_Value:
-							args[i] = ir_build_expr(proc, e->Variable.param_value.ast_value);
-							break;
-						}
-					} else {
-						args[i] = ir_emit_conv(proc, args[i], e->type);
-					}
-				}
-			}
-
-			return ir_emit_call(proc, value, args, ce->inlining, proc->return_ptr_hint_ast == expr);
-		}
-
-		isize arg_index = 0;
-
-		isize arg_count = 0;
-		for_array(i, ce->args) {
-			Ast *arg = ce->args[i];
-			TypeAndValue tav = type_and_value_of_expr(arg);
-			GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s", expr_to_string(arg), expr_to_string(expr));
-			GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg));
-			Type *at = tav.type;
-			if (at->kind == Type_Tuple) {
-				arg_count += at->Tuple.variables.count;
-			} else {
-				arg_count++;
-			}
-		}
-
-		isize param_count = 0;
-		if (pt->params) {
-			GB_ASSERT(pt->params->kind == Type_Tuple);
-			param_count = pt->params->Tuple.variables.count;
-		}
-
-		auto args = array_make<irValue *>(ir_allocator(), cast(isize)gb_max(param_count, arg_count));
-		isize variadic_index = pt->variadic_index;
-		bool variadic = pt->variadic && variadic_index >= 0;
-		bool vari_expand = ce->ellipsis.pos.line != 0;
-		bool is_c_vararg = pt->c_vararg;
-
-		String proc_name = {};
-		if (proc->entity != nullptr) {
-			proc_name = proc->entity->token.string;
-		}
-		TokenPos pos = ast_token(ce->proc).pos;
-
-		TypeTuple *param_tuple = nullptr;
-		if (pt->params) {
-			GB_ASSERT(pt->params->kind == Type_Tuple);
-			param_tuple = &pt->params->Tuple;
-		}
-
-		for_array(i, ce->args) {
-			Ast *arg = ce->args[i];
-			TypeAndValue arg_tv = type_and_value_of_expr(arg);
-			if (arg_tv.mode == Addressing_Type) {
-				args[arg_index++] = ir_value_nil(arg_tv.type);
-			} else {
-				irValue *a = ir_build_expr(proc, arg);
-				Type *at = ir_type(a);
-				if (at->kind == Type_Tuple) {
-					for_array(i, at->Tuple.variables) {
-						Entity *e = at->Tuple.variables[i];
-						irValue *v = ir_emit_struct_ev(proc, a, cast(i32)i);
-						args[arg_index++] = v;
-					}
-				} else {
-					args[arg_index++] = a;
-				}
-			}
+		irValue *res = ir_build_call_expr(proc, expr);
+		if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures
+			GB_ASSERT(is_type_tuple(ir_type(res)));
+			GB_ASSERT(ir_type(res)->Tuple.variables.count == 2);
+			return ir_emit_struct_ev(proc, res, 0);
 		}
-
-
-		if (param_count > 0) {
-			GB_ASSERT_MSG(pt->params != nullptr, "%s %td", expr_to_string(expr), pt->param_count);
-			GB_ASSERT(param_count < 1000000);
-
-			if (arg_count < param_count) {
-				isize end = cast(isize)param_count;
-				if (variadic) {
-					end = variadic_index;
-				}
-				while (arg_index < end) {
-					Entity *e = param_tuple->variables[arg_index];
-					GB_ASSERT(e->kind == Entity_Variable);
-
-					switch (e->Variable.param_value.kind) {
-					case ParameterValue_Constant:
-						args[arg_index++] = ir_value_constant(e->type, e->Variable.param_value.value);
-						break;
-					case ParameterValue_Nil:
-						args[arg_index++] = ir_value_nil(e->type);
-						break;
-					case ParameterValue_Location:
-						args[arg_index++] = ir_emit_source_code_location(proc, proc_name, pos);
-						break;
-					case ParameterValue_Value:
-						args[arg_index++] = ir_build_expr(proc, e->Variable.param_value.ast_value);
-						break;
-					}
-				}
-			}
-
-			if (is_c_vararg) {
-				GB_ASSERT(variadic);
-				GB_ASSERT(!vari_expand);
-				isize i = 0;
-				for (; i < variadic_index; i++) {
-					Entity *e = param_tuple->variables[i];
-					if (e->kind == Entity_Variable) {
-						args[i] = ir_emit_conv(proc, args[i], e->type);
-					}
-				}
-				Type *variadic_type = param_tuple->variables[i]->type;
-				GB_ASSERT(is_type_slice(variadic_type));
-				variadic_type = base_type(variadic_type)->Slice.elem;
-				if (!is_type_any(variadic_type)) {
-					for (; i < arg_count; i++) {
-						args[i] = ir_emit_conv(proc, args[i], variadic_type);
-					}
-				} else {
-					for (; i < arg_count; i++) {
-						args[i] = ir_emit_conv(proc, args[i], default_type(ir_type(args[i])));
-					}
-				}
-			} else if (variadic) {
-				isize i = 0;
-				for (; i < variadic_index; i++) {
-					Entity *e = param_tuple->variables[i];
-					if (e->kind == Entity_Variable) {
-						args[i] = ir_emit_conv(proc, args[i], e->type);
-					}
-				}
-				if (!vari_expand) {
-					Type *variadic_type = param_tuple->variables[i]->type;
-					GB_ASSERT(is_type_slice(variadic_type));
-					variadic_type = base_type(variadic_type)->Slice.elem;
-					for (; i < arg_count; i++) {
-						args[i] = ir_emit_conv(proc, args[i], variadic_type);
-					}
-				}
-			} else {
-				for (isize i = 0; i < param_count; i++) {
-					Entity *e = param_tuple->variables[i];
-					if (e->kind == Entity_Variable) {
-						GB_ASSERT(args[i] != nullptr);
-						args[i] = ir_emit_conv(proc, args[i], e->type);
-					}
-				}
-			}
-
-			if (variadic && !vari_expand && !is_c_vararg) {
-				ir_emit_comment(proc, str_lit("variadic call argument generation"));
-				gbAllocator allocator = ir_allocator();
-				Type *slice_type = param_tuple->variables[variadic_index]->type;
-				Type *elem_type  = base_type(slice_type)->Slice.elem;
-				irValue *slice = ir_add_local_generated(proc, slice_type, true);
-				isize slice_len = arg_count+1 - (variadic_index+1);
-
-				if (slice_len > 0) {
-					irValue *base_array = ir_add_local_generated(proc, alloc_type_array(elem_type, slice_len), true);
-
-					for (isize i = variadic_index, j = 0; i < arg_count; i++, j++) {
-						irValue *addr = ir_emit_array_epi(proc, base_array, cast(i32)j);
-						ir_emit_store(proc, addr, args[i]);
-					}
-
-					irValue *base_elem = ir_emit_array_epi(proc, base_array, 0);
-					irValue *len = ir_const_int(slice_len);
-					ir_fill_slice(proc, slice, base_elem, len);
-				}
-
-				arg_count = param_count;
-				args[variadic_index] = ir_emit_load(proc, slice);
-			}
-		}
-
-		if (variadic && variadic_index+1 < param_count) {
-			for (isize i = variadic_index+1; i < param_count; i++) {
-				Entity *e = param_tuple->variables[i];
-				switch (e->Variable.param_value.kind) {
-				case ParameterValue_Constant:
-					args[i] = ir_value_constant(e->type, e->Variable.param_value.value);
-					break;
-				case ParameterValue_Nil:
-					args[i] = ir_value_nil(e->type);
-					break;
-				case ParameterValue_Location:
-					args[i] = ir_emit_source_code_location(proc, proc_name, pos);
-					break;
-				case ParameterValue_Value:
-					args[i] = ir_build_expr(proc, e->Variable.param_value.ast_value);
-					break;
-				}
-			}
-		}
-
-		isize final_count = param_count;
-		if (is_c_vararg) {
-			final_count = arg_count;
-		}
-
-		auto call_args = array_slice(args, 0, final_count);
-		return ir_emit_call(proc, value, call_args, ce->inlining, proc->return_ptr_hint_ast == expr);
+		return res;
 	case_end;
 
 	case_ast_node(se, SliceExpr, expr);

+ 7 - 1
src/llvm_backend.cpp

@@ -8903,7 +8903,13 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 	case_end;
 
 	case_ast_node(ce, CallExpr, expr);
-		return lb_build_call_expr(p, expr);
+		lbValue res = lb_build_call_expr(p, expr);
+		if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures
+			GB_ASSERT(is_type_tuple(res.type));
+			GB_ASSERT(res.type->Tuple.variables.count == 2);
+			return lb_emit_struct_ev(p, res, 0);
+		}
+		return res;
 	case_end;
 
 	case_ast_node(se, SliceExpr, expr);

+ 1 - 0
src/parser.cpp

@@ -1618,6 +1618,7 @@ void parse_proc_tags(AstFile *f, u64 *tags) {
 		}
 
 		if (false) {}
+		ELSE_IF_ADD_TAG(optional_ok)
 		ELSE_IF_ADD_TAG(require_results)
 		ELSE_IF_ADD_TAG(bounds_check)
 		ELSE_IF_ADD_TAG(no_bounds_check)

+ 3 - 0
src/parser.hpp

@@ -165,7 +165,9 @@ enum ProcInlining {
 enum ProcTag {
 	ProcTag_bounds_check    = 1<<0,
 	ProcTag_no_bounds_check = 1<<1,
+	
 	ProcTag_require_results = 1<<4,
+	ProcTag_optional_ok     = 1<<5,
 };
 
 enum ProcCallingConvention {
@@ -282,6 +284,7 @@ AST_KIND(_ExprBegin,  "",  bool) \
 		Token        close; \
 		Token        ellipsis; \
 		ProcInlining inlining; \
+		bool         optional_ok_one; \
 	}) \
 	AST_KIND(FieldValue,      "field value",              struct { Token eq; Ast *field, *value; }) \
 	AST_KIND(TernaryExpr,     "ternary expression",       struct { Ast *cond, *x, *y; }) \

+ 6 - 3
src/types.cpp

@@ -232,6 +232,7 @@ struct TypeUnion {
 		Array<Type *> abi_compat_params;                  \
 		Type *   abi_compat_result_type;                  \
 		i32      variadic_index;                          \
+		/* TODO(bill): Make this a flag set rather than bools */ \
 		bool     variadic;                                \
 		bool     abi_types_set;                           \
 		bool     require_results;                         \
@@ -242,6 +243,7 @@ struct TypeUnion {
 		bool     has_named_results;                       \
 		bool     diverging; /* no return */               \
 		bool     return_by_pointer;                       \
+		bool     optional_ok;                             \
 		u64      tags;                                    \
 		isize    specialization_count;                    \
 		ProcCallingConvention calling_convention;         \
@@ -1979,9 +1981,10 @@ bool are_types_identical(Type *x, Type *y) {
 	case Type_Proc:
 		if (y->kind == Type_Proc) {
 			return x->Proc.calling_convention == y->Proc.calling_convention &&
-			       x->Proc.c_vararg  == y->Proc.c_vararg  &&
-			       x->Proc.variadic  == y->Proc.variadic  &&
-			       x->Proc.diverging == y->Proc.diverging &&
+			       x->Proc.c_vararg    == y->Proc.c_vararg    &&
+			       x->Proc.variadic    == y->Proc.variadic    &&
+			       x->Proc.diverging   == y->Proc.diverging   &&
+			       x->Proc.optional_ok == y->Proc.optional_ok &&
 			       are_types_identical(x->Proc.params, y->Proc.params) &&
 			       are_types_identical(x->Proc.results, y->Proc.results);
 		}