Représentation des termes
Types admis : atomes/booléens, entiers arbitraires, flottants binary64 finis, tuples, listes propres/impropres et chaînes, maps, bitstrings et records ordinaires sous forme de tuples ; native records (native records) ; funs (valeurs fonctionnelles) ; pids, ports et références locaux (ci-dessous, ports). Les encodages en mots se trouvent dans abi.md.
Propriété
- Les valeurs composées vivent dans le tas de leur processus
(contrat du tas) ou dans la zone de littéraux en lecture
seule d'un module (zones). Les pointeurs de
processus désignent toujours le début d'un objet. L'admission (compilations Debug uniquement ;
Release fait confiance aux mots,
CHECKED_ADMISSION) vérifie la plage possédée et la forme de l'en-tête, en rejetant les adresses étrangères et les formes invalides (admission) ; le type et l'étendue sont décodés depuis l'en-tête. - Un
Termhôte est exactement unWord, trivialement copiable, avec seulementvalue_. Un Term du tas est valide jusqu'au prochain ramassage de son tas ou à la fin du processus (racines) ; il n'épingle rien et ne porte ni tas, ni jeton de durée de vie, ni compteur de ramassages. L'accès lit ensuite de la mémoire déplacée ou libérée et est indéfini ; il n'existe pas d'erreurstale_term. La destruction est sans risque. Les fabriques, les réservations et les tampons de messages conservent leurs propres vérifications de durée de vie et signalent toujoursexpired_context. - La construction valide les enfants, réserve, initialise, puis publie en une seule étape ; un échec annule le stockage et les compteurs.
Term::from_word(word)n'admet que les immédiats indépendants du propriétaire ;Term::from_word(word, context)admet aussi les atomes et les termes du tas de ce contexte. La transmission dans un même tas conserve l'identité.copy_to/ProcessHeap::addcopient un graphe d'un autre processus selon un contrat imposant le même runtime à l'appelant ; les fabriques de termes refusent les entrées étrangères avecwrong_owner(copie entre tas).- Les cellules vivent jusqu'à ce qu'un ramassage les trouve inaccessibles, ou jusqu'à la destruction du tas (ramassage).
Atomes
AtomStoragepropre à chaque runtime, internement paresseux selon l'orthographe UTF-8 exacte, sans normalisation ni éviction.RuntimeOptions::max_atoms1..2^26, 2^20 par défaut ; les noms de modules/exports comptent. Les orthographes existantes réussissent même à pleine capacité.- Jusqu'à 255 scalaires Unicode ; vide, NUL et caractères supplémentaires autorisés ; UTF-8 malformé/trop long/de substitution rejeté.
- Les charges utiles proviennent d'un compteur global au processus et ne sont jamais réutilisées, même entre runtimes. L'index global contient 2^28 numéros, limités par la charge utile d'atome du mot sur les cibles 32 bits (2^26 identités sur toute la durée de vie). Les limites par runtime sont inchangées. Les numéros d'atomes ne quittent jamais un nœud : ETF et les formes sur disque utilisent les orthographes.
detail::atom_tablefournit des lectures sans verrou par numéro d'atome : directement les enregistrements fixes, puis des blocs de 4 096 emplacements conservés pendant toute la durée de vie du processus. ChaqueAtomStoragepossède ses enregistrements par orthographe, les publie dans cet index et n'admet que ceux qu'il possède ou les enregistrements fixes. La destruction retire ses emplacements.- Les Terms d'atomes n'épinglent rien.
atom_spelling()emprunte jusqu'à la destruction du runtime ; cette fonction etatom_utf8()signalentexpired_contextaprès la destruction du runtime d'un atome non fixe. Déplacer un atome entre runtimes signifie interner son orthographe dans la destination tant que le runtime source est en vie. - Les booléens sont les atomes
true/false. Les atomes fixesfalse,true,undefined,oketerror(abi::v1::fixed_atom_spellings) ont les mots des numéros d'atome 0–4 dans chaque runtime, avec des enregistrements statiques immortels, trouvés sans le verrou et comptés ni parsize()ni dansmax_atoms; le code généré les utilise comme constantes. Tout autre mot d'atome est privé au runtime. - Les littéraux composés (chaînes, listes, tuples et maps de littéraux, flottants, bignums, binaries constants) sont des littéraux de module : encodés à la compilation, décodés une seule fois par runtime lors de l'enregistrement dans son tas de littéraux et partagés (zones de littéraux) ; les littéraux entiers de la petite plage sont des immédiats.
- Les autres littéraux atomes se chargent depuis le cache d'atomes du module
(
ModuleDescriptor::atom_cache: l'identité du runtime qui le revendique, puis un mot par emplacement), que remplit le premier runtime qui enregistre le module ; un contexte dont leFrameState::runtimecorrespond à la revendication le lit sans appel, tout autre appelleCLAUSE_atom_v3. La revendication est rendue lorsque le serveur de code du runtime libère le module. - Le C++ du runtime écrit les atomes littéraux sous la forme
"name"_atom(atom_names.hpp) : unCachedAtomstatique par orthographe conserve le dernier mot interné pour celle-ci, etAtomStorage::internle renvoie après une lecture d'index sans verrou lorsque ce runtime l'a émis, en évitant la validation, le verrou partagé et la map d'orthographes. Un mot d'un autre runtime ou d'un runtime détruit échoue à cette vérification et est remplacé ; plusieurs runtimes dans un même processus restent donc corrects (ils ne font que rafraîchir le cache). Les fonctions auxiliaires des builtins prennent unAtomName, une orthographe avec ou sans un tel cache. - L'internement et la recherche sont sûrs depuis des threads de travail d'ordonnanceur concurrents (threads) ; une orthographe garde un seul mot.
Entiers
- Les valeurs tenant dans la charge utile de 28/60 bits de la cible sont des immédiats ; les plus grandes sont des cellules signe/magnitude immuables. Zéro et les petites valeurs sont toujours normalisés en immédiats. Pas de rétrécissement des littéraux à la largeur de l'hôte.
- En
-O2, les opérandes que l'inférence prouve petits sautent leurs tests d'étiquette, et une plage de résultat prouvée saute aussi la vérification de dépassement et le repli (preuves). - Les
+,-,*générés tentent un chemin rapide en ligne sur deux immédiats, sinon appellent le service du runtime :+et-additionnent ou soustraient directement les mots étiquetés (a + (b ^ 15)) et se replient lorsque le mot déborde, ce qui arrive exactement quand le résultat quitte la petite plage ;*calcule en double largeur avec des bornes explicites. De même pourband,bor,bxor,bsr,div,remet les comparaisons de deux petits entiers (formes inline). - Le service multiplie un bignum (ou un petit produit ayant débordé) par un petit entier, membre par membre, directement depuis la cellule de l'opérande vers la nouvelle ; les autres opérations sur les bignums passent par une valeur multiprécision.
divtronque vers zéro ;remprend le signe du dividende ; les opérations bit à bit utilisent un complément à deux infini ; un décalage négatif inverse le sens ; les très grands décalages à droite saturent à 0 ou -1.- Erreurs : mauvais opérandes et diviseur nul →
badarith;abs/1→badarg. - Limites : comme ERTS, une magnitude d'au plus
BIG_ARITY_MAXmots : 4 194 240 bits sur les cibles 64 bits (65 535 mots), 4 194 272 sur 32 bits (131 071 mots) ; le texte décimal suit (1 262 593 et 1 262 602 chiffres). Un résultat arithmétique plus grand lèveerror:system_limitdans un corps (ValueOutcome::system_limit, ABI) et fait échouer un guard ; un segment entier qui extrait une valeur plus grande ne correspond pas. Le compilateur rejette un littéral au-delà de 4 194 240 bits (illegal integer, comme le scanner d'OTP) et un motif constant au-delà (illegal pattern).
Flottants
- Les bits IEEE binary64 sont transmis au runtime sous forme de huit octets dans l'ordre réseau ; NaN et l'infini sont rejetés. Pas de fast-math.
+ - *restent exacts sur deux entiers ; tout opérande flottant utilise binary64./convertit toujours les deux. Les résultats non finis et les diviseurs nuls →badarith.float/1arrondit au plus proche pair ;round/1arrondit les égalités en s'éloignant de zéro ;trunc,floor,ceilrenvoient des entiers arbitraires. Mauvais opérandes →badarg.- L'égalité exacte distingue
1de1.0et0.0de-0.0; la comparaison numérique compare la partie entière exacte et la fraction du flottant, sans jamais arrondir l'entier.min/maxrenvoient le premier opérande en cas d'égalité.
Tuples, listes, chaînes
- Tuple : en-tête d'arité + champs. Cons : mots de tête + queue.
{}et[]sont des immédiats. Les chaînes sont des listes de points de code. Les listes n'ont pas de longueur maximale hormis la mémoire (budget de tas optionnel compris). Les tuples contiennent jusqu'à 16 777 215 éléments (MAX_TUPLE_ARITY,MAX_ARITYVALd'OTP) ; les constructeurs signalent un tuple plus grand commeresource_limit; les builtins (make_tuple,list_to_tuple/1) lèventbadarg. - Services :
hd,tl,length,tuple_size,size,elementindexé à partir de un.
Maps
- Tables immuables triées selon l'ordre exact des clés. Les clés de
construction en double gardent la dernière valeur. La construction trie les
clés (O(n log n) comparaisons ; des clés déjà croissantes sont seulement
vérifiées). Jusqu'à 32 entrées, une map est une seule cellule du tas et une
mise à jour insère par recherche dichotomique dans une copie ; au-delà,
c'est un arbre B+ de cellules du tas mis à jour par copie de chemin en
O(log n) (arbres de maps). Pas de plafond de
taille ni de travail hormis la mémoire, comme dans OTP. Les clés entières et
flottantes diffèrent (y compris
0.0et-0.0, y compris imbriquées). - Les mises à jour
K := Vexigent la clé ;K => Vinsère ou remplace. Les mises à jour préparent une nouvelle table et la publient une seule fois. - Erreurs dans un corps :
{badmap, M},{badkey, K}; les guards rejettent à la place. - Services :
is_map,map_size,map_get,is_map_key, construction, mise à jour.
Bitstrings
- Empaquetées bit de poids fort en premier, avec une longueur exacte en bits et un remplissage à zéro. Jusqu'à 64 octets vivent en ligne dans un binary de tas dimensionné selon les données ; les valeurs plus grandes utilisent un tampon immuable partagé hors du tas, et les tranches sont des cellules sous-binary de leur original (binaries hors tas). Il n'y a pas de taille maximale hormis un budget de tas optionnel du processus ; les segments entiers sont écrits sans construire un entier aussi large que le segment.
- La construction prépare tous les segments avant de publier. Les segments entiers sont tronqués ; l'endianness native vient de la disposition des données de la cible. Un segment entier d'une petite valeur et d'une largeur d'au plus 64 bits est écrit depuis son mot machine (octets entiers d'un coup sur une frontière d'octet) ; les autres prennent le chemin multiprécision. Le code généré construit en ligne les petites constructions de largeur constante (cadres et transferts).
- Ajouts, comme les binaries inscriptibles d'OTP : une construction qui
commence par un binary entier (
<<Acc/binary, ...>>) de plus de 64 octets construit son résultat dans un tampon avec de la place libre (sa taille arrondie à la puissance de deux supérieure, au moins 256 octets) dont la cellule hors tas dans le processus créateur enregistre ses bits utilisés et sa capacité (zéro dans toute autre cellule, copies comprises). Un ajout ultérieur dont la vue de l'accumulateur se termine exactement là écrit les nouveaux octets dans la place libre, qu'aucune vue ne couvre, et renvoie une vue plus longue du même tampon : un sous-binary de la propre cellule hors tas du tampon, qui peut aller au-delà des bits de cette cellule. Tout autre accumulateur (une vue plus ancienne, celle d'un autre processus, une vue non alignée, un tampon plein) est copié une seule fois dans un nouveau tampon, qui double au moins la taille d'un tampon plein. Les autres vues du tampon ne changent jamais. Les tampons libérés sont réutilisés (réutilisation des tampons). - Segments flottants : largeurs 16/32/64 ; la construction peut encoder
l'infini, mais le filtrage rejette les champs infinis/NaN. Les
correspondances de flottants de largeur nulle extraient
0.0. - Les segments UTF-8/16/32 valident les scalaires, les substituts et la troncature.
- Le filtrage n'avance un curseur de bits explicite qu'en cas de succès ;
:allcomme taille explicite est invalide. - Services :
is_binary,is_bitstring,bit_size,byte_size(arrondi supérieur),size(arrondi inférieur),binary_part/2,3. Erreurs →badarg.
Records
- Les records ordinaires se développent en tuples
{Tag, Fields...}. Les déclarations doivent précéder l'utilisation ; les doublons, champs inconnus, références en avant/à soi-même et champs joker invalides sont des erreurs. - La construction évalue les champs dans l'ordre de déclaration : valeur
explicite, sinon valeur par défaut joker
_ = V, sinon valeur par défaut déclarée, sinonundefined. Chaque valeur par défaut est évaluée séparément à chaque utilisation. - Les motifs vérifient l'arité et l'étiquette, puis seulement les champs
listés.
#r.fest l'index à partir de un (étiquette en 1). - L'accès à un champ vérifie l'arité et l'étiquette ; un échec donne
{badrecord, V}dans les corps et un rejet dans les guards. is_record(V, r)utilise l'arité déclarée.is_record/3exige une étiquette atome et une arité entière (non positive → false ; mauvais types →badarg) ; un troisième argument atome est la requête de native record et renvoie false. Les guards exigent des arguments littéraux.- La mise à jour
Expr#r{f = V, ...}évalue les nouvelles valeurs dans l'ordre du source, puisExpr, puis vérifie l'arité et l'étiquette ({badrecord, Value}en cas de non-correspondance, y compris pourExpr#r{}) et construit un nouveau tuple ; les autres champs sont copiés._ = Vest rejeté dans les mises à jour ; les mises à jour sont interdites dans les motifs et les guards. record_info(fields | size, r)se développe à la compilation en la liste des noms de champs ou la taille du tuple. Les deux arguments doivent être des atomes littéraux etrun record tuple déclaré auparavant ; c'est interdit dans les guards, et unrecord_info/2local est rejeté comme déjà défini.- Native records : native records (formes locale, qualifiée, importée et anonyme).
Pids et références
self/0 renvoie le pid du processus appelant, make_ref/0 une nouvelle
référence ; pid_to_list/1 et ref_to_list/1 renvoient leur texte.
- Un pid est un mot immédiat (quatre bits de poids faible
0x3) contenant le numéro du processus. Les numéros proviennent d'une seule séquence globale au processus et ne sont jamais réutilisés, de sorte qu'un runtime n'admet un mot de pid que s'il a attribué ce numéro : un mot forgé (jamais attribué) ou le pid d'un autre runtime donnewrong_owner. Le pid d'un processus terminé reste un terme valide, comme dans OTP. Les cibles 32 bits disposent de 2^28 numéros par exécution du programme, les cibles 64 bits de 2^60 ; créer un processus au-delà échoue avecresource_limit. - Un port est un mot immédiat (quatre bits de poids faible
0x7) contenant son numéro issu de sa propre séquence jamais réutilisée, admis comme un pid ; il s'affiche sous la forme#Port<0.N>et se range entre les funs et les pids (ports). - Une référence est une cellule du tas (
reference: en-tête plus un numéro 64 bits non tracé) admise comme tout terme du tas : seulement dans son propre processus, copiée par valeur entre processus. UnTermhôte sans racine ne peut pas être accédé après un ramassage ou la fin du processus. Les numéros proviennent d'un seul compteur global au processus, donc chaque référence d'une exécution du programme est unique. - Une référence handle (une référence magique d'OTP) est une cellule de référence qui contient aussi un tampon hors tas ; c'est la même référence pour chaque service et elle partage son tampon avec chaque copie (références handle).
- L'affichage suit les identités locales d'OTP : un pid sous la forme
<0.N.S>(N les 28 bits de poids faible de son numéro, S le reste), une référence sous la forme#Ref<0.A.B.C>(C les 18 bits de poids faible de son numéro, B les 32 suivants, A le reste), dans les styles~wet display. Les pids sont ordonnés par numéro, les références aussi, de sorte que les références ultérieures d'un programme se rangent après les précédentes.
Comparaison et ordre
Itérative et sans plafond de travail, comme dans OTP : seule la mémoire pour
les paires en attente limite une comparaison, recherches de clés de map
comprises, et des mots identiques sont égaux sans parcours. Les bitstrings
alignées sur l'octet comparent des octets entiers d'un coup. Ordre : nombres <
atomes < références < funs < ports < pids < tuples < native records < maps <
nil < listes < bitstrings (les funs
s'ordonnent entre elles).
Les atomes se comparent selon leur orthographe UTF-8 (ordre des points de
code) ; les tuples par arité puis par champs ; les maps par taille, puis clés,
puis valeurs ; les bitstrings par bits logiques. Les nombres (petits entiers,
flottants, bignums par signe et membres), les atomes (par leurs orthographes
dans la table des atomes, via l'index sans verrou par numéro), les références
locales (par numéro), les binaries d'octets entiers, et les tuples et listes
de ces valeurs imbriqués jusqu'à 8 niveaux sont ordonnés directement depuis
leurs mots et cellules (detail::word_order), sans admission de termes ; toute
autre paire, un flottant face à un bignum de même longueur en bits, ou un petit
entier au-delà de 2^53 face à un flottant utilise l'ordre structurel. =:=
prend le même chemin (detail::word_exact) : les paires qu'il ordonne
distinctement sont inégales, et un même mot ou deux références qu'il trouve
égales sont égaux ; une réception filtrant la référence d'une réponse n'admet
donc aucun terme. lists:sort/1 et lists:usort/1 trient les mots des éléments
sur place : les listes de petits entiers uniquement ou d'atomes uniquement
par mot ou orthographe, les autres par un tri stable selon l'ordre des mots.
Affichage
format_term (output.hpp)
rend tout terme admis dans l'un des deux styles d'OTP. Les entiers, les tuples
(les records sont des tuples) et l'imbrication sont identiques dans les deux.
~w (TermStyle::write) | erlang:display/1 (TermStyle::display) | |
|---|---|---|
| Atomes | Entre guillemets sauf s'il commence par une minuscule Latin-1 suivie de caractères de nom (avec @) ; les mots réservés et maybe/else sont entre guillemets ; au-delà de Latin-1, échappement en \x{H} | Entre guillemets sauf s'il commence par une minuscule Latin-1 suivie d'alphanumériques ou de _ ; pas de règle spéciale pour les mots réservés et @ ; UTF-8 conservé |
| Flottants | Aller-retour le plus court dans la disposition d'OTP : 0.1, 100.0, 1.0e16, 1.5e-7 | %.6e du C : 1.500000e+00 |
| Listes | Éléments : [104,105], [1,2|3] | Une liste plate d'octets Latin-1 imprimables s'affiche comme "hi" (octets bruts ; seuls \n et " sont échappés) |
| Bitstrings | <<1,2,5:3>> | Un binary ASCII imprimable s'affiche comme <<"hi">>, les autres comme ~w |
| Maps | #{k => v,k2 => v2} | #{k=>v,k2=>v2} |
- Les maps s'affichent dans l'ordre des clés (
maps:iterator(M, ordered), comme~kwd'OTP). Le~wpar défaut eterlang:display/1d'OTP suivent plutôt sa disposition interne : ordre de la table des atomes pour les clés atomes des petites maps (il varie d'une exécution de la VM à l'autre) et ordre de hachage au-delà de 32 clés. Clause ne reproduit pas cet ordre. - Le rendu est itératif, la profondeur n'est donc limitée que par le terme. Le
texte est plafonné à 64 Mio par défaut ; un dépassement (par exemple un
sous-terme largement partagé) échoue avec
resource_limitet ne renvoie aucun texte partiel. - Références (goldens) :
runtime_printingcompare les deux styles avec OTP (tous les résultats du corpus plus des cas limites écrits à la main) ; les lignes display dont l'ordre des maps OTP est interne sont ignorées (fixtures).
Clause