// Copyright (c) 2020 Christoffer Lerno. All rights reserved. // Use of this source code is governed by a LGPLv3.0 // a copy of which can be found in the LICENSE file. #include "sema_internal.h" #include "bigint.h" static AttributeType sema_analyse_attribute(Context *context, Attr *attr, AttributeDomain domain); static bool sema_analyse_struct_union(Context *context, Decl *decl); static inline bool sema_analyse_struct_member(Context *context, Decl *decl) { if (decl->name) { Decl *other = sema_resolve_symbol_in_current_dynamic_scope(context, decl->name); if (other) { SEMA_ERROR(decl, "Duplicate member name '%s'.", other->name); SEMA_PREV(other, "Previous declaration was here."); return false; } if (decl->name) sema_add_member(context, decl); } switch (decl->decl_kind) { case DECL_VAR: assert(decl->var.kind == VARDECL_MEMBER); decl->resolve_status = RESOLVE_RUNNING; if (!sema_resolve_type_info(context, decl->var.type_info)) return decl_poison(decl); decl->type = decl->var.type_info->type; decl->resolve_status = RESOLVE_DONE; return true; case DECL_STRUCT: case DECL_UNION: return sema_analyse_decl(context, decl); default: UNREACHABLE } } static bool sema_analyse_struct_union(Context *context, Decl *decl) { AttributeDomain domain; switch (decl->decl_kind) { case DECL_STRUCT: domain = ATTR_STRUCT; break; case DECL_UNION: domain = ATTR_UNION; break; case DECL_ERR: domain = ATTR_ERROR; break; default: UNREACHABLE } VECEACH(decl->attributes, i) { Attr *attr = decl->attributes[i]; AttributeType attribute = sema_analyse_attribute(context, attr, domain); if (attribute == ATTRIBUTE_NONE) return decl_poison(decl); bool had = false; #define SET_ATTR(_X) had = decl->func._X; decl->func._X = true; break switch (attribute) { case ATTRIBUTE_CNAME: had = decl->cname != NULL; decl->cname = attr->expr->const_expr.string.chars; break; case ATTRIBUTE_SECTION: had = decl->section != NULL; decl->section = attr->expr->const_expr.string.chars; break; case ATTRIBUTE_ALIGN: had = decl->alignment != 0; decl->alignment = attr->alignment; break; case ATTRIBUTE_PACKED: had = decl->is_packed; decl->is_packed = true; break; default: UNREACHABLE } #undef SET_ATTR if (had) { SEMA_TOKID_ERROR(attr->name, "Attribute occurred twice, please remove one."); return decl_poison(decl); } } DEBUG_LOG("Beginning analysis of %s.", decl->name ? decl->name : "anon"); size_t offset = 0; // Default alignment is 1. size_t alignment = 1; size_t size = 0; if (decl->name) context_push_scope(context); VECEACH(decl->strukt.members, i) { Decl *member = decl->strukt.members[i]; if (!decl_ok(member)) { decl_poison(decl); continue; } if (!sema_analyse_struct_member(context, member)) { if (decl_ok(decl)) { decl_poison(decl); continue; } continue; } size_t member_alignment = type_abi_alignment(member->type); size_t member_size = type_size(member->type); if (member_alignment > alignment) alignment = member_alignment; if (decl->decl_kind == DECL_UNION) { if (member_size > size) size = member_size; member->offset = 0; } else { if (!decl->is_packed) { offset = aligned_offset(offset, member_alignment); } member->offset = offset; offset += member_size; } } if (!decl->alignment) decl->alignment = alignment; if (decl->decl_kind != DECL_UNION) { size = offset; if (!decl->is_packed) { size = aligned_offset(size, decl->alignment); } } decl->strukt.size = size; DEBUG_LOG("Struct/union size %d, alignment %d.", (int)size, (int)decl->alignment); if (decl->name) context_pop_scope(context); DEBUG_LOG("Analysis complete."); return decl_ok(decl); } static inline bool sema_analyse_function_param(Context *context, Decl *param, bool is_function, bool *has_default) { *has_default = false; assert(param->decl_kind == DECL_VAR); assert(param->var.kind == VARDECL_PARAM); if (!sema_resolve_type_info(context, param->var.type_info)) { return false; } param->type = param->var.type_info->type; if (param->var.init_expr && !is_function) { SEMA_ERROR(param->var.init_expr, "Function types may not have default arguments."); return false; } if (param->var.init_expr) { Expr *expr = param->var.init_expr; if (!sema_analyse_expr_of_required_type(context, param->type, expr, false)) return false; if (expr->expr_kind != EXPR_CONST) { SEMA_ERROR(expr, "Only constant expressions may be used as default values."); return false; } *has_default = true; } return true; } static inline Type *sema_analyse_function_signature(Context *context, FunctionSignature *signature, bool is_function) { char buffer[MAX_FUNCTION_SIGNATURE_SIZE + 200]; size_t buffer_write_offset = 0; bool all_ok = true; all_ok = sema_resolve_type_info(context, signature->rtype) && all_ok; if (all_ok) { type_append_signature_name(signature->rtype->type, buffer, &buffer_write_offset); if (signature->failable) buffer[buffer_write_offset++] = '!'; buffer[buffer_write_offset++] = '('; } if (vec_size(signature->params) > MAX_PARAMS) { SEMA_ERROR(signature->params[MAX_PARAMS], "Number of params exceeds %d which is unsupported.", MAX_PARAMS); return false; } STable *names = &context->scratch_table; stable_clear(names); VECEACH(signature->params, i) { Decl *param = signature->params[i]; assert(param->resolve_status == RESOLVE_NOT_DONE); param->resolve_status = RESOLVE_RUNNING; bool has_default; if (!sema_analyse_function_param(context, param, is_function, &has_default)) { decl_poison(param); all_ok = false; continue; } signature->has_default = signature->has_default || has_default; param->resolve_status = RESOLVE_DONE; if (i > 0 && all_ok) { buffer[buffer_write_offset++] = ','; } type_append_signature_name(param->var.type_info->type, buffer, &buffer_write_offset); if (param->name) { Decl *prev = stable_set(names, param->name, param); if (prev) { SEMA_ERROR(param, "Duplicate parameter name %s.", param->name); SEMA_PREV(prev, "Previous use of the name was here."); decl_poison(prev); decl_poison(param); all_ok = false; } } } if (signature->variadic) { buffer[buffer_write_offset++] = ','; buffer[buffer_write_offset++] = '.'; buffer[buffer_write_offset++] = '.'; buffer[buffer_write_offset++] = '.'; } buffer[buffer_write_offset++] = ')'; if (!all_ok) return NULL; TokenType type = TOKEN_INVALID_TOKEN; signature->mangled_signature = symtab_add(buffer, buffer_write_offset, fnv1a(buffer, buffer_write_offset), &type); Type *func_type = stable_get(&context->local_symbols, signature->mangled_signature); if (!func_type) { func_type = type_new(TYPE_FUNC, signature->mangled_signature); func_type->canonical = func_type; func_type->func.signature = signature; stable_set(&context->local_symbols, signature->mangled_signature, func_type); } return func_type; } static inline bool sema_analyse_typedef(Context *context, Decl *decl) { if (decl->typedef_decl.is_func) { Type *func_type = sema_analyse_function_signature(context, &decl->typedef_decl.function_signature, false); if (!func_type) return false; decl->type->canonical = type_get_ptr(func_type); return true; } if (!sema_resolve_type_info(context, decl->typedef_decl.type_info)) return false; decl->type->canonical = decl->typedef_decl.type_info->type->canonical; // Do we need anything else? return true; } static inline bool sema_analyse_enum(Context *context, Decl *decl) { // Resolve the type of the enum. if (!sema_resolve_type_info(context, decl->enums.type_info)) return false; Type *type = decl->enums.type_info->type; Type *canonical = type->canonical; // Require an integer type if (!type_is_integer(canonical)) { SEMA_ERROR(decl->enums.type_info, "The enum type must be an integer type not '%s'.", type_to_error_string(type)); return false; } DEBUG_LOG("* Enum type resolved to %s.", type->name); bool success = true; unsigned enums = vec_size(decl->enums.values); BigInt value; BigInt add; bigint_init_unsigned(&add, 1); bigint_init_unsigned(&value, 0); for (unsigned i = 0; i < enums; i++) { Decl *enum_value = decl->enums.values[i]; enum_value->type = decl->type; DEBUG_LOG("* Checking enum constant %s.", enum_value->name); enum_value->enum_constant.ordinal = i; DEBUG_LOG("* Ordinal: %d", i); assert(enum_value->resolve_status == RESOLVE_NOT_DONE); assert(enum_value->decl_kind == DECL_ENUM_CONSTANT); // Start evaluating the constant enum_value->resolve_status = RESOLVE_RUNNING; Expr *expr = enum_value->enum_constant.expr; // Create a "fake" expression. // This will be evaluated later to catch the case if (!expr) { expr = expr_new(EXPR_CONST, source_span_from_token_id(enum_value->name_token)); expr->type = type; expr->resolve_status = RESOLVE_NOT_DONE; bigint_init_bigint(&expr->const_expr.i, &value); expr->const_expr.kind = TYPE_IXX; expr->type = type_compint; enum_value->enum_constant.expr = expr; } // We try to convert to the desired type. if (!sema_analyse_expr_of_required_type(context, type, expr, false)) { success = false; enum_value->resolve_status = RESOLVE_DONE; decl_poison(enum_value); // Reset! bigint_init_unsigned(&value, 0); continue; } assert(type_is_integer(expr->type->canonical)); // Here we might have a non-constant value, if (expr->expr_kind != EXPR_CONST) { SEMA_ERROR(expr, "Expected a constant expression for enum."); decl_poison(enum_value); success = false; // Skip one value. continue; } // Update the value bigint_add(&value, &expr->const_expr.i, &add); DEBUG_LOG("* Value: %s", expr_const_to_error_string(&expr->const_expr)); enum_value->resolve_status = RESOLVE_DONE; } return success; } static inline bool sema_analyse_method(Context *context, Decl *decl) { TypeInfo *parent_type = decl->func.type_parent; if (!sema_resolve_type_info(context, parent_type)) return false; if (!type_may_have_sub_elements(parent_type->type)) { SEMA_ERROR(decl, "Methods can not be associated with '%s'", type_to_error_string(decl->func.type_parent->type)); return false; } Decl *parent = parent_type->type->decl; VECEACH(parent->methods, i) { Decl *function = parent->methods[i]; if (function->name == decl->name) { SEMA_ERROR(decl, "Duplicate name '%s' for method.", function->name); SEMA_PREV(function, "Previous definition here."); return false; } } DEBUG_LOG("Method '%s.%s' analysed.", parent->name, decl->name); vec_add(parent->methods, decl); return true; } static inline AttributeType attribute_by_name(Attr *attr) { const char *attribute = TOKSTR(attr->name); for (unsigned i = 0; i < NUMBER_OF_ATTRIBUTES; i++) { if (attribute_list[i] == attribute) return (AttributeType)i; } return ATTRIBUTE_NONE; } static const char *attribute_domain_to_string(AttributeDomain domain) { switch (domain) { case ATTR_FUNC: return "function"; case ATTR_VAR: return "variable"; case ATTR_ENUM: return "enum"; case ATTR_STRUCT: return "struct"; case ATTR_UNION: return "union"; case ATTR_CONST: return "constant"; case ATTR_ERROR: return "error type"; case ATTR_TYPEDEF: return "typedef"; } UNREACHABLE } static AttributeType sema_analyse_attribute(Context *context, Attr *attr, AttributeDomain domain) { AttributeType type = attribute_by_name(attr); if (type == ATTRIBUTE_NONE) { SEMA_TOKID_ERROR(attr->name, "There is no attribute with the name '%s', did you mistype?", TOKSTR(attr->name)); return ATTRIBUTE_NONE; } static AttributeDomain attribute_domain[NUMBER_OF_ATTRIBUTES] = { [ATTRIBUTE_WEAK] = ATTR_FUNC | ATTR_CONST | ATTR_VAR, [ATTRIBUTE_CNAME] = 0xFF, [ATTRIBUTE_SECTION] = ATTR_FUNC | ATTR_CONST | ATTR_VAR, [ATTRIBUTE_PACKED] = ATTR_STRUCT | ATTR_UNION | ATTR_ERROR, [ATTRIBUTE_NORETURN] = ATTR_FUNC, [ATTRIBUTE_ALIGN] = ATTR_FUNC | ATTR_CONST | ATTR_VAR | ATTR_STRUCT | ATTR_UNION, [ATTRIBUTE_INLINE] = ATTR_FUNC, [ATTRIBUTE_OPAQUE] = ATTR_STRUCT | ATTR_UNION, [ATTRIBUTE_STDCALL] = ATTR_FUNC }; if ((attribute_domain[type] & domain) != domain) { SEMA_TOKID_ERROR(attr->name, "'%s' is not a valid %s attribute.", TOKSTR(attr->name), attribute_domain_to_string(domain)); return ATTRIBUTE_NONE; } switch (type) { case ATTRIBUTE_STDCALL: return type; case ATTRIBUTE_ALIGN: if (!attr->expr) { SEMA_TOKID_ERROR(attr->name, "'align' requires an power-of-2 argument, e.g. align(8)."); return ATTRIBUTE_NONE; } if (!sema_analyse_expr(context, type_usize, attr->expr)) return false; if (attr->expr->expr_kind != EXPR_CONST || !type_is_any_integer(attr->expr->type->canonical)) { SEMA_ERROR(attr->expr, "Expected a constant integer value as argument."); return ATTRIBUTE_NONE; } { BigInt comp; bigint_init_unsigned(&comp, MAX_ALIGNMENT); if (bigint_cmp(&attr->expr->const_expr.i, &comp) == CMP_GT) { SEMA_ERROR(attr->expr, "Alignment must be less or equal to %ull.", MAX_ALIGNMENT); return ATTRIBUTE_NONE; } if (bigint_cmp_zero(&attr->expr->const_expr.i) != CMP_GT) { SEMA_ERROR(attr->expr, "Alignment must be greater than zero."); return ATTRIBUTE_NONE; } uint64_t align = bigint_as_unsigned(&attr->expr->const_expr.i); if (!is_power_of_two(align)) { SEMA_ERROR(attr->expr, "Alignment must be a power of two."); return ATTRIBUTE_NONE; } attr->alignment = align; } return type; case ATTRIBUTE_SECTION: case ATTRIBUTE_CNAME: if (!attr->expr) { SEMA_TOKID_ERROR(attr->name, "'%s' requires a string argument, e.g. %s(\"foo\").", TOKSTR(attr->name), TOKSTR(attr->name)); return ATTRIBUTE_NONE; } if (!sema_analyse_expr(context, NULL, attr->expr)) return false; if (attr->expr->expr_kind != EXPR_CONST || attr->expr->type->canonical != type_string) { SEMA_ERROR(attr->expr, "Expected a constant string value as argument."); return ATTRIBUTE_NONE; } return type; default: if (attr->expr) { SEMA_ERROR(attr->expr, "'%s' should not have any arguments.", TOKSTR(attr->name)); return ATTRIBUTE_NONE; } return type; } } static inline bool sema_analyse_func(Context *context, Decl *decl) { DEBUG_LOG("----Analysing function %s", decl->name); Type *func_type = sema_analyse_function_signature(context, &decl->func.function_signature, true); decl->type = func_type; if (!func_type) return decl_poison(decl); if (decl->func.type_parent) { if (!sema_analyse_method(context, decl)) return decl_poison(decl); } VECEACH(decl->attributes, i) { Attr *attr = decl->attributes[i]; AttributeType attribute = sema_analyse_attribute(context, attr, ATTR_FUNC); if (attribute == ATTRIBUTE_NONE) return decl_poison(decl); bool had = false; #define SET_ATTR(_X) had = decl->func._X; decl->func._X = true; break switch (attribute) { case ATTRIBUTE_CNAME: had = decl->cname != NULL; decl->cname = attr->expr->const_expr.string.chars; break; case ATTRIBUTE_SECTION: had = decl->section != NULL; decl->section = attr->expr->const_expr.string.chars; break; case ATTRIBUTE_ALIGN: had = decl->alignment != 0; decl->alignment = attr->alignment; break; case ATTRIBUTE_NOINLINE: SET_ATTR(attr_noinline); case ATTRIBUTE_STDCALL: SET_ATTR(attr_stdcall); case ATTRIBUTE_INLINE: SET_ATTR(attr_inline); case ATTRIBUTE_NORETURN: SET_ATTR(attr_noreturn); case ATTRIBUTE_WEAK: SET_ATTR(attr_weak); default: UNREACHABLE } #undef SET_ATTR if (had) { SEMA_TOKID_ERROR(attr->name, "Attribute occurred twice, please remove one."); return decl_poison(decl); } if (decl->func.attr_inline && decl->func.attr_noinline) { SEMA_TOKID_ERROR(attr->name, "A function cannot be 'inline' and 'noinline' at the same time."); return decl_poison(decl); } } if (decl->name == kw_main) { if (decl->visibility == VISIBLE_LOCAL) { SEMA_ERROR(decl, "'main' cannot have local visibility."); return false; } decl->visibility = VISIBLE_EXTERN; } DEBUG_LOG("Function analysis done."); return true; } static inline bool sema_analyse_macro(Context *context, Decl *decl) { TypeInfo *rtype = decl->macro_decl.rtype; if (decl->macro_decl.rtype && !sema_resolve_type_info(context, rtype)) return false; VECEACH(decl->macro_decl.parameters, i) { Decl *param = decl->macro_decl.parameters[i]; assert(param->decl_kind == DECL_VAR); switch (param->var.kind) { case VARDECL_PARAM: case VARDECL_PARAM_EXPR: case VARDECL_PARAM_CT: case VARDECL_PARAM_REF: if (param->var.type_info && !sema_resolve_type_info(context, param->var.type_info)) return false; break; case VARDECL_PARAM_CT_TYPE: if (param->var.type_info) { SEMA_ERROR(param->var.type_info, "A compile time type parameter cannot have a type itself."); 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_ALIAS: UNREACHABLE } } return true; } static inline bool sema_analyse_global(Context *context, Decl *decl) { if (decl->var.type_info) { if (!sema_resolve_type_info(context, decl->var.type_info)) return false; decl->type = decl->var.type_info->type; } if (decl->var.init_expr && decl->type) { if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr, false)) return false; if (!expr_is_constant_eval(decl->var.init_expr)) { SEMA_ERROR(decl->var.init_expr, "The expression must be a constant value."); return false; } if (!decl->type) decl->type = decl->var.init_expr->type; } // We expect a constant to actually be parsed correctly so that it has a value, so // this should always be true. assert(decl->type || decl->var.kind == VARDECL_CONST); AttributeDomain domain = decl->var.kind == VARDECL_CONST ? ATTR_CONST : ATTR_FUNC; VECEACH(decl->attributes, i) { Attr *attr = decl->attributes[i]; AttributeType attribute = sema_analyse_attribute(context, attr, domain); if (attribute == ATTRIBUTE_NONE) return decl_poison(decl); bool had = false; #define SET_ATTR(_X) had = decl->func._X; decl->func._X = true; break switch (attribute) { case ATTRIBUTE_CNAME: had = decl->cname != NULL; decl->cname = attr->expr->const_expr.string.chars; break; case ATTRIBUTE_SECTION: had = decl->section != NULL; decl->section = attr->expr->const_expr.string.chars; break; case ATTRIBUTE_ALIGN: had = decl->alignment != 0; decl->alignment = attr->alignment; break; case ATTRIBUTE_WEAK: SET_ATTR(attr_weak); default: UNREACHABLE } #undef SET_ATTR if (had) { SEMA_TOKID_ERROR(attr->name, "Attribute occurred twice, please remove one."); return decl_poison(decl); } } switch (decl->var.kind) { case VARDECL_CONST: assert(decl->var.init_expr); return true; case VARDECL_GLOBAL: return true; default: eprintf("Decl %s %d\n", decl->name, decl->var.kind); UNREACHABLE } } static inline bool sema_analyse_generic(Context *context, Decl *decl) { TODO return true; } static inline bool sema_analyse_define(Context *context, Decl *decl) { Path *path = decl->generic_decl.path; TODO return true; } static inline bool sema_analyse_error(Context *context __unused, Decl *decl) { if (!sema_analyse_struct_union(context, decl)) return false; if (decl->strukt.size > type_size(type_usize)) { SEMA_ERROR(decl, "Error type may not exceed pointer size (%d bytes) it was %d bytes.", type_size(type_usize), decl->strukt.size); return false; } return true; } bool sema_analyse_decl(Context *context, Decl *decl) { if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl); DEBUG_LOG(">>> Analysing %s.", decl->name ? decl->name : "anon"); if (decl->resolve_status == RESOLVE_RUNNING) { SEMA_ERROR(decl, "Recursive definition of '%s'.", decl->name ? decl->name : "anon"); decl_poison(decl); return false; } decl->resolve_status = RESOLVE_RUNNING; decl->module = context->module; switch (decl->decl_kind) { case DECL_STRUCT: case DECL_UNION: if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl); decl_set_external_name(decl); break; case DECL_FUNC: if (!sema_analyse_func(context, decl)) return decl_poison(decl); decl_set_external_name(decl); break; case DECL_MACRO: if (!sema_analyse_macro(context, decl)) return decl_poison(decl); break; case DECL_VAR: if (!sema_analyse_global(context, decl)) return decl_poison(decl); decl_set_external_name(decl); break; case DECL_TYPEDEF: if (!sema_analyse_typedef(context, decl)) return decl_poison(decl); break; case DECL_ENUM: if (!sema_analyse_enum(context, decl)) return decl_poison(decl); decl_set_external_name(decl); break; case DECL_ERR: if (!sema_analyse_error(context, decl)) return decl_poison(decl); decl_set_external_name(decl); break; case DECL_GENERIC: if (!sema_analyse_generic(context, decl)) return decl_poison(decl); break; case DECL_DEFINE: if (!sema_analyse_define(context, decl)) return decl_poison(decl); break; case DECL_ATTRIBUTE: TODO case DECL_POISONED: case DECL_IMPORT: case DECL_ENUM_CONSTANT: case DECL_ARRAY_VALUE: case DECL_CT_ELSE: case DECL_CT_ELIF: case DECL_LABEL: case DECL_CT_SWITCH: case DECL_CT_CASE: case DECL_CT_IF: UNREACHABLE } decl->resolve_status = RESOLVE_DONE; return true; }