Déclarations et types
Cette page décrit les types du langage et les règles de déclaration des constantes, des variables et des nombres nommés.
Déclarations
Le langage définit plusieurs types d'entités étant déclarées, explicitement ou implicitement, par des déclarations. Une telle entité peut être un littéral numérique, un objet, un discriminant, une composante d'enregistrement, un paramètre de boucle, une exception, un type, un sous-type, un sous-programme, un paquet, une unité de tâche, une unité générique, une entrée unique, une famille d'entrées, un paramètre formel (d'un sous-programme, d'une entrée ou d'un sous-programme générique), un paramètre formel générique, un bloc ou une boucle nommés, une instruction étiquetée ou une opération (en particulier, un attribut ou un littéral d'énumération).
Il existe plusieurs formes de déclaration. Une déclaration de base est une forme de déclaration définie comme suit :
declaration_de_base ::= declaration_d_objet | numero_declaration | type_declaration | declaration_de_sous_type | declaration_de_sous-programme | declaration_de_paquet | declaration_de_tache | declaration_generique | declaration_d_exception | instantiation_generique | renommage_declaration | declaration_constante_differee |
Certaines formes de déclaration apparaissent toujours (explicitement) dans le cadre d'une déclaration de base; ces formes sont les spécifications discriminantes, les déclarations de composants, les déclarations d'entrées, les spécifications de paramètres, les déclarations de paramètres génériques et les spécifications de littéraux d'énumération. Une spécification de paramètre de boucle est une forme de déclaration qui n'apparaît que dans certaines formes d'instructions de boucle.
Les autres formes de déclaration sont implicites : le nom d'un bloc, le nom d'une boucle et un libellé d'instruction sont déclarés implicitement. Certaines opérations sont déclarées implicitement. Pour chaque forme de déclaration, les règles de langage définissent une certaine région de texte appelée la portée de la déclaration. Plusieurs formes de déclaration associent un identifiant à une entité déclarée. Dans sa portée, et seulement là, il existe des endroits où il est possible d'utiliser l'identifiant pour faire référence à l'entité déclarée associée; ces endroits sont définis par les règles de visibilité. À ces endroits, l'identifiant est dit être un nom de l'entité (son nom simple) ; le nom est dit désigner l'entité associée.
Certaines formes de spécification de littéraux d'énumération associent un littéral de caractère à l'entité déclarée correspondante. Certaines formes de déclaration associent un symbole d'opérateur ou une autre notation à une opération déclarée explicitement ou implicitement.
Le processus par lequel une déclaration atteint son effet est appelé l'élaboration de la déclaration ; ce processus se produit pendant l'exécution du programme.
Après son élaboration, une déclaration est dite élaborée. Avant la fin de son élaboration (y compris avant l'élaboration), la déclaration n'est pas encore élaborée. L'élaboration de toute déclaration a toujours au moins pour effet d'accomplir ce changement d'état (de non encore élaboré à élaboré). L'expression «l'élaboration n'a pas d'autre effet» est utilisée dans cette section chaque fois que ce changement d'état est le seul effet de l'élaboration pour une forme de déclaration. Un processus d'élaboration est également défini pour les parties déclaratives, les éléments déclaratifs et les unités de compilation.
Les déclarations d'objet, de nombre, de type et de sous-type sont décrites ici. Les déclarations de base restantes sont décrites dans les pages suivantes.
Remarque : les règles de syntaxe utilisent le terme identifiant pour la première occurrence d'un identifiant dans une forme de déclaration; le terme nom simple est utilisé pour toute occurrence d'un identifiant qui désigne déjà une entité déclarée.
Objets et nombres nommés
Un objet est une entité contenant (possédant) une valeur d'un type donné. Un objet est l'un des éléments suivants :
- un objet déclaré par une déclaration d'objet ou par une déclaration de tâche unique,
- un paramètre formel d'un sous-programme, d'une entrée ou d'un sous-programme générique,
- un objet formel générique,
- un paramètre de boucle,
- un objet désigné par une valeur d'un type d'accès,
- une composante ou une tranche d'un autre objet
Une déclaration de nombre est une forme spéciale de déclaration d'objet associant un identifiant à une valeur de type universal_integer ou universal_real :
declaration_d_objet ::= liste_d_identifiants : [constant] sous_type_indication [:= expression]; | liste_d_identifiants : [constant] definition_de_tableau_contraint [:= expression]; numero_declaration ::= liste_d_identifiants : constant := expression_statique_universelle; liste_d_identifiants ::= identifiant [, identifiant] |
Une déclaration d'objet est dite déclaration d'objet unique si sa liste d'identifiants possède un identifiant unique; elle est dite déclaration d'objet multiple si la liste d'identifiants possède deux identifiants ou plus. Une déclaration d'objet multiple est équivalente à une séquence du nombre correspondant de déclarations d'objet unique. Pour chaque identifiant de la liste, la séquence équivalente possède une déclaration d'objet unique formée par cet identifiant, suivi de deux points et de ce qui apparaît à droite des deux points dans la déclaration d'objet multiple; la séquence équivalente est dans le même ordre que la liste d'identifiants. Une équivalence similaire s'applique également aux listes d'identifiants des déclarations de nombre, des déclarations de composants, des spécifications discriminantes, des spécifications de paramètres, des déclarations de paramètres génériques, des déclarations d'exception et des déclarations de constantes différées.
Dans le reste de cette section de référence, des explications sont données pour les déclarations avec un identifiant unique ; les explications correspondantes pour les déclarations avec plusieurs identifiants découlent de l'équivalence indiquée ci-dessus.
Exemple :
- -- la déclaration d'objet multiple
- JEAN, PAUL : PERSON_NAME := new PERSON(SEX => M);
- -- est équivalent aux deux déclarations d'objet unique dans l'ordre donné
- JEAN : PERSON_NAME := new PERSON(SEX => M);
- PAUL : PERSON_NAME := new PERSON(SEX => M);
Déclarations d'objet
Une déclaration d'objet déclare un objet dont le type est donné soit par une indication de sous-type, soit par une définition de tableau contraint. Si la déclaration d'objet inclut le délimiteur composé d'affectation suivi d'une expression, l'expression spécifie une valeur initiale pour l'objet déclaré ; le type de l'expression doit être celui de l'objet.
L'objet déclaré est une constante si le mot réservé constant apparaît dans la déclaration d'objet ; la déclaration doit alors inclure une initialisation explicite. La valeur d'une constante ne peut pas être modifiée après l'initialisation. Les paramètres formels de mode in des sous-programmes et des entrées, et les paramètres formels génériques de mode in, sont également des constantes ; un paramètre de boucle est une constante dans la boucle correspondante ; un sous-composant ou une tranche d'une constante est une constante.
Un objet n'étant pas une constante est appelé une variable (en particulier, l'objet déclaré par une déclaration d'objet n'incluant pas le mot réservé constant est une variable). Les seules façons de modifier la valeur d'une variable sont soit directement par une affectation, soit indirectement lorsque la variable est mise à jour par une instruction d'appel de procédure ou d'entrée (cette action peut être effectuée soit sur la variable elle-même, soit sur une sous-composante de la variable, soit sur une autre variable ayant la variable donnée comme sous-composante).
L'élaboration d'une déclaration d'objet se déroule comme suit :
- L'indication de sous-type ou la définition du tableau contraint est d'abord élaborée. Cela établit le sous-type de l'objet.
- Si la déclaration d'objet comprend une initialisation explicite, la valeur initiale est obtenue en évaluant l'expression correspondante. Sinon, toutes les valeurs initiales implicites de l'objet ou de ses sous-composantes sont évaluées.
- L'objet est créé.
- Toute valeur initiale (explicite ou implicite) est attribuée à l'objet ou au sous-composant correspondant.
Les valeurs initiales implicites sont définies pour les objets déclarés par des déclarations d'objet, et pour les composants de tels objets, dans les cas suivants :
- Si le type d'un objet est un type d'accès, la valeur initiale implicite est la valeur nulle du type d'accès.
- Si le type d'un objet est un type de tâche, la valeur initiale implicite (et unique) désigne une tâche correspondante.
- Si le type d'un objet est un type avec des discriminants et que le sous-type de l'objet est contraint, la valeur initiale implicite (et unique) de chaque discriminant est définie par le sous-type de l'objet.
- Si le type d'un objet est un type composite, la valeur initiale implicite de chaque composant qui a une expression par défaut est obtenue par évaluation de cette expression, sauf si le composant est un discriminant d'un objet contraint (cas précédent).
Dans le cas d'une composante étant lui-même un objet composite et dont la valeur n'est définie ni par une initialisation explicite ni par une expression par défaut, les valeurs initiales implicites des composantes de l'objet composite sont définies par les mêmes règles que pour un objet déclaré.
Les étapes (a) à (d) sont effectuées dans l'ordre indiqué. Pour l'étape (b), si l'expression par défaut d'un discriminant est évaluée, alors cette évaluation est effectuée avant celle des expressions par défaut des sous-composantes dépendant des discriminants, et également avant celle des expressions par défaut incluant le nom du discriminant. Outre la règle précédente, l'évaluation des expressions par défaut est effectuée dans un ordre n'étant pas défini par le langage.
L'initialisation d'un objet (l'objet déclaré ou l'un de ses sous-composantes) vérifie que la valeur initiale appartient au sous-type de l'objet; pour un objet tableau déclaré par une déclaration d'objet, une conversion implicite de sous-type est d'abord appliquée comme pour une instruction d'affectation, sauf si l'objet est une constante dont le sous-type est un type tableau non contraint. L'exception CONSTRAINT_ERROR est levée si cette vérification échoue.
La valeur d'une variable scalaire est indéfinie après l'élaboration de la déclaration d'objet correspondante à moins qu'une valeur initiale ne soit affectée à la variable par une initialisation (explicitement ou implicitement).
Si l'opérande d'une conversion de type ou d'une expression qualifiée est une variable ayant des sous-composantes scalaires avec des valeurs indéfinies, alors les valeurs des sous-composants correspondants du résultat sont indéfinies. L'exécution d'un programme est erronée si elle tente d'évaluer une variable scalaire avec une valeur indéfinie. De même, l'exécution d'un programme est erronée si elle tente d'appliquer un opérateur prédéfini à une variable ayant une sous-composante scalaire avec une valeur indéfinie.
Exemples de déclarations de variables :
Exemples de déclarations constantes :
Remarque : L'expression initialisant un objet constant n'a pas besoin d'être une expression statique. Dans les exemples ci-dessus, LIMIT et LOW_LIMIT sont initialisées avec des expressions statiques, mais TOLERANCE ne l'est pas si DISPERSION est une fonction définie par l'utilisateur.
Déclarations de nombres
Une déclaration de nombre est une forme spéciale de déclaration de constante. Le type de l'expression statique donnée pour l'initialisation d'une déclaration de nombre doit être soit du type universal_integer, soit du type universal_real. La constante déclarée par une déclaration de nombre est appelée un nombre nommé et a le type de l'expression statique.
Remarque : Il résulte de ces règles de type universel que si chaque élément primaire contenu dans l'expression est du type universal_integer, alors le nombre nommé est également de ce type. De même, si chaque élément primaire est du type universal_real, alors le nombre nommé est également de ce type.
Exemples de déclarations de nombres :
Types et sous-types
Un type est caractérisé par un ensemble de valeurs et un ensemble d'opérations.
Il existe plusieurs classes de types. Les types scalaires sont des types entiers, des types réels et des types définis par l'énumération de leurs valeurs; les valeurs de ces types n'ont pas de composantes. Les types tableau et enregistrement sont composites; une valeur d'un type composite est constituée de valeurs composantes. Un type d'accès est un type dont les valeurs donnent accès aux objets. Les types privés sont des types pour lesquels l'ensemble des valeurs possibles est bien défini, mais n'est pas directement disponible pour les utilisateurs de ces types. Enfin, il existe des types de tâches.
Certains types d'enregistrement et privés ont des composants spéciaux appelés discriminants dont les valeurs distinguent les formes alternatives de valeurs de l'un de ces types. Si un type privé a des discriminants, ils sont connus des utilisateurs du type. Par conséquent, un type privé n'est connu que par son nom, ses discriminants s'il y en a, et par l'ensemble d'opérations correspondant.
L'ensemble des valeurs possibles pour un objet d'un type donné peut être soumis à une condition appelée contrainte (le cas où la contrainte n'impose aucune restriction est également inclus); une valeur est dite satisfaire à une contrainte si elle satisfait à la condition correspondante. Un sous-type est un type avec une contrainte ; une valeur est dite appartenir à un sous-type d'un type donné si elle appartient au type et satisfait à la contrainte; le type donné est appelé le type de base du sous-type. Un type est un sous-type de lui-même ; un tel sous-type est dit «sans contrainte», il correspond à une condition qui n'impose aucune restriction. Le type de base d'un type est le type lui-même.
L'ensemble des opérations définies pour un sous-type d'un type donné comprend les opérations définies pour le type; cependant, l'opération d'affectation à une variable ayant un sous-type donné n'affecte que les valeurs appartenant au sous-type. Des opérations supplémentaires, telles que la qualification (dans une expression qualifiée), sont implicitement définies par une déclaration de sous-type.
Certains types ont des valeurs initiales par défaut définies pour les objets du type; certains autres types ont des expressions par défaut définies pour certains ou tous leurs composantes. Certaines opérations de types et de sous-types sont appelées «attributs».
Le terme sous-composante est utilisé dans cette page à la place du terme composante pour indiquer soit une composante, soit une composante d'une autre composante ou sous-composante. Lorsque d'autres sous-composants sont exclus, le terme composante est utilisé à la place.
Un type donné ne doit pas avoir de sous-composante dont le type est le type donné lui-même.
Le nom d'une classe de types est utilisé dans cette page comme qualificatif pour les objets et les valeurs ayant un type de la classe considérée. Par exemple, le terme «objet tableau» est utilisé pour un objet dont le type est un type tableau; de même, le terme «valeur d'accès» est utilisé pour une valeur d'un type d'accès.
Remarque : L'ensemble des valeurs d'un sous-type est un sous-ensemble des valeurs du type de base. Ce sous-ensemble n'a pas besoin d'être un sous-ensemble propre; il peut s'agir d'un sous-ensemble vide.
Déclarations de type
Une déclaration de type déclare un type :
declaration_type ::= declaration_de_type_complet | declaration_de_type_incomplete | declaration_de_type_prive declaration_de_type_complet ::= type identifiant [partie_discriminante] is type_definition; type_definition ::= definition_du_type_d_enumeration | definition_de_type_entier | definition_de_type_reel | definition_du_type_de_tableau | definition_du_type_d_enregistrement | definition_du_type_d_acces | definition_de_type_derive |
L'élaboration d'une déclaration de type complète consiste en l'élaboration de la partie discriminante, s'il y en a une (sauf dans le cas de la déclaration de type complète pour une déclaration de type incomplète ou privée), et en l'élaboration de la définition de type.
Les types créés par l'élaboration de définitions de type distinctes sont des types distincts. De plus, l'élaboration de la définition de type pour un type numérique ou dérivé crée à la fois un type de base et un sous-type du type de base ; il en va de même pour une définition de tableau contraint (l'une des deux formes de définition de type de tableau).
Le nom simple déclaré par une déclaration de type complète désigne le type déclaré, à moins que la déclaration de type ne déclare à la fois un type de base et un sous-type du type de base, auquel cas le nom simple désigne le sous-type, et le type de base est anonyme. Un type est dit anonyme s'il n'a pas de nom simple. À des fins explicatives, ce page de référence fait parfois référence à un type anonyme par un pseudo-nom, écrit en italique, et utilise de tels pseudo-noms aux endroits où la syntaxe requiert normalement un identifiant.
Exemples de définitions de type :
- (WHITE, RED, YELLOW, GREEN, BLUE, BROWN, BLACK)
- range 1 .. 72
- array(1 .. 10) of INTEGER
Exemples de déclarations de type :
- type COLOR is (WHITE, RED, YELLOW, GREEN, BLUE, BROWN, BLACK);
- type COLUMN is range 1 .. 72;
- type TABLE is array(1 .. 10) of INTEGER;
Remarques : Deux définitions de type définissent toujours deux types distincts, même si elles sont textuellement identiques. Ainsi, les définitions de type de tableau données dans les déclarations de A et B ci-dessous définissent des types distincts.
Si A et B sont déclarés par une déclaration d'objet multiple comme ci-dessous, leurs types sont néanmoins différents, puisque la déclaration d'objet multiple est équivalente aux deux déclarations d'objet unique ci-dessus :
- A, B : array(1 .. 10) of BOOLEAN;
Les déclarations de types incomplètes sont utilisées pour la définition de types récursifs et mutuellement dépendants. Les déclarations de types privés sont utilisées dans les spécifications de paquets et dans les déclarations de paramètres génériques.
Déclarations de sous-type
Une déclaration de sous-type déclare un sous-type :
declaration_de_sous_type ::= subtype identifiant is sous_type_indication; sous_type_indication ::= marque_type [contrainte] marque_type ::= type_nom | sous_type_nom contrainte ::= contrainte_de_portee | contrainte_en_virgule_flottante | contrainte_a_point_fixe | contrainte_index | contrainte_discriminante |
Une marque de type désigne un type ou un sous-type. Si une marque de type est le nom d'un type, la marque de type désigne ce type ainsi que le sous-type non contraint correspondant. Le type de base d'une marque de type est, par définition, le type de base du type ou du sous-type désigné par la marque de type.
Une indication de sous-type définit un sous-type du type de base de la marque de type.
Si une contrainte d'index apparaît après une marque de type dans une indication de sous-type, la marque de type ne doit pas déjà imposer une contrainte d'index. De même pour une contrainte discriminante, la marque de type ne doit pas déjà imposer une contrainte discriminante.
L'élaboration d'une déclaration de sous-type consiste en l'élaboration de l'indication de sous-type. L'élaboration d'une indication de sous-type crée un sous-type. Si l'indication de sous-type n'inclut pas de contrainte, le sous-type est le même que celui désigné par la marque de type. L'élaboration d'une indication de sous-type incluant une contrainte se déroule comme suit :
- La contrainte est d'abord élaborée.
- On vérifie ensuite que la contrainte est compatible avec le type ou le sous-type désigné par la marque de type.
La condition imposée par une contrainte est la condition obtenue après élaboration de la contrainte. (Les règles d'élaboration des contraintes sont telles que les expressions et les plages de contraintes sont évaluées par l'élaboration de ces contraintes.) Les règles définissant la compatibilité sont données pour chaque forme de contrainte dans la section appropriée. Ces règles sont telles que si une contrainte est compatible avec un sous-type, alors la condition imposée par la contrainte ne peut pas contredire une condition déjà imposée par le sous-type sur ses valeurs. L'exception CONSTRAINT_ERROR est levée si une vérification de compatibilité échoue.
Exemples de déclarations de sous-types :
Remarque : une déclaration de sous-type ne définit pas un nouveau type.
Classification des opérations
L'ensemble des opérations d'un type inclut les sous-programmes explicitement déclarés ayant un paramètre ou un résultat du type; ces sous-programmes sont nécessairement déclarés après la déclaration de type.
Les opérations restantes sont chacune implicitement déclarées pour une déclaration de type donnée, immédiatement après la définition de type. Ces opérations implicitement déclarées comprennent les opérations de base, les opérateurs prédéfinis et les littéraux d'énumération. Dans le cas d'une déclaration de type dérivé, les opérations implicitement déclarées incluent tous les sous-programmes dérivés. Les opérations implicitement déclarées pour une déclaration de type donnée interviennent après la déclaration de type et avant la déclaration explicite suivante, le cas échéant. Les déclarations implicites des sous-programmes dérivés interviennent en dernier.
Une opération de base est une opération inhérente à l'un des éléments suivants :
- Une affectation (dans les instructions d'affectation et les initialisations), un allocateur, un test d'appartenance ou une forme de contrôle de court-circuit.
- Une composante sélectionné, une composante indexé ou une tranche.
- Une qualification (dans des expressions qualifiées), une conversion de type explicite ou une conversion de type implicite d'une valeur de type universal_integer ou universal_real vers la valeur correspondante d'un autre type numérique.
- Un littéral numérique (pour un type universal), le littéral null (pour un type d'accès), un littéral de chaîne de caractères, un agrégat ou un attribut.
Pour chaque type ou sous-type T, l'attribut suivant est défini :
Attribut | Description |
---|---|
T'BASE | Le type de base de T. Cet attribut n'est autorisé que comme préfixe du nom d'un autre attribut : par exemple, T'BASE 'FIRST. |
Chaque littéral est une opération dont l'évaluation produit la valeur correspondante. De même, un agrégat est une opération dont l'évaluation produit une valeur d'un type composite. Certaines opérations d'un type opèrent sur des valeurs du type, par exemple des opérateurs prédéfinis et certains sous-programmes et attributs. L'évaluation de certaines opérations d'un type renvoie une valeur du type, par exemple des littéraux et certaines fonctions, attributs et opérateurs prédéfinis. L'affectation est une opération opérant sur un objet et une valeur. L'évaluation de l'opération correspondant à un composant sélectionné, un composant indexé ou une tranche produit l'objet ou la valeur désignée par cette forme de nom.
Types dérivés
Une définition de type dérivé définit un nouveau type (de base) dont les caractéristiques sont dérivées de celles d'un type parent; le nouveau type est appelé type dérivé. Une définition de type dérivé définit en outre un sous-type dérivé, étant un sous-type du type dérivé :
definition_de_type_derive new sous_type_indication |
L'indication de sous-type apparaissant après le mot réservé new définit le sous-type parent. Le type parent est le type de base du sous-type parent. Si une contrainte existe pour le sous-type parent, une contrainte similaire existe pour le sous-type dérivé ; la seule différence est que pour une contrainte d'intervalle, et de même pour une contrainte à virgule flottante ou fixe qui inclut une contrainte de plage, la valeur de chaque limite est remplacée par la valeur correspondante du type dérivé. Les caractéristiques du type dérivé sont définies comme suit :
- Le type dérivé appartient à la même classe de types que le type parent. L'ensemble des valeurs possibles pour le type dérivé est une copie de l'ensemble des valeurs possibles pour le type parent. Si le type parent est composite, les mêmes composants existent pour le type dérivé et le sous-type des composantes correspondants est le même.
- Pour chaque opération de base du type parent, il existe une opération de base correspondante du type dérivé. La conversion explicite de type d'une valeur du type parent en valeur correspondante du type dérivé est autorisée et vice versa.
- Pour chaque littéral d'énumération ou opérateur prédéfini du type parent, il existe une opération correspondante pour le type dérivé.
- Si le type parent est un type de tâche, alors pour chaque entrée du type parent, il existe une entrée correspondante pour le type dérivé.
- Si une expression par défaut existe pour un composant d'un objet ayant le type parent, alors la même expression par défaut est utilisée pour le composant correspondant d'un objet ayant le type dérivé.
- Si le type parent est un type d'accès, alors le parent et le type dérivé partagent la même collection; il existe une valeur d'accès nulle pour le type dérivé et c'est la valeur initiale par défaut de ce type.
- Si une clause de représentation explicite existe pour le type parent et si cette clause apparaît avant la définition du type dérivé, alors il existe une clause de représentation correspondante (implicite) pour le type dérivé.
- Certains sous-programmes étant des opérations du type parent sont dits dérivables. Pour chaque sous-programme dérivable du type parent, il existe un sous-programme dérivé correspondant pour le type dérivé. Il existe deux types de sous-programmes dérivables. Tout d'abord, si le type parent est déclaré immédiatement dans la partie visible d'un paquet, alors un sous-programme qui est lui-même explicitement déclaré immédiatement dans la partie visible devient dérivable après la fin de la partie visible, s'il s'agit d'une opération du type parent. (La déclaration explicite se fait par une déclaration de sous-programme, une déclaration de renommage ou une instanciation générique.) Deuxièmement, si le type parent est lui-même un type dérivé, alors tout sous-programme qui a été dérivé par ce type parent est en outre dérivable, à moins que le type parent ne soit déclaré dans la partie visible d'un paquet et que le sous-programme dérivé soit masqué par un sous-programme dérivable du premier type.
Chaque opération du type dérivé est implicitement déclarée à la place de la déclaration du type dérivé. Les déclarations implicites des éventuels sous-programmes dérivés interviennent en dernier.
La spécification d'un sous-programme dérivé est obtenue implicitement par remplacement systématique du type parent par le type dérivé dans la spécification du sous-programme dérivable. Tout sous-type du type parent est également remplacé par un sous-type du type dérivé avec une contrainte similaire (comme pour la transformation d'une contrainte du sous-type parent en contrainte correspondante du sous-type dérivé). Enfin, toute expression du type parent est transformée en opérande d'une conversion de type donnant un résultat du type dérivé.
L'appel d'un sous-programme dérivé équivaut à appeler le sous-programme correspondant du type parent, dans lequel chaque paramètre réel étant du type dérivé est remplacé par une conversion de type de ce paramètre réel vers le type parent (cela signifie qu'une conversion vers le type parent se produit avant l'appel des modes in et in out; une conversion inverse vers le type dérivé se produit après l'appel des modes in out et out). De plus, si le résultat d'une fonction appelée est du type parent, ce résultat est converti vers le type dérivé.
Si un type dérivé ou privé est déclaré immédiatement dans la partie visible d'un paquet, alors, dans cette partie visible, ce type ne doit pas être utilisé comme type parent d'une définition de type dérivé.
Pour l'élaboration d'une définition de type dérivé, l'indication de sous-type est d'abord élaborée, le type dérivé est ensuite créé, et enfin, le sous-type dérivé est créé.
Exemples :
- type LOCALCOORDINATE is new COORDINATE; -- deux types différents
- type MIDWEEK is new DAY range TUE .. THU;
- type COUNTER is new POSITIVE; -- même gamme que POSITIF
-
- type SPECIAL_KEY is new KEY.MANAGER.KEY;
- -- les sous-programmes dérivés ont les spécifications suivantes :
-
- -- procedure GET_KEY(K : out SPECIAL_KEY);
- -- function "<"(X,Y : SPECIALKEY) return BOOLEAN;
Les règles de dérivation des opérations de base et des littéraux d'énumération impliquent que la notation de tout littéral ou agrégat du type dérivé est la même que pour le type parent ; de tels littéraux et agrégats sont dits surchargés. De même, il s'ensuit que la notation pour désigner un composant, un discriminant, une entrée, une tranche ou un attribut est la même pour le type dérivé que pour le type parent.
Le masquage d'un sous-programme dérivé est autorisé même au sein de la même région déclarative Un sous-programme dérivé cache un opérateur prédéfini ayant le même profil de paramètre et de type de résultat.
Une déclaration de sous-programme générique n'est pas dérivable car elle déclare une unité générique plutôt qu'un sous-programme. D'autre part, une instanciation d'un sous-programme générique est un sous-programme (non générique), étant dérivable s'il satisfait aux exigences de dérivabilité des sous-programmes. Si le type parent est un type booléen, les opérateurs relationnels prédéfinis du type dérivé fournissent un résultat du type prédéfini BOOLEAN.
Si une clause de représentation est donnée pour le type parent mais apparaît après la déclaration du type dérivé, aucune clause de représentation correspondante ne s'applique au type dérivé; par conséquent, une clause de représentation explicite pour un tel type dérivé est autorisée.
Pour un sous-programme dérivé, si un paramètre appartient au type dérivé, le sous-type de ce paramètre n'a pas besoin d'avoir de valeur en commun avec le sous-type dérivé.
Types scalaires
Les types scalaires comprennent les types d'énumération, les types entiers et les types réels. Les types d'énumération et les types entiers sont appelés types discrets; chaque valeur d'un type discret possède un numéro de position étant une valeur entière. Les types entiers et les types réels sont appelés types numériques. Tous les types scalaires sont ordonnés, c'est-à-dire que tous les opérateurs relationnels sont prédéfinis pour leurs valeurs :
contrainte_de_portee ::= range intervalle intervalle := attribut_d_intervalle | simple_expression .. simple_expression |
Un intervalle spécifie un sous-ensemble de valeurs d'un type scalaire. L'intervalle L..R spécifie les valeurs de L à R incluses si la relation L <= R est vraie. Les valeurs L et R sont appelées respectivement limite inférieure et limite supérieure de l'intervalle. On dit qu'une valeur V satisfait une contrainte d'intervalle si elle appartient à l'intervalle; la valeur V est dite appartenir à l'intervalle si les relations L <= V et V <= R sont toutes deux VRAIES. Un intervalle nulle est une intervalle pour laquelle la relation R < L est VRAIE ; aucune valeur n'appartient à un intervalle nulle. Les opérateurs <= et < dans les définitions ci-dessus sont les opérateurs prédéfinis du type scalaire.
Si une contrainte d'intervalle est utilisée dans une indication de sous-type, soit directement, soit dans le cadre d'une contrainte à virgule flottante ou fixe, le type des expressions simples (de même que celui des limites d'un attribut d'intervalle) doit être le même que le type de base de la marque de type de l'indication de sous-type. Une contrainte d'intervalle est compatible avec un sous-type si chaque borne de la plage appartient au sous-type, ou si la contrainte d'intervalle définit une intervalle nulle ; sinon, la contrainte d'intervalle n'est pas compatible avec le sous-type.
L'élaboration d'une contrainte d'intervalle consiste en l'évaluation de l'intervalle. L'évaluation d'une plage de 5 définit sa borne inférieure et sa borne supérieure. Si des expressions simples sont données pour spécifier les bornes, l'évaluation de l'intervalle évalue ces expressions simples dans un ordre n'étant pas défini par le langage.
Les attributs
Pour tout type scalaire T ou pour tout sous-type T d'un type scalaire, les attributs suivants sont définis :
Attribut | Description |
---|---|
T'FIRST | Donne la limite inférieure de T. La valeur de cet attribut a le même type que T. |
T'LAST | Donne la limite supérieure de T. La valeur de cet attribut a le même type que T. |
Remarque : les règles d'indexation et d'itération utilisent des valeurs de types discrets.
Types d'énumération
Une définition de type d'énumération définit un type d'énumération :
definition_du_type_d_enumeration ::= (specification_litterale_d_enumeration {, specification_litterale_d_enumeration}) specification_litterale_d_enumeration ::= enumeration_litterale enumeration_litterale identifiant | caractere_litteral |
Les identifiants et les littéraux de caractères listés par une définition de type d'énumération doivent être distincts. Chaque spécification de littéral d'énumération est la déclaration du littéral d'énumération correspondant : cette déclaration est équivalente à la déclaration d'une fonction sans paramètre, le désignateur étant le littéral d'énumération et le type de résultat étant le type d'énumération. L'élaboration d'une définition de type d'énumération crée un type d'énumération; cette élaboration inclut celle de chaque spécification de littéral d'énumération.
Chaque littéral d'énumération produit une valeur d'énumération différente. Les relations d'ordre prédéfinies entre les valeurs d'énumération suivent l'ordre des numéros de position correspondants. Le numéro de position de la valeur du premier littéral d'énumération répertorié est zéro; le numéro de position de chaque autre littéral d'énumération est un de plus que celui de son prédécesseur dans la liste.
Si le même identifiant ou le même littéral de caractère est spécifié dans plusieurs définitions de type d'énumération, les littéraux correspondants sont dits surchargés. À tout endroit où un littéral d'énumération surchargé apparaît dans le texte d'un programme, le type du littéral d'énumération doit pouvoir être déterminé à partir du contexte.
Exemples :
- type DAY is (MON, TUE, WED, THU, FRI, SAT, SUN);
- type SUIT is (CLUBS, DIAMONDS, HEARTS, SPADES);
- type GENDER is (M, F);
- type LEVEL is (LOW, MEDIUM, URGENT);
- type COLOR is (WHITE, RED, YELLOW, GREEN, BLUE, BROWN, BLACK);
- type LIGHT is (RED, AMBER, GREEN); -- Le ROUGE et le VERT sont surchargés
-
- type HEXA is ('A', 'B', 'C', 'D', 'E', 'F');
- type MIXED is ('A', 'B', '*', B, NONE, '?', '%');
-
- subtype WEEKDAY is DAY range MON .. FRI;
- subtype MAJOR is SUIT range HEARTS .. SPADES;
- subtype RAINBOW is COLOR range RED .. BLUE; -- la couleur RED, pas la lumière
Remarque : si un littéral d'énumération apparaît dans un contexte ne suffisant pas à déterminer le type du littéral, la qualification par le nom du type d'énumération est un moyen de résoudre l'ambiguïté.
Types de caractères
Un type d'énumération est dit type caractère si au moins un de ses littéraux d'énumération est un littéral caractère. Le type prédéfini CHARACTER est un type caractère dont les valeurs sont les 128 caractères de l'ensemble de caractères ASCII. Chacun des 95 caractères graphiques de cette ensemble de caractères est désigné par le littéral caractère correspondant.
Exemple :
- type ROMAN_DIGIT is ('I', 'V', 'X', 'L', 'C', 'D', 'M');
Remarques : Le paquet prédéfini ASCII inclut la déclaration de constantes désignant les caractères de contrôle et de constantes désignant les caractères graphiques n'étant pas dans l'ensemble de caractères de base.
Un ensemble de caractères conventionnel tel que EBCDIC peut être déclaré comme type de caractère; les codes internes des caractères peuvent être spécifiés par une clause de représentation d'énumération.
Types booléens
Il existe un type d'énumération prédéfini appelé BOOLEAN. Il contient les deux littéraux FALSE et TRUE ordonnés selon la relation FALSE < TRUE. Un type booléen est soit le type BOOLEAN, soit un type dérivé, directement ou indirectement, d'un type booléen.
Types d'entiers
Une définition de type entier définit un type entier dont l'ensemble de valeurs comprend au moins celles de l'intervalle spécifiée.
definition_de_type_entier := contrainte_de_portee |
Si une contrainte d'intervalle est utilisée comme définition de type entier, chaque limite de l'intervalle doit être définie par une expression statique d'un type entier, mais les deux limites n'ont pas besoin d'avoir le même type entier. (Les limites négatives sont autorisées.)
Une déclaration de type de la forme :
- type T is range L .. R;
est, par définition, équivalent aux déclarations suivantes :
type integer_type is new predefined_integer_type; subtype T is integer_type range integer_type(L) .. integer_type(R); |
où integer_type est un type anonyme, et où le type entier prédéfini est implicitement sélectionné par l'implémentation, de manière à contenir les valeurs L à R incluses. La déclaration de type entier est illégale si aucun des types entiers prédéfinis ne satisfait cette exigence, à l'exception de universal_integer. L'élaboration de la déclaration d'un type entier consiste en l'élaboration des déclarations de type et de sous-type équivalentes.
Les types entiers prédéfinis incluent le type INTEGER. Une implémentation peut également avoir des types prédéfinis tels que SHORT_INTEGER et LONG_INTEGER, ayant respectivement des intervalles (substantiellement) plus courtes et plus longues que INTEGER. La plage de chacun de ces types doit être symétrique par rapport à zéro, à l'exception d'une valeur négative supplémentaire pouvant exister dans certaines implémentations. Le type de base de chacun de ces types est le type lui-même.
Les littéraux entiers sont les littéraux d'un type entier prédéfini anonyme appelé universal_integer dans cette page de référence. Les autres types entiers n'ont pas de littéraux. Cependant, pour chaque type entier, il existe une conversion implicite qui convertit une valeur universal_integer en la valeur correspondante (le cas échéant) du type entier.
Le numéro de position d'une valeur entière est la valeur correspondante du type universal_integer.
Les mêmes opérateurs arithmétiques sont prédéfinis pour tous les types entiers. L'exception NUMERIC_ERROR est levée par l'exécution d'une opération (en particulier une conversion implicite) ne pouvant pas fournir le résultat correct (c'est-à-dire si la valeur correspondant au résultat mathématique n'est pas une valeur de type entier). Cependant, une implémentation n'est pas obligée de déclencher l'exception NUMERIC_ERROR si l'opération fait partie d'une expression plus grande dont le résultat peut être calculé correctement.
Exemples :
Remarques :
Le nom déclaré par une déclaration de type entier est un nom de sous-type. D'autre part, les opérateurs prédéfinis d'un type entier fournissent des résultats dont l'intervalle est définie par le type prédéfini parent; un tel résultat n'a pas besoin d'appartenir au sous-type déclaré, auquel cas une tentative d'affectation du résultat à une variable du sous-type entier génère l'exception CONSTRAINT_ERROR.
La plus petite valeur (la plus négative) prise en charge par les types entiers prédéfinis d'une implémentation est le nombre nommé SYSTEM.MIN_INT et la plus grande valeur (la plus positive) est SYSTEM.MAX_INT.
Opérations de types discrets
Les opérations de base d'un type discret incluent les opérations d'affectation, les tests d'appartenance et la qualification; pour un type booléen, elles incluent les formes de contrôle de court-circuit ; pour un type entier, elles incluent la conversion explicite des valeurs d'autres types numériques en type entier et la conversion implicite des valeurs du type universel_integer en type.
Enfin, pour chaque type discret ou sous-type T, les opérations de base incluent les attributs listés ci-dessous. Dans cette présentation, T est considéré comme un sous-type (le sous-type T) pour toute propriété qui dépend des contraintes imposées par T ; les autres propriétés sont énoncées en termes de type de base de T.
Le premier groupe d'attributs donne les caractéristiques du sous-type T. Ce groupe comprend l'attribut BASE, les attributs FIRST et LAST, l'attribut de représentation SIZE et l'attribut WIDTH défini comme suit :
Attribut | Description |
---|---|
T'WIDTH | Renvoie la longueur d'image maximale sur toutes les valeurs du sous-type T (l'image est la séquence de caractères renvoyée par l'attribut IMAGE). Renvoie zéro pour un intervalle nulle. La valeur de cet attribut est de type universal_integer. |
Tous les attributs du deuxième groupe sont des fonctions avec un seul paramètre. Le paramètre réel correspondant est indiqué ci-dessous par X :
Attribut | Description |
---|---|
T'POS | Cet attribut est une fonction. Le paramètre X doit être une valeur de type de base T. Le type de résultat est le type universal_integer. Le résultat est le numéro de position de la valeur du paramètre. |
T'VAL | Cet attribut est une fonction spéciale avec un seul paramètre pouvant être de n'importe quel type entier. Le type de résultat est le type de base de T. Le résultat est la valeur dont le numéro de position est la valeur universal_integer correspondant à X. L'exception CONSTRAINT_ERROR est levée si la valeur universal_integer correspondant à X n'est pas dans l'intervalle T'POS(T'BASE'FIRST).. T'POS(T'BASE'LAST). |
T'SUCC | Cet attribut est une fonction. Le paramètre X doit être une valeur du type de base T. Le type de résultat est le type de base T. Le résultat est la valeur dont le numéro de position est supérieur de un à celui de X. L'exception CONSTRAINT_ERROR est levée si X est égal à T'BASE'LAST. |
T'PRED | Cet attribut est une fonction. Le paramètre X doit être une valeur du type de base T. Le type du résultat est le type de base de T. Le résultat est la valeur dont le numéro de position est inférieur de un à celui de X. L'exception CONSTRAINT_ERROR est levée si X est égal à T'BASE'FIRST. |
T'IMAGE | Cet attribut est une fonction. Le paramètre X doit être une valeur du type de base T. Le type de résultat est le type prédéfini STRING. Le résultat est l'image de la valeur de X, c'est-à-dire une séquence de caractères représentant la valeur sous forme d'affichage. L'image d'une valeur entière est le littéral décimal correspondant; sans soulignement, zéros non significatifs, exposant ou espaces de fin; mais avec un seul caractère de début étant soit un signe moins, soit un espace. La limite inférieure de l'image est un. L'image d'une valeur d'énumération est soit l'identifiant correspondant en majuscules, soit le littéral de caractère correspondant (y compris les deux apostrophes); ni les espaces de début ni de fin ne sont inclus. L'image d'un caractère C, autre qu'un caractère graphique, est définie par l'implémentation; la seule exigence est que l'image soit telle que C soit égal à CHARACTER'VALUE (CHARACTER'IMAGE (C)). |
T'VALUE | Cet attribut est une fonction. Le paramètre X doit être une valeur du type prédéfini STRING. Le type de résultat est le type de base de T. Les espaces de début et de fin de la séquence de caractères correspondant au paramètre sont ignorés. Pour un type d'énumération, si la séquence de caractères a la syntaxe d'un littéral d'énumération et si ce littéral existe pour le type de base de T, le résultat est la valeur d'énumération correspondante. Pour un type entier, si la séquence de caractères a la syntaxe d'un littéral entier, avec un seul caractère de début facultatif étant un signe plus ou moins, et s'il existe une valeur correspondante dans le type de base de T, le résultat est cette valeur. Dans tous les autres cas, l'exception CONSTRAINT_ERROR est générée. |
De plus, les attributs A'SIZE et A'ADDRESS sont définis pour un objet A de type discret.
Outre les opérations de base, les opérations d'un type discret incluent les opérateurs relationnels prédéfinis. Pour les types d'énumération, les opérations incluent les littéraux d'énumération. Pour les types booléens, les opérations incluent l'opérateur de négation logique unaire prédéfini NOT et les opérateurs logiques prédéfinis. Pour les types entiers, les opérations incluent les opérateurs arithmétiques prédéfinis : ce sont les opérateurs d'addition binaires et unaires - et +, tous les opérateurs de multiplication, l'opérateur unaire abs et l'opérateur d'exponentiation.
Les opérations d'un sous-type sont les opérations correspondantes de son type de base à l'exception des suivantes : affectation, tests d'appartenance, qualification, conversions de type explicites et les attributs du premier groupe ; l'effet de chacune de ces opérations dépend du sous-type (les affectations, les tests d'appartenance, les qualifications et les conversions impliquent une vérification de sous-type; les attributs du premier groupe donnent une caractéristique du sous-type).
Remarques : Pour un sous-type d'un type discret, les résultats fournis par les attributs SUCC, PRED, VAL et VALUE n'ont pas besoin d'appartenir au sous-type; de même, les paramètres réels des attributs POS, SUCC, PRED et IMAGE n'ont pas besoin d'appartenir au sous-type. Les relations suivantes sont satisfaites (en l'absence d'exception) par ces attributs :
- T'POS(T'SUCC(X)) = T'POS(X) + 1
- T'POS(T'PRED(X)) = T'POS(X) - 1
-
- T'VAL(T'POS(X)) = X
- T'POS(T'VAL(N)) = N
Exemples :
- -- Pour les types et sous-types déclarés dans la section précédente, nous avons :
-
- -- COLOR'FIRST = WHITE, COLOR'LAST = BLACK
- -- RAINBOW'FIRST = RED, RAINBOW'LAST = BLUE
-
- -- COLOR'SUCC(BLUE) = RAINBOW'SUCC(BLUE) = BROWN
- -- COLOR'POS (BLUE) = RAINBOW'POS(BLUE) = 4
- -- COLOR'VAL(0) = RAINBOW'VAL(0) = WHITE
Types réels
Les types réels fournissent des approximations des nombres réels, avec des limites relatives aux erreurs pour les types à virgule flottante et avec des limites absolues pour les types à virgule fixe :
definition_de_type_reel ::= contrainte_en_virgule_flottante | contrainte_a_point_fixe |
Un ensemble de nombres appelés numéros de modèle est associé à chaque type réel. Les limites d'erreur sur les opérations prédéfinies sont données en termes de numéros de modèle. Une implémentation du type doit inclure au moins ces numéros de modèle et les représenter exactement.
Un ensemble de nombres dépendant de l'implémentation, appelé numéros de sécurité, est également associé à chaque type réel. L'ensemble des numéros de sécurité d'un type réel doit inclure au moins l'ensemble des numéros de modèle du type. L'intervalle de numéros de sécurité peut être supérieure à l'intervalle de numéros de modèle, mais les limites d'erreur sur les opérations prédéfinies pour les numéros de sécurité sont données par les mêmes règles que pour les numéros de modèle. Les numéros de sécurité fournissent donc des limites d'erreur garanties pour les opérations sur une plage de nombres dépendant de l'implémentation; en revanche, l'intervalle de numéros de modèle dépend uniquement de la définition du type réel et est donc indépendante de l'implémentation.
Les littéraux réels sont les littéraux d'un type réel prédéfini anonyme appelé universal_real dans cette page de référence. Les autres types réels n'ont pas de littéraux. Cependant, pour chaque type réel, il existe une conversion implicite convertissant une valeur universal_real en une valeur du type réel. Si la valeur universal_real est un nombre sûr, la conversion implicite fournit la valeur correspondante; si elle appartient à l'intervalle des nombres sûrs mais n'est pas un nombre sûr, alors la valeur convertie peut être n'importe quelle valeur dans l'intervalle définie par les nombres sûrs immédiatement au-dessus et en dessous de la valeur universal_real.
L'exécution d'une opération qui produit une valeur d'un type réel peut déclencher l'exception NUMERIC_ERROR, si elle ne peut pas fournir un résultat correct (c'est-à-dire si la valeur correspondant à l'un des résultats mathématiques possibles n'appartient pas à la plage des nombres sûrs) ; en particulier, cette exception peut être déclenchée par une conversion implicite. Cependant, une implémentation n'est pas obligée de déclencher l'exception NUMERIC_ERROR si l'opération fait partie d'une expression plus grande dont le résultat peut être calculé correctement.
L'élaboration d'une définition de type réel inclut l'élaboration de la contrainte de virgule flottante ou fixe et crée un type réel.
Remarque : un algorithme écrit pour s'appuyer uniquement sur les propriétés numériques minimales garanties par la définition de type pour les numéros de modèle sera portable sans précautions supplémentaires.
Pour les types à virgule flottante, la limite d'erreur est spécifiée comme une précision relative en donnant le nombre minimum requis de chiffres décimaux significatifs :
contrainte_en_virgule_flottante ::= definition_de_précision_flottante [contrainte_de_portee] definition_de_precision_flottante ::= digits expression_simple_statique |
Le nombre minimum de chiffres décimaux significatifs est spécifié par la valeur de l'expression statique simple de la définition de précision flottante. Cette valeur doit appartenir à un type entier et doit être positive (différente de zéro); elle est désignée par D dans le reste de cette section. Si la contrainte à virgule flottante est utilisée comme définition de type réel et inclut une contrainte de plage, chaque limite de l'intervalle doit être définie par une expression statique d'un type réel, mais les deux limites n'ont pas besoin d'avoir le même type réel.
Pour une base donnée, la forme canonique suivante est définie pour tout nombre de modèle à virgule flottante autre que zéro :
signe * mantisse * (base ** exposant) |
Sous cette forme : le signe est soit +1, soit -1 ; la mantisse est exprimée dans une base numérique donnée par la base; et l'exposant est un nombre entier (éventuellement négatif) tel que la partie entière de la mantisse soit zéro et que le premier chiffre de sa partie fractionnaire ne soit pas un zéro.
Le nombre spécifié D est le nombre minimum de chiffres décimaux requis après le point dans la mantisse décimale (c'est-à-dire si la base est dix). La valeur de D détermine à son tour un nombre correspondant B étant le nombre minimum de chiffres binaires requis après le point dans la mantisse binaire (c'est-à-dire si la base est deux). Le nombre B associé à D est la plus petite valeur telle que la précision relative de la forme binaire ne soit pas inférieure à celle spécifiée pour la forme décimale. (Le nombre B est l'entier immédiatement supérieur à (D*log(10)/log(2)) + 1).
Les numéros de modèle définis par une définition de précision flottante comprennent zéro et tous les nombres dont la forme canonique binaire comporte exactement B chiffres après le point de la mantisse et un exposant dans l'intervalle -4*B .. +4*B. La précision minimale garantie des opérations d'un type à virgule flottante est définie en termes de numéros de modèle de la contrainte à virgule flottante qui forme la définition de type réel correspondante.
Les types à virgule flottante prédéfinis incluent le type FLOAT. Une implémentation peut également avoir des types prédéfinis tels que SHORT_FLOAT et LONG_FLOAT, ayant respectivement (substantiellement) moins et plus de précision que FLOAT. Le type de base de chaque type à virgule flottante prédéfini est le type lui-même. Les numéros de modèle de chaque type à virgule flottante prédéfini sont définis en termes du nombre D de chiffres décimaux renvoyés par l'attribut DIGITS.
Pour chaque type de virgule flottante prédéfini (et donc également pour chaque type en dérivant), un ensemble de nombres sûrs est défini comme suit. Les nombres sûrs ont le même nombre B de chiffres de mantisse que les nombres de modèle du type et ont un exposant dans l'intervalle -E .. +E où E est défini par l'implémentation et au moins égal à 4*B des nombres de modèle. (Par conséquent, les nombres sûrs incluent les nombres de modèle.) Les nombres sûrs d'un sous-type sont ceux de son type de base.
Une déclaration de type à virgule flottante de l'une des deux formes (c'est-à-dire avec ou sans la contrainte d'intervalle facultative indiquée par les crochets) :
type T is digits D [range L .. R]; |
est, par définition, équivalent aux déclarations suivantes :
type type_a_virgule_flottante is new type_a_virgule_flottante_predefini; subtype T is type_a_virgule_flottante digits D [range type_a_virgule_flottante(L) .. type_a_virgule_flottante{R)]; |
où type_a_virgule_flottante est un type anonyme, et où le type de virgule flottante prédéfini est implicitement sélectionné par l'implémentation de sorte que ses numéros de modèle incluent les numéros de modèle définis par D ; de plus, si une plage L R est fournie, alors L et R doivent tous deux appartenir à la plage de nombres sûrs. La déclaration de virgule flottante est illégale si aucun des types de virgule flottante prédéfinis ne satisfait ces exigences, à l'exception d'universal_real. Le nombre maximal de chiffres pouvant être spécifiés dans une définition de précision flottante est donné par le nombre nommé dépendant du système SYSTEM.MAX_DIGITS.
L'élaboration d'une déclaration de type à virgule flottante consiste à élaborer les déclarations de type et de sous-type équivalentes.
Si une contrainte à virgule flottante suit une marque de type dans une indication de sous-type, la marque de type doit désigner un type ou un sous-type à virgule flottante. La contrainte à virgule flottante n'est compatible avec la marque de type que si le nombre D spécifié dans la définition de précision flottante n'est pas supérieur au nombre D correspondant pour le type ou le sous-type désigné par la marque de type. De plus, si la contrainte à virgule flottante comprend une contrainte d'intervalle, la contrainte à virgule flottante n'est compatible avec la marque de type que si la contrainte de plage est elle-même compatible avec la marque de type.
L'élaboration d'une telle indication de sous-type comprend l'élaboration de la contrainte de l'intervalle, s'il y en a une ; elle crée un sous-type à virgule flottante dont les numéros de modèle sont définis par la définition de précision flottante correspondante. Une valeur d'un type à virgule flottante appartient à un sous-type à virgule flottante si et seulement si elle appartient à l'intervalle définie par le sous-type.
Les mêmes opérateurs arithmétiques sont prédéfinis pour tous les types à virgule flottante.
Remarques : une contrainte d'intervalle est autorisée dans une indication de sous-type à virgule flottante, soit directement après la marque de type, soit dans le cadre d'une contrainte à virgule flottante. Dans les deux cas, les limites de la plage doivent appartenir au type de base de la marque de type. L'imposition d'une contrainte à virgule flottante sur une marque de type dans une indication de sous-type ne peut pas réduire l'intervalle de valeurs autorisée à moins qu'elle n'inclue une contrainte d'intervalle (l'intervalle de numéros de modèle correspondant au nombre de chiffres spécifié peut être inférieure à la plage de numéros de la marque de type). Une valeur appartenant à un sous-type à virgule flottante n'a pas besoin d'être un numéro de modèle du sous-type.
Exemples :
- type COEFFICIENT is digits 10 range -1.0 .. 1.0;
-
- type REAL is digits 8;
- type MASS is digits 7 range 0.0 .. 1.0E35;
-
- subtype SHORT_COEFF is COEFFICIENT digits 5; -- un sous-type avec moins de précision
- subtype PROBABILITY is REAL range 0.0 .. 1.0; -- un sous-type avec une portée plus petite
Remarques sur les exemples : La précision implémentée pour COEFFICIENT est celle d'un type prédéfini ayant au moins 10 chiffres de précision. Par conséquent, la spécification de 5 chiffres de précision pour le sous-type SHORT_COEFF est autorisée. Le plus grand numéro de modèle pour le type MASS est d'environ 1,27E30 et donc inférieur à la limite supérieure spécifiée (1,0E35). Par conséquent, la déclaration de ce type n'est légale que si cette limite supérieure se situe dans l'intervalle des nombres sûrs d'un type à virgule flottante prédéfini ayant au moins 7 chiffres de précision.
Opérations des types à virgule flottante
Les opérations de base d'un type à virgule flottante incluent les opérations impliquées dans l'affectation, les tests d'appartenance, la qualification, la conversion explicite de valeurs d'autres types numériques en type à virgule flottante et la conversion implicite de valeurs du type universal_real en type. De plus, pour chaque type ou sous-type à virgule flottante T, les opérations de base incluent les attributs répertoriés ci-dessous. Dans cette présentation, T est considéré comme un sous-type (le sous-type T) pour toute propriété qui dépend des contraintes imposées par T ; les autres propriétés sont énoncées en termes de type de base de T.
Le premier groupe d'attributs donne les caractéristiques du sous-type T. Les attributs de ce groupe sont l'attribut BASE, les attributs FIRST et LAST, l'attribut de représentation SIZE et les attributs suivants :
Attribut | Description |
---|---|
T'DIGITS | Renvoie le nombre de chiffres décimaux dans la mantisse décimale des numéros de modèle du sous-type T. La valeur de cet attribut est de type universal_integer. |
T'MANTISSA | Renvoie le nombre de chiffres binaires dans la mantisse binaire des numéros de modèle du sous-type T. La valeur de cet attribut est de type universal_integer. |
T'EPSILON | Donne la valeur absolue de la différence entre le numéro de modèle 1.0 et le numéro de modèle supérieur, pour le sous-type T. La valeur de cet attribut est de type universal_real. |
T'EMAX | Donne la plus grande valeur d'exposant dans la forme canonique binaire des numéros de modèle du sous-type T. La valeur de cet attribut est de type universal_integer. |
T'SMALL | Donne le plus petit numéro de modèle positif (différent de zéro) du sous-type T. La valeur de cet attribut est de type universal_real. |
T'LARGE | Donne le plus grand numéro de modèle positif du sous-type T. La valeur de cet attribut est de type universal_real. |
Les attributs du deuxième groupe incluent les attributs suivants donnant les caractéristiques des numéros sûrs :
Attribut | Description |
---|---|
T'SAFE_EMAX | Donne la plus grande valeur d'exposant dans la forme canonique binaire des nombres sûrs du type de base T. La valeur de cet attribut est de type universal_integer. |
T'SAFE_SMALL | Génère le plus petit nombre sûr positif (non nul) du type de base de T. La valeur de cet attribut est de type universal_real. |
T'SAFE_LARGE | Donne le plus grand nombre positif sûr du type de base de T. La valeur de cet attribut est de type universal_real. |
De plus, les attributs A'SIZE et A'ADDRESS sont définis pour un objet A de type à virgule flottante. Enfin, pour chaque type à virgule flottante, il existe des attributs dépendants de la machine n'étant pas liés aux numéros de modèle et aux numéros de sécurité. Ils correspondent aux désignateurs d'attributs MACHINE_RADIX, MACHINE_MANTISSA, MACHINE_EMAX, MACHINE_EMIN, MACHINE_ROUNDS et MACHINE_0VERFLOWS.
Outre les opérations de base, les opérations d'un type à virgule flottante incluent les opérateurs relationnels et les opérateurs arithmétiques prédéfinis suivants : les opérateurs d'addition binaires et unaires - et +, les opérateurs de multiplication * et /, l'opérateur unaire abs et l'opérateur d'exponentiation.
Les opérations d'un sous-type sont les opérations correspondantes du type à l'exception des suivantes : affectation, tests d'appartenance, qualification, conversion explicite et les attributs du premier groupe; les effets de ces opérations sont redéfinis en fonction du sous-type.
Remarques : les attributs EMAX, SMALL, LARGE et EPSILON sont fournis pour plus de commodité. Ils sont tous liés à MANTISSA par les formules suivantes :
- T'EMAX = 4*T'MANTISSA
- T'EPSILON = 2.0**(1 - T'MANTISSA)
- T'SMALL = 2.0**(-T'EMAX - 1)
- T'LARGE - 2.0**T'EMAX * (1.0 - 2.0**(-T'MANTISSA))
L'attribut MANTISSA, donnant le nombre de chiffres binaires de la mantisse, est lui-même lié à DIGITS. Les relations suivantes existent entre les caractéristiques des numéros de modèle et celles des numéros sûrs :
- T'BASE'EMAX <= T'SAFE_EMAX
- T'BASE'SMALL >= T'SAFE_SMALL
- T'BASE'LARGE <= T'SAFE_LARGE
Les attributs T'FIRST et T'LAST ne doivent pas nécessairement donner de numéros de modèle ou de sécurité. Si un certain nombre de chiffres est spécifié dans la déclaration d'un type ou sous-type T, l'attribut T'DIGITS donne ce nombre.
Types de points fixes
Pour les types à virgule fixe, la limite d'erreur est spécifiée comme une valeur absolue, appelée delta du type à virgule fixe :
contrainte_a_point_fixe ::= definition_de_precision_fixe [contrainte_de_portee] definition_de_precision_fixe delta expression_simple_statique |
Le delta est spécifié par la valeur de l'expression statique simple de la définition de précision fixe. Cette valeur doit appartenir à un type réel et doit être positive (non nulle). Si la contrainte de point fixe est utilisée comme définition de type réel, elle doit alors inclure une contrainte d'intervalle; chaque limite de l'intervalle spécifié doit être définie par une expression statique de type réel, mais les deux limites n'ont pas besoin d'avoir le même type réel. Si la contrainte de point fixe est utilisée dans une indication de sous-type, la contrainte d'intervalle est facultative.
Une forme canonique est définie pour tout numéro de modèle à virgule fixe autre que zéro. Dans cette forme : le signe est soit +1, soit -1 ; la mantisse est un entier positif (non nul) ; et tout numéro de modèle est un multiple d'un certain nombre réel positif appelé petit, comme suit :
signe * mantisse * petit |
Pour les numéros de modèle définis par une contrainte de point fixe, le nombre petit est choisi comme la plus grande puissance de deux n'étant pas supérieure au delta de la définition de précision fixe. Alternativement, il est possible de spécifier la valeur de petit par une clause de longueur, auquel cas les numéros de modèle sont des multiples de la valeur spécifiée. La précision minimale garantie des opérations d'un type à point fixe est définie en termes de numéros de modèle de la contrainte de point fixe formant la définition de type réel correspondante.
Pour une contrainte de point fixe incluant une contrainte d'intervalle, les numéros de modèle comprennent zéro et tous les multiples de petit dont la mantisse peut être exprimée en utilisant exactement B chiffres binaires, où la valeur de B est choisie comme le plus petit nombre entier pour lequel chaque limite de l'intervalle spécifiée est soit un numéro de modèle, soit se situe à la plus petite distance d'un numéro de modèle. Pour une contrainte de point fixe n'incluant pas de contrainte d'intervalle (cela n'est autorisé qu'après une marque de type, dans une indication de sous-type), les numéros de modèle sont définis par le delta de la définition de précision fixe et par l'intervalle du sous-type indiqué par la marque de type.
Une implémentation doit avoir au moins un type de point fixe prédéfini anonyme. Le type de base de chaque type de point fixe est le type lui-même. Les numéros de modèle de chaque type de point fixe prédéfini comprennent zéro et tous les nombres pour lesquels la mantisse (sous la forme canonique) a le nombre de chiffres binaires renvoyés par l'attribut MANTISSA, et pour lesquels le nombre petit a la valeur renvoyée par l'attribut SMALL.
Une déclaration de type de point fixe de la forme :
type T is delta D range L .. R; |
est, par définition, équivalent aux déclarations suivantes :
type type_a_virgule_fixe is new type_de_point_fixe_predefini; subtype T is type_a_virgule_fixe range type_a_virgule_fixe{L) .. type_a_virgule_fixe(R); |
Dans ces déclarations, type_a_virgule_fixe est un type anonyme et le type de point fixe prédéfini est implicitement sélectionné par l'implémentation de sorte que ses numéros de modèle incluent les numéros de modèle définis par la contrainte de point fixe (c'est-à-dire par D, L et R, et éventuellement par une clause de longueur spécifiant petit).
La déclaration de point fixe est illégale si aucun type prédéfini ne satisfait à ces exigences. Les numéros sûrs d'un type de point fixe sont les numéros de modèle de son type de base. L'élaboration d'une déclaration de type de point fixe consiste en l'élaboration des déclarations de type et de sous-type équivalentes.
Si la contrainte de point fixe suit une marque de type dans une indication de sous-type, la marque de type doit désigner un type ou un sous-type de point fixe. La contrainte de point fixe est compatible avec la marque de type uniquement si le delta spécifié par la définition de précision fixe n'est pas inférieur au delta du type ou du sous-type désigné par la marque de type. De plus, si la contrainte de point fixe inclut une contrainte d'intervalle, la contrainte de point fixe n'est compatible avec la marque de type que si la contrainte d'intervalle est elle-même compatible avec la marque de type.
L'élaboration d'une telle indication de sous-type inclut l'élaboration de la contrainte d'intervalle, s'il y en a une ; elle crée un sous-type de point fixe dont les numéros de modèle sont définis par la contrainte de point fixe correspondante et également par la clause de longueur spécifiant petit, s'il y en a une. Une valeur d'un type de point fixe appartient à un sous-type de point fixe si et seulement si elle appartient à l'intervalle définie par le sous-type.
Les mêmes opérateurs arithmétiques sont prédéfinis pour tous les types de point fixe. La multiplication et la division de valeurs de point fixe donnent des résultats d'un type de point fixe prédéfini anonyme qui est appelé universal_fixed dans cette page de référence; la précision de ce type est arbitrairement fine. Les valeurs de ce type doivent être converties explicitement en un type numérique.
Remarques : Si S est un sous-type d'un type à virgule fixe ou d'un sous-type T, alors l'ensemble des numéros de modèle de S est un sous-ensemble de ceux de T. Si une clause de longueur a été donnée pour T, alors S et T ont la même valeur pour petit. Sinon, comme petit est une puissance de deux, le petit de S est égal au petit de T multiplié par une puissance non négative de deux.
Une contrainte d'intervalle est autorisée dans une indication de sous-type à virgule fixe, soit directement après la marque de type, soit dans le cadre d'une contrainte d'intervalle. Dans les deux cas, les limites de l'intervalle doivent appartenir au type de base de la marque de type.
Exemples :
- type VOLT is delta 0.125 range 0.0 .. 255.0;
- subtype ROUGH_VOLTAGE is VOLT delta 1.0; -- même gamme que VOLT
-
- -- Une fraction pure nécessitant tout l'espace disponible dans un mot
- -- sur une machine à complément à deux peut être déclaré comme le type FRACTION :
-
- DEL : constant := 1,0/2**(WORD_LENGTH - 1);
- type FRACTION is delta DEL range -1.0 .. 1.0 - DEL;
Les opérations de base d'un type à virgule fixe incluent les opérations impliquées dans l'affectation, les tests d'appartenance, la qualification, la conversion explicite de valeurs d'autres types numériques vers le type à virgule fixe et la conversion implicite de valeurs du type universal_real vers le type.
De plus, pour chaque type ou sous-type à virgule fixe T, les opérations de base incluent les attributs répertoriés ci-dessous. Dans cette présentation, T est considéré comme un sous-type (le sous-type T) pour toute propriété qui dépend des contraintes imposées par T ; les autres propriétés sont énoncées en termes de type de base de T.
Le premier groupe d'attributs donne les caractéristiques du sous-type T. Les attributs de ce groupe sont les attributs BASE, les attributs FIRST et LAST, l'attribut de représentation SIZE et les attributs suivants :
Attribut | Description |
---|---|
T'DELTA | Renvoie la valeur du delta spécifié dans la définition de précision fixe pour le sous-type T. La valeur de cet attribut est de type universal_real. |
T'MANTISSA | Renvoie le nombre de chiffres binaires dans la mantisse des numéros de modèle du sous-type T. La valeur de cet attribut est de type universal_integer. |
T'SMALL | Donne le plus petit numéro de modèle positif (différent de zéro) du sous-type T. La valeur de cet attribut est de type universal_real. |
T'LARGE | Donne le plus grand numéro de modèle positif du sous-type T. La valeur de cet attribut est de type universal_real. |
T'FORE | Renvoie le nombre minimal de caractères nécessaires à la partie entière de la représentation décimale de toute valeur du sous-type T, en supposant que la représentation n'inclut pas d'exposant, mais inclut un préfixe d'un caractère qui est soit un signe moins, soit un espace. (Ce nombre minimal n'inclut pas les zéros ou les soulignements superflus, et est d'au moins deux.) La valeur de cet attribut est de type universal_integer. |
T'AFT | Renvoie le nombre de chiffres décimaux nécessaires après le point pour prendre en compte la précision du sous-type T, sauf si le delta du sous-type T est supérieur à 0,1, auquel cas l'attribut renvoie la valeur un. (T'AFT est le plus petit entier positif N pour lequel (10**N)*T'DELTA est supérieur ou égal à un.) La valeur de cet attribut est de type universal_integer. |
Les attributs du deuxième groupe incluent les attributs suivants donnant les caractéristiques des numéros sûrs :
Attribut | Description |
---|---|
T'SAFE_SMALL | Génère le plus petit nombre sûr positif (non nul) du type de base de T. La valeur de cet attribut est de type universal_real. |
T'SAFE_LARGE | Donne le plus grand nombre positif sûr du type de base de T. La valeur de cet attribut est de type universal_real. |
De plus, les attributs A'SIZE et A'ADDRESS sont définis pour un objet A de type à virgule fixe. Enfin, pour chaque type ou sous-type à virgule fixe T, il existe les attributs dépendants de la machine T'MACHINE_ROUNDS et T'MACHINE_OVERFLOWS.
Outre les opérations de base, les opérations d'un type à virgule fixe incluent les opérateurs relationnels et les opérateurs arithmétiques prédéfinis suivants : les opérateurs d'addition binaire et unaire - et +, les opérateurs de multiplication * et /, et l'opérateur abs.
Les opérations d'un sous-type sont les opérations correspondantes du type à l'exception des suivantes : affectation, tests d'appartenance, qualification, conversion explicite et les attributs du premier groupe : les effets de ces opérations sont redéfinis en fonction du sous-type.
Remarques : La valeur de l'attribut T'FORE ne dépend que de la portée du sous-type T. La valeur de l'attribut T'AFT ne dépend que de la valeur de T'DELTA. Les relations suivantes existent entre les attributs d'un type à virgule fixe :
- T'LARGE = (2**T'MANTISSA - 1) * T'SMALL
- T'SAFE_LARGE = T'BASE'LARGE
- T'SAFE_SMALL = T'BASE'SMALL
Types de tableau
Un objet tableau est un objet composite constitué de composantes ayant le même sous-type. Le nom d'une composante d'un tableau utilise une ou plusieurs valeurs d'index appartenant à des types discrets spécifiés. La valeur d'un objet tableau est une valeur composite constituée des valeurs de ses composantes :
definition_du_type_de_tableau ::= definition_de_tableau_sans_contrainte | definition_de_tableau_contraint definition_de_tableau_sans_contrainte ::= array(definition_du_sous_type_d_index |, efinition_du_sous_type_d_index|) of indication_sous_type_de_composante definition_de_tableau_contraint ::= array contrainte_index of indication_sous_type_de_composante definition_du_sous_type_d_index ::= marque_type range <> contrainte_index ::= (intervalle_discret |, intervalle_discret|) intervalle_discret ::= indication_de_sous_type_discrete | range |
Un objet tableau est caractérisé par le nombre d'index (la dimensionnalité du tableau), le type et la position de chaque index, les limites inférieures et supérieures de chaque index, ainsi que le type et la contrainte possible des composantes. L'ordre des index est important.
Un tableau unidimensionnel possède un composant distinct pour chaque valeur d'index possible. Un tableau multidimensionnel possède une composante distincte pour chaque séquence possible de valeurs d'index pouvant être formée en sélectionnant une valeur pour chaque position d'index (dans l'ordre donné). Les valeurs possibles pour un index donné sont toutes les valeurs comprises entre les limites inférieure et supérieure, incluses ; cette intervalle de valeurs est appelée intervalle d'index.
Une définition de tableau sans contrainte définit un type de tableau. Pour chaque objet ayant le type de tableau, le nombre d'index, le type et la position de chaque index, ainsi que le sous-type des composants sont comme dans la définition de type ; les valeurs des limites inférieure et supérieure pour chaque index appartiennent au sous-type d'index correspondant, à l'exception des tableaux nuls. Le sous-type d'index pour une position d'index donnée est, par définition, le sous-type désigné par la marque de type de la définition de sous-type d'index correspondante. Le délimiteur composé <> (appelé boîte) d'une définition de sous-type d'index représente un intervalle indéfinie (différents objets du type n'ont pas besoin d'avoir les mêmes limites). L'élaboration d'une définition de tableau sans contrainte crée un type de tableau; cette élaboration inclut celle de l'indication de sous-type de composante.
Une définition de tableau avec contrainte définit à la fois un type de tableau et un sous-type de ce type :
- Le type de tableau est un type anonyme implicitement déclaré ; ce type est défini par une définition de tableau sans contrainte (implicite), dans laquelle l'indication de sous-type de composante est celle de la définition de tableau avec contrainte, et dans laquelle la marque de type de chaque définition de sous-type d'index désigne le sous-type défini par l'intervalle discrète correspondante.
- Le sous-type de tableau est le sous-type obtenu par l'imposition de la contrainte d'index sur le type de tableau.
Si une définition de tableau restreinte est donnée pour une déclaration de type, le nom simple déclaré par cette déclaration désigne le sous-type de tableau.
L'élaboration d'une définition de tableau restreinte crée le type de tableau et le sous-type de tableau correspondants. Pour cette élaboration, la contrainte d'index et l'indication de sous-type de composant sont élaborées. L'évaluation de chaque intervalle discrète de la contrainte d'index et l'élaboration de l'indication de sous-type de composante sont effectuées dans un ordre n'étant pas défini par le langage de programmation.
Exemples de déclarations de type avec des définitions de tableau non restreintes :
Exemples de déclarations de type avec des définitions de tableau contraintes :
Exemples de déclarations d'objet avec des définitions de tableaux contraints :
Remarque : pour un tableau unidimensionnel, la règle donnée signifie qu'une déclaration de type avec une définition de tableau contrainte telle que :
- type T is array(POSITIVE range MIN .. MAX) of COMPONENT;
est équivalent (en l'absence d'une dépendance d'ordre incorrecte) à la succession de déclarations :
- subtype index_subtype is POSITIVE range MIN .. MAX;
- type array_type is array(index_subtype range <>) of COMPONENT;
- subtype T is array_type(index_subtype);
où index_subtype et array_type sont tous deux anonymes. Par conséquent, T est le nom d'un sous-type et tous les objets déclarés avec cette marque de type sont des tableaux ayant les mêmes limites. Des transformations similaires s'appliquent aux tableaux multidimensionnels.
Une transformation similaire s'applique à un objet dont la déclaration inclut une définition de tableau contraint. Une conséquence de cela est qu'aucun de ces objets n'a le même type.
Contraintes d'index et d'intervalles discrètes
Une contrainte d'index détermine l'intervalle de valeurs possibles pour chaque index d'un type de tableau, et donc les limites de tableau correspondantes.
Pour un intervalle discrète utilisée dans une définition de tableau contraint et définie par un intervalle, une conversion implicite vers le type prédéfini INTEGER est supposée si chaque limite est soit un littéral numérique, un nombre nommé ou un attribut, et le type des deux limites (avant la conversion implicite) est le type universal_integer. Sinon, les deux limites doivent être du même type discret, autre que universal_integer, ce type doit être déterminable indépendamment du contexte, mais en utilisant le fait que le type doit être discret et que les deux limites doivent avoir le même type. Ces règles s'appliquent également à un intervalle discrète utilisée dans une règle d'itération ou dans la déclaration d'une famille d'entrées.
Si une contrainte d'index suit une marque de type dans une indication de sous-type, le type ou le sous-type désigné par la marque de type ne doit pas déjà imposer une contrainte d'index. La marque de type doit désigner soit un type de tableau sans contrainte, soit un type d'accès dont le type désigné est un tel type de tableau. Dans les deux cas, la contrainte d'index doit fournir un intervalle discrète pour chaque index du type de tableau et le type de chaque intervalle discrète doit être le même que celui de l'index correspondant.
Une contrainte d'index est compatible avec le type désigné par la marque de type si et seulement si la contrainte définie par chaque intervalle discrète est compatible avec le sous-type d'index correspondant. Si l'une des intervalles discrètes définit un intervalle nulle, tout tableau ainsi contraint est un tableau nul, n'ayant aucun composante. Une valeur de tableau satisfait une contrainte d'index si à chaque position d'index, la valeur de tableau et la contrainte d'index ont les mêmes limites d'index. (Notez cependant que l'affectation et certaines autres opérations sur les tableaux impliquent une conversion de sous-type implicite.)
Les limites de chaque objet tableau sont déterminées comme suit :
- Pour une variable déclarée par une déclaration d'objet, l'indication de sous-type de la déclaration d'objet correspondante doit définir un sous-type de tableau contraint (et, par conséquent, les limites). La même exigence existe pour l'indication de sous-type d'une déclaration de composante, si le type du composante d'enregistrement est un type de tableau; et pour l'indication de sous-type de composante d'une définition de type de tableau, si le type des composants du tableau est lui-même un type de tableau.
- Pour une constante déclarée par une déclaration d'objet, les limites de la constante sont définies par la valeur initiale si le sous-type de la constante n'est pas contraint; elles sont sinon définies par ce sous-type (dans ce dernier cas, la valeur initiale est le résultat d'une conversion implicite de sous-type). La même règle s'applique à un paramètre formel générique de mode in.
- Pour un objet tableau désigné par une valeur d'accès, les limites doivent être définies par l'allocateur créant l'objet tableau. (L'objet alloué est contraint avec les valeurs correspondantes des bornes.)
- Pour un paramètre formel d'un sous-programme ou d'une entrée, les bornes sont obtenues à partir du paramètre réel correspondant. (Le paramètre formel est contraint avec les valeurs correspondantes des bornes.)
- Pour une déclaration de renommage et pour un paramètre formel générique de mode in out, les bornes sont celles de l'objet renommé ou du paramètre réel générique correspondant.
Pour l'élaboration d'une contrainte d'index, les intervalles discrètes sont évaluées dans un ordre n'étant pas défini par le langage de programmation.
Exemples de déclarations de tableaux incluant une contrainte d'index :
- BOARD : MATRIX(1 .. 8, 1 .. 8);
- RECTANGLE : MATRIX(1 .. 20, 1 .. 30);
- INVERSE : MATRIX(1 .. N, 1 .. N); -- N n'a pas besoin d'être statique
-
- FILTER : BIT_VECTOR(0 .. 31);
Exemple de déclaration de tableau avec un sous-type de tableau contraint :
- MY_SCHEDULE : SCHEDULE; -- tous les tableaux de type SCHEDULE ont les mêmes limites
Exemple de type d'enregistrement avec une composante étant un tableau :
Remarques : L'élaboration d'une indication de sous-type constituée d'une marque de type suivie d'une contrainte d'index vérifie la compatibilité de la contrainte d'index avec la marque de type. Tous les composantes d'un tableau ont le même sous-type. En particulier, pour un tableau de composantes étant des tableaux unidimensionnels, cela signifie que tous les composants ont les mêmes limites et donc la même longueur.
Opérations des types de tableau
Les opérations de base d'un type de tableau incluent les opérations impliquées dans l'affectation et les agrégats (sauf si le type de tableau est limité), les tests d'appartenance, les composantes indexés, la qualification et la conversion explicite ; pour les tableaux unidimensionnels, les opérations de base incluent également les opérations impliquées dans les tranches, ainsi que les littéraux de chaîne de caractères si le type de composante est un type de caractère. Si A est un objet de tableau, une valeur de tableau ou un sous-type de tableau contraint, les opérations de base incluent également les attributs répertoriés ci-dessous. Ces attributs ne sont pas autorisés pour un type de tableau sans contrainte. Le paramètre N utilisé dans les désignateurs d'attribut pour la N-ième dimension d'un tableau doit être une expression statique de type universal_integer. La valeur de N doit être positive (différente de zéro) et ne pas être supérieure à la dimension du tableau :
Attribut | Description |
---|---|
A'FIRST | Renvoie la limite inférieure du première intervalle d'index. La valeur de cet attribut a le même type que cette limite inférieure. |
A'FIRST(N) | Donne la limite inférieure de l'intervalle d'index N. La valeur de cet attribut a le même type que cette limite inférieure. |
A'LAST | Renvoie la limite supérieure du premier intervalle d'index. La valeur de cet attribut a le même type que cette limite supérieure. |
A'LAST(N) | Donne la limite supérieure de l'intervalle d'index N. La valeur de cet attribut a le même type que cette limite supérieure. |
A'RANGE | Donne le premier intervalle d'index, c'est-à-dire l'intervalle A'FIRST .. A'LAST. |
A'RANGE(N) | Donne la N-ième intervalle d'index, c'est-à-dire l'intervalle A'FIRST(N).. A'LAST(N). |
A'LENGTH | Renvoie le nombre de valeurs de la première intervalle d'index (zéro pour une plage nulle). La valeur de cet attribut est de type universal_integer. |
A'LENGTH(N) | Renvoie le nombre de valeurs de l'intervalle d'index N-ième (zéro pour une plage nulle). La valeur de cet attribut est de type universal_integer. |
De plus, l'attribut T'BASE est défini pour un type ou sous-type de tableau T; l'attribut T'SIZE est défini pour un type ou sous-type de tableau T, et les attributs A'SIZE et A'ADDRESS sont définis pour un objet tableau A.
Outre les opérations de base, les opérations d'un type tableau incluent la comparaison prédéfinie pour l'égalité et l'inégalité, à moins que le type tableau ne soit limité. Pour les tableaux unidimensionnels, les opérations incluent la chaîne de caractères, à moins que le type tableau ne soit limité; si le type composante est un type discret, les opérations incluent également tous les opérateurs relationnels prédéfinis; si le type composant est un type booléen, alors les opérations incluent également l'opérateur de négation logique unaire not et les opérateurs logiques.
Exemples :
- -- FILTER'FIRST = 0 FILTER'LAST = 31 FILTER'LENGTH = 32
- -- RECTANGLE'LAST(1) = 20 RECTANGLE'LAST(2) = 30
Remarques : les attributs A'FIRST et A'FIRST(1) génèrent la même valeur. Une relation similaire existe pour les attributs A'LAST, A'RANGE et A'LENGTH. Les relations suivantes sont satisfaites (à l'exception d'un tableau nul) par les attributs ci-dessus si le type d'index est un type entier :
- A'LENGTH = A'LAST - A'FIRST + 1
- A'LENGTH(N) = A'LAST(N) - A'FIRST(N) + 1
Un type de tableau est limité si son type de composante est limité.
Le type STRING
Les valeurs du type prédéfini STRING sont des tableaux unidimensionnels du type prédéfini CHARACTER, indexés par des valeurs du sous-type prédéfini POSITIVE :
Exemples :
Remarques : Les littéraux de chaîne de caractères sont des opérations de base applicables au type STRING et à tout autre type de tableau unidimensionnel dont le type de composante est un type de caractère. L'opérateur de chaîne de caractères est un opérateur prédéfini pour le type STRING et pour les types de tableau unidimensionnel ; il est représenté par &. Les opérateurs relationnels <, <=, > et >= sont définis pour les valeurs de ces types et correspondent à l'ordre lexicographique.
Types d'enregistrement
Un objet enregistrement est un objet composite constitué de composantes nommés. La valeur d'un objet enregistrement est une valeur composite constituée des valeurs de ses composantes.
definition_du_type_d_enregistrement ::= record liste_de_composantes ::= end record liste_de_composantes ::= declaration_de_composante {declaration_de_composante} | [declaration_de_composante] variante_partie | null; declaration_de_composante ::= liste_identifiant : definition_du_sous_type_de_composante [:= expression]; definition_du_sous_type_de_composante ::= sous_type_indication |
Chaque déclaration de composante déclare une composante du type d'enregistrement. Outre les composantes déclarés par les déclarations de composante, les composantes d'un type d'enregistrement incluent tous les composantes déclarés par les spécifications discriminantes de la déclaration de type d'enregistrement. Les identifiants de tous les composantes d'un type d'enregistrement doivent être distincts. L'utilisation d'un nom désignant une composante d'enregistrement autre qu'un discriminant n'est pas autorisée dans la définition de type d'enregistrement déclarant la composante. Une déclaration de composante avec plusieurs identifiants équivaut à une séquence de déclarations de composantes uniques. Chaque déclaration de composante unique déclare un composant d'enregistrement dont le sous-type est spécifié par la définition de sous-type de composante.
Si une déclaration de composante inclut le délimiteur composé d'affectation suivi d'une expression, l'expression est l'expression par défaut de la composante d'enregistrement; l'expression par défaut doit être du type de composante. Les expressions par défaut ne sont pas autorisées pour les composantes ayant d'un type limité.
Si un type d'enregistrement n'a pas de partie discriminante, les mêmes composantes sont présents dans toutes les valeurs du type. Si la liste des composantes d'un type d'enregistrement est définie par le mot réservé null et qu'il n'y a pas de partie discriminante, alors le type d'enregistrement n'a pas de composantes et tous les enregistrements de ce type sont des enregistrements nuls.
L'élaboration d'une définition de type d'enregistrement crée un type d'enregistrement; elle consiste en l'élaboration de toutes les déclarations de composantes correspondantes (uniques), dans l'ordre dans lequel elles apparaissent, y compris toute déclaration de composant dans une partie variante. L'élaboration d'une déclaration de composant consiste en l'élaboration de la définition du sous-type de composante.
Pour l'élaboration d'une définition de sous-type de composante, si la contrainte ne dépend pas d'un discriminant, alors l'indication de sous-type est élaborée. Si, en revanche, la contrainte dépend d'un discriminant, alors l'élaboration consiste en l'évaluation de toute expression incluse n'étant pas un discriminant.
Exemples de déclarations de type d'enregistrement :
Exemples de variables d'enregistrement :
- TOMORROW, YESTERDAY : DATE;
- A, B, C : COMPLEX;
-
- -- les deux composantes de A, B et C sont implicitement initialisés à zéro
Remarques : L'expression par défaut d'une composante d'enregistrement est implicitement évaluée par l'élaboration de la déclaration d'un objet d'enregistrement, en l'absence d'une initialisation explicite. Si une déclaration de composant possède plusieurs identifiants, l'expression est évaluée une fois pour chaque composante de l'objet (puisque la déclaration est équivalente à une séquence de déclarations de composants uniques). Contrairement aux composants d'un tableau, les composantes d'un enregistrement n'ont pas besoin d'être du même type.
Discriminants
Une partie discriminante spécifie les discriminants d'un type. Un discriminant d'un enregistrement est une composante de l'enregistrement. Le type d'un discriminant doit être discret :
partie_discriminante ::= (specification_discriminante {; specification_discriminante}) specification_discriminante ::= liste_identifiant : marque_type [:= expression] |
Une partie discriminante n'est autorisée que dans la déclaration de type pour un type d'enregistrement, dans une déclaration de type privé ou une déclaration de type incomplète (la déclaration complète correspondante doit alors déclarer un type d'enregistrement) et dans la déclaration de paramètre générique pour un type privé formel.
Une spécification discriminante avec plusieurs identifiants est équivalente à une séquence de spécifications discriminantes simples. Chaque spécification discriminante simple déclare un discriminant. Si une spécification discriminante inclut le délimiteur composé d'affectation suivi d'une expression, l'expression est l'expression par défaut du discriminant ; l'expression par défaut doit être du type du discriminant. Les expressions par défaut doivent être fournies pour tous ou pour aucun des discriminants d'une partie discriminante.
L'utilisation du nom d'un discriminant n'est pas autorisée dans les expressions par défaut d'une partie discriminante si la spécification du discriminant est elle-même donnée dans la partie discriminante.
Dans une définition de type d'enregistrement, les seules utilisations autorisées du nom d'un discriminant du type d'enregistrement sont : dans les expressions par défaut des composantes d'enregistrement; dans une partie variante comme nom de discriminant; et dans une définition de sous-type de composante, soit comme limite dans une contrainte d'index, soit pour spécifier une valeur discriminante dans une contrainte discriminante. Un nom de discriminant utilisé dans ces définitions de sous-type de composante doit apparaître seul, et non dans le cadre d'une expression plus large. De telles définitions de sous-type de composant et de telles contraintes sont dites dépendantes d'un discriminant.
On dit qu'un composant dépend d'un discriminant s'il s'agit d'un composant d'enregistrement déclaré dans une partie variante, ou d'une composante d'enregistrement dont la définition de sous-type de composante dépend d'un discriminant, ou enfin, de l'un des sous-composants d'une composante dépendant lui-même d'un discriminant.
Chaque valeur d'enregistrement comprend une valeur pour chaque discriminant spécifié pour le type d'enregistrement; elle comprend également une valeur pour chaque composante d'enregistrement ne dépendant pas d'un discriminant. Les valeurs des discriminants déterminent quelles autres valeurs de composant se trouvent dans la valeur d'enregistrement.
L'affectation directe à un discriminant d'un objet n'est pas autorisée; de plus, un discriminant n'est pas autorisé en tant que paramètre réel de mode in out ou out, ou en tant que paramètre réel générique de mode in out. La seule manière autorisée de modifier la valeur d'un discriminant d'une variable est d'attribuer une valeur (complète) à la variable elle-même. De même, une affectation à la variable elle-même est la seule manière autorisée de modifier la contrainte d'un de ses composantes, si la définition du sous-type du composant dépend d'un discriminant de la variable.
L'élaboration d'une partie discriminante n'a pas d'autre effet.
Exemples :
- type BUFFER(SIZE : BUFFER_SIZE := 100) is
- record
- POS : BUFFER_SIZE := 0;
- VALUE : STRING(1 .. SIZE);
- end record;
-
- type SQUARE(SIDE : INTEGER) is
- record
- MAT : MATRIX(1 .. SIDE, 1 .. SIDE);
- end record;
-
- type DOUBLE_SQUARE(N UMBER : INTEGER) is
- record
- LEFT : SQUARE (NUMBER);
- RIGHT : SQUARE (NUMBER);
- end record;
-
- type ITEM(NUMBER : POSITIVE) is
- record
- CONTENT : INTEGER;
- -- aucune composante ne dépend du discriminant
- end record;
Contraintes discriminantes
Une contrainte discriminante n'est autorisée que dans une indication de sous-type, après une marque de type. Cette marque de type doit désigner soit un type avec des discriminants, soit un type d'accès dont le type désigné est un type avec des discriminants. Une contrainte discriminante spécifie les valeurs de ces discriminants :
contrainte_discriminante ::= (association_discriminante |, association_discriminante|) association_discriminante ::= [ discriminant_simple_nom {| discriminant_simple_nom I =>] expression |
Chaque association discriminante associe une expression à un ou plusieurs discriminants. Une association discriminante est dite nommée si les discriminants sont spécifiés explicitement par leurs noms ; sinon, elle est dite positionnelle. Pour une association positionnelle, le (seul) discriminant est implicitement spécifié par la position, dans l'ordre textuel. Les associations nommées peuvent être données dans n'importe quel ordre, mais si des associations positionnelles et nommées sont utilisées dans la même contrainte discriminante, alors les associations positionnelles doivent apparaître en premier, à leur position normale. Ainsi, une fois qu'une association nommée est utilisée, le reste de la contrainte discriminante ne doit utiliser que des associations nommées.
Pour une association discriminante nommée, les noms des discriminants doivent désigner les discriminants du type pour lequel la contrainte discriminante est donnée. Une association discriminante avec plus d'un nom de discriminant n'est autorisée que si les discriminants nommés sont tous du même type. De plus, pour chaque association discriminante (qu'elle soit nommée ou positionnelle), l'expression et les discriminants associés doivent avoir le même type. Une contrainte discriminante doit fournir exactement une valeur pour chaque discriminant du type.
Une contrainte discriminante est compatible avec le type désigné par une marque de type, si et seulement si chaque valeur discriminante appartient au sous-type du discriminant correspondant. De plus, pour chaque sous-composant dont la spécification de sous-type de composant dépend d'un discriminant, la valeur discriminante est substituée au discriminant dans cette spécification de sous-type de composant et la compatibilité de l'indication de sous-type résultante est vérifiée.
Une valeur composite satisfait une contrainte discriminante si et seulement si chaque discriminant de la valeur composite a la valeur imposée par la contrainte discriminante.
Les valeurs initiales des discriminants d'un objet d'un type à discriminants sont déterminées comme suit :
- Pour une variable déclarée par une déclaration d'objet, l'indication de sous-type de la déclaration d'objet correspondante doit imposer une contrainte de discriminant sauf si des expressions par défaut existent pour les discriminants ; les valeurs discriminantes sont définies soit par la contrainte, soit, en son absence, par les expressions par défaut. La même exigence existe pour l'indication de sous-type d'une déclaration de composant, si le type du composant d'enregistrement possède des discriminants ; et pour l'indication de sous-type de composante d'un type de tableau, si le type des composants du tableau est un type à discriminants.
- Pour une constante déclarée par une déclaration d'objet, les valeurs des discriminants sont celles de la valeur initiale si le sous-type de la constante est sans contrainte; elles sont sinon définies par ce sous-type (dans ce dernier cas, une exception est levée si la valeur initiale n'appartient pas à ce sous-type). La même règle s'applique à un paramètre générique de mode in.
- Pour un objet désigné par une valeur d'accès, les valeurs discriminantes doivent être définies par l'allocateur créant l'objet. (L'objet alloué est contraint avec les valeurs discriminantes correspondantes.)
- Pour un paramètre formel d'un sous-programme ou d'une entrée, les discriminants du paramètre formel sont initialisés avec ceux du paramètre actuel correspondant. (Le paramètre formel est contraint si le paramètre actuel correspondant est contraint, et dans tous les cas si le mode est in ou si le sous-type du paramètre formel est contraint.)
- Pour une déclaration de renommage et pour un paramètre formel générique de mode in out, les discriminants sont ceux de l'objet renommé ou du paramètre actuel générique correspondant.
Pour l'élaboration d'une contrainte discriminante, les expressions données dans les associations discriminantes sont évaluées dans un ordre n'étant pas défini par le langage; l'expression d'une association nommée est évaluée une fois pour chaque discriminant nommé.
Exemples (en utilisant les types déclarés dans la section précédente) :
- LARGE : BUFFER(200); -- contraint, toujours 200 caractères (valeur discriminante explicite)
- MESSAGE : BUFFER; -- sans contrainte, initialement 100 caractères (valeur discriminante par défaut)
- BASIS : SQUARE(5); -- contraint, toujours 5 par 5
- ILLEGAL : SQUARE; -- illégal, un CARRÉ doit être contraint
Remarque : Les règles ci-dessus et les règles définissant l'élaboration d'une déclaration d'objet garantissent que les discriminants ont toujours une valeur. En particulier, si une contrainte discriminante est imposée à une déclaration d'objet, chaque discriminant est initialisé avec la valeur spécifiée par la contrainte. De même, si le sous-type d'un composant a une contrainte discriminante, les discriminants de la composante sont initialisés en conséquence.
Parties variantes
Un type d'enregistrement avec une partie variante spécifie des listes alternatives de composantes. Chaque variante définit les composants pour la ou les valeurs correspondantes du discriminant :
variante_partie ::= case discriminant_simple_nom is variante | variante) end case; variante ::= when choix (| choix) => liste_de_composantes choix ::= expression_simple | intervalle_discrete | others | composante_simple_nom |
Chaque variante commence par une liste de choix qui doivent être du même type que le discriminant de la partie variante. Le type du discriminant d'une partie variante ne doit pas être un type formel générique. Si le sous-type du discriminant est statique, alors chaque valeur de ce sous-type doit être représentée une fois et une seule fois dans l'ensemble des choix de la partie variante, et aucune autre valeur n'est autorisée. Sinon, chaque valeur du type (de base) du discriminant doit être représentée une fois et une seule fois dans l'ensemble des choix.
Les expressions simples et les intervalles discrets données comme choix dans une partie variante doivent être statiques. Un choix défini par un intervalle discrète représente toutes les valeurs de la plage correspondante (aucune si une plage nulle). Le choix autres n'est autorisé que pour la dernière variante et comme son seul choix ; il représente toutes les valeurs (éventuellement aucune) non données dans les choix des variantes précédentes. Un nom simple de composant n'est pas autorisé comme choix d'une variante (bien qu'il fasse partie de la syntaxe du choix).
Une valeur d'enregistrement contient les valeurs des composantes d'une variante donnée si et seulement si la valeur discriminante est égale à l'une des valeurs spécifiées par les choix de la variante. Cette règle s'applique à son tour à toute autre variante étant elle-même incluse dans la liste des composantes de la variante donnée. Si la liste des composants d'une variante est spécifiée par null, la variante n'a aucune composante.
Exemple de type d'enregistrement avec une partie variante :
Exemples de sous-types d'enregistrement :
- subtype DRUM_UNIT is PERIPHERAL(DRUM);
- subtype DISK_UNIT is PERIPHERAL(DISK);
Exemples de variables d'enregistrement contraintes :
- WRITER : PERIPHERAL(UNIT => PRINTER);
- ARCHIVE : DISK_UNIT;
Remarque : Les choix avec des valeurs discrètes sont également utilisés dans les instructions case et dans les agrégats de tableaux. Les choix avec des noms simples de composants sont utilisés dans les agrégats d'enregistrements.
Opérations des types d'enregistrement
Les opérations de base d'un type d'enregistrement incluent les opérations impliquées dans l'affectation et les agrégats (sauf si le type est limité), les tests d'appartenance, la sélection des composantes d'enregistrement, la qualification et la conversion de type (pour les types dérivés).
Pour tout objet A d'un type avec des discriminants, les opérations de base incluent également l'attribut suivant :
Attribut | Description |
---|---|
A'CONSTRAINED | Renvoie la valeur TRUE si une contrainte discriminante s'applique à l'objet A, ou si l'objet est une constante (y compris un paramètre formel ou un paramètre formel générique de mode in); renvoie la valeur FALSE dans le cas contraire. Si A est un paramètre formel générique de mode in out, ou si A est un paramètre formel de mode in out ou out et que la marque de type donnée dans la spécification de paramètre correspondante désigne un type sans contrainte avec des discriminants, alors la valeur de cet attribut est obtenue à partir de celle du paramètre actuel correspondant. La valeur de cet attribut est du type prédéfini BOOLEAN. |
De plus, les attributs T'BASE et T'SIZE sont définis pour un type ou sous-type d'enregistrement T; les attributs A'SIZE et A'ADDRESS sont définis pour un objet d'enregistrement A. Outre les opérations de base, les opérations d'un type d'enregistrement incluent la comparaison prédéfinie pour l'égalité et l'inégalité, sauf si le type est limité.
Remarque : un type d'enregistrement est limité si le type de l'un de ses composants est limité.
Types access
Un objet déclaré par une déclaration d'objet est créé par l'élaboration de la déclaration d'objet et est désigné par un nom simple ou par une autre forme de nom. En revanche, il existe des objets créés par l'évaluation d'allocateurs et n'ayant pas de nom simple. L'accès à un tel objet est obtenu par une valeur access renvoyée par un allocateur; la valeur d'accès est dite désigner l'objet.
definition_du_type_d_acces ::= access sous_type_indication |
Pour chaque type d'accès, il existe un littéral null ayant une valeur d'accès null ne désignant aucun objet. La valeur null d'un type d'accès est la valeur initiale par défaut du type. Les autres valeurs d'un type d'accès sont obtenues par l'évaluation d'une opération spéciale du type, appelée allocateur. Chacune de ces valeurs d'accès désigne un objet du sous-type défini par l'indication de sous-type de la définition du type d'accès ; ce sous-type est appelé le sous-type désigné : le type de base de ce sous-type est appelé le type désigné. Les objets désignés par les valeurs d'un type d'accès forment une collection implicitement associée au type.
L'élaboration d'une définition de type d'accès consiste en l'élaboration de l'indication de sous-type et crée un type d'accès.
Si un objet d'accès est constant, la valeur d'accès contenue ne peut pas être modifiée et désigne toujours le même objet. En revanche, la valeur de l'objet désigné ne doit pas nécessairement rester la même (l'affectation à l'objet désigné est autorisée à moins que le type désigné ne soit limité).
Les seules formes de contrainte autorisées après le nom d'un type access dans une indication de sous-type sont les contraintes d'index et les contraintes discriminantes. Une valeur d'accès appartient à un sous-type correspondant d'un type access soit si la valeur d'accès est la valeur nulle, soit si la valeur de l'objet désigné satisfait la contrainte.
Exemples :
- type FRAME is access MATRIX;
- type BUFFER_NAME is access BUFFER;
Remarques : Une valeur access délivrée par un allocateur peut être affectée à plusieurs objets d'accès. Il est donc possible qu'un objet créé par un allocateur soit désigné par plusieurs variables ou constantes du type access. Une valeur d'accès ne peut désigner qu'un objet créé par un allocateur ; en particulier, elle ne peut pas désigner un objet déclaré par une déclaration d'objet.
Si le type des objets désignés par les valeurs access est un type tableau ou un type avec discriminants, ces objets sont contraints soit par les limites du tableau, soit par les valeurs discriminantes fournies implicitement ou explicitement pour les allocateurs correspondants.
Les valeurs d'accès sont appelées pointeurs ou références dans certains autres langages.
Déclarations de type incomplètes
Il n'existe aucune limitation particulière concernant le type désigné d'un type d'accès. En particulier, le type d'une composante du type désigné peut être un autre type d'accès, voire le même type access. Cela permet d'obtenir des types d'accès mutuellement dépendants et récursifs. Leurs déclarations nécessitent une déclaration de type incomplète (ou privée) préalable pour un ou plusieurs types :
declaration_de_type_incomplete ::= type identifiant [partie_discriminante]; |
Pour chaque déclaration de type incomplète, il doit y avoir une déclaration correspondante d'un type avec le même identifiant. La déclaration correspondante doit être soit une déclaration de type complète, soit la déclaration d'un type de tâche. Dans le reste de cette section, les explications sont données en termes de déclarations de type complètes ; les mêmes règles s'appliquent également aux déclarations de types de tâches. Si la déclaration de type incomplète apparaît immédiatement dans une partie déclarative ou dans la partie visible d'une spécification de paquetage, alors la déclaration de type complète doit apparaître plus tard et immédiatement dans cette partie déclarative ou dans cette partie visible. Si la déclaration de type incomplète apparaît immédiatement dans la partie privée d'un paquetage, alors la déclaration de type complète doit apparaître plus tard et immédiatement dans la partie privée elle-même, ou dans la partie déclarative du corps du paquetage correspondant.
Une partie discriminante doit être donnée dans la déclaration de type complète si et seulement si elle est donnée dans la déclaration de type incomplète; si des parties discriminantes sont données, alors elles doivent être conformes. Avant la fin de la déclaration de type complète, la seule utilisation autorisée d'un nom désignant un type déclaré par une déclaration de type incomplète est comme marque de type dans l'indication de sous-type d'une définition de type d'accès; la seule forme de contrainte autorisée dans cette indication de sous-type est une contrainte discriminante.
L'élaboration d'une déclaration de type incomplète crée un type. Si la déclaration de type incomplète comporte une partie discriminante, cette élaboration inclut celle de la partie discriminante : dans un tel cas, la partie discriminante de la déclaration de type complète n'est pas élaborée.
Exemple de type récursif :
Exemples de types access mutuellement dépendants :
- type PERSON(SEX : GENDER); -- déclaration de type incomplète
- type CAR; -- déclaration de type incomplète
-
- type PERSON_NAME is access PERSON;
- type CAR_NAME is access CAR;
-
- type CAR is
- record
- NUMBER : INTEGER;
- OWNER : PERSON_NAME;
- end record;
-
- type PERSON(SEX : GENDER) is
- record
- NAME : STRING(1 .. 20);
- BIRTH : DATE;
- AGE : INTEGER range 0 .. 130;
- VEHICLE : CAR_NAME;
- case SEX is
- when M => WIFE : PERSON_NAME(SEX => F);
- when F => HUSBAND : PERSON_NAME(SEX => M);
- end case;
- end record;
-
- MY_CAR, Y0UR_CAR, NEXT_CAR : CAR_NAME; -- implicitement initialisé avec une valeur nulle
Opérations des types access
Les opérations de base d'un type access incluent les opérations d'affectation, les allocateurs pour le type d'accès, les tests d'appartenance, la qualification, la conversion explicite et la valeur null littérale. Si le type désigné est un type avec des discriminants, les opérations de base incluent la sélection des discriminants correspondants; si le type désigné est un type d'enregistrement, elles incluent la sélection des composants correspondants ; si le type désigné est un type de tableau, elles incluent la formation de composants indexés et de tranches ; si le type désigné est un type de tâche, elles incluent la sélection d'entrées et de familles d'entrées. De plus, les opérations de base incluent la formation d'un composant sélectionné avec le mot réservé all.
Si le type désigné est un type de tableau, les opérations de base incluent les attributs qui ont les désignateurs d'attribut FIRST, LAST, RANGE et LENGTH (de même, les désignateurs d'attribut de la N-ième dimension). Le préfixe de chacun de ces attributs doit être une valeur du type access. Ces attributs donnent les caractéristiques correspondantes de l'objet désigné. Si le type désigné est un type de tâche, les opérations de base incluent les attributs ayant les désignateurs d'attribut TERMINATED et CALLABLE. Le préfixe de chacun de ces attributs doit être une valeur du type access. Ces attributs donnent les caractéristiques correspondantes des objets de tâche désignés.
De plus, l'attribut T'BASE et les attributs de représentation T'SIZE et T'STORAGE_SIZE sont définis pour un type ou sous-type d'accès T ; les attributs A'SIZE et A ADDRESS sont définis pour un objet access A.
Outre les opérations de base, les opérations d'un type d'accès incluent la comparaison prédéfinie pour l'égalité et l'inégalité.
Parties déclaratives
Une partie déclarative contient des éléments déclaratifs (éventuellement aucun) :
partie_declarative ::= |element_declaratif_de_base| |element_declaratif_ulterieur| element_declaratif_de_base ::= declaration_de_base | clause_de_representation | clause_d_utilisation element_declaratif_ulterieur ::= corps | declaration_sous_programme | declaration_de_paquet | declaration_de_tache | declaration_generique | clause_d_utilisation | instantiation_generique corps ::= corps_propre | corps_stub corps_propre ::= sous_programme_corps | corps_du_paquet | tache_corps |
L'élaboration d'une partie déclarative consiste à élaborer les éventuels éléments déclaratifs dans l'ordre dans lequel ils sont donnés dans la partie déclarative. Après son élaboration, un élément déclaratif est dit élaboré. Avant la fin de son élaboration (y compris avant l'élaboration), l'élément déclaratif n'est pas encore élaboré.
Pour plusieurs formes d'élément déclaratif, les règles de langage (en particulier les règles de portée et de visibilité) sont telles qu'il est soit impossible, soit illégal d'utiliser une entité avant l'élaboration de l'élément déclaratif qui déclare cette entité. Par exemple, il n'est pas possible d'utiliser le nom d'un type pour une déclaration d'objet si la déclaration de type correspondante n'est pas encore élaborée. Dans le cas des corps, les vérifications suivantes sont effectuées :
- Pour un appel de sous-programme, on vérifie que le corps du sous-programme est déjà élaboré.
- Pour l'activation d'une tâche, on vérifie que le corps de l'unité de tâche correspondante est déjà élaboré.
- Pour l'instanciation d'une unité générique qui possède un corps, on vérifie que ce corps est déjà élaboré.
L'exception PROGRAM_ERROR est levée si l'une de ces vérifications échoue.
Si une déclaration de sous-programme, une déclaration de paquet, une déclaration de tâche ou une déclaration générique est un élément déclaratif d'une partie déclarative donnée, alors le corps (s'il y en a un) de l'unité de programme déclarée par l'élément déclaratif doit lui-même être un élément déclaratif de cette partie déclarative (et doit apparaître plus tard). Si le corps est un stub de corps, alors une sous-unité compilée séparément contenant le corps approprié correspondant est requise pour l'unité de programme.