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.