diff options
| -rw-r--r-- | boot/catboot.c | 25 | ||||
| -rw-r--r-- | boot/common.c | 316 | ||||
| -rw-r--r-- | boot/ir.c | 44 | ||||
| -rw-r--r-- | boot/lex.c | 93 | ||||
| -rw-r--r-- | boot/lower.c | 378 | ||||
| -rw-r--r-- | boot/parse.c | 79 | ||||
| -rw-r--r-- | boot/tests/lower/shadowing_error.cskt | 4 | ||||
| -rw-r--r-- | boot/tests/lower/type_cycle_error.cskt | 7 | ||||
| -rw-r--r-- | boot/visit/ir.c | 81 |
9 files changed, 713 insertions, 314 deletions
diff --git a/boot/catboot.c b/boot/catboot.c index 3919a2a..e340d48 100644 --- a/boot/catboot.c +++ b/boot/catboot.c @@ -130,12 +130,11 @@ debug_lower_pass(struct Source_File source_file) } struct Unit unit = { 0 }; - lower_tree(&tree, source_file, &unit); + lower_tree(&tree, &unit); printer(&unit); - // diagnostics are part of the printed unit, so the caller can read - // them from the dump. always returning success keeps snapshot tests - // viable for both the green and the diagnostic paths. + // always succeed. errors live in the printed unit and snapshot tests + // pick them up from there. return 0; } @@ -156,11 +155,12 @@ debug_transpile_pass(struct Source_File source_file) } struct Unit unit = { 0 }; - lower_tree(&tree, source_file, &unit); - if (unit.had_error) { - log_error("lowering finished with errors\n"); - return 1; + lower_tree(&tree, &unit); + FOR_EACH_ARRAY (struct Lower_Error, err, &unit.lower_errors) { + struct Diagnostic d = lower_error_to_diagnostic(err, &unit, source_file); + diagnostic_display(source_file, &d, stderr); } + if (unit.had_error) return 1; struct Transpile_Output output = transpile_output_from_file(stdout); @@ -294,11 +294,12 @@ default_command(struct Command_Arguments* arguments) } struct Unit unit = { 0 }; - lower_tree(&tree, source_file, &unit); - if (unit.had_error) { - log_error("lowering finished with errors\n"); - return COMMAND_FAIL; + lower_tree(&tree, &unit); + FOR_EACH_ARRAY (struct Lower_Error, err, &unit.lower_errors) { + struct Diagnostic d = lower_error_to_diagnostic(err, &unit, source_file); + diagnostic_display(source_file, &d, stderr); } + if (unit.had_error) return COMMAND_FAIL; struct Transpiler transpiler; transpiler_new(&transpiler, transpile_output_from_string()); diff --git a/boot/common.c b/boot/common.c index 0524b3b..85b1422 100644 --- a/boot/common.c +++ b/boot/common.c @@ -938,6 +938,54 @@ string_buffer_append(struct String_Buffer* buffer, struct String s) buffer->data[buffer->length] = '\0'; } +// pairs with `%S` in the printf-like formatters below. +#define STR(s) (int)(s).length, (s).data + +void +_format_translate_format(ascii* dst, const ascii* src, uint dst_size) +{ + uint si = 0, di = 0; + while (di + 4 < dst_size) { + ascii c = src[si]; + if (c == '\0') break; + if (c == '%' && src[si + 1] == 'S') { + dst[di++] = '%'; + dst[di++] = '.'; + dst[di++] = '*'; + dst[di++] = 's'; + si += 2; + } else { + dst[di++] = c; + si++; + } + } + dst[di] = '\0'; +} + +#define MAX_FORMATTED_STRING_SIZE 1024 + +struct String +string_vformat(const ascii* format, va_list args) +{ + ascii translated[MAX_FORMAT_STRING_SIZE]; + _format_translate_format(translated, format, sizeof translated); + + ascii buffer[MAX_FORMATTED_STRING_SIZE]; + int written = vsnprintf(buffer, sizeof buffer, translated, args); + check(written >= 0, "vsnprintf failed"); + return string_new(buffer, (uint)written); +} + +struct String +string_format(const ascii* format, ...) +{ + va_list args; + va_start(args, format); + struct String result = string_vformat(format, args); + va_end(args); + return result; +} + // appends a C-style string to the string buffer. void string_buffer_append_c_str(struct String_Buffer* buffer, const ascii* c_str) @@ -954,6 +1002,17 @@ string_buffer_append_c_str(struct String_Buffer* buffer, const ascii* c_str) buffer->data[buffer->length] = '\0'; } +// appends a formatted string to the buffer. +void +string_buffer_appendf(struct String_Buffer* buffer, const ascii* format, ...) +{ + va_list args; + va_start(args, format); + struct String formatted = string_vformat(format, args); + va_end(args); + string_buffer_append(buffer, formatted); +} + // converts the string buffer to an immutable string. // allocates a new string in the string region. struct String @@ -977,6 +1036,263 @@ struct Source_File struct String path; }; +// byte position within a file. +typedef uint64 Pos; + +// an extent of text within a file, +// defined by its start and end byte positions. +struct Span +{ + Pos start; + Pos end; +}; + +struct Span +span_new(Pos start, Pos end) +{ + return (struct Span){ .start = start, .end = end }; +} + +struct Span +span_empty(void) +{ + return (struct Span){ 0, 0 }; +} + +struct Span +span_width(Pos start, uint width) +{ + return (struct Span){ .start = start, .end = start + width }; +} + +// create a span which encompasses two spans. +struct Span +span_merge(struct Span a, struct Span b) +{ + return (struct Span){ + .start = a.start < b.start ? a.start : b.start, + .end = a.end > b.end ? a.end : b.end, + }; +} + +// expand a span by a number of bytes. +// negative number expands the span to the left, +// positive number expands the span to the right. +struct Span +span_expand(struct Span span, integer by) +{ + uint start_expand = by < 0 ? -by : 0; + uint end_expand = by > 0 ? by : 0; + return (struct Span){ + .start = span.start - start_expand, + .end = span.end + end_expand, + }; +} + +// check if two spans are equal. +bool +span_equals(struct Span a, struct Span b) +{ + return a.start == b.start && a.end == b.end; +} + +// check if span equals = { 0, 0 }. +bool +span_is_empty(struct Span span) +{ + return span_equals(span, (struct Span){ 0, 0 }); +} + +uint +span_length(struct Span span) +{ + uint length = span.end - span.start; + return length == 0 ? 1 : length; +} + +// a cursor position placed within a text file. +struct Cursor +{ + uint line; + uint column; +}; + +struct Cursor +cursor_new(uint line, uint column) +{ + return (struct Cursor){ .line = line, .column = column }; +} + +struct Cursor +cursor_empty(void) +{ + return cursor_new(0, 0); +} + +enum Diagnostic_Severity +{ + DIAGNOSTIC_NOTE, + DIAGNOSTIC_WARNING, + DIAGNOSTIC_ERROR, +}; + +struct Diagnostic +{ + enum Diagnostic_Severity severity; + struct Span span; + // first line of the rendered diagnostic. may contain `\n` to introduce + // extra body lines, each indented under the headline. + struct String message; + // optional. rendered as `hint: <hint>` under the body when non-empty. + struct String hint; + // NOTE(mel): probably overkill for bootstrapping, but i kind of want to figure + // the error format out for future iterations, and want to see it in action, + // to have more thoughts about improving it later on. +}; + +const ascii* +diagnostic_severity_label(enum Diagnostic_Severity severity) +{ + switch (severity) { + case DIAGNOSTIC_NOTE: + return "note."; + case DIAGNOSTIC_WARNING: + return "warning?"; + case DIAGNOSTIC_ERROR: + return "error!"; + default: + return "diagnostic"; + } +} + +const ascii* +diagnostic_severity_color(enum Diagnostic_Severity severity) +{ + switch (severity) { + case DIAGNOSTIC_NOTE: + return ANSI_BLUE; + case DIAGNOSTIC_WARNING: + return ANSI_YELLOW; + case DIAGNOSTIC_ERROR: + return ANSI_RED; + default: + return ANSI_WHITE; + } +} + +// line and column are 1-based. +void +source_position_from_span(struct String source, struct Span span, uint* out_line, uint* out_column) +{ + uint line = 1, column = 1; + uint upto = span.start; + if (upto > string_length(source)) upto = string_length(source); + for (uint i = 0; i < upto; ++i) { + if (string_at(source, i) == '\n') { + line++; + column = 1; + } else { + column++; + } + } + *out_line = line; + *out_column = column; +} + +struct Span +source_line_span(struct String source, struct Span span) +{ + Pos line_start = span.start, line_end = span.start; + while (line_start > 0 && string_at(source, line_start - 1) != '\n') line_start--; + while (line_end < string_length(source) && string_at(source, line_end) != '\n') line_end++; + return span_new(line_start, line_end); +} + +// render a diagnostic to a stream. +void +diagnostic_display(struct Source_File source, struct Diagnostic* d, FILE* out) +{ + uint line, column; + source_position_from_span(source.source, d->span, &line, &column); + + STRING_FORMAT_TO(source.path, out, ANSI_WHITE "%s:%lu:%lu:\n", line, column); + + // first line of the message is the headline. anything after a `\n` is body. + uint headline_end = 0; + while (headline_end < d->message.length && d->message.data[headline_end] != '\n') + headline_end++; + + fprintf(out, ANSI_BOLD "%s%s " ANSI_WHITE, diagnostic_severity_color(d->severity), + diagnostic_severity_label(d->severity)); + fprintf(out, "%.*s", (int)headline_end, d->message.data); + if (d->severity == DIAGNOSTIC_ERROR) fprintf(out, " :("); + fprintf(out, "\n" ANSI_NO_BOLD); + + if (headline_end < d->message.length) { + uint body_start = headline_end + 1; + fprintf(out, "%.*s\n", (int)(d->message.length - body_start), d->message.data + body_start); + } + + if (!string_is_empty(d->hint)) STRING_FORMAT_TO(d->hint, out, " hint: %S\n"); + + struct Span line_span = source_line_span(source.source, d->span); + struct String_View source_line = + string_substring(source.source, line_span.start, line_span.end); + + fprintf(out, ANSI_WHITE "%lu| ", line); + STRING_FORMAT_TO(source_line, out, "%S\n"); + + uint line_number_length = ceil(log10(line + 1)); + fprintf(out, "%s%*s", diagnostic_severity_color(d->severity), + (int)(column + 1 + line_number_length), " "); + uint width = span_length(d->span); + if (width == 0) width = 1; + for (uint w = 0; w < width; w++) fprintf(out, "^"); + fprintf(out, "\n" ANSI_RESET); +} + +void +diagnostic_push_str( + Array(struct Diagnostic) * into, bool* out_had_error, enum Diagnostic_Severity severity, + struct Span span, struct String message) +{ + struct Diagnostic d = { .severity = severity, .span = span, .message = message }; + array_push(into, &d); + if (out_had_error && severity == DIAGNOSTIC_ERROR) *out_had_error = true; +} + +void +diagnostic_push( + Array(struct Diagnostic) * into, bool* out_had_error, enum Diagnostic_Severity severity, + struct Span span, const ascii* format, ...) +{ + va_list args; + va_start(args, format); + struct String message = string_vformat(format, args); + va_end(args); + diagnostic_push_str(into, out_had_error, severity, span, message); +} + +#define diagnostic_emit(unit, severity, span, ...) \ + diagnostic_push(&(unit)->diagnostics, &(unit)->had_error, severity, span, __VA_ARGS__) + +#define diagnostic_emit_str(unit, severity, span, message) \ + diagnostic_push_str(&(unit)->diagnostics, &(unit)->had_error, severity, span, message) + +void +diagnostic_render( + struct Source_File source, FILE* out, enum Diagnostic_Severity severity, struct Span span, + const ascii* format, ...) +{ + va_list args; + va_start(args, format); + struct String message = string_vformat(format, args); + va_end(args); + + struct Diagnostic d = { .severity = severity, .span = span, .message = message }; + diagnostic_display(source, &d, out); +} + // single iteration of the CRC32 checksum algorithm // described in POSIX. // see: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/cksum.html diff --git a/boot/ir.c b/boot/ir.c index 4bdfc6e..6ea055e 100644 --- a/boot/ir.c +++ b/boot/ir.c @@ -444,19 +444,33 @@ struct Import struct Span span; }; -enum Diagnostic_Severity -{ - DIAGNOSTIC_NOTE, - DIAGNOSTIC_WARNING, - DIAGNOSTIC_ERROR, -}; - -// TODO: we might want to take this out to be used for other passes. -struct Diagnostic -{ - enum Diagnostic_Severity severity; +enum Lower_Error_Kind +{ + LOWER_ERROR_NONE, + LOWER_ERROR_UNDEFINED_TYPE, + LOWER_ERROR_DUPLICATE_TYPE, + LOWER_ERROR_DUPLICATE_FUNCTION, + LOWER_ERROR_NAME_SHADOWS, + LOWER_ERROR_TYPE_CYCLE, + LOWER_ERROR_ASSIGNMENT_AS_EXPRESSION, + LOWER_ERROR_RANGE_OUTSIDE_LOOP, + LOWER_ERROR_CONSTRUCT_SUBJECT_NOT_NAME, + LOWER_ERROR_TYPE_EXPRESSION_IN_BODY, + LOWER_ERROR_UNSUPPORTED_TOP_LEVEL, + LOWER_ERROR_UNKNOWN_LOOP_STYLE, + LOWER_ERROR_UNKNOWN_COMPOUND_ASSIGN, + LOWER_ERROR_UNIMPLEMENTED, +}; + +struct Lower_Error +{ + enum Lower_Error_Kind kind; struct Span span; - struct String message; + + // per-error details, meaning depends on each error kind! + struct String name; + struct String detail; + Array(Type_Id) cycle_chain; }; // a single translation unit. @@ -472,7 +486,7 @@ struct Unit Array(Type_Id) type_emission_order; bool had_error; - Array(struct Diagnostic) diagnostics; + Array(struct Lower_Error) lower_errors; }; REGION(struct Type, type) @@ -655,10 +669,6 @@ ir_make_construct(Type_Id type_id, Array(struct Construct_Field) fields, struct return expression_new(EXPRESSION_CONSTRUCT, v, span); } -// type-ref constructors. -// `bare` is the no-modifier form; `pointer` wraps one `&` modifier; `with_mods` -// takes a fully-formed mods array (caller owns it). - struct Type_Ref type_ref_bare(Type_Id type_id) { diff --git a/boot/lex.c b/boot/lex.c index 594905f..18f8690 100644 --- a/boot/lex.c +++ b/boot/lex.c @@ -13,99 +13,6 @@ #define MAX_CHAR_BUFFER_SIZE 256 -// byte position within a file. -typedef uint64 Pos; - -// an extent of text within a file, -// defined by its start and end byte positions. -struct Span -{ - Pos start; - Pos end; -}; - -struct Span -span_new(Pos start, Pos end) -{ - return (struct Span){ .start = start, .end = end }; -} - -struct Span -span_empty(void) -{ - return (struct Span){ 0, 0 }; -} - -struct Span -span_width(Pos start, uint width) -{ - return (struct Span){ .start = start, .end = start + width }; -} - -// create a span which encompasses two spans. -struct Span -span_merge(struct Span a, struct Span b) -{ - return (struct Span){ - .start = a.start < b.start ? a.start : b.start, - .end = a.end > b.end ? a.end : b.end, - }; -} - -// expand a span by a number of bytes. -// negative number expands the span to the left, -// positive number expands the span to the right. -struct Span -span_expand(struct Span span, integer by) -{ - uint start_expand = by < 0 ? -by : 0; - uint end_expand = by > 0 ? by : 0; - return (struct Span){ - .start = span.start - start_expand, - .end = span.end + end_expand, - }; -} - -// check if two spans are equal. -bool -span_equals(struct Span a, struct Span b) -{ - return a.start == b.start && a.end == b.end; -} - -// check if span equals = { 0, 0 }. -bool -span_is_empty(struct Span span) -{ - return span_equals(span, (struct Span){ 0, 0 }); -} - -uint -span_length(struct Span span) -{ - uint length = span.end - span.start; - return length == 0 ? 1 : length; -} - -// a cursor position placed within a text file. -struct Cursor -{ - uint line; - uint column; -}; - -struct Cursor -cursor_new(uint line, uint column) -{ - return (struct Cursor){ .line = line, .column = column }; -} - -struct Cursor -cursor_empty(void) -{ - return cursor_new(0, 0); -} - // what kind of token is it? enum Token_Kind { diff --git a/boot/lower.c b/boot/lower.c index 01762a5..65eb18c 100644 --- a/boot/lower.c +++ b/boot/lower.c @@ -73,10 +73,6 @@ struct Lower_Context // the translation unit being created struct Unit* unit; - // text source of the unit in translation - // used for attaching diagnostics to correct lines - struct Source_File source; - // monotonic counter of synthesized types uint synthetic_type_counter; @@ -89,21 +85,89 @@ struct Lower_Context }; void -lower_emit_error(struct Unit* unit, struct Span span, struct String message) +lower_push_error(struct Unit* unit, struct Lower_Error err) { - struct Diagnostic d = { - .severity = DIAGNOSTIC_ERROR, - .span = span, - .message = message, - }; - array_push(&unit->diagnostics, &d); + array_push(&unit->lower_errors, &err); unit->had_error = true; } +// render the cycle's chain to a nice human-y message. void -lower_emit_error_c(struct Unit* unit, struct Span span, const ascii* message) +lower_format_cycle_chain( + struct String_Buffer* buf, struct Unit* unit, struct Source_File source, Array(Type_Id) * chain) +{ + uint n = array_length(chain); + for (uint k = 0; k < n; ++k) { + Type_Id current_id = *array_at(Type_Id, chain, k); + Type_Id next_id = *array_at(Type_Id, chain, (k + 1) % n); + struct Type* current = *array_at(struct Type*, &unit->types.entries, current_id); + struct Type* next = *array_at(struct Type*, &unit->types.entries, next_id); + uint line, column; + source_position_from_span(source.source, current->span, &line, &column); + + if (k > 0) string_buffer_appendf(buf, "\n"); + string_buffer_appendf( + buf, " %S (line %lu) depends on %S", STR(current->name), line, STR(next->name)); + } +} + +// turn a structured lower error into a displayable diagnostic. +struct Diagnostic +lower_error_to_diagnostic(struct Lower_Error* err, struct Unit* unit, struct Source_File source) { - lower_emit_error(unit, span, string_from_c_string(message)); + struct Diagnostic d = { .severity = DIAGNOSTIC_ERROR, .span = err->span }; + switch (err->kind) { + case LOWER_ERROR_UNDEFINED_TYPE: + d.message = string_format("undefined type '%S'", STR(err->name)); + return d; + case LOWER_ERROR_DUPLICATE_TYPE: + d.message = string_format("duplicate type '%S'", STR(err->name)); + return d; + case LOWER_ERROR_DUPLICATE_FUNCTION: + d.message = string_format("duplicate function '%S'", STR(err->name)); + return d; + case LOWER_ERROR_NAME_SHADOWS: + d.message = string_format("name '%S' shadows an existing binding", STR(err->name)); + return d; + case LOWER_ERROR_TYPE_CYCLE: { + struct String_Buffer buf = string_buffer_new(512); + string_buffer_appendf(&buf, "type cycle detected\n"); + lower_format_cycle_chain(&buf, unit, source, &err->cycle_chain); + d.message = string_buffer_to_string(&buf); + d.hint = string_from_static_c_string( + "break the cycle with a reference (use `&T` instead of `T`)"); + return d; + } + case LOWER_ERROR_ASSIGNMENT_AS_EXPRESSION: + d.message = string_from_static_c_string("assignment cannot appear as an expression"); + return d; + case LOWER_ERROR_RANGE_OUTSIDE_LOOP: + d.message = + string_from_static_c_string("range expressions are only valid in loop initializers"); + return d; + case LOWER_ERROR_CONSTRUCT_SUBJECT_NOT_NAME: + d.message = string_from_static_c_string("construction subject must be a type name"); + return d; + case LOWER_ERROR_TYPE_EXPRESSION_IN_BODY: + d.message = + string_from_static_c_string("type expression not allowed inside a function body"); + return d; + case LOWER_ERROR_UNSUPPORTED_TOP_LEVEL: + d.message = string_from_static_c_string("unsupported top-level statement"); + return d; + case LOWER_ERROR_UNKNOWN_LOOP_STYLE: + d.message = string_from_static_c_string("unknown loop style"); + return d; + case LOWER_ERROR_UNKNOWN_COMPOUND_ASSIGN: + d.message = string_from_static_c_string("unknown compound assignment operator"); + return d; + case LOWER_ERROR_UNIMPLEMENTED: + d.message = string_format("unimplemented: %S", STR(err->detail)); + return d; + default: + d.message = string_from_static_c_string("unknown lower error"); + return d; + } } bool @@ -419,11 +483,13 @@ lower_intern_type_ref(struct Lower_Context* ctx, struct Tree_Type* tree_type) if (lower_type_lookup_by_name(ctx->unit, current->value.name.name, &id)) { ref.type_id = id; } else { - lower_emit_error( - ctx->unit, current->span, - string_concatenate( - ARG_ASCII, "undefined type '", ARG_STRING, current->value.name.name, ARG_ASCII, - "'", ARG_END)); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNDEFINED_TYPE, + .span = current->span, + .name = current->value.name.name, + }); } return ref; } @@ -438,10 +504,22 @@ lower_intern_type_ref(struct Lower_Context* ctx, struct Tree_Type* tree_type) return ref; } case TREE_TYPE_MAP: - lower_emit_error_c(ctx->unit, current->span, "unimplemented: map types"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNIMPLEMENTED, + .span = current->span, + .detail = string_from_static_c_string("map types"), + }); return ref; default: - lower_emit_error_c(ctx->unit, current->span, "unimplemented: this type form"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNIMPLEMENTED, + .span = current->span, + .detail = string_from_static_c_string("this type form"), + }); return ref; } } @@ -494,10 +572,10 @@ lower_register_function_shell(struct Unit* unit, struct String name, struct Span { Function_Id existing; if (lower_function_lookup_by_name(unit, name, &existing)) { - lower_emit_error( - unit, span, - string_concatenate( - ARG_ASCII, "duplicate function '", ARG_STRING, name, ARG_ASCII, "'", ARG_END)); + lower_push_error( + unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_DUPLICATE_FUNCTION, .span = span, .name = name }); return false; } @@ -517,10 +595,9 @@ lower_register_type_shell(struct Unit* unit, struct String name, struct Span spa { Type_Id existing; if (lower_type_lookup_by_name(unit, name, &existing)) { - lower_emit_error( - unit, span, - string_concatenate( - ARG_ASCII, "duplicate type '", ARG_STRING, name, ARG_ASCII, "'", ARG_END)); + lower_push_error( + unit, + (struct Lower_Error){ .kind = LOWER_ERROR_DUPLICATE_TYPE, .span = span, .name = name }); return false; } @@ -570,11 +647,13 @@ lower_fill_type_body(struct Lower_Context* ctx, struct Type* type, struct Tree_T // aliases always need their target's full definition. array_push(&type->depends_on, &target_id); } else { - lower_emit_error( - ctx->unit, tree_type->span, - string_concatenate( - ARG_ASCII, "undefined type '", ARG_STRING, tree_type->value.name.name, - ARG_ASCII, "'", ARG_END)); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNDEFINED_TYPE, + .span = tree_type->span, + .name = tree_type->value.name.name, + }); } break; } @@ -628,7 +707,13 @@ lower_fill_type_body(struct Lower_Context* ctx, struct Type* type, struct Tree_T break; } case TREE_TYPE_CLASS: - lower_emit_error_c(ctx->unit, tree_type->span, "unimplemented: class types"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNIMPLEMENTED, + .span = tree_type->span, + .detail = string_from_static_c_string("class types"), + }); break; default: { // this is a type alias assigning a name to a structural type. @@ -698,11 +783,19 @@ lower_pass_1_fill_bodies(struct Lower_Context* ctx, struct Tree* tree) } if (stmt->kind == TREE_STATEMENT_PRAGMA) { - lower_emit_error_c(ctx->unit, stmt->span, "unimplemented: top-level pragmas"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNIMPLEMENTED, + .span = stmt->span, + .detail = string_from_static_c_string("top-level pragmas"), + }); continue; } - lower_emit_error_c(ctx->unit, stmt->span, "unsupported top-level statement"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ .kind = LOWER_ERROR_UNSUPPORTED_TOP_LEVEL, .span = stmt->span }); } } @@ -778,12 +871,19 @@ lower_expression_binary_operation(struct Lower_Context* ctx, struct Tree_Express enum Binary_Operation op = tree_expr->value.binary_operator.operation; // TODO: maybe we want to support assignment expressions some day. today they aren't. if (op >= BINARY_ASSIGN) { - lower_emit_error_c(ctx->unit, tree_expr->span, "assignment cannot appear as an expression"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_ASSIGNMENT_AS_EXPRESSION, + .span = tree_expr->span, + }); return nil; } if (op == BINARY_RANGE) { - lower_emit_error_c( - ctx->unit, tree_expr->span, "range expressions are only valid in loop initializers"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_RANGE_OUTSIDE_LOOP, .span = tree_expr->span }); return nil; } return ir_make_binary( @@ -800,7 +900,13 @@ lower_expression_call(struct Lower_Context* ctx, struct Tree_Expression* tree_ex // table to reorder, we only handle positional arguments today. FOR_EACH_ARRAY (struct String, name, &group->argument_names) { if (name->length > 0) { - lower_emit_error_c(ctx->unit, tree_expr->span, "unimplemented: named call arguments"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNIMPLEMENTED, + .span = tree_expr->span, + .detail = string_from_static_c_string("named call arguments"), + }); return nil; } } @@ -819,16 +925,23 @@ lower_expression_construct(struct Lower_Context* ctx, struct Tree_Expression* tr { struct Tree_Expression* subject = tree_expr->value.construct.subject; if (!subject || subject->kind != TREE_EXPRESSION_NAME) { - lower_emit_error_c(ctx->unit, tree_expr->span, "construction subject must be a type name"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_CONSTRUCT_SUBJECT_NOT_NAME, + .span = tree_expr->span, + }); return nil; } Type_Id type_id; if (!lower_type_lookup_by_name(ctx->unit, subject->value.name.name, &type_id)) { - lower_emit_error( - ctx->unit, subject->span, - string_concatenate( - ARG_ASCII, "undefined type '", ARG_STRING, subject->value.name.name, ARG_ASCII, "'", - ARG_END)); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNDEFINED_TYPE, + .span = subject->span, + .name = subject->value.name.name, + }); return nil; } @@ -849,13 +962,15 @@ lower_expression_construct(struct Lower_Context* ctx, struct Tree_Expression* tr return ir_make_construct(type_id, fields, tree_expr->span); } -// type-as-expression is only valid as the right-side of a top-level binding; +// type-as-expression is only valid as the right-side of a top-level binding, // anywhere else it is an error. struct Expression* lower_expression_type(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) { - lower_emit_error_c( - ctx->unit, tree_expr->span, "type expression not allowed inside a function body"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_TYPE_EXPRESSION_IN_BODY, .span = tree_expr->span }); return nil; } @@ -918,7 +1033,13 @@ lower_expression(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) case TREE_EXPRESSION_MUST: // TODO: implement these default: - lower_emit_error_c(ctx->unit, tree_expr->span, "unimplemented: this expression kind"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNIMPLEMENTED, + .span = tree_expr->span, + .detail = string_from_static_c_string("this expression kind"), + }); return nil; } } @@ -997,17 +1118,25 @@ lower_statement_declaration(struct Lower_Context* ctx, struct Tree_Statement* tr // TODO: multi-name decls like `var a, b int = ...` need to split into // N sibling declarations or introduce a temporary for the initializer. if (array_length(&decl->names) != 1) { - lower_emit_error_c(ctx->unit, tree_stmt->span, "unimplemented: multi-name declarations"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNIMPLEMENTED, + .span = tree_stmt->span, + .detail = string_from_static_c_string("multi-name declarations"), + }); return nil; } struct String name = *array_at(struct String, &decl->names, 0); if (lower_is_shadowing(ctx, name)) { - lower_emit_error( - ctx->unit, tree_stmt->span, - string_concatenate( - ARG_ASCII, "name '", ARG_STRING, name, ARG_ASCII, "' shadows an existing binding", - ARG_END)); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_NAME_SHADOWS, + .span = tree_stmt->span, + .name = name, + }); return nil; } struct Type_Ref type = lower_intern_type_ref(ctx, decl->type); @@ -1039,16 +1168,26 @@ lower_statement_expression(struct Lower_Context* ctx, struct Tree_Statement* tre if (!lhs_tree || lhs_tree->kind != TREE_EXPRESSION_NAME) { // TODO: implement non-trivial lhs assignments like function // calls for example. not too common but sometimes necessary. - lower_emit_error_c( - ctx->unit, tree_stmt->span, - "unimplemented: compound assignment with non-trivial lvalue"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNIMPLEMENTED, + .span = tree_stmt->span, + .detail = string_from_static_c_string( + "compound assignment with " + "non-trivial lvalue"), + }); return nil; } enum Binary_Operation simple_op = binary_operation_strip_assign(op); if (simple_op == BINARY_NONE) { - lower_emit_error_c( - ctx->unit, tree_stmt->span, "unknown compound assignment operator"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNKNOWN_COMPOUND_ASSIGN, + .span = tree_stmt->span, + }); return nil; } @@ -1064,9 +1203,15 @@ lower_statement_expression(struct Lower_Context* ctx, struct Tree_Statement* tre struct Tree_Expression* subject = incdec->subject; if (!subject || subject->kind != TREE_EXPRESSION_NAME) { // TODO: handle this too, like compound assignments - lower_emit_error_c( - ctx->unit, tree_stmt->span, - "unimplemented: increment/decrement on non-trivial lvalue"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNIMPLEMENTED, + .span = tree_stmt->span, + .detail = string_from_static_c_string( + "increment/decrement on non-trivial " + "lvalue"), + }); return nil; } @@ -1140,9 +1285,15 @@ lower_statement_loop(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt if (loop->style == TREE_STATEMENT_LOOP_STYLE_FOR_EACH) { if (!decl->initializer || decl->initializer->kind != TREE_EXPRESSION_BINARY_OPERATION || decl->initializer->value.binary_operator.operation != BINARY_RANGE) { - lower_emit_error_c( - ctx->unit, tree_stmt->span, - "unimplemented: for-each over non-range collections"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNIMPLEMENTED, + .span = tree_stmt->span, + .detail = string_from_static_c_string( + "for-each over non-range " + "collections"), + }); return nil; } init = decl->initializer->value.binary_operator.left_operand; @@ -1152,8 +1303,13 @@ lower_statement_loop(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt } if (array_length(&decl->names) != 1) { - lower_emit_error_c( - ctx->unit, tree_stmt->span, "unimplemented: for-loop with multi-name declaration"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNIMPLEMENTED, + .span = tree_stmt->span, + .detail = string_from_static_c_string("for-loop with multi-name declaration"), + }); return nil; } struct String name = *array_at(struct String, &decl->names, 0); @@ -1163,11 +1319,13 @@ lower_statement_loop(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt lower_push_scope(ctx, SCOPE_TYPE_BLOCK); if (lower_is_shadowing(ctx, name)) { - lower_emit_error( - ctx->unit, tree_stmt->span, - string_concatenate( - ARG_ASCII, "name '", ARG_STRING, name, ARG_ASCII, - "' shadows an existing binding", ARG_END)); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_NAME_SHADOWS, + .span = tree_stmt->span, + .name = name, + }); lower_pop_scope(ctx, SCOPE_TYPE_BLOCK); return nil; } @@ -1226,7 +1384,10 @@ lower_statement_loop(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt return ir_make_block_statement(outer, tree_stmt->span); } default: - lower_emit_error_c(ctx->unit, tree_stmt->span, "unknown loop style"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNKNOWN_LOOP_STYLE, .span = tree_stmt->span }); return nil; } } @@ -1267,7 +1428,13 @@ lower_statement(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt) case TREE_STATEMENT_CONTINUE: return lower_statement_continue(ctx, tree_stmt); default: - lower_emit_error_c(ctx->unit, tree_stmt->span, "unimplemented: this statement kind"); + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNIMPLEMENTED, + .span = tree_stmt->span, + .detail = string_from_static_c_string("this statement kind"), + }); return nil; } } @@ -1317,50 +1484,32 @@ lower_pass_2(struct Lower_Context* ctx) uint lower_span_to_line(struct String source, struct Span span) { - uint line = 1; - uint upto = span.start; - if (upto > string_length(source)) upto = string_length(source); - for (uint i = 0; i < upto; ++i) { - if (string_at(source, i) == '\n') line++; - } + uint line, column; + source_position_from_span(source, span, &line, &column); return line; } -// builds a nice & friendly cycle detection message and emits it as a diagnostic. -// `chain` is the path we walked while looking for the cycle, and -// `cycle_start` is the chain index at which the cycle closes. +// `chain` is the walk we took while looking for the cycle, `cycle_start` is +// the index in it at which the cycle closes back on itself. void -lower_report_type_cycle( - struct Unit* unit, struct Source_File source, struct _Array* chain, uint cycle_start) +lower_report_type_cycle(struct Unit* unit, struct _Array* chain, uint cycle_start) { - struct String_Buffer buf = string_buffer_new(512); - string_buffer_append_c_str(&buf, "type cycle detected:\n"); - uint chain_len = array_length(chain); + Array(Type_Id) cycle_copy = array_new(Type_Id, chain_len - cycle_start); for (uint k = cycle_start; k < chain_len; ++k) { - Type_Id current_id = *array_at(Type_Id, chain, k); - Type_Id next_id; - if (k + 1 < chain_len) - next_id = *array_at(Type_Id, chain, k + 1); - else - next_id = *array_at(Type_Id, chain, cycle_start); - - struct Type* current = *array_at(struct Type*, &unit->types.entries, current_id); - struct Type* next = *array_at(struct Type*, &unit->types.entries, next_id); - uint line = lower_span_to_line(source.source, current->span); - - ascii line_buf[256]; - snprintf( - line_buf, sizeof line_buf, " %s (line %lu) depends on %s\n", - string_c_str(current->name), line, string_c_str(next->name)); - string_buffer_append_c_str(&buf, line_buf); + Type_Id id = *array_at(Type_Id, chain, k); + array_push(&cycle_copy, &id); } - string_buffer_append_c_str( - &buf, "hint: break the cycle with a reference (use `&T` instead of `T`). :)"); Type_Id origin_id = *array_at(Type_Id, chain, cycle_start); struct Type* origin = *array_at(struct Type*, &unit->types.entries, origin_id); - lower_emit_error(unit, origin->span, string_buffer_to_string(&buf)); + lower_push_error( + unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_TYPE_CYCLE, + .span = origin->span, + .cycle_chain = cycle_copy, + }); } // topologically sorting the unit's type dependency graph. @@ -1370,7 +1519,7 @@ lower_report_type_cycle( // are always first to emit when their vertex in-degree is 0. // cycles reported as diagnostic, partial emission order will still be completed. void -lower_topological_sort_dependency_graph(struct Unit* unit, struct Source_File source) +lower_topological_sort_dependency_graph(struct Unit* unit) { const uint done_sentinel = (uint)-1; // marks vertecies that have been processed @@ -1461,7 +1610,7 @@ lower_topological_sort_dependency_graph(struct Unit* unit, struct Source_File so } } if (found_at < chain_len) { - lower_report_type_cycle(unit, source, &chain, found_at); + lower_report_type_cycle(unit, &chain, found_at); return; } @@ -1471,7 +1620,7 @@ lower_topological_sort_dependency_graph(struct Unit* unit, struct Source_File so } void -lower_tree(struct Tree* tree, struct Source_File source, struct Unit* unit) +lower_tree(struct Tree* tree, struct Unit* unit) { unit->types.entries = array_new(struct Type*, 256); unit->types.by_hash = array_new(struct Type_Hash_To_Id, 256); @@ -1483,7 +1632,7 @@ lower_tree(struct Tree* tree, struct Source_File source, struct Unit* unit) unit->imports = array_new(struct Import, 32); unit->type_emission_order = array_new(Type_Id, 256); unit->had_error = false; - unit->diagnostics = array_new(struct Diagnostic, 64); + unit->lower_errors = array_new(struct Lower_Error, 64); unit->types.primitive_int_id = lower_seed_primitive(unit, "int"); unit->types.primitive_uint_id = lower_seed_primitive(unit, "uint"); @@ -1496,7 +1645,6 @@ lower_tree(struct Tree* tree, struct Source_File source, struct Unit* unit) struct Lower_Context ctx = { .unit = unit, - .source = source, .synthetic_type_counter = 0, .synthetic_counter = 0, .scope_stack = array_new(struct Scope, 32), @@ -1510,5 +1658,5 @@ lower_tree(struct Tree* tree, struct Source_File source, struct Unit* unit) lower_pass_1(&ctx, tree); lower_pass_2(&ctx); } - lower_topological_sort_dependency_graph(unit, source); + lower_topological_sort_dependency_graph(unit); } diff --git a/boot/parse.c b/boot/parse.c index db5e029..2b5d859 100644 --- a/boot/parse.c +++ b/boot/parse.c @@ -103,56 +103,22 @@ parser_error_is_none(const struct Parser_Error* error) return error->kind == PARSER_ERROR_NONE; } -struct Span -token_span_to_line_span(struct Span span, struct String source) -{ - Pos line_start = span.start, line_end = span.start; - - // go backwards from the start of the token's span to find line start. - while (line_start > 0 && string_at(source, line_start - 1) != '\n') line_start--; - // go forwards from the end of the token's span to find line end. - while (line_end < string_length(source) && string_at(source, line_end) != '\n') line_end++; - - return span_new(line_start, line_end); -} - -// print out nice, human-readable error message -// pointing to the location of the error in the source file. -// TODO: bring out the infrastructure for displaying errors in the same format -// outside of the parser, so that it can be used in other places. void parser_error_display(const struct Parser_Error* error, struct Source_File source_file) { if (parser_error_is_none(error)) return; - uint line = error->cause.location.line; - uint column = error->cause.location.column; - - struct Token cause = error->cause; - - STRING_FORMAT_TO(source_file.path, stderr, ANSI_WHITE "%s:%lu:%lu:\n", line, column); - fprintf(stderr, ANSI_BOLD ANSI_RED "error: " ANSI_WHITE); + struct String_View lexeme = token_lexeme(&error->cause, source_file.source); if (error->subkind != PARSER_ERROR_NONE) { - fprintf(stderr, "%s: %s", parser_error_kind_to_string(error->kind), - parser_error_kind_to_string(error->subkind)); + diagnostic_render( + source_file, stderr, DIAGNOSTIC_ERROR, error->cause.span, "%s: %s, got '%S'", + parser_error_kind_to_string(error->kind), parser_error_kind_to_string(error->subkind), + STR(lexeme)); } else { - fprintf(stderr, "%s", parser_error_kind_to_string(error->kind)); + diagnostic_render( + source_file, stderr, DIAGNOSTIC_ERROR, error->cause.span, "%s, got '%S'", + parser_error_kind_to_string(error->kind), STR(lexeme)); } - struct String_View cause_lexeme = token_lexeme(&cause, source_file.source); - STRING_FORMAT_TO(cause_lexeme, stderr, ", got '%S' :(\n"); - - struct Span line_span = token_span_to_line_span(cause.span, source_file.source); - - struct String_View source_line = - string_substring(source_file.source, line_span.start, line_span.end); - - fprintf(stderr, ANSI_WHITE ANSI_NO_BOLD "%lu| ", line); - STRING_FORMAT_TO(source_line, stderr, "%S\n"); - - uint line_number_length = ceil(log10(line + 1)); - fprintf(stderr, ANSI_RED "%*s", (int)(column + 1 + line_number_length), " "); - for (uint w = 0; w < span_length(cause.span); w++) { fprintf(stderr, "^"); } - fprintf(stderr, "\n" ANSI_RESET); } // a parser context descibes the current surrounding structure @@ -405,7 +371,8 @@ parser_block_node(struct Parser* p, struct Parser_Error* error) struct Tree_Statement* current = nil; while (!parser_probe(p, TOKEN_CURLY_CLOSE)) { - struct Tree_Statement* statement = CHECK_RETURN(parser_statement(p, error), struct Tree_Block); + struct Tree_Statement* statement = + CHECK_RETURN(parser_statement(p, error), struct Tree_Block); // statement ending token isn't required when the block ends on the same line, // as in e.g.: `if (true) { print("yes") }` @@ -956,7 +923,8 @@ parser_expression_primary_boolean(struct Parser* p, struct Parser_Error* error) "expected boolean literal"); bool literal = token.kind == TOKEN_WORD_TRUE; union Tree_Expression_Value expr_value = { .bool_literal = { literal } }; - return tree_expression_new(TREE_EXPRESSION_BOOLEAN_LITERAL, expr_value, token.span, token.location); + return tree_expression_new( + TREE_EXPRESSION_BOOLEAN_LITERAL, expr_value, token.span, token.location); } struct Tree_Expression* @@ -1008,7 +976,8 @@ parser_expression_type(struct Parser* p, struct Parser_Error* error) struct Span span = span_merge(start_token.span, type->span); return tree_expression_new( - TREE_EXPRESSION_TYPE, (union Tree_Expression_Value){ .type = { type } }, span, start_token.location); + TREE_EXPRESSION_TYPE, (union Tree_Expression_Value){ .type = { type } }, span, + start_token.location); } struct Tree_Expression* @@ -1198,8 +1167,8 @@ parser_expression_postfix(struct Parser* p, struct Parser_Error* error) struct Span span = span_merge(expression->span, token.span); union Tree_Expression_Value value = { .increment_decrement = inc_dec }; - expression = - tree_expression_new(TREE_EXPRESSION_INCREMENT_DECREMENT, value, span, token.location); + expression = tree_expression_new( + TREE_EXPRESSION_INCREMENT_DECREMENT, value, span, token.location); continue; } @@ -1234,7 +1203,8 @@ parser_expression_unary_operation(struct Parser* p, struct Parser_Error* error) struct Span span = span_merge(token.span, inc_dec.subject->span); union Tree_Expression_Value value = { .increment_decrement = inc_dec }; - return tree_expression_new(TREE_EXPRESSION_INCREMENT_DECREMENT, value, span, token.location); + return tree_expression_new( + TREE_EXPRESSION_INCREMENT_DECREMENT, value, span, token.location); } return parser_expression_postfix(p, error); @@ -1357,7 +1327,8 @@ parser_statement_conditional(struct Parser* p, struct Parser_Error* error) CONTEXT_END(in_statement_clause); struct Tree_Block then_block = CHECK(parser_block_node(p, error)); - conditional.conditions[conditional.condition_count++] = (struct Tree_Statement_Conditional_Branch){ + conditional + .conditions[conditional.condition_count++] = (struct Tree_Statement_Conditional_Branch){ .when = if_condition, .then = then_block, }; @@ -1397,8 +1368,8 @@ parser_statement_conditional(struct Parser* p, struct Parser_Error* error) } return tree_statement_new( - TREE_STATEMENT_CONDITIONAL, (union Tree_Statement_Value){ .conditional = conditional }, span, - if_token.location); + TREE_STATEMENT_CONDITIONAL, (union Tree_Statement_Value){ .conditional = conditional }, + span, if_token.location); } struct Tree_Statement* @@ -1475,7 +1446,8 @@ parser_statement_block(struct Parser* p, struct Parser_Error* error) { struct Tree_Block block = CHECK(parser_block_node(p, error)); return tree_statement_new( - TREE_STATEMENT_BLOCK, (union Tree_Statement_Value){ .block = { block } }, block.span, block.location); + TREE_STATEMENT_BLOCK, (union Tree_Statement_Value){ .block = { block } }, block.span, + block.location); } struct Tree_Statement* @@ -1496,7 +1468,8 @@ parser_statement_break(struct Parser* p, struct Parser_Error* error) { struct Token break_token = CHECK(parser_need(p, TOKEN_WORD_BREAK, error)); struct Span span = break_token.span; - return tree_statement_new(TREE_STATEMENT_BREAK, (union Tree_Statement_Value){ 0 }, span, break_token.location); + return tree_statement_new( + TREE_STATEMENT_BREAK, (union Tree_Statement_Value){ 0 }, span, break_token.location); } struct Tree_Statement* diff --git a/boot/tests/lower/shadowing_error.cskt b/boot/tests/lower/shadowing_error.cskt index b22dddf..34eacc4 100644 --- a/boot/tests/lower/shadowing_error.cskt +++ b/boot/tests/lower/shadowing_error.cskt @@ -25,5 +25,5 @@ main = fun () { (declaration x (ref int) (initializer (expr 1))) ))) (emission_order 0 1 2 3 4 5 6 7) - (diagnostics - (error "name 'x' shadows an existing binding"))) + (lower_errors + (name-shadows x))) \ No newline at end of file diff --git a/boot/tests/lower/type_cycle_error.cskt b/boot/tests/lower/type_cycle_error.cskt index 29b791e..c6bfb6c 100644 --- a/boot/tests/lower/type_cycle_error.cskt +++ b/boot/tests/lower/type_cycle_error.cskt @@ -22,8 +22,5 @@ B = type { a A } (type 8 A structure (field b (ref B)) (depends_on 9)) (type 9 B structure (field a (ref A)) (depends_on 8))) (emission_order 0 1 2 3 4 5 6 7) - (diagnostics - (error "type cycle detected: - A (line 1) depends on B - B (line 2) depends on A -hint: break the cycle with a reference (use `&T` instead of `T`). :)"))) + (lower_errors + (type-cycle (chain A B)))) \ No newline at end of file diff --git a/boot/visit/ir.c b/boot/visit/ir.c index 1920f3b..5317276 100644 --- a/boot/visit/ir.c +++ b/boot/visit/ir.c @@ -66,7 +66,7 @@ struct Visit_Table void (*visit_block)(struct Visit* visitor, struct Block* block); void (*visit_type_ref)(struct Visit* visitor, struct Type_Ref* ref); void (*visit_import)(struct Visit* visitor, struct Import* import); - void (*visit_diagnostic)(struct Visit* visitor, struct Diagnostic* diagnostic); + void (*visit_lower_error)(struct Visit* visitor, struct Lower_Error* error); }; void @@ -82,8 +82,7 @@ walk_unit(struct Visit* visit, struct Unit* unit) FOR_EACH_ARRAY (struct Function*, function, &unit->functions.entries) VISIT(visit_function, *function); FOR_EACH_ARRAY (struct Import, import, &unit->imports) VISIT(visit_import, import); - FOR_EACH_ARRAY (struct Diagnostic, diagnostic, &unit->diagnostics) - VISIT(visit_diagnostic, diagnostic); + FOR_EACH_ARRAY (struct Lower_Error, error, &unit->lower_errors) VISIT(visit_lower_error, error); } void @@ -362,7 +361,7 @@ walk_block(struct Visit* visit, struct Block* block) WALK_LEAF_FUNCTION(walk_type_ref, struct Type_Ref*); WALK_LEAF_FUNCTION(walk_import, struct Import*); -WALK_LEAF_FUNCTION(walk_diagnostic, struct Diagnostic*); +WALK_LEAF_FUNCTION(walk_lower_error, struct Lower_Error*); struct Visit_Table walk_functions = { .visit_unit = walk_unit, @@ -407,7 +406,7 @@ struct Visit_Table walk_functions = { .visit_block = walk_block, .visit_type_ref = walk_type_ref, .visit_import = walk_import, - .visit_diagnostic = walk_diagnostic, + .visit_lower_error = walk_lower_error, }; void @@ -515,15 +514,15 @@ printer_visit_unit(struct Visit* visit, struct Unit* unit) PRINT(")"); } - if (array_length(&unit->diagnostics) > 0) { + if (array_length(&unit->lower_errors) > 0) { PRINT("\n"); printer_indent(p); - PRINT("(diagnostics"); + PRINT("(lower_errors"); p->indentation_level++; - FOR_EACH_ARRAY (struct Diagnostic, diagnostic, &unit->diagnostics) { + FOR_EACH_ARRAY (struct Lower_Error, error, &unit->lower_errors) { PRINT("\n"); printer_indent(p); - VISIT(visit_diagnostic, diagnostic); + VISIT(visit_lower_error, error); } p->indentation_level--; PRINT(")"); @@ -947,15 +946,63 @@ printer_visit_import(struct Visit* visit, struct Import* import) } void -printer_visit_diagnostic(struct Visit* visit, struct Diagnostic* diagnostic) +printer_visit_lower_error(struct Visit* visit, struct Lower_Error* error) { PRINTER_PREAMBLE - const ascii* sev = "note"; - if (diagnostic->severity == DIAGNOSTIC_WARNING) - sev = "warning"; - else if (diagnostic->severity == DIAGNOSTIC_ERROR) - sev = "error"; - PRINT("(%s \"%s\")", sev, string_c_str(diagnostic->message)); + + switch (error->kind) { + case LOWER_ERROR_UNDEFINED_TYPE: + PRINT("(undefined-type %s)", string_c_str(error->name)); + return; + case LOWER_ERROR_DUPLICATE_TYPE: + PRINT("(duplicate-type %s)", string_c_str(error->name)); + return; + case LOWER_ERROR_DUPLICATE_FUNCTION: + PRINT("(duplicate-function %s)", string_c_str(error->name)); + return; + case LOWER_ERROR_NAME_SHADOWS: + PRINT("(name-shadows %s)", string_c_str(error->name)); + return; + case LOWER_ERROR_TYPE_CYCLE: { + PRINT("(type-cycle (chain"); + FOR_EACH_ARRAY (Type_Id, id, &error->cycle_chain) { + struct Type* type = printer_type_at(p, *id); + if (type) + PRINT(" %s", string_c_str(type->name)); + else + PRINT(" #%lu", *id); + } + PRINT("))"); + return; + } + case LOWER_ERROR_ASSIGNMENT_AS_EXPRESSION: + PRINT("(assignment-as-expression)"); + return; + case LOWER_ERROR_RANGE_OUTSIDE_LOOP: + PRINT("(range-outside-loop)"); + return; + case LOWER_ERROR_CONSTRUCT_SUBJECT_NOT_NAME: + PRINT("(construct-subject-not-name)"); + return; + case LOWER_ERROR_TYPE_EXPRESSION_IN_BODY: + PRINT("(type-expression-in-body)"); + return; + case LOWER_ERROR_UNSUPPORTED_TOP_LEVEL: + PRINT("(unsupported-top-level)"); + return; + case LOWER_ERROR_UNKNOWN_LOOP_STYLE: + PRINT("(unknown-loop-style)"); + return; + case LOWER_ERROR_UNKNOWN_COMPOUND_ASSIGN: + PRINT("(unknown-compound-assign)"); + return; + case LOWER_ERROR_UNIMPLEMENTED: + PRINT("(unimplemented \"%s\")", string_c_str(error->detail)); + return; + default: + PRINT("(unknown-error)"); + return; + } } struct Visit_Table printer_visit_functions = { @@ -1001,7 +1048,7 @@ struct Visit_Table printer_visit_functions = { .visit_block = printer_visit_block, .visit_type_ref = printer_visit_type_ref, .visit_import = printer_visit_import, - .visit_diagnostic = printer_visit_diagnostic, + .visit_lower_error = printer_visit_lower_error, }; void |
