Clauses de représentation et fonctionnalités dépendantes de la mise en oeuvre
Cette page décrit les clauses de représentation, certaines fonctionnalités dépendantes de l'implémentation et d'autres fonctionnalités utilisées dans la programmation système.
Clauses de représentation
Les clauses de représentation précisent comment les types de langage de programmation doivent être cartographiés sur la machine sous-jacente. Elles peuvent être fournies pour fournir une représentation plus efficace ou pour s'interfacer avec des fonctionnalités étant en dehors du domaine du langage (par exemple, le matériel périphérique).
clause_de_representation ::= clause_de_representation_de_type | clause_adresse clause_de_representation_de_type ::= clause_de_longueur | clause_de_representation_d_enumeration | clause_de_representation_d_enregistrement |
Une clause de représentation de type s'applique soit à un type, soit à un premier sous-type nommé (c'est-à-dire à un sous-type déclaré par une déclaration de type, le type de base étant donc anonyme). Une telle clause de représentation s'applique à tous les objets ayant ce type ou ce premier sous-type nommé. Au plus une clause de représentation d'énumération ou d'enregistrement est autorisée pour un type donné : une clause de représentation d'énumération n'est autorisée que pour un type d'énumération ; une clause de représentation d'enregistrement, uniquement pour un type d'enregistrement. (D'un autre côté, plusieurs clauses de longueur peuvent être fournies pour un type donné ; de plus, une clause de longueur et une clause de représentation d'énumération ou d'enregistrement peuvent être fournies.) Une clause de longueur est la seule forme de clause de représentation autorisée pour un type dérivé d'un type parent qui possède des sous-programmes dérivables (définis par l'utilisateur).
Une clause d'adresse s'applique soit à un objet, soit à un sous-programme, un paquet ou une unité de tâche, soit à une entrée. Au plus une clause d'adresse est autorisée pour chacune de ces entités.
Une clause de représentation et la déclaration de l'entité à laquelle la clause s'applique doivent toutes deux apparaître immédiatement dans la même partie déclarative, spécification de paquet ou spécification de tâche; la déclaration doit apparaître avant la clause. En l'absence d'une clause de représentation pour une déclaration donnée, une représentation par défaut de cette déclaration est déterminée par l'implémentation. Une telle détermination par défaut se produit au plus tard à la fin de la partie déclarative, de la spécification de paquet ou de la spécification de tâche l'entourant immédiatement. Pour une déclaration donnée dans une partie déclarative, cette détermination par défaut se produit avant tout corps inclus.
Dans le cas d'un type, certaines occurrences de son nom impliquent que la représentation du type doit déjà avoir été déterminée. Par conséquent, ces occurrences forcent la détermination par défaut de tout aspect de la représentation n'étant pas déjà déterminé par une clause de représentation de type antérieure. Cette détermination par défaut est également forcée par des occurrences similaires du nom d'un sous-type du type, ou du nom de tout type ou sous-type ayant des sous-composantes du type. Une occurrence forcée est toute occurrence autre que dans une déclaration de type ou de sous-type, une spécification de sous-programme, une déclaration d'entrée, une déclaration de constante différée, un pragma ou une clause de représentation pour le type lui-même. Dans tous les cas, une occurrence dans une expression est toujours forcée.
Une clause de représentation pour une entité donnée ne doit pas apparaître après une occurrence du nom de l'entité si cette occurrence force une détermination par défaut de la représentation pour l'entité. 8 Des restrictions similaires existent pour les clauses d'adresse. Pour un objet, toute occurrence de son nom (après la déclaration de l'objet) est une occurrence forcée. Pour un sous-programme, un paquet, une unité de tâche ou une entrée, toute occurrence d'un attribut de représentation d'une telle entité est une occurrence forcée.
L'effet de l'élaboration d'une clause de représentation est de définir les aspects correspondants de la représentation.
L'interprétation de certaines des expressions apparaissant dans les clauses de représentation dépend de l'implémentation, par exemple, les expressions spécifiant des adresses. Une implémentation peut limiter son acceptation des clauses de représentation à celles qui peuvent être traitées simplement par le matériel sous-jacent. Si une clause de représentation est acceptée par une implémentation, le compilateur doit garantir que l'effet net du programme n'est pas modifié par la présence de la clause, à l'exception des clauses d'adresse et des parties du programme interrogeant les attributs de représentation. Si un programme contient une clause de représentation qui n'est pas acceptée, le programme est illégal.
Alors qu'une clause de représentation est utilisée pour imposer certaines caractéristiques de cartographie d'une entité sur la machine sous-jacente, les pragma peuvent être utilisés pour fournir à une implémentation des critères pour la sélection d'un tel que la cartographie. Le pragma PACK spécifie que la minimisation d'entreposage doit être le critère principal lors de la sélection de la représentation d'un type d'enregistrement ou de tableau. Son format est la suivante :
pragma PACK (type_simple_nom); |
Le paquet signifie que les écarts entre les zones d'entreposage allouées aux composantes consécutives doivent être minimisés. Il n'a cependant pas besoin d'affecter la cartographie de chaque composante sur l'entreposage. Cette cartographie peut lui-même être influencé par un pragma (ou contrôlé par une clause de représentation) pour la composante ou le type de composante. La position d'un pragma PACK et les restrictions sur le type nommé sont régies par les mêmes règles que pour une clause de représentation ; en particulier, le pragma doit apparaître avant toute utilisation d'un attribut de représentation de l'entité de paquet.
Le pragma PACK est le seul pragma de représentation défini par le langage. Des pragma de représentation supplémentaires peuvent être fournis par une implémentation. (contrairement aux clauses de représentation, un pragma n'étant pas accepté par l'implémentation est ignoré.)
Remarque : aucune clause de représentation n'est autorisée pour un type formel générique.
Clauses de longueur
Une clause de longueur spécifie une quantité d'entreposage associée à un type.
clause_de_longueur ::= for attribut use expression_simple; |
L'expression doit être d'un type numérique et est évaluée lors de l'élaboration de la clause de longueur (sauf s'il s'agit d'une expression statique). Le préfixe de l'attribut doit désigner soit un type, soit un premier sous-type nommé. Le préfixe est appelé T dans ce qui suit. Les seuls désignateurs d'attribut autorisés dans une clause de longueur sont SIZE, STORAGE_SIZE et SMALL. L'effet de la clause de longueur dépend du désignateur d'attribut :
- Spécification de taille : T'SIZE : L'expression doit être une expression statique d'un type entier. La valeur de l'expression spécifie une limite supérieure pour le nombre de bits à allouer aux objets du type ou du premier sous-type nommé T. La spécification de taille doit prévoir suffisamment d'espace d'entreposage pour accueillir chaque valeur autorisée de ces objets. Une spécification de taille pour un type composite peut affecter la taille des espaces entre les zones de stockage allouées aux composantes consécutifs. En revanche, elle n'a pas besoin d'affecter la taille de la zone d'entreposage allouée à chaque composant. La spécification de taille n'est autorisée que si les contraintes sur T et sur ses sous-composantes (le cas échéant) sont statiques. Dans le cas d'un type de tableau sans contrainte, les sous-types d'index doivent également être statiques.
- Spécification de la taille de la collection : T'STORAGE_SIZE : Le préfixe T doit désigner un type d'accès. L'expression doit être d'un type entier (mais n'a pas besoin d'être statique); sa valeur spécifie le nombre d'unités d'entreposage à réserver pour la collection, c'est-à-dire l'espace d'entreposage nécessaire pour contenir tous les objets désignés par des valeurs du type d'accès et par des valeurs d'autres types dérivés du type d'accès, directement ou indirectement. Cette forme de clause de longueur n'est pas autorisée pour un type dérivé d'un type d'accès.
- Spécification d'entreposage pour une activation de tâche : T'STORAGE_SIZE : Le préfixe T doit désigner un type de tâche. L'expression doit être de type entier (mais n'a pas besoin d'être statique); sa valeur spécifie le nombre d'unités d'entreposage à réserver pour une activation (et non le code) d'une tâche de ce type.
- Spécification de small pour un type à virgule fixe : T'SMALL : Le préfixe T doit désigner le premier sous-type nommé d'un type à virgule fixe. L'expression doit être une expression statique de type réel ; sa valeur ne doit pas être supérieure au delta du premier sous-type nommé. L'effet de la clause de longueur est d'utiliser cette valeur de small pour la représentation des valeurs du type de base à virgule fixe. (La clause de longueur affecte également la quantité de stockage pour les objets ayant ce type.)
Remarques : une spécification de taille est autorisée pour un type d'accès, de tâche ou de virgule fixe, qu'une autre forme de clause de longueur soit également donnée pour le type ou non.
Ce qui est considéré comme faisant partie d'entreposage réservé à une collection ou à l'activation d'une tâche dépend de l'implémentation. Le contrôle offert par les clauses de longueur est donc relatif aux conventions d'implémentation. Par exemple, le langage ne définit pas si l'entreposage réservé à l'activation d'une tâche inclut le stockage nécessaire à la collection associée à un type d'accès déclaré dans le corps de la tâche. Il ne définit pas non plus la méthode d'allocation pour les objets désignés par des valeurs d'un type d'accès. Par exemple, l'espace alloué pourrait être sur une pile; alternativement, un schéma d'allocation dynamique général ou un entreposage fixe pourrait être utilisé.
Les objets alloués dans une collection n'ont pas besoin d'avoir la même taille si le type désigné est un type de tableau sans contrainte ou un type sans contrainte avec discriminants. Notez également que l'allocateur lui-même peut nécessiter de l'espace pour les tables et les liens internes. Par conséquent, une clause de longueur pour la collection d'un type d'accès ne donne pas toujours un contrôle précis sur le nombre maximal d'objets alloués.
Exemples :
- -- déclarations supposées :
-
- type MEDIUM is range 0 .. 65000;
- type SHORT is delta 0.01 range -100.0 .. 100.0;
- type DEGREE is delta 0.1 range -360.0 .. 360.0;
-
- BYTE : constant := 8;
- PAGE : constant := 2000;
-
- -- clauses de longueur :
-
- for COLOR'SIZE use 1*BYTE;
- for MEDIUM'SIZE use 2*BYTE;
- for SHORTSIZE use 15;
-
- for CAR_NAME'STORAGE_SIZE use -- environ 2000 voitures
- 2000 *((CAR'SIZE/SYSTEM.STORAGE_UNIT) + 1);
-
- for KEYBOARD_DRIVER'STORAGE_SIZE use UPAGE;
-
- for DEGREE'SMALL use 360.0/2**(SYSTEM.ST0RAGE_UNIT - 1);
Remarques sur les exemples : Dans la clause de longueur pour SHORT, quinze bits constituent le minimum nécessaire, puisque la définition de type requiert SHORT'SMALL = 2.0**(-7) et SHORT"MANTISSA = 14. La clause de longueur pour DEGREE force les numéros de modèle à couvrir exactement l'intervalle du type.
Clauses de représentation d'énumération
Une clause de représentation d'énumération spécifie les codes internes des littéraux du type d'énumération nommé dans la clause :
clause_de_representation_d_enumeration ::= for type_simple_nom use agregat; |
L'agrégat utilisé pour spécifier cette cartographie est écrit sous la forme d'un agrégat unidimensionnel, pour lequel le sous-type d'index est le type d'énumération et le type de composant est universal_integer. Tous les littéraux du type d'énumération doivent être fournis avec des codes entiers distincts, et tous les choix et valeurs de composante donnés dans l'agrégat doivent être statiques. Les codes entiers spécifiés pour le type d'énumération doivent satisfaire la relation de classement prédéfinie du type.
Exemple :
Remarques : les attributs SUCC, PRED et POS sont définis même pour les types d'énumération avec une représentation non contiguë ; leur définition correspond à la déclaration de type (logique) et n'est pas affectée par la clause de représentation d'énumération. Dans l'exemple, en raison de la nécessité d'éviter les valeurs omises, ces fonctions risquent d'être implémentées moins efficacement qu'elles ne pourraient l'être en l'absence d'une clause de représentation. Des considérations similaires s'appliquent lorsque de tels types sont utilisés pour l'indexation.
Clauses de représentation d'enregistrement
Une clause de représentation d'enregistrement spécifie la représentation d'entreposage des enregistrements, c'est-à-dire l'ordre, la position et la taille des composantes d'enregistrement (y compris les discriminants, le cas échéant) :
clause_de_representation_d_enregistrement ::= for type_simple_nom use record [clause_d_alignement] {clause_de_composante} end record; clause_d_alignement ::= at mod expression_simple_statique; clause_de_composante ::= nom_composantes at expression_simple_statique range intervalle_statique; |
L'expression simple donnée après les mots réservés at mod dans une clause d'alignement, ou après le mot réservé at dans une clause de composante, doit être une expression statique de type entier. Si les limites de l'intervalle d'une clause de composante sont définies par des expressions simples, alors chaque limite de la plage doit être définie par une expression statique de type entier, mais les deux limites n'ont pas besoin d'avoir le même type entier.
Une clause d'alignement force chaque enregistrement du type donné à être alloué à une adresse de départ étant un multiple de la valeur de l'expression donnée (c'est-à-dire que l'adresse modulo l'expression doit être zéro). Une implémentation peut placer des restrictions sur les alignements autorisés.
Une clause de composante spécifie l'emplacement d'entreposage d'une composante, par rapport au début de l'enregistrement. L'entier défini par l'expression statique d'une clause de composante est une adresse relative exprimée en unités d'entreposage. L'intervalle définit les positions de bits de l'emplacement de l'entreposage, par rapport à l'unité d'entreposage. La première unité d'entreposage d'un enregistrement est numérotée zéro. Le premier bit d'une unité de stockage est numéroté zéro. L'ordre des bits dans une unité de stockage dépend de la machine et peut s'étendre aux unités de stockage adjacentes. (Pour une machine spécifique, la taille en bits d'une unité d'entreposage est donnée par le numéro nommé SYSTEM.STORAGE_UNIT dépendant de la configuration.) La question de savoir si une composante est autorisé à chevaucher une limite d'entreposage et, si oui, comment, est définie par l'implémentation.
Au plus une clause de composante est autorisée pour chaque composante du type d'enregistrement, y compris pour chaque discriminant (des clauses de composante peuvent être données pour certains, tous ou aucun des composantes). Si aucune clause de composante n'est donnée pour un composante, le choix de l'emplacement d'entreposage de la composante est laissé au compilateur. Si des clauses de composante sont données pour tous les composantes, la clause de représentation d'enregistrement spécifie complètement la représentation du type d'enregistrement et doit être respectée exactement par le compilateur.
Les emplacements d'entreposage dans une variante d'enregistrement ne doivent pas se chevaucher, mais le chevauchement de l'entreposage pour des variantes distinctes est autorisé. Chaque clause de composante doit prévoir suffisamment d'espace d'entreposage pour accueillir chaque valeur autorisée de la composante. Une clause de composante n'est autorisée pour une composante que si une contrainte sur cette composante ou sur l'un de ses sous-composantes est statique.
Une implémentation peut générer des noms désignant des composantes dépendants de l'implémentation (par exemple, une composante contenant le déplacement d'une autre composante). De tels noms dépendants de l'implémentation peuvent être utilisés dans des clauses de représentation d'enregistrement (ces noms ne doivent pas nécessairement être des noms simples ; par exemple, ils peuvent être des attributs dépendants de l'implémentation).
Exemple :
- WORD : constant := 4; -- l'unité d'entreposage est l'octet, 4 octets par mot
-
- type STATE is (A, M, W, P);
- type MODE is (FIX, DEC, EXP, SIGNIF);
-
- type BYTE_MASK is array (0 .. 7) of BOOLEAN
- type STATE_MASK is array (STATE) of BOOLEAN
- type MODE_MASK is array (MODE) of BOOLEAN
-
- type PROGRAM_STATUS_WORD is
- record
- SYSTEM_MASK : BYTE_MASK;
- PROTECTION_KEY : INTEGER range 0 .. 3;
- MACHINE_STATE : STATE_MASK;
- INTERRUPT_CAUSE : INTERRUPTION_CODE;
- ILC : INTEGER range 0 .. 3;
- CC : INTEGER range 0 .. 3;
- PROGRAM_MASK : M0DE_MASK;
- INST__ADDRESS : ADDRESS;
- end record;
-
- for PROGRAM_STATUS_WORD use
- record at mod 8;
- SYSTEM_MASK at 0*WORD range 0 .. 7;
- PROTECTION_KEY at 0*WORD range 10 ..11; -- utilise les bits 8, 9
- MACHINE_STATE at 0*WORD range 12 .. 15;
- INTERRUPT_CAUSE at 0*W0RD range 16 .. 31;
- ILC at 1*WORD range 0 .. 1; -- deuxième mot
- CC at 1*WORD range 2 .. 3:
- PROGRAM_MASK at 1*WORD range 4 .. 7;
- INST_ADDRESS at 1*WORD range 8 .. 31;
- end record;
-
- for PROGRAM_STATUS_WORD'SIZE use 8*SYSTEM.STORAGE_UNIT;
Remarque sur l'exemple : la clause de représentation d'enregistrement définit la disposition de l'enregistrement. La clause de longueur garantit que huit unités d'entreposage exactement sont utilisées.
Clauses d'adresse
Une clause d'adresse spécifie une adresse requise dans l'entreposage pour une entité :
clause_adresse ::= for nom_simple use at expression_simple; |
L'expression donnée après le mot réservé at doit être du type ADDRESS défini dans le paquet SYSTEM; ce paquet doit être nommé par une clause with s'appliquant à l'unité de compilation dans laquelle la clause address apparaît. Les conventions définissant l'interprétation d'une valeur de type ADDRESS comme une adresse, comme un niveau d'interruption ou quoi que ce soit d'autre, dépendent de l'implémentation. La nature autorisée du nom simple et la signification de l'adresse correspondante sont les suivantes :
- Nom d'un objet : l'adresse est celle requise pour l'objet (variable ou constante).
- Nom d'un sous-programme, d'un paquetage ou d'une unité de tâche : l'adresse est celle requise pour le code machine associé au corps de l'unité de programme.
- Nom d'une entrée unique : l'adresse spécifie une interruption matérielle à laquelle l'entrée unique doit être liée.
Si le nom simple est celui d'une seule tâche, la clause d'adresse est censée faire référence à l'unité de tâche et non à l'objet de tâche. Dans tous les cas, la clause d'adresse n'est légale que si exactement une déclaration avec cet identifiant apparaît auparavant, immédiatement dans la même partie déclarative, spécification de paquet ou spécification de tâche. Un nom déclaré par une déclaration de renommage n'est pas autorisé comme nom simple.
Les clauses d'adresse ne doivent pas être utilisées pour obtenir des superpositions d'objets ou des superpositions d'unités de programme. Une interruption donnée ne doit pas non plus être liée à plus d'une entrée. Tout programme utilisant des clauses d'adresse pour obtenir de tels effets est erroné.
Exemple :
Remarques : les règles ci-dessus impliquent que si deux sous-programmes se surchargent mutuellement et sont visibles à un moment donné, une clause d'adresse pour l'un d'eux n'est pas légale à ce stade. De même, si une spécification de tâche déclare des entrées qui se surchargent mutuellement, elles ne peuvent pas être des entrées d'interruption. La syntaxe n'autorise pas une clause d'adresse pour une unité de bibliothèque. Une implémentation peut fournir des pragma pour la spécification des superpositions de programmes.
Interruptions
Une clause d'adresse donnée pour une entrée associe l'entrée à un périphérique susceptible de provoquer une interruption; une telle entrée est appelée dans cette section entrée d'interruption. Si des informations de contrôle sont fournies lors d'une interruption, elles sont transmises à une entrée d'interruption associée sous la forme d'un ou plusieurs paramètres de mode in ; seuls les paramètres de ce mode sont autorisés.
Une interruption agit comme un appel d'entrée émis par une tâche matérielle dont la priorité est supérieure à la priorité du programme principal, et également supérieure à la priorité de toute tâche définie par l'utilisateur (c'est-à-dire toute tâche dont le type est déclaré par une unité de tâche dans le programme). L'appel d'entrée peut être un appel d'entrée ordinaire, un appel d'entrée temporisé ou un appel d'entrée conditionnel, selon le type d'interruption et l'implémentation.
Si une instruction select contient à la fois une alternative terminate et une alternative accept pour une entrée d'interruption, une implémentation peut alors imposer des exigences supplémentaires pour la sélection de l'alternative terminate.
Exemple :
Remarques : les appels d'entrée d'interruption doivent uniquement avoir la sémantique décrite ci-dessus; ils peuvent être implémentés en faisant exécuter directement par le matériel les instructions d'acceptation appropriées.
Les interruptions en file d'attente correspondent aux appels d'entrée ordinaires. Les interruptions perdues si elles ne sont pas immédiatement traitées correspondent aux appels d'entrée conditionnels. Il est une conséquence des règles de priorité qu'une instruction d'acceptation exécutée en réponse à une interruption a la priorité sur les tâches ordinaires définies par l'utilisateur et peut être exécutée sans invoquer au préalable une action de planification.
L'un des effets possibles d'une clause d'adresse pour une entrée d'interruption est de spécifier la priorité de l'interruption (directement ou indirectement). Les appels directs à une entrée d'interruption sont autorisés.
Changement de représentation
Une seule clause de représentation est autorisée pour un type donné et un aspect donné de sa représentation. Par conséquent, si une représentation alternative est nécessaire, il est nécessaire de déclarer un deuxième type, dérivé du premier, et de spécifier une représentation différente pour le deuxième type.
Exemple :
- -- PACKED_DESCRIPTOR et DESCRIPTOR sont deux types différents ayant des caractéristiques identiques, hormis leur représentation
- type DESCRIPTOR is
- record
- -- composantes d'un descripteur
- end record;
-
- type PACKED_DESCRIPTOR is new DESCRIPTOR;
-
- for PACKED_DESCRIPTOR use
- record
- -- clauses de composantes pour certains ou pour tous les composantes
- end record;
Le changement de représentation peut désormais être réalisé par affectation avec des conversions de type explicites :
- D : DESCRIPTOR;
- P : PACKED_DESCRIPTOR;
-
- P := PACKED_DESCRIPTOR(D); -- paquet D
- D := DESCRIPTOR(P); -- dépaqueté P
Le système de paquets
Pour chaque implémentation, il existe un paquet de bibliothèque prédéfini appelé SYSTEM incluant les définitions de certaines caractéristiques dépendantes de la configuration. La spécification du paquet SYSTEM dépend de l'implémentation et doit être donnée dans l'annexe F. La partie visible de ce paquet doit contenir au moins les déclarations suivantes.
- package SYSTEM is
- type ADDRESS is implementation_defined:
- type NAME is implementation_defined_enumeration_type;
-
- SYSTEM_NAME : constant NAME := implementation_defined:
-
- STORAGE_UNIT : constant := implementation_defined;
- MEMORY_SIZE : constant := implementation_defined;
-
- -- Numéros nommés dépendants du système :
- MIN_INT : constant := implementation_defined;
- MAX_INT : constant := implementation_defined;
- MAX_DIGITS : constant := implementation_defined;
- MAX_MANTISSA : constant := implementation_defined;
- FINE_DELTA : constant := implementation_defined;
- TICK : constant := implementation_defined;
-
- -- Autres déclarations dépendantes du système
- subtype PRIORITY is INTEGER range implementation_defined;
-
- end SYSTEM;
Le type ADDRESS est le type des adresses fournies dans les clauses d'adresse; c'est également le type du résultat fourni par l'attribut ADDRESS. Les valeurs du type d'énumération NAME sont les noms des configurations de machines alternatives gérées par l'implémentation; l'une d'entre elles est la constante SYSTEM_NAME. Le nombre nommé STORAGE_UNIT est le nombre de bits par unité d'entreposage ; le nombre nommé MEMORY_SIZE est le nombre d'unités d'entreposage disponibles dans la configuration ; ces nombres nommés sont de type universal_integer.
Une forme alternative du paquet SYSTEM, avec des valeurs données pour SYSTEM_NAME, STORAGE_UNIT et MEMORY_SIZE, peut être obtenue au moyen des pragma correspondants. Ces pragma ne sont autorisés qu'au début d'une compilation, avant la première unité de compilation (le cas échéant) de la compilation.
pragma SYSTEM_NAME(enumeration_litterale); |
L'effet du pragma ci-dessus est d'utiliser le littéral d'énumération avec l'identifiant spécifié pour la définition de la constante SYSTEM_NAME. Ce pragma n'est autorisé que si l'identifiant spécifié correspond à l'un des littéraux du type NAME.
pragma STORAGE_UNIT(litteral_numerique); |
L'effet du pragma ci-dessus est d'utiliser la valeur du littéral numérique spécifié pour la définition du nombre nommé STORAGE_UNIT :
pragma MEMORY_SIZE(numeric_literal); |
L'effet du pragma ci-dessus est d'utiliser la valeur du littéral numérique spécifié pour la définition du nombre nommé MEMORY_SIZE.
La compilation de l'un de ces pragma entraîne une recompilation implicite du paquet SYSTEM. Par conséquent, toute unité de compilation nommant SYSTEM dans sa clause de contexte devient obsolète après cette recompilation implicite. Une implémentation peut imposer des limitations supplémentaires à l'utilisation de ces pragma. Par exemple, une implémentation peut les autoriser uniquement au début de la première compilation, lors de la création d'une nouvelle bibliothèque de programmes.
Remarque : il est une conséquence des règles de visibilité qu'une déclaration donnée dans le paquet SYSTEM n'est pas visible dans une unité de compilation à moins que ce paquet ne soit mentionné par une clause with s'appliquant (directement ou indirectement) à l'unité de compilation.
Nombres nommés dépendants du système
Dans le paquet SYSTEM, les nombres nommés suivants sont déclarés. Les nombres FINE_DELTA et TICK sont de type universal_real; les autres sont de type universal_integer.
Nom | Description |
---|---|
MIN_INT | La plus petite valeur (la plus négative) de tous les types d'entiers prédéfinis. |
MAX_INT | La valeur la plus grande (la plus positive) de tous les types d'entiers prédéfinis. |
MAX_DIGITS | La plus grande valeur autorisée pour le nombre de chiffres décimaux significatifs dans une contrainte à virgule flottante. |
MAX_MANTISSA | Le plus grand nombre possible de chiffres binaires dans la mantisse des numéros de modèle d'un sous-type à virgule fixe. |
FINE_DELTA | Le plus petit delta autorisé dans une contrainte à point fixe qui a la contrainte de plage -1,0 .. 1,0. |
TICK | La période de base de l'horloge, en secondes. |
Attributs de représentation
Les valeurs de certaines caractéristiques dépendantes de l'implémentation peuvent être obtenues en interrogeant les attributs de représentation appropriés. Ces attributs sont décrits ci-dessous.
Pour tout objet, unité de programme, étiquette ou entrée X :
Attribut | Description |
---|---|
X'ADDRESS | Renvoie l'adresse de la première des unités de stockage allouées à X. Pour un sous-programme, un paquet, une unité de tâche ou un libellé, cette valeur fait référence au code machine associé au corps ou à l'instruction correspondant. Pour une entrée pour laquelle une clause d'adresse a été donnée, la valeur fait référence à l'interruption matérielle correspondante. La valeur de cet attribut est du type ADDRESS défini dans le paquet SYSTEM. |
Pour tout type ou sous-type X, ou pour tout objet X :
Attribut | Description |
---|---|
X'SIZE | Appliqué à un objet, il donne le nombre de bits alloués pour contenir l'objet. Appliqué à un type ou à un sous-type, il donne le nombre minimal de bits requis par l'implémentation pour contenir tout objet possible de ce type ou de ce sous-type. La valeur de cet attribut est de type universal_integer. |
Pour les deux attributs de représentation ci-dessus, si le préfixe est le nom d'une fonction, l'attribut est compris comme un attribut de la fonction (et non du résultat de l'appel de la fonction). De même, si le type du préfixe est un type d'accès, l'attribut est compris comme un attribut du préfixe (et non de l'objet désigné : les attributs de ce dernier peuvent être écrits avec un préfixe se terminant par le mot réservé all).
Pour toute composante C d'un objet record R :
Attribut | Description |
---|---|
R.C'POSITION | Renvoie le déplacement, à partir du début de la première unité d'entreposage occupée par l'enregistrement, de la première des unités d'entreposage occupées par C. Ce déplacement est mesuré en unités d'entreposage. La valeur de cet attribut est de type universal_integer. |
R.C'FIRST_BIT | Renvoie le déplacement, à partir du début de la première unité d'entreposage occupée par C, du premier bit occupé par C. Ce déplacement est mesuré en bits. La valeur de cet attribut est de type universal_integer. |
R.C'LAST_BIT | Renvoie le déplacement, à partir du début de la première des unités de stockage occupées par C, du dernier bit occupé par C. Ce déplacement est mesuré en bits. La valeur de cet attribut est de type universal_integer. |
Pour tout type ou sous-type d'accès T :
Attribut | Description |
---|---|
T'STORAGE_SIZE | Donne le nombre total d'unités d'entreposage réservées à la collection associée au type de base de T. La valeur de cet attribut est de type universal_integer. |
Pour tout type de tâche ou objet de tâche T :
Attribut | Description |
---|---|
T'STORAGE_SIZE | Renvoie le nombre d'unités d'entreposage réservées pour chaque activation d'une tâche de type T ou pour l'activation de l'objet tâche T. La valeur de cet attribut est de type universal_integer. |
Remarques : Pour un objet tâche X, l'attribut X'SIZE donne le nombre de bits utilisés pour contenir l'objet X, tandis que X'STORAGE_SIZE donne le nombre d'unités d'entreposage allouées pour l'activation de la tâche désignée par X. Pour un paramètre formel X, si le passage de paramètre est réalisé par copie, alors l'attribut X ADDRESS donne l'adresse de la copie locale ; si le passage de paramètre est réalisé par référence, alors l'adresse est celle du paramètre actuel.
Attributs de représentation des types réels
Pour chaque type ou sous-type réel T, les attributs suivants dépendants de la machine sont définis, n'étant pas liés aux numéros de modèle. Les programmes utilisant ces attributs peuvent ainsi exploiter des propriétés allant au-delà des propriétés minimales associées au type numérique. Des précautions doivent donc être prises lors de l'utilisation de ces attributs dépendants de la machine si l'on veut garantir la portabilité.
Pour les types à virgule flottante et à virgule fixe :
Attribut | Description |
---|---|
T'MACHINE_ROUNDS | Renvoie la valeur TRUE si chaque opération arithmétique prédéfinie sur les valeurs du type de base T renvoie un résultat exact ou effectue un arrondi ; renvoie la valeur FALSE dans le cas contraire. La valeur de cet attribut est du type prédéfini BOOLEAN. |
T'MACHINE_OVERFLOWS | Renvoie la valeur TRUE si chaque opération prédéfinie sur les valeurs du type de base T fournit un résultat correct ou génère l'exception NUMERIC_ERROR dans les situations de dépassement; renvoie la valeur FALSE dans le cas contraire. La valeur de cet attribut est du type prédéfini BOOLEAN. |
Pour les types à virgule flottante, les attributs suivants fournissent les caractéristiques de la représentation machine sous-jacente, en termes de forme canonique :
Attribut | Description |
---|---|
T'MACHINE_RADIX | Renvoie la valeur du base utilisé par la représentation machine du type de base de T. La valeur de cet attribut est de type universal_integer. |
T'MACHINE_MANTISSA | Renvoie le nombre de chiffres de la mantisse pour la représentation machine du type de base de T (les chiffres sont des chiffres étendus compris entre 0 et T'MACHINE_RADIX-1). La valeur de cet attribut est de type universal_integer. |
T'MACHINE_EMAX | Donne la plus grande valeur d'exposant pour la représentation machine du type de base de T. La valeur de cet attribut est de type universal_integer. |
T'MACHINE_EMIN | Donne la plus petite valeur (la plus négative) de l'exposant pour la représentation machine du type de base de T. La valeur de cet attribut est de type universal_integer. |
Pour de nombreuses machines, le plus grand nombre de machines représentables de type F est presque&nbps;:
- (F'MACHINE_RADIX)**(F'MACHINE_EMAX),
et le plus petit nombre positif représentable est :
- F'MACHINE_RADIX**(F'MACHINE_EMIN-1)
Insertions de code machine
Une insertion de code machine peut être réalisée par un appel à une procédure dont la séquence d'instructions contient des instructions de code.
code_declaration ::= marque_type'enregistrement_agregat; |
Une instruction de code n'est autorisée que dans la séquence d'instructions d'un corps de procédure. Si un corps de procédure contient des instructions de code, alors dans ce corps de procédure, la seule forme d'instruction autorisée est une instruction de code (étiquetée ou non), les seuls éléments déclaratifs autorisés sont les clauses use et aucun gestionnaire d'exception n'est autorisé (les commentaires et les pragma sont autorisés comme d'habitude).
Chaque instruction machine apparaît comme un agrégat d'enregistrement d'un type d'enregistrement définissant l'instruction correspondante. Le type de base de la marque de type d'une instruction de code doit être déclaré dans le paquet de bibliothèque prédéfini appelé MACHINE_CODE ; ce paquet doit être nommé par une clause with s'appliquant à l'unité de compilation dans laquelle l'instruction de code se produit. Une implémentation n'est pas obligée de fournir un tel paquet.
Une implémentation est autorisée à imposer d'autres restrictions sur les agrégats d'enregistrement autorisés dans les instructions de code. Par exemple, elle peut exiger que les expressions contenues dans de tels agrégats soient des expressions statiques.
Une implémentation peut fournir des pragma dépendants de la machine spécifiant les conventions de registre et les conventions d'appel.
Exemple :
Interface avec d'autres langages
Un sous-programme écrit dans un autre langage peut être appelé à partir d'un programme Ada à condition que toute la communication soit réalisée via des paramètres et des résultats de fonction. Un pragma de la forme :
pragma INTERFACE (nom_de_langage, nom_sous_programme); |
doit être donné pour chaque sous-programme de ce type; un nom de sous-programme peut représenter plusieurs sous-programmes surchargés. Ce pragma est autorisé à la place d'un élément déclaratif et doit s'appliquer dans ce cas à un sous-programme déclaré par un élément déclaratif antérieur de la même partie déclarative ou spécification de paquet. Le pragma est également autorisé pour une unité de bibliothèque ; dans ce cas, le pragma doit apparaître après la déclaration du sous-programme et avant toute unité de compilation ultérieure. Le pragma spécifie l'autre langage (et donc les conventions d'appel) et informe le compilateur qu'un module objet sera fourni pour le sous-programme correspondant. Un corps n'est pas autorisé pour un tel sous-programme (même pas sous la forme d'un stub de corps) puisque les instructions du sous-programme sont écrites dans un autre langage.
Cette capacité n'a pas besoin d'être fournie par toutes les implémentations. Une implémentation peut imposer des restrictions sur les formes et les emplacements autorisés des paramètres et des appels.
Exemple :
Remarques : Les conventions utilisées par d'autres processeurs de langage appelant des programmes Ada ne font pas partie de la définition du langage de programmation Ada. Ces conventions doivent être définies par ces autres processeurs de langage de programmation.
Le pragma INTERFACE n'est pas défini pour les sous-programmes génériques.
Programmation non contrôlée
Les sous-programmes de bibliothèque génériques prédéfinis UNCHECKED_DEALLOCATION et UNCHECKED_CONVERSION sont utilisés pour la désallocation d'entreposage non contrôlée et pour les conversions de type non contrôlées.
Libération de l'espace d'entreposage non contrôlée
La libération de l'espace d'entreposage non contrôlée d'un objet désigné par une valeur d'un type d'accès est obtenue par un appel à une procédure obtenue par instanciation de la procédure générique UNCHECKED_DEALLOCATION. Par exemple :
- procedure FREE is new UNCHECKED_DEALLOCATION (object_type_name, access_type_name);
Une telle procédure FREE a l'effet suivant :
- après l'exécution de FREE (X), la valeur de X est nulle ;
- FREE (X), lorsque X est déjà égal à null, n'a aucun effet ;
- FREE (X), lorsque X n'est pas égal à null, indique que l'objet désigné par X n'est plus nécessaire et que l'espace d'entreposage qu'il occupe doit être récupéré.
Si X et Y désignent le même objet, alors l'accès à cet objet via Y est erroné si cet accès est effectué (ou tenté) après l'appel FREE (X); l'effet de chacun de ces accès n'est pas défini par le langage de programmation.
Remarques : Il est une conséquence des règles de visibilité que la procédure générique UNCHECKED_DEALLOCATION n'est pas visible dans une unité de compilation à moins que cette procédure générique ne soit mentionnée par une clause with s'appliquant à l'unité de compilation.
Si X désigne un objet tâche, l'appel FREE (X) n'a aucun effet sur la tâche désignée par la valeur de cet objet tâche. Il en va de même pour tout sous-composant de l'objet désigné par X, si cette sous-composante est un objet tâche.
Conversions de type non contrôlées
Une conversion de type non contrôlée peut être réalisée par un appel à une fonction obtenue par instanciation de la fonction générique UNCHECKED_CONVERSION.
L'effet d'une conversion non contrôlée est de renvoyer la valeur du paramètre (non interprétée) comme une valeur du type cible, c'est-à-dire que le modèle binaire définissant la valeur source est renvoyé inchangé comme modèle binaire définissant une valeur du type cible. Une implémentation peut imposer des restrictions sur les conversions non contrôlées, par exemple des restrictions dépendant des tailles respectives des objets du type source et cible.
Chaque fois que des conversions non contrôlées sont utilisées, il incombe au programmeur de s'assurer que ces conversions conservent les propriétés garanties par le langage pour les objets du type cible. Les programmes qui violent ces propriétés au moyen de conversions non contrôlées sont erronés.
Remarque : C'est une conséquence des règles de visibilité que la fonction générique UNCHECKED_CONVERSION n'est pas visible dans une unité de compilation à moins que cette fonction générique ne soit mentionnée par une clause with s'appliquant à l'unité de compilation.