|
|
|
|
@@ -989,6 +989,26 @@ resolve_field_access(Ast_Expr *node, Ast_Scope *context){
|
|
|
|
|
return resolve_field_access(next, ((Ast_Decl *)type->ast)->scope);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Ast_Expr *
|
|
|
|
|
unpack_ast_call_expr_for_builtin(Ast_Call *call){
|
|
|
|
|
if(call->exprs.len != 1) {
|
|
|
|
|
compiler_error(call->pos, "Expected exactly 1 argument inside a builtin function call got instead %d", (int)call->exprs.len);
|
|
|
|
|
}
|
|
|
|
|
return call->exprs[0]->item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function bool
|
|
|
|
|
expr_atom_is_equal_intern(Ast_Expr *expr, Intern_String intern){
|
|
|
|
|
assert(expr->kind == AST_IDENT || expr->kind == AST_BINARY);
|
|
|
|
|
if(expr->kind == AST_IDENT){
|
|
|
|
|
Ast_Atom *atom = (Ast_Atom *)expr;
|
|
|
|
|
if(atom->intern_val == intern) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Operand
|
|
|
|
|
resolve_expr(Ast_Expr *ast, Resolve_Flag flags, Ast_Type *compound_context){
|
|
|
|
|
if(!ast && is_flag_set(flags, AST_CAN_BE_NULL)) return {};
|
|
|
|
|
@@ -1202,127 +1222,136 @@ resolve_expr(Ast_Expr *ast, Resolve_Flag flags, Ast_Type *compound_context){
|
|
|
|
|
BREAK();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CASE(ALIGN_OF, Builtin){
|
|
|
|
|
Operand name = resolve_expr(node->expr, inherit_flag(flags, AST_CANT_BE_NULL));
|
|
|
|
|
if(!name.is_const) compiler_error(node->pos, "align_of requires a constant value");
|
|
|
|
|
Ast_Type *type = name.type == type_type ? name.type_val : name.type;
|
|
|
|
|
Value v = value_int(type->align);
|
|
|
|
|
rewrite_into_const(node, Ast_Builtin, v);
|
|
|
|
|
return operand_const_rvalue(v);
|
|
|
|
|
BREAK();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CASE(SIZE_OF, Builtin){
|
|
|
|
|
Operand name = resolve_expr(node->expr, inherit_flag(flags, AST_CANT_BE_NULL));
|
|
|
|
|
if(!name.is_const) compiler_error(node->pos, "size_of requires a constant value");
|
|
|
|
|
Ast_Type *type = name.type == type_type ? name.type_val : name.type;
|
|
|
|
|
Value v = value_int(type->size);
|
|
|
|
|
rewrite_into_const(node, Ast_Builtin, v);
|
|
|
|
|
return operand_const_rvalue(v);
|
|
|
|
|
BREAK();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CASE(LENGTH_OF, Builtin){
|
|
|
|
|
Operand name = resolve_expr(node->expr, inherit_flag(flags, AST_CANT_BE_NULL));
|
|
|
|
|
node->resolved_type = type_s64;
|
|
|
|
|
if(is_array(name.type)){
|
|
|
|
|
Value value = value_int(name.type->arr.size);
|
|
|
|
|
rewrite_into_const(node, Ast_Builtin, value);
|
|
|
|
|
return operand_const_rvalue(value);
|
|
|
|
|
}
|
|
|
|
|
else if(name.type->kind == TYPE_UNTYPED_STRING){
|
|
|
|
|
Value value = value_int(name.intern_val.len);
|
|
|
|
|
rewrite_into_const(node, Ast_Builtin, value);
|
|
|
|
|
return operand_const_rvalue(value);
|
|
|
|
|
}
|
|
|
|
|
else if(is_array(name.type) || is_slice(name.type) || is_string(name.type)){
|
|
|
|
|
return operand_rvalue(type_s64);
|
|
|
|
|
}
|
|
|
|
|
else compiler_error(node->pos, "Can't get length of type %Q", typestring(name.type));
|
|
|
|
|
BREAK();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CASE(CALL, Call){
|
|
|
|
|
Operand name = resolve_expr(node->name, inherit_flag(flags, AST_CANT_BE_NULL));
|
|
|
|
|
if(name.type->kind != TYPE_LAMBDA)
|
|
|
|
|
compiler_error(node->pos, "Calling %Q which is not a [Lambda]", typestring(name.type));
|
|
|
|
|
if(expr_atom_is_equal_intern(node->name, intern_sizeof)){
|
|
|
|
|
Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node);
|
|
|
|
|
|
|
|
|
|
Scratch scratch;
|
|
|
|
|
Array<Ast_Call_Item *> items = {scratch};
|
|
|
|
|
S64 was_name_indexed = false;
|
|
|
|
|
S64 default_iter = 0;
|
|
|
|
|
|
|
|
|
|
Ast_Lambda *lambda = (Ast_Lambda *)name.type->ast;
|
|
|
|
|
For_Named(lambda->args, lambda_arg){
|
|
|
|
|
assert(lambda_arg->type);
|
|
|
|
|
|
|
|
|
|
Ast_Call_Item *item = 0;
|
|
|
|
|
For(node->exprs){
|
|
|
|
|
assert(!is_flag_set(it->call_flags, CALL_INDEX));
|
|
|
|
|
if(is_flag_set(it->call_flags, CALL_NAME)){
|
|
|
|
|
Ast_Atom *name = it->name;
|
|
|
|
|
assert(name->kind == AST_IDENT);
|
|
|
|
|
was_name_indexed = true;
|
|
|
|
|
if(name->intern_val.str == lambda_arg->name.str)
|
|
|
|
|
item = it;
|
|
|
|
|
}
|
|
|
|
|
else if(node->exprs.get_index(&it) == default_iter){
|
|
|
|
|
default_iter++;
|
|
|
|
|
item = it;
|
|
|
|
|
}
|
|
|
|
|
else if(node->exprs.get_index(&it) > default_iter){
|
|
|
|
|
compiler_error(it->pos, "Positional argument after named argument");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(item) break;
|
|
|
|
|
Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL));
|
|
|
|
|
if(!name.is_const){
|
|
|
|
|
compiler_error(node->pos, "size_of requires a constant value");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(item){
|
|
|
|
|
set_flag(item->call_flags, CALL_INCLUDED);
|
|
|
|
|
Operand expr = resolve_expr(item->item, AST_CANT_BE_NULL, lambda_arg->type);
|
|
|
|
|
if(is_flag_set(lambda_arg->flags, AST_ANY_VARGS)){
|
|
|
|
|
make_sure_value_is_compatible_with_type(item->pos, &expr, lambda_arg->type->base, TYPE_AND_EXPR_REQUIRED);
|
|
|
|
|
item->resolved_type = expr.type;
|
|
|
|
|
Ast_Type *type = name.type == type_type ? name.type_val : name.type;
|
|
|
|
|
Value v = value_int(type->size);
|
|
|
|
|
rewrite_into_const(node, Ast_Builtin, v);
|
|
|
|
|
return operand_const_rvalue(v);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if(expr_atom_is_equal_intern(node->name, intern_lengthof)){
|
|
|
|
|
Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node);
|
|
|
|
|
|
|
|
|
|
Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL));
|
|
|
|
|
node->kind = AST_LENGTH_OF;
|
|
|
|
|
node->resolved_type = type_s64;
|
|
|
|
|
if(is_array(name.type)){
|
|
|
|
|
Value value = value_int(name.type->arr.size);
|
|
|
|
|
rewrite_into_const(node, Ast_Builtin, value);
|
|
|
|
|
return operand_const_rvalue(value);
|
|
|
|
|
}
|
|
|
|
|
else if(name.type->kind == TYPE_UNTYPED_STRING){
|
|
|
|
|
Value value = value_int(name.intern_val.len);
|
|
|
|
|
rewrite_into_const(node, Ast_Builtin, value);
|
|
|
|
|
return operand_const_rvalue(value);
|
|
|
|
|
}
|
|
|
|
|
else if(is_array(name.type) || is_slice(name.type) || is_string(name.type)){
|
|
|
|
|
return operand_rvalue(type_s64);
|
|
|
|
|
}
|
|
|
|
|
else compiler_error(node->pos, "Can't get length of type %Q", typestring(name.type));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if(expr_atom_is_equal_intern(node->name, intern_alignof)){
|
|
|
|
|
Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node);
|
|
|
|
|
Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL));
|
|
|
|
|
if(!name.is_const) compiler_error(node->pos, "align_of requires a constant value");
|
|
|
|
|
Ast_Type *type = name.type == type_type ? name.type_val : name.type;
|
|
|
|
|
Value v = value_int(type->align);
|
|
|
|
|
rewrite_into_const(node, Ast_Builtin, v);
|
|
|
|
|
return operand_const_rvalue(v);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
Operand name = resolve_expr(node->name, inherit_flag(flags, AST_CANT_BE_NULL));
|
|
|
|
|
if(name.type->kind != TYPE_LAMBDA)
|
|
|
|
|
compiler_error(node->pos, "Calling %Q which is not a [Lambda]", typestring(name.type));
|
|
|
|
|
|
|
|
|
|
Scratch scratch;
|
|
|
|
|
Array<Ast_Call_Item *> items = {scratch};
|
|
|
|
|
S64 was_name_indexed = false;
|
|
|
|
|
S64 default_iter = 0;
|
|
|
|
|
|
|
|
|
|
Ast_Lambda *lambda = (Ast_Lambda *)name.type->ast;
|
|
|
|
|
For_Named(lambda->args, lambda_arg){
|
|
|
|
|
assert(lambda_arg->type);
|
|
|
|
|
|
|
|
|
|
Ast_Call_Item *item = 0;
|
|
|
|
|
For(node->exprs){
|
|
|
|
|
assert(!is_flag_set(it->call_flags, CALL_INDEX));
|
|
|
|
|
if(is_flag_set(it->call_flags, CALL_NAME)){
|
|
|
|
|
Ast_Atom *name = it->name;
|
|
|
|
|
assert(name->kind == AST_IDENT);
|
|
|
|
|
was_name_indexed = true;
|
|
|
|
|
if(name->intern_val.str == lambda_arg->name.str)
|
|
|
|
|
item = it;
|
|
|
|
|
}
|
|
|
|
|
else if(node->exprs.get_index(&it) == default_iter){
|
|
|
|
|
default_iter++;
|
|
|
|
|
item = it;
|
|
|
|
|
}
|
|
|
|
|
else if(node->exprs.get_index(&it) > default_iter){
|
|
|
|
|
compiler_error(it->pos, "Positional argument after named argument");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(item) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(item){
|
|
|
|
|
set_flag(item->call_flags, CALL_INCLUDED);
|
|
|
|
|
Operand expr = resolve_expr(item->item, AST_CANT_BE_NULL, lambda_arg->type);
|
|
|
|
|
if(is_flag_set(lambda_arg->flags, AST_ANY_VARGS)){
|
|
|
|
|
make_sure_value_is_compatible_with_type(item->pos, &expr, lambda_arg->type->base, TYPE_AND_EXPR_REQUIRED);
|
|
|
|
|
item->resolved_type = expr.type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else{
|
|
|
|
|
make_sure_value_is_compatible_with_type(item->pos, &expr, lambda_arg->type, TYPE_AND_EXPR_REQUIRED);
|
|
|
|
|
item->resolved_type = lambda_arg->type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try_propagating_resolved_type_to_untyped_literals(item->item, item->resolved_type);
|
|
|
|
|
item->resolved_index = lambda->args.get_index(&lambda_arg);
|
|
|
|
|
items.add(item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else{
|
|
|
|
|
make_sure_value_is_compatible_with_type(item->pos, &expr, lambda_arg->type, TYPE_AND_EXPR_REQUIRED);
|
|
|
|
|
item->resolved_type = lambda_arg->type;
|
|
|
|
|
if(!lambda_arg->expr)
|
|
|
|
|
compiler_error(lambda_arg->pos, "Required value: %Q in lambda call was not passed", lambda_arg->name);
|
|
|
|
|
|
|
|
|
|
// @note: default values are typechecked when they get resolved
|
|
|
|
|
Ast_Call_Item *call_item = ast_call_item(lambda_arg->expr->pos, 0, 0, lambda_arg->expr);
|
|
|
|
|
call_item->resolved_type = lambda_arg->expr->resolved_type;
|
|
|
|
|
call_item->resolved_index = lambda->args.get_index(&lambda_arg);
|
|
|
|
|
set_flag(call_item->call_flags, CALL_INCLUDED);
|
|
|
|
|
items.add(call_item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try_propagating_resolved_type_to_untyped_literals(item->item, item->resolved_type);
|
|
|
|
|
item->resolved_index = lambda->args.get_index(&lambda_arg);
|
|
|
|
|
items.add(item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else{
|
|
|
|
|
if(!lambda_arg->expr)
|
|
|
|
|
compiler_error(lambda_arg->pos, "Required value: %Q in lambda call was not passed", lambda_arg->name);
|
|
|
|
|
node->exprs = items.tight_copy(pctx->perm);
|
|
|
|
|
node->resolved_type = name.type->func.ret;
|
|
|
|
|
|
|
|
|
|
// @note: default values are typechecked when they get resolved
|
|
|
|
|
Ast_Call_Item *call_item = ast_call_item(lambda_arg->expr->pos, 0, 0, lambda_arg->expr);
|
|
|
|
|
call_item->resolved_type = lambda_arg->expr->resolved_type;
|
|
|
|
|
call_item->resolved_index = lambda->args.get_index(&lambda_arg);
|
|
|
|
|
set_flag(call_item->call_flags, CALL_INCLUDED);
|
|
|
|
|
items.add(call_item);
|
|
|
|
|
// @note: check if all arguments are included and cleanup
|
|
|
|
|
For(node->exprs){
|
|
|
|
|
if(!is_flag_set(it->call_flags, CALL_INCLUDED))
|
|
|
|
|
compiler_error(it->pos, "Unknown argument to a function call, couldn't match it with any of the declared arguments");
|
|
|
|
|
else unset_flag(it->call_flags, CALL_INCLUDED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return operand_rvalue(name.type->func.ret);
|
|
|
|
|
//
|
|
|
|
|
// CALL End
|
|
|
|
|
//
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node->exprs = items.tight_copy(pctx->perm);
|
|
|
|
|
node->resolved_type = name.type->func.ret;
|
|
|
|
|
|
|
|
|
|
// @note: check if all arguments are included and cleanup
|
|
|
|
|
For(node->exprs){
|
|
|
|
|
if(!is_flag_set(it->call_flags, CALL_INCLUDED))
|
|
|
|
|
compiler_error(it->pos, "Unknown argument to a function call, couldn't match it with any of the declared arguments");
|
|
|
|
|
else unset_flag(it->call_flags, CALL_INCLUDED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return operand_rvalue(name.type->func.ret);
|
|
|
|
|
//
|
|
|
|
|
// CALL End
|
|
|
|
|
//
|
|
|
|
|
BREAK();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|