Phases of translation
Les fichiers source C++ sont traités par le compilateur pour produire des programmes C++.
Processus de traduction
Le texte d'un programme C++ est conservé dans des unités appelées fichiers source .
Les fichiers source C++ subissent une traduction pour devenir une unité de traduction , comprenant les étapes suivantes :
- Associe chaque fichier source à une séquence de caractères.
- Convertit chaque séquence de caractères en une séquence de jetons de prétraitement, séparés par des espaces.
- Convertit chaque jeton de prétraitement en jeton, formant une séquence de jetons.
- Convertit chaque séquence de jetons en unité de traduction.
Un programme C++ peut être formé à partir d'unités de traduction traduites. Les unités de traduction traduites et les unités instanciées (les unités instanciées sont décrites dans la phase 8 ci-dessous) peuvent être sauvegardées individuellement ou sauvegardées dans une bibliothèque. Plusieurs unités de traduction communiquent entre elles par (par exemple) des symboles avec liaison externe ou des fichiers de données. Les unités de traduction peuvent être traduites séparément puis liées ultérieurement pour produire un programme exécutable.
Le processus ci-dessus peut être organisé en 9 phases de traduction .
Jetons de prétraitement
Un jeton de prétraitement est l'élément lexical minimal du langage dans les phases de traduction 3 à 6.
Les catégories de jetons de préprocesseur sont :
- noms d'en-tête (tels que < iostream > ou "myfile.h" )
|
(depuis C++20) |
- identifiants
- nombres de prétraitement (voir ci-dessous)
- littéraux de caractères , incluant les littéraux de caractères définis par l'utilisateur (depuis C++11)
- littéraux de chaîne , incluant les littéraux de chaîne définis par l'utilisateur (depuis C++11)
- opérateurs et ponctuateurs , incluant les jetons alternatifs
- caractères individuels non-blancs qui ne correspondent à aucune autre catégorie
-
Le programme est mal formé si le caractère correspondant à cette catégorie est
- apostrophe ( ' , U+0027),
- guillemet ( " , U+0022), ou
- un caractère n'appartenant pas au jeu de caractères de base .
Nombres de prétraitement
L'ensemble des jetons de prétraitement d'un nombre de prétraitement est un sur-ensemble de l'union des ensembles de jetons des littéraux entiers et des littéraux à virgule flottante :
.
(facultatif)
digit
pp-continue-seq
(facultatif)
|
|||||||||
| digit | - | un des chiffres 0-9 |
| pp-continue-seq | - | une séquence de pp-continue s |
Chaque pp-continue est l'un des éléments suivants :
| identifier-continue | (1) | ||||||||
| exp-char sign-char | (2) | ||||||||
.
|
(3) | ||||||||
’
digit
|
(4) | (depuis C++14) | |||||||
’
nondigit
|
(5) | (depuis C++14) | |||||||
| identifier-continue | - | tout caractère non initial d'un identifiant valide |
| exp-char | - |
l'un des caractères
P
,
p
,
(depuis C++11)
E
et
e
|
| sign-char | - |
l'un des caractères
+
et
-
|
| digit | - | l'un des chiffres 0-9 |
| nondigit | - | l'une des lettres latines A/a-Z/z et le tiret bas |
Un nombre de prétraitement n'a pas de type ni de valeur ; il acquiert les deux après une conversion réussie en un jeton littéral entier/flottant.
Espace blanc
Espace blanc consiste en commentaires , caractères d'espacement, ou les deux.
Les caractères suivants sont des caractères d'espacement :
- tabulation de caractère (U+0009)
- saut de ligne / caractère de nouvelle ligne (U+000A)
- tabulation de ligne (U+000B)
- saut de page (U+000C)
- espace (U+0020)
Les espaces blancs sont généralement utilisés pour séparer les jetons de préprocesseur, avec les exceptions suivantes :
- Il ne s'agit pas d'un séparateur dans le nom d'en-tête, le littéral de caractère et le littéral de chaîne.
- Les jetons de préprocessing séparés par des espaces blancs contenant des caractères de nouvelle ligne ne peuvent pas former des directives de préprocessing .
#include "my header" // OK, utilisation d'un nom d'en-tête contenant des espaces #include/*hello*/<iostream> // OK, utilisation d'un commentaire comme espace blanc #include <iostream> // Erreur : #include ne peut pas s'étendre sur plusieurs lignes "str ing" // OK, un seul jeton de prétraitement (littéral de chaîne) ' ' // OK, un seul jeton de prétraitement (littéral de caractère)
Maximal munch
Le maximal munch est la règle utilisée dans la phase 3 lors de la décomposition du fichier source en tokens de préprocesseur.
Si l'entrée a été analysée en jetons de prétraitement jusqu'à un caractère donné (sinon, le prochain jeton de prétraitement ne sera pas analysé, ce qui rend l'ordre d'analyse unique), le prochain jeton de prétraitement est généralement pris comme étant la plus longue séquence de caractères qui pourrait constituer un jeton de prétraitement, même si cela devait entraîner l'échec d'une analyse ultérieure. Ceci est communément appelé maximal munch .
int foo = 1; int bar = 0xE+foo; // Erreur : nombre de prétraitement invalide 0xE+foo int baz = 0xE + foo; // OK
En d'autres termes, la règle du « maximal munch » favorise les opérateurs et ponctuateurs multi-caractères :
int foo = 1; int bar = 2; int num1 = foo+++++bar; // Erreur : traité comme "foo++ ++ +baz", pas "foo++ + ++baz" int num2 = -----foo; // Erreur : traité comme "-- -- -foo", pas "- -- --foo"
La règle du maximal munch comporte les exceptions suivantes :
- Les jetons de prétraitement de nom d'en-tête ne sont formés que dans les cas suivants :
-
- après le include jeton de préprocesseur dans une directive #include
|
(depuis C++17) |
|
(depuis C++20) |
std::vector<int> x; // OK, “int” n'est pas un nom d'en-tête
- Si les trois caractères suivants sont < :: et que le caractère subséquent n'est ni : ni > , le < est traité comme un jeton de prétraitement autonome plutôt que comme le premier caractère du jeton alternatif < :: .
struct Foo { static const int v = 1; }; std::vector<::Foo> x; // OK, <: n'est pas interprété comme le jeton alternatif pour [ extern int y<::>; // OK, identique à “extern int y[];” int z<:::Foo::value:>; // OK, identique à “int z[::Foo::value];”
template<int i> class X { /* ... */ }; template<class T> class Y { /* ... */ }; Y<X<1>> x3; // OK, déclare une variable « x3 » de type « Y<X<1> > » Y<X<6>>1>> x4; // Erreur de syntaxe Y<X<(6>>1)>> x5; // OK
#define R "x" const char* s = R"y"; // littéral de chaîne brute mal formé, pas "x" "y" const char* s2 = R"(a)" "b)"; // un littéral de chaîne brute suivi d'un littéral de chaîne normale |
(depuis C++11) |
Jetons
Un token est l'élément lexical minimal du langage dans la phase de traduction 7.
Les catégories de token sont :
- identifiants
- mots-clés
- littéraux
- opérateurs et ponctuateurs (sauf les opérateurs de préprocesseur)
Phases de traduction
La traduction est effectuée as if dans l'ordre de la phase 1 à la phase 9. Les implémentations se comportent comme si ces phases distinctes se produisaient, bien qu'en pratique différentes phases puissent être fusionnées.
Phase 1 : Mappage des caractères source
|
1)
Les octets individuels du fichier source sont mappés (de manière définie par l'implémentation) aux caractères du
jeu de caractères source de base
. En particulier, les indicateurs de fin de ligne dépendants du système d'exploitation sont remplacés par des caractères de nouvelle ligne.
2)
L'ensemble des caractères de fichier source acceptés est défini par l'implémentation
(depuis C++11)
. Tout caractère de fichier source qui ne peut pas être mappé à un caractère du
jeu de caractères source de base
est remplacé par son
nom de caractère universel
(échappé avec
\u
ou
\U
) ou par une forme définie par l'implémentation qui est traitée de manière équivalente.
|
(jusqu'à C++23) | ||
|
Les fichiers d'entrée qui sont une séquence d'unités de code UTF-8 (fichiers UTF-8) sont garantis d'être pris en charge. L'ensemble des autres types de fichiers d'entrée pris en charge est défini par l'implémentation. Si l'ensemble n'est pas vide, le type d'un fichier d'entrée est déterminé de manière définie par l'implémentation qui inclut un moyen de désigner les fichiers d'entrée comme fichiers UTF-8, indépendamment de leur contenu (la reconnaissance du marqueur d'ordre des octets n'est pas suffisante).
|
(depuis C++23) |
Phase 2 : Éclissage des lignes
Phase 3 : Lexicalisation
// The following #include directive can de decomposed into 5 preprocessing tokens: // punctuators (#, < and >) // │ // ┌────────┼────────┐ // │ │ │ #include <iostream> // │ │ // │ └── header name (iostream) // │ // └─────────── identifier (include)
// Error: partial string literal "abc
// Error: partial comment /* comment
|
Lorsque les caractères du fichier source sont consommés pour former le prochain jeton de préprocesseur (c'est-à-dire qu'ils ne sont pas consommés dans le cadre d'un commentaire ou d'autres formes d'espace blanc), les noms de caractères universels sont reconnus et remplacés par l'élément désigné du
jeu de caractères de traduction
, sauf lors de la correspondance d'une séquence de caractères dans l'un des jetons de préprocesseur suivants :
|
(depuis C++23) |
|
2)
Toutes les transformations effectuées durant
la phase 1 et
(jusqu'à C++23)
la phase 2 entre les guillemets doubles initiaux et finaux de tout
littéral de chaîne brut
sont annulées.
|
(depuis C++11) |
- Chaque commentaire est remplacé par un caractère d'espace.
- Les caractères de nouvelle ligne sont conservés.
- Le fait que chaque séquence non vide de caractères d'espace blanc autres que la nouvelle ligne soit conservée ou remplacée par un caractère d'espace n'est pas spécifié.
Phase 4 : Prétraitement
Phase 5 : Détermination des encodages courants des littéraux de chaîne
|
1)
Tous les caractères dans les
littéraux de caractères
et les
littéraux de chaîne
sont convertis du jeu de caractères source vers l'
encodage
(qui peut être un encodage de caractères multi-octets tel que UTF-8, tant que les 96 caractères du
jeu de caractères de base
ont des représentations en un seul octet).
2)
Les
séquences d'échappement
et les noms de caractères universels dans les littéraux de caractères et les littéraux de chaîne non bruts sont développés et convertis vers l'encodage du littéral.
Si le caractère spécifié par un nom de caractère universel ne peut pas être encodé comme un seul point de code dans l'encodage de littéral correspondant, le résultat est défini par l'implémentation, mais il est garanti de ne pas être un caractère (large) nul. |
(jusqu'en C++23) |
|
Pour une séquence de deux ou plusieurs jetons de littéral de chaîne adjacents, un préfixe d'encodage commun est déterminé comme décrit ici . Chaque jeton de littéral de chaîne est alors considéré comme ayant ce préfixe d'encodage commun. (La conversion des caractères est déplacée vers la phase 3) |
(depuis C++23) |
Phase 6 : Concaténation des littéraux de chaîne
Les littéraux de chaîne adjacents sont concaténés.
Phase 7 : Compilation
La compilation a lieu : chaque jeton de prétraitement est converti en un jeton . Les jetons sont analysés syntaxiquement et sémantiquement, puis traduits en tant qu' unité de traduction .
Phase 8 : Instanciation des templates
Chaque unité de traduction est examinée pour produire une liste d'instanciations de modèles requises, y compris celles demandées par les instanciations explicites . Les définitions des modèles sont localisées, et les instanciations requises sont effectuées pour produire des unités d'instanciation .
Phase 9 : Édition des liens
Les unités de traduction, les unités d'instanciation et les composants de bibliothèque nécessaires pour satisfaire les références externes sont rassemblés en une image de programme qui contient les informations nécessaires à l'exécution dans son environnement d'exécution.
Notes
Les fichiers sources, les unités de traduction et les unités de traduction traduites ne doivent pas nécessairement être stockés sous forme de fichiers, et il n'est pas nécessaire qu'il y ait une correspondance un-à-un entre ces entités et toute représentation externe. La description est uniquement conceptuelle et ne spécifie aucune implémentation particulière.
|
La conversion effectuée lors de la phase 5 peut être contrôlée par des options de ligne de commande dans certaines implémentations : gcc et clang utilisent - finput - charset pour spécifier l'encodage du jeu de caractères source, - fexec - charset et - fwide - exec - charset pour spécifier respectivement les encodages des littéraux ordinaires et larges, tandis que Visual Studio 2015 Update 2 et ultérieur utilise / source - charset et / execution - charset pour spécifier respectivement le jeu de caractères source et l'encodage des littéraux. |
(jusqu'à C++23) |
Certains compilateurs n'implémentent pas les unités d'instanciation (également appelées template repositories ou template registries ) et compilent simplement chaque instanciation de template à la phase 7, stockant le code dans le fichier objet où elle est implicitement ou explicitement demandée, puis l'éditeur de liens fusionne ces instanciations compilées en une seule à la phase 9.
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Applicable à | Comportement publié | Comportement corrigé |
|---|---|---|---|
| CWG 787 | C++98 |
le comportement était indéfini si un fichier source non vide ne se
terminait pas par un caractère de nouvelle ligne à la fin de la phase 2 |
ajouter un caractère de nouvelle ligne terminal
dans ce cas |
| CWG 1104 | C++98 |
le jeton alternatif
<
:
provoquait
std::
vector
<
::
std::
string
>
d'être traité comme std:: vector [ : std:: string > |
ajout d'une règle d'analyse lexicale
supplémentaire pour traiter ce cas |
| CWG 1775 | C++11 |
la formation d'un nom de caractère universel à l'intérieur d'un littéral
de chaîne brut en phase 2 résultait en un comportement indéfini |
rendu bien défini |
| CWG 2747 | C++98 | la phase 2 vérifiait la jonction de fin de fichier après le collage, ce qui est inutile | suppression de la vérification |
| P2621R3 | C++98 |
les noms de caractères universels n'étaient pas autorisés à
être formés par collage de lignes ou concaténation de jetons |
autorisé |
Références
- Norme C++23 (ISO/CEI 14882:2024) :
-
- 5.2 Phases de traduction [lex.phases]
- Norme C++20 (ISO/CEI 14882:2020) :
-
- 5.2 Phases de traduction [lex.phases]
- Norme C++17 (ISO/CEI 14882:2017) :
-
- 5.2 Phases de traduction [lex.phases]
- Norme C++14 (ISO/CEI 14882:2014) :
-
- 2.2 Phases de la traduction [lex.phases]
- Norme C++11 (ISO/CEI 14882:2011) :
-
- 2.2 Phases de traduction [lex.phases]
- Norme C++03 (ISO/CEI 14882:2003) :
-
- 2.1 Phases de traduction [lex.phases]
- Norme C++98 (ISO/CEI 14882:1998) :
-
- 2.1 Phases de traduction [lex.phases]
Voir aussi
|
Documentation C
pour
Phases de traduction
|