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

9.4 KiB

24. Casts et conversions

24.1 Principe général — V1 REQUIS — FIGÉ EN PRINCIPE

Saselang distingue explicitement :

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.

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.

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.

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 :

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 :

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 :

floor()
ceil()
round()
truncate()

Elles se composent avec la conversion :

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 :

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 :

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 :

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 :

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 :

NotFinite
NotIntegral
OutOfRange
Inexact

Sens :

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 :

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 :

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.