Clause
← All dokumentation

Översatt från det engelska originalet · 06042fa · 2026-10-09 · Läs på engelska

Semantisk analys

Körs efter parsning vid standardkompilering och --print-types; åtgärder som bara gäller syntax hoppar över den. Fel stoppar den berörda batchen före LLVM; diagnostik behåller makro-/include-ursprung, och senare indata diagnostiseras ändå.

Kontroller av moduler och anrop

Bindningar

Varje bindning har en funktionsrelativ identitet clause[N].local[M]. Förekomster är definitioner, läsningar eller kontroller av exakt likhet, märkta med kontexten huvud/guard/kropp. Analysen är deterministisk.

Genomgångar är iterativa med en modulbudget på 1 000 000 arbetsenheter. Uttömning eller något semantiskt fel rensar modulens bindnings- och normaliseringstabeller.

Typer och specifikationer

En privat typgraf representerar alla parsade typformer oberoende av runtime-layout: singletoner, intervall, behållare, roller för map-fält, funktionsprodukter och olösta tillämpningar. Unioner plattas ut och dedupliceras; term() är toppen och none() botten. Standardvärden: 16 384 noder, 16 unionsmedlemmar, 100 000 arbetsposter per översättning. Uttömning breddar till term() med en synlig flagga och smalnar aldrig av en representation.

Deklarerad metadata (-type, -opaque, -nominal, -export_type, -spec, -callback, -optional_callbacks) följer OTP:s typespec-referens och det fastlåsta beteendet hos erl_lint/erl_internal/erl_types:

Inferens

Inferensen är skild från deklarerade typer och litar aldrig på specs.

Inferensdomän

Beslut i plan 11 step 58A (semantic/types/lattice). Ett faktum är en mängd värden som en variabel eller ett resultat kan ha. Fakta förenas där kontrollflöden möts (klausuler, grenar) och breddas mellan omgångarna i en rekursiv komponent; varje budget nedan breddar sunt till en större mängd och avvisar aldrig ett program. Fakta skrivs ut som Erlang-typer, kategorier med sina inbyggda namn.

FaktumUtskriftFöreningBudget och breddning
Ingentingnone()IdentitetEn funktion som aldrig returnerar förblir none()
Vad som helstterm()Absorberar varje faktumdynamic() och any() är term()
Heltal42, 1 | 3 | 7Union av singletonerFler än 8 singletoner blir sitt intervall
Heltalsintervall1..10, 0..255Minsta intervall som rymmer bådaEn gräns som flyttats mellan omgångar går till nästa tröskel: en nedre till 1, sedan 0, sedan obegränsad; en övre till -1, sedan obegränsad
Obegränsade heltalpos_integer() (1 och uppåt), non_neg_integer() (0 och uppåt), neg_integer() (-1 och nedåt), integer()Minsta intervall som rymmer båda, utskrivet med sin kategori—
Flyttalfloat()——
Talnumber()Ett intervall eller en kategori av heltal förenat med float()Singletonheltal med float() förblir 1 | float()
Atomerok, error | ok, boolean()Union av singletoner; exakt false och true skrivs ut som boolean()Fler än 8 singletoner blir atom()
Identifierarepid(), port(), reference()——
Tupler{ok, 1}, tuple(), #point{x :: 0, y :: _}Tupler av samma storlek vars första element inte är två olika atomer (deras tagg) förenas element för element; övriga förblir separata medlemmarFler än 16 element blir tuple() om inte varje element är känt; fler än 8 separata former blir tuple(). En tupel med en synlig tupel-records namn och storlek skrivs ut som recorden (step 58J)
Listor[], [T], [T, ...], nonempty_improper_list(H, T), [1, a], [a, b | T]Elementen förenas; [] med en icke-tom lista ger en möjligen tom lista; ofullständiga listor förenar heads och tails. Listor med två eller fler kända element behåller sina positioner (step 58J; en Clause-notation, typspråket saknar en sådan, element skrivs ut med | inom parentes): positionella listor av samma längd förenas position för position, annars förenas de som vanliga listorEn lista av 0..1114111 (char()) skrivs ut som string() eller nonempty_string(); en möjligen tom lista av _ skrivs ut som list()
Maps#{}, #{a := 1}, #{1..17 => a}, map()Maps med samma nycklar förenas värde för värde; maps med andra nycklar förenas till en association av sina förenade nycklar och värden (=>: vilken nyckel som helst kan saknas, step 58J)Fler än 16 nycklar förenas till en association
Funsfun((term()) -> 1), fun()Funs med samma aritet förenar sina resultat; andra ariteter ger fun()—
Bitstrings<<_:16>>, <<_:3, _:_*2>>, binary()Den kortare storleken plus varje skillnad mellan storlekar som enhetBas och enhet 0/8, 8/8, 0/1, 1/1 skrivs ut som binary(), nonempty_binary(), bitstring(), nonempty_bitstring()

Sänkningen (lowering) använder dessa fakta. Genererad IR konverterar aldrig ett heltal till en heappekare; varje tjänsteresultat som kan misslyckas läses endast på sin framgångsväg, och formkontroller dominerar extraktion. Policy för specialisering: specialization.md.

--print-types

Skriver ut varje modul i batchen (i indata-/målordning, biblioteksmoduler efter dem) som Erlang-källkod (utskrift av källkod) med det som typinferensen hittade. Utdata går till stdout och är läsbara för människor, inte Erlang och inte ett utbytesformat. Varningar stannar på 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.

Förväntningar på inferensen

tests/fixtures/inference/*.erl registrerar vad inferensen bör hitta för varje funktion, och vad den hittar i dag. Varje modul blir CTest-testet inference_<module> (tests/compiler/inference/expectations.py):

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

Utskrift av typer

semantic::types::type_source(graph, type) (semantic/types/printing) återger en typ ur typgrafen i Erlangs typsyntax: _ för godtycklig term (term(), skrivet av TERM_SOURCE för korthetens skull; typsyntaxen läser _ som any()), none(), atomer och heltal, 1..5, {ok, T}, tuple(), [T], [T, ...], #{K => V, K := V}, #r{f :: T}, <<_:B, _:_*U>>, fun((A) -> R), A | B. En fun med flera funktionstyper skriver ut dem i en Clause-notation, fun((1) -> one; (_) -> other): Erlangs typsyntax har ingen överlagrad fun-typ, och en union av fun-typer betyder något annat. En unions heltal skrivs ut i värdeordning där dess första heltal står, på varandra följande som ett intervall (1 | 2 | 3 | 5 skrivs ut som 1..3 | 5; faktumet behåller singletonerna). Fördefinierade erlang-typer tappar sin modul; referenser till deklarerade typer förblir namngivna. En nodbudget begränsar texten; utöver den, och djupare än 32 nivåers nästling, står ... i stället.

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