Files
c3c/src/compiler/sema_expr.c

2914 lines
83 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the GNU LGPLv3.0 license
// a copy of which can be found in the LICENSE file.
#include "sema_internal.h"
#include "bigint.h"
/*
* TODOs
* - Check all returns correctly
* - Disallow jumping in and out of an expression block.
*/
static Expr **expr_copy_expr_list_from_macro(Context *context, Expr *macro, Expr **expr_list);
static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_expr);
static Ast *ast_copy_from_macro(Context *context, Expr *macro, Ast *source);
static Ast **ast_copy_list_from_macro(Context *context, Expr *macro, Ast **to_copy);
#define MACRO_COPY_EXPR(x) x = expr_copy_from_macro(context, macro, x)
#define MACRO_COPY_TYPE(x) x = type_info_copy_from_macro(context, macro, x)
#define MACRO_COPY_TYPE_LIST(x) x = type_info_copy_list_from_macro(context, macro, x)
#define MACRO_COPY_EXPR_LIST(x) x = expr_copy_expr_list_from_macro(context, macro, x)
#define MACRO_COPY_AST_LIST(x) x = ast_copy_list_from_macro(context, macro, x)
#define MACRO_COPY_AST(x) x = ast_copy_from_macro(context, macro, x)
static inline bool is_const(Expr *expr)
{
return expr->expr_kind == EXPR_CONST;
}
static inline bool both_const(Expr *left, Expr *right)
{
return left->expr_kind == EXPR_CONST && right->expr_kind == EXPR_CONST;
}
static inline bool both_any_integer(Expr *left, Expr *right)
{
return type_is_any_integer(left->type->canonical) && type_is_any_integer(right->type->canonical);
}
int sema_check_comp_time_bool(Context *context, Expr *expr)
{
if (!sema_analyse_expr_of_required_type(context, type_bool, expr)) return -1;
if (expr->expr_kind != EXPR_CONST)
{
SEMA_ERROR(expr, "$if requires a compile time constant value.");
return -1;
}
return expr->const_expr.b;
}
static bool expr_is_ltype(Expr *expr)
{
switch (expr->expr_kind)
{
case EXPR_IDENTIFIER:
return expr->identifier_expr.decl->decl_kind == DECL_VAR && (expr->identifier_expr.decl->var.kind == VARDECL_LOCAL
|| expr->identifier_expr.decl->var.kind == VARDECL_GLOBAL
|| expr->identifier_expr.decl->var.kind == VARDECL_PARAM);
case EXPR_UNARY:
return expr->unary_expr.operator == UNARYOP_DEREF;
case EXPR_ACCESS:
return expr_is_ltype(expr->access_expr.parent);
case EXPR_GROUP:
return expr_is_ltype(expr->group_expr);
case EXPR_SUBSCRIPT:
return true;
default:
return false;
}
}
static inline bool sema_type_error_on_binop(Expr *expr)
{
const char *c = token_type_to_string(binaryop_to_token(expr->binary_expr.operator));
SEMA_ERROR(expr,
"%s is not defined in the expression '%s' %s '%s'.",
c,
type_to_error_string(expr->binary_expr.left->type),
c,
type_to_error_string(expr->binary_expr.right->type));
return false;
}
static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *expr)
{
Expr *left = expr->ternary_expr.then_expr;
Expr *cond = expr->ternary_expr.cond;
// Normal
if (left)
{
if (!sema_analyse_expr(context, type_bool, cond)) return expr_poison(expr);
if (!sema_analyse_expr(context, to, left)) return expr_poison(expr);
}
else
{
// Elvis
if (!sema_analyse_expr(context, to, cond)) return expr_poison(expr);
Type *type = cond->type->canonical;
if (type->type_kind != TYPE_BOOL && cast_to_bool_kind(type) == CAST_ERROR)
{
SEMA_ERROR(cond, "Cannot convert expression to boolean.");
return false;
}
left = cond;
}
Expr *right = expr->ternary_expr.else_expr;
if (!sema_analyse_expr(context, to, right)) return expr_poison(expr);
Type *left_canonical = left->type->canonical;
Type *right_canonical = right->type->canonical;
if (left_canonical != right_canonical)
{
Type *max = type_find_max_type(left_canonical, right_canonical);
if (!max)
{
SEMA_ERROR(expr, "Cannot find a common parent type of '%s' and '%s'",
type_to_error_string(left_canonical), type_to_error_string(right_canonical));
return false;
}
if (!cast_implicit(left, max) || !cast_implicit(right, max)) return false;
}
expr->type = left->type;
return true;
}
static inline bool sema_expr_analyse_enum_constant(Expr *expr, const char *name, Decl *decl)
{
VECEACH(decl->enums.values, i)
{
Decl *enum_constant = decl->enums.values[i];
if (enum_constant->name == name)
{
assert(enum_constant->resolve_status == RESOLVE_DONE);
expr->type = enum_constant->type;
expr->const_expr = enum_constant->enum_constant.expr->const_expr;
expr->expr_kind = EXPR_CONST;
return true;
}
}
return false;
}
static inline bool sema_expr_analyse_error_constant(Expr *expr, const char *name, Decl *decl)
{
VECEACH(decl->error.error_constants, i)
{
Decl *error_constant = decl->error.error_constants[i];
if (error_constant->name == name)
{
assert(error_constant->resolve_status == RESOLVE_DONE);
expr->type = decl->type;
expr->expr_kind = EXPR_CONST;
expr->const_expr.error_constant = error_constant;
expr->const_expr.kind = TYPE_ERROR;
return true;
}
}
return false;
}
static inline bool find_possible_inferred_identifier(Type *to, Expr *expr)
{
if (to->canonical->type_kind != TYPE_ENUM && to->canonical->type_kind != TYPE_ERROR) return false;
Decl *parent_decl = to->canonical->decl;
switch (parent_decl->decl_kind)
{
case DECL_ENUM:
return sema_expr_analyse_enum_constant(expr, expr->identifier_expr.identifier, parent_decl);
case DECL_ERROR:
return sema_expr_analyse_error_constant(expr, expr->identifier_expr.identifier, parent_decl);
case DECL_UNION:
case DECL_STRUCT:
return false;
default:
UNREACHABLE
}
}
static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr *expr)
{
Decl *ambiguous_decl;
Decl *decl = sema_resolve_symbol(context, expr->identifier_expr.identifier, expr->identifier_expr.path, &ambiguous_decl);
if (!decl && !expr->identifier_expr.path && to)
{
if (find_possible_inferred_identifier(to, expr)) return true;
}
if (!decl)
{
SEMA_ERROR(expr, "The symbol '%s' could not be found.", expr->identifier_expr.identifier);
return false;
}
// Already handled
if (!decl_ok(decl)) return false;
if (ambiguous_decl)
{
SEMA_ERROR(expr,
"Ambiguous symbol '%s' both defined in %s and %s, please add the module name to resolve the ambiguity",
expr->identifier_expr.identifier,
decl->module->name->module,
ambiguous_decl->module->name->module);
return false;
}
if (decl->decl_kind == DECL_FUNC && !expr->identifier_expr.path && decl->module != context->module)
{
SEMA_ERROR(expr, "Functions from other modules, must be prefixed with the module name");
return false;
}
if (decl->decl_kind == DECL_MACRO)
{
SEMA_ERROR(expr, "Macro expansions must be prefixed with '@', try using '@%s(...)' instead.", decl->name);
return false;
}
assert(decl->type);
expr->identifier_expr.decl = decl;
expr->type = decl->type;
return true;
}
static inline bool sema_expr_analyse_binary_sub_expr(Context *context, Type *to, Expr *left, Expr *right)
{
return sema_analyse_expr(context, to, left) & sema_analyse_expr(context, to, right);
}
static inline bool sema_expr_analyse_var_call(Context *context, Type *to, Expr *expr) { TODO }
static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Expr *expr) { TODO };
static inline int find_index_of_named_parameter(Decl** func_params, Expr *expr)
{
if (expr->expr_kind != EXPR_IDENTIFIER || expr->identifier_expr.path)
{
SEMA_ERROR(expr, "Expected the name of a function parameter here, enclose the assignment expression in ().");
return -1;
}
const char *name = expr->identifier_expr.identifier;
VECEACH(func_params, i)
{
if (func_params[i]->name == name) return (int)i;
}
SEMA_ERROR(expr, "There's no parameter with the name '%s', if you want an assignment expression, enclose it in ().", name);
return -1;
}
static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr *expr, Decl *decl)
{
Expr **args = expr->call_expr.arguments;
FunctionSignature *signature = &decl->func.function_signature;
Decl **func_params = signature->params;
expr->call_expr.throw_info = CALLOCS(ThrowInfo);
expr->call_expr.throw_info->defers = context->current_scope->defers;
unsigned error_params = signature->throw_any || signature->throws;
if (error_params)
{
if (context->try_nesting == 0)
{
SEMA_ERROR(expr, "Function '%s' throws errors, this call must be prefixed 'try'.", decl->name);
return false;
}
// Add errors to current context.
if (signature->throw_any)
{
vec_add(context->error_calls, throw_new_union(expr->span, THROW_TYPE_CALL_ANY, expr->call_expr.throw_info));
}
else
{
if (vec_size(signature->throws) == 1)
{
vec_add(context->error_calls, throw_new_single(expr->span, THROW_TYPE_CALL_THROW_ONE, expr->call_expr.throw_info, signature->throws[0]->type));
}
else
{
vec_add(context->error_calls, throw_new_multiple(expr->span, expr->call_expr.throw_info, signature->throws));
}
}
}
unsigned func_param_count = vec_size(func_params);
unsigned num_args = vec_size(args);
unsigned entries_needed = func_param_count > num_args ? func_param_count : num_args;
Expr **actual_args = VECNEW(Expr*, entries_needed);
for (unsigned i = 0; i < entries_needed; i++) vec_add(actual_args, NULL);
memset(actual_args, 0, entries_needed * sizeof(Expr*));
bool uses_named_parameters = false;
for (unsigned i = 0; i < num_args; i++)
{
Expr *arg = args[i];
// Named parameters
if (arg->expr_kind == EXPR_BINARY && arg->binary_expr.operator == BINARYOP_ASSIGN)
{
uses_named_parameters = true;
int index = find_index_of_named_parameter(func_params, arg->binary_expr.left);
if (index < 0) return false;
if (actual_args[index])
{
SEMA_ERROR(arg, "The parameter '%s' was already set once.", func_params[index]->name);
return false;
}
if (!sema_analyse_expr_of_required_type(context, func_params[index]->type, arg->binary_expr.right)) return false;
actual_args[index] = arg->binary_expr.right;
continue;
}
if (i >= func_param_count)
{
if (!signature->variadic)
{
SEMA_ERROR(expr, "Too many parameters for this function.");
return false;
}
if (!sema_analyse_expr_of_required_type(context, NULL, arg)) return false;
actual_args[i] = arg;
continue;
}
if (uses_named_parameters)
{
SEMA_ERROR(expr, "A regular parameter cannot follow a named parameter.");
return false;
}
if (!sema_analyse_expr_of_required_type(context, func_params[i]->type, arg)) return false;
actual_args[i] = arg;
}
for (unsigned i = 0; i < entries_needed; i++)
{
if (actual_args[i]) continue;
if (func_params[i]->var.init_expr)
{
actual_args[i] = func_params[i]->var.init_expr;
continue;
}
SEMA_ERROR(expr, "Parameter '%s' was not set.", func_params[i]->name);
return false;
}
expr->type = decl->func.function_signature.rtype->type;
expr->call_expr.arguments = actual_args;
return true;
}
static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr)
{
// TODO
Expr *func_expr = expr->call_expr.function;
// TODO check
if (!sema_analyse_expr(context, to, func_expr)) return false;
Decl *decl;
switch (func_expr->expr_kind)
{
case EXPR_TYPE_ACCESS:
decl = func_expr->type_access.method;
break;
case EXPR_IDENTIFIER:
decl = func_expr->identifier_expr.decl;
break;
default:
TODO
}
switch (decl->decl_kind)
{
case DECL_VAR:
return sema_expr_analyse_var_call(context, to, expr);
case DECL_FUNC:
return sema_expr_analyse_func_call(context, to, expr, decl);
case DECL_MACRO:
SEMA_ERROR(expr, "Macro calls must be preceeded by '@'.");
return false;
case DECL_GENERIC:
return sema_expr_analyse_generic_call(context, to, expr);
case DECL_POISONED:
return false;
default:
SEMA_ERROR(expr, "The expression cannot be called.");
return false;
}
}
static inline bool sema_expr_analyse_subscript_after_parent_resolution(Context *context, Type *parent, Expr *expr)
{
assert(expr->expr_kind == EXPR_SUBSCRIPT);
Expr *subscripted = expr->subscript_expr.expr;
Type *type = parent ? parent->canonical : subscripted->type->canonical;
Expr *index = expr->subscript_expr.index;
Type *inner_type = type_get_indexed_type(type);
if (!inner_type)
{
SEMA_ERROR((parent ? expr : subscripted), "Cannot index '%s'.", type_to_error_string(type));
return false;
}
if (!sema_analyse_expr(context, type_isize, index)) return false;
// Unless we already have type_usize, cast to type_isize;
if (index->type->canonical->type_kind != type_usize->canonical->type_kind)
{
if (!cast_implicit(index, type_isize)) return false;
}
// Check range
if (index->expr_kind == EXPR_CONST)
{
switch (type->type_kind)
{
case TYPE_ARRAY:
{
BigInt size;
bigint_init_unsigned(&size, type->array.len);
if (bigint_cmp(&size, &index->const_expr.i) != CMP_GT)
{
SEMA_ERROR(index, "Array index out of bounds, was %s, exceeding max index of %llu.",
bigint_to_error_string(&index->const_expr.i, 10), type->array.len - 1);
return false;
}
FALLTHROUGH;
}
case TYPE_VARARRAY:
case TYPE_SUBARRAY:
if (bigint_cmp_zero(&index->const_expr.i) == CMP_LT)
{
SEMA_ERROR(index, "Array index out of bounds, was %s.", bigint_to_error_string(&index->const_expr.i, 10));
return false;
}
break;
default:
break;
}
}
expr->type = inner_type;
return true;
}
static inline bool sema_expr_analyse_subscript(Context *context, Type *to, Expr *expr)
{
if (!sema_analyse_expr(context, NULL, expr->subscript_expr.expr)) return false;
return sema_expr_analyse_subscript_after_parent_resolution(context, NULL, expr);
}
static inline bool sema_expr_analyse_method_function(Context *context, Expr *expr, Decl *decl, bool is_pointer)
{
const char *name = expr->access_expr.sub_element.string;
VECEACH(decl->method_functions, i)
{
Decl *function = decl->method_functions[i];
if (function->name == name)
{
// TODO
return true;
}
}
SEMA_ERROR(expr, "Cannot find method function '%s.%s'", decl->name, name);
return false;
}
static Decl *strukt_recursive_search_member(Decl *strukt, const char *name)
{
VECEACH(strukt->strukt.members, i)
{
Decl *member = strukt->strukt.members[i];
if (member->name == name) return member;
if (!member->name && type_is_structlike(member->type->canonical))
{
Decl *result = strukt_recursive_search_member(member->type->canonical->decl, name);
if (result)
{
return result;
}
}
}
return NULL;
}
static inline bool sema_expr_analyse_group(Context *context, Type *to, Expr *expr)
{
if (!sema_analyse_expr(context, to, expr->group_expr)) return false;
*expr = *expr->group_expr;
return true;
}
static inline bool sema_expr_analyse_access(Context *context, Expr *expr)
{
if (!sema_analyse_expr(context, NULL, expr->access_expr.parent)) return false;
assert(expr->expr_kind == EXPR_ACCESS);
assert(expr->access_expr.parent->resolve_status == RESOLVE_DONE);
Type *parent_type = expr->access_expr.parent->type;
Type *type = parent_type->canonical;
bool is_pointer = type->type_kind == TYPE_POINTER;
if (is_pointer)
{
type = type->pointer;
}
if (!type_may_have_method_functions(type))
{
SEMA_ERROR(expr, "Cannot access '%s' on '%s'", expr->access_expr.sub_element.string, type_to_error_string(parent_type));
return false;
}
Decl *decl = type->decl;
switch (decl->decl_kind)
{
case DECL_ENUM:
case DECL_ERROR:
return sema_expr_analyse_method_function(context, expr, decl, is_pointer);
case DECL_STRUCT:
case DECL_UNION:
break;
default:
UNREACHABLE
}
Decl *member = strukt_recursive_search_member(decl, expr->access_expr.sub_element.string);
if (!member)
{
SEMA_TOKEN_ERROR(expr->access_expr.sub_element, "There is no element '%s.%s'.", decl->name, expr->access_expr.sub_element.string);
return false;
}
if (is_pointer)
{
Expr *deref = expr_new(EXPR_UNARY, expr->span);
deref->unary_expr.operator = UNARYOP_DEREF;
deref->unary_expr.expr = expr->access_expr.parent;
deref->resolve_status = RESOLVE_DONE;
deref->type = type;
expr->access_expr.parent = deref;
}
expr->type = member->type;
expr->access_expr.ref = member;
return true;
}
static inline bool sema_expr_analyse_type_access(Context *context, Type *to, Expr *expr)
{
TypeInfo *type_info = expr->type_access.type;
if (!sema_resolve_type_info(context, type_info)) return false;
Type *canonical = type_info->type->canonical;
if (expr->type_access.name.type == TOKEN_TYPEID)
{
expr->type = type_typeid;
expr->expr_kind = EXPR_TYPEID;
expr->typeid_expr = type_info;
expr->resolve_status = RESOLVE_DONE;
return true;
}
if (!type_may_have_method_functions(canonical))
{
SEMA_ERROR(expr, "'%s' does not have method functions.", type_to_error_string(type_info->type));
return false;
}
Decl *decl = canonical->decl;
// TODO add more constants that can be inspected?
// e.g. SomeEnum.values, MyUnion.x.offset etc?
switch (decl->decl_kind)
{
case DECL_ENUM:
if (expr->type_access.name.type == TOKEN_CONST_IDENT)
{
if (!sema_expr_analyse_enum_constant(expr, expr->type_access.name.string, decl))
{
SEMA_ERROR(expr, "'%s' has no enumeration value '%s'.", decl->name, expr->type_access.name.string);
return false;
}
return true;
}
break;
case DECL_ERROR:
if (expr->type_access.name.type == TOKEN_CONST_IDENT)
{
if (!sema_expr_analyse_error_constant(expr, expr->type_access.name.string, decl))
{
SEMA_ERROR(expr, "'%s' has no error type '%s'.", decl->name, expr->type_access.name.string);
return false;
}
return true;
}
break;
case DECL_UNION:
case DECL_STRUCT:
break;
default:
UNREACHABLE
}
VECEACH(type_info->type->decl->method_functions, i)
{
Decl *function = type_info->type->decl->method_functions[i];
if (expr->type_access.name.string == function->name)
{
expr->type_access.method = function;
expr->type = function->func.function_signature.rtype->type;
return true;
}
}
SEMA_ERROR(expr, "No function '%s.%s' found.", type_to_error_string(type_info->type), expr->type_access.name.string);
return false;
}
static DesignatedPath *sema_analyse_init_path(Context *context, DesignatedPath *parent, Expr *expr, bool *has_found_match, bool *has_reported_error);
static DesignatedPath *sema_analyse_init_identifier_string(Context *context, DesignatedPath *parent_path, const char *string, bool *has_found_match, bool *has_reported_error)
{
assert(type_is_structlike(parent_path->type));
Decl **members = parent_path->type->decl->strukt.members;
VECEACH(members, i)
{
Decl *member = members[i];
if (member->name == string)
{
DesignatedPath *sub_path = CALLOCS(DesignatedPath);
sub_path->type = member->type;
sub_path->kind = DESIGNATED_IDENT;
sub_path->index = i;
parent_path->sub_path = sub_path;
*has_found_match = true;
return sub_path;
}
if (!member->name)
{
DesignatedPath temp_path;
temp_path.type = member->type;
DesignatedPath *found = sema_analyse_init_identifier_string(context, &temp_path, string, has_found_match, has_reported_error);
if (!found) continue;
DesignatedPath *real_path = malloc_arena(sizeof(DesignatedPath));
*real_path = temp_path;
real_path->index = i;
real_path->kind = DESIGNATED_IDENT;
parent_path->sub_path = real_path;
*has_found_match = true;
return found;
}
}
return NULL;
}
static DesignatedPath *sema_analyse_init_access(Context *context, DesignatedPath *parent, Expr *access_expr, bool *has_found_match, bool *has_reported_error)
{
DesignatedPath *last_path = sema_analyse_init_path(context, parent, access_expr->access_expr.parent, has_found_match, has_reported_error);
if (!last_path) return NULL;
DesignatedPath *path = sema_analyse_init_identifier_string(context, last_path, access_expr->access_expr.sub_element.string, has_found_match, has_reported_error);
if (!path && has_found_match && !has_reported_error)
{
SEMA_TOKEN_ERROR(access_expr->access_expr.sub_element, "'%s' is not a valid sub member.", access_expr->access_expr.sub_element.string);
*has_reported_error = true;
}
return path;
}
static bool expr_cast_to_index(Expr *index)
{
if (index->expr_kind == EXPR_RANGE)
{
TODO
}
if (index->type->canonical->type_kind == type_usize->canonical->type_kind) return true;
return cast_implicit(index, type_isize);
}
static bool expr_check_index_in_range(Type *type, Expr *index)
{
if (index->expr_kind == EXPR_RANGE)
{
return expr_check_index_in_range(type, index->range_expr.left) & expr_check_index_in_range(type, index->range_expr.right);
}
assert(type == type->canonical);
if (index->expr_kind == EXPR_CONST)
{
switch (type->type_kind)
{
case TYPE_ARRAY:
{
BigInt size;
bigint_init_unsigned(&size, type->array.len);
if (bigint_cmp(&size, &index->const_expr.i) != CMP_GT)
{
SEMA_ERROR(index, "Array index out of bounds, was %s, exceeding max index of %llu.",
bigint_to_error_string(&index->const_expr.i, 10), type->array.len - 1);
return false;
}
FALLTHROUGH;
}
case TYPE_VARARRAY:
case TYPE_SUBARRAY:
if (bigint_cmp_zero(&index->const_expr.i) == CMP_LT)
{
SEMA_ERROR(index, "Array index out of bounds, was %s.", bigint_to_error_string(&index->const_expr.i, 10));
return false;
}
break;
case TYPE_STRING:
TODO
default:
UNREACHABLE
}
}
return true;
}
static DesignatedPath *sema_analyse_init_subscript(Context *context, DesignatedPath *parent, Expr *expr, bool *has_found_match, bool *has_reported_error)
{
assert(expr->expr_kind == EXPR_SUBSCRIPT);
DesignatedPath *path = parent;
if (expr->subscript_expr.expr)
{
path = sema_analyse_init_path(context, parent, expr->subscript_expr.expr, has_found_match, has_reported_error);
}
if (!path) return NULL;
Type *type = path->type;
if (type->canonical->type_kind == TYPE_POINTER)
{
SEMA_ERROR(expr, "It's not possible to subscript a pointer field in a designated initializer.");
*has_reported_error = true;
return NULL;
}
Expr *index = expr->subscript_expr.index;
Type *inner_type = type_get_indexed_type(type);
if (!inner_type)
{
SEMA_ERROR(expr, "Not possible to index a value of type '%s'.", type_to_error_string(type));
*has_reported_error = true;
return NULL;
}
if (!sema_analyse_expr(context, type_isize, index))
{
*has_reported_error = true;
return NULL;
}
// Unless we already have type_usize, cast to type_isize;
if (!expr_cast_to_index(index))
{
*has_reported_error = true;
return NULL;
}
// Check range
if (!expr_check_index_in_range(type->canonical, index))
{
*has_reported_error = true;
return NULL;
}
DesignatedPath *sub_path = CALLOCS(DesignatedPath);
path->sub_path = sub_path;
sub_path->type = inner_type;
sub_path->kind = DESIGNATED_SUBSCRIPT;
sub_path->index_expr = index;
*has_found_match = true;
return sub_path;
}
static DesignatedPath *sema_analyse_init_path(Context *context, DesignatedPath *parent, Expr *expr, bool *has_found_match, bool *has_reported_error)
{
switch (expr->expr_kind)
{
case EXPR_ACCESS:
return sema_analyse_init_access(context, parent, expr, has_found_match, has_reported_error);
case EXPR_IDENTIFIER:
return sema_analyse_init_identifier_string(context, parent, expr->identifier_expr.identifier, has_found_match, has_reported_error);
case EXPR_SUBSCRIPT:
return sema_analyse_init_subscript(context, parent, expr, has_found_match, has_reported_error);
default:
return NULL;
}
}
static bool sema_expr_analyse_designated_initializer(Context *context, Type *assigned, Expr *initializer)
{
Expr **init_expressions = initializer->expr_initializer.initializer_expr;
bool is_structlike = type_is_structlike(assigned->canonical);
VECEACH(init_expressions, i)
{
Expr *expr = init_expressions[i];
// 1. Ensure that're seeing expr = expr on the top level.
if (expr->expr_kind != EXPR_BINARY || expr->binary_expr.operator != BINARYOP_ASSIGN)
{
if (is_structlike)
{
SEMA_ERROR(expr, "Expected an initializer on the format 'foo = 123' here.");
}
else
{
SEMA_ERROR(expr, "Expected an initializer on the format '[1] = 123' here.");
}
return false;
}
Expr *init_expr = expr->binary_expr.left;
DesignatedPath path = { .type = assigned };
bool has_reported_error = false;
bool has_found_match = false;
DesignatedPath *last_path = sema_analyse_init_path(context, &path, init_expr, &has_found_match, &has_reported_error);
if (!has_reported_error && !last_path)
{
SEMA_ERROR(expr, "This is not a valid member of '%s'.", type_to_error_string(assigned));
return false;
}
Expr *value = expr->binary_expr.right;
if (!sema_analyse_expr_of_required_type(context, last_path->type, value)) return false;
expr->expr_kind = EXPR_DESIGNATED_INITIALIZER;
expr->designated_init_expr.path = path.sub_path;
expr->designated_init_expr.value = value;
expr->resolve_status = RESOLVE_DONE;
}
initializer->expr_initializer.init_type = INITIALIZER_DESIGNATED;
return true;
}
/**
* Perform analysis for a plain initializer, that is one initializing all fields.
* @return true if analysis succeeds.
*/
static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, Decl *assigned, Expr *initializer)
{
Expr **elements = initializer->expr_initializer.initializer_expr;
Decl **members = assigned->strukt.members;
initializer->expr_initializer.init_type = INITIALIZER_NORMAL;
unsigned size = vec_size(elements);
unsigned expected_members = vec_size(members);
// 1. For struct number of members must be the same as the size of the struct.
// Since we already handled the case with an empty initializer before going here
// zero entries must be an error.
assert(size > 0 && "We should already have handled the size == 0 case.");
if (expected_members == 0)
{
// Generate a nice error message for zero.
SEMA_ERROR(elements[0], "Too many elements in initializer, it must be empty.");
return false;
}
// 2. In case of a union, only expect a single entry.
if (assigned->decl_kind == DECL_UNION) expected_members = 1;
// 3. Loop through all elements.
VECEACH(elements, i)
{
// 4. Check if we exceeded the list of elements in the struct/union.
// This way we can check the other elements which might help the
// user pinpoint where they put the double elements.
if (i >= expected_members)
{
SEMA_ERROR(elements[i], "Too many elements in initializer, expected only %d.", expected_members);
return false;
}
// 5. We know the required type, so resolve the expression.
if (!sema_analyse_expr_of_required_type(context, members[i]->type, elements[i])) return false;
}
// 6. There's the case of too few values as well. Mark the last element as wrong.
if (expected_members > size)
{
SEMA_ERROR(elements[size - 1], "Too few elements in initializer, there should be elements after this one.");
return false;
}
// 7. Done!
return true;
}
/**
* Perform analysis for a plain initializer, that is one initializing all fields.
* @return true if analysis succeeds.
*/
static inline bool sema_expr_analyse_array_plain_initializer(Context *context, Type *assigned, Expr *initializer)
{
Expr **elements = initializer->expr_initializer.initializer_expr;
assert(assigned->type_kind == TYPE_ARRAY && "The other types are not done yet.");
Type *inner_type = type_get_indexed_type(assigned);
assert(inner_type);
initializer->expr_initializer.init_type = INITIALIZER_NORMAL;
unsigned size = vec_size(elements);
unsigned expected_members = assigned->array.len;
assert(size > 0 && "We should already have handled the size == 0 case.");
if (expected_members == 0)
{
// Generate a nice error message for zero.
SEMA_ERROR(elements[0], "Too many elements in initializer, it must be empty.");
return false;
}
VECEACH(elements, i)
{
if (i >= expected_members)
{
SEMA_ERROR(elements[i], "Too many elements in initializer, expected only %d.", expected_members);
return false;
}
if (!sema_analyse_expr_of_required_type(context, inner_type, elements[i])) return false;
}
if (expected_members > size)
{
SEMA_ERROR(elements[size - 1], "Too few elements in initializer, %d elements are needed.", expected_members);
return false;
}
// 7. Done!
return true;
}
static inline bool sema_expr_analyse_initializer(Context *context, Type *assigned, Expr *expr)
{
expr->type = assigned;
Expr **init_expressions = expr->expr_initializer.initializer_expr;
// 1. Zero size init will initialize to empty.
if (vec_size(init_expressions) == 0)
{
expr->expr_initializer.init_type = INITIALIZER_ZERO;
return true;
}
// 2. Check if we might have a designated initializer
// this means that in this case we're actually not resolving macros here.
if (init_expressions[0]->expr_kind == EXPR_BINARY && init_expressions[0]->binary_expr.operator == BINARYOP_ASSIGN)
{
return sema_expr_analyse_designated_initializer(context, assigned, expr);
}
// 3. Otherwise use the plain initializer.
if (assigned->type_kind == TYPE_ARRAY)
{
return sema_expr_analyse_array_plain_initializer(context, assigned, expr);
}
else
{
return sema_expr_analyse_struct_plain_initializer(context, assigned->decl, expr);
}
}
static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to, Expr *expr)
{
assert(to);
Type *assigned = to->canonical;
assert(assigned);
switch (assigned->type_kind)
{
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_ARRAY:
return sema_expr_analyse_initializer(context, assigned, expr);
case TYPE_VARARRAY:
TODO
default:
break;
}
// Fix error on compound literals
SEMA_ERROR(expr, "Cannot assign expression to '%s'.", type_to_error_string(to));
return false;
}
static inline bool sema_expr_analyse_expr_list(Context *context, Type *to, Expr *expr)
{
bool success = true;
size_t last = vec_size(expr->expression_list) - 1;
VECEACH(expr->expression_list, i)
{
success &= sema_analyse_expr_of_required_type(context, i == last ? to : NULL, expr->expression_list[i]);
}
return success;
}
static inline bool sema_expr_analyse_cast(Context *context, Type *to, Expr *expr)
{
Expr *inner = expr->cast_expr.expr;
if (!sema_resolve_type_info(context, expr->cast_expr.type_info)) return false;
if (!sema_analyse_expr_of_required_type(context, NULL, inner)) return false;
if (!cast(inner, expr->cast_expr.type_info->type, CAST_TYPE_EXPLICIT)) return false;
// TODO above is probably not right, cast type not set.
// Overwrite cast.
SourceRange loc = expr->span;
*expr = *inner;
expr->span = loc;
return true;
}
/**
* Analyse a = b
* @return true if analysis works
*/
static bool sema_expr_analyse_assign(Context *context, Expr *expr, Expr *left, Expr *right)
{
// 1. Evaluate left side
if (!sema_analyse_expr(context, NULL, left)) return false;
// 2. Check assignability
if (!expr_is_ltype(left))
{
SEMA_ERROR(left, "Expression is not assignable.");
return false;
}
// 3. Evaluate right side to required type.
if (!sema_analyse_expr_of_required_type(context, left->type, right)) return false;
// 4. Set the result to the type on the right side.
expr->type = right->type;
return true;
}
/**
* Analyse *%= *= /= %= ^= |= &=
*
* @return true if analysis worked.
*/
static bool sema_expr_analyse_common_assign(Context *context, Expr *expr, Expr *left, Expr *right, bool int_only)
{
// 1. Analyse left side.
if (!sema_analyse_expr(context, NULL, left)) return false;
// 2. Verify that the left side is assignable.
if (!expr_is_ltype(left))
{
SEMA_ERROR(left, "Expression is not assignable.");
return false;
}
// 3. If this is only defined for ints (*%, ^= |= &= %=) verify that this is an int.
if (int_only && !type_is_any_integer(left->type))
{
SEMA_ERROR(left, "Expected an integer here.");
return false;
}
// 4. In any case, these ops are only defined on numbers.
if (!type_is_numeric(left->type))
{
SEMA_ERROR(left, "Expected a numeric type here.");
return false;
}
// 5. Cast the right hand side to the one on the left
if (!sema_analyse_expr_of_required_type(context, left->type->canonical, right)) return false;
// 6. Check for zero in case of div or mod.
if (right->expr_kind == EXPR_CONST)
{
if (expr->binary_expr.operator == BINARYOP_DIV_ASSIGN)
{
switch (right->const_expr.kind)
{
case ALL_INTS:
if (bigint_cmp_zero(&right->const_expr.i) == CMP_EQ)
{
SEMA_ERROR(right, "Division by zero not allowed.");
return false;
}
break;
case ALL_FLOATS:
if (right->const_expr.f == 0)
{
SEMA_ERROR(right, "Division by zero not allowed.");
return false;
}
break;
default:
UNREACHABLE
}
}
else if (expr->binary_expr.operator == BINARYOP_MOD_ASSIGN)
{
switch (right->const_expr.kind)
{
case ALL_INTS:
if (bigint_cmp_zero(&right->const_expr.i) == CMP_EQ)
{
SEMA_ERROR(right, "% by zero not allowed.");
return false;
}
break;
default:
UNREACHABLE
}
}
}
// 7. Assign type
expr->type = left->type;
return true;
}
static BinaryOp binary_mod_op_to_non_mod(BinaryOp op)
{
switch (op)
{
case BINARYOP_MULT_MOD:
return BINARYOP_MULT;
case BINARYOP_MULT_MOD_ASSIGN:
return BINARYOP_MULT_ASSIGN;
case BINARYOP_SUB_MOD:
return BINARYOP_SUB;
case BINARYOP_SUB_MOD_ASSIGN:
return BINARYOP_SUB_ASSIGN;
case BINARYOP_ADD_MOD:
return BINARYOP_ADD;
case BINARYOP_ADD_MOD_ASSIGN:
return BINARYOP_ADD_ASSIGN;
default:
return op;
}
}
/**
* Handle a += b, a +%= b, a -= b, a -%= b
* @return true if analysis succeeded.
*/
static bool sema_expr_analyse_add_sub_assign(Context *context, Expr *expr, Expr *left, Expr *right)
{
bool is_mod = expr->binary_expr.operator == BINARYOP_ADD_MOD_ASSIGN
|| expr->binary_expr.operator == BINARYOP_SUB_MOD_ASSIGN;
// 1. Analyse the left hand side
if (!sema_analyse_expr(context, NULL, left)) return false;
// 2. Ensure the left hand side is assignable
if (!expr_is_ltype(left))
{
SEMA_ERROR(left, "Expression is not assignable.");
return false;
}
Type *left_type_canonical = left->type->canonical;
expr->type = left->type;
// 3. Attempt to analyse and cast this side to the same type if possible.
if (!sema_analyse_expr(context, left->type, right)) return false;
// 4. In the pointer case we have to treat this differently.
if (left_type_canonical->type_kind == TYPE_POINTER)
{
// 5. Prevent +%= and -%=
if (is_mod)
{
SEMA_ERROR(expr, "Cannot use %s with pointer arithmetics, use %s instead.",
token_type_to_string(binaryop_to_token(expr->binary_expr.operator)),
token_type_to_string(binaryop_to_token(binary_mod_op_to_non_mod(expr->binary_expr.operator))));
return false;
}
// 5. Convert any compile time values to runtime
cast_to_smallest_runtime(right);
// 6. Finally, check that the right side is indeed an integer.
if (!type_is_integer(right->type->canonical))
{
SEMA_ERROR(right, "The right side was '%s' but only integers are valid on the right side of %s when the left side is a pointer.",
type_to_error_string(right->type),
token_type_to_string(binaryop_to_token(expr->binary_expr.operator)));
return false;
}
return true;
}
// 5. Otherwise we cast rhs to lhs
if (!cast_implicit(right, left->type)) return false;
// 6. We expect a numeric type on both left and right
if (!type_is_numeric(left->type))
{
SEMA_ERROR(left, "Expected a numeric type here.");
return false;
}
// 7. Prevent +%= and -%= on non integers
if (is_mod && !type_is_integer(left->type->canonical))
{
SEMA_ERROR(expr, "%s can only be used for integer arithmetics, for other cases use %s instead.",
token_type_to_string(binaryop_to_token(expr->binary_expr.operator)),
token_type_to_string(binaryop_to_token(binary_mod_op_to_non_mod(expr->binary_expr.operator))));
return false;
}
return true;
}
static bool binary_arithmetic_promotion(Expr *left, Expr *right, Type *left_type, Type *right_type)
{
Type *max = type_find_max_type(left_type, right_type);
return max && type_is_numeric(max) && cast_implicit(left, max) && cast_implicit(right, max);
}
/**
* Analyse a - b
* @return true if analysis succeeded
*/
static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
{
// TODO enums
bool is_mod = expr->binary_expr.operator == BINARYOP_SUB_MOD;
// 1. Analyse a and b. Do not push down if this is a -%
if (!sema_expr_analyse_binary_sub_expr(context, is_mod ? NULL : to, left, right)) return false;
Type *left_type = left->type->canonical;
Type *right_type = right->type->canonical;
// 2. Handle the ptr - x and ptr - other_pointer
if (left_type->type_kind == TYPE_POINTER)
{
// 3. Is this -%? That's not ok for pointer maths.
if (is_mod)
{
SEMA_ERROR(expr, "'-%%' is not valid for pointer maths, use '-' instead.");
return false;
}
// 4. ptr - other pointer
if (right_type->type_kind == TYPE_POINTER)
{
// 5. Require that both types are the same.
if (left_type != right_type)
{
SEMA_ERROR(expr, "'%s' - '%s' is not allowed. Subtracting pointers of different types from each other is not possible.", type_to_error_string(left_type), type_to_error_string(right_type));
return false;
}
// 5. usize only if that is the recipient
if (to && to->canonical->type_kind == type_usize->canonical->type_kind)
{
expr->type = to;
return true;
}
expr->type = type_isize;
return true;
}
// 5. Cast any compile time int into smallest runtime version if we have a compile time constant.
cast_to_smallest_runtime(right);
// 6. No need for further casts, just it is an integer.
if (!type_is_integer(right_type))
{
SEMA_ERROR(expr, "Cannot subtract '%s' from '%s'", type_to_error_string(left_type), type_to_error_string(right_type));
return false;
}
expr->type = left->type;
return true;
}
// 7. Attempt arithmetic promotion, to promote both to a common type.
if (!binary_arithmetic_promotion(left, right, left_type, right_type))
{
SEMA_ERROR(expr, "Cannot subtract '%s' from '%s'", type_to_error_string(left_type), type_to_error_string(right_type));
return false;
}
// 8. Handle constant folding.
if (both_const(left, right))
{
switch (left->const_expr.kind)
{
case ALL_INTS:
bigint_sub(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
break;
case ALL_FLOATS:
// IMPROVE precision.
expr->const_expr.f = left->const_expr.f - right->const_expr.f;
break;
default:
UNREACHABLE
}
expr->expr_kind = EXPR_CONST;
expr->const_expr.kind = left->const_expr.kind;
}
// 9. Is this -%? That's not ok unless we are adding integers.
if (is_mod && !type_is_any_integer(left->type->canonical))
{
SEMA_ERROR(expr, "'-%%' is only valid for integer subtraction, use '-' instead.");
return false;
}
expr->type = left->type;
return true;
}
/**
* Analyse a + b / a +% b
* @return true if it succeeds.
*/
static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
{
// TODO enums
bool is_mod = expr->binary_expr.operator == BINARYOP_ADD_MOD;
// 1. Promote everything to the recipient type if possible
// this is safe in the pointer case actually.
if (!sema_expr_analyse_binary_sub_expr(context, is_mod ? NULL : to, left, right)) return false;
Type *left_type = left->type->canonical;
Type *right_type = right->type->canonical;
// 2. To detect pointer additions, reorder if needed
if (right_type->type_kind == TYPE_POINTER && left_type->type_kind != TYPE_POINTER)
{
Expr *temp = right;
right = left;
left = temp;
right_type = left_type;
left_type = left->type->canonical;
}
// 4. The "left" will now always be the pointer.
// so check if we want to do the normal pointer add special handling.
if (left_type->type_kind == TYPE_POINTER)
{
// 4a. Check that the other side is an integer of some sort.
if (!type_is_any_integer(right_type))
{
SEMA_ERROR(right, "A value of type '%s' cannot be added to '%s', an integer was expected here.",
type_to_error_string(right->type),
type_to_error_string(left->type));
return false;
}
// 4b. Cast it to usize or isize depending on underlying type.
// Either is fine, but it looks a bit nicer if we actually do this and keep the sign.
bool success = cast_implicit(right, type_is_unsigned(right_type) ? type_usize : type_isize);
// No need to check the cast we just ensured it was an integer.
assert(success && "This should always work");
// 4c. Set the type.
expr->type = left->type;
// 4d. Is this +%? That's not ok for pointers!
if (is_mod)
{
SEMA_ERROR(expr, "You cannot use '+%%' with pointer addition, use '+' instead.");
return false;
}
return true;
}
// 5. Do the binary arithmetic promotion (finding a common super type)
// If none can be find, send an error.
if (!binary_arithmetic_promotion(left, right, left_type, right_type))
{
SEMA_ERROR(expr, "Cannot add '%s' to '%s'", type_to_error_string(left_type), type_to_error_string(right_type));
return false;
}
// 6. Handle the "both const" case. We should only see ints and floats at this point.
if (both_const(left, right))
{
switch (left->const_expr.kind)
{
case ALL_INTS:
bigint_add(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
break;
case ALL_FLOATS:
expr->const_expr.f = left->const_expr.f + right->const_expr.f;
break;
default:
UNREACHABLE
}
expr->expr_kind = EXPR_CONST;
expr->const_expr.kind = left->const_expr.kind;
}
// 7. Is this +%? That's not ok unless we are adding integers.
if (is_mod && !type_is_any_integer(left->type->canonical))
{
SEMA_ERROR(expr, "'+%%' is only valid for integer addition, use '+' instead.");
return false;
}
// 7. Set the type
expr->type = left->type;
return true;
}
/**
* Analyse a * b and a *% b
*
* Will analyse a and b and convert them to the "to" type if possible.
* It will then try to promote both to a common type,
* check that *% is only used on an integer and then perform constant folding.
*
* @return true if analysis worked.
*/
static bool sema_expr_analyse_mult(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
{
bool is_mod = expr->binary_expr.operator == BINARYOP_MULT_MOD;
// 1. Analyse the sub expressions.
if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false;
Type *left_type = left->type->canonical;
Type *right_type = right->type->canonical;
// 2. Perform promotion to a common type.
if (!binary_arithmetic_promotion(left, right, left_type, right_type))
{
SEMA_ERROR(expr, "Cannot multiply '%s' with '%s'", type_to_error_string(left->type), type_to_error_string(right->type));
return false;
}
// 3. Set the type.
expr->type = left->type;
// Might have changed
left_type = left->type->canonical;
// 4. Prevent *% use on non-integers.
if (is_mod && !type_is_any_integer(left_type))
{
SEMA_ERROR(expr, "*% can only be used with integer types, try * instead.");
return false;
}
// 5. Handle constant folding.
if (both_const(left, right))
{
expr->expr_kind = EXPR_CONST;
expr->const_expr.kind = left->const_expr.kind;
switch (left->const_expr.kind)
{
case ALL_INTS:
// 5a. Do mod mult if applicable.
if (is_mod && left_type != type_compint)
{
bigint_mul_wrap(&expr->const_expr.i,
&left->const_expr.i,
&right->const_expr.i,
is_mod,
left_type->builtin.bitsize);
return true;
}
bigint_mul(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
break;
case ALL_FLOATS:
expr->const_expr.f = left->const_expr.f * right->const_expr.f;
break;
default:
UNREACHABLE
}
}
// 6. All done.
return true;
}
/**
* Analyse a / b
* @return true if analysis completed ok.
*/
static bool sema_expr_analyse_div(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
{
// 1. Analyse sub expressions.
if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false;
Type *left_type = left->type->canonical;
Type *right_type = right->type->canonical;
// 2. Perform promotion to a common type.
if (!binary_arithmetic_promotion(left, right, left_type, right_type))
{
SEMA_ERROR(expr, "Cannot divide '%s' by '%s'.", type_to_error_string(left_type), type_to_error_string(right_type));
return false;
}
expr->type = left->type;
// 3. Check for a constant 0 on the right hand side.
if (is_const(right))
{
switch (right->const_expr.kind)
{
case ALL_INTS:
if (bigint_cmp_zero(&right->const_expr.i) == CMP_EQ)
{
SEMA_ERROR(right, "This expression evaluates to zero and division by zero is not allowed.");
return false;
}
break;
case ALL_FLOATS:
if (right->const_expr.f == 0)
{
SEMA_ERROR(right, "This expression evaluates to zero and division by zero is not allowed.");
return false;
}
break;
default:
UNREACHABLE
}
}
// 4. Perform constant folding.
if (both_const(left, right))
{
switch (left->const_expr.kind)
{
case ALL_INTS:
bigint_div_floor(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
break;
case ALL_FLOATS:
expr->const_expr.f = left->const_expr.f / right->const_expr.f;
break;
default:
UNREACHABLE
}
}
// 5. Done.
return true;
}
/**
* Analyse a % b
* @return true if analysis succeeds.
*/
static bool sema_expr_analyse_mod(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
{
// 1. Analyse both sides.
if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false;
// 2. Make sure we have some sort of integer on both sides.
if (!type_is_any_integer(right->type->canonical) || !type_is_any_integer(left->type->canonical))
{
return sema_type_error_on_binop(expr);
}
// 3. a % 0 is not valid, so detect it.
if (is_const(right) && bigint_cmp_zero(&right->const_expr.i) == CMP_EQ)
{
SEMA_ERROR(expr->binary_expr.right, "Cannot perform % with a constant zero.");
return false;
}
// 4. Constant fold
if (both_const(left, right))
{
expr_replace(expr, left);
// 4a. Remember this is remainder.
bigint_rem(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
}
expr->type = left->type;
return true;
}
/**
* Analyse a ^ b, a | b, a & b
* @return true if the analysis succeeded.
*/
static bool sema_expr_analyse_bit(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
{
// 1. Convert to top down type if possible.
if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false;
// 2. Check that both are integers.
if (!both_any_integer(left, right))
{
return sema_type_error_on_binop(expr);
}
// 3. Promote to the same type.
Type *left_type = left->type->canonical;
Type *right_type = right->type->canonical;
if (!binary_arithmetic_promotion(left, right, left_type, right_type))
{
return sema_type_error_on_binop(expr);
}
// 4. Do constant folding if both sides are constant.
if (both_const(left, right))
{
expr_replace(expr, left);
switch (expr->binary_expr.operator)
{
case BINARYOP_BIT_AND:
bigint_and(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
break;
case BINARYOP_BIT_XOR:
bigint_xor(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
break;
case BINARYOP_BIT_OR:
bigint_or(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
break;
default:
UNREACHABLE;
}
}
// 5. Assign the type
expr->type = left->type;
return true;
}
/**
* Analyse >> and << operations.
* @return true if the analysis succeeded.
*/
static bool sema_expr_analyse_shift(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
{
// 1. Analyze the two sub lhs & rhs *without coercion*
if (!sema_expr_analyse_binary_sub_expr(context, NULL, left, right)) return false;
// 2. Only integers may be shifted.
if (!both_any_integer(left, right))
{
return sema_type_error_on_binop(expr);
}
// 3. For a constant right hand side we will make a series of checks.
if (is_const(right))
{
// 3a. Make sure the value does not exceed the bitsize of
// the left hand side. We ignore this check for lhs being a constant.
if (left->type->canonical->type_kind != TYPE_IXX)
{
BigInt bitsize;
bigint_init_unsigned(&bitsize, left->type->canonical->builtin.bitsize);
if (bigint_cmp(&right->const_expr.i, &bitsize) == CMP_GT)
{
SEMA_ERROR(right, "The shift exceeds bitsize of '%s'.", type_to_error_string(left->type));
return false;
}
}
// 3b. Make sure that the right hand side is positive.
if (bigint_cmp_zero(&right->const_expr.i) == CMP_LT)
{
SEMA_ERROR(right, "A shift must be a positive number.");
return false;
}
// 2c. Cast the value to the smallest runtime type.
cast_to_smallest_runtime(right);
// 4. Fold constant expressions.
if (is_const(left))
{
// 4a. For >> this is always an arithmetic shift.
if (expr->binary_expr.operator == BINARYOP_SHR)
{
expr_replace(expr, left);
bigint_shr(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
return true;
}
// 4b. The << case needs to behave differently for bigints and fixed bit integers.
expr_replace(expr, left);
if (left->const_expr.kind == TYPE_IXX)
{
bigint_shl(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
}
else
{
int bit_count = left->type->canonical->builtin.bitsize;
bool is_signed = !type_kind_is_unsigned(left->const_expr.kind);
bigint_shl_trunc(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i, bit_count, is_signed);
}
return true;
}
}
// 5. We might have the case 2 << x. In that case we will to cast the left hand side to the receiving type.
if (!cast_implicit(left, to)) return false;
// 6. As a last out, we make sure that a comptime int has a real type. We pick i64 for this.
if (!cast_to_runtime(left)) return false;
expr->type = left->type;
return true;
}
/**
* Analyse a <<= b a >>= b
* @return true is the analysis succeeds, false otherwise.
*/
static bool sema_expr_analyse_shift_assign(Context *context, Expr *expr, Expr *left, Expr *right)
{
// 1. Analyze the two sub lhs & rhs *without coercion*
if (!sema_expr_analyse_binary_sub_expr(context, NULL, left, right)) return false;
// 2. Ensure the left hand side is assignable
if (!expr_is_ltype(left))
{
SEMA_ERROR(left, "Expression is not assignable.");
return false;
}
// 2. Only integers may be shifted.
if (!both_any_integer(left, right)) return sema_type_error_on_binop(expr);
// 3. For a constant right hand side we will make a series of checks.
if (is_const(right))
{
// 3a. Make sure the value does not exceed the bitsize of
// the left hand side.
BigInt bitsize;
bigint_init_unsigned(&bitsize, left->type->canonical->builtin.bitsize);
if (bigint_cmp(&right->const_expr.i, &bitsize) == CMP_GT)
{
SEMA_ERROR(right, "The shift exceeds bitsize of '%s'.", type_to_error_string(left->type));
return false;
}
// 3b. Make sure that the right hand side is positive.
if (bigint_cmp_zero(&right->const_expr.i) == CMP_LT)
{
SEMA_ERROR(right, "A shift must be a positive number.");
return false;
}
// 3c. Cast the rhs to the smallest runtime type.
cast_to_smallest_runtime(right);
}
// 4. Set the type
expr->type = left->type;
return true;
}
static bool sema_expr_analyse_and(Context *context, Expr *expr, Expr *left, Expr *right)
{
if (!sema_expr_analyse_binary_sub_expr(context, NULL, left, right)) return false;
if (!cast_implicit(left, type_bool) || !cast_implicit(right, type_bool)) return false;
expr->type = type_bool;
if (both_const(left, right))
{
expr_replace(expr, left);
expr->const_expr.b &= right->const_expr.b;
}
return true;
}
static bool sema_expr_analyse_or(Context *context, Expr *expr, Expr *left, Expr *right)
{
if (!sema_expr_analyse_binary_sub_expr(context, NULL, left, right)) return false;
if (!cast_implicit(left, type_bool) || !cast_implicit(right, type_bool)) return false;
if (both_const(left, right))
{
expr_replace(expr, left);
expr->const_expr.b |= right->const_expr.b;
}
expr->type = type_bool;
return true;
}
static void cast_to_max_bit_size(Expr *left, Expr *right, Type *left_type, Type *right_type)
{
int bit_size_left = left_type->builtin.bitsize;
int bit_size_right = right_type->builtin.bitsize;
assert(bit_size_left && bit_size_right);
if (bit_size_left == bit_size_right) return;
if (bit_size_left < bit_size_right)
{
Type *to = left->type->type_kind < TYPE_U8
? type_signed_int_by_bitsize(bit_size_right)
: type_unsigned_int_by_bitsize(bit_size_right);
bool success = cast_implicit(left, to);
assert(success);
return;
}
Type *to = right->type->type_kind < TYPE_U8
? type_signed_int_by_bitsize(bit_size_right)
: type_unsigned_int_by_bitsize(bit_size_right);
bool success = cast_implicit(right, to);
assert(success);
}
/**
* Analyze a == b, a != b, a > b, a < b, a >= b, a <= b
* @return
*/
static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Expr *right)
{
// 1. Analyse left and right side without any conversions.
if (!sema_expr_analyse_binary_sub_expr(context, NULL, left, right)) return false;
bool is_equality_type_op = expr->binary_expr.operator == BINARYOP_NE || expr->binary_expr.operator == BINARYOP_EQ;
Type *left_type = left->type->canonical;
Type *right_type = right->type->canonical;
// 2. Handle the case of signed comparisons.
// This happens when either side has a definite integer type
// and those are either signed or unsigned.
// If either side is compint, then this does not happen.
if ((type_is_unsigned(left_type) && type_is_signed(right_type))
|| (type_is_signed(left_type) && type_is_unsigned(right_type)))
{
// 2a. Resize so that both sides have the same bit width. This will always work.
cast_to_max_bit_size(left, right, left_type, right_type);
}
else
{
// 3. In the normal case, treat this as a binary op, finding the max type.
Type *max = type_find_max_type(left_type, right_type);
// 4. If no common type, then that's an error:
if (!max)
{
SEMA_ERROR(expr, "'%s' and '%s' are different types and cannot be compared.",
type_to_error_string(left->type), type_to_error_string(right->type));
};
// 5. Most types can do equality, but not all can do comparison,
// so we need to check that as well.
if (!is_equality_type_op)
{
switch (max->type_kind)
{
case TYPE_POISONED:
return false;
case TYPE_VOID:
case TYPE_TYPEDEF:
UNREACHABLE
case TYPE_BOOL:
case TYPE_ENUM:
case TYPE_ERROR:
case TYPE_FUNC:
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_ERROR_UNION:
case TYPE_STRING:
case TYPE_ARRAY:
case TYPE_VARARRAY:
case TYPE_SUBARRAY:
case TYPE_META_TYPE:
// Only != and == allowed.
goto ERR;
case ALL_INTS:
case ALL_FLOATS:
// All comparisons allowed
break;
case TYPE_POINTER:
// Only comparisons between the same type is allowed. Subtypes not allowed.
if (left_type != right_type)
{
SEMA_ERROR(expr, "Cannot compare pointers of different types.");
return false;
}
break;
}
}
// 6. Do the implicit cast.
if (!cast_implicit(left, max)) goto ERR;
if (!cast_implicit(right, max)) goto ERR;
}
// 7. Do constant folding.
if (both_const(left, right))
{
expr->const_expr.b = expr_const_compare(&left->const_expr, &right->const_expr, expr->binary_expr.operator);
expr->const_expr.kind = TYPE_BOOL;
expr->expr_kind = EXPR_CONST;
}
// 8. Set the type to bool
expr->type = type_bool;
return true;
ERR:
SEMA_ERROR(expr, "Cannot evaluate '%s' %s '%s'", type_to_error_string(left_type), token_type_to_string(binaryop_to_token(expr->binary_expr.operator)), type_to_error_string(right_type));
return false;
}
/**
* Analyse *a
* @return true if analysis succeeds.
*/
static bool sema_expr_analyse_deref(Context *context, Expr *expr, Expr *inner)
{
Type *canonical = inner->type->canonical;
// 1. Check that we have a pointer, or dereference is not allowed.
if (canonical->type_kind != TYPE_POINTER)
{
SEMA_ERROR(inner, "Cannot dereference a value of type '%s'", type_to_error_string(inner->type));
return false;
}
// 2. This could be a constant, in which case it is a nil which is an error.
if (inner->expr_kind == EXPR_CONST)
{
SEMA_ERROR(inner, "Dereferencing nil is not allowed.");
return false;
}
// 3. Now the type might not be a pointer because of a typedef,
// otherwise we need to use the the canonical representation.
Type *deref_type = inner->type->type_kind != TYPE_POINTER ? inner->type : canonical;
// 4. And... set the type.
expr->type = deref_type->pointer;
return true;
}
/**
* Analyse &a
* @return true if analysis succeeds.
*/
static bool sema_expr_analyse_addr(Context *context, Expr *expr, Expr *inner)
{
// 1. Check that it is an lvalue.
if (!expr_is_ltype(inner))
{
SEMA_ERROR(inner, "It is not possible to take the address of values, only of variables and memory locations.", type_to_error_string(inner->type));
return false;
}
// 2. Get the pointer of the underlying type.
expr->type = type_get_ptr(inner->type);
return true;
}
static bool sema_expr_analyse_neg(Context *context, Type *to, Expr *expr, Expr *inner)
{
Type *canonical = inner->type->canonical;
if (!builtin_may_negate(canonical))
{
SEMA_ERROR(expr, "Cannot negate %s.", type_to_error_string(inner->type));
return false;
}
if (inner->expr_kind != EXPR_CONST)
{
expr->type = inner->type;
return true;
}
bool is_negmod = expr->unary_expr.operator == UNARYOP_NEGMOD;
expr_replace(expr, inner);
switch (expr->const_expr.kind)
{
case ALL_INTS:
if (is_negmod)
{
if (canonical->type_kind != TYPE_IXX)
{
SEMA_ERROR(expr, "Cannot use % on compile time integers, you need to first cast it to an integer type e.g. -%cast(-128, char).");
// Continue parsing, pretending this is a -
bigint_negate(&expr->const_expr.i, &inner->const_expr.i);
return true;
}
bigint_negate_wrap(&expr->const_expr.i,
&inner->const_expr.i,
inner->type->canonical->builtin.bitsize);
return true;
}
bigint_negate(&expr->const_expr.i, &inner->const_expr.i);
if (expr_const_int_overflowed(&expr->const_expr))
{
SEMA_ERROR(expr, "Negating %s overflows '%s'.", expr_const_to_error_string(&expr->const_expr), type_to_error_string(expr->type));
return false;
}
return true;
case ALL_FLOATS:
expr->const_expr.f = -expr->const_expr.f;
break;
default:
UNREACHABLE
}
return true;
}
/**
* Analyse ~x and ~123
*
* @return
*/
static bool sema_expr_analyse_bit_not(Context *context, Type *to, Expr *expr, Expr *inner)
{
Type *canonical = inner->type->canonical;
if (!type_is_any_integer(canonical) && canonical != type_bool)
{
SEMA_ERROR(expr, "Cannot bit negate '%s'.", type_to_error_string(inner->type));
return false;
}
// The simple case, non-const.
if (inner->expr_kind != EXPR_CONST)
{
expr->type = inner->type;
return true;
}
expr_replace(expr, inner);
switch (expr->const_expr.kind)
{
case ALL_SIGNED_INTS:
case ALL_UNSIGNED_INTS:
bigint_negate_wrap(&expr->const_expr.i, &inner->const_expr.i, canonical->builtin.bitsize);
break;
case TYPE_IXX:
bigint_negate(&expr->const_expr.i, &inner->const_expr.i);
break;
case TYPE_BOOL:
expr->const_expr.b = !expr->const_expr.b;
break;
default:
UNREACHABLE
}
return true;
}
static bool sema_expr_analyse_not(Context *context, Type *to, Expr *expr, Expr *inner)
{
expr->type = type_bool;
if (inner->expr_kind == EXPR_CONST)
{
switch (expr->const_expr.kind)
{
case ALL_INTS:
expr->const_expr.b = bigint_cmp_zero(&inner->const_expr.i) == CMP_EQ;
break;
case TYPE_BOOL:
expr->const_expr.b = !inner->const_expr.b;
break;
case ALL_FLOATS:
expr->const_expr.b = inner->const_expr.f == 0.0;
break;
case TYPE_STRING:
expr->const_expr.b = !inner->const_expr.string.len;
break;
case TYPE_ERROR:
case TYPE_ENUM:
TODO
default:
UNREACHABLE
}
expr->const_expr.kind = TYPE_BOOL;
expr->expr_kind = EXPR_CONST;
return true;
}
Type *canonical = inner->type->canonical;
switch (canonical->type_kind)
{
case TYPE_POISONED:
case TYPE_IXX:
case TYPE_FXX:
case TYPE_TYPEDEF:
case TYPE_ERROR_UNION:
UNREACHABLE
case TYPE_FUNC:
case TYPE_ARRAY:
case TYPE_POINTER:
case TYPE_VARARRAY:
case TYPE_SUBARRAY:
case TYPE_BOOL:
case TYPE_I8:
case TYPE_I16:
case TYPE_I32:
case TYPE_I64:
case TYPE_U8:
case TYPE_U16:
case TYPE_U32:
case TYPE_U64:
case TYPE_F32:
case TYPE_F64:
return true;
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_VOID:
case TYPE_STRING:
case TYPE_ENUM:
case TYPE_ERROR:
case TYPE_META_TYPE:
SEMA_ERROR(expr, "Cannot use 'not' on %s", type_to_error_string(inner->type));
return false;
}
UNREACHABLE
}
static inline bool sema_expr_analyse_incdec(Context *context, Type *to, Expr *expr, Expr *inner)
{
if (!expr_is_ltype(inner))
{
SEMA_ERROR(inner, "Expression cannot be assigned to.");
return false;
}
if (!type_is_numeric(inner->type->canonical) && inner->type->canonical->type_kind != TYPE_POINTER)
{
SEMA_ERROR(inner, "Expression must be a number or a pointer.");
return false;
}
expr->type = inner->type;
return true;
}
static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *expr)
{
assert(expr->resolve_status == RESOLVE_RUNNING);
Expr *left = expr->binary_expr.left;
Expr *right = expr->binary_expr.right;
switch (expr->binary_expr.operator)
{
case BINARYOP_ASSIGN:
return sema_expr_analyse_assign(context, expr, left, right);
case BINARYOP_MULT:
case BINARYOP_MULT_MOD:
return sema_expr_analyse_mult(context, to, expr, left, right);
case BINARYOP_ADD:
case BINARYOP_ADD_MOD:
return sema_expr_analyse_add(context, to, expr, left, right);
case BINARYOP_ADD_ASSIGN:
case BINARYOP_ADD_MOD_ASSIGN:
case BINARYOP_SUB_ASSIGN:
case BINARYOP_SUB_MOD_ASSIGN:
return sema_expr_analyse_add_sub_assign(context, expr, left, right);
case BINARYOP_SUB:
case BINARYOP_SUB_MOD:
return sema_expr_analyse_sub(context, to, expr, left, right);
case BINARYOP_DIV:
return sema_expr_analyse_div(context, to, expr, left, right);
case BINARYOP_MULT_ASSIGN:
case BINARYOP_DIV_ASSIGN:
return sema_expr_analyse_common_assign(context, expr, left, right, false);
case BINARYOP_MULT_MOD_ASSIGN:
case BINARYOP_BIT_AND_ASSIGN:
case BINARYOP_BIT_OR_ASSIGN:
case BINARYOP_BIT_XOR_ASSIGN:
case BINARYOP_MOD_ASSIGN:
return sema_expr_analyse_common_assign(context, expr, left, right, true);
case BINARYOP_MOD:
return sema_expr_analyse_mod(context, to, expr, left, right);
case BINARYOP_AND:
return sema_expr_analyse_and(context, expr, left, right);
case BINARYOP_OR:
return sema_expr_analyse_or(context, expr, left, right);
case BINARYOP_BIT_OR:
case BINARYOP_BIT_XOR:
case BINARYOP_BIT_AND:
return sema_expr_analyse_bit(context, to, expr, left, right);
case BINARYOP_NE:
case BINARYOP_EQ:
case BINARYOP_GT:
case BINARYOP_GE:
case BINARYOP_LT:
case BINARYOP_LE:
return sema_expr_analyse_comp(context, expr, left, right);
case BINARYOP_SHR:
case BINARYOP_SHL:
return sema_expr_analyse_shift(context, to, expr, left, right);
case BINARYOP_SHR_ASSIGN:
case BINARYOP_SHL_ASSIGN:
return sema_expr_analyse_shift_assign(context, expr, left, right);
case BINARYOP_ERROR:
break;
}
UNREACHABLE
}
static inline bool sema_expr_analyse_unary(Context *context, Type *to, Expr *expr)
{
assert(expr->resolve_status == RESOLVE_RUNNING);
Expr *inner = expr->unary_expr.expr;
if (!sema_analyse_expr(context, NULL, inner)) return false;
switch (expr->unary_expr.operator)
{
case UNARYOP_DEREF:
return sema_expr_analyse_deref(context, expr, inner);
case UNARYOP_ADDR:
return sema_expr_analyse_addr(context, expr, inner);
case UNARYOP_NEG:
case UNARYOP_NEGMOD:
return sema_expr_analyse_neg(context, to, expr, inner);
case UNARYOP_BITNEG:
return sema_expr_analyse_bit_not(context, to, expr, inner);
case UNARYOP_NOT:
return sema_expr_analyse_not(context, to, expr, inner);
case UNARYOP_DEC:
case UNARYOP_INC:
return sema_expr_analyse_incdec(context, to, expr, inner);
case UNARYOP_ERROR:
return false;
}
UNREACHABLE
}
static inline bool sema_expr_analyse_post_unary(Context *context, Type *to, Expr *expr)
{
assert(expr->resolve_status == RESOLVE_RUNNING);
Expr *inner = expr->post_expr.expr;
if (!sema_analyse_expr(context, NULL, inner)) return false;
return sema_expr_analyse_incdec(context, to, expr, inner);
}
static inline bool sema_expr_analyse_try(Context *context, Type *to, Expr *expr)
{
context->try_nesting++;
// Duplicates code in try for statements.. :(
unsigned prev_throws = vec_size(context->error_calls);
bool success = sema_analyse_expr(context, to, expr->try_expr.expr);
context->try_nesting--;
if (!success) return false;
unsigned new_throws = vec_size(context->error_calls);
if (new_throws == prev_throws)
{
if (expr->try_expr.type == TRY_STMT)
{
SEMA_ERROR(expr->try_expr.stmt, "No error to 'try' in the statement that follows, please remove the 'try'.");
}
else
{
SEMA_ERROR(expr->try_expr.expr, "No error to 'try' in the expression, please remove the 'try'.");
}
return false;
}
expr->type = expr->try_expr.expr->type;
bool found = false;
for (unsigned i = prev_throws; i < new_throws; i++)
{
// At least one uncaught error found!
if (!context->error_calls[i].throw_info->is_completely_handled)
{
found = true;
break;
}
}
if (expr->try_expr.else_expr)
{
CatchInfo info = { .kind = CATCH_TRY_ELSE, .try_else = expr };
// Absorb all errors.
for (unsigned i = prev_throws; i < new_throws; i++)
{
Throw *throw = &context->error_calls[i];
// Skip handled errors
if (throw[i].throw_info->is_completely_handled) continue;
throw->throw_info->is_completely_handled = true;
vec_add(throw->throw_info->catches, info);
}
// Resize to remove the throws from consideration.
vec_resize(context->error_calls, prev_throws);
if (!sema_analyse_expr(context, to, expr->try_expr.else_expr)) return false;
}
if (!found)
{
if (expr->try_expr.type == TRY_STMT)
{
SEMA_ERROR(expr->try_expr.stmt, "All errors in the following statement was caught, please remove the 'try'.");
}
else
{
SEMA_ERROR(expr->try_expr.expr, "All errors in the expression was caught, please remove the 'try'.");
}
return false;
}
return true;
}
static Ast *ast_shallow_copy(Ast *source)
{
Ast *copy = malloc_arena(sizeof(Ast));
memcpy(copy, source, sizeof(Ast));
return copy;
}
static Expr *expr_shallow_copy(Expr *source)
{
Expr *copy = malloc_arena(sizeof(Expr));
memcpy(copy, source, sizeof(Expr));
return copy;
}
static TypeInfo *type_info_copy_from_macro(Context *context, Expr *macro, TypeInfo *source)
{
if (!source) return NULL;
TypeInfo *copy = malloc_arena(sizeof(TypeInfo));
memcpy(copy, source, sizeof(TypeInfo));
switch (source->kind)
{
case TYPE_INFO_POISON:
return copy;
case TYPE_INFO_IDENTIFIER:
assert(source->resolve_status == RESOLVE_NOT_DONE);
TODO
break;
case TYPE_INFO_EXPRESSION:
assert(source->resolve_status == RESOLVE_NOT_DONE);
copy->unresolved_type_expr = expr_copy_from_macro(context, macro, source->unresolved_type_expr);
return copy;
case TYPE_INFO_ARRAY:
assert(source->resolve_status == RESOLVE_NOT_DONE);
copy->array.len = expr_copy_from_macro(context, macro, source->array.len);
copy->array.base = type_info_copy_from_macro(context, macro, source->array.base);
return copy;
case TYPE_INFO_INC_ARRAY:
assert(source->resolve_status == RESOLVE_NOT_DONE);
copy->array.base = type_info_copy_from_macro(context, macro, source->array.base);
return copy;
case TYPE_INFO_POINTER:
assert(source->resolve_status == RESOLVE_NOT_DONE);
copy->pointer = type_info_copy_from_macro(context, macro, source->pointer);
return copy;
}
UNREACHABLE
}
static Ast** ast_copy_list_from_macro(Context *context, Expr *macro, Ast **to_copy)
{
Ast **result = NULL;
VECEACH(to_copy, i)
{
vec_add(result, ast_copy_from_macro(context, macro, to_copy[i]));
}
return result;
}
static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_expr)
{
if (!source_expr) return NULL;
Expr *expr = expr_shallow_copy(source_expr);
switch (source_expr->expr_kind)
{
case EXPR_TYPEOF:
MACRO_COPY_EXPR(expr->typeof_expr);
return expr;
case EXPR_COMPOUND_LITERAL:
MACRO_COPY_EXPR(expr->expr_compound_literal.initializer);
MACRO_COPY_TYPE(expr->expr_compound_literal.type_info);
return expr;
case EXPR_DESIGNATED_INITIALIZER:
// Created during semantic analysis
UNREACHABLE
case EXPR_RANGE:
MACRO_COPY_EXPR(expr->range_expr.left);
MACRO_COPY_EXPR(expr->range_expr.right);
return expr;
case EXPR_EXPR_BLOCK:
MACRO_COPY_AST_LIST(expr->expr_block.stmts);
return expr;
case EXPR_POISONED:
return source_expr;
case EXPR_TRY:
MACRO_COPY_EXPR(expr->try_expr.expr);
MACRO_COPY_EXPR(expr->try_expr.else_expr);
return expr;
case EXPR_CONST:
return expr;
case EXPR_BINARY:
MACRO_COPY_EXPR(expr->binary_expr.left);
MACRO_COPY_EXPR(expr->binary_expr.right);
return expr;
case EXPR_TERNARY:
MACRO_COPY_EXPR(expr->ternary_expr.cond);
MACRO_COPY_EXPR(expr->ternary_expr.then_expr);
MACRO_COPY_EXPR(expr->ternary_expr.else_expr);
return expr;
case EXPR_UNARY:
MACRO_COPY_EXPR(expr->unary_expr.expr);
return expr;
case EXPR_POST_UNARY:
MACRO_COPY_EXPR(expr->post_expr.expr);
return expr;
case EXPR_TYPEID:
MACRO_COPY_TYPE(expr->typeid_expr);
return expr;
case EXPR_IDENTIFIER:
TODO
break;
case EXPR_TYPE_ACCESS:
MACRO_COPY_TYPE(expr->type_access.type);
return expr;
case EXPR_CALL:
MACRO_COPY_EXPR(expr->call_expr.function);
MACRO_COPY_EXPR_LIST(expr->call_expr.arguments);
return expr;
case EXPR_SUBSCRIPT:
MACRO_COPY_EXPR(expr->subscript_expr.expr);
MACRO_COPY_EXPR(expr->subscript_expr.index);
return expr;
case EXPR_GROUP:
MACRO_COPY_EXPR(expr->group_expr->group_expr);
return expr;
case EXPR_ACCESS:
MACRO_COPY_EXPR(expr->access_expr.parent);
return expr;
case EXPR_INITIALIZER_LIST:
MACRO_COPY_EXPR_LIST(expr->expr_initializer.initializer_expr);
return expr;
case EXPR_EXPRESSION_LIST:
MACRO_COPY_EXPR_LIST(expr->expression_list);
return expr;
case EXPR_CAST:
MACRO_COPY_EXPR(expr->cast_expr.expr);
MACRO_COPY_TYPE(expr->cast_expr.type_info);
return expr;
case EXPR_SCOPED_EXPR:
MACRO_COPY_EXPR(expr->expr_scope.expr);
return expr;
case EXPR_MACRO_EXPR:
MACRO_COPY_EXPR(expr->macro_expr);
return expr;
}
UNREACHABLE
}
static Expr **expr_copy_expr_list_from_macro(Context *context, Expr *macro, Expr **expr_list)
{
Expr **result = NULL;
VECEACH(expr_list, i)
{
vec_add(result, expr_copy_from_macro(context, macro, expr_list[i]));
}
return result;
}
static TypeInfo** type_info_copy_list_from_macro(Context *context, Expr *macro, TypeInfo **to_copy)
{
TypeInfo **result = NULL;
VECEACH(to_copy, i)
{
vec_add(result, type_info_copy_from_macro(context, macro, to_copy[i]));
}
return result;
}
static Ast *ast_copy_from_macro(Context *context, Expr *macro, Ast *source)
{
Ast *ast = ast_shallow_copy(source);
switch (source->ast_kind)
{
case AST_POISONED:
return ast;
case AST_ASM_STMT:
TODO
case AST_ATTRIBUTE:
UNREACHABLE
case AST_BREAK_STMT:
return ast;
case AST_CASE_STMT:
MACRO_COPY_AST(ast->case_stmt.body);
MACRO_COPY_EXPR(ast->case_stmt.expr);
return ast;
break;
case AST_CATCH_STMT:
MACRO_COPY_AST(ast->catch_stmt.body);
return ast;
case AST_COMPOUND_STMT:
MACRO_COPY_AST_LIST(ast->compound_stmt.stmts);
return ast;
case AST_CONTINUE_STMT:
return ast;
case AST_CT_IF_STMT:
MACRO_COPY_EXPR(ast->ct_if_stmt.expr);
MACRO_COPY_AST(ast->ct_if_stmt.elif);
MACRO_COPY_AST(ast->ct_if_stmt.then);
return ast;
case AST_CT_ELIF_STMT:
MACRO_COPY_EXPR(ast->ct_elif_stmt.expr);
MACRO_COPY_AST(ast->ct_elif_stmt.then);
MACRO_COPY_AST(ast->ct_elif_stmt.elif);
return ast;
case AST_CT_ELSE_STMT:
MACRO_COPY_AST(ast->ct_else_stmt);
return ast;
case AST_CT_FOR_STMT:
MACRO_COPY_AST(ast->ct_for_stmt.body);
MACRO_COPY_EXPR(ast->ct_for_stmt.expr);
return ast;
case AST_CT_SWITCH_STMT:
MACRO_COPY_EXPR(ast->ct_switch_stmt.cond);
MACRO_COPY_AST_LIST(ast->ct_switch_stmt.body);
return ast;
case AST_CT_DEFAULT_STMT:
MACRO_COPY_AST(ast->ct_default_stmt);
return ast;
case AST_CT_CASE_STMT:
MACRO_COPY_AST(ast->ct_case_stmt.body);
MACRO_COPY_TYPE_LIST(ast->ct_case_stmt.types);
return ast;
case AST_DECLARE_STMT:
TODO
return ast;
case AST_DEFAULT_STMT:
MACRO_COPY_AST(ast->case_stmt.body);
return ast;
case AST_DEFER_STMT:
assert(!ast->defer_stmt.prev_defer);
MACRO_COPY_AST(ast->defer_stmt.body);
return ast;
case AST_DO_STMT:
MACRO_COPY_AST(ast->do_stmt.body);
MACRO_COPY_EXPR(ast->do_stmt.expr);
return ast;
case AST_EXPR_STMT:
MACRO_COPY_EXPR(ast->expr_stmt);
return ast;
case AST_FOR_STMT:
MACRO_COPY_EXPR(ast->for_stmt.cond);
MACRO_COPY_EXPR(ast->for_stmt.incr);
MACRO_COPY_AST(ast->for_stmt.body);
MACRO_COPY_AST(ast->for_stmt.init);
return ast;
case AST_GENERIC_CASE_STMT:
MACRO_COPY_AST(ast->generic_case_stmt.body);
// ast->generic_case_stmt.types = ...
TODO
return ast;
case AST_GENERIC_DEFAULT_STMT:
MACRO_COPY_AST(ast->generic_default_stmt);
return ast;
case AST_GOTO_STMT:
MACRO_COPY_AST(ast->goto_stmt.label);
// TODO fixup name, which needs to be macro local.
TODO
return ast;
case AST_IF_STMT:
MACRO_COPY_AST(ast->if_stmt.cond);
MACRO_COPY_AST(ast->if_stmt.decl);
MACRO_COPY_AST(ast->if_stmt.else_body);
MACRO_COPY_AST(ast->if_stmt.then_body);
return ast;
case AST_LABEL:
assert(!ast->label_stmt.defer);
assert(!ast->label_stmt.in_defer);
// TODO fixup name which needs to be macro local.
TODO
return ast;
case AST_NOP_STMT:
return ast;
case AST_RETURN_STMT:
MACRO_COPY_EXPR(ast->return_stmt.expr);
// TODO handle conversions?
TODO
return ast;
case AST_DECL_EXPR_LIST:
MACRO_COPY_AST_LIST(ast->decl_expr_stmt);
return ast;
case AST_SWITCH_STMT:
MACRO_COPY_AST(ast->switch_stmt.decl);
MACRO_COPY_AST(ast->switch_stmt.cond);
MACRO_COPY_AST_LIST(ast->switch_stmt.cases);
return ast;
case AST_THROW_STMT:
MACRO_COPY_EXPR(ast->throw_stmt.throw_value);
return ast;
case AST_NEXT_STMT:
TODO
return ast;
case AST_VOLATILE_STMT:
TODO
return ast;
case AST_WHILE_STMT:
MACRO_COPY_AST(ast->while_stmt.cond);
MACRO_COPY_AST(ast->while_stmt.decl);
MACRO_COPY_AST(ast->while_stmt.body);
return ast;
case AST_SCOPED_STMT:
MACRO_COPY_AST(ast->scoped_stmt.stmt);
return ast;
}
UNREACHABLE;
}
static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *macro, Expr *inner)
{
Expr *func_expr = inner->call_expr.function;
if (!sema_analyse_expr(context, NULL, func_expr)) return false;
Decl *decl;
switch (func_expr->expr_kind)
{
case EXPR_TYPE_ACCESS:
TODO
case EXPR_IDENTIFIER:
decl = func_expr->identifier_expr.decl;
break;
default:
TODO
}
if (decl->decl_kind != DECL_MACRO)
{
SEMA_ERROR(macro, "A macro was expected here.");
return false;
}
Expr **args =func_expr->call_expr.arguments;
Decl **func_params = decl->macro_decl.parameters;
// TODO handle bare macros.
// TODO handle $ args and # args
unsigned num_args = vec_size(args);
// unsigned num_params = vec_size(func_params);
for (unsigned i = 0; i < num_args; i++)
{
Expr *arg = args[i];
Decl *param = func_params[i];
if (!sema_analyse_expr(context, param->type, arg)) return false;
}
Ast *body = ast_copy_from_macro(context, inner, decl->macro_decl.body);
TODO
}
static inline bool sema_expr_analyse_macro_call2(Context *context, Type *to, Expr *expr, Decl *macro)
{
Ast *macro_parent;
// TODO handle loops
Decl *stored_macro = context->evaluating_macro;
Type *stored_rtype = context->rtype;
context->evaluating_macro = macro;
context->rtype = macro->macro_decl.rtype->type;
// Handle escaping macro
bool success = sema_analyse_statement(context, macro->macro_decl.body);
context->evaluating_macro = stored_macro;
context->rtype = stored_rtype;
if (!success) return false;
TODO
return success;
}
static inline bool sema_expr_analyse_macro_expr(Context *context, Type *to, Expr *expr)
{
Expr *inner = expr->macro_expr;
switch (inner->expr_kind)
{
case EXPR_CALL:
return sema_expr_analyse_macro_call(context, to, expr, inner);
case EXPR_ACCESS:
case EXPR_IDENTIFIER:
// Allow @f unrolling?
default:
SEMA_ERROR(expr, "Expected a macro name after '@'");
return false;
}
}
static inline bool sema_expr_analyse_type(Context *context, Type *to, Expr *expr)
{
if (!sema_resolve_type_info(context, expr->typeid_expr))
{
return expr_poison(expr);
}
expr->type = type_typeid;
return true;
}
static inline Ast **context_push_returns(Context *context)
{
Ast** old_returns = context->returns;
if (context->returns_cache)
{
context->returns = context->returns_cache;
context->returns_cache = NULL;
vec_resize(context->returns, 0);
}
else
{
context->returns = NULL;
}
return old_returns;
}
static inline void context_pop_returns(Context *context, Ast **restore)
{
if (!context->returns_cache && context->returns)
{
context->returns_cache = context->returns;
}
context->returns = restore;
}
static inline bool sema_expr_analyse_expr_block(Context *context, Type *to, Expr *expr)
{
bool success = true;
expr->type = type_void;
Type *prev_expected_block_type = context->expected_block_type;
Ast **saved_returns = context_push_returns(context);
context->expected_block_type = to;
context_push_scope_with_flags(context, SCOPE_EXPR_BLOCK);
VECEACH(expr->expr_block.stmts, i)
{
if (!sema_analyse_statement(context, expr->expr_block.stmts[i]))
{
success = false;
goto EXIT;
}
}
if (!vec_size(context->returns))
{
if (to)
{
SEMA_ERROR(expr, "Expected at least one return out of expression block to return a value of type %s.", type_to_error_string(to));
success = false;
}
goto EXIT;
}
Expr *first_return_expr = context->returns[0]->return_stmt.expr;
Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void;
bool all_returns_needs_casts = false;
// Let's unify the return statements.
VECEACH(context->returns, i)
{
Ast *return_stmt = context->returns[i];
Expr *ret_expr = return_stmt->return_stmt.expr;
bool last_expr_was_void = left_canonical == type_void;
Type *right_canonical = ret_expr ? ret_expr->type->canonical : type_void;
bool current_expr_was_void = right_canonical == type_void;
if (i > 0 && last_expr_was_void != current_expr_was_void)
{
SEMA_ERROR(return_stmt, "You can't combine empty returns with value returns.");
SEMA_PREV(context->returns[i - 1], "Previous return was here.");
success = false;
goto EXIT;
}
if (to)
{
if (current_expr_was_void)
{
SEMA_ERROR(return_stmt, "The return must be a value of type '%s'.", type_to_error_string(to));
success = false;
goto EXIT;
}
if (!cast_implicit(ret_expr, to))
{
success = false;
goto EXIT;
}
continue;
}
// The simple case.
if (left_canonical == right_canonical) continue;
// Try to find a common type:
Type *max = type_find_max_type(left_canonical, right_canonical);
if (!max)
{
SEMA_ERROR(return_stmt, "Cannot find a common parent type of '%s' and '%s'",
type_to_error_string(left_canonical), type_to_error_string(right_canonical));
SEMA_PREV(context->returns[i - 1], "The previous return was here.");
success = false;
goto EXIT;
}
left_canonical = max;
all_returns_needs_casts = true;
}
if (all_returns_needs_casts)
{
VECEACH(context->returns, i)
{
Ast *return_stmt = context->returns[i];
Expr *ret_expr = return_stmt->return_stmt.expr;
if (!cast_implicit(ret_expr, left_canonical))
{
success = false;
goto EXIT;
}
}
}
expr->type = left_canonical;
EXIT:
context_pop_scope(context);
context_pop_returns(context, saved_returns);
context->expected_block_type = prev_expected_block_type;
return success;
}
static inline bool sema_expr_analyse_range(Context *context, Type *to, Expr *expr)
{
TODO
}
static inline bool sema_expr_analyse_compound_literal(Context *context, Type *to, Expr *expr)
{
if (!sema_resolve_type_info(context, expr->expr_compound_literal.type_info)) return false;
Type *type = expr->expr_compound_literal.type_info->type;
if (!sema_expr_analyse_initializer_list(context, type, expr->expr_compound_literal.initializer)) return false;
expr->type = type;
return true;
}
static inline bool sema_expr_analyse_typeof(Context *context, Expr *expr)
{
if (!sema_analyse_expr(context, NULL, expr->typeof_expr)) return false;
Type *type = expr->typeof_expr->type->canonical;
expr->expr_kind = EXPR_TYPEID;
expr->typeid_expr = type_info_new_base(type, expr->typeof_expr->span);
expr->type = type_typeid;
return true;
}
static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *expr)
{
switch (expr->expr_kind)
{
case EXPR_POISONED:
return false;
case EXPR_DESIGNATED_INITIALIZER:
// Created during semantic analysis
UNREACHABLE
case EXPR_SCOPED_EXPR:
UNREACHABLE
case EXPR_TYPEOF:
return sema_expr_analyse_typeof(context, expr);
case EXPR_COMPOUND_LITERAL:
return sema_expr_analyse_compound_literal(context, to, expr);
case EXPR_EXPR_BLOCK:
return sema_expr_analyse_expr_block(context, to, expr);
case EXPR_MACRO_EXPR:
return sema_expr_analyse_macro_expr(context, to, expr);
case EXPR_TRY:
return sema_expr_analyse_try(context, to, expr);
case EXPR_RANGE:
return sema_expr_analyse_range(context, to, expr);
case EXPR_CONST:
return true;
case EXPR_BINARY:
return sema_expr_analyse_binary(context, to, expr);
case EXPR_TERNARY:
return sema_expr_analyse_ternary(context, to, expr);
case EXPR_UNARY:
return sema_expr_analyse_unary(context, to, expr);
case EXPR_POST_UNARY:
return sema_expr_analyse_post_unary(context, to, expr);
case EXPR_TYPEID:
return sema_expr_analyse_type(context, to, expr);
case EXPR_IDENTIFIER:
return sema_expr_analyse_identifier(context, to, expr);
case EXPR_TYPE_ACCESS:
return sema_expr_analyse_type_access(context, to, expr);
case EXPR_CALL:
return sema_expr_analyse_call(context, to, expr);
case EXPR_SUBSCRIPT:
return sema_expr_analyse_subscript(context, to, expr);
case EXPR_GROUP:
return sema_expr_analyse_group(context, to, expr);
case EXPR_ACCESS:
return sema_expr_analyse_access(context, expr);
case EXPR_INITIALIZER_LIST:
return sema_expr_analyse_initializer_list(context, to, expr);
case EXPR_CAST:
return sema_expr_analyse_cast(context, to, expr);
case EXPR_EXPRESSION_LIST:
return sema_expr_analyse_expr_list(context, to, expr);
}
UNREACHABLE
}
bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr)
{
if (!sema_analyse_expr(context, to, expr)) return false;
return cast_implicit(expr, to);
}
bool sema_analyse_expr(Context *context, Type *to, Expr *expr)
{
switch (expr->resolve_status)
{
case RESOLVE_NOT_DONE:
expr->resolve_status = RESOLVE_RUNNING;
break;
case RESOLVE_RUNNING:
SEMA_ERROR(expr, "Recursive resolution of expression");
return expr_poison(expr);
case RESOLVE_DONE:
return expr_ok(expr);
}
if (!sema_analyse_expr_dispatch(context, to, expr)) return expr_poison(expr);
expr->resolve_status = RESOLVE_DONE;
return to ? cast(expr, to, CAST_TYPE_OPTIONAL_IMPLICIT) : true;
}