import "@std/mem" import "@std/arraylist" import "@std/enummap" SourceId :: distinct usize #! borrows a source path and text owned by the compilation or caller. Source :: struct { path []u8 text []u8 } Span :: struct { start u32 end u32 source SourceId = SourceId(0) } DiagnosticId :: distinct u32 no_diagnostic :: maxval!(DiagnosticId) Code :: enum { invalid_character float_must_end_with_digit unterminated_string unexpected_token duplicate_name unknown_name expected_type type_not_value invalid_void_type type_mismatch invalid_operand integer_division not_callable argument_count_mismatch cannot_infer_type missing_return return_type_mismatch yield_outside_block missing_yield yield_type_mismatch condition_not_bool invalid_assignment_target break_outside_loop continue_outside_loop invalid_range_bound invalid_for_iterable range_value_not_supported range_pointer_capture range_index_capture invalid_array_length array_length_mismatch invalid_array_element_type array_size_overflow invalid_index_type not_indexable invalid_index_assignment index_out_of_bounds unknown_field duplicate_field missing_field tuple_arity_mismatch not_record recursive_record invalid_record_field_type missing_entry invalid_entry literal_out_of_range inexact_integer_literal invalid_escape read_before_initialization write_before_initialization initialization_cycle integer_overflow division_by_zero import_cycle invalid_import_path package_unavailable namespace_not_value not_namespace invalid_import_alias invalid_enum_backing invalid_enum_value enum_context_required invalid_distinct_backing invalid_union_tag union_initializer_count invalid_match_subject invalid_match_pattern duplicate_match_pattern non_exhaustive_match unreachable_match_arm invalid_match_capture invalid_inline_match_subject invalid_inline_match_capture inactive_union_field invalid_pointer_type invalid_address_operand invalid_dereference invalid_pointer_assignment invalid_pointer_access null_unwrap invalid_slice_bounds sentinel_mismatch overlapping_memory_copy unsupported_foreign_call invalid_fallible_type invalid_sum_type invalid_fallible_operand try_outside_fallible incompatible_propagated_error catch_type_mismatch errdefer_outside_fallible cleanup_control_flow comptime_argument_required cannot_infer_generic specialization_cycle specialization_limit comptime_execution_limit comptime_value_escape inline_loop_control invalid_generated_field_name invalid_struct_layout invalid_c_record_field_type missing_metadata_import invalid_metadata_abi invalid_undefined invalid_shift_count unreachable_executed compile_error invalid_compile_error } Diagnostic :: struct { code Code span Span custom_message ?[]u8 = null } #! owns diagnostic storage and retains its allocator; release with deinit. Store :: struct { list arraylist.ArrayList(Diagnostic) } CapacityError :: enum { diagnostic_limit_reached, storage_limit_reached } #! retains `allocator` for added diagnostics; the caller owns the returned Store. init proc(allocator mem.Allocator) Store { return Store{ list = arraylist.init(allocator) } } #! releases diagnostic storage with the retained allocator. deinit proc(store @mut Store) void { for (store.list.items) |diagnostic| { if (diagnostic.custom_message) |text| mem.free(store.list.allocator, text) } arraylist.deinit(&store.list) } #! allocates from the retained allocator; the returned ID is valid until deinit. add proc(store @mut Store, code Code, span Span) DiagnosticId ! (mem.AllocError | CapacityError) { if (store.list.items.len >= usize(no_diagnostic)) return .diagnostic_limit_reached id :: DiagnosticId(store.list.items.len) try arraylist.append(&store.list, Diagnostic{ code = code, span = span }) return id } #! copies `text`; the store owns the message until deinit. add_message proc(store @mut Store, code Code, span Span, text []u8) DiagnosticId ! (mem.AllocError | CapacityError) { owned :: try mem.alloc(u8, store.list.allocator, text.len) errdefer mem.free(store.list.allocator, owned) memcopy!(owned, text) id :: try add(store, code, span) store.list.items[usize(id)].custom_message = owned return id } #! borrows either the store-owned custom message or process-lifetime code text. message proc(diagnostic Diagnostic) []u8 { if (diagnostic.custom_message) |text| return text return enummap.get(Code, []u8, &messages, diagnostic.code)? } get proc(store @Store, id DiagnosticId) ?Diagnostic { if (usize(id) >= store.list.items.len) return null return store.list.items[usize(id)] } count proc(store @Store) usize { return store.list.items.len } #! constructed at compile time so missing entries fail compilation; message bytes live for the process lifetime. messages :: $enummap.init(Code, []u8, { invalid_character = "invalid character", float_must_end_with_digit = "float literal must end with a digit", unterminated_string = "unterminated string literal", unexpected_token = "unexpected token", duplicate_name = "duplicate name or forbidden shadowing", unknown_name = "unknown name", expected_type = "annotation must name a type", type_not_value = "type name cannot be used as a value", invalid_void_type = "void cannot be stored or used as a parameter type", type_mismatch = "incompatible value type", invalid_operand = "operator requires compatible operand types", integer_division = "division requires float operands", invalid_shift_count = "shift count must be smaller than the integer bit width", not_callable = "value is not callable", argument_count_mismatch = "argument count does not match the function signature", cannot_infer_type = "cannot infer a type without a type anchor", missing_return = "nonvoid function can finish without returning a value", unreachable_executed = "reached unreachable statement", return_type_mismatch = "return does not match the function result type", yield_outside_block = "yield requires an enclosing value block", missing_yield = "nonvoid value block can finish without yielding a value", yield_type_mismatch = "yield does not match the value block result type", condition_not_bool = "condition must have type bool", invalid_assignment_target = "assignment requires a mutable local binding", break_outside_loop = "break requires an enclosing loop", continue_outside_loop = "continue requires an enclosing loop", invalid_range_bound = "range bounds must have compatible integer types", invalid_for_iterable = "for-loop iteration requires an array, slice, string, single pointer to an array, direct integer range, or inline tuple", range_value_not_supported = "first-class range values are not implemented", range_pointer_capture = "range loops do not support pointer captures", range_index_capture = "range loops do not support index captures", invalid_array_length = "array length must be a nonnegative supported compile-time integer", array_length_mismatch = "array literal length does not match its type", invalid_array_element_type = "array element annotations require a supported concrete value type", array_size_overflow = "array storage size exceeds the supported address space", invalid_index_type = "array index must have type usize", not_indexable = "indexing is currently implemented for fixed arrays only", invalid_index_assignment = "indexed assignment requires a writable array element", index_out_of_bounds = "index out of bounds", unknown_field = "record has no such field", duplicate_field = "record field is specified more than once", missing_field = "record initializer is missing a required field", tuple_arity_mismatch = "tuple initializer arity does not match its type", not_record = "field selection requires a record or tuple", recursive_record = "record has a recursive by-value layout", invalid_record_field_type = "record fields require a concrete value type", missing_entry = "program requires a main function", invalid_entry = "main must be a function with no parameters", literal_out_of_range = "numeric literal is outside its runtime type's range", inexact_integer_literal = "integer literal is not exactly representable as float; use a floating literal to allow rounding", invalid_escape = "invalid string escape", read_before_initialization = "local value was read before initialization", write_before_initialization = "local value was assigned before initialization", initialization_cycle = "global initialization cycle", integer_overflow = "integer arithmetic overflow", division_by_zero = "integer division by zero", import_cycle = "package import cycle", invalid_import_path = "invalid import path", package_unavailable = "cannot load package directory", namespace_not_value = "package namespace cannot be used as a value", not_namespace = "member selection requires a package namespace", invalid_import_alias = "import requires an explicit valid identifier alias", invalid_enum_backing = "enum backing must be a concrete integer type", invalid_enum_value = "enum values require explicit backing and increasing representable constant integers", enum_context_required = "enum literal requires an enum or tagged union context", invalid_distinct_backing = "distinct requires a concrete runtime backing type", invalid_union_tag = "union tag must be an enum containing every union variant", union_initializer_count = "union construction requires exactly one member", invalid_match_subject = "match subject must be an enum, tagged union, or scalar", invalid_match_pattern = "invalid match pattern", duplicate_match_pattern = "duplicate or overlapping match pattern", non_exhaustive_match = "match is not exhaustive", unreachable_match_arm = "match arm is unreachable", invalid_match_capture = "payload capture requires compatible nonvoid tagged union variants", invalid_inline_match_subject = "inline match arm requires an enum or tagged union", invalid_inline_match_capture = "inline match arm requires one enum capture or one payload capture and an optional tag", inactive_union_field = "tagged union field is not active", invalid_pointer_type = "pointer requires a supported concrete pointee type", invalid_address_operand = "address-of requires addressable storage", invalid_dereference = "dereference requires a pointer", invalid_pointer_assignment = "pointer assignment requires a mutable pointee", invalid_pointer_access = "pointer does not refer to live accessible storage", null_unwrap = "cannot unwrap a null optional", invalid_slice_bounds = "slice bounds are invalid", sentinel_mismatch = "slice sentinel does not match the expected value", overlapping_memory_copy = "memory copy source and destination overlap", unsupported_foreign_call = "foreign function calls are unsupported by the interpreter", invalid_fallible_type = "fallible type requires a concrete success type and an enum, struct, or tagged union error channel", invalid_sum_type = "sum composition requires compatible named enum, struct, or tagged union variants", invalid_fallible_operand = "try and catch require a fallible operand", try_outside_fallible = "try requires an enclosing fallible function", incompatible_propagated_error = "propagated errors are not a subset of the function error channel", catch_type_mismatch = "catch handler must produce the success type or terminate", errdefer_outside_fallible = "errdefer requires an enclosing fallible function", cleanup_control_flow = "cleanup cannot transfer control out of itself", comptime_argument_required = "generic argument must be a compile-time value", cannot_infer_generic = "cannot infer consistent compile-time arguments from runtime arguments", specialization_cycle = "compile-time specialization depends on its unfinished result", specialization_limit = "compile-time specialization exceeds its recursion or instance limit", comptime_execution_limit = "compile-time execution exceeds its recursion or step limit", comptime_value_escape = "compile-time result cannot escape evaluation as a source value", inline_loop_control = "break or continue targeting an inline loop must be compile-time-resolvable", invalid_generated_field_name = "generated field name must be an ASCII identifier", invalid_struct_layout = "generated record layout must be .auto or .c; C-layout records require at least one field", invalid_c_record_field_type = "record field is not a supported C-layout value type", missing_metadata_import = "typeinfo! requires importing @std/meta", invalid_metadata_abi = "@std/meta declarations do not match the compiler reflection ABI", invalid_undefined = "undefined requires a concrete mutable local declaration", compile_error = "explicit compile-time error", invalid_compile_error = "compile_error! requires one compile-time string argument", })