Clause
← Toda la documentación

Traducido del original en inglés · 06042fa · 2026-10-09 · Leer en inglés

Análisis semántico

Se ejecuta después del análisis sintáctico en la compilación predeterminada y con --print-types; las acciones solo sintácticas lo omiten. Los errores detienen el lote afectado antes de LLVM; los diagnósticos conservan los orígenes de macros/inclusiones, y las entradas posteriores se siguen diagnosticando.

Comprobaciones de módulo y de llamadas

Vinculaciones

Cada vinculación tiene una identidad relativa a la función clause[N].local[M]. Las apariciones son definiciones, lecturas o comprobaciones de igualdad exacta, etiquetadas con el contexto de cabecera/guard/cuerpo. El análisis es determinista.

Los recorridos son iterativos con un presupuesto por módulo de 1,000,000 unidades de trabajo. El agotamiento o cualquier error semántico vacía las tablas de vinculaciones y de normalización del módulo.

Tipos y especificaciones

Un grafo de tipos privado representa todas las formas de tipo analizadas independientemente de la disposición en tiempo de ejecución: singletons, rangos, contenedores, roles de campos de map, productos de funciones y aplicaciones sin resolver. Las uniones se aplanan y deduplican; term() es el máximo y none() el mínimo. Valores predeterminados: 16,384 nodos, 16 miembros por unión, 100,000 elementos de trabajo por traducción. El agotamiento amplía a term() con un indicador visible y nunca estrecha una representación.

Los metadatos declarados (-type, -opaque, -nominal, -export_type, -spec, -callback, -optional_callbacks) siguen la referencia de typespecs de OTP y el comportamiento fijado de erl_lint/erl_internal/erl_types:

Inferencia

La inferencia es independiente de los tipos declarados y nunca confía en las specs.

Dominio de inferencia

Decisión del plan 11 step 58A (semantic/types/lattice). Un hecho es un conjunto de valores que puede tener una variable o un resultado. Los hechos se unen donde confluye el flujo de control (cláusulas, ramas) y se amplían entre las rondas de una componente recursiva; cada presupuesto que sigue amplía de forma correcta a un conjunto mayor, y nunca rechaza un programa. Los hechos se imprimen como tipos de Erlang, y las categorías por sus nombres predefinidos.

HechoImpresiónUniónPresupuesto y ampliación
Nadanone()IdentidadUna función que nunca retorna sigue siendo none()
Cualquier cosaterm()Absorbe cualquier hechodynamic() y any() son term()
Enteros42, 1 | 3 | 7Unión de singletonsMás de 8 singletons se convierten en su rango
Rango de enteros1..10, 0..255El menor rango que contiene ambosUn límite que se movió entre rondas pasa al siguiente umbral: uno inferior a 1, después 0, después sin límite; uno superior a -1, después sin límite
Enteros sin límitepos_integer() (1 en adelante), non_neg_integer() (0 en adelante), neg_integer() (-1 hacia abajo), integer()El menor intervalo que contiene ambos, impreso por su categoría—
Flotantesfloat()——
Númerosnumber()Un rango o categoría de enteros unido con float()Los enteros singleton con float() se quedan como 1 | float()
Átomosok, error | ok, boolean()Unión de singletons; exactamente false y true se imprimen como boolean()Más de 8 singletons se convierten en atom()
Identificadorespid(), port(), reference()——
Tuplas{ok, 1}, tuple(), #point{x :: 0, y :: _}Las tuplas del mismo tamaño cuyos primeros elementos no son dos átomos distintos (su etiqueta) se unen elemento a elemento; las demás quedan como miembros separadosMás de 16 elementos se convierten en tuple() salvo que se conozcan todos los elementos; más de 8 formas separadas se convierten en tuple(). Una tupla con el nombre y el tamaño de un record de tupla visible se imprime como el record (step 58J)
Listas[], [T], [T, ...], nonempty_improper_list(H, T), [1, a], [a, b | T]Los elementos se unen; [] con una lista no vacía da una posiblemente vacía; las listas impropias unen cabezas y colas. Las listas de dos o más elementos conocidos conservan sus posiciones (step 58J; una notación de Clause, el lenguaje de tipos no tiene ninguna, con los elementos impresos con | entre paréntesis): las listas posicionales de una misma longitud se unen posición a posición, y en otro caso se unen como listas simplesUna lista de 0..1114111 (char()) se imprime string() o nonempty_string(); una lista posiblemente vacía de _ se imprime list()
Maps#{}, #{a := 1}, #{1..17 => a}, map()Los maps con las mismas claves se unen valor a valor; los maps de otras claves se unen en una única asociación de sus claves y valores unidos (=>: cualquier clave puede faltar, step 58J)Más de 16 claves se unen en una única asociación
Funsfun((term()) -> 1), fun()Los funs de una misma aridad unen sus resultados; otras aridades dan fun()—
Bitstrings<<_:16>>, <<_:3, _:_*2>>, binary()El tamaño menor más cada diferencia de tamaños como unidadBase y unidad 0/8, 8/8, 0/1, 1/1 se imprimen binary(), nonempty_binary(), bitstring(), nonempty_bitstring()

El lowering consume estos hechos. El IR generado nunca convierte un entero en un puntero al heap; el resultado de cada servicio que puede fallar se carga solo en su camino de éxito, y las comprobaciones de forma dominan la extracción. Política de especialización: specialization.md.

--print-types

Imprime cada módulo del lote (en orden de entrada/objetivo, con los módulos de biblioteca después) como código fuente Erlang (impresión de código fuente) junto con lo que encontró la inferencia de tipos. La salida va a stdout y es legible por personas; no es Erlang ni un formato de intercambio. Los avisos permanecen en 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.

Expectativas de inferencia

tests/fixtures/inference/*.erl registran lo que la inferencia debería encontrar para cada función, y lo que encuentra hoy. Cada módulo se convierte en el CTest inference_<module> (tests/compiler/inference/expectations.py):

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

Impresión de tipos

semantic::types::type_source(graph, type) (semantic/types/printing) representa un tipo del grafo de tipos en la sintaxis de tipos de Erlang: _ para cualquier término (term(), escrito por TERM_SOURCE por brevedad; la sintaxis de tipos lee _ como any()), none(), átomos y enteros, 1..5, {ok, T}, tuple(), [T], [T, ...], #{K => V, K := V}, #r{f :: T}, <<_:B, _:_*U>>, fun((A) -> R), A | B. Un fun con varios tipos de función los imprime en una notación de Clause, fun((1) -> one; (_) -> other): la sintaxis de tipos de Erlang no tiene un tipo fun sobrecargado, y una unión de tipos fun significa otra cosa. Los enteros de una unión se imprimen en orden de valor donde está su primer entero, y los consecutivos como un rango (1 | 2 | 3 | 5 se imprime 1..3 | 5; el hecho conserva los singletons). Los tipos predefinidos de erlang omiten su módulo; las referencias a tipos declarados conservan su nombre. Un presupuesto de nodos acota el texto; al superarlo, y por debajo de 32 niveles de anidamiento, aparece ... en su lugar.

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