From 97f7a558faaf206bb7d10eaf3adce99322fd9541 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 19 Apr 2020 21:45:04 +0100 Subject: [PATCH] `#optional_ok` tag for procedures --- core/container/array.odin | 10 +- core/mem/mem.odin | 15 +- src/check_expr.cpp | 52 +++- src/check_type.cpp | 17 ++ src/common.cpp | 2 +- src/ir.cpp | 608 +++++++++++++++++++------------------- src/llvm_backend.cpp | 8 +- src/parser.cpp | 1 + src/parser.hpp | 3 + src/types.cpp | 9 +- 10 files changed, 417 insertions(+), 308 deletions(-) diff --git a/core/container/array.odin b/core/container/array.odin index 273657eb6..123440b2e 100644 --- a/core/container/array.odin +++ b/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 { diff --git a/core/mem/mem.odin b/core/mem/mem.odin index 99a16a6c9..bafac60d5 100644 --- a/core/mem/mem.odin +++ b/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 { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 9879d3b8b..357c6b53a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5750,12 +5750,41 @@ bool check_assignment_arguments(CheckerContext *ctx, Array 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; } diff --git a/src/check_type.cpp b/src/check_type.cpp index 6df824ec1..73f732b45 100644 --- a/src/check_type.cpp +++ b/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) { diff --git a/src/common.cpp b/src/common.cpp index ba9f297a3..af6ceee63 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -468,8 +468,8 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) { struct StringIntern { - isize len; StringIntern *next; + isize len; char str[1]; }; diff --git a/src/ir.cpp b/src/ir.cpp index 6490995e8..78642a917 100644 --- a/src/ir.cpp +++ b/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(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(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; + 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); } - - 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(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(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); + return res; case_end; case_ast_node(se, SliceExpr, expr); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index b03f195b9..cc6eccff6 100644 --- a/src/llvm_backend.cpp +++ b/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); diff --git a/src/parser.cpp b/src/parser.cpp index e0cd60d79..eb5bdf7b6 100644 --- a/src/parser.cpp +++ b/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) diff --git a/src/parser.hpp b/src/parser.hpp index 1b236b921..9ef1a8720 100644 --- a/src/parser.hpp +++ b/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; }) \ diff --git a/src/types.cpp b/src/types.cpp index 530a02df7..1590d0a43 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -232,6 +232,7 @@ struct TypeUnion { Array 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); }