Clause
← Gesamte Dokumentation

Übersetzt aus dem englischen Original · 06042fa · 2026-10-09 · Auf Englisch lesen

Semantische Analyse

Läuft nach dem Parsen bei der Standardkompilierung und bei --print-types; reine Syntaxaktionen überspringen sie. Fehler stoppen den betroffenen Batch vor LLVM; Diagnosen behalten ihre Makro-/Include-Herkunft, und spätere Eingaben werden weiterhin diagnostiziert.

Modul- und Aufrufprüfungen

Bindungen

Jede Bindung hat eine funktionsrelative Identität clause[N].local[M]. Vorkommen sind Definitionen, Lesezugriffe oder Prüfungen auf exakte Gleichheit, markiert mit Kopf-/Guard-/Rumpfkontext. Die Analyse ist deterministisch.

Durchläufe sind iterativ mit einem Modulbudget von 1.000.000 Arbeitseinheiten. Erschöpfung oder ein beliebiger semantischer Fehler leert die Bindungs- und Normalisierungstabellen des Moduls.

Typen und Spezifikationen

Ein privater Typgraph stellt alle geparsten Typformen unabhängig vom Runtime-Layout dar: Singletons, Bereiche, Container, Rollen von Map-Feldern, Funktionsprodukte und unaufgelöste Anwendungen. Unions werden abgeflacht und dedupliziert; term() ist das obere und none() das untere Element. Standardwerte: 16.384 Knoten, 16 Union-Mitglieder, 100.000 Arbeitsschritte pro Übersetzung. Erschöpfung weitet auf term() mit sichtbarem Flag auf und engt nie eine Repräsentation ein.

Deklarierte Metadaten (-type, -opaque, -nominal, -export_type, -spec, -callback, -optional_callbacks) folgen der OTP-Typespec-Referenz und dem festgelegten Verhalten von erl_lint/erl_internal/erl_types:

Inferenz

Die Inferenz ist von deklarierten Typen getrennt und vertraut Specs nie.

Inferenzdomäne

Entscheidung von Plan 11 step 58A (semantic/types/lattice). Ein Fakt ist eine Menge von Werten, die eine Variable oder ein Ergebnis haben kann. Fakten werden dort vereinigt, wo der Kontrollfluss zusammenläuft (Klauseln, Zweige), und zwischen den Runden einer rekursiven Komponente aufgeweitet; jedes der folgenden Budgets weitet korrekt auf eine größere Menge auf und weist nie ein Programm zurück. Fakten werden als Erlang-Typen ausgegeben, Kategorien unter ihren eingebauten Namen.

FaktAusgabeVereinigungBudget und Aufweitung
Nichtsnone()Neutrales ElementEine Funktion, die nie zurückkehrt, bleibt none()
Allesterm()Absorbiert jeden Faktdynamic() und any() sind term()
Ganzzahlen42, 1 | 3 | 7Union der SingletonsMehr als 8 Singletons werden zu ihrem Bereich
Ganzzahlbereich1..10, 0..255Kleinster Bereich, der beide enthältEine Schranke, die sich zwischen Runden bewegt hat, springt zum nächsten Schwellenwert: eine untere auf 1, dann 0, dann unbeschränkt; eine obere auf -1, dann unbeschränkt
Unbeschränkte Ganzzahlenpos_integer() (ab 1), non_neg_integer() (ab 0), neg_integer() (-1 und darunter), integer()Kleinstes Intervall, das beide enthält, ausgegeben nach seiner Kategorie—
Gleitkommazahlenfloat()——
Zahlennumber()Ein Bereich oder eine Kategorie von Ganzzahlen, vereinigt mit float()Singleton-Ganzzahlen mit float() bleiben 1 | float()
Atomeok, error | ok, boolean()Union der Singletons; genau false und true werden als boolean() ausgegebenMehr als 8 Singletons werden zu atom()
Bezeichnerpid(), port(), reference()——
Tupel{ok, 1}, tuple(), #point{x :: 0, y :: _}Tupel gleicher Größe, deren erste Elemente nicht zwei verschiedene Atome sind (ihr Tag), werden elementweise vereinigt; andere bleiben getrennte MitgliederMehr als 16 Elemente werden zu tuple(), sofern nicht jedes Element bekannt ist; mehr als 8 getrennte Formen werden zu tuple(). Ein Tupel mit Name und Größe eines sichtbaren Tupel-records wird als record ausgegeben (step 58J)
Listen[], [T], [T, ...], nonempty_improper_list(H, T), [1, a], [a, b | T]Elemente werden vereinigt; [] mit einer nichtleeren Liste ergibt eine möglicherweise leere; unechte Listen vereinigen Köpfe und Tails. Listen aus zwei oder mehr bekannten Elementen behalten ihre Positionen (step 58J; eine Clause-Notation, die Typsprache hat keine, Elemente mit | in Klammern ausgegeben): positionelle Listen einer Länge werden Position für Position vereinigt, sonst als einfache ListenEine Liste von 0..1114111 (char()) wird als string() oder nonempty_string() ausgegeben; eine möglicherweise leere Liste von _ als list()
Maps#{}, #{a := 1}, #{1..17 => a}, map()Maps mit denselben Schlüsseln werden Wert für Wert vereinigt; Maps anderer Schlüssel werden zu einer Assoziation ihrer vereinigten Schlüssel und Werte vereinigt (=>: jeder Schlüssel darf fehlen, step 58J)Mehr als 16 Schlüssel werden zu einer Assoziation vereinigt
Funsfun((term()) -> 1), fun()Funs einer Stelligkeit vereinigen ihre Ergebnisse; andere Stelligkeiten ergeben fun()—
Bitstrings<<_:16>>, <<_:3, _:_*2>>, binary()Die kürzere Größe plus jede Größendifferenz als EinheitBasis und Einheit 0/8, 8/8, 0/1, 1/1 werden als binary(), nonempty_binary(), bitstring(), nonempty_bitstring() ausgegeben

