Fallstricke beim Schreiben von Hilde
Diese Liste sammelt jeden Fallstrick, auf den wir beim Schreiben des Compilers, der Standardbibliothek und der Anwendungen darauf gestoßen sind. Zu jedem stehen der Fehlercode und die Meldung des Compilers, der falsche und der richtige Code und eine Regel in einem Satz. Die Zeile „Jetzt“ sagt, wie der Compiler heute damit umgeht: Der Fallstrick ist beseitigt, die Meldung nennt ihn und schlägt die Korrektur vor, oder es hat sich nichts geändert, mit Begründung. Neue Einträge kommen ans Ende.
Sprache und Compiler
1. Ein linearer Wert wird in einem Zweig verbraucht, der zurückkehrt
- Fehler:
H4004Linearity Error, "The variableboxis used inconsistently in the branches of an if statement." - Falsch:
if bad then destroy(x); return y; end if;undxwird weiter unten verwendet - Richtig vorher: ein
else, das den Rest enthält, oder die Entscheidung vor dem if - Jetzt: beseitigt. Ein Zweig, der auf jedem Pfad mit einem return endet, erreicht die Anweisung nach dem if oder dem case nicht, und sein return hat geprüft, dass jede lineare Variable verbraucht ist; die übrigen Zweige allein bestimmen den Zustand danach.
2. Ein Integer-Literal ist ein Int32, wo seine Stelle einen anderen Typ verlangt
- Fehler:
H3060"Expected a value of typeNat64, but got a value of typeInt32",H3003"The type parameterTcannot be bound to bothInt64andInt32",H3027"Cannot cast toInt64fromInt32",H3055für den Konstruktor - Falsch:
pages := 1;mit einemNat64,insertBack(&!odd, 3)mit einemBuffer[Int64],55 + idLengthmit einemIndex - Richtig vorher: eine typisierte Konstante,
constant one: Nat64 := 1; - Jetzt: beseitigt. Ein Literal übernimmt den Typ der Variable oder des Pfads, dem es zugewiesen wird, des anderen Operanden eines Operators auf beiden Seiten, eines generischen Parameters, den die anderen Argumente binden, und des Typs, den ein Konstruktor oder generischer Aufruf haben soll; der zugewiesene Wert eines Konstruktors erhält den Typ seiner Variable. Ein Literal bleibt nur dort ein Int32, wo nichts einen Typ verlangt. Eines wird nun abgelehnt, das vorher angenommen wurde:
1 + nmitnalsNat64war eine Int32-Addition, dienwortlos verengte, und ist jetzt einNat64. - Weiterhin: ein Literal, das über einen Pfad einer Referenz zugewiesen wird,
p->n := 0;mitp: &![Counter, R]undn: Index, istH3060(Int32, nicht Index); zuerst binden,let zero: Index := 0; p->n := zero;.
3. Keine Operatorrangfolge
- Fehler:
H0005Parse Error, "Error during parse." - Falsch:
a or b or c,start + 32 <= n,at + 826 * 86400 - Richtig:
(a or b) or c,(start + 32) <= n,at + (826 * 86400) - Jetzt: eine Kette aus einem der assoziativen Operatoren
+,*,andundorwird geparst und von links gruppiert; jeder andere Operator nach einer Operation istH0009, das sagt, dass Hilde keine Rangfolge kennt, und die Klammern um die Operation vor dem Operator vorschlägt. Eine Rangfolge selbst bleibt absichtlich draußen.
4. Eine lokale Variable heißt wie eine Deklaration des Moduls
- Fehler:
H6045"The variableallowedcollides with the name of a declaration." - Regel: eine lokale Variable oder ein Parameter trägt einen Namen, den keine Deklaration des Moduls hat.
- Jetzt: unverändert; die Meldung nennt die Kollision. Eine Kollision innerhalb eines Moduls sieht ihr Autor und behebt sie in derselben Datei.
5. Ein Parameter heißt wie eine importierte Funktion
- Fehler:
H3055"startis generic but unconstrained", oderH3050fürstart + 1: der Name meinte die importierte Funktion - Jetzt: beseitigt. Eine lokale Variable oder ein Parameter, der wie eine importierte Deklaration heißt, meint die lokale Variable innerhalb ihrer Funktion. Eine Ablehnung wie bei Punkt 4 würde ein Modul brechen, sobald ein anderes Modul, das es importiert, eine Funktion desselben Namens hinzufügt.
6. Die Schnittstelle importiert, was ihre Verträge aufrufen
- Fehler:
H6039"I can't find a callable namedlength." - Jetzt:
H6060nennt das Modul, das eine Funktion dieses Namens deklariert, gibt die Importzeile an und sagt, dass eine Schnittstelle importiert, was ihre eigenen Verträge aufrufen.
7. Eine Typklasse in einer Einschränkung, die das Modul nicht importiert
- Fehler:
H9999Internal Error, "No typeclass with the given name." - Jetzt:
H3105, das die Typklasse und das Modul nennt, das sie deklariert, mit der Importzeile.
8. Eine Instanz, deren Modul nicht importiert ist
- Fehler:
H3061"the typeInt64does not implement the typeclassTotalOrder." - Jetzt:
H3061sagt, wo eine Instanz in einem nicht importierten Modul existiert, welches Modul sie enthält und wie man es importiert.
9. Kein valueOr in der Sprache
- Fehler:
H6039"I can't find a callable namedvalueOr." - Regel:
valueOrist ein Hilfsmittel einzelner Testdateien; ein Modul, das es will, deklariert es selbst. - Jetzt: unverändert für einen Namen, den kein Modul deklariert; ein Name, den ein anderes Modul deklariert, erhält
H6060(Punkt 6).
10. Ein Vertrag, der ein Byte eines String liest
- Fehler:
H3098"This condition callsStandard.String.nthByte -> ... -> @embed" - Jetzt: unverändert; die Meldung nennt die Aufrufkette bis hinunter zum C.
11. Ein Feld einer Schreibreferenz oder eine Neuausleihe, zweimal verwendet
- Fehler:
H4011"Cannot borrow ... because it is already consumed", "Cannot reborrow ... because it is already borrowed" - Regel:
&!(r->field)und eine weitergereichte Schreibreferenz verbrauchen die Referenz; eine Ausleihe in einem Ausdruck gilt bis zum Ende ihrer Anweisung. - Jetzt: die Regel bleibt, sie ist das, was Linearität ausmacht;
H4011sagt sie: für eine verbrauchte Schreibreferenz eine eigene Neuausleihe für jede Verwendung; für eine Ausleihe eine Anweisung je Ausleihe.
12. Ein geliehenes Literal und der Apostroph
- Fehler:
H0005fürget(&!cache, &1, 0);H0001"Character not allowed" für'\'' - Jetzt:
'\''ist der Apostroph, 39.&1istH0011, das sagt, dass nur eine Variable ausgeliehen wird, und die Bindung zeigt, die zuerst zu schreiben ist.
13. Ein reserviertes Wort als Name
- Fehler:
H0008, ein Parse-Fehler, der das Wort nennt - Jetzt:
H0008schlägt einen freien Namen für die Wörter vor, die am häufigsten dafür gehalten werden:fromsince,tountil,bodycontent,endfinish,typekind,casechoice,recordentry,unionvariant.
14. Ein Index auf dem Ergebnis eines Aufrufs
- Fehler:
H0005 - Jetzt:
H0010sagt, dass ein Index nach einem Namen steht, und zeigt die Bindung, die zuerst zu schreiben ist. Nicht beseitigt: ein Index auf einem Aufruf würde einen Wert ausleihen, der keine Variable hat, in der er lebt.
15. Ein Index einer Modulkonstante
- Fehler:
H6039"I can't find a variable nameddigits." ohne Stelle im Quelltext - Jetzt:
H6059mit der Stelle: ein Pfad wird über eine Ausleihe gelesen, eine Konstante kann nicht ausgeliehen werden, und die Meldung zeigt die lokale Variable, die zuerst zu binden ist.
16. Ein Pfad in einen Wert statt in eine Referenz
- Fehler:
H6043"The head of a reference transform expression must be of a reference type, but I gotStamp" - Jetzt:
H6043zeigt die Funktion über&[T, R], die&(r->slot)zurückgibt.
17. Eine Bindung eines case, die einen vorher gebundenen Namen nimmt
- Fehler:
H6020"push_var: var with the name stamp already exists" - Jetzt:
H6020sagt, dass ein Name bis zum Ende seines Blocks gebunden bleibt, auch nachdem sein Wert verbraucht ist, und schlägt einen anderen Namen vor.
18. Eine lineare var erneut zugewiesen und None einer linearen Option zu früh gebaut
- Fehler:
H4010"Cannot assign to the variableresultbecause it is not yet consumed.";H4012"The variablenothingis not consumed by the time of the return statement." - Regel: eine Option eines linearen Typs ist linear, auch als None, und eine var eines linearen Typs erhält einen neuen Wert erst, nachdem der alte verbraucht ist.
- Jetzt: die Regel bleibt;
H4010sagt sie, undH4012sagt sie für eine Variable einer Option oder eines Either.
19. Ein linearer Wert in den Zweigen eines if-Ausdrucks
- Fehler:
H4005"The variableplainis consumed multiple times within the same expression." - Jetzt: beseitigt: ein Zweig läuft, also ist ein Wert, den beide Zweige verbrauchen, einmal verbraucht. Dabei gefunden: ein Wert, den nur ein Zweig verbrauchte, wurde angenommen und blieb auf dem anderen Pfad unverbraucht; das ist jetzt
H4015.
20. Ein Span einer Ausleihe einer lokalen Variable, gebunden mit einem Regionsnamen
- Fehler:
H3054"The slotthereshould have typeSpan[Nat8, Region<5782>]but is declared to have typeSpan[Nat8, R]" - Jetzt:
H3054sagt für eine Region einer Ausleihe einer lokalen Variable, dass sie im Quelltext keinen Namen hat, und wie man den Wert stattdessen verwendet.
21. Zwei Module exportieren denselben Namen
- Fehler:
H2027"Colliding imports" - Jetzt:
H2027zeigt den Import mitas.
22. \u{...} in einem String-Literal
- Fehler: keiner; die sechs Zeichen blieben wie geschrieben im String
- Jetzt:
\u{X}schreibt den Codepunkt X, ein bis sechs Hexadezimalziffern, als UTF-8; ein Codepunkt über 10FFFF oder ein Surrogat istH6058.
23. Eine importierte C-Funktion innerhalb von @embed
- Fehler: gcc, "implicit declaration of function
duckdb_close" - Jetzt:
H3106: ein Aufruf eines C-Namens, den das Programm überpragma Foreign_Importimportiert, innerhalb des Textes eines @embed, wird mit dem aufzurufenden Hilde-Namen abgelehnt.
24. None() als Argument eines Parameters vom Typ Option
- Fehler:
H3055"Noneis generic but unconstrained" - Falsch:
second(box, None())mit einem Parameterchoice: Option[Int32] - Richtig:
let none: Option[Int32] := None();undsecond(box, none) - Jetzt: angenommen: ein mit
H3055abgelehntes Argument wird noch einmal mit dem Typ seines Parameters geprüft, wenn dieser Typ ausgeschrieben ist oder durch den Typ bestimmt wird, den der Aufruf erwartet; so wird auchRight(right => None())in einemletvonEither[Int32, Option[Int32]]angenommen. Ein Parameter vom eigenen Typparameter des Aufgerufenen, wieignore(None())oderisEmpty(None())überOption[T], entscheidet weiterhin nichts und bleibtH3055.
Außerhalb des Compilers
25. Prüfungen, die eine erzeugte C-Variable nennen
- Regel: nach einer Änderung der Namen im erzeugten C in
scripts/nach den alten Namen suchen, bevor die Prüfungen wieder laufen. - Jetzt: unverändert; es ist eine Regel für den, der den Codegenerator ändert.
26. SQLite und SQLCipher in einem Programm
- Fehler:
H2044"This programme links bothsqlite3andsqlcipher" - Jetzt: unverändert; die Meldung nennt beide, und Standard.SqlCipher öffnet einfache Dateien mit
openPlainWriterundopenPlainReader.
27. Eine Einstellung von DuckDB, die zu allem Ja sagt
- Regel: ein Schutz wird dort geprüft, wo er wirkt: Standard.DuckDB liest die Einstellungen nach dem Öffnen zurück.
28. Ein Commit von DuckDB nach einer fehlgeschlagenen Anweisung
- Regel: eine Datenbank, die eine Transaktion beim ersten Fehler abbricht, kann ihren Commit dennoch als Erfolg melden; Standard.DuckDB macht mit einem Fehler einen Rollback.
29. Eine data:-URL in einem URL-Attribut einer Vorlage
- Wirkung:
src="#ZgotmplZ"statt des Bildes - Regel: in
href,srcund Attributen mitsrc,urioderurlim Namen wird jedes Schema außer http, https und mailto zu#ZgotmplZ, absichtlich; ein Wert, den der Server selbst erzeugt, geht mit der Begründung daneben durch|safe.
30. Eine Funktion eines neueren Hilde in einer Anwendung, die mit einem älteren gebaut ist
- Fehler:
H2030für eine Funktion, die das Hilde, mit dem die Anwendung gebaut wird, noch nicht hat - Regel: eine Anwendung baut gegen das Hilde, mit dem sie gebaut wird; was dort fehlt, fehlt für sie.
31. Ein verankertes Muster mit einem Literal nach seinem Anfang
- Jetzt: in Standard.Regex behoben: ein verankertes Muster überspringt die Suche nach seinem geforderten Literal.
32. = zwischen zwei Spans
- Fehler: cc, "invalid operands to binary ==" bei
hilde_span_t - Falsch:
word = "export "mitword: Span[Nat8, Static] - Richtig:
equal(word, "export ")mitequalaus Standard.Text - Jetzt:
H3107: ein Vergleich von Spans, Records oder Unions wird vom Typprüfer abgelehnt,H3108, wo ein Aufruf eine Typvariable zu einem davon macht.
33. Ein reserviertes Wort als Name: is, skip und die anderen
- Fehler: ein Parse-Fehler; damals als
H2017für ein anderes Modul gezeigt (34) - Falsch:
function is(found: Option[String], expected: Span[Nat8, Static]): Bool,let skip: Int32 := 3; - Richtig:
function holds(...),let skipCount: Int32 := 3; - Jetzt:
H0008nennt das Wort und schlägt einen freien Namen vor, im Text und als Vorschlag der JSON-Form (skipergibtskipCount,isergibtholds). - Die reservierten Wörter, alle:
and,as,body,borrow,case,constant,do,else,end,ensures,false,for,from,function,generic,if,import,instance,invariant,is,let,method,module,nil,not,of,or,pragma,raise,raises,record,requires,return,sizeof,skip,then,to,true,type,typeclass,union,var,when,while, und die TypwörterFree,Linear,Region,TypeundSpan(die Syntax, Abschnitt 1).
34. Ein unbekanntes Modul für einen Parse-Fehler an anderer Stelle
- Fehler:
H2017"Unknown moduleStandard.String" fürBindings.File - Jetzt: ein Modul, dessen Kopf nicht gelesen werden kann, lässt die Reihenfolge ihren eigenen Parse-Fehler melden, statt die Module unsortiert zu lassen.
35. Ein erfundener Wert zum Zurückgeben nach abort
- Fehler:
H2031"The functionxdoes not end in a return statement.", wenn eine Funktion aufabort(...)endete und danach nichts folgte - Falsch:
abort("bad"); return 0;, ein Wert, den nie ein Aufrufer sieht - Richtig:
abort("bad");als letzte Anweisung - Jetzt: beseitigt.
abortgibtNeverzurück, und ein Aufruf einer Funktion, dieNeverzurückgibt, beendet einen Pfad wie ein return; 139 der 160 Werte sind weg.
36. Ein Docstring zwischen pragma Slot und seiner Deklaration
- Fehler:
H0005"Error during parse." am Docstring - Falsch:
pragma Slot;, dann ein Docstring, dannfunction sqliteClose(...) - Richtig: der Docstring vor
pragma Slot;, oder ein---Kommentar über der Gruppe von Slots, den die Vertragsprüfung für jeden Slot der Gruppe nimmt; beide prüfen mit Exit 0 - Regel: zwischen
pragma Slot;und der Deklaration, die es markiert, steht nichts. - Jetzt: nichts geändert: der Parser lehnt dort weiterhin einen Docstring ab, und die Vertragsprüfung nimmt den Kommentar über einer Gruppe von Slots für jeden von ihnen.
37. Eine Schreibreferenz weitergereicht, wo eine Leseferenz verlangt wird
- Fehler:
H3060"Expected a value of type&[Manifest, R], but got a value of type&![Manifest, Region<...>]." - Falsch:
peek(x)oderpeek(&~x)mitx: &![T, R]undpeeküber&[T, R] - Richtig: den Wert nehmen und zurückgeben,
function set(m: Manifest, ...): Manifest, und darin abwechselnd zum Lesen und zum Schreiben ausleihen; oder der Funktion ebenfalls eine Schreibreferenz geben, wie esStandard.Buffer.lengthMuttut - Jetzt: nichts geändert: eine Schreibreferenz wird, solange sie gehalten wird, von keiner anderen Funktion durchgelesen.
38. Ein Feld eines Ergebnisses in einem ensures ausgeliehen
- Fehler:
H6043"The head of a reference transform expression must be of a reference type" fürensures length(&(result.name)) = spanLength(name) - Falsch:
ensures length(&(result.name)) > 0;, ein Pfad in den Wert result - Richtig: eine Zugriffsfunktion des Moduls, die
&resultnimmt und gibt, was die Bedingung braucht,ensures nameLength(&result) = spanLength(name);; eine, die einen Span über asSpan liefert, wird in einem Vertrag abgelehnt (H3098), eine, die eine Länge liefert, wird angenommen - Jetzt: die Meldung nennt die Form mit einer Referenz und einer Funktion, wie bei Fallstrick 16; sonst nichts geändert, da ein Vertrag sein Ergebnis liest wie jeder Ausdruck einen Wert.
39. Ein Feld des Ergebnisses eines Aufrufs
- Fehler:
H0005"Error during parse." am Punkt, ohne Hinweis - Falsch:
nodeAt().kind,nth(xs, i).kind - Richtig:
let node: Node := nodeAt();und danachnode.kind - Jetzt:
H0012sagt, dass ein Feld nach einem Namen steht, und zeigt die Bindung, die zuerst zu schreiben ist, wieH0010es für einen Index tut. Nicht beseitigt: die Grammatik nimmt einen Pfad nur von einem Namen.