mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 07:08:48 +00:00
Generic procedures generate types on use
This commit is contained in:
@@ -2,7 +2,15 @@ import (
|
|||||||
"fmt.odin";
|
"fmt.odin";
|
||||||
)
|
)
|
||||||
|
|
||||||
|
proc new_type(T: type) -> ^T {
|
||||||
|
return ^T(alloc_align(size_of(T), align_of(T)));
|
||||||
|
}
|
||||||
|
|
||||||
proc main() {
|
proc main() {
|
||||||
|
var ptr = new_type(int);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
let program = "+ + * - /";
|
let program = "+ + * - /";
|
||||||
var accumulator = 0;
|
var accumulator = 0;
|
||||||
|
|
||||||
@@ -18,4 +26,5 @@ proc main() {
|
|||||||
|
|
||||||
fmt.printf("The program \"%s\" calculates the value %d\n",
|
fmt.printf("The program \"%s\" calculates the value %d\n",
|
||||||
program, accumulator);
|
program, accumulator);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
+168
-137
@@ -1030,7 +1030,7 @@ void check_bit_field_type(Checker *c, Type *bit_field_type, Type *named_type, As
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_variadic_) {
|
Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_variadic_, Array<Operand> *operands) {
|
||||||
if (_params == NULL) {
|
if (_params == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -1041,6 +1041,11 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (operands != NULL) {
|
||||||
|
GB_ASSERT(operands->count == params.count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
isize variable_count = 0;
|
isize variable_count = 0;
|
||||||
for_array(i, params) {
|
for_array(i, params) {
|
||||||
AstNode *field = params[i];
|
AstNode *field = params[i];
|
||||||
@@ -1096,32 +1101,45 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (type_expr->kind == AstNode_HelperType) {
|
if (type_expr->kind == AstNode_HelperType) {
|
||||||
type = make_type_generic(c->allocator, 0);
|
if (operands != NULL) {
|
||||||
|
Operand o = (*operands)[i];
|
||||||
|
if (o.mode == Addressing_Type) {
|
||||||
|
type = o.type;
|
||||||
|
} else {
|
||||||
|
error(o.expr, "Expected a type to assign to a type parameter");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
type = make_type_generic(c->allocator, 0);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
type = check_type(c, type_expr);
|
type = check_type(c, type_expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (default_value != NULL) {
|
if (default_value != NULL) {
|
||||||
Operand o = {};
|
if (type_expr->kind == AstNode_HelperType) {
|
||||||
if (default_value->kind == AstNode_BasicDirective &&
|
error(default_value, "A type parameter may not have a default value");
|
||||||
default_value->BasicDirective.name == "caller_location") {
|
|
||||||
init_preload(c);
|
|
||||||
default_is_location = true;
|
|
||||||
o.type = t_source_code_location;
|
|
||||||
o.mode = Addressing_Value;
|
|
||||||
} else {
|
} else {
|
||||||
check_expr_with_type_hint(c, &o, default_value, type);
|
Operand o = {};
|
||||||
|
if (default_value->kind == AstNode_BasicDirective &&
|
||||||
if (is_operand_nil(o)) {
|
default_value->BasicDirective.name == "caller_location") {
|
||||||
default_is_nil = true;
|
init_preload(c);
|
||||||
} else if (o.mode != Addressing_Constant) {
|
default_is_location = true;
|
||||||
error(default_value, "Default parameter must be a constant");
|
o.type = t_source_code_location;
|
||||||
|
o.mode = Addressing_Value;
|
||||||
} else {
|
} else {
|
||||||
value = o.value;
|
check_expr_with_type_hint(c, &o, default_value, type);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
check_is_assignable_to(c, &o, type);
|
if (is_operand_nil(o)) {
|
||||||
|
default_is_nil = true;
|
||||||
|
} else if (o.mode != Addressing_Constant) {
|
||||||
|
error(default_value, "Default parameter must be a constant");
|
||||||
|
} else {
|
||||||
|
value = o.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check_is_assignable_to(c, &o, type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1154,7 +1172,13 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
|
|||||||
AstNode *name = p->names[j];
|
AstNode *name = p->names[j];
|
||||||
if (ast_node_expect(name, AstNode_Ident)) {
|
if (ast_node_expect(name, AstNode_Ident)) {
|
||||||
Entity *param = NULL;
|
Entity *param = NULL;
|
||||||
if (type->kind == Type_Generic) {
|
bool is_generic = type->kind == Type_Generic;
|
||||||
|
if (operands != NULL) {
|
||||||
|
Operand o = (*operands)[j];
|
||||||
|
is_generic = o.mode == Addressing_Type && o.type == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_generic) {
|
||||||
param = make_entity_type_name(c->allocator, scope, name->Ident, type);
|
param = make_entity_type_name(c->allocator, scope, name->Ident, type);
|
||||||
} else {
|
} else {
|
||||||
param = make_entity_param(c->allocator, scope, name->Ident, type,
|
param = make_entity_param(c->allocator, scope, name->Ident, type,
|
||||||
@@ -1497,11 +1521,12 @@ bool abi_compat_return_by_value(gbAllocator a, ProcCallingConvention cc, Type *a
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
|
// NOTE(bill): `operands` is for generating non generic procedure type
|
||||||
|
void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array<Operand> *operands = NULL) {
|
||||||
ast_node(pt, ProcType, proc_type_node);
|
ast_node(pt, ProcType, proc_type_node);
|
||||||
|
|
||||||
bool variadic = false;
|
bool variadic = false;
|
||||||
Type *params = check_get_params(c, c->context.scope, pt->params, &variadic);
|
Type *params = check_get_params(c, c->context.scope, pt->params, &variadic, operands);
|
||||||
Type *results = check_get_results(c, c->context.scope, pt->results);
|
Type *results = check_get_results(c, c->context.scope, pt->results);
|
||||||
|
|
||||||
isize param_count = 0;
|
isize param_count = 0;
|
||||||
@@ -1509,6 +1534,7 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
|
|||||||
if (params) param_count = params ->Tuple.variable_count;
|
if (params) param_count = params ->Tuple.variable_count;
|
||||||
if (results) result_count = results->Tuple.variable_count;
|
if (results) result_count = results->Tuple.variable_count;
|
||||||
|
|
||||||
|
type->Proc.node = proc_type_node;
|
||||||
type->Proc.scope = c->context.scope;
|
type->Proc.scope = c->context.scope;
|
||||||
type->Proc.params = params;
|
type->Proc.params = params;
|
||||||
type->Proc.param_count = param_count;
|
type->Proc.param_count = param_count;
|
||||||
@@ -1541,14 +1567,19 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
|
|||||||
is_generic = true;
|
is_generic = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GB_ASSERT(type->Proc.is_generic == is_generic);
|
if (operands == NULL) {
|
||||||
|
GB_ASSERT(type->Proc.is_generic == is_generic);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
type->Proc.abi_compat_params = gb_alloc_array(c->allocator, Type *, param_count);
|
type->Proc.abi_compat_params = gb_alloc_array(c->allocator, Type *, param_count);
|
||||||
for (isize i = 0; i < param_count; i++) {
|
for (isize i = 0; i < param_count; i++) {
|
||||||
Type *original_type = type->Proc.params->Tuple.variables[i]->type;
|
Entity *e = type->Proc.params->Tuple.variables[i];
|
||||||
Type *new_type = type_to_abi_compat_param_type(c->allocator, original_type);
|
if (e->kind == Entity_Variable) {
|
||||||
type->Proc.abi_compat_params[i] = new_type;
|
Type *original_type = e->type;
|
||||||
|
Type *new_type = type_to_abi_compat_param_type(c->allocator, original_type);
|
||||||
|
type->Proc.abi_compat_params[i] = new_type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE(bill): The types are the same
|
// NOTE(bill): The types are the same
|
||||||
@@ -4829,6 +4860,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
|
|||||||
|
|
||||||
enum CallArgumentError {
|
enum CallArgumentError {
|
||||||
CallArgumentError_None,
|
CallArgumentError_None,
|
||||||
|
CallArgumentError_NoneProcedureType,
|
||||||
CallArgumentError_WrongTypes,
|
CallArgumentError_WrongTypes,
|
||||||
CallArgumentError_NonVariadicExpand,
|
CallArgumentError_NonVariadicExpand,
|
||||||
CallArgumentError_VariadicTuple,
|
CallArgumentError_VariadicTuple,
|
||||||
@@ -4902,23 +4934,28 @@ bool check_unpack_arguments(Checker *c, isize lhs_count, Array<Operand> *operand
|
|||||||
return optional_ok;
|
return optional_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(Checker *c, AstNode *call, Type *proc_type, Array<Operand> operands, CallArgumentErrorMode show_error_mode, i64 *score_)
|
#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(Checker *c, AstNode *call, Type *proc_type, Array<Operand> operands, CallArgumentErrorMode show_error_mode, i64 *score_, Type **result_type_)
|
||||||
typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType);
|
typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType);
|
||||||
|
|
||||||
|
|
||||||
CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
||||||
ast_node(ce, CallExpr, call);
|
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 = 0;
|
||||||
isize param_count_excluding_defaults = 0;
|
isize param_count_excluding_defaults = 0;
|
||||||
bool variadic = proc_type->Proc.variadic;
|
bool variadic = pt->variadic;
|
||||||
bool vari_expand = (ce->ellipsis.pos.line != 0);
|
bool vari_expand = (ce->ellipsis.pos.line != 0);
|
||||||
i64 score = 0;
|
i64 score = 0;
|
||||||
bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
|
bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
|
||||||
|
|
||||||
|
|
||||||
TypeTuple *param_tuple = NULL;
|
TypeTuple *param_tuple = NULL;
|
||||||
|
|
||||||
if (proc_type->Proc.params != NULL) {
|
if (pt->params != NULL) {
|
||||||
param_tuple = &proc_type->Proc.params->Tuple;
|
param_tuple = &pt->params->Tuple;
|
||||||
|
|
||||||
param_count = param_tuple->variable_count;
|
param_count = param_tuple->variable_count;
|
||||||
if (variadic) {
|
if (variadic) {
|
||||||
@@ -4945,120 +4982,126 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CallArgumentError err = CallArgumentError_None;
|
||||||
|
Type *final_proc_type = proc_type;
|
||||||
|
|
||||||
if (vari_expand && !variadic) {
|
if (vari_expand && !variadic) {
|
||||||
if (show_error) {
|
if (show_error) {
|
||||||
error(ce->ellipsis,
|
error(ce->ellipsis,
|
||||||
"Cannot use `..` in call to a non-variadic procedure: `%.*s`",
|
"Cannot use `..` in call to a non-variadic procedure: `%.*s`",
|
||||||
LIT(ce->proc->Ident.string));
|
LIT(ce->proc->Ident.string));
|
||||||
}
|
}
|
||||||
if (score_) *score_ = score;
|
err = CallArgumentError_NonVariadicExpand;
|
||||||
return CallArgumentError_NonVariadicExpand;
|
} else if (vari_expand && pt->c_vararg) {
|
||||||
}
|
|
||||||
if (vari_expand && proc_type->Proc.c_vararg) {
|
|
||||||
if (show_error) {
|
if (show_error) {
|
||||||
error(ce->ellipsis,
|
error(ce->ellipsis,
|
||||||
"Cannot use `..` in call to a `#c_vararg` variadic procedure: `%.*s`",
|
"Cannot use `..` in call to a `#c_vararg` variadic procedure: `%.*s`",
|
||||||
LIT(ce->proc->Ident.string));
|
LIT(ce->proc->Ident.string));
|
||||||
}
|
}
|
||||||
if (score_) *score_ = score;
|
err = CallArgumentError_NonVariadicExpand;
|
||||||
return CallArgumentError_NonVariadicExpand;
|
} else if (operands.count == 0 && param_count_excluding_defaults == 0) {
|
||||||
}
|
err = CallArgumentError_None;
|
||||||
|
} else {
|
||||||
if (operands.count == 0 && param_count_excluding_defaults == 0) {
|
i32 error_code = 0;
|
||||||
if (score_) *score_ = score;
|
if (operands.count < param_count_excluding_defaults) {
|
||||||
return CallArgumentError_None;
|
error_code = -1;
|
||||||
}
|
} else if (!variadic && operands.count > param_count) {
|
||||||
|
error_code = +1;
|
||||||
i32 error_code = 0;
|
|
||||||
if (operands.count < param_count_excluding_defaults) {
|
|
||||||
error_code = -1;
|
|
||||||
} else if (!variadic && operands.count > param_count) {
|
|
||||||
error_code = +1;
|
|
||||||
}
|
|
||||||
if (error_code != 0) {
|
|
||||||
CallArgumentError err = CallArgumentError_TooManyArguments;
|
|
||||||
char *err_fmt = "Too many arguments for `%s`, expected %td arguments";
|
|
||||||
if (error_code < 0) {
|
|
||||||
err = CallArgumentError_TooFewArguments;
|
|
||||||
err_fmt = "Too few arguments for `%s`, expected %td arguments";
|
|
||||||
}
|
}
|
||||||
|
if (error_code != 0) {
|
||||||
if (show_error) {
|
err = CallArgumentError_TooManyArguments;
|
||||||
gbString proc_str = expr_to_string(ce->proc);
|
char *err_fmt = "Too many arguments for `%s`, expected %td arguments";
|
||||||
error(call, err_fmt, proc_str, param_count_excluding_defaults);
|
if (error_code < 0) {
|
||||||
gb_string_free(proc_str);
|
err = CallArgumentError_TooFewArguments;
|
||||||
}
|
err_fmt = "Too few arguments for `%s`, expected %td arguments";
|
||||||
if (score_) *score_ = score;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
CallArgumentError err = CallArgumentError_None;
|
|
||||||
|
|
||||||
|
|
||||||
GB_ASSERT(proc_type->Proc.params != NULL);
|
|
||||||
Entity **sig_params = param_tuple->variables;
|
|
||||||
isize operand_index = 0;
|
|
||||||
isize max_operand_count = gb_min(param_count, operands.count);
|
|
||||||
for (; operand_index < max_operand_count; operand_index++) {
|
|
||||||
Entity *e = sig_params[operand_index];
|
|
||||||
Type *t = e->type;
|
|
||||||
Operand o = operands[operand_index];
|
|
||||||
if (e->kind == Entity_TypeName) {
|
|
||||||
GB_ASSERT(proc_type->Proc.is_generic);
|
|
||||||
GB_ASSERT(!variadic);
|
|
||||||
if (o.mode == Addressing_Invalid) {
|
|
||||||
continue;
|
|
||||||
} else if (o.mode != Addressing_Type) {
|
|
||||||
error(o.expr, "Expected a type for the argument");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
score += assign_score_function(1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (variadic) {
|
|
||||||
o = operands[operand_index];
|
|
||||||
}
|
|
||||||
i64 s = 0;
|
|
||||||
if (!check_is_assignable_to_with_score(c, &o, t, &s)) {
|
|
||||||
if (show_error) {
|
if (show_error) {
|
||||||
check_assignment(c, &o, t, str_lit("argument"));
|
gbString proc_str = expr_to_string(ce->proc);
|
||||||
|
error(call, err_fmt, proc_str, param_count_excluding_defaults);
|
||||||
|
gb_string_free(proc_str);
|
||||||
}
|
}
|
||||||
err = CallArgumentError_WrongTypes;
|
} else {
|
||||||
}
|
if (pt->is_generic) {
|
||||||
score += s;
|
Scope *scope = make_scope(pt->scope->parent, c->allocator);
|
||||||
}
|
CheckerContext prev = c->context;
|
||||||
|
defer (c->context = prev);
|
||||||
|
c->context.scope = scope;
|
||||||
|
|
||||||
if (variadic) {
|
final_proc_type = alloc_type(c->allocator, Type_Proc);
|
||||||
bool variadic_expand = false;
|
check_procedure_type(c, final_proc_type, pt->node, &operands);
|
||||||
Type *slice = sig_params[param_count]->type;
|
}
|
||||||
GB_ASSERT(is_type_slice(slice));
|
|
||||||
Type *elem = base_type(slice)->Slice.elem;
|
|
||||||
Type *t = elem;
|
TypeProc *pt = &final_proc_type->Proc;
|
||||||
for (; operand_index < operands.count; operand_index++) {
|
|
||||||
Operand o = operands[operand_index];
|
GB_ASSERT(pt->params != NULL);
|
||||||
if (vari_expand) {
|
Entity **sig_params = pt->params->Tuple.variables;
|
||||||
variadic_expand = true;
|
isize operand_index = 0;
|
||||||
t = slice;
|
isize max_operand_count = gb_min(param_count, operands.count);
|
||||||
if (operand_index != param_count) {
|
for (; operand_index < max_operand_count; operand_index++) {
|
||||||
if (show_error) {
|
Entity *e = sig_params[operand_index];
|
||||||
error(o.expr, "`..` in a variadic procedure can only have one variadic argument at the end");
|
Type *t = e->type;
|
||||||
|
Operand o = operands[operand_index];
|
||||||
|
if (e->kind == Entity_TypeName) {
|
||||||
|
// GB_ASSERT(!variadic);
|
||||||
|
if (o.mode == Addressing_Invalid) {
|
||||||
|
continue;
|
||||||
|
} else if (o.mode != Addressing_Type) {
|
||||||
|
error(o.expr, "Expected a type for the argument");
|
||||||
}
|
}
|
||||||
if (score_) *score_ = score;
|
|
||||||
return CallArgumentError_MultipleVariadicExpand;
|
score += assign_score_function(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (variadic) {
|
||||||
|
o = operands[operand_index];
|
||||||
|
}
|
||||||
|
i64 s = 0;
|
||||||
|
if (!check_is_assignable_to_with_score(c, &o, t, &s)) {
|
||||||
|
if (show_error) {
|
||||||
|
check_assignment(c, &o, t, str_lit("argument"));
|
||||||
|
}
|
||||||
|
err = CallArgumentError_WrongTypes;
|
||||||
|
}
|
||||||
|
score += s;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variadic) {
|
||||||
|
bool variadic_expand = false;
|
||||||
|
Type *slice = sig_params[param_count]->type;
|
||||||
|
GB_ASSERT(is_type_slice(slice));
|
||||||
|
Type *elem = base_type(slice)->Slice.elem;
|
||||||
|
Type *t = elem;
|
||||||
|
for (; operand_index < operands.count; operand_index++) {
|
||||||
|
Operand o = operands[operand_index];
|
||||||
|
if (vari_expand) {
|
||||||
|
variadic_expand = true;
|
||||||
|
t = slice;
|
||||||
|
if (operand_index != param_count) {
|
||||||
|
if (show_error) {
|
||||||
|
error(o.expr, "`..` in a variadic procedure can only have one variadic argument at the end");
|
||||||
|
}
|
||||||
|
if (score_) *score_ = score;
|
||||||
|
return CallArgumentError_MultipleVariadicExpand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i64 s = 0;
|
||||||
|
if (!check_is_assignable_to_with_score(c, &o, t, &s)) {
|
||||||
|
if (show_error) {
|
||||||
|
check_assignment(c, &o, t, str_lit("argument"));
|
||||||
|
}
|
||||||
|
err = CallArgumentError_WrongTypes;
|
||||||
|
}
|
||||||
|
score += s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i64 s = 0;
|
|
||||||
if (!check_is_assignable_to_with_score(c, &o, t, &s)) {
|
|
||||||
if (show_error) {
|
|
||||||
check_assignment(c, &o, t, str_lit("argument"));
|
|
||||||
}
|
|
||||||
err = CallArgumentError_WrongTypes;
|
|
||||||
}
|
|
||||||
score += s;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (score_) *score_ = score;
|
if (score_) *score_ = score;
|
||||||
|
if (result_type_) *result_type_ = final_proc_type->Proc.results;
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5208,6 +5251,7 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (score_) *score_ = score;
|
if (score_) *score_ = score;
|
||||||
|
if (result_type_) *result_type_ = pt->results;
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -5267,7 +5311,7 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod
|
|||||||
Type *pt = base_type(p->type);
|
Type *pt = base_type(p->type);
|
||||||
if (pt != NULL && is_type_proc(pt)) {
|
if (pt != NULL && is_type_proc(pt)) {
|
||||||
i64 score = 0;
|
i64 score = 0;
|
||||||
CallArgumentError err = call_checker(c, call, pt, operands, CallArgumentMode_NoErrors, &score);
|
CallArgumentError err = call_checker(c, call, pt, operands, CallArgumentMode_NoErrors, &score, &result_type);
|
||||||
if (err == CallArgumentError_None) {
|
if (err == CallArgumentError_None) {
|
||||||
valids[valid_count].index = i;
|
valids[valid_count].index = i;
|
||||||
valids[valid_count].score = score;
|
valids[valid_count].score = score;
|
||||||
@@ -5312,18 +5356,11 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod
|
|||||||
add_entity_use(c, expr, e);
|
add_entity_use(c, expr, e);
|
||||||
proc_type = e->type;
|
proc_type = e->type;
|
||||||
i64 score = 0;
|
i64 score = 0;
|
||||||
CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score);
|
CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score, &result_type);
|
||||||
|
|
||||||
if (proc_type != NULL && is_type_proc(proc_type)) {
|
|
||||||
result_type = base_type(proc_type)->Proc.results;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
i64 score = 0;
|
i64 score = 0;
|
||||||
CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score);
|
CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score, &result_type);
|
||||||
if (proc_type != NULL && is_type_proc(proc_type)) {
|
|
||||||
result_type = base_type(proc_type)->Proc.results;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result_type;
|
return result_type;
|
||||||
@@ -5473,13 +5510,7 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Type *pt = base_type(proc_type);
|
Type *pt = base_type(proc_type);
|
||||||
bool results_are_generic = false;
|
if (result_type == NULL) {
|
||||||
if (is_type_proc(pt) && pt->Proc.results != NULL) {
|
|
||||||
results_are_generic = is_type_generic(pt->Proc.results);
|
|
||||||
}
|
|
||||||
if (results_are_generic) {
|
|
||||||
operand->mode = Addressing_NoValue;
|
|
||||||
} else if (result_type == NULL) {
|
|
||||||
operand->mode = Addressing_NoValue;
|
operand->mode = Addressing_NoValue;
|
||||||
} else {
|
} else {
|
||||||
GB_ASSERT(is_type_tuple(result_type));
|
GB_ASSERT(is_type_tuple(result_type));
|
||||||
|
|||||||
+3
-3
@@ -221,6 +221,7 @@ ExprInfo make_expr_info(bool is_lhs, AddressingMode mode, Type *type, ExactValue
|
|||||||
|
|
||||||
|
|
||||||
struct Scope {
|
struct Scope {
|
||||||
|
AstNode * node;
|
||||||
Scope * parent;
|
Scope * parent;
|
||||||
Scope * prev, *next;
|
Scope * prev, *next;
|
||||||
Scope * first_child;
|
Scope * first_child;
|
||||||
@@ -241,8 +242,6 @@ gb_global Scope *universal_scope = NULL;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct DelayedDecl {
|
struct DelayedDecl {
|
||||||
Scope * parent;
|
Scope * parent;
|
||||||
AstNode *decl;
|
AstNode *decl;
|
||||||
@@ -278,6 +277,7 @@ struct CheckerInfo {
|
|||||||
Map<DeclInfo *> entities; // Key: Entity *
|
Map<DeclInfo *> entities; // Key: Entity *
|
||||||
Map<Entity *> foreigns; // Key: String
|
Map<Entity *> foreigns; // Key: String
|
||||||
Map<AstFile *> files; // Key: String (full path)
|
Map<AstFile *> files; // Key: String (full path)
|
||||||
|
|
||||||
Map<isize> type_info_map; // Key: Type *
|
Map<isize> type_info_map; // Key: Type *
|
||||||
isize type_info_count;
|
isize type_info_count;
|
||||||
};
|
};
|
||||||
@@ -423,12 +423,12 @@ void destroy_scope(Scope *scope) {
|
|||||||
void add_scope(Checker *c, AstNode *node, Scope *scope) {
|
void add_scope(Checker *c, AstNode *node, Scope *scope) {
|
||||||
GB_ASSERT(node != NULL);
|
GB_ASSERT(node != NULL);
|
||||||
GB_ASSERT(scope != NULL);
|
GB_ASSERT(scope != NULL);
|
||||||
|
scope->node = node;
|
||||||
map_set(&c->info.scopes, hash_node(node), scope);
|
map_set(&c->info.scopes, hash_node(node), scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void check_open_scope(Checker *c, AstNode *node) {
|
void check_open_scope(Checker *c, AstNode *node) {
|
||||||
GB_ASSERT(node != NULL);
|
|
||||||
node = unparen_expr(node);
|
node = unparen_expr(node);
|
||||||
GB_ASSERT(node->kind == AstNode_Invalid ||
|
GB_ASSERT(node->kind == AstNode_Invalid ||
|
||||||
is_ast_node_stmt(node) ||
|
is_ast_node_stmt(node) ||
|
||||||
|
|||||||
+27
-7
@@ -189,18 +189,26 @@ void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) {
|
|||||||
ir_fprintf(f, ", ");
|
ir_fprintf(f, ", ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isize param_index = 0;
|
||||||
for (isize i = 0; i < param_count; i++) {
|
for (isize i = 0; i < param_count; i++) {
|
||||||
if (i > 0) {
|
if (param_index > 0) {
|
||||||
ir_fprintf(f, ", ");
|
ir_fprintf(f, ", ");
|
||||||
}
|
}
|
||||||
|
Entity *e = t->Proc.params->Tuple.variables[i];
|
||||||
|
if (e->kind != Entity_Variable) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (i+1 == param_count && t->Proc.c_vararg) {
|
if (i+1 == param_count && t->Proc.c_vararg) {
|
||||||
ir_fprintf(f, "...");
|
ir_fprintf(f, "...");
|
||||||
} else {
|
} else {
|
||||||
ir_print_type(f, m, t->Proc.abi_compat_params[i]);
|
ir_print_type(f, m, t->Proc.abi_compat_params[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
param_index++;
|
||||||
}
|
}
|
||||||
if (t->Proc.calling_convention == ProcCC_Odin) {
|
if (t->Proc.calling_convention == ProcCC_Odin) {
|
||||||
if (param_count > 0) {
|
if (param_index > 0) {
|
||||||
ir_fprintf(f, ", ");
|
ir_fprintf(f, ", ");
|
||||||
}
|
}
|
||||||
ir_print_type(f, m, t_context_ptr);
|
ir_print_type(f, m, t_context_ptr);
|
||||||
@@ -354,11 +362,16 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
|
|||||||
ir_print_type(f, m, t->Tuple.variables[0]->type);
|
ir_print_type(f, m, t->Tuple.variables[0]->type);
|
||||||
} else {
|
} else {
|
||||||
ir_fprintf(f, "{");
|
ir_fprintf(f, "{");
|
||||||
|
isize index = 0;
|
||||||
for (isize i = 0; i < t->Tuple.variable_count; i++) {
|
for (isize i = 0; i < t->Tuple.variable_count; i++) {
|
||||||
if (i > 0) {
|
if (index > 0) {
|
||||||
ir_fprintf(f, ", ");
|
ir_fprintf(f, ", ");
|
||||||
}
|
}
|
||||||
ir_print_type(f, m, t->Tuple.variables[i]->type);
|
Entity *e = t->Tuple.variables[i];
|
||||||
|
if (e->kind == Entity_Variable) {
|
||||||
|
ir_print_type(f, m, e->type);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ir_fprintf(f, "}");
|
ir_fprintf(f, "}");
|
||||||
}
|
}
|
||||||
@@ -1551,17 +1564,22 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isize param_index = 0;
|
||||||
if (param_count > 0) {
|
if (param_count > 0) {
|
||||||
TypeTuple *params = &proc_type->params->Tuple;
|
TypeTuple *params = &proc_type->params->Tuple;
|
||||||
for (isize i = 0; i < param_count; i++) {
|
for (isize i = 0; i < param_count; i++) {
|
||||||
Entity *e = params->variables[i];
|
Entity *e = params->variables[i];
|
||||||
Type *original_type = e->type;
|
Type *original_type = e->type;
|
||||||
Type *abi_type = proc_type->abi_compat_params[i];
|
Type *abi_type = proc_type->abi_compat_params[i];
|
||||||
if (i > 0) {
|
if (param_index > 0) {
|
||||||
ir_fprintf(f, ", ");
|
ir_fprintf(f, ", ");
|
||||||
}
|
}
|
||||||
|
if (e->kind != Entity_Variable) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (i+1 == params->variable_count && proc_type->c_vararg) {
|
if (i+1 == params->variable_count && proc_type->c_vararg) {
|
||||||
ir_fprintf(f, " ...");
|
ir_fprintf(f, " ...");
|
||||||
} else {
|
} else {
|
||||||
ir_print_type(f, m, abi_type);
|
ir_print_type(f, m, abi_type);
|
||||||
if (e->flags&EntityFlag_NoAlias) {
|
if (e->flags&EntityFlag_NoAlias) {
|
||||||
@@ -1577,10 +1595,12 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
param_index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (proc_type->calling_convention == ProcCC_Odin) {
|
if (proc_type->calling_convention == ProcCC_Odin) {
|
||||||
if (param_count > 0) {
|
if (param_index > 0) {
|
||||||
ir_fprintf(f, ", ");
|
ir_fprintf(f, ", ");
|
||||||
}
|
}
|
||||||
ir_print_type(f, m, t_context_ptr);
|
ir_print_type(f, m, t_context_ptr);
|
||||||
|
|||||||
+11
-2
@@ -34,6 +34,7 @@ struct AstFile {
|
|||||||
// NOTE(bill): Used to prevent type literals in control clauses
|
// NOTE(bill): Used to prevent type literals in control clauses
|
||||||
isize expr_level;
|
isize expr_level;
|
||||||
bool allow_range; // NOTE(bill): Ranges are only allowed in certain cases
|
bool allow_range; // NOTE(bill): Ranges are only allowed in certain cases
|
||||||
|
bool allow_gen_proc_type;
|
||||||
bool in_foreign_block;
|
bool in_foreign_block;
|
||||||
|
|
||||||
Array<AstNode *> decls;
|
Array<AstNode *> decls;
|
||||||
@@ -2729,9 +2730,13 @@ AstNode *parse_proc_decl(AstFile *f) {
|
|||||||
String link_name = {};
|
String link_name = {};
|
||||||
|
|
||||||
|
|
||||||
|
bool prev_allow_gen_proc_type = f->allow_gen_proc_type;
|
||||||
|
f->allow_gen_proc_type = true;
|
||||||
AstNode *name = parse_ident(f);
|
AstNode *name = parse_ident(f);
|
||||||
AstNode *type = parse_proc_type(f, token, &link_name);
|
AstNode *type = parse_proc_type(f, token, &link_name);
|
||||||
u64 tags = type->ProcType.tags;
|
u64 tags = type->ProcType.tags;
|
||||||
|
f->allow_gen_proc_type = prev_allow_gen_proc_type;
|
||||||
|
|
||||||
|
|
||||||
if (f->curr_token.kind == Token_OpenBrace) {
|
if (f->curr_token.kind == Token_OpenBrace) {
|
||||||
if ((tags & ProcTag_foreign) != 0) {
|
if ((tags & ProcTag_foreign) != 0) {
|
||||||
@@ -3404,12 +3409,13 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
|
|||||||
|
|
||||||
isize total_name_count = 0;
|
isize total_name_count = 0;
|
||||||
bool allow_ellipsis = allowed_flags&FieldFlag_ellipsis;
|
bool allow_ellipsis = allowed_flags&FieldFlag_ellipsis;
|
||||||
|
bool allow_type_token = f->allow_gen_proc_type && allow_default_parameters;
|
||||||
|
|
||||||
while (f->curr_token.kind != follow &&
|
while (f->curr_token.kind != follow &&
|
||||||
f->curr_token.kind != Token_Colon &&
|
f->curr_token.kind != Token_Colon &&
|
||||||
f->curr_token.kind != Token_EOF) {
|
f->curr_token.kind != Token_EOF) {
|
||||||
u32 flags = parse_field_prefixes(f);
|
u32 flags = parse_field_prefixes(f);
|
||||||
AstNode *param = parse_var_type(f, allow_ellipsis, allow_default_parameters);
|
AstNode *param = parse_var_type(f, allow_ellipsis, allow_type_token);
|
||||||
AstNodeAndFlags naf = {param, flags};
|
AstNodeAndFlags naf = {param, flags};
|
||||||
array_add(&list, naf);
|
array_add(&list, naf);
|
||||||
if (f->curr_token.kind != Token_Comma) {
|
if (f->curr_token.kind != Token_Comma) {
|
||||||
@@ -3436,7 +3442,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
|
|||||||
|
|
||||||
if (f->curr_token.kind != Token_Eq) {
|
if (f->curr_token.kind != Token_Eq) {
|
||||||
expect_token_after(f, Token_Colon, "field list");
|
expect_token_after(f, Token_Colon, "field list");
|
||||||
type = parse_var_type(f, allow_ellipsis, allow_default_parameters);
|
type = parse_var_type(f, allow_ellipsis, allow_type_token);
|
||||||
}
|
}
|
||||||
if (allow_token(f, Token_Eq)) {
|
if (allow_token(f, Token_Eq)) {
|
||||||
// TODO(bill): Should this be true==lhs or false==rhs?
|
// TODO(bill): Should this be true==lhs or false==rhs?
|
||||||
@@ -3788,7 +3794,10 @@ AstNode *parse_type_or_ident(AstFile *f) {
|
|||||||
|
|
||||||
case Token_proc: {
|
case Token_proc: {
|
||||||
Token token = f->curr_token; next_token(f);
|
Token token = f->curr_token; next_token(f);
|
||||||
|
bool prev_allow_gen_proc_type = f->allow_gen_proc_type;
|
||||||
|
f->allow_gen_proc_type = false;
|
||||||
AstNode *pt = parse_proc_type(f, token, NULL);
|
AstNode *pt = parse_proc_type(f, token, NULL);
|
||||||
|
f->allow_gen_proc_type = prev_allow_gen_proc_type;
|
||||||
if (pt->ProcType.tags != 0) {
|
if (pt->ProcType.tags != 0) {
|
||||||
syntax_error(token, "A procedure type cannot have tags");
|
syntax_error(token, "A procedure type cannot have tags");
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-22
@@ -1,4 +1,5 @@
|
|||||||
struct Scope;
|
struct Scope;
|
||||||
|
struct AstNode;
|
||||||
|
|
||||||
enum BasicKind {
|
enum BasicKind {
|
||||||
Basic_Invalid,
|
Basic_Invalid,
|
||||||
@@ -135,18 +136,19 @@ struct TypeRecord {
|
|||||||
i64 * offsets; \
|
i64 * offsets; \
|
||||||
}) \
|
}) \
|
||||||
TYPE_KIND(Proc, struct { \
|
TYPE_KIND(Proc, struct { \
|
||||||
Scope *scope; \
|
AstNode *node; \
|
||||||
Type * params; /* Type_Tuple */ \
|
Scope * scope; \
|
||||||
Type * results; /* Type_Tuple */ \
|
Type * params; /* Type_Tuple */ \
|
||||||
i32 param_count; \
|
Type * results; /* Type_Tuple */ \
|
||||||
i32 result_count; \
|
i32 param_count; \
|
||||||
bool return_by_pointer; \
|
i32 result_count; \
|
||||||
Type **abi_compat_params; \
|
bool return_by_pointer; \
|
||||||
Type * abi_compat_result_type; \
|
Type ** abi_compat_params; \
|
||||||
bool variadic; \
|
Type * abi_compat_result_type; \
|
||||||
bool require_results; \
|
bool variadic; \
|
||||||
bool c_vararg; \
|
bool require_results; \
|
||||||
bool is_generic; \
|
bool c_vararg; \
|
||||||
|
bool is_generic; \
|
||||||
ProcCallingConvention calling_convention; \
|
ProcCallingConvention calling_convention; \
|
||||||
}) \
|
}) \
|
||||||
TYPE_KIND(Map, struct { \
|
TYPE_KIND(Map, struct { \
|
||||||
@@ -1164,7 +1166,7 @@ bool are_types_identical(Type *x, Type *y) {
|
|||||||
for (isize i = 0; i < x->Tuple.variable_count; i++) {
|
for (isize i = 0; i < x->Tuple.variable_count; i++) {
|
||||||
Entity *xe = x->Tuple.variables[i];
|
Entity *xe = x->Tuple.variables[i];
|
||||||
Entity *ye = y->Tuple.variables[i];
|
Entity *ye = y->Tuple.variables[i];
|
||||||
if (!are_types_identical(xe->type, ye->type)) {
|
if (xe->kind != ye->kind || !are_types_identical(xe->type, ye->type)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2383,19 +2385,24 @@ gbString write_type_to_string(gbString str, Type *type) {
|
|||||||
for (isize i = 0; i < type->Tuple.variable_count; i++) {
|
for (isize i = 0; i < type->Tuple.variable_count; i++) {
|
||||||
Entity *var = type->Tuple.variables[i];
|
Entity *var = type->Tuple.variables[i];
|
||||||
if (var != NULL) {
|
if (var != NULL) {
|
||||||
GB_ASSERT(var->kind == Entity_Variable);
|
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
str = gb_string_appendc(str, ", ");
|
str = gb_string_appendc(str, ", ");
|
||||||
}
|
}
|
||||||
if (var->flags&EntityFlag_CVarArg) {
|
if (var->kind == Entity_Variable) {
|
||||||
str = gb_string_appendc(str, "#c_vararg ");
|
if (var->flags&EntityFlag_CVarArg) {
|
||||||
}
|
str = gb_string_appendc(str, "#c_vararg ");
|
||||||
if (var->flags&EntityFlag_Ellipsis) {
|
}
|
||||||
Type *slice = base_type(var->type);
|
if (var->flags&EntityFlag_Ellipsis) {
|
||||||
str = gb_string_appendc(str, "..");
|
Type *slice = base_type(var->type);
|
||||||
GB_ASSERT(is_type_slice(var->type));
|
str = gb_string_appendc(str, "..");
|
||||||
str = write_type_to_string(str, slice->Slice.elem);
|
GB_ASSERT(is_type_slice(var->type));
|
||||||
|
str = write_type_to_string(str, slice->Slice.elem);
|
||||||
|
} else {
|
||||||
|
str = write_type_to_string(str, var->type);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
GB_ASSERT(var->kind == Entity_TypeName);
|
||||||
|
str = gb_string_appendc(str, "type/");
|
||||||
str = write_type_to_string(str, var->type);
|
str = write_type_to_string(str, var->type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user