Das Lowering verarbeitet diese Fakten. Erzeugte IR wandelt nie eine Ganzzahl in einen Heap-Zeiger um; jedes fehlbare Serviceergebnis wird nur auf seinem Erfolgspfad geladen, und Formprüfungen dominieren die Extraktion. Spezialisierungsstrategie: specialization.md.

--print-types

Gibt jedes Modul des Batches (in Eingabe-/Zielreihenfolge, Bibliotheksmodule danach) als Erlang-Quelltext aus (Quelltextausgabe) zusammen mit dem, was die Typinferenz gefunden hat. Die Ausgabe erfolgt auf stdout und ist menschenlesbar, weder Erlang noch ein Austauschformat. Warnungen bleiben auf stderr.

%% module "branches" source="branches.erl" target="" declared=complete inferred=complete
-module(branches).
-export([mixed/1]).

%% inferred: mixed(_) -> 1..2
mixed(X) ->
    case X of
        1 ->
            1;
        _ ->
            2
    end :: 1..2.

Inferenzerwartungen

tests/fixtures/inference/*.erl halten fest, was die Inferenz für jede Funktion finden sollte und was sie heute findet. Jedes Modul wird zum CTest inference_<module> (tests/compiler/inference/expectations.py):

%% expect: sum() -> 3
%% today: sum() -> _
sum() -> 1 + 2.

Ausgabe von Typen

semantic::types::type_source(graph, type) (semantic/types/printing) stellt einen Typ des Typgraphen in Erlang-Typsyntax dar: _ für einen beliebigen Term (term(), der Kürze halber von TERM_SOURCE geschrieben; die Typsyntax liest _ als any()), none(), Atome und Ganzzahlen, 1..5, {ok, T}, tuple(), [T], [T, ...], #{K => V, K := V}, #r{f :: T}, <<_:B, _:_*U>>, fun((A) -> R), A | B. Ein fun mit mehreren Funktionstypen gibt sie in einer Clause-Notation aus, fun((1) -> one; (_) -> other): die Erlang-Typsyntax kennt keinen überladenen fun-Typ, und eine Union von fun-Typen bedeutet etwas anderes. Die Ganzzahlen einer Union werden in Wertreihenfolge dort ausgegeben, wo ihre erste Ganzzahl steht, aufeinanderfolgende als Bereich (1 | 2 | 3 | 5 wird als 1..3 | 5 ausgegeben; der Fakt behält die Singletons). Vordefinierte erlang-Typen verlieren ihr Modul; Referenzen auf deklarierte Typen bleiben benannt. Ein Knotenbudget begrenzt den Text; jenseits davon, und unterhalb von 32 Verschachtelungsebenen, steht ... an seiner Stelle.

clau --print-types answer.erl client.erl
clau --print-types --project project.toml --target demo --verbose