Files
c3c/src/compiler/sema_expr.c

6403 lines
181 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 <math.h>
/*
* TODOs
* - Disallow jumping in and out of an expression block.
*/
static inline bool sema_cast_rvalue(Context *context, Type *to, Expr *expr);
int BINOP_PREC_REQ[BINARYOP_LAST] =
{
// bitwise operations
[BINARYOP_BIT_OR] = 1,
[BINARYOP_BIT_XOR] = 1,
[BINARYOP_BIT_AND] = 1,
// comparison operations
[BINARYOP_GT] = 2,
[BINARYOP_GE] = 2,
[BINARYOP_LT] = 2,
[BINARYOP_LE] = 2,
[BINARYOP_NE] = 2,
[BINARYOP_EQ] = 2,
[BINARYOP_SHR] = 3,
[BINARYOP_SHL] = 3,
};
static Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl)
{
Expr *embedded_struct = expr_new(EXPR_ACCESS, parent->span);
embedded_struct->resolve_status = RESOLVE_DONE;
embedded_struct->access_expr.parent = parent;
embedded_struct->access_expr.ref = parent_decl->strukt.members[0];
expr_set_type(embedded_struct, embedded_struct->access_expr.ref->type);
return embedded_struct;
}
static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *expr);
static inline bool expr_const_int_valid(Expr *expr, Type *type)
{
if (expr_const_int_overflowed(&expr->const_expr))
{
SEMA_ERROR(expr, "Cannot fit '%s' into type '%s'.", expr_const_to_error_string(&expr->const_expr), type_to_error_string(type));
return false;
}
if (bigint_cmp_zero(&expr->const_expr.i) == CMP_LT && type_kind_is_unsigned(expr->const_expr.kind))
{
SEMA_ERROR(expr, "'%s' underflows type '%s'.", expr_const_to_error_string(&expr->const_expr), type_to_error_string(type));
return false;
}
return true;
}
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(type_flatten(left->type)) && type_is_any_integer(type_flatten(right->type));
}
void expr_copy_properties(Expr *to, Expr *from)
{
to->failable = from->failable;
to->pure = from->pure;
to->constant = from->constant;
}
void expr_copy_types(Expr *to, Expr *from)
{
to->type = from->type;
to->original_type = from->original_type;
}
void expr_insert_addr(Expr *original)
{
assert(original->resolve_status == RESOLVE_DONE);
if (original->expr_kind == EXPR_UNARY && original->unary_expr.operator == UNARYOP_DEREF)
{
*original = *original->unary_expr.expr;
return;
}
Expr *inner = expr_alloc();
*inner = *original;
original->expr_kind = EXPR_UNARY;
expr_set_type(original, type_get_ptr(inner->type));
original->unary_expr.operator = UNARYOP_ADDR;
original->unary_expr.expr = inner;
original->pure = false;
original->constant = false;
original->failable = inner->failable;
}
Expr *expr_variable(Decl *decl)
{
Expr *expr = expr_new(EXPR_IDENTIFIER, decl->span);
expr->identifier_expr.decl = decl;
expr->pure = true;
expr->constant = false;
expr->resolve_status = RESOLVE_DONE;
expr_set_type(expr, decl->type);
return expr;
}
void expr_insert_deref(Expr *original)
{
assert(original->resolve_status == RESOLVE_DONE);
assert(original->type->canonical->type_kind == TYPE_POINTER);
if (original->expr_kind == EXPR_UNARY && original->unary_expr.operator == UNARYOP_ADDR)
{
*original = *original->unary_expr.expr;
return;
}
Type *pointee = original->type->type_kind == TYPE_POINTER ? original->type->pointer : original->type->canonical->pointer;
Expr *inner = expr_alloc();
*inner = *original;
original->expr_kind = EXPR_UNARY;
expr_set_type(original, pointee);
original->unary_expr.operator = UNARYOP_DEREF;
original->unary_expr.expr = inner;
original->pure = false;
original->constant = false;
original->failable = inner->failable;
}
static void expr_unify_binary_properties(Expr *expr, Expr *left, Expr *right)
{
expr->pure = left->pure & right->pure;
expr->constant = left->constant & right->constant;
expr->failable = left->failable | right->failable;
}
static void expr_unify_binary(Expr *expr, Expr *left, Expr *right)
{
expr->type = left->type;
expr->original_type = type_find_max_type(left->original_type->canonical, right->original_type->canonical);
expr_unify_binary_properties(expr, left, right);
}
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 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;
}
int sema_check_comp_time_bool(Context *context, Expr *expr)
{
if (!sema_analyse_expr_of_required_type(context, type_bool, expr, false)) return -1;
if (expr->expr_kind != EXPR_CONST)
{
SEMA_ERROR(expr, "Compile time evaluation requires a compile time constant value.");
return -1;
}
return expr->const_expr.b;
}
bool expr_is_ltype(Expr *expr)
{
switch (expr->expr_kind)
{
case EXPR_CONST_IDENTIFIER:
return false;
case EXPR_CT_IDENT:
return true;
case EXPR_IDENTIFIER:
{
Decl *decl = expr->identifier_expr.decl;
if (decl->decl_kind != DECL_VAR) return false;
decl = decl_raw(decl);
switch (decl->var.kind)
{
case VARDECL_LOCAL_CT:
case VARDECL_LOCAL_CT_TYPE:
case VARDECL_LOCAL:
case VARDECL_GLOBAL:
case VARDECL_PARAM:
case VARDECL_PARAM_REF:
return true;
case VARDECL_CONST:
case VARDECL_MEMBER:
case VARDECL_PARAM_CT:
case VARDECL_PARAM_CT_TYPE:
case VARDECL_PARAM_EXPR:
return false;
case VARDECL_UNWRAPPED:
case VARDECL_ERASE:
case VARDECL_REWRAPPED:
UNREACHABLE
}
}
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:
case EXPR_SLICE:
return true;
default:
return false;
}
}
static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr)
{
Decl *decl = expr->identifier_expr.decl;
decl = decl_flatten(decl);
expr->identifier_expr.is_rvalue = true;
switch (decl->decl_kind)
{
case DECL_FUNC:
SEMA_ERROR(expr, "Expected function followed by (...) or prefixed by &.");
return expr_poison(expr);
case DECL_MACRO:
SEMA_ERROR(expr, "Expected macro followed by (...) or prefixed by '&'.");
return expr_poison(expr);
case DECL_GENERIC:
SEMA_ERROR(expr, "Expected generic followed by (...) or prefixed by '&'.");
return expr_poison(expr);
case DECL_ERRVALUE:
SEMA_ERROR(expr, "Did you forget a '!' after '%s'?", decl->name);
return expr_poison(expr);
case DECL_ENUM_CONSTANT:
expr_replace(expr, decl->enum_constant.expr);
return true;
case DECL_VAR:
break;
case DECL_DISTINCT:
case DECL_TYPEDEF:
UNREACHABLE
case DECL_POISONED:
return expr_poison(expr);
case DECL_LABEL:
SEMA_ERROR(expr, "Did you intend to use the label '%s' here?", decl->name);
return expr_poison(expr);
case DECL_INTERFACE:
SEMA_ERROR(expr, "Expected interface followed by '.'.");
return expr_poison(expr);
case DECL_BITSTRUCT:
SEMA_ERROR(expr, "Expected bitstruct followed by (...) or '.'.");
return expr_poison(expr);
case DECL_STRUCT:
SEMA_ERROR(expr, "Expected struct followed by (...) or '.'.");
return expr_poison(expr);
case DECL_UNION:
SEMA_ERROR(expr, "Expected union followed by (...) or '.'.");
return expr_poison(expr);
case DECL_ENUM:
SEMA_ERROR(expr, "Expected enum name followed by '.' and an enum value.");
return expr_poison(expr);
case DECL_ERRTYPE:
SEMA_ERROR(expr, "Expected errtype name followed by '.' and an error value.");
return expr_poison(expr);
case DECL_ARRAY_VALUE:
UNREACHABLE
case DECL_IMPORT:
case DECL_CT_IF:
case DECL_CT_ELSE:
case DECL_CT_ELIF:
case DECL_CT_SWITCH:
case DECL_CT_CASE:
case DECL_ATTRIBUTE:
case DECL_CT_ASSERT:
case DECL_DEFINE:
UNREACHABLE
}
switch (decl->var.kind)
{
case VARDECL_CONST:
if (!type_is_builtin(decl->type->type_kind)) break;
expr_replace(expr, copy_expr(decl->var.init_expr));
return sema_analyse_expr(context, to, expr);
case VARDECL_PARAM_EXPR:
expr_replace(expr, copy_expr(decl->var.init_expr));
assert(decl->var.init_expr->resolve_status == RESOLVE_DONE);
return true;
case VARDECL_PARAM_CT_TYPE:
TODO
case VARDECL_PARAM_REF:
expr_replace(expr, copy_expr(decl->var.init_expr));
return sema_cast_rvalue(context, to, expr);
case VARDECL_PARAM:
case VARDECL_GLOBAL:
case VARDECL_LOCAL:
case VARDECL_UNWRAPPED:
break;
case VARDECL_MEMBER:
SEMA_ERROR(expr, "Expected '%s' followed by a method call or property.", decl->name);
return expr_poison(expr);
case VARDECL_PARAM_CT:
TODO
break;
case VARDECL_LOCAL_CT:
TODO
break;
case VARDECL_LOCAL_CT_TYPE:
TODO
break;
case VARDECL_ERASE:
case VARDECL_REWRAPPED:
UNREACHABLE
}
return true;
}
static ExprFailableStatus expr_is_failable(Expr *expr)
{
if (expr->expr_kind != EXPR_IDENTIFIER) return FAILABLE_NO;
Decl *decl = expr->identifier_expr.decl;
if (decl->decl_kind != DECL_VAR) return FAILABLE_NO;
if (decl->var.kind == VARDECL_UNWRAPPED && decl->var.alias->var.failable) return FAILABLE_UNWRAPPED;
return decl->var.failable ? FAILABLE_YES : FAILABLE_NO;
}
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_quoted_error_string(expr->binary_expr.left->type),
c, type_quoted_error_string(expr->binary_expr.right->type));
return false;
}
static bool expr_cast_to_index(Expr *index)
{
switch (index->type->canonical->type_kind)
{
case TYPE_IXX:
if (!bigint_fits_in_bits(&index->const_expr.i, 64, true))
{
SEMA_ERROR(index, "The index is out of range, it must fit in a signed 64 bit integer.");
return false;
}
return cast(index, type_iptrdiff);
case TYPE_I8:
case TYPE_I16:
case TYPE_I32:
case TYPE_I64:
return cast(index, type_iptrdiff);
case TYPE_U8:
case TYPE_U16:
case TYPE_U32:
case TYPE_U64:
return cast(index, type_uptrdiff);
case TYPE_U128:
SEMA_ERROR(index, "You need to explicitly cast this to a uint or ulong.");
return false;
case TYPE_I128:
SEMA_ERROR(index, "You need to explicitly cast this to a int or long.");
return false;
default:
SEMA_ERROR(index, "Cannot implicitly convert '%s' to an index.", type_to_error_string(index->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 (!cast_implicit(cond, type_bool)) return expr_poison(expr);
if (!sema_analyse_expr(context, to, left)) return expr_poison(expr);
expr->failable = left->failable | cond->failable;
}
else
{
// Elvis
if (!sema_analyse_expr(context, to, cond)) return expr_poison(expr);
expr->failable = cond->failable;
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);
expr->pure = cond->pure & left->pure & right->pure;
expr->constant = cond->constant & left->constant & right->pure;
expr->failable |= right->failable;
Type *left_canonical = left->type->canonical;
Type *right_canonical = right->type->canonical;
if (type_is_ct(left_canonical) && type_is_ct(right_canonical))
{
if (to)
{
if (!cast_implicit(left, to) || !cast_implicit(right, to)) return false;
}
else
{
if (!cast_implicitly_to_runtime(left) || !cast_implicitly_to_runtime(right)) return false;
}
left_canonical = left->type->canonical;
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_unify_binary(expr, left, right);
return true;
}
static inline Decl *decl_find_enum_constant(TokenId token, Decl *decl)
{
const char *name = TOKSTR(token);
VECEACH(decl->enums.values, i)
{
Decl *enum_constant = decl->enums.values[i];
if (enum_constant->name == name)
{
return enum_constant;
}
}
return NULL;
}
static inline bool sema_expr_analyse_enum_constant(Expr *expr, TokenId name, Decl *decl)
{
Decl *enum_constant = decl_find_enum_constant(name, decl);
if (!enum_constant) return false;
assert(enum_constant->resolve_status == RESOLVE_DONE);
expr_set_type(expr, decl->type);
expr->access_expr.ref = enum_constant;
expr->expr_kind = EXPR_MEMBER_ACCESS;
return true;
}
static inline bool find_possible_inferred_identifier(Type *to, Expr *expr)
{
if (to->canonical->type_kind != TYPE_ENUM && to->canonical->type_kind != TYPE_ERRTYPE) return false;
Decl *parent_decl = to->canonical->decl;
switch (parent_decl->decl_kind)
{
case DECL_ENUM:
case DECL_ERRTYPE:
return sema_expr_analyse_enum_constant(expr, expr->identifier_expr.identifier, parent_decl);
case DECL_UNION:
case DECL_STRUCT:
return false;
default:
UNREACHABLE
}
}
bool expr_is_constant_eval(Expr *expr)
{
// TODO rethink this.
switch (expr->expr_kind)
{
case EXPR_CONST:
return true;
case EXPR_COMPOUND_LITERAL:
return expr_is_constant_eval(expr->expr_compound_literal.initializer);
case EXPR_INITIALIZER_LIST:
{
Expr** init_exprs = expr->initializer_expr.initializer_expr;
switch (expr->initializer_expr.init_type)
{
case INITIALIZER_NORMAL:
{
VECEACH(init_exprs, i)
{
if (!expr_is_constant_eval(init_exprs[i])) return false;
}
return true;
}
default:
return false;
}
}
default:
return false;
}
}
static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr *expr)
{
Decl *ambiguous_decl = NULL;
Decl *private_symbol = NULL;
expr->pure = true;
DEBUG_LOG("Now resolving %s", TOKSTR(expr->identifier_expr.identifier));
Decl *decl = sema_resolve_normal_symbol(context,
expr->identifier_expr.identifier,
expr->identifier_expr.path,
false);
if (!decl_ok(decl)) return false;
if (!decl && !expr->identifier_expr.path && to)
{
if (find_possible_inferred_identifier(to, expr)) return true;
}
if (!decl)
{
decl = sema_resolve_normal_symbol(context,
expr->identifier_expr.identifier,
expr->identifier_expr.path,
true);
assert(!decl_ok(decl));
return false;
}
// Already handled
if (!decl_ok(decl)) 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->resolve_status != RESOLVE_DONE)
{
if (!sema_analyse_decl(context, decl)) return decl_poison(decl);
}
if (decl->decl_kind == DECL_VAR && decl->var.failable)
{
expr->failable = true;
}
if (decl->decl_kind == DECL_VAR)
{
switch (decl->var.kind)
{
case VARDECL_CONST:
if (!decl->type)
{
Expr *copy = copy_expr(decl->var.init_expr);
if (!sema_analyse_expr(context, to, copy)) return false;
if (!expr_is_constant_eval(copy))
{
SEMA_ERROR(expr, "Constant value did not evaluate to a constant.");
return false;
}
expr_replace(expr, copy);
return true;
}
if (decl->var.failable)
{
SEMA_ERROR(expr, "Constants may never be 'failable', please remove the '!'.");
return false;
}
break;
default:
break;
}
}
if (!decl->type) decl->type = type_void;
expr->identifier_expr.decl = decl;
expr_set_type(expr, decl->type);
expr->pure = true;
expr->constant = false;
DEBUG_LOG("Resolution successful of %s.", decl->name);
return true;
}
static inline bool sema_expr_analyse_macro_expansion(Context *context, Expr *expr)
{
Expr *inner = expr->macro_expansion_expr.inner;
if (inner->expr_kind == EXPR_IDENTIFIER)
{
if (!inner->identifier_expr.path && TOKSTR(inner->identifier_expr.identifier) == context->macro_scope.body_param)
{
expr->expr_kind = EXPR_MACRO_BODY_EXPANSION;
expr->body_expansion_expr.ast = NULL;
expr->body_expansion_expr.declarations = NULL;
expr->resolve_status = RESOLVE_NOT_DONE;
expr->type = expr->original_type = type_void;
return true;
}
}
if (!sema_analyse_expr_value(context, NULL, inner)) return false;
Decl *decl;
switch (inner->expr_kind)
{
case EXPR_IDENTIFIER:
decl = inner->identifier_expr.decl;
break;
case EXPR_ACCESS:
decl = inner->access_expr.ref;
break;
default:
SEMA_ERROR(expr, "Expected a macro identifier here.");
return false;
}
if (decl->decl_kind != DECL_MACRO)
{
SEMA_ERROR(inner, "Expected a macro identifier here.");
return false;
}
expr->macro_expansion_expr.decl = decl;
expr_copy_properties(expr, inner);
return true;
}
static inline bool sema_expr_analyse_ct_identifier(Context *context, Expr *expr)
{
expr->pure = true;
DEBUG_LOG("Now resolving %s", TOKSTR(expr->ct_ident_expr.identifier));
Decl *decl = sema_resolve_normal_symbol(context,
expr->ct_ident_expr.identifier,
NULL, true);
// Already handled
if (!decl_ok(decl))
{
return expr_poison(expr);
}
DEBUG_LOG("Resolution successful of %s.", decl->name);
assert(decl->decl_kind == DECL_VAR);
assert(decl->resolve_status == RESOLVE_DONE);
expr->ct_ident_expr.decl = decl;
expr_set_type(expr, decl->type);
expr->pure = true;
expr->constant = true;
return true;
}
static inline bool sema_expr_analyse_hash_identifier(Context *context, Type *to, Expr *expr)
{
expr->pure = true;
DEBUG_LOG("Now resolving %s", TOKSTR(expr->hash_ident_expr.identifier));
Decl *decl = sema_resolve_normal_symbol(context,
expr->hash_ident_expr.identifier,
NULL, true);
// Already handled
if (!decl_ok(decl))
{
return expr_poison(expr);
}
DEBUG_LOG("Resolution successful of %s.", decl->name);
assert(decl->decl_kind == DECL_VAR);
assert(decl->resolve_status == RESOLVE_DONE);
assert(decl->var.init_expr->resolve_status == RESOLVE_DONE);
expr_replace(expr, copy_expr(decl->var.init_expr));
return sema_analyse_expr(context, to, expr);
}
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 int find_index_of_named_parameter(Decl **func_params, Expr *expr)
{
if (vec_size(expr->designator_expr.path) != 1)
{
SEMA_ERROR(expr, "Expected the name of a function parameter here, this looks like a member path.");
return -1;
}
DesignatorElement *element = expr->designator_expr.path[0];
if (element->kind != DESIGNATOR_FIELD)
{
SEMA_ERROR(expr, "Expected the name of a function parameter here, this looks like an array path field.");
return -1;
}
const char *name = element->field;
VECEACH(func_params, i)
{
if (func_params[i]->name == name) return (int)i;
}
SEMA_ERROR(expr, "There's no parameter with the name '%s'.", name);
return -1;
}
static inline bool sema_expr_analyse_intrinsic_fp_invocation(Context *context, Expr *expr, Decl *decl, Type *to)
{
unsigned arguments = vec_size(expr->call_expr.arguments);
if (arguments != 1)
{
SEMA_ERROR(expr, "Expected 1 argument to intrinsic %s.", decl->name);
return false;
}
Expr *arg = expr->call_expr.arguments[0];
if (!sema_analyse_expr(context, to, arg)) return false;
// Convert ints to float comptime float.
if (type_is_any_integer(arg->type->canonical))
{
if (!cast_implicit(arg, type_compfloat)) return false;
}
// If this is not a float argument => error.
if (!type_is_float(arg->type->canonical))
{
SEMA_ERROR(arg, "Expected a floating point argument.", decl->name);
return false;
}
// We lower to a real float in case we got a compfloat.
if (!cast_implicitly_to_runtime(arg)) return false;
// The expression type is the argument type.
expr_set_type(expr, arg->type);
return true;
}
static inline bool sema_expr_analyse_intrinsic_invocation(Context *context, Expr *expr, Decl *decl, Type *to)
{
if (decl->name == kw___ceil || decl->name == kw___trunc || decl->name == kw___round || decl->name == kw___sqrt)
{
return sema_expr_analyse_intrinsic_fp_invocation(context, expr, decl, to);
}
UNREACHABLE
}
static inline bool expr_may_unpack_as_vararg(Expr *expr, Type *variadic_base_type)
{
Type *base_type = variadic_base_type->canonical;
Type *canonical = expr->type->canonical;
switch (canonical->type_kind)
{
case TYPE_ARRAY:
case TYPE_SUBARRAY:
return canonical->array.base == base_type;
case TYPE_POINTER:
if (canonical->pointer->type_kind == TYPE_ARRAY) return canonical->pointer->array.base == base_type;
return false;
default:
return false;
}
}
typedef struct
{
bool macro;
bool failable;
TokenId block_parameter;
Decl **params;
Expr *struct_var;
Variadic variadic;
} CalledDecl;
static inline bool expr_promote_vararg(Context *context, Expr *arg)
{
Type *arg_type = arg->type->canonical;
// 1. Is it compile time?
if (type_is_ct(arg_type))
{
// TODO Fix the int conversion.
// 1. Pick double / CInt
Type *target_type = type_is_any_integer(arg_type) ? type_cint() : type_double;
return cast_implicit(arg, target_type);
}
// 2. Promote any integer or bool to at least CInt
if (type_is_promotable_integer(arg_type) || arg_type == type_bool)
{
return cast(arg, type_cint());
}
// 3. Promote any float to at least double
if (type_is_promotable_float(arg->type))
{
return cast(arg, type_double);
}
return true;
}
static inline bool sema_check_invalid_body_arguments(Context *context, Expr *call, CalledDecl *callee)
{
Decl **body_arguments = call->call_expr.body_arguments;
bool has_body_arguments = vec_size(body_arguments) > 0;
// 1. Check if there are body arguments but no actual block.
if (has_body_arguments && !callee->block_parameter.index)
{
if (callee->macro)
{
SEMA_ERROR(body_arguments[0], "This macro does not support arguments.");
}
else
{
SEMA_ERROR(body_arguments[0], "Only macro calls may have body arguments for a trailing block.");
}
return false;
}
// 2. If there is a body then...
if (call->call_expr.body)
{
// 2a. If not a macro then this is an error.
if (!callee->macro)
{
SEMA_ERROR(call, "Only macro calls may take a trailing block.");
return false;
}
// 2b. If we don't have a block parameter, then this is an error as well
if (!callee->block_parameter.index)
{
SEMA_ERROR(call, "This macro does not support trailing statements, please remove it.");
return false;
}
// 2c. This is a macro and it has a block parameter. Everything is fine!
return true;
}
// 3. If we don't have a body, then if it has a block parameter this is an error.
if (callee->block_parameter.index)
{
assert(callee->macro);
SEMA_ERROR(call, "Expected call to have a trailing statement, did you forget to add it?");
return false;
}
// 4. No body and no block parameter, this is fine.
return true;
}
static inline bool sema_expand_call_arguments(Context *context, Expr *call, Decl **params, Expr **args, unsigned func_param_count, bool variadic)
{
unsigned num_args = vec_size(args);
// 1. We need at least as many function locations as we have parameters.
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);
// TODO this should not be needed:
memset(actual_args, 0, entries_needed * sizeof(Expr*));
// 2. Loop through the parameters.
bool uses_named_parameters = false;
for (unsigned i = 0; i < num_args; i++)
{
Expr *arg = args[i];
// 3. Handle named parameters
if (arg->expr_kind == EXPR_DESIGNATOR)
{
// 8a. We have named parameters, that will add some restrictions.
uses_named_parameters = true;
// 8b. Find the location of the parameter.
int index = find_index_of_named_parameter(params, arg);
// 8c. If it's not found then this is an error.
if (index < 0) return false;
// 8d. We might actually be finding the typed vararg at the end,
// this is an error.
if (params[index]->var.vararg)
{
SEMA_ERROR(arg, "Vararg parameters may not be named parameters, use normal parameters instead.", params[index]->name);
return false;
}
// 8e. We might have already set this parameter, that is not allowed.
if (actual_args[index])
{
SEMA_ERROR(arg, "The parameter '%s' was already set.", params[index]->name);
return false;
}
// 8g. Set the parameter and update failability.
actual_args[index] = arg->designator_expr.value;
call->failable |= arg->designator_expr.value->failable;
continue;
}
// 9. Check for previous use of named parameters, this is not allowed
// like foo(.a = 1, 3) => error.
if (uses_named_parameters)
{
SEMA_ERROR(call, "A regular parameter cannot follow a named parameter.");
return false;
}
// 10. If we exceed the function parameter count (remember we reduced this by one
// in the case of typed vararg) we're now in a variadic list.
if (i >= func_param_count)
{
// 11. We might have a typed variadic argument.
if (!variadic)
{
// 15. We have too many parameters...
SEMA_ERROR(arg, "This argument would would exceed the number of parameters, did you add to many arguments?");
return false;
}
// 11a. Look if we did an unsplat
if (call->call_expr.unsplat_last)
{
// 11b. Is this the last argument, or did we get some before the unpack?
if (i < num_args - 1)
{
SEMA_ERROR(arg,
"This looks like a variable argument before an unpacked variable which isn't allowed. Did you add too many arguments?");
return false;
}
}
}
actual_args[i] = arg;
}
// 17. Set default values.
for (unsigned i = 0; i < entries_needed; i++)
{
// 17a. Assigned a value - skip
if (actual_args[i]) continue;
// 17b. Set the init expression.
if (params[i]->var.init_expr)
{
assert(params[i]->var.init_expr->resolve_status == RESOLVE_DONE);
actual_args[i] = params[i]->var.init_expr;
continue;
}
// 17c. Vararg not set? That's fine.
if (params[i]->var.vararg) continue;
// 17d. Argument missing, that's bad.
SEMA_ERROR(call, "The mandatory parameter '%s' was not set, please add it.", params[i]->name);
return false;
}
call->call_expr.arguments = actual_args;
return true;
}
static inline bool sema_expr_analyse_call_invocation(Context *context, Expr *call, CalledDecl callee)
{
// 1. Check body arguments.
if (!sema_check_invalid_body_arguments(context, call, &callee)) return false;
call->failable = callee.failable;
// 2. Pick out all the arguments and parameters.
Expr **args = call->call_expr.arguments;
Decl **params = callee.params;
unsigned num_args = vec_size(args);
// 3. If this is a type call, then we have an implicit first argument.
if (callee.struct_var)
{
// 3a. Insert an argument first, by adding null to the end and then moving all arguments
// by one step.
vec_add(args, NULL);
for (unsigned i = num_args; i > 0; i--)
{
args[i] = args[i - 1];
}
// 3b. Then insert the argument.
args[0] = callee.struct_var;
num_args++;
call->call_expr.arguments = args;
call->call_expr.is_type_method = true;
assert(!call->call_expr.is_pointer_call);
}
// 4. Check for unsplat of the last argument.
bool unsplat = call->call_expr.unsplat_last;
if (unsplat)
{
// 4a. Is this *not* a variadic function/macro? - Then that's an error.
if (callee.variadic == VARIADIC_NONE)
{
SEMA_ERROR(call->call_expr.arguments[num_args - 1],
"Unpacking is only allowed for %s with variable parameters.",
callee.macro ? "macros" : "functions");
return false;
}
}
// 5. Zero out all argument slots.
unsigned func_param_count = vec_size(params);
// 6. We might have a typed variadic call e.g. foo(int, double...)
// get that type.
Type *variadic_type = NULL;
if (callee.variadic == VARIADIC_TYPED)
{
// 7a. The parameter type is <type>[], so we get the <type>
assert(params[func_param_count - 1]->type->type_kind == TYPE_SUBARRAY);
variadic_type = params[func_param_count - 1]->type->array.base;
// 7b. The last function parameter is implicit so we will pretend it's not there.
func_param_count--;
}
if (!sema_expand_call_arguments(context, call, params, args, func_param_count, callee.variadic != VARIADIC_NONE)) return false;
args = call->call_expr.arguments;
num_args = vec_size(args);
// 7. Loop through the parameters.
for (unsigned i = 0; i < num_args; i++)
{
Expr *arg = args[i];
// 10. If we exceed the function parameter count (remember we reduced this by one
// in the case of typed vararg) we're now in a variadic list.
if (i >= func_param_count)
{
// 11. We might have a typed variadic argument.
if (variadic_type)
{
// 11a. Look if we did an unsplat
if (call->call_expr.unsplat_last)
{
// 11c. Analyse the expression. We don't use any type inference here since
// foo(...{ 1, 2, 3 }) is a fairly worthless thing.
if (!sema_analyse_expr(context, NULL, arg)) return false;
// 11d. If it is allowed.
if (!expr_may_unpack_as_vararg(arg, variadic_type))
{
SEMA_ERROR(arg, "It's not possible to unpack %s as vararg of type %s",
type_quoted_error_string(arg->type),
type_quoted_error_string(variadic_type));
return false;
}
}
else
{
// 11e. A simple variadic value:
if (!sema_analyse_expr_of_required_type(context, variadic_type, arg, true)) return false;
}
// Set the argument at the location.
call->failable |= arg->failable;
continue;
}
// 12. We might have a naked variadic argument
if (callee.variadic == VARIADIC_RAW)
{
// 12a. Analyse the expression.
if (!sema_analyse_expr(context, NULL, arg)) return false;
// 12b. In the case of a compile time variable non macros we cast to c_int / double.
if (!callee.macro)
{
if (!expr_promote_vararg(context, arg)) return false;
}
// Set the argument at the location.
call->failable |= arg->failable;
continue;
}
UNREACHABLE
}
Decl *param = params[i];
// 16. Analyse a regular argument.
switch (param->var.kind)
{
case VARDECL_PARAM_REF:
// &foo
if (!sema_analyse_expr_value(context, param->type, arg)) return false;
if (param->type && param->type->canonical != arg->type->canonical)
{
SEMA_ERROR(arg, "'%s' cannot be implicitly cast to '%s'.", type_to_error_string(arg->type), type_to_error_string(param->type));
return false;
}
break;
case VARDECL_PARAM:
// foo
if (!sema_analyse_expr_of_required_type(context, param->type, arg, true)) return false;
if (!param->type && !cast_implicitly_to_runtime(arg))
{
SEMA_ERROR(arg, "Constant cannot implicitly be cast to a real type.");
return false;
}
if (callee.macro)
{
param->alignment = type_abi_alignment(param->type ? param->type : arg->type);
}
break;
case VARDECL_PARAM_EXPR:
// #foo
// We push a scope here as this will prevent the expression from modifying
// compile time variables during evaluation:
assert(callee.macro);
SCOPE_START
if (!sema_analyse_expr_of_required_type(context, param->type, arg, true)) return SCOPE_POP_ERROR();
SCOPE_END;
break;
case VARDECL_PARAM_CT:
// $foo
assert(callee.macro);
if (!sema_analyse_expr_of_required_type(context, param->type, arg, false)) return false;
if (!expr_is_constant_eval(arg))
{
SEMA_ERROR(arg, "A compile time parameter must always be a constant, did you mistake it for a normal paramter?");
return false;
}
break;
case VARDECL_PARAM_CT_TYPE:
// $Foo
if (!sema_analyse_expr_value(context, NULL, arg)) return false;
// TODO check typeof
if (arg->expr_kind != EXPR_TYPEINFO)
{
SEMA_ERROR(arg, "A type, like 'int' or 'double' was expected for the parameter '%s'.", param->name);
return false;
}
break;
case VARDECL_CONST:
case VARDECL_GLOBAL:
case VARDECL_LOCAL:
case VARDECL_MEMBER:
case VARDECL_LOCAL_CT:
case VARDECL_LOCAL_CT_TYPE:
case VARDECL_UNWRAPPED:
case VARDECL_REWRAPPED:
case VARDECL_ERASE:
UNREACHABLE
}
if (param->type)
{
if (!cast_implicit(arg, param->type)) return false;
}
else
{
param->type = arg->type;
}
call->failable |= arg->failable;
}
return true;
}
static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionSignature *signature, Expr *expr, Decl *decl, Type *to, Expr *struct_var)
{
CalledDecl callee = {
.macro = false,
.block_parameter = NO_TOKEN_ID,
.struct_var = struct_var,
.params = signature->params,
.variadic = signature->variadic,
.failable = signature->failable,
};
if (!sema_expr_analyse_call_invocation(context, expr, callee)) return false;
// 2. Builtin? We handle that elsewhere.
if (decl->func_decl.is_builtin)
{
assert(!struct_var);
return sema_expr_analyse_intrinsic_invocation(context, expr, decl, to);
}
expr_set_type(expr, signature->rtype->type);
return true;
}
static inline bool sema_expr_analyse_var_call(Context *context, Type *to, Expr *expr, Decl *var_decl)
{
Type *func_ptr_type = var_decl->type->canonical;
expr->failable |= var_decl->var.failable;
if (func_ptr_type->type_kind != TYPE_POINTER || func_ptr_type->pointer->type_kind != TYPE_FUNC)
{
SEMA_ERROR(expr, "Only macros, functions and function pointers maybe invoked, this is of type '%s'.", type_to_error_string(var_decl->type));
return false;
}
expr->call_expr.is_pointer_call = true;
return sema_expr_analyse_func_invocation(context,
func_ptr_type->pointer->func.signature,
expr,
func_ptr_type->decl,
to, NULL);
}
static inline Type *unify_returns(Context *context, Type *to)
{
bool all_returns_need_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 = to == 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.");
return NULL;
}
if (to != type_void)
{
if (current_expr_was_void)
{
SEMA_ERROR(return_stmt, "The return must be a value of type '%s'.", type_to_error_string(to));
return NULL;
}
if (!cast_implicit(ret_expr, to))
{
return NULL;
}
continue;
}
// The simple case.
if (to == right_canonical) continue;
// Try to find a common type:
Type *max = type_find_max_type(to, right_canonical);
if (!max)
{
SEMA_ERROR(return_stmt, "Cannot find a common parent type of '%s' and '%s'",
type_to_error_string(to), type_to_error_string(right_canonical));
SEMA_PREV(context->returns[i - 1], "The previous return was here.");
return false;
}
to = max;
all_returns_need_casts = true;
}
if (all_returns_need_casts)
{
VECEACH(context->returns, i)
{
Ast *return_stmt = context->returns[i];
Expr *ret_expr = return_stmt->return_stmt.expr;
if (!cast_implicit(ret_expr, to))
{
return NULL;
}
}
}
return to;
}
static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr *expr, Decl *decl, Expr *struct_var)
{
expr->call_expr.is_pointer_call = false;
return sema_expr_analyse_func_invocation(context, &decl->func_decl.function_signature, expr, decl, to, struct_var);
}
static bool sema_check_stmt_compile_time(Context *context, Ast *ast);
static bool sema_check_expr_compile_time(Context *context, Expr *expr)
{
switch (expr->expr_kind)
{
case EXPR_CONST:
return true;
case EXPR_MACRO_BLOCK:
VECEACH(expr->macro_block.stmts, i)
{
if (!sema_check_stmt_compile_time(context, expr->macro_block.stmts[i])) return false;
}
return true;
default:
return false;
}
UNREACHABLE
}
static bool sema_check_stmt_compile_time(Context *context, Ast *ast)
{
switch (ast->ast_kind)
{
case AST_NOP_STMT:
return true;
case AST_RETURN_STMT:
if (!ast->return_stmt.expr) return true;
return expr_is_constant_eval(ast->return_stmt.expr);
case AST_EXPR_STMT:
return sema_check_expr_compile_time(context, ast->expr_stmt);
case AST_CT_COMPOUND_STMT:
case AST_COMPOUND_STMT:
VECEACH(ast->compound_stmt.stmts, i)
{
if (!sema_check_stmt_compile_time(context, ast->ct_compound_stmt[i])) return false;
}
return true;
default:
return false;
}
}
static bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *call_expr, Expr *struct_var, Decl *decl)
{
assert(decl->decl_kind == DECL_MACRO);
// TODO failable
if (context->macro_scope.depth >= MAX_MACRO_NESTING)
{
SEMA_ERROR(call_expr, "Too deep nesting (more than %d levels) when evaluating this macro.", MAX_MACRO_NESTING);
return false;
}
Decl **params = copy_decl_list(decl->macro_decl.parameters);
CalledDecl callee = {
.macro = true,
.block_parameter = decl->macro_decl.block_parameter,
.params = params,
.struct_var = struct_var
};
if (!sema_expr_analyse_call_invocation(context, call_expr, callee)) return false;
Decl **func_params = decl->macro_decl.parameters;
Expr **args = call_expr->call_expr.arguments;
VECEACH(params, i)
{
Decl *param = params[i];
param->var.init_expr = args[i];
}
unsigned body_params = vec_size(call_expr->call_expr.body_arguments);
unsigned expected_body_params = vec_size(decl->macro_decl.body_parameters);
if (expected_body_params > body_params)
{
SEMA_ERROR(call_expr, "Not enough parameters for the macro body, expected %d.", expected_body_params);
return false;
}
if (expected_body_params < body_params)
{
SEMA_ERROR(call_expr, "Too many parameters for the macro body, expected %d.", expected_body_params);
return false;
}
for (unsigned i = 0; i < expected_body_params; i++)
{
Decl *body_param = decl->macro_decl.body_parameters[i];
assert(body_param->resolve_status == RESOLVE_DONE);
Decl *body_arg = call_expr->call_expr.body_arguments[i];
if (!body_arg->var.type_info)
{
SEMA_ERROR(body_arg, "Expected a type parameter before this variable name.");
return false;
}
if (!sema_resolve_type_info(context, body_arg->var.type_info)) return false;
body_arg->type = body_arg->var.type_info->type;
if (body_param->var.type_info)
{
Type *declare_type = body_param->var.type_info->type->canonical;
if (declare_type != body_arg->type)
{
SEMA_ERROR(body_arg->var.type_info, "This parameter should be '%s' but was '%s'",
type_to_error_string(declare_type),
type_quoted_error_string(body_arg->type));
return false;
}
}
if (!body_arg->alignment) body_arg->alignment = type_alloca_alignment(body_arg->type);
}
Decl **first_local = context->macro_scope.macro ? context->macro_scope.locals_start : context->locals;
MacroScope old_macro_scope = context->macro_scope;
bool ok = true;
SCOPE_OUTER_START
for (unsigned i = 0; i < expected_body_params; i++)
{
Decl *body_arg = call_expr->call_expr.body_arguments[i];
sema_add_local(context, body_arg);
}
context->macro_scope = (MacroScope){
.body_param = decl->macro_decl.block_parameter.index ? TOKSTR(decl->macro_decl.block_parameter) : NULL,
.macro = decl,
.inline_line = TOKLOC(call_expr->span.loc)->line,
.original_inline_line = old_macro_scope.depth ? old_macro_scope.original_inline_line : TOKLOC(call_expr->span.loc)->line,
.locals_start = context->active_scope.current_local,
.depth = old_macro_scope.depth + 1,
.yield_symbol_start = first_local,
.yield_body = call_expr->call_expr.body,
.yield_symbol_end = context->active_scope.current_local,
.yield_args = call_expr->call_expr.body_arguments,
};
Ast *body = copy_ast(decl->macro_decl.body);
TypeInfo *foo = decl->macro_decl.rtype;
Ast **saved_returns = context_push_returns(context);
context->expected_block_type = foo ? foo->type : to;
SCOPE_START_WITH_FLAGS(SCOPE_MACRO);
VECEACH(params, i)
{
Decl *param = params[i];
sema_add_local(context, param);
}
VECEACH(body->compound_stmt.stmts, i)
{
if (!sema_analyse_statement(context, body->compound_stmt.stmts[i]))
{
ok = false;
goto EXIT;
}
}
if (!vec_size(context->returns))
{
if (to)
{
SEMA_ERROR(decl, "Missing return in macro that evaluates to %s.", type_to_error_string(to));
ok = false;
goto EXIT;
}
}
Expr *first_return_expr = vec_size(context->returns) ? context->returns[0]->return_stmt.expr : NULL;
Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void;
// Let's unify the return statements.
left_canonical = unify_returns(context, left_canonical);
if (!left_canonical)
{
ok = false;
goto EXIT;
}
expr_set_type(call_expr, left_canonical);
if (vec_size(context->returns) == 1)
{
Expr *result = context->returns[0]->return_stmt.expr;
if (result && expr_is_constant_eval(result))
{
if (sema_check_stmt_compile_time(context, body))
{
expr_replace(call_expr, result);
goto EXIT;
}
}
}
call_expr->expr_kind = EXPR_MACRO_BLOCK;
call_expr->macro_block.stmts = body->compound_stmt.stmts;
call_expr->macro_block.params = params;
call_expr->macro_block.args = args;
EXIT:
SCOPE_END;
context_pop_returns(context, saved_returns);
SCOPE_OUTER_END;
context->macro_scope = old_macro_scope;
return ok;
}
static inline Decl *sema_generate_generic_function(Context *context, Expr *call_expr, Expr *struct_var, Decl *decl, const char *mangled_name)
{
TODO
return NULL;
}
static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Expr *call_expr, Expr *struct_var, Decl *decl)
{
assert(decl->decl_kind == DECL_GENERIC);
Expr **args = call_expr->call_expr.arguments;
Decl **func_params = decl->macro_decl.parameters;
unsigned explicit_args = vec_size(args);
unsigned total_args = explicit_args;
scratch_buffer_clear();
scratch_buffer_append(decl->external_name);
// TODO revisit name mangling
if (struct_var)
{
total_args++;
scratch_buffer_append_char('@');
scratch_buffer_append(struct_var->type->canonical->name);
}
if (total_args != vec_size(func_params))
{
// TODO HANDLE VARARGS
SEMA_ERROR(call_expr, "Mismatch on number of arguments.");
return false;
}
int offset = total_args - explicit_args;
for (unsigned i = 0; i < explicit_args; i++)
{
Expr *arg = args[i];
Decl *param = func_params[i + offset];
if (param->var.kind == VARDECL_PARAM_CT_TYPE)
{
if (!sema_analyse_expr_value(context, NULL, arg)) return false;
if (arg->expr_kind != EXPR_TYPEINFO)
{
SEMA_ERROR(arg, "A type, like 'int' or 'double' was expected for the parameter '%s'.", param->name);
return false;
}
scratch_buffer_append_char(',');
scratch_buffer_append(arg->type_expr->type->canonical->name);
continue;
}
if (param->var.type_info)
{
if (!sema_analyse_expr_of_required_type(context, param->var.type_info->type, arg, true)) return false;
}
else
{
if (!sema_analyse_expr(context, NULL, arg)) return false;
if (!cast_implicitly_to_runtime(arg)) return false;
}
scratch_buffer_append_char(',');
scratch_buffer_append(arg->type->canonical->name);
}
const char *mangled_name = scratch_buffer_interned();
Decl **generic_cache = decl->module->generic_cache;
Decl *found = NULL;
VECEACH(generic_cache, i)
{
if (generic_cache[i]->external_name == mangled_name)
{
found = generic_cache[i];
break;
}
}
if (!found)
{
found = sema_generate_generic_function(context, call_expr, struct_var, decl, mangled_name);
}
// Remove type parameters from call.
for (unsigned i = 0; i < explicit_args; i++)
{
Decl *param = func_params[i + offset];
if (param->var.kind == VARDECL_PARAM_CT_TYPE)
{
for (unsigned j = i + 1; j < explicit_args; j++)
{
args[j - 1] = args[j];
}
explicit_args--;
i--;
}
}
vec_resize(args, explicit_args);
// Perform the normal func call on the found declaration.
return sema_expr_analyse_func_call(context, to, call_expr, found, struct_var);
}
static bool sema_analyse_body_expansion(Context *context, Expr *call)
{
Decl *macro = context->macro_scope.macro;
assert(macro);
assert(macro->macro_decl.block_parameter.index);
ExprCall *call_expr = &call->call_expr;
if (vec_size(call_expr->body_arguments))
{
SEMA_ERROR(call, "Nested expansion is not possible.");
return false;
}
if (call_expr->unsplat_last)
{
SEMA_ERROR(call, "Expanding parameters is not allowed for macro invocations.");
return false;
}
unsigned expressions = vec_size(call_expr->arguments);
if (expressions != vec_size(macro->macro_decl.body_parameters))
{
SEMA_ERROR(call, "Expected %d parameter(s).", vec_size(macro->macro_decl.body_parameters));
return false;
}
Expr **args = call_expr->arguments;
for (unsigned i = 0; i < expressions; i++)
{
Expr *expr = args[i];
Decl *param = context->macro_scope.yield_args[i];
if (!sema_analyse_expr(context, param->type, expr)) return false;
}
assert(call_expr->function->expr_kind == EXPR_MACRO_BODY_EXPANSION);
expr_replace(call, call_expr->function);
call->body_expansion_expr.values = args;
call->body_expansion_expr.declarations = context->macro_scope.yield_args;
bool in_yield = context->macro_scope.in_yield;
context->macro_scope.in_yield = true;
call->body_expansion_expr.ast = copy_ast(context->macro_scope.yield_body);
bool success = sema_analyse_statement(context, call->body_expansion_expr.ast);
context->macro_scope.in_yield = in_yield;
return success;
}
bool sema_expr_analyse_general_call(Context *context, Type *to, Expr *expr, Decl *decl, Expr *struct_var, bool is_macro)
{
int force_inline = -1;
VECEACH(expr->call_expr.attributes, i)
{
Attr *attr = expr->call_expr.attributes[i];
AttributeType attribute = sema_analyse_attribute(context, attr, ATTR_CALL);
if (attribute == ATTRIBUTE_NONE) return expr_poison(expr);
bool had = false;
switch (attribute)
{
case ATTRIBUTE_INLINE:
case ATTRIBUTE_NOINLINE:
if (decl->decl_kind != DECL_FUNC)
{
SEMA_TOKID_ERROR(attr->name, "Inline / noinline attribute is only allowed for direct function/method calls");
return expr_poison(expr);
}
force_inline = attribute == ATTRIBUTE_INLINE ? 1 : 0;
break;
default:
UNREACHABLE;
}
}
expr->call_expr.is_type_method = struct_var != NULL;
switch (decl->decl_kind)
{
case DECL_MACRO:
if (!is_macro)
{
SEMA_ERROR(expr, "A macro neeeds to be called with a '@' prefix, please add it.");
return false;
}
expr->call_expr.func_ref = decl;
return sema_expr_analyse_macro_call(context, to, expr, struct_var, decl);
case DECL_VAR:
if (is_macro)
{
SEMA_ERROR(expr, "A function cannot be called with a '@' prefix, please remove it.");
return false;
}
assert(struct_var == NULL);
return sema_expr_analyse_var_call(context, to, expr, decl);
case DECL_FUNC:
if (is_macro)
{
SEMA_ERROR(expr, "A function cannot be called with a '@' prefix, please remove it.");
return false;
}
expr->call_expr.func_ref = decl;
expr->call_expr.force_inline = force_inline == 1;
expr->call_expr.force_noinline = force_inline == 0;
return sema_expr_analyse_func_call(context, to, expr, decl, struct_var);
case DECL_GENERIC:
if (is_macro)
{
SEMA_ERROR(expr, "A generic function cannot be called with a '@' prefix, please remove it.");
return false;
}
expr->call_expr.func_ref = decl;
return sema_expr_analyse_generic_call(context, to, expr, struct_var, decl);
case DECL_POISONED:
return false;
default:
SEMA_ERROR(expr, "This expression cannot be called.");
return false;
}
}
static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr)
{
expr->constant = false;
expr->pure = false;
Expr *func_expr = expr->call_expr.function;
if (!sema_analyse_expr_value(context, NULL, func_expr)) return false;
if (func_expr->expr_kind == EXPR_MACRO_BODY_EXPANSION)
{
return sema_analyse_body_expansion(context, expr);
}
expr->failable = func_expr->failable;
Decl *decl;
Expr *struct_var = NULL;
bool macro = false;
switch (func_expr->expr_kind)
{
case EXPR_IDENTIFIER:
decl = func_expr->identifier_expr.decl;
break;
case EXPR_ACCESS:
decl = func_expr->access_expr.ref;
if (decl->decl_kind == DECL_FUNC || decl->decl_kind == DECL_MACRO)
{
expr_insert_addr(func_expr->access_expr.parent);
struct_var = func_expr->access_expr.parent;
macro = decl->decl_kind == DECL_MACRO;
}
break;
case EXPR_MACRO_EXPANSION:
decl = func_expr->macro_expansion_expr.decl;
macro = true;
break;
case EXPR_LEN:
if (func_expr->type == type_void)
{
expr_replace(expr, func_expr);
expr_set_type(expr, type_usize);
return true;
}
FALLTHROUGH;
case EXPR_TYPEINFO:
if (func_expr->type_expr->resolve_status == RESOLVE_DONE)
{
SEMA_ERROR(expr, "A type cannot be followed by (), if you intended a cast, use (type)(expression).");
}
else
{
SEMA_ERROR(expr, "A type cannot be followed by (), did you mean to use ({})?");
}
return false;
default:
SEMA_ERROR(expr, "This value cannot be invoked, did you accidentally add ()?");
return false;
}
decl = decl_flatten(decl);
return sema_expr_analyse_general_call(context, to, expr, decl, struct_var, macro);
}
static bool expr_check_index_in_range(Context *context, Type *type, Expr *index_expr, bool end_index, bool from_end)
{
assert(type == type->canonical);
if (index_expr->expr_kind != EXPR_CONST) return true;
if (!bigint_fits_in_bits(&index_expr->const_expr.i, 64, true))
{
SEMA_ERROR(index_expr, "Index does not fit into an 64-signed integer.");
return false;
}
int64_t index = bigint_as_signed(&index_expr->const_expr.i);
if (from_end && index < 0)
{
SEMA_ERROR(index_expr, "Negative numbers are not allowed when indexing from the end.");
return false;
}
switch (type->type_kind)
{
case TYPE_POINTER:
assert(!from_end);
return true;
case TYPE_ARRAY:
{
int64_t len = (int64_t)type->array.len;
if (from_end)
{
index = len - index;
}
// Checking end can only be done for arrays.
if (end_index && index >= len)
{
SEMA_ERROR(index_expr, "Array end index out of bounds, was %lld, exceeding array length %lld.", (long long)index, (long long)len);
return false;
}
if (!end_index && index >= len)
{
SEMA_ERROR(index_expr, "Array index out of bounds, was %lld, exceeding max array index %lld.", (long long)index, (long long)len - 1);
return false;
}
break;
}
case TYPE_STRLIT:
case TYPE_SUBARRAY:
// If not from end, just check the negative values.
if (!from_end) break;
// From end we can only do sanity checks ^0 is invalid for non-end index. ^-1 and less is invalid for all.
if (index == 0 && !end_index)
{
SEMA_ERROR(index_expr,
"Array index out of bounds, index from end (%lld) must be greater than zero or it will exceed the max array index.",
(long long) index);
return false;
}
return true;
default:
UNREACHABLE
}
if (index < 0)
{
SEMA_ERROR(index_expr, "Array index out of bounds, using a negative array index is only allowed with pointers.");
return false;
}
return true;
}
static Type *sema_expr_find_indexable_type_recursively(Type **type, Expr **parent)
{
while (1)
{
Type *inner_type = type_get_indexed_type(*type);
if (!inner_type && type_is_substruct(*type))
{
Expr *embedded_struct = expr_access_inline_member(*parent, (*type)->decl);
*type = embedded_struct->type->canonical;
*parent = embedded_struct;
continue;
}
return inner_type;
}
}
static inline bool sema_expr_analyse_subscript(Context *context, Expr *expr)
{
if (!sema_analyse_expr(context, NULL, expr->subscript_expr.expr)) return false;
expr->failable = expr->subscript_expr.expr->failable;
assert(expr->expr_kind == EXPR_SUBSCRIPT);
Expr *subscripted = expr->subscript_expr.expr;
Type *type = type_flatten(subscripted->type);
Expr *index = expr->subscript_expr.index;
Type *current_type = type;
Expr *current_expr = subscripted;
Type *inner_type = sema_expr_find_indexable_type_recursively(&current_type, &current_expr);
if (!inner_type)
{
SEMA_ERROR(subscripted, "Cannot index '%s'.", type_to_error_string(type));
return false;
}
if (!sema_analyse_expr(context, NULL, index)) return false;
expr->constant = index->constant & subscripted->constant;
expr->pure = index->pure & subscripted->pure;
// Cast to an appropriate type for index.
if (!expr_cast_to_index(index)) return false;
// Check range
if (!expr_check_index_in_range(context, current_type, index, false, expr->subscript_expr.from_back)) return false;
expr->subscript_expr.expr = current_expr;
expr->failable |= index->failable;
expr_set_type(expr, inner_type);
return true;
}
static inline bool sema_expr_analyse_slice(Context *context, Expr *expr)
{
if (!sema_analyse_expr(context, NULL, expr->slice_expr.expr)) return false;
expr->failable = expr->slice_expr.expr->failable;
assert(expr->expr_kind == EXPR_SLICE);
Expr *subscripted = expr->slice_expr.expr;
expr->pure = subscripted->pure;
expr->constant = subscripted->constant;
Type *type = type_flatten(subscripted->type);
Expr *start = expr->slice_expr.start;
Expr *end = expr->slice_expr.end;
Expr *current_expr = subscripted;
Type *inner_type = sema_expr_find_indexable_type_recursively(&type, &current_expr);
if (!inner_type)
{
SEMA_ERROR(subscripted, "Cannot index '%s'.", type_to_error_string(subscripted->type));
return false;
}
expr->slice_expr.expr = current_expr;
if (!sema_analyse_expr(context, type_int, start)) return false;
expr->pure &= start->pure;
expr->constant &= start->constant;
if (end && !sema_analyse_expr(context, type_int, end)) return false;
expr->pure &= !end || end->pure;
expr->constant &= !end || end->constant;
// Fix index sizes
if (!expr_cast_to_index(start)) return false;
if (end && !expr_cast_to_index(end)) return false;
// Check range
if (type->type_kind == TYPE_POINTER)
{
if (expr->slice_expr.start_from_back)
{
SEMA_ERROR(expr->slice_expr.start, "Indexing from the end is not allowed for pointers.");
return false;
}
if (!end)
{
SEMA_ERROR(expr, "Omitting end index is not allowed for pointers.");
return false;
}
if (end && expr->slice_expr.end_from_back)
{
SEMA_ERROR(expr->slice_expr.end, "Indexing from the end is not allowed for pointers.");
return false;
}
}
if (!expr_check_index_in_range(context, type, start, false, expr->slice_expr.start_from_back)) return false;
if (end && !expr_check_index_in_range(context, type, end, true, expr->slice_expr.end_from_back)) return false;
if (start && end && start->expr_kind == EXPR_CONST && end->expr_kind == EXPR_CONST)
{
if (type->type_kind == TYPE_ARRAY)
{
BigInt len;
bigint_init_unsigned(&len, type->array.len);
BigInt result;
if (expr->slice_expr.start_from_back)
{
bigint_sub(&result, &len, &start->const_expr.i);
start->const_expr.i = result;
expr->slice_expr.start_from_back = false;
}
if (expr->slice_expr.end_from_back)
{
bigint_sub(&result, &len, &end->const_expr.i);
end->const_expr.i = result;
expr->slice_expr.end_from_back = false;
}
}
if (expr->slice_expr.start_from_back && expr->slice_expr.end_from_back)
{
if (expr_const_compare(&start->const_expr, &end->const_expr, BINARYOP_LT))
{
SEMA_ERROR(start, "Start index greater than end index.");
return false;
}
}
else if (!expr->slice_expr.start_from_back && !expr->slice_expr.end_from_back)
{
if (expr_const_compare(&start->const_expr, &end->const_expr, BINARYOP_GT))
{
SEMA_ERROR(start, "Start index greater than end index.");
return false;
}
}
}
expr->failable |= start->failable;
expr_set_type(expr, type_get_subarray(inner_type));
return true;
}
static inline bool sema_expr_analyse_group(Context *context, Type *to, Expr *expr)
{
if (!sema_analyse_expr(context, NULL, expr->group_expr)) return false;
*expr = *expr->group_expr;
return true;
}
static inline void expr_rewrite_to_int_const(Expr *expr_to_rewrite, Type *type, uint64_t value)
{
expr_to_rewrite->expr_kind = EXPR_CONST;
expr_const_set_int(&expr_to_rewrite->const_expr, value, type->canonical->type_kind);
expr_set_type(expr_to_rewrite, type);
expr_to_rewrite->constant = true;
expr_to_rewrite->pure = true;
expr_to_rewrite->resolve_status = RESOLVE_DONE;
}
static inline void expr_rewrite_to_string(Expr *expr_to_rewrite, const char *string)
{
expr_to_rewrite->expr_kind = EXPR_CONST;
expr_to_rewrite->constant = true;
expr_to_rewrite->const_expr.kind = TYPE_STRLIT;
expr_to_rewrite->const_expr.string.chars = (char *)string;
expr_to_rewrite->const_expr.string.len = (int)strlen(string);
expr_to_rewrite->pure = true;
expr_to_rewrite->resolve_status = RESOLVE_DONE;
expr_set_type(expr_to_rewrite, type_compstr);
}
static bool sema_expr_analyse_typeinfo(Context *context, Expr *expr)
{
expr->constant = true;
expr->pure = true;
TypeInfo *type_info = expr->type_expr;
if (!sema_resolve_type_info(context, type_info)) return false;
expr_set_type(expr, type_typeinfo);
return true;
}
/*
static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr)
{
Type *type = expr->access_expr.parent->type->decl->member_decl.type_info->type;
Type *canonical = type->canonical;
const char *sub_element = TOKSTR(expr->access_expr.sub_element);
if (sub_element == kw_sizeof)
{
expr_rewrite_to_int_const(expr, type_usize, type_size(canonical));
return true;
}
if (sub_element == kw_offsetof)
{
TODO // calculate offset.
}
// Possibly alignof
if (!type_may_have_sub_elements(type))
{
SEMA_ERROR(expr, "'%s' does not have a member '%s'.", type_to_error_string(type), sub_element);
return false;
}
Decl *decl = canonical->decl;
switch (decl->decl_kind)
{
case DECL_ENUM:
if (TOKTYPE(expr->access_expr.sub_element) == TOKEN_CONST_IDENT)
{
if (!sema_expr_analyse_enum_constant(expr, sub_element, decl))
{
SEMA_ERROR(expr,
"'%s' has no enumeration value '%s'.",
decl->name,
sub_element);
return false;
}
return true;
}
break;
case DECL_ERR:
case DECL_UNION:
case DECL_STRUCT:
break;
default:
UNREACHABLE
}
VECEACH(decl->methods, i)
{
Decl *function = decl->methods[i];
if (sub_element == function->name)
{
expr->access_expr.ref = function;
expr->type = function->type;
return true;
}
}
if (decl_is_struct_type(decl))
{
VECEACH(decl->strukt.members, i)
{
Decl *member = decl->strukt.members[i];
if (sub_element == member->name)
{
expr->access_expr.ref = member;
expr->type = member->member_decl.reference_type;
return true;
}
}
}
SEMA_ERROR(expr,
"No function or member '%s.%s' found.",
type_to_error_string(type),
sub_element);
return false;
}
*/
static void add_members_to_context(Context *context, Decl *decl)
{
VECEACH(decl->methods, i)
{
Decl *func = decl->methods[i];
sema_add_member(context, func);
}
while (decl->decl_kind == DECL_DISTINCT)
{
Type *type = decl->distinct_decl.base_type->canonical;
if (!type_is_user_defined(type)) break;
decl = type->decl;
}
if (decl_is_struct_type(decl))
{
Decl **members = decl->strukt.members;
VECEACH(members, i)
{
Decl *member = members[i];
if (member->name == NULL)
{
add_members_to_context(context, member);
continue;
}
sema_add_member(context, member);
}
}
}
static Expr *enum_minmax_value(Decl *decl, BinaryOp comparison)
{
assert(decl->decl_kind == DECL_ENUM);
bool is_signed = type_is_signed(decl->enums.type_info->type->canonical);
Expr *expr = NULL;
VECEACH(decl->enums.values, i)
{
Decl *enum_constant = decl->enums.values[i];
Expr *candidate = enum_constant->enum_constant.expr;
assert(candidate->expr_kind == EXPR_CONST);
if (!expr)
{
expr = candidate;
continue;
}
if (expr_const_compare(&candidate->const_expr, &expr->const_expr, comparison))
{
expr = candidate;
}
}
return expr;
}
/**
* 1. .A -> It is an enum constant.
* 2. .foo -> It is a function.
* 3. .@foo -> It is a macro.
* 4. .#bar -> It is an identifier to resolve as a member or a function
* 5. .@#bar -> It is an identifier to resolve as a macro
*/
static TokenId sema_expr_resolve_access_child(Expr *child)
{
switch (child->expr_kind)
{
case EXPR_IDENTIFIER:
case EXPR_CONST_IDENTIFIER:
// Not allowed obviously.
if (child->identifier_expr.path) break;
return child->identifier_expr.identifier;
case EXPR_HASH_IDENT:
TODO
case EXPR_MACRO_EXPANSION:
child = child->macro_expansion_expr.inner;
switch (child->expr_kind)
{
case EXPR_IDENTIFIER:
case EXPR_HASH_IDENT:
return sema_expr_resolve_access_child(child);
default:
SEMA_ERROR(child, "Expected a macro name.");
return INVALID_TOKEN_ID;
}
default:
break;
}
SEMA_ERROR(child, "Expected an identifier here.");
return INVALID_TOKEN_ID;
}
static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, bool was_group, bool is_macro, TokenId identifier_token)
{
// 1. Foo*.sizeof is not allowed, it must be (Foo*).sizeof
if (!was_group && type_kind_is_derived(parent->type->type_kind))
{
SEMA_ERROR(expr->access_expr.parent, "Array and pointer types must be enclosed in (), did you forget it?");
return false;
}
expr->constant = true;
expr->pure = true;
TokenType type = TOKTYPE(identifier_token);
// 2. Handle Foo.typeid => return a typeid expression.
if (type == TOKEN_TYPEID)
{
expr_set_type(expr, type_typeid);
expr->expr_kind = EXPR_TYPEID;
expr->typeid_expr = parent;
expr->resolve_status = RESOLVE_DONE;
return true;
}
Type *canonical = parent->type->canonical;
const char *name = TOKSTR(identifier_token);
// 3. Handle float.nan, double.inf etc
if (type_is_float(canonical))
{
if (name == kw_nan)
{
expr->expr_kind = EXPR_CONST;
expr->const_expr.kind = canonical->type_kind;
#if LONG_DOUBLE
expr->const_expr.f = nanl("");
#else
expr->const_expr.f = nan("");
#endif
expr_set_type(expr, parent->type);
expr->constant = true;
expr->pure = true;
expr->resolve_status = RESOLVE_DONE;
return true;
}
if (name == kw_inf)
{
expr->expr_kind = EXPR_CONST;
expr->const_expr.kind = parent->type->canonical->type_kind;
expr->const_expr.f = INFINITY;
expr_set_type(expr, parent->type->canonical);
expr->constant = true;
expr->pure = true;
expr->resolve_status = RESOLVE_DONE;
return true;
}
}
if (!type_may_have_sub_elements(canonical))
{
SEMA_ERROR(expr, "'%s' does not have a property '%s'.", type_to_error_string(parent->type), name);
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 (type == TOKEN_CONST_IDENT)
{
if (!sema_expr_analyse_enum_constant(expr, identifier_token, decl))
{
SEMA_ERROR(expr, "'%s' has no enumeration value '%s'.", decl->name, name);
return false;
}
return true;
}
if (name == kw_elements)
{
expr_rewrite_to_int_const(expr, type_compint, vec_size(decl->enums.values));
return true;
}
if (name == kw_max)
{
Expr *max = enum_minmax_value(decl, BINARYOP_GT);
if (!max)
{
expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0);
return true;
}
expr_replace(expr, max);
return true;
}
if (name == kw_min)
{
Expr *min = enum_minmax_value(decl, BINARYOP_LT);
if (!min)
{
expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0);
return true;
}
expr_replace(expr, min);
return true;
}
break;
case DECL_ERRTYPE:
if (type == TOKEN_CONST_IDENT)
{
if (!sema_expr_analyse_enum_constant(expr, identifier_token, decl))
{
SEMA_ERROR(expr, "'%s' has no error value '%s'.", decl->name, name);
return false;
}
return true;
}
if (name == kw_elements)
{
expr_rewrite_to_int_const(expr, type_compint, vec_size(decl->enums.values));
return true;
}
if (name == kw_max)
{
Expr *max = enum_minmax_value(decl, BINARYOP_GT);
if (!max)
{
expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0);
return true;
}
expr_replace(expr, max);
return true;
}
if (name == kw_min)
{
Expr *min = enum_minmax_value(decl, BINARYOP_LT);
if (!min)
{
expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0);
return true;
}
expr_replace(expr, min);
return true;
}
break;
case DECL_UNION:
case DECL_STRUCT:
case DECL_DISTINCT:
break;
default:
UNREACHABLE
}
VECEACH(decl->methods, i)
{
Decl *function = decl->methods[i];
if (name == function->name)
{
expr->access_expr.ref = function;
expr_set_type(expr, function->type);
return true;
}
}
SEMA_ERROR(expr, "No function '%s.%s' found.", type_to_error_string(parent->type), name);
return false;
}
static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr, bool is_macro, TokenId identifier_token)
{
Expr *parent = expr->access_expr.parent;
expr->constant = true;
expr->pure = true;
Expr *child = expr->access_expr.child;
TokenType type = TOKTYPE(identifier_token);
const char *name = TOKSTR(identifier_token);
Decl *ref = parent->access_expr.ref;
bool is_leaf_member_ref = ref->decl_kind == DECL_VAR;
if (type == TOKEN_TYPEID && !is_macro)
{
expr_set_type(expr, type_typeid);
expr->expr_kind = EXPR_TYPEID;
if (is_leaf_member_ref)
{
expr->typeid_expr = ref->var.type_info;
}
else
{
expr->typeid_expr = type_info_new_base(ref->type, parent->span);
}
expr->resolve_status = RESOLVE_DONE;
return true;
}
if (name == kw_ordinal && !is_macro)
{
if (ref->decl_kind == DECL_ENUM_CONSTANT || ref->decl_kind == DECL_ERRVALUE)
{
expr_rewrite_to_int_const(expr, type_compint, ref->enum_constant.ordinal);
return true;
}
}
// If we have something like struct Foo { Bar b; int c; struct d { int e; } }
// If we are inspecting Foo.c we're done here. Otherwise handle struct d / Bar b case
// The same way.
if (is_leaf_member_ref)
{
if (!type_is_structlike(ref->type))
{
if (is_macro)
{
SEMA_ERROR(expr, "'%s' does not have a macro '%s'.", type_to_error_string(ref->type), name);
return false;
}
SEMA_ERROR(expr, "'%s' does not have a member or property '%s'.", type_to_error_string(ref->type), name);
return false;
}
// Pretend to be an inline struct.
ref = ref->type->decl;
}
VECEACH(ref->methods, i)
{
Decl *decl = ref->methods[i];
bool is_macro_decl = decl->decl_kind == DECL_MACRO;
if (name == decl->name)
{
if (is_macro != is_macro_decl)
{
if (is_macro)
{
SEMA_ERROR(child, "Method '%s' should not be prefixed with '@'.", name);
}
else
{
SEMA_ERROR(child, "Macro method '%s' must be prefixed with '@'.", name);
}
return false;
}
expr->access_expr.ref = decl;
expr_set_type(expr, decl->type);
return true;
}
}
// We want a distinct to be able to access members.
Decl *flat_ref = ref;
while (flat_ref->decl_kind == DECL_DISTINCT)
{
Type *flat_type = flat_ref->distinct_decl.base_type->canonical;
if (!type_is_user_defined(flat_type)) break;
flat_ref = flat_type->decl;
}
switch (flat_ref->decl_kind)
{
case DECL_STRUCT:
case DECL_UNION:
VECEACH(flat_ref->strukt.members, i)
{
Decl *member = ref->strukt.members[i];
if (name == member->name)
{
if (is_macro)
{
SEMA_ERROR(child, "member '%s' should not be prefixed with '@'.", name);
return false;
}
expr->expr_kind = EXPR_MEMBER_ACCESS;
expr->access_expr.ref = member;
expr_set_type(expr, member->type);
return true;
}
}
default:
break;
}
SEMA_ERROR(expr, "No function or member '%s.%s' found.", "todo", name);
TODO
return false;
}
/**
* Analyse "x.y"
*/
static inline bool sema_expr_analyse_access(Context *context, Expr *expr)
{
Expr *parent = expr->access_expr.parent;
bool was_group = parent->expr_kind == EXPR_GROUP;
// 1. Resolve the left hand
if (!sema_analyse_expr_value(context, NULL, parent)) return false;
// 2. The right hand side may be a @ident or ident
Expr *child = expr->access_expr.child;
bool is_macro = child->expr_kind == EXPR_MACRO_EXPANSION;
// 3. Find the actual token.
TokenId identifier_token = sema_expr_resolve_access_child(child);
if (TOKEN_IS_INVALID(identifier_token)) return false;
// 2. If our left hand side is a type, e.g. MyInt.abc, handle this here.
if (parent->expr_kind == EXPR_TYPEINFO)
{
return sema_expr_analyse_type_access(expr, parent->type_expr, was_group, is_macro, identifier_token);
}
// 3. The left hand side may already be indexing into a type:
// x.y.z. In this case "x.y" is the parent.
if (parent->expr_kind == EXPR_MEMBER_ACCESS)
{
return sema_expr_analyse_member_access(context, expr, is_macro, identifier_token);
}
// 6. Copy failability
expr->failable = parent->failable;
assert(expr->expr_kind == EXPR_ACCESS);
assert(parent->resolve_status == RESOLVE_DONE);
// 7. Is this a pointer? If so we insert a deref.
bool is_pointer = parent->type->canonical->type_kind == TYPE_POINTER;
if (is_pointer)
{
if (!sema_cast_rvalue(context, NULL, parent)) return false;
expr_insert_deref(expr->access_expr.parent);
parent = expr->access_expr.parent;
}
// 8. Depending on parent type, we have some hard coded types
const char *kw = TOKSTR(identifier_token);
Expr *current_parent = parent;
Type *type = parent->type->canonical;
Type *flat_type = type_flatten(type);
CHECK_DEEPER:
// 9. Fix hard coded function `len` on subarrays and arrays
if (!is_macro && kw == kw_len)
{
if (flat_type->type_kind == TYPE_SUBARRAY)
{
expr->expr_kind = EXPR_LEN;
expr->len_expr.inner = parent;
expr_set_type(expr, type_void);
expr->resolve_status = RESOLVE_DONE;
return true;
}
if (flat_type->type_kind == TYPE_ARRAY)
{
expr_rewrite_to_int_const(expr, type_compint, flat_type->array.len);
return true;
}
}
// 9. At this point we may only have distinct, struct, union, error, enum
if (!type_may_have_sub_elements(type))
{
SEMA_ERROR(expr, "There is no member or method '%s' on '%s'", kw, type_to_error_string(parent->type));
return false;
}
// 10. Dump all members and methods into the scope.
Decl *decl = type->decl;
Decl *member;
SCOPE_START
add_members_to_context(context, decl);
member = sema_resolve_symbol_in_current_dynamic_scope(context, kw);
SCOPE_END;
if (!member)
{
Decl *ambiguous = NULL;
Decl *private = NULL;
member = sema_resolve_method(context, decl, kw, &ambiguous, &private);
if (member)
{
context_register_external_symbol(context, member);
}
}
// 11. If we didn't find a match...
if (!member)
{
// 11a. We have a potential embedded struct check:
if (type_is_substruct(type))
{
Expr *embedded_struct = expr_access_inline_member(parent, type->decl);
current_parent = embedded_struct;
type = embedded_struct->type->canonical;
goto CHECK_DEEPER;
}
// 11b. Otherwise we give up.
SEMA_ERROR(expr, "There is no field or method '%s.%s'.", decl->name, kw);
return false;
}
// 12a. If the member was a macro and it isn't prefixed with `@` then that's an error.
if (member->decl_kind == DECL_MACRO && !is_macro)
{
SEMA_ERROR(expr, "Expected '@' before the macro name.");
return false;
}
// 12b. If the member was *not* a macro but was prefixed with `@` then that's an error.
if (member->decl_kind != DECL_MACRO && is_macro)
{
SEMA_ERROR(expr, "'@' should only be placed in front of macro names.");
return false;
}
// 13. Copy properties.
expr->access_expr.parent = current_parent;
expr->constant = expr->access_expr.parent->constant;
expr->pure = expr->access_expr.parent->pure;
expr_set_type(expr, member->type);
expr->access_expr.ref = member;
return true;
}
static Decl *sema_resolve_element_for_name(Decl** decls, DesignatorElement **elements, unsigned *index)
{
DesignatorElement *element = elements[*index];
const char *name = element->field;
unsigned old_index = *index;
VECEACH(decls, i)
{
Decl *decl = decls[i];
// The simple case, we have a match.
if (decl->name == name)
{
element->index = i;
return decl;
}
if (!decl->name)
{
assert(type_is_structlike(decl->type));
// Anonymous struct
Decl *found = sema_resolve_element_for_name(decl->strukt.members, elements, index);
// No match, continue...
if (!found) continue;
// Special handling, we now need to patch the elements
unsigned current_size = vec_size(elements);
// Add an element at the end.
vec_add(elements, NULL);
// Shift all elements
for (unsigned j = current_size; j > old_index; j--)
{
elements[j] = elements[j - 1];
}
// Create our anon field.
DesignatorElement *anon_element = CALLOCS(DesignatorElement);
anon_element->kind = DESIGNATOR_FIELD;
anon_element->index = i;
elements[old_index] = anon_element;
// Advance
(*index)++;
return found;
}
}
return NULL;
}
static int64_t sema_analyse_designator_index(Context *context, Expr *index)
{
if (!sema_analyse_expr_value(context, type_uint, index))
{
return -1;
}
// Unless we already have type_usize, cast to type_isize;
if (!expr_cast_to_index(index))
{
return -1;
}
if (index->expr_kind != EXPR_CONST)
{
SEMA_ERROR(index, "The index must be a constant value.");
return -1;
}
if (!bigint_fits_in_bits(&index->const_expr.i, 64, true))
{
SEMA_ERROR(index, "The value of the index does not fit in a long.");
return -1;
}
int64_t index_val = bigint_as_signed(&index->const_expr.i);
if (index_val < 0)
{
SEMA_ERROR(index, "Negative index values is not allowed.");
return -1;
}
return index_val;
}
static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorElement **elements, unsigned *curr_index, bool *is_constant, bool *did_report_error, ArrayIndex *max_index)
{
Type *type_lowered = type_lowering(type);
DesignatorElement *element = elements[*curr_index];
if (element->kind == DESIGNATOR_ARRAY || element->kind == DESIGNATOR_RANGE)
{
if (type_lowered->type_kind != TYPE_ARRAY && type_lowered->type_kind != TYPE_INFERRED_ARRAY)
{
return NULL;
}
ByteSize len = type_lowered->type_kind == TYPE_INFERRED_ARRAY ? MAX_ARRAYINDEX : type_lowered->array.len;
ArrayIndex index = sema_analyse_designator_index(context, element->index_expr);
if (index < 0)
{
*did_report_error = true;
return NULL;
}
if (index >= (ArrayIndex)len)
{
SEMA_ERROR(element->index_expr, "The index may must be less than the array length (which was %llu).", (unsigned long long)len);
*did_report_error = true;
return NULL;
}
element->index = index;
if (max_index && *max_index < index) *max_index = index;
if (element->kind == DESIGNATOR_RANGE)
{
int64_t end_index = sema_analyse_designator_index(context, element->index_end_expr);
if (end_index < 0)
{
*did_report_error = true;
return NULL;
}
if (index > end_index)
{
SEMA_ERROR(element->index_end_expr, "End index must be greater than start index.");
*did_report_error = true;
return NULL;
}
if (end_index > (ArrayIndex)len)
{
*did_report_error = true;
SEMA_ERROR(element->index_expr, "The index may must be less than the array length (which was %llu).", (unsigned long long)len);
return NULL;
}
element->index_end = end_index;
if (max_index && *max_index < end_index) *max_index = end_index;
}
return type_lowered->array.base;
}
assert(element->kind == DESIGNATOR_FIELD);
if (!type_is_structlike(type_lowered))
{
return NULL;
}
Decl *member = sema_resolve_element_for_name(type_lowered->decl->strukt.members, elements, curr_index);
if (!member) return NULL;
return member->type;
}
static Type *sema_expr_analyse_designator(Context *context, Type *current, Expr *expr, ArrayIndex *max_index)
{
DesignatorElement **path = expr->designator_expr.path;
// Walk down into this path
bool is_constant = true;
bool did_report_error = false;
for (unsigned i = 0; i < vec_size(path); i++)
{
Type *new_current = sema_find_type_of_element(context, current, path, &i, &is_constant, &did_report_error, i == 0 ? max_index : NULL);
if (!new_current)
{
if (!did_report_error) SEMA_ERROR(expr, "This is not a valid member of '%s'.", type_to_error_string(current));
return NULL;
}
current = new_current;
}
expr->constant = is_constant;
expr->pure = is_constant;
return current;
}
static void sema_update_const_initializer_with_designator(ConstInitializer *const_init,
DesignatorElement **curr,
DesignatorElement **end,
Expr *value);
static void sema_create_const_initializer_value(ConstInitializer *const_init, Expr *value)
{
// Possibly this is already a const initializers, in that case
// overwrite what is inside, eg [1] = { .a = 1 }
if (value->expr_kind == EXPR_INITIALIZER_LIST && value->initializer_expr.init_type == INITIALIZER_CONST)
{
*const_init = *value->initializer_expr.initializer;
value->initializer_expr.initializer = const_init;
return;
}
const_init->init_value = value;
const_init->type = type_flatten(value->type);
const_init->kind = CONST_INIT_VALUE;
}
static bool is_empty_initializer_list(Expr *value)
{
return value->initializer_expr.init_type == INITIALIZER_CONST && value->initializer_expr.initializer->kind == CONST_INIT_ZERO;
}
static void sema_create_const_initializer(ConstInitializer *const_init, Expr *initializer);
/**
* Update a struct element, e.g. { .a = 1 } or { .a[12] = { .b } }
*/
static inline void sema_update_const_initializer_with_designator_struct(ConstInitializer *const_init,
DesignatorElement **curr,
DesignatorElement **end,
Expr *value)
{
// Get the current path element that we're processing
DesignatorElement *element = curr[0];
assert(element->kind == DESIGNATOR_FIELD);
DesignatorElement **next_element = curr + 1;
bool is_last_path_element = next_element == end;
// Optimize in case this is a zero, e.g. [12].b = {}
if (is_last_path_element && is_empty_initializer_list(value))
{
const_init->kind = CONST_INIT_ZERO;
return;
}
Decl **elements = const_init->type->decl->strukt.members;
// Convert a zero struct and expand it into all its parts.
if (const_init->kind == CONST_INIT_ZERO)
{
// Allocate array containing all elements { a, b, c ... }
ConstInitializer **const_inits = MALLOC(sizeof(ConstInitializer *) * vec_size(elements));
VECEACH(elements, i)
{
// Create zero initializers for each of those { a: zeroinit, b: zeroinit, ... }
ConstInitializer *element_init = MALLOC(sizeof(ConstInitializer));
element_init->type = type_flatten(elements[i]->type);
element_init->kind = CONST_INIT_ZERO;
const_inits[i] = element_init;
}
// Change type to CONST_INIT_STRUCT since we expanded.
const_init->init_struct = const_inits;
const_init->kind = CONST_INIT_STRUCT;
}
// We should always have expanded the struct at this point.
assert(const_init->kind == CONST_INIT_STRUCT);
// Find the ConstInitializer to change
ConstInitializer *sub_element = const_init->init_struct[element->index];
// If this isn't the last element, we recurse.
if (!is_last_path_element)
{
sema_update_const_initializer_with_designator(sub_element, next_element, end, value);
return;
}
// Otherwise we update the value in that particular element.
sema_create_const_initializer_value(sub_element, value);
}
/**
* Update a union element, which is different from structs, since we're here
* only keeping a single value.
* Note that if we have two fields "a" and "b", then in this case { .b = 2, .a = 1 },
* we will completely discard the information in ".b = 2", even if there had been
* an overlap. In essence we're implicitly clearing the memory when we assign to .a, meaning
* we are allowed to completely overwrite the "memory" of .b
*/
static inline void sema_update_const_initializer_with_designator_union(ConstInitializer *const_init,
DesignatorElement **curr,
DesignatorElement **end,
Expr *value)
{
DesignatorElement *element = curr[0];
assert(element->kind == DESIGNATOR_FIELD);
ConstInitializer *sub_element = const_init->init_union.element;
// If it's an empty initializer, just clear everything back to CONST_INIT_ZERO
// That is we get for example: { .b = { 1, 3 }, .a = { } }. This would then simply
// become { }
DesignatorElement **next_element = curr + 1;
bool is_at_last_path_element = next_element == end;
if (is_at_last_path_element && is_empty_initializer_list(value))
{
const_init->kind = CONST_INIT_ZERO;
return;
}
// If we currently have a zero, then we create a sub element that is zero.
if (const_init->kind == CONST_INIT_ZERO)
{
sub_element = const_init->init_union.element = CALLOCS(ConstInitializer);
sub_element->kind = CONST_INIT_ZERO;
const_init->init_union.element = sub_element;
}
else if (element->index != const_init->init_union.index)
{
// We will zero out the sub element if it wasn't a union
sub_element->kind = CONST_INIT_ZERO;
}
// Update of the sub element.
sub_element->type = type_flatten(const_init->type->decl->strukt.members[element->index]->type);
// And the index
const_init->init_union.index = element->index;
// And the type
const_init->kind = CONST_INIT_UNION;
// If we're not at the last element in the path, descend further.
if (!is_at_last_path_element)
{
sema_update_const_initializer_with_designator(sub_element, next_element, end, value);
return;
}
// Otherwise just set the current type.
sema_create_const_initializer_value(sub_element, value);
}
/**
* Update an array { [2] = 1 }
*/
static inline void sema_update_const_initializer_with_designator_array(ConstInitializer *const_init,
DesignatorElement **curr,
DesignatorElement **end,
Expr *value)
{
DesignatorElement *element = curr[0];
ArrayIndex low_index = element->index;
ArrayIndex high_index = element->kind == DESIGNATOR_RANGE ? element->index_end : element->index;
assert(element->kind == DESIGNATOR_ARRAY || element->kind == DESIGNATOR_RANGE);
// Expand zero into array.
if (const_init->kind == CONST_INIT_ZERO)
{
const_init->kind = CONST_INIT_ARRAY;
const_init->init_array.elements = NULL;
}
Type *element_type = type_flatten(const_init->type->array.base);
DesignatorElement **next_element = curr + 1;
bool is_last_path_element = next_element == end;
// Get all the elements in the array
ConstInitializer **array_elements = const_init->init_array.elements;
unsigned array_count = vec_size(array_elements);
ArrayIndex insert_index = 0;
for (ArrayIndex index = low_index; index <= high_index; index++)
{
// Walk to the insert point or until we reached the end of the array.
while (insert_index < array_count && array_elements[insert_index]->init_array_value.index < index)
{
insert_index++;
}
// Pick up the initializer at the insert point.
ConstInitializer *initializer = insert_index < array_count ? array_elements[insert_index] : NULL;
ConstInitializer *inner_value;
// If we don't have an initializer, the location needs to be at the end.
// Create and append:
if (!initializer)
{
initializer = MALLOC(sizeof(ConstInitializer));
initializer->type = element_type;
initializer->kind = CONST_INIT_ARRAY_VALUE;
initializer->init_array_value.index = index;
inner_value = MALLOC(sizeof(ConstInitializer));
inner_value->type = element_type;
inner_value->kind = CONST_INIT_ZERO;
initializer->init_array_value.element = inner_value;
vec_add(array_elements, initializer);
array_count++;
}
else
{
// If we already have an initializer "found"
// it might be after the index. In this case, we
// need to do an insert.
if (initializer->init_array_value.index != insert_index)
{
assert(initializer->init_array_value.index > insert_index);
// First we add a null at the end.
vec_add(array_elements, NULL);
// Shift all values one step up:
for (unsigned i = array_count; i > insert_index; i--)
{
array_elements[i] = array_elements[i - 1];
}
// Then we create our new entry.
initializer = MALLOC(sizeof(ConstInitializer));
initializer->type = element_type;
initializer->kind = CONST_INIT_ARRAY_VALUE;
initializer->init_array_value.index = index;
inner_value = MALLOC(sizeof(ConstInitializer));
inner_value->type = element_type;
inner_value->kind = CONST_INIT_ZERO;
initializer->init_array_value.element = inner_value;
// And assign it to the location.
array_elements[insert_index] = initializer;
}
}
const_init->init_array.elements = array_elements;
inner_value = initializer->init_array_value.element;
// Update
if (!is_last_path_element)
{
sema_update_const_initializer_with_designator(inner_value, next_element, end, value);
continue;
}
sema_create_const_initializer_value(inner_value, value);
}
}
static inline void sema_update_const_initializer_with_designator(
ConstInitializer *const_init,
DesignatorElement **curr,
DesignatorElement **end,
Expr *value)
{
switch (const_init->type->type_kind)
{
case TYPE_STRUCT:
sema_update_const_initializer_with_designator_struct(const_init, curr, end, value);
return;
case TYPE_UNION:
sema_update_const_initializer_with_designator_union(const_init, curr, end, value);
return;
case TYPE_ARRAY:
sema_update_const_initializer_with_designator_array(const_init, curr, end, value);
return;
case TYPE_VECTOR:
TODO
default:
UNREACHABLE
}
}
/**
* Create a const initializer.
*/
static void sema_create_const_initializer(ConstInitializer *const_init, Expr *initializer)
{
const_init->kind = CONST_INIT_ZERO;
// Flatten the type since the external type might be typedef or a distinct type.
const_init->type = type_flatten(initializer->type);
// Loop through the initializers.
Expr **init_expressions = initializer->initializer_expr.initializer_expr;
VECEACH(init_expressions, i)
{
Expr *expr = init_expressions[i];
DesignatorElement **path = expr->designator_expr.path;
Expr *value = expr->designator_expr.value;
sema_update_const_initializer_with_designator(const_init, path, path + vec_size(path), value);
}
}
static void debug_dump_const_initializer(ConstInitializer *init, const char *name, unsigned indent)
{
for (unsigned i = 0; i < indent; i++) printf(" ");
if (name)
{
printf("%s : %s", name, init->type->name ?: "WTF");
}
else
{
printf("%s", init->type->name ?: "WTF");
}
switch (init->kind)
{
case CONST_INIT_ZERO:
printf(" = 0\n");
return;
case CONST_INIT_UNION:
{
printf(" ->\n");
Decl** members = init->type->decl->strukt.members;
debug_dump_const_initializer(init->init_union.element, members[init->init_union.index]->name, indent + 1);
return;
}
case CONST_INIT_STRUCT:
{
printf(" ->\n");
Decl** members = init->type->decl->strukt.members;
unsigned member_count = vec_size(members);
for (unsigned i = 0; i < member_count; i++)
{
debug_dump_const_initializer(init->init_struct[i], members[i]->name, indent + 1);
}
return;
}
case CONST_INIT_ARRAY_VALUE:
{
printf(" [%llu] ->\n", (unsigned long long)init->init_array_value.index);
debug_dump_const_initializer(init->init_array_value.element, "", indent + 1);
return;
}
case CONST_INIT_VALUE:
{
assert(init->init_value->expr_kind == EXPR_CONST);
printf(" = ");
expr_const_fprint(stdout, &init->init_value->const_expr);
puts("");
return;
}
case CONST_INIT_ARRAY:
printf(" [//] ->\n");
{
VECEACH(init->init_array.elements, i)
{
debug_dump_const_initializer(init->init_array.elements[i], NULL, indent + 1);
}
}
return;
case CONST_INIT_ARRAY_FULL:
printf(" [: ->\n");
{
VECEACH(init->init_array_full, i)
{
debug_dump_const_initializer(init->init_array_full[i], NULL, indent + 1);
}
}
return;
}
UNREACHABLE
}
static bool sema_expr_analyse_designated_initializer(Context *context, Type *assigned, Expr *initializer)
{
Expr **init_expressions = initializer->initializer_expr.initializer_expr;
Type *original = assigned->canonical;
bool is_structlike = type_is_structlike(assigned->canonical);
initializer->pure = true;
initializer->constant = true;
ArrayIndex max_index = -1;
VECEACH(init_expressions, i)
{
Expr *expr = init_expressions[i];
Type *result = sema_expr_analyse_designator(context, original, expr, &max_index);
if (!result) return false;
if (!sema_analyse_expr_of_required_type(context, result, expr->designator_expr.value, true)) return false;
expr->pure &= expr->designator_expr.value->pure;
expr->constant &= expr->designator_expr.value->constant;
expr->failable |= expr->designator_expr.value->failable;
expr->resolve_status = RESOLVE_DONE;
initializer->pure &= expr->pure;
initializer->constant &= expr->constant;
}
if (!is_structlike && initializer->type->type_kind == TYPE_INFERRED_ARRAY)
{
initializer->type = sema_type_lower_by_size(initializer->type, max_index + 1);
}
if (initializer->constant)
{
ConstInitializer *const_init = MALLOC(sizeof(ConstInitializer));
sema_create_const_initializer(const_init, initializer);
initializer->initializer_expr.init_type = INITIALIZER_CONST;
initializer->initializer_expr.initializer = const_init;
return true;
}
initializer->initializer_expr.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)
{
initializer->pure = true;
initializer->constant = true;
Expr **elements = initializer->initializer_expr.initializer_expr;
Decl **members = assigned->strukt.members;
initializer->initializer_expr.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.
Expr *element = elements[i];
if (i >= expected_members)
{
SEMA_ERROR(element, "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], 0)) return false;
initializer->pure &= element->pure;
initializer->constant &= element->constant;
initializer->failable |= element->failable;
}
// 6. There's the case of too few values as well. Mark the last field 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;
}
if (initializer->constant)
{
ConstInitializer *const_init = CALLOCS(ConstInitializer);
const_init->kind = CONST_INIT_STRUCT;
const_init->type = type_flatten(initializer->type);
ConstInitializer **inits = MALLOC(sizeof(ConstInitializer *) * vec_size(elements));
VECEACH(elements, i)
{
Expr *expr = elements[i];
if (expr->expr_kind == EXPR_INITIALIZER_LIST)
{
assert(expr->constant);
inits[i] = expr->initializer_expr.initializer;
continue;
}
ConstInitializer *element_init = MALLOC(sizeof(ConstInitializer));
sema_create_const_initializer_value(element_init, expr);
inits[i] = element_init;
}
const_init->init_struct = inits;
initializer->initializer_expr.init_type = INITIALIZER_CONST;
initializer->initializer_expr.initializer = const_init;
}
// 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)
{
initializer->pure = true;
initializer->constant = true;
Expr **elements = initializer->initializer_expr.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->initializer_expr.init_type = INITIALIZER_NORMAL;
unsigned size = vec_size(elements);
unsigned expected_members = assigned->array.len;
if (assigned->type_kind != TYPE_ARRAY) expected_members = size;
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)
{
Expr *element = elements[i];
if (i >= expected_members)
{
SEMA_ERROR(element, "Too many elements in initializer, expected only %d.", expected_members);
return false;
}
if (!sema_analyse_expr_of_required_type(context, inner_type, element, true)) return false;
initializer->failable |= element->failable;
initializer->pure &= element->pure;
initializer->constant &= element->constant;
}
if (expected_members > size)
{
SEMA_ERROR(elements[size - 1], "Too few elements in initializer, %d elements are needed.", expected_members);
return false;
}
if (initializer->constant)
{
ConstInitializer *const_init = CALLOCS(ConstInitializer);
const_init->kind = CONST_INIT_ARRAY_FULL;
const_init->type = type_flatten(initializer->type);
ConstInitializer **inits = MALLOC(sizeof(ConstInitializer *) * vec_size(elements));
VECEACH(elements, i)
{
Expr *expr = elements[i];
if (expr->expr_kind == EXPR_INITIALIZER_LIST)
{
assert(expr->constant);
inits[i] = expr->initializer_expr.initializer;
continue;
}
ConstInitializer *element_init = MALLOC(sizeof(ConstInitializer));
sema_create_const_initializer_value(element_init, expr);
inits[i] = element_init;
}
const_init->init_array_full = inits;
initializer->initializer_expr.init_type = INITIALIZER_CONST;
initializer->initializer_expr.initializer = const_init;
}
// 7. Done!
return true;
}
static inline bool sema_expr_analyse_initializer(Context *context, Type *external_type, Type *assigned, Expr *expr)
{
Expr **init_expressions = expr->initializer_expr.initializer_expr;
unsigned init_expression_count = vec_size(init_expressions);
// 1. Zero size init will initialize to empty.
if (init_expression_count == 0)
{
external_type = sema_type_lower_by_size(external_type, 0);
assigned = sema_type_lower_by_size(assigned, 0);
expr_set_type(expr, external_type);
ConstInitializer *initializer = CALLOCS(ConstInitializer);
initializer->kind = CONST_INIT_ZERO;
initializer->type = type_flatten(expr->type);
expr->initializer_expr.init_type = INITIALIZER_CONST;
expr->initializer_expr.initializer = initializer;
expr->constant = true;
expr->pure = true;
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_DESIGNATOR)
{
expr_set_type(expr, external_type);
return sema_expr_analyse_designated_initializer(context, assigned, expr);
}
external_type = sema_type_lower_by_size(external_type, init_expression_count);
assigned = sema_type_lower_by_size(assigned, init_expression_count);
expr_set_type(expr, external_type);
// 3. Otherwise use the plain initializer.
if (assigned->type_kind == TYPE_ARRAY || assigned->type_kind == TYPE_INFERRED_ARRAY)
{
return sema_expr_analyse_array_plain_initializer(context, assigned, expr);
}
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 = type_flatten(to);
switch (assigned->type_kind)
{
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_ARRAY:
case TYPE_INFERRED_ARRAY:
return sema_expr_analyse_initializer(context, to, assigned, expr);
default:
break;
}
// Fix error on compound literals
SEMA_ERROR(expr, "'%s' cannot use compound literal initialization, did you intend to use a cast?", type_to_error_string(to));
return false;
}
static inline bool sema_expr_analyse_expr_list(Context *context, Type *to, Expr *expr)
{
bool success = true;
ByteSize last = vec_size(expr->expression_list) - 1;
bool constant = true;
bool pure = true;
VECEACH(expr->expression_list, i)
{
Expr *checked_expr = expr->expression_list[i];
success &= sema_analyse_expr_of_required_type(context, i == last ? to : NULL, checked_expr, 0);
expr->failable |= checked_expr->failable;
constant &= checked_expr->constant;
pure &= checked_expr->pure;
}
expr->pure = pure;
expr->constant = constant;
return success;
}
static inline bool sema_expr_analyse_cast(Context *context, Type *to, Expr *expr)
{
Expr *inner = expr->cast_expr.expr;
bool success = sema_resolve_type_info(context, expr->cast_expr.type_info);
success &= sema_analyse_expr(context, NULL, inner);
expr->pure = inner->pure;
expr->constant = inner->constant;
if (!success) return false;
Type *target_type = expr->cast_expr.type_info->type;
if (!cast_may_explicit(inner->type, target_type))
{
SEMA_ERROR(expr, "Cannot cast %s to %s.", type_quoted_error_string(inner->type), type_quoted_error_string(target_type));
return false;
}
cast(inner, target_type);
expr_replace(expr, inner);
return true;
}
static inline bool sema_expr_analyse_slice_assign(Context *context, Expr *expr, Type *left_type, Expr *right, ExprFailableStatus lhs_is_failable)
{
// 1. Evaluate right side to required type.
if (!sema_analyse_expr_of_required_type(context, left_type->array.base, right, lhs_is_failable != FAILABLE_NO)) return false;
Expr *left = expr->binary_expr.left;
expr_copy_types(expr, right);
expr->expr_kind = EXPR_SLICE_ASSIGN;
expr->slice_assign_expr.left = left;
expr->slice_assign_expr.right = right;
return true;
}
bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *left_type, Expr *right, ExprFailableStatus lhs_is_failable)
{
if (expr && expr->binary_expr.left->expr_kind == EXPR_SLICE)
{
return sema_expr_analyse_slice_assign(context, expr, left_type, right, lhs_is_failable);
}
// 1. Evaluate right side to required type.
if (!sema_analyse_expr(context, left_type, right)) return false;
if (right->failable && lhs_is_failable != FAILABLE_YES)
{
if (lhs_is_failable == FAILABLE_UNWRAPPED)
{
SEMA_ERROR(expr->binary_expr.left, "The variable is unwrapped in this context, if you don't want to unwrap it, use () around the variable to suppress unwrapping, like 'catch err = (x)' and 'try (x)'.");
return false;
}
if (!left_type) left_type = right->type;
SEMA_ERROR(right, "'%s!' cannot be converted into '%s'.", type_to_error_string(right->type), type_to_error_string(left_type));
return false;
}
// 2. Evaluate right hand side, making special concession for inferred arrays.
do
{
if (!left_type) break;
if (left_type->canonical->type_kind == TYPE_INFERRED_ARRAY && right->type->canonical->type_kind == TYPE_ARRAY)
{
if (left_type->canonical->array.base == right->type->canonical->array.base) break;
}
assert(right->type->canonical->type_kind != TYPE_INFERRED_ARRAY);
if (!cast_implicit(right, left_type)) return false;
} while (0);
// 3. Set the result to the type on the right side.
if (expr) expr_copy_types(expr, right);
return true;
}
static inline bool sema_expr_begin_analyse(Expr *expr)
{
switch (expr->resolve_status)
{
case RESOLVE_NOT_DONE:
expr->resolve_status = RESOLVE_RUNNING;
return true;
case RESOLVE_RUNNING:
SEMA_ERROR(expr, "Recursive resolution of expression");
return expr_poison(expr);
case RESOLVE_DONE:
return false;
}
UNREACHABLE
}
static inline bool sema_expr_analyse_ct_identifier_lvalue(Context *context, Expr *expr)
{
if (!sema_expr_begin_analyse(expr)) return expr_ok(expr);
Decl *ambiguous_decl = NULL;
Decl *private_symbol = NULL;
DEBUG_LOG("Now resolving %s", TOKSTR(expr->ct_ident_expr.identifier));
Decl *decl = sema_resolve_normal_symbol(context,
expr->ct_ident_expr.identifier,
NULL, false);
// Skip if poisoned.
if (!decl_ok(decl)) return false;
if (!decl)
{
SEMA_ERROR(expr, "The compile time variable '%s' was not defined in this scope.", TOKSTR(expr->ct_ident_expr.identifier));
return expr_poison(expr);
}
if (decl->var.scope_depth < context->active_scope.depth)
{
SEMA_ERROR(expr, "Cannot modify '%s' inside of a runtime scope.", decl->name);
return false;
}
expr->ct_ident_expr.decl = decl;
expr->resolve_status = RESOLVE_DONE;
return true;
}
static bool sema_expr_analyse_ct_identifier_assign(Context *context, Expr *expr, Expr *left, Expr *right)
{
if (!sema_expr_analyse_ct_identifier_lvalue(context, left)) return false;
// 3. Evaluate right side to required type.
if (!sema_expr_analyse_assign_right_side(context, expr, left->type, right, true)) return false;
left->ct_ident_expr.decl->var.init_expr = right;
expr_replace(expr, right);
return true;
}
static bool sema_expr_analyse_ct_type_identifier_assign(Context *context, Expr *expr, Expr *left, Expr *right)
{
TypeInfo *info = left->type_expr;
if (info->kind != TYPE_INFO_IDENTIFIER || info->unresolved.path || TOKTYPE(info->unresolved.name_loc) != TOKEN_CT_TYPE_IDENT)
{
SEMA_ERROR(left, "A type cannot be assigned to.");
return false;
}
TokenId token = info->unresolved.name_loc;
if (!sema_analyse_expr_value(context, NULL, right)) return false;
if (right->expr_kind != EXPR_TYPEINFO)
{
SEMA_ERROR(right, "Expected a type here.");
return false;
}
Decl *decl = sema_resolve_normal_symbol(context, token, NULL, false);
if (!decl)
{
decl = decl_new(DECL_VAR, token, VISIBLE_LOCAL);
decl->var.kind = VARDECL_LOCAL_CT_TYPE;
if (!sema_add_local(context, decl)) return false;
}
decl = sema_resolve_normal_symbol(context, token, NULL, true);
decl->var.init_expr = right;
expr->expr_kind = EXPR_NOP;
expr->type = type_void;
return true;
}
/**
* Analyse a = b
* @return true if analysis works
*/
static bool sema_expr_analyse_assign(Context *context, Expr *expr, Expr *left, Expr *right)
{
expr->constant = false;
expr->pure = false;
// 1. Evaluate left side
if (left->expr_kind == EXPR_CT_IDENT)
{
return sema_expr_analyse_ct_identifier_assign(context, expr, left, right);
}
if (left->expr_kind == EXPR_TYPEINFO)
{
return sema_expr_analyse_ct_type_identifier_assign(context, expr, left, right);
}
if (!sema_analyse_expr_value(context, NULL, left)) return false;
// 2. Check assignability
if (!expr_is_ltype(left))
{
SEMA_ERROR(left, "This expression is not assignable, did you make a mistake?");
return false;
}
ExprFailableStatus failable_status = expr_is_failable(left);
// 3. Evaluate right side to required type.
if (!sema_expr_analyse_assign_right_side(context, expr, left->type, right, failable_status)) return false;
if (failable_status == FAILABLE_UNWRAPPED && right->failable)
{
return sema_rewrap_var(context, left->identifier_expr.decl);
}
return true;
}
/**
* Analyse define $foo = ...
*
* @return true if analysis worked.
*/
static bool sema_expr_analyse_ct_common_assign(Context *context, Expr *expr, Expr *left)
{
// 1. Analyse left side.
if (!sema_expr_analyse_ct_identifier_lvalue(context, left)) return false;
Decl *left_var = left->ct_ident_expr.decl;
Expr *left_value = left_var->var.init_expr;
assert(left_value);
assert(!left_value->failable);
expr->binary_expr.left = left_value;
expr->binary_expr.operator = binaryop_assign_base_op(expr->binary_expr.operator);
if (!sema_expr_analyse_binary(context, NULL, expr)) return false;
if (expr->expr_kind != EXPR_CONST)
{
SEMA_ERROR(expr->binary_expr.right, "Expected a constant expression.");
return false;
}
left->ct_ident_expr.decl->var.init_expr = expr;
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)
{
expr->pure = false;
expr->constant = false;
if (left->expr_kind == EXPR_CT_IDENT)
{
return sema_expr_analyse_ct_common_assign(context, expr, left);
}
// 1. Analyse left side.
if (!sema_analyse_expr_value(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, expr_is_failable(left))) 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_copy_types(expr, left);
return true;
}
/**
* Handle 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)
{
if (left->expr_kind == EXPR_CT_IDENT)
{
return sema_expr_analyse_ct_common_assign(context, expr, left);
}
// 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;
// 4. Attempt to analyse and cast this side to the same type if possible.
if (!sema_analyse_expr(context, left->type, right)) return false;
// 3. Copy type & set properties.
expr_copy_types(expr, left);
expr->pure = false;
expr->constant = false;
expr->failable = left->failable | right->failable;
// 5. In the pointer case we have to treat this differently.
if (left_type_canonical->type_kind == TYPE_POINTER)
{
// 6. Convert any compile time values to runtime
if (!cast_implicitly_to_runtime(right)) return false;
// 7. 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;
}
// 8. Otherwise we cast rhs to lhs
if (!cast_implicit(right, left->type)) return false;
// 9. 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;
}
return true;
}
static Type *numeric_arithmetic_promotion(Type *type)
{
if (!type) return NULL;
switch (type->type_kind)
{
case ALL_SIGNED_INTS:
if (type->builtin.bitsize < platform_target.width_c_int) return type_cint();
return type;
case ALL_UNSIGNED_INTS:
if (type->builtin.bitsize < platform_target.width_c_int) return type_cuint();
return type;
case TYPE_F16:
// Promote F16 to a real type.
return type_float;
default:
return type;
}
}
static bool binary_arithmetic_promotion(Context *context, Expr *left, Expr *right, Type *left_type, Type *right_type, Expr *parent, const char *error_message)
{
Type *max = numeric_arithmetic_promotion(type_find_max_type(left_type, right_type));
if (!max || !type_is_numeric(max))
{
if (!error_message)
{
return sema_type_error_on_binop(parent);
}
SEMA_ERROR(parent, error_message, type_quoted_error_string(left->type), type_quoted_error_string(right->type));
return false;
}
return cast_implicit(left, max) & cast_implicit(right, max);
}
static bool sema_check_int_type_fit(Expr *expr, Type *target_type)
{
if (!target_type) return true;
Type *type = expr->type->canonical;
if (!type_is_any_integer(target_type->canonical) || !type_is_any_integer(type)) return true;
if (type_size(type) > type_size(target_type))
{
SEMA_ERROR(expr, "A '%s' cannot implicitly convert into '%s'.", type_to_error_string(expr->type), type_to_error_string(target_type));
return false;
}
return true;
}
static void unify_voidptr(Expr *left, Expr *right, Type **left_type_ref, Type **right_type_ref)
{
if (*left_type_ref == *right_type_ref) return;
if (*left_type_ref == type_voidptr)
{
cast(left, *right_type_ref);
*left_type_ref = *right_type_ref;
}
if (*right_type_ref == type_voidptr)
{
cast(right, *left_type_ref);
*right_type_ref = *left_type_ref;
}
}
/**
* Analyse a - b
* @return true if analysis succeeded
*/
static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
{
// 1. Analyse a and b.
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. Handle the ptr - x and ptr - other_pointer
if (left_type->type_kind == TYPE_POINTER)
{
// 3. ptr - other pointer
if (right_type->type_kind == TYPE_POINTER)
{
// 3a. Require that both types are the same.
unify_voidptr(left, right, &left_type, &right_type);
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;
}
// 3b. Set the type
expr_set_type(expr, type_iptrdiff);
// 3c. Set all other properties
expr_unify_binary_properties(expr, left, right);
// 3d. Otherwise check against assigned type so for example short x = &p - &q is an error.
return sema_check_int_type_fit(expr, to);
}
right_type = right->type->canonical;
// 4. Check that the right hand side is an integer.
if (!type_is_any_integer(right_type))
{
SEMA_ERROR(expr, "Cannot subtract '%s' from '%s'", type_to_error_string(right_type), type_to_error_string(left_type));
return false;
}
// 5. Make sure that the integer does not exceed iptrdiff in size.
if (type_size(right_type) > type_size(type_iptrdiff))
{
SEMA_ERROR(expr, "Cannot subtract a '%s' from a pointer, please first cast it to '%s'.", type_to_error_string(right_type), type_to_error_string(type_iptrdiff));
return false;
}
// 6. Convert to iptrdiff
if (!cast_implicit(right, type_iptrdiff)) return true;
// 7. Assign the type of the left side.
expr_copy_types(expr, left);
expr_unify_binary_properties(expr, left, right);
return true;
}
// 7. Attempt arithmetic promotion, to promote both to a common type.
if (!binary_arithmetic_promotion(context, left, right, left_type, right_type, expr, "The subtraction %s - %s is not possible."))
{
return false;
}
left_type = left->type->canonical;
expr_unify_binary(expr, left, right);
// 8. Handle constant folding.
if (both_const(left, right))
{
expr_replace(expr, left);
switch (left_type->type_kind)
{
case ALL_INTS:
bigint_sub(&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
}
}
return true;
}
/**
* Analyse a + b
* @return true if it succeeds.
*/
static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
{
// 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, 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;
expr->binary_expr.left = left;
expr->binary_expr.right = right;
}
// 3. 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)
{
// 3a. 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;
}
// 3b. 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_iptrdiff);
// No need to check the cast we just ensured it was an integer.
assert(success && "This should always work");
(void)success;
// 3c. Set the type and other properties.
expr_copy_types(expr, left);
expr_unify_binary_properties(expr, left, right);
return true;
}
// 4. Do an binary arithmetic promotion
if (!binary_arithmetic_promotion(context, left, right, left_type, right_type, expr, "Cannot do the addition %s + %s."))
{
return false;
}
// 5. Handle the "both const" case. We should only see ints and floats at this point.
if (both_const(left, right))
{
expr_replace(expr, left);
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
}
}
// 6. Set the type & other properties.
expr_unify_binary(expr, left, right);
return true;
}
/**
* Analyse a * b
*
* Will analyse a and b and then use arithmetic promotion on each.
* It will then try to promote both to a common type,
*
* @return true if analysis worked.
*/
static bool sema_expr_analyse_mult(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
{
// 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(context, left, right, left_type, right_type, expr, "Cannot multiply %s by %s"))
{
return false;
}
// 3. Handle constant folding.
if (both_const(left, right))
{
expr_replace(expr, left);
switch (left->const_expr.kind)
{
case ALL_INTS:
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
}
}
// 4. Update failable / pure / type etc.
expr_unify_binary(expr, left, right);
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(context, left, right, left_type, right_type, expr, "Cannot divide %s by %s."))
{
return false;
}
// 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:
break;
default:
UNREACHABLE
}
}
// 4. Perform constant folding.
if (both_const(left, right))
{
expr_replace(expr, left);
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.
expr_unify_binary(expr, left, right);
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;
Type *left_type = left->type->canonical;
Type *right_type = right->type->canonical;
// 2. Perform promotion to a common type.
if (!binary_arithmetic_promotion(context, left, right, left_type, right_type, expr, "Cannot divide %s by %s."))
{
return false;
}
// 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_unify_binary(expr, left, right);
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(context, left, right, left_type, right_type, expr, NULL))
{
return false;
}
// 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_unify_binary(expr, left, right);
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 lhs with target type.
if (!sema_analyse_expr(context, to, left)) return false;
// 2. Analyse rhs without target type.
if (!sema_analyse_expr(context, NULL, right)) return false;
// 3. Only integers may be shifted.
if (!both_any_integer(left, right))
{
return sema_type_error_on_binop(expr);
}
// 4. Promote lhs using the usual numeric promotion.
if (!cast_implicit(left, numeric_arithmetic_promotion(left->type))) return false;
expr->pure = left->pure & right->pure;
expr->constant = left->constant & right->constant;
expr->failable = left->failable | right->failable;
// 5. For a constant right hand side we will make a series of checks.
if (is_const(right))
{
// 5a. 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;
}
}
// 5b. 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;
}
// 6. Fold constant expressions.
if (is_const(left))
{
// 6a. 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;
}
// 6b. 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;
}
}
// 7. In the case of 2 >> x, we will want to cast to the target type if available, otherwise we use a ct->runtime promotion.
if (type_is_ct(left->type))
{
if (to)
{
// 7a. The target type exists, convert to this type after arithmetic promotion.
if (!cast_implicit(left, numeric_arithmetic_promotion(to))) return false;
left->original_type = to;
}
else
{
// 7b. Otherwise, use runtime promotion.
if (!cast_implicitly_to_runtime(left)) return false;
}
}
expr_copy_types(expr, left);
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;
expr->pure = false;
expr->constant = false;
expr->failable = left->failable | right->failable;
// 2. Ensure the left hand side is assignable
if (!expr_is_ltype(left))
{
SEMA_ERROR(left, "Expression is not assignable.");
return false;
}
// 3. Only integers may be shifted.
if (!both_any_integer(left, right)) return sema_type_error_on_binop(expr);
// 4. For a constant right hand side we will make a series of checks.
if (is_const(right))
{
// 4a. 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;
}
// 4b. 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;
}
}
// 5. Set the type using the left hand side.
expr_copy_types(expr, left);
return true;
}
static bool sema_expr_analyse_and(Context *context, Expr *expr, Expr *left, Expr *right)
{
if (!sema_analyse_expr(context, type_bool, left) || !sema_analyse_expr(context, type_bool, right)) return false;
if (!cast_implicit(left, type_bool) || !cast_implicit(right, type_bool)) return false;
expr_set_type(expr, type_bool);
if (both_const(left, right))
{
expr_replace(expr, left);
expr->const_expr.b &= right->const_expr.b;
}
expr_unify_binary_properties(expr, left, right);
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_unify_binary_properties(expr, left, right);
expr_set_type(expr, type_bool);
return true;
}
static void cast_to_max_bit_size(Context *context, 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_int_signed_by_bitsize(bit_size_right)
: type_int_unsigned_by_bitsize(bit_size_right);
bool success = cast_implicit(left, to);
assert(success);
return;
}
Type *to = right->type->type_kind < TYPE_U8
? type_int_signed_by_bitsize(bit_size_right)
: type_int_unsigned_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 = type_flatten(left->type);
Type *right_type = type_flatten(right->type);
// 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(context, 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->canonical, right->type->canonical);
// 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)
{
Type *effective_type = max;
if (max->type_kind == TYPE_DISTINCT)
{
effective_type = max->decl->distinct_decl.base_type;
}
switch (effective_type->type_kind)
{
case TYPE_POISONED:
return false;
case TYPE_VOID:
case TYPE_TYPEINFO:
case TYPE_TYPEDEF:
case TYPE_DISTINCT:
case TYPE_INFERRED_ARRAY:
UNREACHABLE
case TYPE_BOOL:
case TYPE_ENUM:
case TYPE_ERRTYPE:
case TYPE_FUNC:
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_ANYERR:
case TYPE_STRLIT:
case TYPE_ARRAY:
case TYPE_SUBARRAY:
case TYPE_TYPEID:
case TYPE_VIRTUAL_ANY:
case TYPE_VIRTUAL:
case TYPE_BITSTRUCT:
// 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 && left_type != type_voidptr && right_type != type_voidptr)
{
SEMA_ERROR(expr, "Cannot compare pointers of different types.");
return false;
}
break;
case TYPE_VECTOR:
TODO
}
}
// 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_unify_binary_properties(expr, left, right);
expr_set_type(expr, 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', it must be a pointer.", type_to_error_string(inner->type));
return false;
}
if (canonical->pointer == type_void)
{
SEMA_ERROR(inner, "A 'void*' cannot be dereferenced, you need to first cast it to a concrete type.");
return false;
}
// 2. This could be a constant, in which case it is a null which is an error.
if (inner->expr_kind == EXPR_CONST)
{
SEMA_ERROR(inner, "Dereferencing null is not allowed, did you do it by mistake?");
return false;
}
// 3. Now the type might not be a pointer because of a typedef,
// otherwise we need to use the canonical representation.
Type *deref_type = inner->type->type_kind != TYPE_POINTER ? inner->type : canonical;
// 4. And... set the type.
expr_set_type(expr, deref_type->pointer);
expr_copy_properties(expr, inner);
return true;
}
static inline bool sema_take_addr_of_var(Expr *expr, Decl *decl, bool *is_constant)
{
if (decl->decl_kind != DECL_VAR) return false;
*is_constant = false;
switch (decl->var.kind)
{
case VARDECL_GLOBAL:
if (decl->type == type_void)
{
SEMA_ERROR(expr, "You cannot take the address of a global of type '%s'.",
decl->var.failable ? "void!" : "void");
return false;
}
*is_constant = true;
return true;
case VARDECL_LOCAL:
if (decl->type == type_void)
{
SEMA_ERROR(expr, "You cannot take the address of a variable with type '%s'.",
decl->var.failable ? "void!" : "void");
return false;
}
return true;
case VARDECL_PARAM:
case VARDECL_PARAM_REF:
if (decl->type == type_void)
{
SEMA_ERROR(expr, "You cannot take the address of a parameter with type '%s'.",
decl->var.failable ? "void!" : "void");
return false;
}
return true;
case VARDECL_CONST:
*is_constant = true;
if (!decl->var.type_info)
{
SEMA_ERROR(expr, "The constant is not typed, either type it or use && to take the reference to a temporary.");
SEMA_PREV(decl, "The constant was defined here.");
return false;
}
assert(decl->type != type_void);
return true;
case VARDECL_PARAM_EXPR:
SEMA_ERROR(expr, "It is not possible to take the address of a captured expression, but you can use && to take a reference to the temporary value.");
return false;
case VARDECL_PARAM_CT:
case VARDECL_PARAM_CT_TYPE:
case VARDECL_LOCAL_CT_TYPE:
case VARDECL_LOCAL_CT:
// May not be reached due to EXPR_CT_IDENT being handled elsewhere.
UNREACHABLE;
case VARDECL_MEMBER:
case VARDECL_UNWRAPPED:
case VARDECL_REWRAPPED:
case VARDECL_ERASE:
UNREACHABLE
}
UNREACHABLE
}
static inline bool sema_take_addr_of_ident(Expr *inner, bool *is_constant)
{
Decl *decl = inner->identifier_expr.decl;
decl = decl_raw(decl);
switch (decl->decl_kind)
{
case DECL_ENUM_CONSTANT:
case DECL_FUNC:
*is_constant = true;
return true;
case DECL_VAR:
return sema_take_addr_of_var(inner, decl, is_constant);
default:
SEMA_ERROR(inner, "It is not possible to take the address of a '%s'.", type_to_error_string(inner->type));
return false;
}
}
static bool sema_take_addr_of(Expr *inner, bool *is_constant)
{
switch (inner->expr_kind)
{
case EXPR_CT_IDENT:
SEMA_ERROR(inner, "It's not possible to take the address of a compile time value.");
return false;
case EXPR_MACRO_EXPANSION:
SEMA_ERROR(inner, "It's not possible to take the address of a macro.");
return false;
case EXPR_IDENTIFIER:
case EXPR_CONST_IDENTIFIER:
return sema_take_addr_of_ident(inner, is_constant);
case EXPR_UNARY:
*is_constant = inner->constant;
if (inner->unary_expr.operator == UNARYOP_DEREF) return true;
break;
case EXPR_ACCESS:
return sema_take_addr_of(inner->access_expr.parent, is_constant);
case EXPR_GROUP:
return sema_take_addr_of(inner->group_expr, is_constant);
case EXPR_SUBSCRIPT:
{
bool index_was_const = false;
if (!sema_take_addr_of(inner->subscript_expr.expr, &index_was_const)) return false;
*is_constant = index_was_const && inner->constant;
return true;
}
case EXPR_SLICE:
{
*is_constant = false;
bool dummy;
return sema_take_addr_of(inner->slice_expr.expr, &dummy);
}
default:
break;
}
SEMA_ERROR(inner, "To take the address of a temporary value, use '&&' instead of '&'.", type_to_error_string(inner->type));
return false;
}
/**
* Analyse &a
* @return true if analysis succeeds.
*/
static bool sema_expr_analyse_addr(Context *context, Type *to, Expr *expr, Expr *inner)
{
// 1. Evaluate the expression
REDO:
switch (inner->expr_kind)
{
case EXPR_POISONED:
return false;
case EXPR_GROUP:
// We want to collapse any grouping here.
expr_replace(inner, inner->group_expr);
goto REDO;
default:
if (!sema_analyse_expr_value(context, NULL, inner)) return expr_poison(expr);
}
// 2. Take the address.
bool is_constant = false;
if (!sema_take_addr_of(inner, &is_constant)) return expr_poison(expr);
// 3. Get the pointer of the underlying type.
expr_set_type(expr, type_get_ptr(inner->type));
expr->constant = is_constant;
expr->pure = inner->pure;
expr->failable = inner->failable;
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;
}
expr_copy_properties(expr, inner);
if (inner->expr_kind != EXPR_CONST)
{
expr_copy_types(expr, inner);
return true;
}
expr_replace(expr, inner);
switch (expr->const_expr.kind)
{
case ALL_INTS:
bigint_negate(&expr->const_expr.i, &inner->const_expr.i);
if (expr_const_int_overflowed(&expr->const_expr))
{
SEMA_ERROR(expr, "%s does not fit in '%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)
{
expr_copy_properties(expr, inner);
Type *canonical = inner->type->canonical;
if (canonical->type_kind == TYPE_IXX)
{
SEMA_ERROR(expr, "Cannot bit negate an untyped integer literal, please first cast it to a concrete type.", type_to_error_string(inner->type));
return false;
}
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_copy_types(expr, inner);
return true;
}
expr_replace(expr, inner);
switch (expr->const_expr.kind)
{
case ALL_SIGNED_INTS:
bigint_not(&expr->const_expr.i, &inner->const_expr.i, canonical->builtin.bitsize, true);
break;
case ALL_UNSIGNED_INTS:
bigint_not(&expr->const_expr.i, &inner->const_expr.i, canonical->builtin.bitsize, false);
break;
case TYPE_BOOL:
expr->const_expr.b = !expr->const_expr.b;
break;
case TYPE_IXX:
default:
UNREACHABLE
}
return true;
}
static bool sema_expr_analyse_not(Expr *expr, Expr *inner)
{
expr_copy_properties(expr, inner);
expr_set_type(expr, 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_STRLIT:
expr->const_expr.b = !inner->const_expr.string.len;
break;
case TYPE_ARRAY:
expr->const_expr.b = !inner->const_expr.bytes.len;
break;
case TYPE_ERRTYPE:
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 (type_flatten(canonical)->type_kind)
{
case TYPE_POISONED:
case TYPE_IXX:
case TYPE_FXX:
case TYPE_TYPEDEF:
case TYPE_ANYERR:
case TYPE_DISTINCT:
case TYPE_INFERRED_ARRAY:
case TYPE_BITSTRUCT:
UNREACHABLE
case TYPE_FUNC:
case TYPE_ARRAY:
case TYPE_POINTER:
case TYPE_VIRTUAL:
case TYPE_VIRTUAL_ANY:
case TYPE_SUBARRAY:
case TYPE_BOOL:
case TYPE_VECTOR:
case ALL_REAL_FLOATS:
case ALL_UNSIGNED_INTS:
case ALL_SIGNED_INTS:
return true;
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_VOID:
case TYPE_STRLIT:
case TYPE_ENUM:
case TYPE_ERRTYPE:
case TYPE_TYPEID:
case TYPE_TYPEINFO:
SEMA_ERROR(expr, "Cannot use 'not' on %s", type_to_error_string(inner->type));
return false;
}
UNREACHABLE
}
static inline bool sema_expr_analyse_ct_incdec(Context *context, Expr *expr, Expr *inner)
{
assert(inner->expr_kind == EXPR_CT_IDENT);
if (!sema_expr_analyse_ct_identifier_lvalue(context, inner)) return false;
Decl *var = inner->ct_ident_expr.decl;
Expr *start_value = var->var.init_expr;
assert(start_value->expr_kind == EXPR_CONST);
switch (start_value->const_expr.kind)
{
case ALL_INTS:
break;
default:
SEMA_ERROR(expr, "The compile time variable '%s' does not hold an integer.", var->name);
return false;
}
Expr *end_value = expr_copy(start_value);
// Make the change.
BigInt change;
bigint_init_signed(&change, expr->unary_expr.operator == UNARYOP_DEC ? -1 : 1);
bigint_add(&end_value->const_expr.i, &start_value->const_expr.i, &change);
if (!expr_const_int_valid(end_value, start_value->type)) return false;
var->var.init_expr = end_value;
if (expr->expr_kind == EXPR_POST_UNARY)
{
expr_replace(expr, start_value);
}
else
{
expr_replace(expr, end_value);
}
return true;
}
/**
* Analyse foo++ foo-- --foo ++foo
* @return false if analysis fails.
*/
static inline bool sema_expr_analyse_incdec(Context *context, Expr *expr, Expr *inner)
{
expr->constant = false;
expr->pure = false;
expr->failable = inner->failable;
if (!expr_is_ltype(inner))
{
SEMA_ERROR(inner, "Expression cannot be assigned to.");
return false;
}
if (inner->expr_kind == EXPR_CT_IDENT)
{
return sema_expr_analyse_ct_incdec(context, expr, inner);
}
Type *type = type_flatten(inner->type);
if (!type_is_numeric(type) && type->type_kind != TYPE_POINTER)
{
SEMA_ERROR(inner, "Expression must be a number or a pointer.");
return false;
}
expr_copy_types(expr, inner);
return true;
}
static inline bool sema_expr_analyse_taddr(Context *context, Type *to, Expr *expr, Expr *inner)
{
Type *inferred_type = NULL;
if (to->type_kind == TYPE_POINTER)
{
inferred_type = to->pointer;
}
if (!sema_analyse_expr(context, inferred_type, inner)) return false;
expr_copy_properties(expr, inner);
expr_set_type(expr, type_get_ptr(inner->type));
return true;
}
static bool unclear_op_precedence(Expr *left_side, Expr * main_expr, Expr *right_side)
{
int precedence_main = BINOP_PREC_REQ[main_expr->binary_expr.operator];
if (left_side->expr_kind == EXPR_BINARY)
{
int precedence_left = BINOP_PREC_REQ[left_side->binary_expr.operator];
return precedence_left && (precedence_left == precedence_main);
}
if (right_side->expr_kind == EXPR_BINARY)
{
int precedence_right = BINOP_PREC_REQ[right_side->binary_expr.operator];
return precedence_right && (precedence_right == precedence_main);
}
return false;
}
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;
// check if both sides have a binary operation where the precedence is unclear. Example: a ^ b | c
if (unclear_op_precedence(left, expr, right))
{
SEMA_ERROR(expr, "You need to add explicit parentheses to clarify precedence.");
return expr_poison(expr);
}
// Don't push down bool conversions for example.
if (to && !type_is_numeric(to)) to = NULL;
switch (expr->binary_expr.operator)
{
case BINARYOP_ASSIGN:
return sema_expr_analyse_assign(context, expr, left, right);
case BINARYOP_MULT:
return sema_expr_analyse_mult(context, to, expr, left, right);
case BINARYOP_ADD:
return sema_expr_analyse_add(context, to, expr, left, right);
case BINARYOP_ADD_ASSIGN:
case BINARYOP_SUB_ASSIGN:
return sema_expr_analyse_add_sub_assign(context, expr, left, right);
case BINARYOP_SUB:
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_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;
switch (expr->unary_expr.operator)
{
case UNARYOP_DEREF:
if (!sema_analyse_expr(context, NULL, inner)) return false;
return sema_expr_analyse_deref(context, expr, inner);
case UNARYOP_ADDR:
return sema_expr_analyse_addr(context, to, expr, inner);
case UNARYOP_NEG:
if (!sema_analyse_expr(context, to, inner)) return false;
return sema_expr_analyse_neg(context, to, expr, inner);
case UNARYOP_BITNEG:
if (!sema_analyse_expr(context, to, inner)) return false;
return sema_expr_analyse_bit_not(context, to, expr, inner);
case UNARYOP_NOT:
if (!sema_analyse_expr(context, NULL, inner)) return false;
return sema_expr_analyse_not(expr, inner);
case UNARYOP_DEC:
case UNARYOP_INC:
if (!sema_analyse_expr_value(context, NULL, inner)) return false;
return sema_expr_analyse_incdec(context, expr, inner);
case UNARYOP_TADDR:
return sema_expr_analyse_taddr(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_value(context, NULL, inner)) return false;
return sema_expr_analyse_incdec(context, expr, inner);
}
static inline bool sema_expr_analyse_try_assign(Context *context, Expr *expr, bool implicitly_create_variable)
{
expr->constant = false;
Expr *init = expr->try_assign_expr.init;
Expr *lhs = expr->try_assign_expr.expr;
Type *lhs_type;
// If we have:
// a. In a context where implicit declaration of variables occur
// b. The LHS is an identifier without PATH
// c. And this variable does not exist in the scope.
// Create it (when we know the type)
bool create_variable;
if (implicitly_create_variable && lhs->expr_kind == EXPR_IDENTIFIER && !lhs->identifier_expr.path
&& !sema_resolve_normal_symbol(context, lhs->identifier_expr.identifier, NULL, false))
{
create_variable = true;
lhs_type = NULL;
}
else
{
// Otherwise we just analyse it.
create_variable = false;
if (expr->try_assign_expr.is_try)
{
if (!sema_analyse_expr_value(context, NULL, lhs)) return false;
}
else
{
if (!sema_analyse_expr_of_required_type(context, type_anyerr, lhs, true)) return false;
}
if (!expr_is_ltype(lhs))
{
SEMA_ERROR(lhs, "This expression is not assignable, did you make a mistake?");
return false;
}
ExprFailableStatus failable_status = expr_is_failable(lhs);
if (failable_status == FAILABLE_YES)
{
SEMA_ERROR(lhs, "A 'try' assignment is not possible with failable on the left hand side, did you intend 'try (variable = expr)'?");
return false;
}
lhs_type = lhs->type;
}
if (expr->try_assign_expr.is_try)
{
if (!sema_analyse_expr_of_required_type(context, lhs_type, init, true)) return false;
}
else
{
if (!sema_analyse_expr_of_required_type(context, NULL, init, true)) return false;
}
if (!init->failable)
{
SEMA_ERROR(init, "Expected a failable expression to '%s'.", expr->try_assign_expr.is_try ? "try" : "catch");
return false;
}
if (create_variable)
{
lhs_type = init->type;
Decl *decl = decl_new_var(lhs->identifier_expr.identifier, type_info_new_base(lhs_type, lhs->span), VARDECL_LOCAL, VISIBLE_LOCAL);
TODO
// try_expr->try_expr.implicit_decl = decl;
if (!sema_add_local(context, decl)) return false;
if (!sema_analyse_expr_value(context, NULL, lhs)) return false;
}
expr->constant = false;
expr->pure = init->pure & lhs->pure;
expr_set_type(expr, type_bool);
return true;
}
static inline bool sema_expr_analyse_try(Context *context, Expr *expr)
{
Expr *inner = expr->try_expr.expr;
if (!sema_analyse_expr(context, NULL, inner)) return false;
expr->pure = inner->pure;
expr->constant = false;
if (!inner->failable)
{
SEMA_ERROR(expr->try_expr.expr, "Expected a failable expression to '%s'.", expr->expr_kind == EXPR_TRY ? "try" : "catch");
return false;
}
expr_set_type(expr, expr->try_expr.is_try ? type_bool : type_anyerr);
return true;
}
static inline bool sema_expr_analyse_else(Context *context, Type *to, Expr *expr)
{
Expr *inner = expr->else_expr.expr;
bool success = sema_analyse_expr(context, to, inner);
expr->pure = inner->pure;
expr->constant = false;
if (!success) return false;
Type *type = inner->type;
if (!inner->failable)
{
SEMA_ERROR(inner, "No failable to 'else' in the expression, please remove the 'else'.");
return false;
}
if (expr->else_expr.is_jump)
{
expr->pure = false;
if (!sema_analyse_statement(context, expr->else_expr.else_stmt)) return false;
expr_copy_types(expr, inner);
return true;
}
// First we analyse the "else" and try to implictly cast.
if (!sema_analyse_expr(context, type, expr->else_expr.else_expr)) return false;
expr->pure &= expr->else_expr.else_expr->pure;
// Here we might need to insert casts.
Type *common = type_find_max_type(type, expr->else_expr.else_expr->type);
if (!cast_implicit(expr->else_expr.else_expr, common)) return false;
expr->type = common;
expr->original_type = type_find_max_type(inner->original_type, expr->else_expr.else_expr->original_type);
return cast_implicit(expr->else_expr.expr, common);
}
static inline bool sema_expr_analyse_guard(Context *context, Type *to, Expr *expr)
{
Expr *inner = expr->guard_expr.inner;
bool success = sema_analyse_expr(context, to, inner);
expr->guard_expr.defer = context->active_scope.defer_last;
if (!success) return false;
expr_copy_types(expr, inner);
expr->pure = false;
expr->constant = false;
if (!inner->failable)
{
SEMA_ERROR(expr, "No failable to rethrow before '!!' in the expression, please remove '!!'.");
return false;
}
if (!context->failable_return)
{
SEMA_ERROR(expr, "This expression implicitly returns with a failable result, but the function does not allow failable results. Did you mean to use 'else' instead?");
return false;
}
return true;
}
static inline bool sema_expr_analyse_const(Type *to, Expr *expr)
{
expr->constant = true;
expr->pure = true;
return true;
}
static inline bool sema_expr_analyse_type(Context *context, Expr *expr)
{
if (!sema_resolve_type_info(context, expr->typeid_expr))
{
return expr_poison(expr);
}
expr_set_type(expr, type_typeid);
return true;
}
static inline bool sema_expr_analyse_expr_block(Context *context, Type *to, Expr *expr)
{
bool success = true;
expr_set_type(expr, type_void);
bool saved_expr_failable_return = context->expr_failable_return;
Type *prev_expected_block_type = context->expected_block_type;
Ast **saved_returns = context_push_returns(context);
context->expected_block_type = to;
SCOPE_START_WITH_FLAGS(SCOPE_EXPR_BLOCK)
PUSH_CONTINUE(NULL);
PUSH_BREAK(NULL);
PUSH_NEXT(NULL, NULL);
VECEACH(expr->expr_block.stmts, i)
{
if (!sema_analyse_statement(context, expr->expr_block.stmts[i]))
{
success = false;
goto EXIT;
}
}
if (!context->active_scope.jump_end && to)
{
SEMA_ERROR(expr, "Expected the block to return with a value of type %s.", type_to_error_string(to));
success = false;
}
if (!vec_size(context->returns)) 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;
// Let's unify the return statements.
left_canonical = unify_returns(context, left_canonical);
if (!left_canonical)
{
success = false;
goto EXIT;
}
expr_set_type(expr, left_canonical);
EXIT:
POP_BREAKCONT();
POP_NEXT();
SCOPE_END;
context_pop_returns(context, saved_returns);
expr->failable = context->expr_failable_return;
context->expr_failable_return = saved_expr_failable_return;
context->expected_block_type = prev_expected_block_type;
expr->constant = false;
expr->pure = false;
return success;
}
static inline bool sema_expr_analyse_compound_literal(Context *context, 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_replace(expr, expr->expr_compound_literal.initializer);
return true;
}
static inline bool sema_expr_analyse_typeof(Context *context, Expr *expr)
{
if (!sema_analyse_expr(context, NULL, expr->typeof_expr)) return false;
expr->pure = expr->typeof_expr->pure;
expr->constant = expr->typeof_expr->constant;
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_set_type(expr, type_typeid);
return true;
}
static inline bool sema_expr_analyse_failable(Context *context, Type *to, Expr *expr)
{
Expr *inner = expr->failable_expr;
if (!sema_analyse_expr(context, NULL, inner)) return false;
expr->pure = inner->pure;
expr->constant = inner->constant;
if (inner->failable)
{
SEMA_ERROR(inner, "The inner expression is already a failable.");
}
Type *type = inner->type->canonical;
if (inner->expr_kind == EXPR_FAILABLE)
{
SEMA_ERROR(inner, "It looks like you added one too many '!' after the error.");
return false;
}
if (type->type_kind != TYPE_ERRTYPE && type->type_kind != TYPE_ANYERR)
{
SEMA_ERROR(inner, "You cannot use the '!' operator on expressions of type '%s'", type_to_error_string(type));
return false;
}
if (!to)
{
expr_set_type(expr, type_void);
return true;
}
expr->failable = true;
expr_set_type(expr, to);
return true;
}
static inline bool sema_expr_analyse_placeholder(Context *context, Type *to, Expr *expr)
{
const char *string = TOKSTR(expr->placeholder_expr.identifier);
if (string == kw_FILE)
{
expr_rewrite_to_string(expr, context->file->name);
return true;
}
if (string == kw_FUNC)
{
if (!context->active_function_for_analysis)
{
expr_rewrite_to_string(expr, "<GLOBAL>");
return true;
}
expr_rewrite_to_string(expr, context->active_function_for_analysis->name);
return true;
}
if (string == kw_LINEREAL)
{
expr_rewrite_to_int_const(expr, type_compint, TOKLOC(expr->placeholder_expr.identifier)->line);
return true;
}
if (string == kw_LINE)
{
if (context->macro_scope.depth)
{
expr_rewrite_to_int_const(expr, type_compint, context->macro_scope.original_inline_line);
}
else
{
expr_rewrite_to_int_const(expr, type_compint, TOKLOC(expr->placeholder_expr.identifier)->line);
}
return true;
}
Expr *value = stable_get(&global_context.compiler_defines, string);
if (!value)
{
SEMA_ERROR(expr, "The placeholder constant '%s' was not defined, did you mistype or forget to add it?", string);
return false;
}
expr_replace(expr, value);
if (expr->expr_kind == EXPR_CONST && type_kind_is_any_integer(expr->const_expr.kind) && value->const_expr.i.digit_count > 1)
{
bigint_init_bigint(&expr->const_expr.i, &value->const_expr.i);
}
return true;
}
typedef struct MiniLexer_
{
const char *chars;
uint32_t index;
} MiniLexer;
static inline char minilex_next(MiniLexer *lexer)
{
return lexer->chars[lexer->index++];
}
static inline char minilex_peek(MiniLexer *lexer, int32_t offset)
{
return lexer->chars[lexer->index + offset];
}
static inline bool minilex_match(MiniLexer *lexer, char c)
{
if (lexer->chars[lexer->index] != c) return false;
lexer->index++;
return true;
}
static inline void minilex_skip_whitespace(MiniLexer *lexer)
{
char c;
while (1)
{
c = lexer->chars[lexer->index];
if ((c != ' ') && (c != '\t')) break;
lexer->index++;
}
}
uint64_t minilex_parse_number(MiniLexer *lexer, uint64_t max)
{
assert(max < UINT64_MAX);
ByteSize value = 0;
while (is_number(minilex_peek(lexer, 0)))
{
ByteSize old_value = value;
value = value * 10 + minilex_next(lexer) - '0';
if (old_value > value || value > max)
{
return UINT64_MAX;
}
}
return value;
}
static inline bool sema_analyse_idents_string(Context *context, MiniLexer *lexer, ExprFlatElement **elements_ref)
{
ExprFlatElement *elements = NULL;
char c;
ExprFlatElement element;
while (1)
{
minilex_skip_whitespace(lexer);
if (minilex_match(lexer, '['))
{
minilex_skip_whitespace(lexer);
if (!is_number(minilex_peek(lexer, 0))) return false;
uint64_t value = minilex_parse_number(lexer, MAX_ARRAYINDEX);
if (value == UINT64_MAX) return false;
if (!minilex_match(lexer, ']')) return false;
element.array = true;
element.index = (ArrayIndex)value;
}
else
{
scratch_buffer_clear();
while (is_alphanum_(minilex_peek(lexer, 0)))
{
scratch_buffer_append_char(minilex_next(lexer));
}
if (!global_context.scratch_buffer_len) return false;
TokenType token_type;
const char *ident = symtab_find(global_context.scratch_buffer,
global_context.scratch_buffer_len,
fnv1a(global_context.scratch_buffer, global_context.scratch_buffer_len),
&token_type);
if (!ident || token_type != TOKEN_IDENT) return false;
element.array = false;
element.ident = ident;
}
vec_add(elements, element);
minilex_skip_whitespace(lexer);
if (minilex_match(lexer, '\0')) break;
if (!minilex_match(lexer, '.') && minilex_peek(lexer, 0) != '[') return false;
}
*elements_ref = elements;
return true;
}
static inline bool sema_analyse_identifier_path_string(Context *context, Expr *expr, Decl **decl_ref, Type **type_ref, ExprFlatElement **idents_ref)
{
const char *chars = expr->const_expr.string.chars;
uint32_t len = expr->const_expr.string.len;
if (!len)
{
SEMA_ERROR(expr, "Expected a name here.");
return false;
}
// TODO HANDLE VIRTUAL!
MiniLexer lexer = { .chars = chars };
// Do we parse a path?
Path *path = NULL;
// Skip past the start.
while (is_lower_(minilex_peek(&lexer, 0))) minilex_next(&lexer);
minilex_skip_whitespace(&lexer);
// Yes, parse a path.
if (minilex_match(&lexer, ':'))
{
if (!minilex_match(&lexer, ':')) goto FAIL_PARSE;
lexer.index = 0;
NEXT_PART:
scratch_buffer_clear();
minilex_skip_whitespace(&lexer);
while (is_lower(minilex_peek(&lexer, 0)))
{
scratch_buffer_append_char(minilex_next(&lexer));
}
minilex_skip_whitespace(&lexer);
if (!(minilex_match(&lexer, ':') && minilex_match(&lexer, ':')))
{
UNREACHABLE;
}
uint32_t start_index = lexer.index;
minilex_skip_whitespace(&lexer);
while (is_lower_(minilex_peek(&lexer, 0))) minilex_next(&lexer);
minilex_skip_whitespace(&lexer);
if (minilex_match(&lexer, ':'))
{
if (!minilex_match(&lexer, ':')) goto FAIL_PARSE;
scratch_buffer_append_len("::", 2);
lexer.index = start_index;
goto NEXT_PART;
}
lexer.index = start_index;
path = path_create_from_string(scratch_buffer_to_string(), global_context.scratch_buffer_len, expr->span);
}
else
{
lexer.index = 0;
}
scratch_buffer_clear();
minilex_skip_whitespace(&lexer);
while (is_alphanum_(minilex_peek(&lexer, 0)))
{
scratch_buffer_append_char(minilex_next(&lexer));
}
if (!global_context.scratch_buffer_len) goto FAIL;
TokenType token_type;
const char *symbol = symtab_find(global_context.scratch_buffer,
global_context.scratch_buffer_len,
fnv1a(global_context.scratch_buffer, global_context.scratch_buffer_len),
&token_type);
if (!symbol)
{
SEMA_ERROR(expr, "'%s' could not be found, did you misspell it?", chars);
return false;
}
Type *type = NULL;
Decl *decl = NULL;
if (token_is_type(token_type))
{
type = type_from_token(token_type);
}
else
{
decl = TRY_DECL_OR(sema_resolve_string_symbol(context, symbol, expr->span, path), false);
if (decl->decl_kind == DECL_TYPEDEF)
{
type = decl->typedef_decl.type_info->type;
decl = NULL;
}
}
if (type || decl_is_user_defined_type(decl))
{
if (decl)
{
type = decl->type;
decl = NULL;
}
while (1)
{
minilex_skip_whitespace(&lexer);
if (minilex_match(&lexer, '*'))
{
type = type_get_ptr(type);
continue;
}
if (!minilex_match(&lexer, '[')) break;
minilex_skip_whitespace(&lexer);
if (minilex_match(&lexer, ']'))
{
type = type_get_subarray(type);
continue;
}
ByteSize array_size = 0;
while (is_number(minilex_peek(&lexer, 0)))
{
ByteSize old_array_size = array_size;
array_size = array_size * 10 + minilex_next(&lexer) - '0';
if (old_array_size > array_size || array_size > MAX_ARRAYINDEX)
{
SEMA_ERROR(expr, "Array index out of bounds.");
return false;
}
}
minilex_skip_whitespace(&lexer);
if (!minilex_match(&lexer, ']')) goto FAIL_PARSE;
type = type_get_array(type, array_size);
}
}
minilex_skip_whitespace(&lexer);
if (minilex_match(&lexer, '\0')) goto EXIT;
minilex_skip_whitespace(&lexer);
if (!minilex_match(&lexer, '.'))
{
SEMA_ERROR(expr, "A '.' was expected after '%s'.", symbol);
return false;
}
if (!sema_analyse_idents_string(context, &lexer, idents_ref))
{
SEMA_ERROR(expr, "The path to an existing member was expected after '%s', did you make a mistake?", symbol);
return false;
}
EXIT:
*decl_ref = decl;
*type_ref = type;
return true;
FAIL:
SEMA_ERROR(expr, "'%s' is not a known identifier, did you misspell it?", chars);
return false;
FAIL_PARSE:
SEMA_ERROR(expr, "'%s' could not be parsed as an identifier, did you make a mistake?", chars);
return false;
}
static inline bool sema_analyse_ct_call_parameters(Context *context, Expr *expr)
{
if (expr->resolve_status == RESOLVE_DONE) return true;
Expr **elements = expr->ct_call_expr.arguments;
unsigned count = vec_size(elements);
if (count > 2)
{
SEMA_ERROR(elements[2], "The function can only take one or two arguments.");
return expr_poison(expr);
}
Expr *first_expr = elements[0];
if (!sema_analyse_expr_value(context, NULL, first_expr)) return false;
ExprFlatElement *flatpath = NULL;
Expr *second_expr = NULL;
if (count == 2)
{
second_expr = elements[1];
if (second_expr->expr_kind != EXPR_FLATPATH)
{
if (!sema_analyse_expr_value(context, NULL, second_expr)) return false;
if (second_expr->expr_kind != EXPR_CONST || second_expr->const_expr.kind != TYPE_STRLIT)
{
SEMA_ERROR(second_expr, "Expected a string here.");
return false;
}
MiniLexer lexer = { .chars = second_expr->const_expr.string.chars };
if (!sema_analyse_idents_string(context, &lexer, &flatpath))
{
SEMA_ERROR(expr, "The path to an existing member was expected, did you make a mistake?");
return false;
}
}
else
{
flatpath = second_expr->flatpath_expr;
}
}
Decl *decl = NULL;
Type *type = NULL;
if (first_expr->expr_kind == EXPR_CONST && first_expr->const_expr.kind == TYPE_STRLIT)
{
ExprFlatElement *path_elements = NULL;
if (!sema_analyse_identifier_path_string(context, first_expr, &decl, &type, &path_elements)) return false;
if (path_elements)
{
if (flatpath)
{
SEMA_ERROR(elements[1], "You cannot combine a string with a member path with a separate path here.");
return false;
}
flatpath = path_elements;
}
}
else
{
switch (first_expr->expr_kind)
{
case EXPR_IDENTIFIER:
decl = first_expr->identifier_expr.decl;
break;
case EXPR_TYPEINFO:
type = first_expr->type_expr->type;
break;
case EXPR_TYPEOF:
type = first_expr->typeof_expr->type;
break;
default:
break;
}
}
if (!type && !decl)
{
SEMA_ERROR(first_expr, "A type or declaration was expected.");
return false;
}
if (!decl && type_is_user_defined(type)) decl = type->decl;
if (decl)
{
if (!sema_analyse_decl(context, decl)) return false;
if (decl)
{
switch (decl->decl_kind)
{
case DECL_DISTINCT:
case DECL_ENUM:
case DECL_ERRTYPE:
case DECL_FUNC:
case DECL_STRUCT:
case DECL_TYPEDEF:
case DECL_UNION:
break;
case DECL_VAR:
if (decl->type) break;
FALLTHROUGH;
default:
SEMA_ERROR(first_expr, "A type or typed declaration was expected.");
return false;
}
}
type = decl->type;
}
expr->ct_call_expr.flatpath = flatpath;
expr->ct_call_expr.decl = decl;
expr->ct_call_expr.type = type;
return true;
}
static inline bool sema_expr_analyse_ct_sizeof(Context *context, Type *to, Expr *expr)
{
Expr *first = expr->ct_call_expr.arguments[0];
if (!sema_analyse_ct_call_parameters(context, expr)) return false;
Type *type = expr->ct_call_expr.type->canonical;
ExprFlatElement *elements = expr->ct_call_expr.flatpath;
VECEACH(elements, i)
{
ExprFlatElement element = elements[i];
type = type_flatten_distinct(type);
if (element.array)
{
if (type->type_kind != TYPE_ARRAY)
{
SEMA_ERROR(first, "%s is not a fixed size array.", type_quoted_error_string(expr->ct_call_expr.type));
return false;
}
type = type->array.base;
continue;
}
if (!type_is_structlike(type))
{
if (i == 0)
{
SEMA_ERROR(first, "%s has no members.", type_quoted_error_string(expr->ct_call_expr.type));
}
else
{
SEMA_ERROR(first, "There is no such member in %s.", type_quoted_error_string(expr->ct_call_expr.type));
}
return false;
}
Decl *decl = type->decl;
SCOPE_START
add_members_to_context(context, type->decl);
decl = sema_resolve_symbol_in_current_dynamic_scope(context, element.ident);
SCOPE_END;
if (!decl)
{
SEMA_ERROR(first, "There is no such member in %s.", type_quoted_error_string(expr->ct_call_expr.type));
return false;
}
type = decl->type;
}
expr_rewrite_to_int_const(expr, type_compint, type_size(type));
return true;
}
static inline bool decl_is_local(Decl *decl)
{
if (decl->decl_kind != DECL_VAR) return false;
VarDeclKind kind = decl->var.kind;
return kind == VARDECL_PARAM_CT_TYPE
|| kind == VARDECL_PARAM
|| kind == VARDECL_PARAM_CT
|| kind == VARDECL_LOCAL
|| kind == VARDECL_LOCAL_CT_TYPE
|| kind == VARDECL_LOCAL_CT
|| kind == VARDECL_PARAM_REF
|| kind == VARDECL_PARAM_EXPR
|| kind == VARDECL_MEMBER;
}
static inline bool sema_expr_analyse_ct_nameof(Context *context, Type *to, Expr *expr)
{
if (expr->resolve_status == RESOLVE_DONE) return true;
Expr **elements = expr->ct_call_expr.arguments;
unsigned count = vec_size(elements);
if (count > 1)
{
SEMA_ERROR(elements[1], "The function only takes one argument.");
return expr_poison(expr);
}
Expr *first_expr = elements[0];
if (!sema_analyse_expr_value(context, NULL, first_expr)) return false;
TokenType name_type = expr->ct_call_expr.token_type;
Decl *decl = NULL;
Type *type = NULL;
if (first_expr->expr_kind == EXPR_CONST && first_expr->const_expr.kind == TYPE_STRLIT)
{
ExprFlatElement *path_elements = NULL;
if (!sema_analyse_identifier_path_string(context, first_expr, &decl, &type, &path_elements)) return false;
if (path_elements)
{
SEMA_ERROR(first_expr, "You cannot take the name of sub elements by string name.");
return false;
}
}
else if (first_expr->expr_kind == EXPR_TYPEINFO)
{
type = first_expr->type_expr->type->canonical;
}
else
{
if (first_expr->expr_kind != EXPR_IDENTIFIER)
{
SEMA_ERROR(first_expr, "Expected an identifier or a string.");
return false;
}
decl = first_expr->identifier_expr.decl;
}
if (type)
{
if (!sema_resolve_type(context, type)) return false;
// TODO type_is_builtin is wrong also this does not cover virtual.
if (name_type == TOKEN_CT_EXTNAMEOF)
{
if (!type_is_user_defined(type))
{
SEMA_ERROR(first_expr, "Only user defined types have an external name.");
return false;
}
expr_rewrite_to_string(expr, type->decl->extname ?: type->decl->external_name);
return true;
}
if (name_type == TOKEN_CT_NAMEOF || type_is_builtin(type->type_kind))
{
expr_rewrite_to_string(expr, type->name);
return true;
}
scratch_buffer_clear();
scratch_buffer_append(type->decl->module->name->module);
scratch_buffer_append("::");
scratch_buffer_append(type->name);
expr_rewrite_to_string(expr, scratch_buffer_interned());
return true;
}
assert(decl);
if (!sema_analyse_decl(context, decl)) return false;
if (name_type == TOKEN_CT_EXTNAMEOF)
{
if (!decl->external_name)
{
SEMA_ERROR(first_expr, "'%s' does not have an external name.", decl->name);
return false;
}
expr_rewrite_to_string(expr, decl->extname ?: decl->external_name);
return true;
}
if (!decl->module || name_type == TOKEN_CT_NAMEOF || decl_is_local(decl))
{
expr_rewrite_to_string(expr, decl->name);
return true;
}
scratch_buffer_clear();
scratch_buffer_append(decl->module->name->module);
scratch_buffer_append("::");
scratch_buffer_append(decl->name);
expr_rewrite_to_string(expr, scratch_buffer_interned());
return true;
}
static inline bool sema_expr_analyse_ct_alignof(Context *context, Type *to, Expr *expr)
{
Expr *first = expr->ct_call_expr.arguments[0];
if (!sema_analyse_ct_call_parameters(context, expr)) return false;
Type *type = expr->ct_call_expr.type->canonical;
ExprFlatElement *elements = expr->ct_call_expr.flatpath;
Decl *decl = expr->ct_call_expr.decl;
AlignSize align = decl && !decl_is_user_defined_type(decl) ? expr->ct_call_expr.decl->alignment : type_abi_alignment(type);
VECEACH(elements, i)
{
ExprFlatElement element = elements[i];
type = type_flatten_distinct(type);
if (element.array)
{
if (type->type_kind != TYPE_ARRAY)
{
SEMA_ERROR(first, "%s is not a fixed size array.", type_quoted_error_string(expr->ct_call_expr.type));
return false;
}
type = type->array.base;
ByteSize size = type_size(type);
align = (AlignSize)type_min_alignment(size * element.index, align);
continue;
}
if (!type_is_structlike(type))
{
if (i == 0)
{
SEMA_ERROR(first, "%s has no members.", type_quoted_error_string(expr->ct_call_expr.type));
}
else
{
SEMA_ERROR(first, "There is no such member in %s.", type_quoted_error_string(expr->ct_call_expr.type));
}
return false;
}
Decl *decl = type->decl;
SCOPE_START
add_members_to_context(context, type->decl);
decl = sema_resolve_symbol_in_current_dynamic_scope(context, element.ident);
SCOPE_END;
if (!decl)
{
SEMA_ERROR(first, "There is no such member in %s.", type_quoted_error_string(expr->ct_call_expr.type));
return false;
}
type = decl->type;
align = type_min_alignment(decl->offset, align);
}
expr_rewrite_to_int_const(expr, type_compint, align);
return true;
}
static inline bool sema_expr_analyse_ct_offsetof(Context *context, Type *to, Expr *expr)
{
Expr *first = expr->ct_call_expr.arguments[0];
if (!sema_analyse_ct_call_parameters(context, expr)) return false;
Type *type = expr->ct_call_expr.type->canonical;
ExprFlatElement *elements = expr->ct_call_expr.flatpath;
if (!vec_size(elements))
{
SEMA_ERROR(first, "Expected a path to get the offset of.");
return false;
}
ByteSize offset = 0;
VECEACH(elements, i)
{
ExprFlatElement element = elements[i];
type = type_flatten_distinct(type);
if (element.array)
{
if (type->type_kind != TYPE_ARRAY)
{
SEMA_ERROR(first, "%s is not a fixed size array.", type_quoted_error_string(expr->ct_call_expr.type));
return false;
}
type = type->array.base;
offset += type_size(type) * element.index;
continue;
}
if (!type_is_structlike(type))
{
if (i == 0)
{
SEMA_ERROR(first, "%s has no members.", type_quoted_error_string(expr->ct_call_expr.type));
}
else
{
SEMA_ERROR(first, "There is no such member in %s.", type_quoted_error_string(expr->ct_call_expr.type));
}
return false;
}
Decl *decl = type->decl;
SCOPE_START
add_members_to_context(context, type->decl);
decl = sema_resolve_symbol_in_current_dynamic_scope(context, element.ident);
SCOPE_END;
if (!decl)
{
SEMA_ERROR(first, "There is no such member in %s.", type_quoted_error_string(expr->ct_call_expr.type));
return false;
}
type = decl->type;
offset += decl->offset;
}
expr_rewrite_to_int_const(expr, type_compint, offset);
return true;
}
static inline bool sema_expr_analyse_ct_call(Context *context, Type *to, Expr *expr)
{
switch (expr->ct_call_expr.token_type)
{
case TOKEN_CT_SIZEOF:
return sema_expr_analyse_ct_sizeof(context, to, expr);
case TOKEN_CT_ALIGNOF:
return sema_expr_analyse_ct_alignof(context, to, expr);
case TOKEN_CT_OFFSETOF:
return sema_expr_analyse_ct_offsetof(context, to, expr);
case TOKEN_CT_QNAMEOF:
case TOKEN_CT_NAMEOF:
case TOKEN_CT_EXTNAMEOF:
return sema_expr_analyse_ct_nameof(context, to, expr);
default:
UNREACHABLE
}
}
static inline bool sema_expr_analyse_decl(Context *context, Type *to, Expr *expr)
{
if (!sema_analyse_var_decl(context, expr->decl_expr)) return false;
expr_set_type(expr, expr->decl_expr->type);
expr->pure = !expr->decl_expr->var.init_expr || expr->decl_expr->var.init_expr->pure;
expr->constant = expr->decl_expr->var.kind == VARDECL_CONST;
return true;
}
static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *expr)
{
switch (expr->expr_kind)
{
case EXPR_COND:
case EXPR_UNDEF:
case EXPR_ENUM_CONSTANT:
case EXPR_MEMBER_ACCESS:
case EXPR_DESIGNATOR:
case EXPR_MACRO_BODY_EXPANSION:
case EXPR_FLATPATH:
case EXPR_NOP:
case EXPR_TRY_UNWRAP_CHAIN:
case EXPR_TRY_UNWRAP:
case EXPR_CATCH_UNWRAP:
UNREACHABLE
case EXPR_TRY_DECL:
TODO
case EXPR_DECL:
return sema_expr_analyse_decl(context, to, expr);
case EXPR_CT_CALL:
return sema_expr_analyse_ct_call(context, to, expr);
case EXPR_HASH_IDENT:
return sema_expr_analyse_hash_identifier(context, to, expr);
case EXPR_CT_IDENT:
return sema_expr_analyse_ct_identifier(context, expr);
case EXPR_FAILABLE:
return sema_expr_analyse_failable(context, to, expr);
case EXPR_PLACEHOLDER:
return sema_expr_analyse_placeholder(context, to, expr);
case EXPR_POISONED:
return false;
case EXPR_LEN:
case EXPR_SLICE_ASSIGN:
// Created during semantic analysis
UNREACHABLE
case EXPR_MACRO_BLOCK:
case EXPR_SCOPED_EXPR:
UNREACHABLE
case EXPR_TYPEINFO:
return sema_expr_analyse_typeinfo(context, expr);
case EXPR_SLICE:
return sema_expr_analyse_slice(context, expr);
case EXPR_TRY:
return sema_expr_analyse_try(context, expr);
case EXPR_TRY_ASSIGN:
return sema_expr_analyse_try_assign(context, expr, false);
case EXPR_TYPEOF:
return sema_expr_analyse_typeof(context, expr);
case EXPR_ELSE:
return sema_expr_analyse_else(context, to, expr);
case EXPR_COMPOUND_LITERAL:
return sema_expr_analyse_compound_literal(context, expr);
case EXPR_EXPR_BLOCK:
return sema_expr_analyse_expr_block(context, to, expr);
case EXPR_GUARD:
return sema_expr_analyse_guard(context, to, expr);
case EXPR_CONST:
case EXPR_BYTES:
return sema_expr_analyse_const(to, expr);
case EXPR_BINARY:
if (!sema_expr_analyse_binary(context, to, expr)) return false;
if (expr->expr_kind == EXPR_BINARY)
{
expr->failable = expr->binary_expr.left->failable | expr->binary_expr.right->failable;
}
return true;
case EXPR_TERNARY:
return sema_expr_analyse_ternary(context, to, expr);
case EXPR_UNARY:
if (!sema_expr_analyse_unary(context, to, expr)) return false;
if (expr->expr_kind == EXPR_UNARY) expr->failable = expr->unary_expr.expr->failable;
return true;
case EXPR_POST_UNARY:
if (!sema_expr_analyse_post_unary(context, to, expr)) return false;
if (expr->expr_kind == EXPR_UNARY) expr->failable = expr->unary_expr.expr->failable;
return true;
case EXPR_TYPEID:
return sema_expr_analyse_type(context, expr);
case EXPR_MACRO_EXPANSION:
return sema_expr_analyse_macro_expansion(context, expr);
case EXPR_CONST_IDENTIFIER:
case EXPR_IDENTIFIER:
return sema_expr_analyse_identifier(context, to, expr);
case EXPR_CALL:
return sema_expr_analyse_call(context, to, expr);
case EXPR_SUBSCRIPT:
return sema_expr_analyse_subscript(context, 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, bool may_be_failable)
{
if (!sema_analyse_expr(context, to, expr)) return false;
if (expr->failable && !may_be_failable)
{
if (!to) to = expr->type;
SEMA_ERROR(expr, "'%s!' cannot be converted into '%s'.", type_to_error_string(expr->type), type_to_error_string(to));
return false;
}
return to ? cast_implicit(expr, to) : true;
}
static inline bool sema_cast_ct_ident_rvalue(Context *context, Type *to, Expr *expr)
{
Decl *decl = expr->ct_ident_expr.decl;
Expr *copy = copy_expr(decl->var.init_expr);
if (!sema_analyse_expr(context, to, copy)) return false;
expr_replace(expr, copy);
return true;
}
static inline bool sema_cast_rvalue(Context *context, Type *to, Expr *expr)
{
if (!expr_ok(expr)) return false;
switch (expr->expr_kind)
{
case EXPR_MACRO_BODY_EXPANSION:
if (!expr->body_expansion_expr.ast)
{
SEMA_ERROR(expr, "'@%s' must be followed by ().", context->macro_scope.body_param);
return false;
}
case EXPR_ACCESS:
if (expr->access_expr.ref->decl_kind == DECL_FUNC)
{
SEMA_ERROR(expr, "A function name must be followed by '(' or preceeded by '&'.");
return false;
}
if (expr->access_expr.ref->decl_kind == DECL_MACRO)
{
SEMA_ERROR(expr, "A macro name must be followed by '('.");
return false;
}
break;
case EXPR_MEMBER_ACCESS:
switch (expr->access_expr.ref->decl_kind)
{
case DECL_ERRVALUE:
return true;
case DECL_ENUM_CONSTANT:
{
Type *original_type = expr->access_expr.ref->type;
expr_replace(expr, expr->access_expr.ref->enum_constant.expr);
expr->original_type = original_type;
return true;
}
default:
SEMA_ERROR(expr, "A member must be followed by '.' plus a property like 'sizeof'.");
return false;
}
case EXPR_LEN:
if (expr->type != type_void) return true;
SEMA_ERROR(expr, "Expected () after 'len' for subarrays.");
return false;
case EXPR_TYPEINFO:
SEMA_ERROR(expr, "A type must be followed by either (...) or '.'.");
return false;
case EXPR_ENUM_CONSTANT:
assert(expr->expr_enum->enum_constant.expr->expr_kind == EXPR_CONST);
expr->const_expr = expr->expr_enum->enum_constant.expr->const_expr;
expr->expr_kind = EXPR_CONST;
break;
case EXPR_MACRO_EXPANSION:
SEMA_ERROR(expr, "Expected macro followed by (...).", expr->ct_macro_ident_expr.identifier);
return expr_poison(expr);
case EXPR_CT_IDENT:
if (!sema_cast_ct_ident_rvalue(context, to, expr)) return false;
break;
case EXPR_IDENTIFIER:
case EXPR_CONST_IDENTIFIER:
if (!sema_cast_ident_rvalue(context, to, expr)) return false;
break;
default:
break;
}
return true;
}
bool sema_analyse_expr_value(Context *context, Type *to, Expr *expr)
{
switch (expr->resolve_status)
{
case RESOLVE_NOT_DONE:
expr->resolve_status = RESOLVE_RUNNING;
if (!sema_analyse_expr_dispatch(context, to, expr)) return expr_poison(expr);
expr->resolve_status = RESOLVE_DONE;
return true;
case RESOLVE_RUNNING:
SEMA_ERROR(expr, "Recursive resolution of expression");
return expr_poison(expr);
case RESOLVE_DONE:
return expr_ok(expr);
default:
UNREACHABLE
}
}
ArrayIndex sema_get_initializer_const_array_size(Context *context, Expr *initializer, bool *may_be_array, bool *is_const_size)
{
assert(initializer->expr_kind == EXPR_INITIALIZER_LIST);
Expr **initializers = initializer->initializer_expr.initializer_expr;
*may_be_array = true;
*is_const_size = true;
unsigned element_count = vec_size(initializers);
// If it's empty or the first element is not a designator, we assume an array list
// with that many elements.
if (!element_count || initializers[0]->expr_kind != EXPR_DESIGNATOR) return element_count;
ArrayIndex size = 0;
// Otherwise we assume everything's a designator.
VECEACH(initializers, i)
{
Expr *sub_initializer = initializers[i];
if (sub_initializer->expr_kind != EXPR_DESIGNATOR)
{
// Simply messed up: a mix of designators and regular ones.
return -1;
}
DesignatorElement *element = sub_initializer->designator_expr.path[0];
switch (element->kind)
{
case DESIGNATOR_FIELD:
// Struct, abandon!
*may_be_array = false;
return -1;
case DESIGNATOR_ARRAY:
{
ArrayIndex index = sema_analyse_designator_index(context, element->index_expr);
if (index < 0 || element->index_expr->expr_kind != EXPR_CONST)
{
*is_const_size = false;
return -1;
}
size = MAX(size, index + 1);
break;
}
case DESIGNATOR_RANGE:
{
ArrayIndex index = sema_analyse_designator_index(context, element->index_end_expr);
if (index < 0 || element->index_end_expr->expr_kind != EXPR_CONST)
{
*is_const_size = false;
return -1;
}
size = MAX(size, index + 1);
break;
}
default:
UNREACHABLE
}
}
return size;
}
bool sema_analyse_expr(Context *context, Type *to, Expr *expr)
{
return sema_analyse_expr_value(context, to, expr) && sema_cast_rvalue(context, to, expr);
}