Files
saselang-bible/chapters/024-casts-et-conversions.md
2026-09-12 18:12:13 +02:00

264 lines
9.4 KiB
Markdown

# 24. Casts et conversions
## 24.1 Principe général — V1 REQUIS — FIGÉ EN PRINCIPE
Saselang distingue explicitement :
```text
conversion de valeur
cast de hiérarchie nominale
bitcast de représentation binaire
```
Ces mécanismes ne sont pas des alias.
`bitcast<T>(value)` est défini au chapitre 20 et ne constitue jamais une conversion numérique.
## 24.2 Conversions implicites numériques — V1 REQUIS — FIGÉ
Il n'existe aucune promotion numérique implicite générale entre deux valeurs déjà typées de types primitifs différents.
```text
int8 small = ...;
int32 large = small; // ERROR
int32 explicitLarge = small::toInt32(); // OK
```
Le typage contextuel d'un littéral non encore typé reste une règle distincte.
## 24.3 Upcast — V1 REQUIS — FIGÉ
Un upcast compatible dans une hiérarchie de classes ou vers une interface satisfaite est une assignation de sous-typage normale et ne nécessite pas une syntaxe de cast dédiée.
```text
Dog dog = ...;
Animal animal = dog;
Serializable serializable = dog;
```
## 24.4 Downcast et raffinement — V1 REQUIS — FIGÉ EN PRINCIPE
Le mécanisme canonique de downcast V1 est le test `is` suivi du raffinement de type dans le scope positif.
```text
Animal animal = ...;
if (animal is Dog) {
// animal est raffiné en Dog ici.
}
```
Lorsqu'un test du type runtime **exact** est nécessaire, `instanceof` est utilisé.
Saselang n'introduit pas pour le moment de syntaxe générale concurrente telle que `(Dog)value`, `value as Dog` ou `cast<Dog>(value)`. Une telle opération ne pourra être ajoutée que si un besoin distinct de `is`/`instanceof` + refinement est démontré et si son contrat d'échec est explicite.
## 24.5 API numérique du Core — V1 REQUIS — DIRECTION FIGÉE
Les conversions numériques sont exposées comme membres standard des primitives définis par le Core. Les noms de méthodes ne sont pas des mots-clés du langage.
Le compilateur connaît les règles nécessaires pour typer, vérifier, constant-fold et abaisser ces opérations vers Sase IR, mais l'inventaire exhaustif de la surface API appartient au Core.
Principe de nommage :
```text
toTarget()
conversion exacte et totale
tryToTarget()
conversion exacte pour la valeur courante, récupérable si impossible
roundToTarget()
perte de précision explicitement acceptée, conversion totale
tryRoundToTarget()
perte de précision explicitement acceptée, mais conversion pouvant échouer
saturateToTarget()
saturation explicite lorsqu'aucun arrondi supplémentaire n'est nécessaire
saturatingRoundToTarget()
arrondi + saturation explicitement annoncés
wrapToTarget()
wrapping entier explicite
```
Une forme n'existe que si elle apporte une sémantique observable différente d'une autre forme déjà disponible pour la paire source/destination.
Ainsi, lorsqu'un `toTarget()` exact et total existe, les variantes `tryToTarget()`, `saturateToTarget()` ou `wrapToTarget()` qui produiraient exactement le même résultat pour tout le domaine source sont absentes.
Règle de composition : deux opérations Core ne sont fusionnées dans un même nom que si leur séparation en opérations successives modifierait la sémantique, perdrait de l'information ou empêcherait d'exprimer le même contrat.
Lorsqu'une composition existante est strictement équivalente, aucun alias combiné n'est ajouté au Core.
## 24.6 `float -> integer` — V1 REQUIS — DIRECTION FIGÉE
Un flottant ne possède jamais un simple `toIntXX()` ou `toUintXX()`.
La conversion exacte stricte utilise :
```text
tryToIntXX()
tryToUintXX()
```
Elle réussit uniquement si la valeur source est finie, mathématiquement entière et représentable exactement dans le type destination. Sinon elle retourne `Result::Err(NumericConversionError(...))`.
Les politiques mathématiques de traitement de la partie fractionnaire restent des opérations Core séparées :
```text
floor()
ceil()
round()
truncate()
```
Elles se composent avec la conversion :
```text
value::floor()::tryToInt32()
value::ceil()::tryToInt32()
value::round()::tryToInt32()
value::truncate()::tryToInt32()
```
Saselang n'introduit donc pas les alias redondants `tryFloorToInt32()`, `tryCeilToInt32()`, `tryRoundToInt32()` ou `tryTruncateToInt32()` lorsque ces compositions possèdent exactement la même sémantique.
Les politiques de dépassement de domaine suivent la même règle :
```text
value::floor()::trySaturateToInt32()
value::round()::tryWrapToInt32()
```
`trySaturateToIntXX()` exige une valeur finie et mathématiquement entière, puis sature aux bornes du type destination. Les échecs qui ne relèvent pas de la plage, par exemple `NaN`, une infinité ou une valeur fractionnaire, restent des `NumericConversionError`.
`tryWrapToIntXX()` exige également une valeur finie et mathématiquement entière, puis applique le wrapping entier modulo `2^N` défini par Saselang.
Une méthode combinée reste admise lorsqu'une décomposition changerait le contrat ou perdrait l'information nécessaire. C'est notamment le cas de `saturatingRoundToFloat32()` pour certains narrowings flottants : un `roundToFloat32()` séparé pourrait échouer avant que la saturation ne puisse être appliquée.
Aucun comportement de conversion ne dépend d'un profil, d'un linter ou d'un backend.
## 24.7 `float -> float`, valeurs IEEE spéciales — V1 REQUIS — FIGÉ
Toute conversion de valeur flottante préserve les catégories sémantiques suivantes lorsque la destination est un type flottant Saselang :
```text
NaN -> NaN
+Infinity -> +Infinity
-Infinity -> -Infinity
+0 -> +0
-0 -> -0
```
Pour `NaN`, la conversion garantit uniquement que la destination reste `NaN`.
Elle ne garantit pas lors d'une conversion de valeur :
```text
payload NaN
quiet/signaling bit
signe du NaN
représentation binaire exacte
```
Ces propriétés relèvent d'un éventuel contrat binaire distinct ; `bitcast<T>` ne peut les préserver que lorsque ses propres contraintes, notamment de taille, sont satisfaites.
`tryToFloatXX()` traite `NaN` et les infinités comme des valeurs sémantiques représentables du type flottant destination. Le mot « exact » désigne ici l'exactitude de la valeur sémantique Saselang, pas l'identité bit-à-bit d'un payload NaN.
Pour une valeur finie :
```text
tryToFloatXX()
exige une représentation exacte
tryRoundToFloatXX()
accepte l'arrondi canonique
refuse une valeur finie hors domaine fini destination
saturatingRoundToFloatXX()
accepte l'arrondi canonique
sature une valeur finie hors domaine vers +/-Target::Max
```
Les infinités ne sont pas saturées puisqu'elles sont déjà des valeurs représentables du format flottant destination.
## 24.8 `NumericConversionError` — V1 REQUIS — NOM DE TRAVAIL / CODES FIGÉS
`NumericConversionError` est retenu comme nom de travail du `ResultError` Core utilisé par les conversions numériques récupérables.
Les catégories sémantiques V1 sont :
```text
NotFinite
NotIntegral
OutOfRange
Inexact
```
Sens :
```text
NotFinite
NaN ou +/-Infinity lorsqu'une valeur finie est requise
NotIntegral
float -> integer alors que la valeur n'est pas mathématiquement entière
OutOfRange
valeur mathématique hors du domaine de la destination
Inexact
valeur dans le domaine destination mais non représentable exactement
alors que l'opération exige l'exactitude
```
`NumericConversionError` reste volontairement généraliste et minimal. Il n'ajoute pas automatiquement :
```text
sourceType
targetType
sourceValue
timestamp
backend
roundingMode
```
au-delà de l'état commun fourni par `ResultError`.
Le nom concret et la représentation numérique interne des codes pourront encore être ajustés avant stabilisation publique, mais les catégories sémantiques ci-dessus font partie du contrat V1.
## 24.9 Réduction anti-doublon par paire — V1 REQUIS — FIGÉ
La matrice examine chaque paire `Source -> Target` et n'expose que les opérations dont le comportement peut réellement être distingué sur le domaine source.
Ainsi, pour une paire `float -> integer` dont toutes les valeurs finies mathématiquement entières sont déjà dans le domaine signé destination, `trySaturateToTarget()` et `tryWrapToTarget()` seraient des doublons de `tryToTarget()` et n'existent pas.
Pour une destination non signée, les valeurs négatives suffisent généralement à rendre les politiques checked, saturating et wrapping distinctes.
La matrice exhaustive en annexe est normative sur ce point.
## 24.10 Disponibilité Core et target — V1 REQUIS — FIGÉ EN PRINCIPE
Les conversions fondamentales de la matrice sont des capacités Core obligatoires dès lors que les types source et destination sont supportés par le target.
L'absence d'une instruction matérielle native ne justifie pas l'absence d'une conversion si une émulation logicielle conforme est raisonnablement possible.
Un target réduit peut ne pas supporter un type fondamental donné. Dans ce cas l'indisponibilité porte sur le type/capacité fondamentale, pas sur une sélection arbitraire de ses méthodes de conversion.
Voir également le chapitre 40.
## 24.11 Matrice exhaustive — ANNEXE NORMATIVE EN COURS DE GEL
L'inventaire source → destination des opérations de conversion est maintenu séparément afin d'éviter les oublis, doublons et incohérences.
Voir :
```text
annexes/A-numeric-conversions.md
```
L'annexe doit lister le maximum de possibilités sémantiquement distinctes avant réduction finale de l'API Core.
---