#include "eval.h" #include "ast.h" #include "gen_vec.h" #include "hashmap.h" #include "vector.h" #include "vector_impl.h" #include #include #define PRIMITIF_TO(name, al) \ const TypeObject PRIMITIF_##name = {.kind = TypePrimitif, .align = _ALIGN_##al, .type.primitif = Primitif_##name} PRIMITIF_TO(u8, 1); PRIMITIF_TO(u16, 2); PRIMITIF_TO(u32, 4); PRIMITIF_TO(u64, 8); PRIMITIF_TO(i8, 1); PRIMITIF_TO(i16, 2); PRIMITIF_TO(i32, 4); PRIMITIF_TO(i64, 8); PRIMITIF_TO(f32, 4); PRIMITIF_TO(f64, 8); PRIMITIF_TO(char, 1); PRIMITIF_TO(bool, 1); #undef PRIMITIF_TO void array_drop(Array a) { free(a.type); } void type_drop(TypeObject t) { if (t.kind == TypeArray) { array_drop(t.type.array); } } void struct_drop(StructObject s) { vec_drop(s.fields); } void message_drop(MessageObject m) { vec_drop(m.fields); } void messages_drop(MessagesObject m) { vec_drop(m.messages); } static Alignment max_alignment(Alignment a, Alignment b) { if (a.value > b.value) { return a; } else { return b; } } static char *attributes_to_string(Attributes attrs, bool and) { uint32_t count = 0; Attributes attributes[ATTRIBUTES_COUNT]; #define handle(a) \ if (attrs & Attr_##a) \ attributes[count++] = Attr_##a; handle(versioned); #undef handle CharVec res = vec_init(); for (size_t i = 0; i < count; i++) { if (i == 0) { } else if (i < count - 1) { vec_push_array(&res, ", ", 2); } else if (and) { vec_push_array(&res, " and ", 5); } else { vec_push_array(&res, " or ", 4); } #define _case(x) \ case Attr_##x: \ vec_push_array(&res, #x, sizeof(#x) - 1); \ break switch (attributes[i]) { _case(versioned); default: vec_push_array(&res, "(invalid attribute)", 19); break; } #undef _case } vec_push(&res, '\0'); return res.data; } static inline EvalError err_duplicate_def(Span first, Span second, AstTag type, StringSlice ident) { return (EvalError){ .dup = {.tag = EETDuplicateDefinition, .first = first, .second = second, .type = type, .ident = ident} }; } static inline EvalError err_unknown(Span span, AstTag type, StringSlice ident) { return (EvalError){ .unk = {.tag = EETUnknown, .span = span, .type = type, .ident = ident} }; } static inline EvalError err_empty(Span span, AstTag type, StringSlice ident) { return (EvalError){ .empty = {.tag = EETEmptyType, .span = span, .type = type, .ident = ident} }; } void eval_error_report(Source *src, EvalError *err) { switch (err->tag) { case EETUnknown: { EvalErrorUnknown unk = err->unk; ReportSpan span = {.span = unk.span, .sev = ReportSeverityError}; const char *type = ""; switch (unk.type) { case ATConstant: type = "constant"; break; case ATAttribute: type = "attribute"; break; default: type = "identifier"; break; } char *help = NULL; if (unk.type == ATAttribute) { char *attributes = attributes_to_string(~0, false); help = msprintf("expected %s", attributes); free(attributes); } source_report( src, unk.span.loc, ReportSeverityError, &span, 1, help, "Unknown %s '%.*s'", type, unk.ident.len, unk.ident.ptr ); if (help != NULL) { free(help); } break; } case EETDuplicateDefinition: { EvalErrorDuplicateDefinition dup = err->dup; ReportSpan spans[] = { {.span = dup.first, .sev = ReportSeverityNote, .message = msprintf("first definition of '%.*s' here", dup.ident.len, dup.ident.ptr)}, {.span = dup.second, .sev = ReportSeverityError, .message = "redefined here"} }; const char *type = ""; switch (dup.type) { case ATConstant: type = "constant"; break; case ATIdent: type = "identifier"; break; case ATField: type = "field"; break; default: break; } source_report( src, dup.second.loc, ReportSeverityError, spans, 2, NULL, "Duplicate definition of %s '%.*s'", type, dup.ident.len, dup.ident.ptr ); free((char *)spans[0].message); break; } case EETCycle: { EvalErrorCycle cycle = err->cycle; const char *type = ""; if (cycle.type == ATConstant) { type = "constant"; } else if (cycle.type == ATTypeDecl) { type = "type declaration"; } // Check if the spans are ordered bool spans_ascending = true; bool spans_descending = true; for (size_t i = 1; i < cycle.spans.len; i++) { int comp = span_compare(&cycle.spans.data[i - 1], &cycle.spans.data[i]); spans_ascending = spans_ascending && comp >= 0; spans_descending = spans_descending && comp <= 0; if (!spans_ascending && !spans_descending) break; } bool ordered = spans_ascending | spans_descending; if (ordered) { // If they are, we can print the info on a span each (less noisy output) ReportSpanVec spans = vec_init(); vec_grow(&spans, cycle.spans.len); for (size_t i = 0; i < cycle.spans.len; i++) { ReportSeverity sev; char *message; StringSlice name = cycle.idents.data[i]; StringSlice next_name = cycle.idents.data[(i + 1) % cycle.idents.len]; if (cycle.spans.len == 1) { // Special case for a constant equal to itself sev = ReportSeverityError; message = msprintf("%.*s requires evaluating itself", name.len, name.ptr); } else if (i == 0) { // First equality sev = ReportSeverityError; message = msprintf("%.*s requires evaluating %.*s", name.len, name.ptr, next_name.len, next_name.ptr); } else if (i < cycle.spans.len - 1) { sev = ReportSeverityNote; message = msprintf("... which requires %.*s ...", next_name.len, next_name.ptr); } else { // Looparound sev = ReportSeverityNote; message = msprintf("... which again requires %.*s", next_name.len, next_name.ptr); } vec_push(&spans, ((ReportSpan){.span = cycle.spans.data[i], .sev = sev, .message = message})); } source_report( src, cycle.spans.data[0].loc, ReportSeverityError, spans.data, spans.len, NULL, "cycle detected when evaluating %s '%.*s'", type, cycle.idents.data[0].len, cycle.idents.data[0].ptr ); for (size_t i = 0; i < spans.len; i++) { free((char *)spans.data[i].message); } vec_drop(spans); } else { // If they aren't we have to use a report per span (because the lines are not ordered) ReportSpan span; span.span = cycle.spans.data[0]; span.sev = ReportSeverityError; if (cycle.spans.len >= 2) { span.message = NULL; } else { span.message = msprintf("%.*s requires evaluating itself", cycle.idents.data[0].len, cycle.idents.data[0].ptr); } source_report( src, cycle.spans.data[0].loc, ReportSeverityError, &span, 1, NULL, "cycle detected when evaluating %s '%.*s'", type, cycle.idents.data[0].len, cycle.idents.data[0].ptr ); if (span.message != NULL) { free((char *)span.message); } span.sev = ReportSeverityNote; span.message = NULL; for (size_t i = 1; i < cycle.idents.len; i++) { span.span = cycle.spans.data[i]; StringSlice name = cycle.idents.data[i]; if (i == cycle.idents.len - 1) { span.message = msprintf("which again requires evaluating %.*s", cycle.idents.data[0].len, cycle.idents.data[0].ptr); } source_report( src, span.span.loc, ReportSeverityNote, &span, 1, NULL, "... which requires evaluating %.*s ...", name.len, name.ptr ); } free((char *)span.message); } break; } case EETInfiniteStruct: { EvalErrorInfiniteStruct infs = err->infs; ReportSpanVec spans = vec_init(); CharVec structs = vec_init(); vec_grow(&spans, infs.fields.len + infs.structs.len); vec_push(&structs, '\''); SpannedStringSlice last_struct = infs.structs.data[infs.structs.len - 1]; vec_push_array(&structs, last_struct.slice.ptr, last_struct.span.len); vec_push(&structs, '\''); for (int i = infs.structs.len - 2; i >= 0; i--) { if (i == 1) { vec_push_array(&structs, " and ", 5); } else { vec_push_array(&structs, ", ", 2); } SpannedStringSlice s = infs.structs.data[i]; vec_push(&structs, '\''); vec_push_array(&structs, s.slice.ptr, s.slice.len); vec_push(&structs, '\''); } vec_push(&structs, '\0'); for (int i = infs.structs.len - 1; i >= 0; i--) { ReportSpan span[] = { {.sev = ReportSeverityError, .message = NULL, .span = infs.structs.data[i].span}, {.sev = ReportSeverityNote, .message = "recursive without limit", .span = infs.fields.data[i].span } }; vec_push_array(&spans, span, 2); } source_report( src, infs.structs.data[0].span.loc, ReportSeverityError, spans.data, spans.len, "insert some limiting indirection ('[]', '&[]', or '&[^max size]') to break the cycle", "recursive struct%s %s ha%s infinite size", infs.structs.len > 1 ? "s" : "", structs.data, infs.structs.len > 1 ? "ve" : "s" ); vec_drop(spans); vec_drop(structs); break; } case EETEmptyType: { EvalErrorEmptyType empty = err->empty; char *type = ""; if (empty.type == ATStruct) { type = "struct"; } else if (empty.type == ATMessage) { type = "message"; } ReportSpan span = {.span = empty.span, .sev = ReportSeverityError, .message = "zero sized types aren't allowed"}; source_report( src, empty.span.loc, ReportSeverityError, &span, 1, NULL, "%s '%.*s' doesn't have any field", type, empty.ident.len, empty.ident.ptr ); break; } } fprintf(stderr, "\n"); } void eval_error_drop(EvalError err) { switch (err.tag) { case EETCycle: vec_drop(err.cycle.idents); vec_drop(err.cycle.spans); break; case EETInfiniteStruct: vec_drop(err.infs.structs); vec_drop(err.infs.fields); default: break; } } static inline StringSlice string_slice_from_token(Token t) { return (StringSlice){.ptr = t.lexeme, .len = t.span.len}; } static SpannedStringSlice sss_from_token(Token t) { return (SpannedStringSlice){.slice.ptr = t.lexeme, .slice.len = t.span.len, .span = t.span}; } typedef struct { Hashmap *constants; Hashmap *typedefs; Hashmap *layouts; Hashmap *unresolved; Hashmap *names; PointerVec type_objects; AstItemVec *items; EvalErrorVec errors; MessagesObjectVec messages; } EvaluationContext; typedef struct { StringSlice constant; Token value; Span name_span; Span span; } UnresolvedConstant; typedef struct { StringSlice type; AstNode value; Span name_span; Span span; } UnresolvedTypeDef; typedef struct { TypeObject *type; StringSlice name; } TypeName; impl_hashmap_delegate(unconst, UnresolvedConstant, string_slice, constant); impl_hashmap_delegate(const, Constant, string_slice, name); impl_hashmap_delegate(untypd, UnresolvedTypeDef, string_slice, type); impl_hashmap_delegate(typedef, TypeDef, string_slice, name); impl_hashmap( layout, Layout, { return hash(state, (byte *)&v->type, sizeof(TypeObject *)); }, { return a->type == b->type; } ); impl_hashmap( typename, TypeName, { return hash(state, (byte *)&v->type, sizeof(TypeObject *)); }, { return a->type == b->type; } ); static uint64_t get_ast_number_value(EvaluationContext *ctx, AstNumber number) { if (number.token.type == Number) { return number.token.lit; } else { // The token is an Ident StringSlice ident = string_slice_from_token(number.token); Constant *c = hashmap_get(ctx->constants, &(Constant){.name = ident}); if (c != NULL) { // If the constant is invalid we make up a value to continue checking for errors // (Since it is invalid there already has been at least one and we know this code // can't go to the next stage) return c->valid ? c->value : 0; } else { // This constant doesn't exist: raise an error and return dummy value to continue vec_push(&ctx->errors, err_unknown(number.token.span, ATConstant, ident)); return 0; } } } static Sizing ast_size_to_sizing(EvaluationContext *ctx, AstSize size, uint64_t *value) { if (size.tag == ATMaxSize) { *value = get_ast_number_value(ctx, size.value); return SizingMax; } else if (size.tag == ATFixedSize) { *value = get_ast_number_value(ctx, size.value); return SizingFixed; } else { *value = UINT16_MAX; return SizingMax; } } static void _type_print(Hashmap *type_set, TypeObject *type) { if (type == NULL) { fprintf(stderr, "(invalid)"); return; } if (hashmap_set(type_set, &type)) { if (type->kind == TypeStruct) { fprintf(stderr, "%.*s", type->type.struct_.name.len, type->type.struct_.name.ptr); } else { fprintf(stderr, "(recursion)"); } return; }; if (type->kind == TypePrimitif) { #define _case(t) \ case Primitif_##t: \ fprintf(stderr, #t); \ break switch (type->type.primitif) { _case(u8); _case(u16); _case(u32); _case(u64); _case(i8); _case(i16); _case(i32); _case(i64); _case(f32); _case(f64); _case(char); _case(bool); } #undef _case } else if (type->kind == TypeArray) { _type_print(type_set, (TypeObject *)type->type.array.type); if (type->type.array.heap) fprintf(stderr, "&"); if (type->type.array.sizing == SizingFixed) fprintf(stderr, "[%lu]", type->type.array.size); else if (type->type.array.sizing == SizingMax) fprintf(stderr, "[^%lu]", type->type.array.size); else fprintf(stderr, "[]"); } else { StructObject s = *(StructObject *)&type->type.struct_; fprintf(stderr, "{ "); for (size_t i = 0; i < s.fields.len; i++) { fprintf(stderr, "%.*s: ", s.fields.data[i].name.len, s.fields.data[i].name.ptr); _type_print(type_set, s.fields.data[i].type); if (i < s.fields.len - 1) { fprintf(stderr, ", "); } } fprintf(stderr, " }"); } } __attribute__((unused)) static void type_print(TypeObject *type) { Hashmap *type_set = hashmap_init(pointer_hash, pointer_equal, NULL, sizeof(TypeObject *)); _type_print(type_set, type); hashmap_drop(type_set); } static TypeObject *resolve_type(EvaluationContext *ctx, SpannedStringSlice name); static TypeObject *ast_type_to_type_obj(EvaluationContext *ctx, AstType type) { if (type.tag == ATHeapArray || type.tag == ATFieldArray) { TypeObject *res = malloc(sizeof(TypeObject)); assert_alloc(res); vec_push(&ctx->type_objects, res); res->kind = TypeArray; res->type.array.heap = type.tag == ATHeapArray; res->type.array.sizing = ast_size_to_sizing(ctx, type.array.size, &res->type.array.size); res->type.array.type = (struct TypeObject *)ast_type_to_type_obj(ctx, *(AstType *)type.array.type); res->align.value = 0; return res; } else { // Otherwise the type is an identifier return resolve_type(ctx, sss_from_token(type.ident.token)); } } static TypeObject *resolve_type(EvaluationContext *ctx, SpannedStringSlice name) { TypeDef *type_def = hashmap_get(ctx->typedefs, &(TypeDef){.name = name.slice}); if (type_def != NULL) { // Type is already resolved return type_def->value; } // Type isn't defined anywhere if (ctx->unresolved == NULL || !hashmap_has(ctx->unresolved, &(UnresolvedTypeDef){.type = name.slice})) { vec_push(&ctx->errors, err_unknown(name.span, ATIdent, name.slice)); return NULL; } UnresolvedTypeDef *untd = hashmap_get(ctx->unresolved, &(UnresolvedTypeDef){.type = name.slice}); if (untd->value.tag == ATIdent || untd->value.tag == ATFieldArray || untd->value.tag == ATHeapArray) { hashmap_set(ctx->typedefs, &(TypeDef){.name = name.slice, .value = NULL}); TypeObject *value = ast_type_to_type_obj(ctx, *(AstType *)&untd->value); hashmap_set(ctx->typedefs, &(TypeDef){.name = name.slice, .value = value}); return value; } else { // Otherwise the value is a struct AstStruct str = untd->value.struct_; TypeObject *value = malloc(sizeof(TypeObject)); { FieldVec fields = vec_init(); vec_grow(&fields, str.fields.len); assert_alloc(value); vec_push(&ctx->type_objects, value); value->kind = TypeStruct; value->type.struct_.fields = *(AnyVec *)&fields; value->type.struct_.name = name.slice; value->type.struct_.has_funcs = false; value->align.value = 0; hashmap_set(ctx->typedefs, &(TypeDef){.name = name.slice, .value = value}); } StructObject *stro = (StructObject *)&value->type.struct_; for (size_t i = 0; i < str.fields.len; i++) { Field f; f.name = string_slice_from_token(str.fields.data[i].name); f.name_span = str.fields.data[i].name.span; f.type = ast_type_to_type_obj(ctx, str.fields.data[i].type); vec_push(&stro->fields, f); } return value; } } // Check struct object for direct recursion, returns true if the struct contains a reference to rec somewhere static bool check_for_recursion( EvaluationContext *ctx, EvalErrorInfiniteStruct *err, Hashmap *checked, Hashmap *invalids, TypeObject *rec, StructObject *str ) { // Shortcircuit if we already checked this struct // This also avoids running into recursion // (In the case of invalids there already has been an error, so we don't produce another) if (hashmap_set(checked, &str) || hashmap_has(invalids, &str)) { return false; } for (size_t i = 0; i < str->fields.len; i++) { Field f = str->fields.data[i]; TypeObject *type = f.type; if (type == NULL) continue; // Non heap arrays work very much the same as regular fields, Fixed size array as well (with added indirection) while (type->kind == TypeArray && (!type->type.array.heap || type->type.array.sizing == SizingFixed)) { type = type->type.array.type; } // Anything else won't recurse: primitives can't, and heap arrays add indirection // (Field arrays have been eliminated above) if (type->kind != TypeStruct) { continue; } // If we got here the type is a struct StructObject *obj = (StructObject *)&type->type.struct_; if (type == rec || check_for_recursion(ctx, err, checked, invalids, rec, obj)) { // The struct contains rec UnresolvedTypeDef *unr = hashmap_get(ctx->unresolved, &(UnresolvedTypeDef){.type = str->name}); SpannedStringSlice struct_ = {.slice = unr->type, .span = unr->name_span}; AstField af = unr->value.struct_.fields.data[i]; // af can be either ATFieldArray or ATIdent while (af.type.tag == ATFieldArray || af.type.tag == ATHeapArray) { af.type = *(AstType *)af.type.array.type; } SpannedStringSlice field = sss_from_token(af.type.ident.token); vec_push(&err->structs, struct_); vec_push(&err->fields, field); hashmap_set(invalids, &str); return true; } } return false; } static Alignment resolve_alignment(TypeObject *type, Hashmap *seen) { // Check if the type has already been resolved if (type->align.value != 0) { return type->align; } // Avoid cycles: if we already have seen this type (but not resolved), no need to check it again // (since we're computing the max) if (hashmap_set(seen, &type)) { return ALIGN_1; } if (type->kind == TypeStruct) { Alignment res = ALIGN_1; StructObject *s = (StructObject *)&type->type.struct_; for (size_t i = 0; i < s->fields.len; i++) { res = max_alignment(res, resolve_alignment(s->fields.data[i].type, seen)); } return res; } // Type is type array (since primitive already have an alignment) debug_assert(type->kind == TypeArray, ""); if (type->type.array.sizing == SizingMax) { uint64_t size = type->type.array.size; Alignment res; if (size <= UINT8_MAX) { res = ALIGN_1; } else if (size <= UINT16_MAX) { res = ALIGN_2; } else if (size <= UINT32_MAX) { res = ALIGN_4; } else { res = ALIGN_8; } res = max_alignment(res, resolve_alignment(type->type.array.type, seen)); return res; } // Type is fixed size array return resolve_alignment(type->type.array.type, seen); } void field_accessor_drop(FieldAccessor fa) { vec_drop(fa.indices); } FieldAccessor field_accessor_clone(FieldAccessor *fa) { return (FieldAccessor){.type = fa->type, .size = fa->size, .indices = vec_clone(&fa->indices)}; } void layout_drop(void *l) { vec_drop(((Layout *)l)->fields); } static void add_fields(FieldAccessorVec *v, TypeObject *t, const uint64_t *base, size_t len) { if (t->kind == TypePrimitif) { FieldAccessor fa = {.indices = vec_init()}; #define _case(typ, n) \ case Primitif_##typ: \ fa.size = n; \ fa.type = (TypeObject *)&PRIMITIF_##typ; \ break; switch (t->type.primitif) { _case(bool, 1); _case(char, 1); _case(i8, 1); _case(u8, 1); _case(i16, 2); _case(u16, 2); _case(i32, 4); _case(u32, 4); _case(f32, 4); _case(i64, 8); _case(u64, 8); _case(f64, 8); } #undef _case vec_push_array(&fa.indices, base, len); vec_push(v, fa); } else if (t->kind == TypeStruct) { StructObject *s = (StructObject *)&t->type.struct_; UInt64Vec new_base = vec_init(); vec_grow(&new_base, len + 1); vec_push_array(&new_base, base, len); vec_push(&new_base, 0); for (size_t i = 0; i < s->fields.len; i++) { new_base.data[len] = i; add_fields(v, s->fields.data[i].type, new_base.data, new_base.len); } vec_drop(new_base); } else { // Type is array if (t->type.array.sizing == SizingMax) { FieldAccessor fa = {.indices = vec_init()}; FieldAccessor fl = {.indices = vec_init()}; vec_grow(&fa.indices, len + 1); vec_grow(&fl.indices, len + 1); vec_push_array(&fa.indices, base, len); vec_push_array(&fl.indices, base, len); vec_push(&fa.indices, 1); vec_push(&fl.indices, 0); fa.size = 0; fa.type = t->type.array.type; uint64_t size = t->type.array.size; if (size <= UINT8_MAX) { fl.size = 1; fl.type = (TypeObject *)&PRIMITIF_u8; } else if (size <= UINT16_MAX) { fl.size = 2; fl.type = (TypeObject *)&PRIMITIF_u16; } else if (size <= UINT32_MAX) { fl.size = 4; fl.type = (TypeObject *)&PRIMITIF_u32; } else { fl.size = 8; fl.type = (TypeObject *)&PRIMITIF_u64; } vec_push(v, fa); vec_push(v, fl); } else { UInt64Vec new_base = vec_init(); vec_grow(&new_base, len + 1); vec_push_array(&new_base, base, len); vec_push(&new_base, 0); for (size_t i = 0; i < t->type.array.size; i++) { new_base.data[len] = i; add_fields(v, t->type.array.type, new_base.data, new_base.len); } vec_drop(new_base); } } } static int fa_compare(const void *a, const void *b) { const FieldAccessor *fa = (const FieldAccessor *)a; const FieldAccessor *fb = (const FieldAccessor *)b; if (fb->size != 0 && fa->size == 0) return 1; if (fa->size != 0 && fb->size == 0) return -1; return (int)fb->type->align.value - (int)fa->type->align.value; } Layout type_layout(TypeObject *type) { Layout l = {.type = type, .fields = vec_init()}; add_fields(&l.fields, type, NULL, 0); qsort(l.fields.data, l.fields.len, sizeof(FieldAccessor), fa_compare); return l; } static void resolve_types(EvaluationContext *ctx) { AstItemVec *items = ctx->items; Hashmap *untypds = hashmap_init(untypd_hash, untypd_equal, NULL, sizeof(UnresolvedTypeDef)); ctx->unresolved = untypds; // Get the unresolved type definitions in the map and report duplicates for (int i = 0; i < items->len; i++) { if (items->data[i].tag != ATStruct && items->data[i].tag != ATTypeDecl) { continue; } UnresolvedTypeDef td; if (items->data[i].tag == ATTypeDecl) { AstTypeDecl t = items->data[i].type_decl; td.type = string_slice_from_token(t.name); td.span = t.span; td.name_span = t.name.span; td.value.type = t.value; } else { AstStruct s = items->data[i].struct_; td.type = string_slice_from_token(s.ident); td.span = s.span; td.name_span = s.ident.span; td.value.struct_ = s; if (s.fields.len == 0) { vec_push(&ctx->errors, err_empty(s.ident.span, ATStruct, td.type)); } } UnresolvedTypeDef *original = hashmap_get(untypds, &td); if (original != NULL) { vec_push(&ctx->errors, err_duplicate_def(original->name_span, td.name_span, ATIdent, original->type)); vec_take(items, i); i--; // Update value to last definition hashmap_set(untypds, &td); } else { hashmap_set(untypds, &td); } } // Check for type declarations cycles / and resolve type declarations (give them a value) for (int i = 0; i < items->len; i++) { if (items->data[i].tag != ATTypeDecl) { continue; } hashmap_clear(ctx->names); AstTypeDecl td = items->data[i].type_decl; StringSlice name = string_slice_from_token(td.name); hashmap_set(ctx->names, &name); bool valid = true; AstType value = td.value; SpanVec spans = vec_init(); StringSliceVec idents = vec_init(); vec_push(&spans, td.span); vec_push(&idents, name); while (true) { // Skip indirections while (value.tag == ATFieldArray || value.tag == ATHeapArray) { value = *(AstType *)value.array.type; } // Value is now an AstIdent. SpannedStringSlice next = sss_from_token(value.ident.token); if (hashmap_set(ctx->names, &next.slice)) { // We evaluate to a type we've already visited: cycle size_t index; // Loop over idents (members of the cycle), set them as invalid and find the index // of the first member of the cycle, the members before aren't actually part of it: // A = B, B = C, C = B, A isn't part of the cycle (B <-> C) and shouldn't be reported // (but is invalid) for (size_t i = 0; i < idents.len; i++) { if (string_slice_equal(&idents.data[i], &next.slice)) { index = i; } hashmap_set(ctx->typedefs, &(TypeDef){.name = idents.data[i], .value = NULL}); } vec_splice(&spans, 0, index); vec_splice(&idents, 0, index); EvalErrorCycle cycle; cycle.tag = EETCycle; cycle.type = ATTypeDecl; cycle.spans = spans; cycle.idents = idents; // reinitialize the vectors to be dropped at the end. // vec_init doesn't do any allocation so this is free spans = (SpanVec)vec_init(); idents = (StringSliceVec)vec_init(); EvalError err = {.cycle = cycle}; vec_push(&ctx->errors, err); break; } TypeDef *resolved = hashmap_get(ctx->typedefs, &(TypeDef){.name = next.slice}); if (resolved != NULL) { // The type declaration evaluates to a resolved type (a primitif type, or an invalid type) if (resolved->value == NULL) { // the type it evaluates to is invalid, so it is too valid = false; break; } // The type it evaluates to is valid: the type declaration doesn't contain any cycle break; } UnresolvedTypeDef *unr = hashmap_get(untypds, &(UnresolvedTypeDef){.type = next.slice}); if (unr == NULL) { // The type evaluates to an unknown identifier // Report error and set as invalid vec_push(&ctx->errors, err_unknown(next.span, ATIdent, next.slice)); valid = false; break; } if (unr->value.tag == ATStruct) { // The type declarations evaluates to an (unresolved) struct: it can't cycle break; } else { // The type declarations evaluates to another type declarations: we continue checking vec_push(&spans, unr->span); vec_push(&idents, next.slice); value = unr->value.type; } } vec_drop(spans); vec_drop(idents); if (!valid) { // Set invalid hashmap_set(ctx->typedefs, &(TypeDef){.name = name, .value = NULL}); } } hashmap_clear(ctx->names); // Resolves types (this accepts recursive types) for (int i = 0; i < items->len; i++) { if (items->data[i].tag != ATStruct && items->data[i].tag != ATTypeDecl) { continue; } SpannedStringSlice name; if (items->data[i].tag == ATStruct) { name = sss_from_token(items->data[i].struct_.ident); } else { name = sss_from_token(items->data[i].type_decl.name); } resolve_type(ctx, name); } Hashmap *checked = hashmap_init(pointer_hash, pointer_equal, NULL, sizeof(StructObject *)); Hashmap *invalids = hashmap_init(pointer_hash, pointer_equal, NULL, sizeof(StructObject *)); // Check for recursive types without indirections (infinite size) for (int i = 0; i < items->len; i++) { // TypeDecl can't be recursive if (items->data[i].tag != ATStruct) { continue; } TypeDef *td = hashmap_get(ctx->typedefs, &(TypeDef){.name = string_slice_from_token(items->data[i].struct_.ident)}); TypeObject *start = td->value; StructObject *str = (StructObject *)&start->type.struct_; EvalErrorInfiniteStruct err = {.tag = EETInfiniteStruct, .fields = vec_init(), .structs = vec_init()}; if (check_for_recursion(ctx, &err, checked, invalids, start, str)) { EvalError e = {.infs = err}; vec_push(&ctx->errors, e); }; hashmap_clear(checked); } // Check structs for duplicate fields Hashmap *names = hashmap_init(sss_hash, sss_equal, NULL, sizeof(SpannedStringSlice)); for (int i = 0; i < items->len; i++) { if (items->data[i].tag != ATStruct) { continue; } TypeDef *td = hashmap_get(ctx->typedefs, &(TypeDef){.name = string_slice_from_token(items->data[i].struct_.ident)}); StructObject *str = (StructObject *)&td->value->type.struct_; for (size_t i = 0; i < str->fields.len; i++) { Field f = str->fields.data[i]; SpannedStringSlice *prev = hashmap_get(names, &(SpannedStringSlice){.slice = f.name}); if (prev != NULL) { vec_push(&ctx->errors, err_duplicate_def(prev->span, f.name_span, ATField, f.name)); continue; } hashmap_set(names, &(SpannedStringSlice){.slice = f.name, .span = f.name_span}); } hashmap_clear(names); } hashmap_drop(names); hashmap_drop(checked); hashmap_drop(invalids); hashmap_drop(untypds); ctx->unresolved = NULL; } static void resolve_constants(EvaluationContext *ctx) { AstItemVec *items = ctx->items; Hashmap *unconsts = hashmap_init(unconst_hash, unconst_equal, NULL, sizeof(UnresolvedConstant)); Hashmap *names = ctx->names; Hashmap *constants = ctx->constants; // Load unresolved constants into map (and check for duplicates) for (int i = 0; i < items->len; i++) { if (items->data[i].tag != ATConstant) { continue; } AstConstant c = items->data[i].constant; UnresolvedConstant constant = {.constant = string_slice_from_token(c.name), .name_span = c.name.span, .span = c.span, .value = c.value.token}; UnresolvedConstant *original = hashmap_get(unconsts, &constant); if (original != NULL) { vec_push(&ctx->errors, err_duplicate_def(original->name_span, constant.name_span, ATConstant, original->constant)); vec_take(items, i); i--; // Update value to last hashmap_set(unconsts, &constant); } else { hashmap_set(unconsts, &constant); } } for (size_t i = 0; i < items->len; i++) { if (items->data[i].tag != ATConstant) { continue; } UnresolvedConstant *unc = hashmap_get(unconsts, &(UnresolvedConstant){.constant = string_slice_from_token(items->data[i].constant.name)}); hashmap_clear(names); hashmap_set(names, &unc->constant); Token value = unc->value; while (value.type == Ident) { StringSlice ident = string_slice_from_token(value); Constant *resolved = hashmap_get(constants, &(Constant){.name = ident}); // If the constant is set to another that is already resolved if (resolved != NULL) { if (!resolved->valid) { // If the constant is invalid, break here, we know we won't be resolving this break; } // We expect a token out of this loop, but we don't have one here, so we make one up that works // only value.lit and value.type are read value.type = Number; value.lit = resolved->value; break; } if (hashmap_has(names, &ident)) { // Cycle detected on ident EvalErrorCycle cycle; cycle.tag = EETCycle; cycle.type = ATConstant; cycle.spans = (SpanVec)vec_init(); cycle.idents = (StringSliceVec)vec_init(); // Walk the cycle again, keeping track of the spans, and marking every member // as invalid UnresolvedConstant *start = hashmap_get(unconsts, &(UnresolvedConstant){.constant = ident}); UnresolvedConstant *cur = start; do { vec_push(&cycle.spans, cur->span); vec_push(&cycle.idents, cur->constant); hashmap_set(constants, &(Constant){.name = cur->constant, .value = 0, .valid = false}); cur = hashmap_get(unconsts, &(UnresolvedConstant){.constant = string_slice_from_token(cur->value)}); } while (cur != start); EvalError err = {.cycle = cycle}; vec_push(&ctx->errors, err); break; } // Get the constant the current is set to UnresolvedConstant *c = hashmap_get(unconsts, &(UnresolvedConstant){.constant = ident}); if (c == NULL) { // Constant doesn't exist // throw error and mark invalid vec_push(&ctx->errors, err_unknown(unc->value.span, ATConstant, ident)); break; } hashmap_set(names, &ident); value = c->value; } if (value.type == Ident) { // Constant couldn't be resolved hashmap_set(constants, &(Constant){.name = unc->constant, .value = 0, .valid = false}); } else { hashmap_set(constants, &(Constant){.name = unc->constant, .value = value.lit, .valid = true}); } } hashmap_drop(unconsts); hashmap_clear(names); } static void resolve_messages(EvaluationContext *ctx) { AstItemVec *items = ctx->items; Hashmap *names = hashmap_init(sss_hash, sss_equal, NULL, sizeof(SpannedStringSlice)); Hashmap *message_names = hashmap_init(sss_hash, sss_equal, NULL, sizeof(SpannedStringSlice)); Hashmap *field_names = hashmap_init(sss_hash, sss_equal, NULL, sizeof(SpannedStringSlice)); ctx->messages = (MessagesObjectVec)vec_init(); uint64_t version = ~0; for (size_t i = 0; i < items->len; i++) { if (items->data[i].tag == ATVersion) { AstVersion v = items->data[i].version; version = get_ast_number_value(ctx, v.version); continue; } if (items->data[i].tag != ATMessages) { continue; } AstMessages m = items->data[i].messages; SpannedStringSlice name = sss_from_token(m.name); Attributes attrs = AttrNone; MessagesObject res; res.name = name.slice; res.messages = (MessageObjectVec)vec_init(); res.version = version; SpannedStringSlice *prev_name = hashmap_get(names, &name); if (prev_name != NULL) { vec_push(&ctx->errors, err_duplicate_def(prev_name->span, name.span, ATIdent, name.slice)); } else { hashmap_set(names, &name); } for (size_t j = 0; j < m.children.len; j++) { if (m.children.data[j].tag == ATAttribute) { AstAttribute attr = m.children.data[j].attribute; const char *a = attr.ident.lexeme; uint32_t len = attr.ident.span.len; #define _case(x) \ if (strncmp(#x, a, sizeof(#x) - 1 > len ? sizeof(#x) - 1 : len) == 0) { \ attrs |= Attr_##x; \ continue; \ } _case(versioned); // If we get to here none of the above matched vec_push(&ctx->errors, err_unknown(attr.ident.span, ATAttribute, string_slice_from_token(attr.ident))); #undef _case } else { AstMessage msg = m.children.data[j].message; SpannedStringSlice name = sss_from_token(msg.ident); SpannedStringSlice *prev_name = hashmap_get(message_names, &name); if (prev_name != NULL) { vec_push(&ctx->errors, err_duplicate_def(prev_name->span, name.span, ATIdent, name.slice)); } else { hashmap_set(message_names, &name); } MessageObject message; message.name = name.slice; message.attributes = attrs; message.fields = (FieldVec)vec_init(); vec_grow(&message.fields, msg.fields.len); for (size_t k = 0; k < msg.fields.len; k++) { Field f; f.name = string_slice_from_token(msg.fields.data[k].name); f.name_span = msg.fields.data[k].name.span; f.type = ast_type_to_type_obj(ctx, msg.fields.data[k].type); vec_push(&message.fields, f); SpannedStringSlice *prev = hashmap_get(field_names, &(SpannedStringSlice){.slice = f.name}); if (prev != NULL) { vec_push(&ctx->errors, err_duplicate_def(prev->span, f.name_span, ATField, f.name)); continue; } hashmap_set(field_names, &(SpannedStringSlice){.slice = f.name, .span = f.name_span}); } hashmap_clear(field_names); vec_push(&res.messages, message); // Reset attributes after a message attrs = AttrNone; } } hashmap_clear(message_names); vec_push(&ctx->messages, res); version = ~0; } hashmap_drop(names); hashmap_drop(message_names); hashmap_drop(field_names); } void resolve_additional_type_info(EvaluationContext *ctx) { // Resolve alignment of all living type objects Hashmap *seen = hashmap_init(pointer_hash, pointer_equal, NULL, sizeof(TypeObject *)); for (size_t i = 0; i < ctx->type_objects.len; i++) { ((TypeObject *)ctx->type_objects.data[i])->align = resolve_alignment(ctx->type_objects.data[i], seen); hashmap_clear(seen); } // Compute type layouts Hashmap *layouts = hashmap_init(layout_hash, layout_equal, layout_drop, sizeof(Layout)); for (size_t i = 0; i < ctx->type_objects.len; i++) { Layout l = type_layout(ctx->type_objects.data[i]); hashmap_set(layouts, &l); } #define _case(x) \ { \ Layout l = type_layout((TypeObject *)&PRIMITIF_##x); \ hashmap_set(layouts, &l); \ } _case(u8); _case(u16); _case(u32); _case(u64); _case(i8); _case(i16); _case(i32); _case(i64); _case(char); _case(bool); #undef _case ctx->layouts = layouts; hashmap_drop(seen); } void program_drop(Program p) { for (size_t i = 0; i < p.type_objects.len; i++) { TypeObject *ptr = p.type_objects.data[i]; if (ptr->kind == TypeStruct) { StructObject *str = (StructObject *)&ptr->type.struct_; vec_drop(str->fields); } free(ptr); } vec_drop(p.type_objects); hashmap_drop(p.typedefs); hashmap_drop(p.layouts); vec_drop(p.messages); } // Resolve statics of an AST (constants and type declarations); EvaluationResult resolve_statics(AstContext *ctx) { EvaluationContext ectx; // resolved constants: value is a number, and the constant may be invalid ectx.constants = hashmap_init(const_hash, const_equal, NULL, sizeof(Constant)); ectx.typedefs = hashmap_init(typedef_hash, typedef_equal, NULL, sizeof(TypeDef)); // Set of names used to check for cycles ectx.names = hashmap_init(string_slice_hash, string_slice_equal, NULL, sizeof(StringSlice)); ectx.unresolved = NULL; ectx.items = &ctx->root->items.items; ectx.errors = (EvalErrorVec)vec_init(); ectx.type_objects = (PointerVec)vec_init(); { #define add_prim(type_name, type_size) \ do { \ hashmap_set( \ ectx.typedefs, \ &(TypeDef){.name.ptr = #type_name, .name.len = sizeof(#type_name) - 1, .value = (TypeObject *)&PRIMITIF_##type_name} \ ); \ } while (0) add_prim(u8, 1); add_prim(u16, 2); add_prim(u32, 4); add_prim(u64, 8); add_prim(i8, 1); add_prim(i16, 2); add_prim(i32, 4); add_prim(i64, 8); add_prim(f32, 4); add_prim(f64, 8); add_prim(char, 1); add_prim(bool, 1); #undef add_prim } resolve_constants(&ectx); resolve_types(&ectx); resolve_messages(&ectx); resolve_additional_type_info(&ectx); hashmap_drop(ectx.names); hashmap_drop(ectx.constants); Program p; p.typedefs = ectx.typedefs; p.layouts = ectx.layouts; p.type_objects = ectx.type_objects; p.messages = ectx.messages; return (EvaluationResult){.program = p, .errors = ectx.errors}; }