Define diagnostics and the safety boundary
Archived MVP specification. For ASPL 0.5.0, see the current reference and native HTTP guide.
Type: grilling Status: resolved Blocked by: 04, 05, 06, 09, 11 Parent: Find the ASPL MVP specification
Question
What source locations, error categories, Arabic-default and English-selected wording behavior, recovery rules, runtime traps, and unsafe-operation markers must ASPL specify so invalid source and failed C operations produce predictable outcomes?
Answer
ASPL keeps source diagnostics, policy rejections, load failures, runtime traps, configured-limit stops, cancellation, host failures, and internal failures distinct. Every reported problem has a stable machine identity before a host renders it. Arabic is the default diagnostic language; a host may explicitly select English without changing any reason, argument, span, edit, ordering, or outcome.
The compiler recovers only within a source stage and never publishes a later representation after an error. The VM reports only the closed trap catalog below as ASPL traps. Unsafe markers acknowledge foreign risk at both source use sites but do not turn arbitrary C behavior into checked behavior.
Outcome model
The public structured result distinguishes these classes:
| Class | Meaning |
|---|---|
source-diagnostic |
A source finding with error or warning severity. An error blocks the current compilation. |
policy-rejection |
Valid ASPL source or bytecode cannot run under the selected host capability policy. |
load-failure |
Serialized bytecode, a native envelope, or a required foreign import cannot be loaded. |
trap |
Defined ASPL semantics stopped a running program immediately and uncatchably. |
limit-stop |
A configured compile, load, or execution ceiling stopped otherwise valid work. |
cancelled |
The host requested cooperative cancellation. |
host-failure |
Allocation or another host service failed independently of ASPL source semantics. |
internal-failure |
A compiler, packager, or VM invariant failed. |
A normal halt with an i64 status from 0 through 255 is not a diagnostic. Failure class and reason are separate fields. Reasons use lowercase ASCII kebab case and remain fixed throughout the MVP conformance contract. Rendered output shows the reason by default. Unstable host text may add detail but never changes identity or test expectations.
Source diagnostic records
An ASPL diagnostic contains:
- a stable reason;
errororwarningseverity;- one primary half-open byte span in the original input buffer;
- zero or more ordered, labeled secondary spans;
- reason-specific typed arguments;
- zero or one non-overlapping machine-applicable source edit; and
- independent
errors-truncatedandwarnings-truncatedresult flags.
Notes and suggestions are localized attachments, not extra severities. The record contains no final prose. It may contain source spelling, normalized spelling, Unicode code points, types, counts, enum values, bytecode indices, and exact scalar values as typed arguments. It never contains a rendered pointer value.
SourceText converts spans to one-based logical line and Unicode scalar-value columns. CRLF counts as one line break. A leading BOM remains part of the byte offsets, so the first following token starts at byte offset three. The internal end is exclusive. Human range prose names the inclusive last scalar, while a zero-length insertion names a single insertion column.
Language selection and interpolation
The default catalog is Modern Standard Arabic. The host may explicitly request English. Source keyword aliases, identifier script, source contents, process locale, environment locale, and terminal direction never select the catalog. The CLI decision owns the user-facing language option.
Both catalogs contain the same reason keys, argument schemas, secondary labels, notes, and suggestions. Missing either template is a build or release-conformance failure; runtime rendering never falls back between languages. Severity labels are خطأ, تحذير, ملاحظة, and اقتراح in Arabic and error, warning, note, and help in English.
Reason keys, paths, display labels, identifiers, exact source spellings, type spellings, library names, C symbols, and numeric values are never translated. The renderer quotes and directionally isolates every interpolated field. It writes all numbers with ASCII digits. A control character is always rendered as its escape plus U+XXXX NAME; no diagnostic writes a raw bidi control to a terminal.
The normal terminal header is logically:
<source-label>:<line>:<column>: <localized-severity> [<reason>]: <localized-message>
For a failure without a source location, the header begins with the localized severity. Directional isolation preserves this field order in Arabic and English terminals.
Source catalog
The following table fixes the MVP source reasons and primary message templates. Braced fields are typed arguments, not preformatted fragments.
| Reason | Severity | Arabic template | English template |
|---|---|---|---|
invalid-utf8 |
error | ترميز UTF-8 غير صالح. |
Invalid UTF-8 encoding. |
misplaced-bom |
error | لا يُسمح بعلامة BOM إلا في بداية الملف. |
A BOM is allowed only at the start of the file. |
non-ascii-digit |
error | استخدم رقم ASCII {replacement} بدلًا من {found}. |
Use the ASCII digit {replacement} instead of {found}. |
invalid-source-character |
error | المحرف {code-point} غير مسموح به في مصدر ASPL. |
Character {code-point} is not allowed in ASPL source. |
invalid-identifier-character |
error | المحرف {code-point} غير مسموح به في المعرّف {name}. |
Character {code-point} is not allowed in identifier {name}. |
mixed-script-identifier |
error | المعرّف {name} يخلط بين الكتابتين العربية واللاتينية. |
Identifier {name} mixes Arabic and Latin scripts. |
bidi-control-not-allowed |
error | محرف الاتجاه {code-point} غير مسموح به هنا. |
Directional control {code-point} is not allowed here. |
bidi-control-splits-token |
error | محرف الاتجاه {code-point} يقسم رمزًا لغويًا. |
Directional control {code-point} splits a token. |
visible-separator-required |
error | يلزم فاصل مرئي بين هذين الرمزين. |
A visible separator is required between these tokens. |
invalid-numeric-literal |
error | العدد {spelling} ليس بصيغة عدد صالحة. |
{spelling} is not a valid numeric literal. |
unterminated-string |
error | السلسلة النصية غير مغلقة. |
String literal is not terminated. |
raw-line-break-in-string |
error | لا يُسمح بفاصل سطر خام داخل سلسلة نصية. |
A raw line break is not allowed in a string literal. |
invalid-string-escape |
error | تسلسل الهروب {spelling} غير معروف. |
Unknown string escape {spelling}. |
invalid-unicode-escape |
error | تسلسل Unicode {spelling} لا يحدد قيمة سلمية صالحة. |
Unicode escape {spelling} does not name a valid scalar value. |
unterminated-block-comment |
error | التعليق الكتلي غير مغلق. |
Block comment is not terminated. |
raw-bidi-control |
warning | يحتوي {context} على محرف اتجاه خام {code-point}. |
{context} contains raw directional control {code-point}. |
confusable-identifiers |
warning | قد يلتبس المعرّفان {name} و{other-name} بصريًا. |
Identifiers {name} and {other-name} may be visually confusable. |
confusable-reserved-name |
warning | قد يلتبس المعرّف {name} بالاسم المحجوز {reserved-name}. |
Identifier {name} may be visually confusable with reserved name {reserved-name}. |
expected-token |
error | كان المتوقع {expected}، لكن وُجد {found}. |
Expected {expected}, found {found}. |
expected-expression |
error | كان المتوقع تعبيرًا، لكن وُجد {found}. |
Expected an expression, found {found}. |
expected-statement |
error | كان المتوقع تعليمة، لكن وُجد {found}. |
Expected a statement, found {found}. |
expected-declaration |
error | كان المتوقع تعريف دالة أو تصريحًا خارجيًا، لكن وُجد {found}. |
Expected a function or foreign declaration, found {found}. |
unexpected-token |
error | الرمز {found} غير متوقع هنا. |
Unexpected token {found}. |
numeric-literal-out-of-range |
error | القيمة {spelling} خارج مجال النوع {type}. |
{spelling} is outside the range of type {type}. |
undefined-name |
error | الاسم {name} غير معرّف. |
Name {name} is not defined. |
name-not-callable |
error | الاسم {name} ليس دالة قابلة للاستدعاء. |
Name {name} is not callable. |
callable-name-conflict |
error | الاسم القابل للاستدعاء {name} معرّف مسبقًا. |
Callable name {name} is already defined. |
visible-name-conflict |
error | الاسم {name} يتعارض مع اسم ظاهر. |
Name {name} conflicts with a visible name. |
duplicate-foreign-target |
error | الرمز C {symbol} من المكتبة {library} مصرح به مسبقًا. |
C symbol {symbol} from library {library} is already declared. |
missing-entry-function |
error | البرنامج لا يعرّف دالة البداية. |
Program does not define the entry function. |
multiple-entry-functions |
error | البرنامج يعرّف أكثر من دالة رئيسية. |
Program defines more than one entry function. |
invalid-entry-signature |
error | يجب ألا تستقبل دالة البداية معاملات وأن تعيد i64. |
The entry function must take no parameters and return i64. |
foreign-entry-not-allowed |
error | لا يمكن أن تكون دالة البداية تصريحًا خارجيًا. |
The entry function cannot be foreign. |
entry-call-not-allowed |
error | لا يمكن لمصدر ASPL استدعاء دالة البداية. |
ASPL source cannot call the entry function. |
type-mismatch |
error | كان النوع المتوقع {expected}، لكن النوع الفعلي {actual} في {context}. |
Expected type {expected}, found {actual} in {context}. |
argument-count-mismatch |
error | يحتاج الاستدعاء إلى {expected} من المعاملات، لكنه تلقى {actual}. |
Call expects {expected} arguments but received {actual}. |
immutable-assignment |
error | لا يمكن إسناد قيمة جديدة إلى الربط الثابت {name}. |
Cannot assign to immutable binding {name}. |
invalid-assignment-target |
error | الاسم {name} ليس ربطًا محليًا قابلًا للإسناد. |
Name {name} is not an assignable local binding. |
unit-value-not-allowed |
error | لا يمكن استخدام نتيجة unit كقيمة في {context}. |
A unit result cannot be used as a value in {context}. |
return-value-required |
error | يجب أن تعيد الدالة قيمة من النوع {type}. |
Function must return a value of type {type}. |
return-value-not-allowed |
error | لا يمكن لدالة unit أن تعيد قيمة. |
A unit function cannot return a value. |
missing-return |
error | قد تصل الدالة {name} إلى نهايتها دون إعادة {type}. |
Function {name} may reach its end without returning {type}. |
unreachable-statement |
error | هذه التعليمة غير قابلة للوصول. |
This statement is unreachable. |
unknown-foreign-adapter |
error | ملف التكييف الخارجي {adapter} غير معروف. |
Unknown foreign adapter profile {adapter}. |
foreign-signature-mismatch |
error | التوقيع المعلن لا يطابق ملف التكييف {adapter}. |
Declared signature does not match adapter profile {adapter}. |
invalid-foreign-library-name |
error | يجب أن يكون اسم المكتبة سلسلة غير فارغة بلا NUL. |
Library name must be a nonempty string without NUL. |
invalid-foreign-symbol-name |
error | يجب أن يكون اسم رمز C سلسلة غير فارغة بلا NUL. |
C symbol name must be a nonempty string without NUL. |
unsafe-required-on-foreign-declaration |
error | يجب تعليم التصريح الخارجي بالكلمة {marker}. |
Foreign declaration must be marked {marker}. |
unsafe-required-on-foreign-call |
error | يجب تعليم الاستدعاء الخارجي بالكلمة {marker}. |
Foreign call must be marked {marker}. |
unsafe-on-safe-call |
error | لا تُستخدم العلامة {marker} مع استدعاء آمن. |
A safe call must not be marked {marker}. |
The type-mismatch context enum distinguishes initializer annotation, assignment, return, argument, condition, unary operand, binary operand, equality, and explicit conversion. The renderer localizes those context names. A normalized-name collision uses visible-name-conflict, points to the earlier declaration, and carries both original spellings and the shared NFC spelling; its catalog exposes exactly two record shapes: name with no secondary span, or name, other-name, and normalized-name with earlier-declaration.
The raw-control warning occurs once per raw control in a string or comment. Confusable warnings compare every distinct declared identifier against the other declarations, both keyword sets, and the اطبع and print builtin aliases under the pinned LTR and RTL skeletons. One colliding declaration pair produces one finding at the later declaration, with the earlier declaration as a secondary span. A reserved-name collision carries name, reserved-name, and a reserved-kind enum of keyword or builtin; the renderer shows that kind as localized structured detail. Arabic renders the values as كلمة محجوزة and دالة مضمنة, while English renders keyword and builtin. Skeleton values never appear in output.
ASCII digit input and replacement
Numeric syntax and identifier continuations use ASCII digits 0 through 9 in Arabic, English, and mixed source. ASPL-aware editors convert Arabic-Indic digits ٠١٢٣٤٥٦٧٨٩ and Extended Arabic-Indic digits ۰۱۲۳۴۵۶۷۸۹ to their ASCII equivalents while typing or pasting executable source. They preserve exact contents inside strings and comments.
An editor converts only when its lossless lexical view can classify the insertion as executable source. If the surrounding malformed text makes that uncertain, it leaves the input unchanged. The compiler then reports non-ascii-digit for each maximal contiguous convertible digit span and attaches the complete ASCII replacement. Other Unicode decimal digits remain invalid source or identifier characters and receive no guessed conversion.
The compiler never silently normalizes stored source bytes. This keeps command-line builds reproducible while the playground gives Arabic keyboard users the expected input behavior.
Excerpts and bidirectional safety
The terminal renderer uses an escaped logical-order excerpt. A single-line finding shows the complete primary line when it is at most 160 scalar values. Longer lines show a 160-scalar window around the primary span with explicit omission marks. A multiline finding shows the first and last affected lines with an omission line between them. A secondary span on another line receives its own compact excerpt.
Tabs, bidi controls, other default-ignorables, and malformed bytes appear visibly. A terminal underline is permitted only when the renderer can calculate the visual columns safely. Otherwise it prints the exact start and end line and scalar columns in prose. It never presents an approximate caret under RTL or mixed-direction text. The website uses its token-aware source-to-display mapping for exact highlighting.
Recovery, ordering, and caps
Malformed UTF-8 stops SourceText construction immediately. The decoder reports the first maximal invalid byte subsequence and produces no LexedSource. Lexing consumes one invalid scalar or the complete malformed lexical token, then resumes at the next valid scalar or token boundary. An unterminated line string stops at its line break; an unterminated block comment consumes through end of file. The parser does not run after a lexical error.
The parser synchronizes at a statement terminator, a closing brace, a valid statement-start token at the current brace depth, or a top-level FN or UNSAFE FOREIGN start at depth zero. A synchronization token remains available to the construct that owns it. The checker does not run after a parser error.
The checker creates an internal error type after an invalid expression. A later diagnostic whose only cause is that error type is suppressed. It still reports independent errors in unaffected operands, later statements, later functions, and collected declarations. This internal type never reaches CheckedProgram.
Warnings from every completed stage survive a later error. The compiler sorts records by primary byte offset, then puts errors before warnings at the same offset, then sorts by reason. It removes a record only when class, reason, primary span, ordered secondary spans, and typed arguments all match.
One compilation reports at most 20 errors and 20 warnings across all source stages. Reaching a cap suppresses only that severity and sets its matching truncation flag. The renderer adds one localized summary line for a set flag; that line is not another diagnostic. Limits and sorting do not depend on hash-table iteration, allocation addresses, catalog language, or source alias choice.
Policy, load, limit, host, and internal reasons
The host-capability reason is foreign-imports-not-supported. From source compilation it is a policy-rejection with the first foreign declaration as its location and the total import count as an argument. For externally supplied bytecode it is a load-failure with the first import index and count.
Core load reasons are:
invalid-bytecode, with the stable validator-rule name plus the relevant byte offset or table index;unsupported-bytecode-version, with expected and actual versions;invalid-native-envelope, with the failed envelope-rule name;foreign-library-open-failed, with import index and exact library string; andforeign-symbol-lookup-failed, with import index, exact library string, and exact symbol string.
The numbered validation and native-envelope rules already fixed by the bytecode and packaging decisions supply the stable rule-name enums. Fresh compiler output rejected by the validator is internal-failure with reason fresh-bytecode-rejected, not a load failure.
Configured limits use limit-stop and one of these reasons:
- compilation:
source-byte-limit,lexical-item-limit,syntax-node-limit,nesting-limit, orcompile-memory-limit; - loading and host policy:
bytecode-byte-limit,function-count-limit,import-count-limit,constant-count-limit,string-byte-limit,bytecode-instruction-limit, orregister-count-limit; and - execution:
execution-instruction-limit,frame-limit,slot-limit,vm-memory-limit,output-limit, ortime-limit.
Every limit result carries the configured ceiling and observed or requested count when one exists. Host-specific decisions choose the numeric ceilings. Cancellation uses class cancelled, reason cancelled, and the phase that observed it.
Core host reasons are allocation-failed and host-operation-failed, with the failed operation as a stable enum. Core internal reasons are compiler-invariant-failed, fresh-bytecode-rejected, vm-invariant-failed, native-template-invalid, and completed-elf-invalid. The CLI decision may add filesystem operation enums without turning I/O errors into source diagnostics.
Trap catalog and arguments
Version 1 has exactly five ASPL trap reasons:
| Reason | Required arguments |
|---|---|
integer-overflow |
Operation enum and integer operands. The enum is add, subtract, multiply, negate, divide, or remainder. |
integer-zero-divisor |
Division or remainder enum plus the dividend. |
invalid-f64-to-i64-conversion |
Exact input bits plus NaN, positive infinity, negative infinity, too low, or too high classification. |
invalid-process-status |
The returned i64. |
foreign-string-contains-nul |
First NUL byte offset and total string byte length. |
The minimum i64 divided or remaindered by -1 is integer-overflow. Frame, slot, instruction, memory, output, and time ceilings are limit stops. Invalid bytecode is a load failure. An unexpected register tag, uninitialized read, array-bound failure after validation, or other broken VM invariant is an internal failure.
A trap keeps the frame stack available for read-only reporting and performs no language-level unwinding or cleanup. Cause arguments are bounded scalars. Renderers show integer values with ASCII digits and floating bits as fixed-width hexadecimal. They never show C pointer values or native addresses.
Source maps and call traces
The optional in-memory ASPL source map replaces the narrower InstructionMap name. It maps final function and function-relative instruction indices to the smallest responsible source span and maps each final import index to its foreign declaration span. It is part of CompiledBytecode only for a current source compilation and never enters bytecode or an ELF payload.
Direct aspl run execution and the playground use the source map for traps. A failed native import also points to its declaration when the source map exists. Generated native programs and externally supplied bytecode report the bytecode function name and function-relative instruction index instead.
A trace lists the current frame first. It shows every frame when the depth is at most 16. A deeper trace shows the eight innermost and eight outermost frames with the exact omitted count between them. Mapped traces show the responsible span for the current instruction and the call site for callers. Unmapped traces show function names and instruction indices. Native backtraces, C frames, and host addresses are never part of an ASPL trace.
Unsafe markers and foreign failures
Every foreign declaration and foreign call must carry its own source marker. Correct markers are silent and disappear before bytecode serialization. They authorize no operation, add no runtime guard, and make no promise about the foreign implementation.
For a missing declaration marker, the compiler inserts the Arabic or English unsafe alias paired with the written خارجي or foreign token plus one ASCII space immediately before that token. For a missing call marker, it uses the alias written on the target foreign declaration and inserts it plus one space before the callee. These are machine-applicable edits. Marking an ASPL function call or the output builtin unsafe produces unsafe-on-safe-call and a removal edit.
Native preparation still processes every import before main. A library or symbol failure is a load failure, stops at the first sorted import, and permits no program side effect. A source map points to the declaration during direct execution. The renderer may append the current dlerror result as host detail after escaping invalid UTF-8 and controls, folding line breaks, and limiting the raw input to 512 bytes. That text is optional and excluded from identity and conformance snapshots.
An embedded NUL found while making a C-string loan traps at the foreign call before control enters C. Foreign return values remain ordinary ASPL data under their adapter rules. ASPL does not reinterpret errno, floating exception flags, or library-specific error conventions.
A foreign function may crash, terminate, stall, or corrupt the process; retain a call-scoped string; return an invalid pointer; or violate its declared C type. Those cases remain outside ASPL's diagnostic and trap guarantees. The unsafe marker makes that boundary visible but cannot contain it.
Conformance contract
The diagnostics suite must cover:
- every source reason in both catalogs, with identical structured records before rendering;
- catalog completeness and exact Arabic-default versus explicitly selected English behavior;
- path, identifier, type, library, symbol, number, and code-point interpolation without translation or bidi leakage;
- UTF-8 byte spans, BOM offsets, all accepted line terminators, CRLF, combining marks, multiline spans, long-line clipping, and safe RTL fallback without a false caret;
- stage gates, each recovery boundary, poison-error cascade suppression, stable ordering, exact duplicate removal, both 20-record caps, and each truncation flag;
- every raw-control and LTR or RTL confusable warning case fixed by the Unicode decision, including keyword and builtin reserved-name collisions with exact kind details;
- typed and pasted Arabic-Indic and Extended Arabic-Indic conversion outside strings and comments, preservation inside them, uncertain-editor fallback, compiler replacement edits, and rejection of every other Unicode digit;
- every semantic rejection from the core and foreign-interoperability acceptance matrices under the catalog above;
- all five traps with exact typed arguments, frame preservation, mapped and unmapped locations, traces below and above 16 frames, and absence of pointers or native addresses;
- missing, correct, and misplaced unsafe markers in Arabic, English, and mixed source, including
unsafe-on-safe-callfor user-defined and builtin calls with exact insertion or removal edits; - source and raw-bytecode host-policy rejection, malformed and incompatible loads, each configured-limit reason, cancellation at each probe, allocator injection, and internal-invariant injection; and
- missing library, missing symbol, optional sanitized host detail, import source mapping, and proof that
maindid not begin.
The CLI ticket owns stream selection, color policy, language flags, process exit codes, and behavior when output itself fails. The playground ticket owns interactive presentation and exact numeric limits, but it must implement the digit conversion, source-map highlighting, safe excerpts, outcome classes, and stop behavior fixed here.