mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 03:51:18 +00:00
1642 lines
46 KiB
C
1642 lines
46 KiB
C
// 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"
|
|
|
|
|
|
|
|
|
|
static bool sema_analyse_struct_union(Context *context, Decl *decl);
|
|
|
|
static bool sema_check_section(Context *context, Decl *decl, Attr *attr)
|
|
{
|
|
const char *section_string = attr->expr->const_expr.string.chars;
|
|
decl->section = section_string;
|
|
// No restrictions except for MACH-O
|
|
if (platform_target.object_format != OBJ_FORMAT_MACHO)
|
|
{
|
|
return true;
|
|
}
|
|
scratch_buffer_clear();
|
|
StringSlice slice = strtoslice(section_string);
|
|
StringSlice segment = strnexttok(&slice, ',');
|
|
StringSlice section = strnexttok(&slice, ',');
|
|
StringSlice attrs = strnexttok(&slice, ',');
|
|
StringSlice stub_size_str = strnexttok(&slice, ',');
|
|
|
|
if (slice.len)
|
|
{
|
|
SEMA_ERROR(attr->expr, "Too many parts to the Mach-o section description.");
|
|
}
|
|
slicetrim(&segment);
|
|
if (segment.len == 0)
|
|
{
|
|
SEMA_ERROR(attr->expr, "The segment is missing, did you type it correctly?");
|
|
return false;
|
|
}
|
|
slicetrim(§ion);
|
|
if (section.len == 0)
|
|
{
|
|
SEMA_ERROR(attr->expr, "Mach-o requires 'segment,section' as the format, did you type it correctly?");
|
|
return false;
|
|
}
|
|
if (section.len > 16)
|
|
{
|
|
SEMA_ERROR(attr->expr, "Mach-o requires the section to be at the most 16 characters, can you shorten it?");
|
|
return false;
|
|
}
|
|
// TODO improve checking
|
|
return true;
|
|
}
|
|
static bool sema_check_unique_parameters(Decl **decls)
|
|
{
|
|
STable *table = &global_context.scratch_table;
|
|
stable_clear(table);
|
|
|
|
VECEACH(decls, i)
|
|
{
|
|
Decl *param = decls[i];
|
|
if (param->name)
|
|
{
|
|
Decl *old = stable_set(table, param->name, param);
|
|
if (old)
|
|
{
|
|
SEMA_ERROR(param, "Parameter name occurred twice, try renaming one of them.");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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_union_members(Context *context, Decl *decl, Decl **members)
|
|
{
|
|
ByteSize max_size = 0;
|
|
MemberIndex max_alignment_element = 0;
|
|
AlignSize max_alignment = 0;
|
|
|
|
VECEACH(members, i)
|
|
{
|
|
Decl *member = 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;
|
|
}
|
|
|
|
AlignSize member_alignment = type_abi_alignment(member->type);
|
|
ByteSize member_size = type_size(member->type);
|
|
|
|
// Update max alignment
|
|
if (member_alignment > max_alignment)
|
|
{
|
|
max_alignment = member_alignment;
|
|
max_alignment_element = i;
|
|
}
|
|
// Update max size
|
|
if (member_size > max_size)
|
|
{
|
|
//max_size_element = i;
|
|
max_size = member_size;
|
|
// If this is bigger than the previous with max
|
|
// alignment, pick this as the maximum size field.
|
|
if (max_alignment_element != (MemberIndex)i && max_alignment == member_alignment)
|
|
{
|
|
max_alignment_element = (MemberIndex)i;
|
|
}
|
|
}
|
|
// Offset is always 0
|
|
member->offset = 0;
|
|
}
|
|
|
|
if (!decl_ok(decl)) return false;
|
|
|
|
// 1. If packed, then the alignment is zero, unless previously given
|
|
if (decl->is_packed && !decl->alignment) decl->alignment = 1;
|
|
|
|
// 2. Otherwise pick the highest of the natural alignment and the given alignment.
|
|
if (!decl->is_packed) decl->alignment = MAX(decl->alignment, max_alignment);
|
|
|
|
// We're only packed if the max alignment is > 1
|
|
decl->is_packed = decl->is_packed && max_alignment > 1;
|
|
|
|
decl->strukt.union_rep = max_alignment_element;
|
|
|
|
// All members share the same alignment
|
|
VECEACH(members, i)
|
|
{
|
|
members[i]->alignment = decl->alignment;
|
|
}
|
|
|
|
if (!max_size)
|
|
{
|
|
decl->strukt.size = 0;
|
|
decl->alignment = 1;
|
|
return true;
|
|
}
|
|
|
|
// The actual size might be larger than the max size due to alignment.
|
|
unsigned size = aligned_offset(max_size, decl->alignment);
|
|
|
|
unsigned rep_size = type_size(members[max_alignment_element]->type);
|
|
|
|
// If the actual size is bigger than the real size, add
|
|
// padding.
|
|
if (size > rep_size)
|
|
{
|
|
decl->strukt.padding = size - rep_size;
|
|
}
|
|
|
|
decl->strukt.size = size;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool sema_analyse_struct_members(Context *context, Decl *decl, Decl **members)
|
|
{
|
|
// Default alignment is 1 even if the it is empty.
|
|
AlignSize natural_alignment = 1;
|
|
bool is_unaligned = false;
|
|
ByteSize size = 0;
|
|
ByteSize offset = 0;
|
|
bool is_packed = decl->is_packed;
|
|
VECEACH(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;
|
|
}
|
|
|
|
if (!decl_ok(decl)) return false;
|
|
|
|
AlignSize member_natural_alignment = type_abi_alignment(member->type);
|
|
AlignSize member_alignment = is_packed ? 1 : member_natural_alignment;
|
|
Attr **attributes = member->attributes;
|
|
unsigned count = vec_size(attributes);
|
|
for (unsigned j = 0; j < count; j++)
|
|
{
|
|
Attr *attribute = attributes[j];
|
|
if (!sema_analyse_attribute(context, attribute, ATTR_GLOBAL)) return false;
|
|
if (TOKSTR(attribute->name) == kw_align)
|
|
{
|
|
member_alignment = attribute->alignment;
|
|
// Update total alignment if we have a member that has bigger alignment.
|
|
if (member_alignment > decl->alignment) decl->alignment = member_alignment;
|
|
}
|
|
}
|
|
|
|
// If the member alignment is higher than the currently detected alignment,
|
|
// then we update the natural alignment
|
|
if (member_natural_alignment > natural_alignment)
|
|
{
|
|
natural_alignment = member_natural_alignment;
|
|
}
|
|
|
|
// In the case of a struct, we will align this to the next offset,
|
|
// using the alignment of the member.
|
|
unsigned align_offset = aligned_offset(offset, member_alignment);
|
|
|
|
unsigned natural_align_offset = aligned_offset(offset, member_natural_alignment);
|
|
|
|
// If the natural align is different from the aligned offset we have two cases:
|
|
if (natural_align_offset != align_offset)
|
|
{
|
|
// If the natural alignment is greater, in this case the struct is unaligned.
|
|
if (member_natural_alignment > member_alignment)
|
|
{
|
|
assert(natural_align_offset > align_offset);
|
|
is_unaligned = true;
|
|
}
|
|
else
|
|
{
|
|
// Otherwise we have a greater offset, and in this case
|
|
// we add padding for the difference.
|
|
assert(natural_align_offset < align_offset);
|
|
member->padding = align_offset - offset;
|
|
}
|
|
}
|
|
|
|
member->alignment = member_alignment;
|
|
|
|
offset = align_offset;
|
|
member->offset = offset;
|
|
offset += type_size(member->type);
|
|
}
|
|
|
|
// Set the alignment:
|
|
|
|
// 1. If packed, use the alignment given, otherwise set to 1.
|
|
if (decl->is_packed && !decl->alignment) decl->alignment = 1;
|
|
|
|
// 2. Otherwise pick the highest of the natural alignment and the given alignment.
|
|
if (!decl->is_packed) decl->alignment = MAX(decl->alignment, natural_alignment);
|
|
|
|
// We must now possibly add the end padding.
|
|
// First we calculate the actual size
|
|
size = aligned_offset(offset, decl->alignment);
|
|
|
|
// We might get a size that is greater than the natural alignment
|
|
// in this case we need an additional padding
|
|
if (size > aligned_offset(offset, natural_alignment))
|
|
{
|
|
decl->strukt.padding = size - offset;
|
|
}
|
|
|
|
// If the size is smaller the naturally aligned struct, then it is also unaligned
|
|
if (size < aligned_offset(offset, natural_alignment))
|
|
{
|
|
is_unaligned = true;
|
|
}
|
|
if (is_unaligned && size > offset)
|
|
{
|
|
assert(!decl->strukt.padding);
|
|
decl->strukt.padding = size - offset;
|
|
}
|
|
decl->is_packed = is_unaligned;
|
|
decl->strukt.size = size;
|
|
return true;
|
|
}
|
|
|
|
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_ERRTYPE:
|
|
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_decl._X; decl->func_decl._X = true; break
|
|
switch (attribute)
|
|
{
|
|
case ATTRIBUTE_EXTNAME:
|
|
had = decl->extname != NULL;
|
|
decl->extname = 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;
|
|
case ATTRIBUTE_OPAQUE:
|
|
had = decl->is_opaque;
|
|
decl->is_opaque = 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");
|
|
bool success;
|
|
if (decl->name)
|
|
{
|
|
SCOPE_START
|
|
if (decl->decl_kind == DECL_UNION)
|
|
{
|
|
success = sema_analyse_union_members(context, decl, decl->strukt.members);
|
|
}
|
|
else
|
|
{
|
|
success = sema_analyse_struct_members(context, decl, decl->strukt.members);
|
|
}
|
|
SCOPE_END;
|
|
}
|
|
else
|
|
{
|
|
if (decl->decl_kind == DECL_UNION)
|
|
{
|
|
success = sema_analyse_union_members(context, decl, decl->strukt.members);
|
|
}
|
|
else
|
|
{
|
|
success = sema_analyse_struct_members(context, decl, decl->strukt.members);
|
|
}
|
|
}
|
|
DEBUG_LOG("Struct/union size %d, alignment %d.", (int)decl->strukt.size, (int)decl->alignment);
|
|
DEBUG_LOG("Analysis complete.");
|
|
if (!success) return decl_poison(decl);
|
|
return decl_ok(decl);
|
|
}
|
|
|
|
static bool sema_analyse_bitstruct(Context *context, Decl *decl)
|
|
{
|
|
int overlap = -1;
|
|
VECEACH(decl->attributes, i)
|
|
{
|
|
Attr *attr = decl->attributes[i];
|
|
|
|
AttributeType attribute = sema_analyse_attribute(context, attr, ATTR_BITSTRUCT);
|
|
if (attribute == ATTRIBUTE_NONE) return decl_poison(decl);
|
|
|
|
bool had = false;
|
|
#define SET_ATTR(_X) had = decl->func_decl._X; decl->func_decl._X = true; break
|
|
switch (attribute)
|
|
{
|
|
case ATTRIBUTE_OVERLAP:
|
|
had = (overlap != -1);
|
|
overlap = 1;
|
|
break;
|
|
case ATTRIBUTE_OPAQUE:
|
|
had = decl->is_opaque;
|
|
decl->is_opaque = 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");
|
|
if (!sema_resolve_type_info(context, decl->bitstruct.base_type)) return false;
|
|
Type *type = decl->bitstruct.base_type->type->canonical;
|
|
Type *base_type = type->type_kind == TYPE_ARRAY ? type->array.base : type;
|
|
if (!type_is_integer(base_type))
|
|
{
|
|
SEMA_ERROR(decl->bitstruct.base_type, "The type of the bitstruct cannot be %s but must be an integer or an array of integers.",
|
|
type_quoted_error_string(decl->bitstruct.base_type->type));
|
|
return false;
|
|
}
|
|
Decl **members = decl->bitstruct.members;
|
|
bool success = true;
|
|
SCOPE_START
|
|
VECEACH(members, i)
|
|
{
|
|
Decl *member = members[i];
|
|
if (!sema_resolve_type_info(context, member->var.type_info))
|
|
{
|
|
success = false;
|
|
continue;
|
|
}
|
|
Type *member_type = type_flatten_for_bitstruct(member->var.type_info->type);
|
|
if (!type_is_integer(member_type) && member_type != type_bool)
|
|
{
|
|
SEMA_ERROR(member->var.type_info, "%s is not supported in a bitstruct, only enums, integer and boolean values may be used.",
|
|
type_quoted_error_string(member->var.type_info->type));
|
|
success = false;
|
|
continue;
|
|
}
|
|
}
|
|
SCOPE_END;
|
|
if (!success) return decl_poison(decl);
|
|
TODO
|
|
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);
|
|
// We need to check that the parameters are not typeless nor are of any macro parameter kind.
|
|
if (param->var.kind != VARDECL_PARAM)
|
|
{
|
|
SEMA_ERROR(param, "Only regular parameters are allowed for functions.");
|
|
return false;
|
|
}
|
|
if (!param->var.type_info)
|
|
{
|
|
SEMA_ERROR(param, "Only typed parameters are allowed for functions.");
|
|
return false;
|
|
}
|
|
if (!sema_resolve_type_info(context, param->var.type_info))
|
|
{
|
|
return false;
|
|
}
|
|
if (param->var.vararg)
|
|
{
|
|
param->var.type_info->type = type_get_subarray(param->var.type_info->type);
|
|
}
|
|
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_assigned_expr(context, param->type, expr, false)) return false;
|
|
Expr *inner = expr;
|
|
while (inner->expr_kind == EXPR_CAST) inner = expr->cast_expr.expr;
|
|
if (inner->expr_kind != EXPR_CONST)
|
|
{
|
|
SEMA_ERROR(expr, "Only constant expressions may be used as default values.");
|
|
return false;
|
|
}
|
|
*has_default = true;
|
|
}
|
|
param->alignment = type_abi_alignment(param->type);
|
|
return true;
|
|
}
|
|
|
|
static inline Type *sema_analyse_function_signature(Context *context, FunctionSignature *signature, bool is_real_function)
|
|
{
|
|
bool all_ok = true;
|
|
all_ok = sema_resolve_type_info(context, signature->rtype) && all_ok;
|
|
if (vec_size(signature->params) > MAX_PARAMS)
|
|
{
|
|
SEMA_ERROR(signature->params[MAX_PARAMS], "Number of params exceeds %d which is unsupported.", MAX_PARAMS);
|
|
return NULL;
|
|
}
|
|
STable *names = &global_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_real_function, &has_default))
|
|
{
|
|
decl_poison(param);
|
|
all_ok = false;
|
|
continue;
|
|
}
|
|
signature->has_default = signature->has_default || has_default;
|
|
param->resolve_status = RESOLVE_DONE;
|
|
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 (!all_ok) return NULL;
|
|
c_abi_func_create(signature);
|
|
return type_find_function_type(signature);
|
|
}
|
|
|
|
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_distinct(Context *context, Decl *decl)
|
|
{
|
|
if (decl->distinct_decl.typedef_decl.is_func)
|
|
{
|
|
Type *func_type = sema_analyse_function_signature(context, &decl->distinct_decl.typedef_decl.function_signature, false);
|
|
if (!func_type) return false;
|
|
decl->distinct_decl.base_type = type_get_ptr(func_type);
|
|
return true;
|
|
}
|
|
TypeInfo *info = decl->distinct_decl.typedef_decl.type_info;
|
|
if (!sema_resolve_type_info(context, info)) return false;
|
|
Type *base = type_flatten_distinct(info->type);
|
|
decl->distinct_decl.base_type = base;
|
|
switch (base->type_kind)
|
|
{
|
|
case TYPE_STRLIT:
|
|
case TYPE_FUNC:
|
|
case TYPE_TYPEDEF:
|
|
case TYPE_DISTINCT:
|
|
case CT_TYPES:
|
|
UNREACHABLE
|
|
return false;
|
|
case TYPE_FAILABLE:
|
|
SEMA_ERROR(decl, "You cannot create a distinct type from a failable.");
|
|
return false;
|
|
case TYPE_VIRTUAL_ANY:
|
|
case TYPE_VIRTUAL:
|
|
SEMA_ERROR(decl, "You cannot create a distinct type from a virtual type.");
|
|
return false;
|
|
case TYPE_ERRTYPE:
|
|
SEMA_ERROR(decl, "You cannot create a distinct type from an error type.");
|
|
return false;
|
|
case TYPE_ANYERR:
|
|
SEMA_ERROR(decl, "You cannot create a distinct type from an error union.");
|
|
return false;
|
|
case TYPE_VOID:
|
|
case TYPE_TYPEID:
|
|
SEMA_ERROR(decl, "Cannot create a distinct type from %s.", type_quoted_error_string(base));
|
|
case TYPE_BOOL:
|
|
case ALL_INTS:
|
|
case ALL_FLOATS:
|
|
case TYPE_POINTER:
|
|
case TYPE_ENUM:
|
|
case TYPE_BITSTRUCT:
|
|
case TYPE_STRUCT:
|
|
case TYPE_UNION:
|
|
case TYPE_ARRAY:
|
|
case TYPE_SUBARRAY:
|
|
case TYPE_VECTOR:
|
|
break;
|
|
}
|
|
// 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);
|
|
Int128 value = { 0, 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;
|
|
REMINDER("Do range check");
|
|
expr->const_expr.ixx = (Int) { value, canonical->type_kind };
|
|
expr->const_expr.const_kind = CONST_INTEGER;
|
|
expr->const_expr.narrowable = true;
|
|
expr->type = canonical;
|
|
enum_value->enum_constant.expr = expr;
|
|
}
|
|
|
|
// We try to convert to the desired type.
|
|
if (!sema_analyse_expr_of_required_type(context, type, expr))
|
|
{
|
|
success = false;
|
|
enum_value->resolve_status = RESOLVE_DONE;
|
|
decl_poison(enum_value);
|
|
// Reset!
|
|
value = (Int128) { 0, 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
|
|
value = i128_add64(value, 1);
|
|
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_error(Context *context, Decl *decl)
|
|
{
|
|
bool success = true;
|
|
unsigned enums = vec_size(decl->enums.values);
|
|
|
|
for (unsigned i = 0; i < enums; i++)
|
|
{
|
|
Decl *enum_value = decl->enums.values[i];
|
|
enum_value->type = decl->type;
|
|
DEBUG_LOG("* Checking error value %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_ERRVALUE);
|
|
|
|
// Start evaluating the constant
|
|
enum_value->resolve_status = RESOLVE_DONE;
|
|
}
|
|
return success;
|
|
}
|
|
|
|
static inline const char *name_by_decl(Decl *method_like)
|
|
{
|
|
switch (method_like->decl_kind)
|
|
{
|
|
case DECL_MACRO:
|
|
return "macro method";
|
|
case DECL_FUNC:
|
|
return "method";
|
|
case DECL_GENERIC:
|
|
return "generic method";
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
}
|
|
static inline bool sema_add_method_like(Context *context, Type *parent_type, Decl *method_like)
|
|
{
|
|
assert(parent_type->canonical == parent_type);
|
|
Decl *parent = parent_type->decl;
|
|
const char *name = method_like->name;
|
|
Decl *method = sema_find_extension_method_in_module(context->module, parent_type, name);
|
|
if (method)
|
|
{
|
|
SEMA_TOKID_ERROR(method_like->name_token, "This %s is already defined in this module.", name_by_decl(method_like));
|
|
SEMA_TOKID_PREV(method->name_token, "The previous definition was here.");
|
|
return false;
|
|
}
|
|
Decl *ambiguous = NULL;
|
|
Decl *private = NULL;
|
|
method = sema_resolve_method(context, parent, name, &ambiguous, &private);
|
|
if (method)
|
|
{
|
|
SEMA_TOKID_ERROR(method_like->name_token, "This %s is already defined for '%s'.", name_by_decl(method_like), parent_type->name);
|
|
SEMA_TOKID_PREV(method->name_token, "The previous definition was here.");
|
|
return false;
|
|
}
|
|
scratch_buffer_clear();
|
|
if (method_like->visibility <= VISIBLE_MODULE)
|
|
{
|
|
scratch_buffer_append(parent->name);
|
|
scratch_buffer_append_char('.');
|
|
scratch_buffer_append(method_like->name);
|
|
}
|
|
else
|
|
{
|
|
scratch_buffer_append(parent->external_name);
|
|
scratch_buffer_append("__");
|
|
scratch_buffer_append(method_like->name);
|
|
}
|
|
method_like->external_name = scratch_buffer_interned();
|
|
DEBUG_LOG("Method-like '%s.%s' analysed.", parent->name, method_like->name);
|
|
if (parent->module == context->module)
|
|
{
|
|
vec_add(parent->methods, method_like);
|
|
}
|
|
else
|
|
{
|
|
vec_add(context->module->method_extensions, method_like);
|
|
}
|
|
return true;
|
|
|
|
}
|
|
|
|
static inline bool sema_analyse_method(Context *context, Decl *decl)
|
|
{
|
|
TypeInfo *parent_type = decl->func_decl.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_decl.type_parent->type));
|
|
return false;
|
|
}
|
|
Type *type = parent_type->type->canonical;
|
|
return sema_add_method_like(context, type, decl);
|
|
}
|
|
|
|
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_LOCAL:
|
|
return "local variable";
|
|
case ATTR_BITSTRUCT:
|
|
return "bitstruct";
|
|
case ATTR_INTERFACE:
|
|
return "interface";
|
|
case ATTR_MEMBER:
|
|
return "member";
|
|
case ATTR_FUNC:
|
|
return "function";
|
|
case ATTR_GLOBAL:
|
|
return "global 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";
|
|
case ATTR_CALL:
|
|
return "call";
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
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_GLOBAL,
|
|
[ATTRIBUTE_EXTNAME] = ~ATTR_CALL,
|
|
[ATTRIBUTE_SECTION] = ATTR_FUNC | ATTR_CONST | ATTR_GLOBAL,
|
|
[ATTRIBUTE_PACKED] = ATTR_STRUCT | ATTR_UNION,
|
|
[ATTRIBUTE_NORETURN] = ATTR_FUNC,
|
|
[ATTRIBUTE_ALIGN] = ATTR_FUNC | ATTR_CONST | ATTR_LOCAL | ATTR_GLOBAL | ATTR_STRUCT | ATTR_UNION | ATTR_MEMBER,
|
|
[ATTRIBUTE_INLINE] = ATTR_FUNC | ATTR_CALL,
|
|
[ATTRIBUTE_NOINLINE] = ATTR_FUNC | ATTR_CALL,
|
|
[ATTRIBUTE_OPAQUE] = ATTR_STRUCT | ATTR_UNION,
|
|
[ATTRIBUTE_USED] = ~ATTR_CALL,
|
|
[ATTRIBUTE_UNUSED] = ~ATTR_CALL,
|
|
[ATTRIBUTE_NAKED] = ATTR_FUNC,
|
|
[ATTRIBUTE_CDECL] = ATTR_FUNC,
|
|
[ATTRIBUTE_STDCALL] = ATTR_FUNC,
|
|
[ATTRIBUTE_VECCALL] = ATTR_FUNC,
|
|
[ATTRIBUTE_REGCALL] = ATTR_FUNC,
|
|
[ATTRIBUTE_FASTCALL] = ATTR_FUNC,
|
|
[ATTRIBUTE_OVERLAP] = ATTR_BITSTRUCT
|
|
};
|
|
|
|
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_CDECL:
|
|
case ATTRIBUTE_FASTCALL:
|
|
case ATTRIBUTE_STDCALL:
|
|
case ATTRIBUTE_VECCALL:
|
|
case ATTRIBUTE_REGCALL:
|
|
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, attr->expr)) return false;
|
|
if (attr->expr->expr_kind != EXPR_CONST || !type_is_integer(attr->expr->type->canonical))
|
|
{
|
|
SEMA_ERROR(attr->expr, "Expected a constant integer value as argument.");
|
|
return ATTRIBUTE_NONE;
|
|
}
|
|
{
|
|
if (int_ucomp(attr->expr->const_expr.ixx, MAX_ALIGNMENT, BINARYOP_GT))
|
|
{
|
|
SEMA_ERROR(attr->expr, "Alignment must be less or equal to %ull.", MAX_ALIGNMENT);
|
|
return ATTRIBUTE_NONE;
|
|
}
|
|
if (int_ucomp(attr->expr->const_expr.ixx, 0, BINARYOP_LE))
|
|
{
|
|
SEMA_ERROR(attr->expr, "Alignment must be greater than zero.");
|
|
return ATTRIBUTE_NONE;
|
|
}
|
|
uint64_t align = int_to_u64(attr->expr->const_expr.ixx);
|
|
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_EXTNAME:
|
|
if (context->module->is_generic)
|
|
{
|
|
SEMA_TOKID_ERROR(attr->name, "'extname' attributes are not allowed in generic modules.");
|
|
return false;
|
|
}
|
|
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, attr->expr)) return false;
|
|
if (attr->expr->expr_kind != EXPR_CONST || attr->expr->type->canonical != type_compstr)
|
|
{
|
|
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_doc_header(Ast *docs, Decl **params, Decl **extra_params)
|
|
{
|
|
if (!docs) return true;
|
|
assert(docs->ast_kind == AST_DOCS);
|
|
Ast **doc_directives = docs->directives;
|
|
VECEACH(doc_directives, i)
|
|
{
|
|
Ast *directive = doc_directives[i];
|
|
if (directive->doc_directive.kind != DOC_DIRECTIVE_PARAM) continue;
|
|
TokenId param = directive->doc_directive.param.param;
|
|
const char *param_name = TOKSTR(param);
|
|
VECEACH(params, j)
|
|
{
|
|
if (params[j]->name == param_name) goto NEXT;
|
|
}
|
|
VECEACH(extra_params, j)
|
|
{
|
|
if (extra_params[j]->name == param_name) goto NEXT;
|
|
}
|
|
SEMA_TOKID_ERROR(param, "There is no parameter '%s', did you misspell it?", param_name);
|
|
return false;
|
|
NEXT:
|
|
continue;
|
|
}
|
|
return true;
|
|
}
|
|
static inline bool sema_update_call_convention(Decl *decl, CallABI abi)
|
|
{
|
|
bool had = decl->func_decl.function_signature.call_abi > 0;
|
|
decl->func_decl.function_signature.call_abi = abi;
|
|
return had;
|
|
}
|
|
|
|
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_decl.function_signature, true);
|
|
|
|
decl->type = func_type;
|
|
if (!func_type) return decl_poison(decl);
|
|
if (decl->func_decl.type_parent)
|
|
{
|
|
if (!sema_analyse_method(context, decl)) return decl_poison(decl);
|
|
}
|
|
else
|
|
{
|
|
if (decl->name == kw_main)
|
|
{
|
|
if (decl->visibility == VISIBLE_LOCAL)
|
|
{
|
|
SEMA_ERROR(decl, "'main' cannot have local visibility.");
|
|
return false;
|
|
}
|
|
decl->visibility = VISIBLE_EXTERN;
|
|
}
|
|
decl_set_external_name(decl);
|
|
}
|
|
if (!sema_analyse_doc_header(decl->docs, decl->func_decl.function_signature.params, NULL)) 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_decl._X; decl->func_decl._X = true; break
|
|
|
|
switch (attribute)
|
|
{
|
|
case ATTRIBUTE_EXTNAME:
|
|
had = decl->extname != NULL;
|
|
decl->extname = attr->expr->const_expr.string.chars;
|
|
break;
|
|
case ATTRIBUTE_SECTION:
|
|
had = decl->section != NULL;
|
|
if (!sema_check_section(context, decl, attr)) return decl_poison(decl);
|
|
break;
|
|
case ATTRIBUTE_ALIGN:
|
|
had = decl->alignment != 0;
|
|
decl->alignment = attr->alignment;
|
|
break;
|
|
case ATTRIBUTE_NOINLINE: SET_ATTR(attr_noinline);
|
|
case ATTRIBUTE_STDCALL:
|
|
if (platform_target.arch == ARCH_TYPE_X86 || platform_target.arch == ARCH_TYPE_X86_64)
|
|
{
|
|
had = sema_update_call_convention(decl, CALL_X86_STD);
|
|
}
|
|
else if (platform_target.arch == ARCH_TYPE_ARM || platform_target.arch == ARCH_TYPE_ARMB)
|
|
{
|
|
had = sema_update_call_convention(decl, CALL_AAPCS);
|
|
}
|
|
break;
|
|
case ATTRIBUTE_CDECL:
|
|
had = sema_update_call_convention(decl, CALL_C);
|
|
break;
|
|
case ATTRIBUTE_VECCALL:
|
|
switch (platform_target.arch)
|
|
{
|
|
case ARCH_TYPE_X86_64:
|
|
case ARCH_TYPE_X86:
|
|
had = sema_update_call_convention(decl, CALL_X86_VECTOR);
|
|
break;
|
|
case ARCH_TYPE_ARM:
|
|
case ARCH_TYPE_ARMB:
|
|
case ARCH_TYPE_AARCH64:
|
|
case ARCH_TYPE_AARCH64_32:
|
|
case ARCH_TYPE_AARCH64_BE:
|
|
had = sema_update_call_convention(decl, CALL_AAPCS_VFP);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case ATTRIBUTE_FASTCALL:
|
|
if (platform_target.arch == ARCH_TYPE_X86)
|
|
{
|
|
had = sema_update_call_convention(decl, CALL_X86_FAST);
|
|
}
|
|
break;
|
|
case ATTRIBUTE_REGCALL:
|
|
had = decl->func_decl.function_signature.call_abi > 0;
|
|
if (platform_target.arch == ARCH_TYPE_X86)
|
|
{
|
|
had = sema_update_call_convention(decl, CALL_X86_REG);
|
|
}
|
|
break;
|
|
case ATTRIBUTE_INLINE: SET_ATTR(attr_inline);
|
|
case ATTRIBUTE_NORETURN: SET_ATTR(attr_noreturn);
|
|
case ATTRIBUTE_WEAK: SET_ATTR(attr_weak);
|
|
case ATTRIBUTE_NAKED: SET_ATTR(attr_naked);
|
|
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_decl.attr_inline && decl->func_decl.attr_noinline)
|
|
{
|
|
SEMA_TOKID_ERROR(attr->name, "A function cannot be 'inline' and 'noinline' at the same time.");
|
|
return decl_poison(decl);
|
|
}
|
|
}
|
|
DEBUG_LOG("Function analysis done.");
|
|
return true;
|
|
}
|
|
|
|
static bool sema_analyse_macro_method(Context *context, Decl *decl)
|
|
{
|
|
if (!sema_resolve_type_info(context, decl->macro_decl.type_parent)) return false;
|
|
Type *parent_type = decl->macro_decl.type_parent->type;
|
|
if (!type_may_have_sub_elements(parent_type))
|
|
{
|
|
SEMA_ERROR(decl->macro_decl.type_parent,
|
|
"Methods can not be associated with '%s'",
|
|
type_to_error_string(parent_type));
|
|
return false;
|
|
}
|
|
Type *expected_first_element = type_get_ptr(parent_type);
|
|
if (!vec_size(decl->macro_decl.parameters))
|
|
{
|
|
SEMA_ERROR(decl, "Expected at least one parameter - of type %s.", type_quoted_error_string(expected_first_element));
|
|
return false;
|
|
}
|
|
Decl *first_param = decl->macro_decl.parameters[0];
|
|
if (!first_param->type)
|
|
{
|
|
SEMA_ERROR(first_param, "The first parameter must have the explicit type %s.", type_quoted_error_string(expected_first_element));
|
|
return false;
|
|
}
|
|
if (first_param->type->canonical != expected_first_element->canonical)
|
|
{
|
|
SEMA_ERROR(first_param, "The first parameter must be of type %s.", expected_first_element);
|
|
return false;
|
|
}
|
|
if (first_param->var.kind != VARDECL_PARAM)
|
|
{
|
|
SEMA_ERROR(first_param, "The first parameter must be a regular value parameter.");
|
|
return false;
|
|
}
|
|
return sema_add_method_like(context, parent_type, decl);
|
|
}
|
|
|
|
static inline bool sema_analyse_macro(Context *context, Decl *decl)
|
|
{
|
|
bool is_generic = decl->decl_kind == DECL_GENERIC;
|
|
TypeInfo *rtype = decl->macro_decl.rtype;
|
|
if (decl->macro_decl.rtype && !sema_resolve_type_info(context, rtype)) return decl_poison(decl);
|
|
decl->macro_decl.context = context;
|
|
VECEACH(decl->macro_decl.parameters, i)
|
|
{
|
|
Decl *param = decl->macro_decl.parameters[i];
|
|
param->resolve_status = RESOLVE_RUNNING;
|
|
assert(param->decl_kind == DECL_VAR);
|
|
switch (param->var.kind)
|
|
{
|
|
case VARDECL_PARAM_EXPR:
|
|
case VARDECL_PARAM_CT:
|
|
case VARDECL_PARAM_REF:
|
|
if (is_generic)
|
|
{
|
|
SEMA_ERROR(param, "Only regular parameters and type parameters are allowed for generic functions.");
|
|
return decl_poison(decl);
|
|
}
|
|
FALLTHROUGH;
|
|
case VARDECL_PARAM:
|
|
if (param->var.type_info)
|
|
{
|
|
if (!sema_resolve_type_info(context, param->var.type_info)) return decl_poison(decl);
|
|
param->type = param->var.type_info->type;
|
|
}
|
|
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 decl_poison(decl);
|
|
}
|
|
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
|
|
}
|
|
param->resolve_status = RESOLVE_DONE;
|
|
}
|
|
if (is_generic && vec_size(decl->macro_decl.body_parameters))
|
|
{
|
|
SEMA_ERROR(decl->macro_decl.body_parameters[0], "Trailing block syntax is not allowed for generic functions.");
|
|
return decl_poison(decl);
|
|
}
|
|
VECEACH(decl->macro_decl.body_parameters, i)
|
|
{
|
|
Decl *param = decl->macro_decl.body_parameters[i];
|
|
param->resolve_status = RESOLVE_RUNNING;
|
|
assert(param->decl_kind == DECL_VAR);
|
|
switch (param->var.kind)
|
|
{
|
|
case VARDECL_PARAM:
|
|
if (param->var.type_info && !sema_resolve_type_info(context, param->var.type_info)) return decl_poison(decl);
|
|
break;
|
|
case VARDECL_PARAM_EXPR:
|
|
case VARDECL_PARAM_CT:
|
|
case VARDECL_PARAM_REF:
|
|
case VARDECL_PARAM_CT_TYPE:
|
|
SEMA_ERROR(param, "Only plain variables are allowed as body parameters.");
|
|
return decl_poison(decl);
|
|
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
|
|
}
|
|
param->resolve_status = RESOLVE_DONE;
|
|
}
|
|
if (!sema_check_unique_parameters(decl->macro_decl.parameters)) return decl_poison(decl);
|
|
if (!sema_check_unique_parameters(decl->macro_decl.body_parameters)) return decl_poison(decl);
|
|
if (!sema_analyse_doc_header(decl->docs, decl->macro_decl.parameters, decl->macro_decl.body_parameters)) return decl_poison(decl);
|
|
if (decl->macro_decl.type_parent)
|
|
{
|
|
if (!sema_analyse_macro_method(context, decl)) return decl_poison(decl);
|
|
}
|
|
decl->type = type_void;
|
|
return true;
|
|
}
|
|
|
|
|
|
bool sema_analyse_attributes_for_var(Context *context, Decl *decl)
|
|
{
|
|
|
|
AttributeDomain domain;
|
|
switch (decl->var.kind)
|
|
{
|
|
case VARDECL_CONST:
|
|
domain = ATTR_CONST;
|
|
break;
|
|
case VARDECL_GLOBAL:
|
|
domain = ATTR_GLOBAL;
|
|
break;
|
|
default:
|
|
domain = ATTR_LOCAL;
|
|
break;
|
|
}
|
|
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_decl._X; decl->func_decl._X = true; break
|
|
switch (attribute)
|
|
{
|
|
case ATTRIBUTE_EXTNAME:
|
|
had = decl->extname != NULL;
|
|
decl->extname = attr->expr->const_expr.string.chars;
|
|
break;
|
|
case ATTRIBUTE_SECTION:
|
|
had = decl->section != NULL;
|
|
if (!sema_check_section(context, decl, attr)) return decl_poison(decl);
|
|
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);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Analyse a regular global or local declaration, e.g. int x = 123
|
|
*/
|
|
bool sema_analyse_var_decl(Context *context, Decl *decl)
|
|
{
|
|
assert(decl->decl_kind == DECL_VAR && "Unexpected declaration type");
|
|
|
|
// We expect a constant to actually be parsed correctly so that it has a value, so
|
|
// this should always be true.
|
|
assert(decl->var.type_info || decl->var.kind == VARDECL_CONST);
|
|
|
|
bool is_global = decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST;
|
|
|
|
if (!sema_analyse_attributes_for_var(context, decl)) return false;
|
|
|
|
if (is_global)
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
// Add a local to the current context, will throw error on shadowing.
|
|
if (!sema_add_local(context, decl)) return decl_poison(decl);
|
|
}
|
|
|
|
// 1. Local constants: const int FOO = 123.
|
|
if (decl->var.kind == VARDECL_CONST)
|
|
{
|
|
Expr *init_expr = decl->var.init_expr;
|
|
// 1a. We require an init expression.
|
|
if (!init_expr)
|
|
{
|
|
SEMA_ERROR(decl, "Constants need to have an initial value.");
|
|
return false;
|
|
}
|
|
// 1b. We require defined constants
|
|
if (init_expr->expr_kind == EXPR_UNDEF)
|
|
{
|
|
SEMA_ERROR(decl, "Constants cannot be undefined.");
|
|
return false;
|
|
}
|
|
if (!decl->var.type_info)
|
|
{
|
|
if (!sema_analyse_expr(context, init_expr)) return false;
|
|
decl->type = init_expr->type;
|
|
if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type);
|
|
|
|
// Skip further evaluation.
|
|
goto EXIT_OK;
|
|
}
|
|
}
|
|
|
|
if (!sema_resolve_type_info_maybe_inferred(context, decl->var.type_info, decl->var.init_expr != NULL)) return decl_poison(decl);
|
|
decl->type = decl->var.type_info->type;
|
|
|
|
if (decl->var.is_static)
|
|
{
|
|
scratch_buffer_clear();
|
|
scratch_buffer_append(context->active_function_for_analysis->name);
|
|
scratch_buffer_append_char('.');
|
|
scratch_buffer_append(decl->name);
|
|
decl->external_name = scratch_buffer_interned();
|
|
}
|
|
|
|
if (decl->var.init_expr)
|
|
{
|
|
bool type_is_inferred = decl->type->type_kind == TYPE_INFERRED_ARRAY;
|
|
Expr *init = decl->var.init_expr;
|
|
// Handle explicit undef
|
|
if (init->expr_kind == EXPR_UNDEF)
|
|
{
|
|
if (type_is_inferred)
|
|
{
|
|
SEMA_ERROR(decl->var.type_info, "Size of the array cannot be inferred with explicit undef.");
|
|
return false;
|
|
}
|
|
goto EXIT_OK;
|
|
}
|
|
|
|
if (!type_is_inferred)
|
|
{
|
|
// Pre resolve to avoid problem with recursive definitions.
|
|
decl->resolve_status = RESOLVE_DONE;
|
|
if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type);
|
|
}
|
|
|
|
if (!sema_expr_analyse_assign_right_side(context, NULL, decl->type, init, false)) return decl_poison(decl);
|
|
|
|
if (type_is_inferred)
|
|
{
|
|
Type *right_side_type = init->type->canonical;
|
|
assert(right_side_type->type_kind == TYPE_ARRAY);
|
|
decl->type = type_get_array(decl->type->array.base, right_side_type->array.len);
|
|
}
|
|
else if (decl->type)
|
|
{
|
|
decl->var.init_expr->type = decl->type;
|
|
}
|
|
|
|
Expr *init_expr = decl->var.init_expr;
|
|
|
|
// 1. Check type.
|
|
if (!sema_analyse_assigned_expr(context, decl->type, init_expr, false)) return false;
|
|
|
|
// 2. Check const-ness
|
|
if ((is_global || decl->var.is_static) && !expr_is_constant_eval(init_expr, CONSTANT_EVAL_ANY))
|
|
{
|
|
SEMA_ERROR(init_expr, "The expression must be a constant value.");
|
|
}
|
|
else
|
|
{
|
|
if (decl->var.unwrap && init->type->type_kind != TYPE_FAILABLE)
|
|
{
|
|
SEMA_ERROR(decl->var.init_expr, "A failable expression was expected here.");
|
|
return decl_poison(decl);
|
|
}
|
|
}
|
|
if (init_expr->expr_kind == EXPR_CONST) init_expr->const_expr.narrowable = false;
|
|
}
|
|
EXIT_OK:
|
|
if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
static Context *copy_context(Module *module, Context *c)
|
|
{
|
|
Context *copy = context_create(c->file);
|
|
copy->imports = copy_decl_list(c->imports);
|
|
copy->global_decls = copy_decl_list(c->global_decls);
|
|
copy->module = module;
|
|
assert(!c->functions && !c->macro_methods && !c->methods && !c->enums && !c->ct_ifs && !c->types && !c->interfaces && !c->external_symbol_list);
|
|
return copy;
|
|
}
|
|
|
|
static Module *sema_instantiate_module(Context *context, Module *module, Path *path, TypeInfo **parms)
|
|
{
|
|
Module *new_module = compiler_find_or_create_module(path, NULL, module->is_private);
|
|
new_module->is_generic = true;
|
|
Context **contexts = module->contexts;
|
|
VECEACH(contexts, i)
|
|
{
|
|
vec_add(new_module->contexts, copy_context(new_module, contexts[i]));
|
|
}
|
|
Context *first_context = new_module->contexts[0];
|
|
VECEACH(module->parameters, i)
|
|
{
|
|
TokenId param = module->parameters[i];
|
|
Decl *decl = decl_new_with_type(param, DECL_TYPEDEF, VISIBLE_PUBLIC);
|
|
decl->resolve_status = RESOLVE_DONE;
|
|
TypeInfo *type_info = parms[i];
|
|
assert(type_info->resolve_status == RESOLVE_DONE);
|
|
decl->typedef_decl.type_info = type_info;
|
|
decl->type->name = decl->name;
|
|
decl->type->canonical = type_info->type->canonical;
|
|
vec_add(first_context->global_decls, decl);
|
|
}
|
|
return new_module;
|
|
}
|
|
|
|
static bool sema_analyse_parameterized_define(Context *c, Decl *decl)
|
|
{
|
|
Path *decl_path;
|
|
TokenId name;
|
|
switch (decl->define_decl.define_kind)
|
|
{
|
|
case DEFINE_IDENT_GENERIC:
|
|
decl_path = decl->define_decl.path;
|
|
name = decl->define_decl.identifier;
|
|
break;
|
|
case DEFINE_TYPE_GENERIC:
|
|
{
|
|
TypeInfo *define_type = decl->define_decl.type_info;
|
|
if (define_type->resolve_status == RESOLVE_DONE && type_is_user_defined(define_type->type))
|
|
{
|
|
SEMA_ERROR(define_type, "Expected a user defined type for parameterization.");
|
|
return decl_poison(decl);
|
|
}
|
|
decl_path = define_type->unresolved.path;
|
|
name = define_type->unresolved.name_loc;
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
Decl *alias = sema_resolve_parameterized_symbol(c, name, decl_path);
|
|
if (!decl_ok(alias))
|
|
{
|
|
return decl_poison(decl);
|
|
}
|
|
|
|
Module *module = alias->module;
|
|
TypeInfo **params = decl->define_decl.generic_params;
|
|
unsigned parameter_count = vec_size(module->parameters);
|
|
assert(parameter_count > 0);
|
|
if (parameter_count != vec_size(params))
|
|
{
|
|
sema_error_range((SourceSpan) { params[0]->span.loc, VECLAST(params)->span.end_loc }, "The generic module expected %d arguments, but you only supplied %d, did you make a mistake?",
|
|
parameter_count, vec_size(decl->define_decl.generic_params));
|
|
return decl_poison(decl);
|
|
}
|
|
scratch_buffer_clear();
|
|
scratch_buffer_append_len(module->name->module, module->name->len);
|
|
scratch_buffer_append_char('.');
|
|
VECEACH(decl->define_decl.generic_params, i)
|
|
{
|
|
TypeInfo *type_info = decl->define_decl.generic_params[i];
|
|
if (!sema_resolve_type_info(c, type_info)) return decl_poison(decl);
|
|
if (i != 0) scratch_buffer_append_char('.');
|
|
const char *type_name = type_info->type->canonical->name;
|
|
scratch_buffer_append(type_name);
|
|
}
|
|
TokenType ident_type = TOKEN_IDENT;
|
|
const char *path_string = scratch_buffer_interned();
|
|
Module *instantiated_module = global_context_find_module(path_string);
|
|
if (!instantiated_module)
|
|
{
|
|
Path *path = CALLOCS(Path);
|
|
path->module = path_string;
|
|
path->span = module->name->span;
|
|
path->len = global_context.scratch_buffer_len;
|
|
instantiated_module = sema_instantiate_module(c, module, path, decl->define_decl.generic_params);
|
|
sema_analyze_stage(instantiated_module, c->module->stage);
|
|
}
|
|
if (global_context.errors_found) return decl_poison(decl);
|
|
const char *name_str = TOKSTR(name);
|
|
Decl *symbol = module_find_symbol(instantiated_module, name_str);
|
|
assert(symbol);
|
|
context_register_external_symbol(c, symbol);
|
|
switch (decl->define_decl.define_kind)
|
|
{
|
|
case DEFINE_IDENT_GENERIC:
|
|
decl->define_decl.alias = symbol;
|
|
return true;
|
|
case DEFINE_TYPE_GENERIC:
|
|
{
|
|
Type *type = type_new(TYPE_TYPEDEF, decl->name);
|
|
decl->type = type;
|
|
decl->decl_kind = DECL_TYPEDEF;
|
|
type->canonical = symbol->type->canonical;
|
|
return true;
|
|
}
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
}
|
|
static void decl_define_type(Decl *decl, Type *actual_type)
|
|
{
|
|
Type *type = type_new(TYPE_TYPEDEF, decl->name);
|
|
type->decl = decl;
|
|
type->canonical = actual_type->canonical;
|
|
decl->type = type;
|
|
}
|
|
|
|
static inline bool sema_analyse_define(Context *c, Decl *decl)
|
|
{
|
|
// 1. The plain define
|
|
if (decl->define_decl.define_kind == DEFINE_IDENT_ALIAS)
|
|
{
|
|
Decl *symbol = sema_resolve_normal_symbol(c,
|
|
decl->define_decl.identifier,
|
|
decl->define_decl.path,
|
|
true);
|
|
if (!decl_ok(symbol)) return false;
|
|
decl->type = symbol->type;
|
|
decl->define_decl.alias = symbol;
|
|
return true;
|
|
}
|
|
|
|
// 2. Handle type generics.
|
|
return sema_analyse_parameterized_define(c, decl);
|
|
}
|
|
|
|
|
|
|
|
|
|
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_INTERFACE:
|
|
TODO
|
|
case DECL_BITSTRUCT:
|
|
if (!sema_analyse_bitstruct(context, decl)) return decl_poison(decl);
|
|
decl_set_external_name(decl);
|
|
break;
|
|
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);
|
|
break;
|
|
case DECL_MACRO:
|
|
case DECL_GENERIC:
|
|
if (!sema_analyse_macro(context, decl)) return decl_poison(decl);
|
|
break;
|
|
case DECL_VAR:
|
|
if (!sema_analyse_var_decl(context, decl)) return decl_poison(decl);
|
|
decl_set_external_name(decl);
|
|
break;
|
|
case DECL_DISTINCT:
|
|
if (!sema_analyse_distinct(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_ERRTYPE:
|
|
if (!sema_analyse_error(context, decl)) return decl_poison(decl);
|
|
decl_set_external_name(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:
|
|
case DECL_CT_ASSERT:
|
|
case DECL_ERRVALUE:
|
|
UNREACHABLE
|
|
}
|
|
decl->resolve_status = RESOLVE_DONE;
|
|
return true;
|
|
}
|