<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Traductions littérales</title>
    <description>Histoires de compilation, thèmes et versions informatiques
</description>
    <link>https://blog.merigoux.fr/fr/</link>
    <atom:link href="https://blog.merigoux.fr/fr/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 04 Nov 2026 01:00:04 +0100</pubDate>
    <lastBuildDate>Wed, 04 Nov 2026 01:00:04 +0100</lastBuildDate>
    <generator>Jekyll v3.5.1</generator>
    
      <item>
        <title>Pour un Bureau de Transformation du Droit en Code</title>
        <description>&lt;p&gt;&lt;em&gt;Cet article a également été publié par le &lt;a href=&quot;https://www.lemonde.fr/blog/binaire/2022/11/29/pour-un-bureau-de-transformation-du-droit-en-code/&quot;&gt;blog Binaire du Monde&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Je ne comprends pas pourquoi mon allocation a été réduite le mois dernier.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Tu peux aller voir le simulateur, mais ne t’y fies pas trop c’est approximatif.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Je crois que j’ai été trop prélevée, je ne comprends pas comment mon taux a été calculé.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nombreux sont celles et ceux d’entre nous pour qui ces
phrases rappellent une expérience vécue. Quel est le point commun
entre ces situations ? Derrière les impôts, les allocations, les cotisations,
les retraites et nombre de dispositifs qui régissent notre vie en société,
se trouvent des opérations de calcul effectuées par des logiciels. Les interfaces
de ces logiciels ont remplacé les &lt;a href=&quot;https://halshs.archives-ouvertes.fr/halshs-00438938&quot;&gt;préposés au guichet&lt;/a&gt; depuis plus de trente ans,
avec des mots d’ordre de modernité,
d’efficacité et de facilité d’utilisation. Certes, je fais partie de la génération
qui a largement évité le remplissage de formulaires en trois exemplaires grâce aux
télédéclarations et aux nouveaux sites Internet du gouvernement,
dont l’initiative est aujourd’hui coordonnée
par la &lt;a href=&quot;https://www.numerique.gouv.fr/DINUM/&quot;&gt;DINUM&lt;/a&gt;. Mais force est de constater que derrière les interfaces
des logiciels, le traitement automatique de ces demandes génère de nombreuses
frictions et un sentiment de méfiance, qui est une des causes du &lt;a href=&quot;https://www.francetvinfo.fr/economie/menages/allocations-familiales/enquete-aides-sociales-ces-milliards-deuros-que-les-francais-ne-reclament-pas_5429830.html&quot;&gt;non-recours&lt;/a&gt; :
c’est la fracture numérique. Et les conséquences de cette fracture sont d’autant
plus graves que les personnes sont dans des situations précaires ou défavorisées.
C’est pour cette raison que je me penche depuis deux ans
sur &lt;em&gt;les programmes informatiques qui appliquent automatiquement le droit&lt;/em&gt;.&lt;/p&gt;

&lt;figure&gt;
	&lt;img src=&quot;/fr/assets/illustrations/simuaplcnaf.png&quot; alt=&quot;image&quot; /&gt;
	&lt;figcaption&gt;
		La page de résultats typique d'un &lt;a href=&quot;https://wwwd.caf.fr/wps/portal/caffr/aidesetservices/lesservicesenligne/estimervosdroits/lelogement&quot;&gt;simulateur officiel&lt;/a&gt;
	&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Mais comment ce sujet est-il devenu mon dada ? J’ai eu la chance de faire
des études poussées d’informatique dans une filière où la France est en pointe,
les &lt;a href=&quot;https://www.theatlantic.com/technology/archive/2017/09/saving-the-world-from-code/540393/&quot;&gt;méthodes formelles&lt;/a&gt;, qui s’occupent justement de sécuriser le fonctionnement
des logiciels dits “critiques” : contrôle des centrales nucléaires, pilotage
automatique des trains, des avions, etc. Puisque l’état de droit se réalise
quotidiennement et pour des millions de personnes par les logiciels qui
appliquent automatiquement le droit, je les considère
également comme critiques.&lt;/p&gt;

&lt;p&gt;Cependant, les méthodes formelles ne sont pas magiques.
Elles sont vulnérables par leur talon d’Achille : tout ce qui n’est pas capturé
dans le code informatique qu’on étudie. C’est avec cette limite en tête que je me
suis penché sur une fondation passant un peu inaperçue des édifices
mathématiques construits dans mon domaine de recherche : comment est-on sûr
que le programme informatique que l’on produit est bien celui que l’on veut  ?&lt;/p&gt;

&lt;h2 id=&quot;algorithmes-publics--service-public-administratif-ou-commun-numérique-&quot;&gt;Algorithmes publics : service public administratif ou commun numérique ?&lt;/h2&gt;

&lt;p&gt;C’est là où mon chemin croise celui du calcul des impôts et des prestations
sociales. En effet, tous les paramètres du calcul sont décrits très précisément par des
lois et des décrets. Facile alors : il suffit de traduire ce corpus juridique
en code informatique et hop, on applique le droit automatiquement. Facile, vraiment ?
Cette seule traduction fait l’objet de tout un domaine de recherche &lt;a href=&quot;https://link.springer.com/article/10.1007/s10506-022-09329-4&quot;&gt;depuis&lt;/a&gt;
&lt;a href=&quot;https://link.springer.com/article/10.1007/s10506-022-09326-7&quot;&gt;trois&lt;/a&gt; &lt;a href=&quot;https://link.springer.com/article/10.1007/s10506-022-09327-6&quot;&gt;décennies&lt;/a&gt;. Spoiler : c’est en fait très compliqué.&lt;/p&gt;

&lt;p&gt;En effet, la structure des textes juridiques est basée sur une forme de logique propre
qui rentre mal dans les cases habituelles des
informaticiens. De plus, il ne suffit pas de lire les lois et décrets pour savoir
comment le programme informatique doit fonctionner : il faut les interpréter,
et ce n’est pas une mince affaire ! C’est en partie pour cela que les juristes
font de très longues études et travaillent en collectif, par exemple dans une
administration ou un tribunal, où ils exercent leur expertise.&lt;/p&gt;

&lt;p&gt;Concrètement, la traduction du droit en code informatique entraîne une &lt;a href=&quot;https://hal.inria.fr/hal-03712130&quot;&gt;myriade de
micro-choix&lt;/a&gt; habituellement invisibles mais qui peuvent entraîner des
conséquences concrètes sur les utilisateurs du logiciel.
Par exemple, un logiciel peut prévoir
une case supplémentaire dans son formulaire d’entrée pour accommoder votre
situation particulière prévue par le droit, ou bien vous laisser vous débrouiller
pour trouver une combinaison magique des autres cases qui déclenche le “bon” résultat.&lt;/p&gt;

&lt;figure&gt;
	&lt;img src=&quot;/fr/assets/illustrations/interfaceimpots.png&quot; alt=&quot;image&quot; /&gt;
	&lt;figcaption&gt;
		Interface du &lt;a href=&quot;https://simulateur-ir-ifi.impots.gouv.fr/calcul_impot&quot;&gt;
    simulateur d'impôt sur le revenu&lt;/a&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Le logiciel qui applique automatiquement le droit doit être compris comme une extension du pouvoir
de l’administration, qui parfois peut être amenée à faire des choix discrétionnaires.
Celle-ci possède une marge de manoeuvre pour interpréter la loi, mais ces interprétations
peuvent être contestée devant le tribunal administratif.
Cependant, une prise de décision automatique par un logiciel peut obscurcir les
moyens par lesquels le droit est appliqué. Le législateur a donc prévu des garde-fous pour que les personnes
puissent comprendre ce qui leur arrive, et contester si elles le souhaitent.
Ainsi, l’&lt;a href=&quot;https://www.legifrance.gouv.fr/loda/article_lc/LEGIARTI000037823131/2022-11-09/&quot;&gt;article 47-2 de la loi informatique et libertés&lt;/a&gt; impose
à l’administration responsable du logiciel :&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[…] la maîtrise du traitement algorithmique et de ses évolutions
afin de pouvoir expliquer, en détail et sous une forme intelligible, […] la manière dont le traitement a été mis en œuvre [..]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Le code source de tels logiciels est aussi librement communicable à quiconque
en fait la demande au titre de l’article &lt;a href=&quot;https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000033218936&quot;&gt;L300-2 du code des relations entre le
public et l’administration&lt;/a&gt;. Enfin, un &lt;a href=&quot;https://guides.etalab.gouv.fr/algorithmes/&quot;&gt;guide pratique à l’usage des administrations&lt;/a&gt; rédigé
par Etalab résume le cadre juridique applicable.&lt;/p&gt;

&lt;p&gt;Tous ces droits sont beaux sur le papier mais ils se heurtent à une dure
réalité : la dette technique des administrations. La dette technique en informatique
grandit au fil du temps, de l’obsolescence des technologies et de la perte
de compétences sur les logiciels. J’ai &lt;a href=&quot;/fr/2021/03/01/moderniser-dgfip.html&quot;&gt;déjà documenté&lt;/a&gt; ce à quoi
pouvait ressembler cette dette technique à la DGFiP sur le calcul de l’impôt sur le
revenu : la complexité de la sédimentation des évolutions du droit au fil
des années rend difficile une application effective des droits théoriques exposés
ci-devant. En effet, on peut publier le code source mais si celui ci est incompréhensible
et/ou impossible à exécuter sur sa machine
(si vous arrivez à faire tourner le &lt;a href=&quot;https://www.nextinpact.com/article/28136/106298-les-allocations-familales-nous-ouvrent-code-source-leur-calculateur-daides&quot;&gt;code COBOL de la CNAF&lt;/a&gt;, &lt;a href=&quot;mailto:denis.merigoux@inria.fr&quot;&gt;prévenez-moi&lt;/a&gt; !),
cela ne sert pas à grand chose.
De plus, savoir expliquer en détail toutes les étapes d’un calcul automatique
nécessite une machinerie informatique qui est absente
de la plupart des logiciels de l’administration.&lt;/p&gt;

&lt;figure&gt;
	&lt;img src=&quot;/fr/assets/illustrations/code_cnaf.png&quot; alt=&quot;image&quot; /&gt;
	&lt;figcaption&gt;
		Extrait des sources COBOL &lt;a href=&quot;https://www.nextinpact.com/article/28136/106298-les-allocations-familales-nous-ouvrent-code-source-leur-calculateur-daides&quot;&gt;
    des logiciels de la CNAF&lt;/a&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Face à cette situation, il y a ceux qui poussent pour une réforme complète
de la manière de travailler des administrations, sous le slogan “&lt;em&gt;rules as code&lt;/em&gt;”.
Leur &lt;a href=&quot;https://www.oecd-ilibrary.org/governance/cracking-the-code_3afe6ba5-en&quot;&gt;manifeste&lt;/a&gt; est admirablement complet : il faut que les règles soient
écrites avec la manière de les informatiser en tête, dans un format
directement transposable dans un logiciel. Les services publics quant à eux
&lt;a href=&quot;https://apolitical.co/solution-articles/en/designing-for-legitimacy&quot;&gt;doivent se réorganiser&lt;/a&gt; pour atteindre des objectifs de transparence et
de prise en compte des alertes quant à l’impact des décisions automatisées (“&lt;em&gt;&lt;a href=&quot;https://www.digital.govt.nz/blog/what-is-better-rules/&quot;&gt;better rules&lt;/a&gt;&lt;/em&gt;”).
Cela pose un idéal à atteindre, mais tellement éloigné des pratiques actuelles
qu’il est difficile d’y aller directement. On peut également critiquer la volonté
d’étendre le domaine de l’automatisation au delà du &lt;em&gt;statu quo&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Pour réformer les manières de faire des administrations, d’autres proposent de
passer par la construction d’un &lt;a href=&quot;https://communs.mattischneider.fr/&quot;&gt;commun numérique&lt;/a&gt;.
En France, c’est le cas d’&lt;a href=&quot;https://fr.openfisca.org/&quot;&gt;OpenFisca&lt;/a&gt; qui promeut depuis une dizaine d’années une base de code
contributive contenant actuellement la quasi-intégralité du calcul des
impôts et prestations sociales. Originant de l’&lt;a href=&quot;https://openfisca.org/doc/manifest-history.html&quot;&gt;Institut des Politiques publiques&lt;/a&gt;
et d’une volonté des économistes universitaire, il est actuellement utilisé par
l’application &lt;a href=&quot;https//leximpact.an.fr/&quot;&gt;LexImpact&lt;/a&gt; (Assemblée nationale), et les simulateurs
&lt;a href=&quot;http://1jeune1solution.gouv.fr/&quot;&gt;1jeune1solution&lt;/a&gt; (ministère du travail) et &lt;a href=&quot;https://estime.pole-emploi.fr/&quot;&gt;Estime&lt;/a&gt; (Pôle emploi).
Ce commun a vocation à être utilisé également par des entreprises, des associations, etc.&lt;/p&gt;

&lt;p&gt;Son application principale dans l’administration, portée par une start-up d’État de la DINUM, le
&lt;a href=&quot;https://beta.gouv.fr/startups/mes-aides.html&quot;&gt;simulateur de droits mes-aides&lt;/a&gt; devenu le &lt;a href=&quot;http://mesdroitssociaux.gouv.fr/&quot;&gt;Portail Numérique des Droits Sociaux&lt;/a&gt;,
a connu une histoire tumultueuse &lt;a href=&quot;https://hal.archives-ouvertes.fr/hal-03138569&quot;&gt;racontée par ma collègue
Marie Alauzen&lt;/a&gt;, et qui pourrait se résumer par un choc des cultures qui a
dégénéré en conflit. En effet, la base de code d’OpenFisca a été
originellement pensée uniquement pour la simulation et non la production
de décisions administratives : elle n’a jamais été utilisée par les ministères
pour calculer “pour de vrai” (&lt;em&gt;liquider&lt;/em&gt;) les impôts ou les prestations sociales.
Les ministères sociaux craignaient alors que les résultats parfois erronés
de mes-aides ne leur soient opposés alors qu’ils n’avaient pas le contrôle total
sur la base de code du logiciel.&lt;/p&gt;

&lt;figure&gt;
	&lt;img src=&quot;/fr/assets/illustrations/simupndss.png&quot; alt=&quot;image&quot; /&gt;
	&lt;figcaption&gt;
		Avertissement sur la page de résultat du &lt;a href=&quot;https://www.mesdroitssociaux.gouv.fr&quot;&gt;
    portail numérique des droits sociaux&lt;/a&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Par ailleurs, le code
d’OpenFisca ne prend pas en compte tous les cas particuliers pourtant prévus
par le droit. Le processus de transformation du droit en code qui y est à l’œuvre
se base en partie sur des sources juridiques (lois, décrets) mais pas de manière
systématique. Certes, l’objectif d’OpenFisca
est de tendre vers la systématisation, mais ce processus &lt;a href=&quot;https://github.com/openfisca/openfisca-france/pull/1428&quot;&gt;dont j’ai fait l’expérience&lt;/a&gt;,
combiné aux spécificités techniques d’OpenFisca (Python), ne permet pas selon moi
d’atteindre un niveau d’assurance et de correction suffisant pour la production de décisions
administratives automatisées. Ce constat vaut également pour &lt;a href=&quot;https://publi.codes/&quot;&gt;Publicodes&lt;/a&gt;,
autre langage de programmation utilisé pour traduire le droit en code dans des simulateurs
développés et maintenu par l’administration (comme &lt;a href=&quot;https://mon-entreprise.urssaf.fr/simulateurs&quot;&gt;mon-entreprise.fr&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;comment-avancer-concrètement-&quot;&gt;Comment avancer concrètement ?&lt;/h2&gt;

&lt;p&gt;Nous avons maintenant tous les ingrédients du cocktail. D’un côté,
une science informatique de la précision et des décennies de recherche sur
la traduction du droit en code. De l’autre côté, une situation
frustrante où des administrations, même mises sous la pression d’initiatives externes,
n’arrivent pas à éliminer leur dette technique pour remplir leurs obligations
légales et diminuer les frictions liées à l’application automatique du droit.&lt;/p&gt;

&lt;p&gt;Je propose donc de mélanger tout cela au &lt;em&gt;shaker&lt;/em&gt; et d’esquisser un chemin
pour faire avancer l’état de l’application automatique du droit en France,
surtout en ce qui concerne les transferts financiers (impôts, prestions sociales).
J’identifie plusieurs axes concrets nécessaires à cette avancée.&lt;/p&gt;

&lt;h3 id=&quot;audit-et-rétro-ingénierie-des-systèmes-dinformation-administratifs&quot;&gt;Audit et rétro-ingénierie des systèmes d’information administratifs&lt;/h3&gt;

&lt;p&gt;Comme toutes les grandes organisations, l’administration a accumulé
depuis l’informatisation de ses systèmes dans les années 1960 à 1980 une certaine
dette technique. Cette dette technique se traduit par une perte
de maîtrise sur l’évolution et le changement des logiciels. “&lt;em&gt;L’informatique ne le permet pas…&lt;/em&gt;”,
&lt;a href=&quot;https://www.numerama.com/politique/720319-allocation-adultes-handicapes-non-notre-systeme-informatique-nest-pas-le-probleme.html&quot;&gt;admet la secrétaire d’État chargée des personnes handicapées&lt;/a&gt; le 17 juin 2021
au Sénat en réponse à une question sur la déconjugalisation de l’aide adulte handicapé (AAH).
Rien de conceptuellement impossible pourtant sur le calcul de la déconjugalisation,
mais le système d’information de la CNAF semble incapable d’effectuer les multiples passes de
calcul nécessaires à cette réforme, à l’instar du &lt;a href=&quot;https://hal.inria.fr/hal-03781578&quot;&gt;partage des aides au logement
en cas de garde alternée&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Comment débloquer ce genre de situations ? C’est ici que mon domaine d’expertise entre en scène.
Grâce à une analyse précise et exhaustive, il est
possible de rétro-ingénierier le comportement d’un logiciel accablé par la
dette technique, pour ensuite le répliquer à l’aide d’une technologie plus
moderne ou plus adaptée.&lt;/p&gt;

&lt;p&gt;Pour ce faire, point de baguette magique, mais le travail laborieux
et méticuleux d’archéologues informatiques, qui viennent
fouiller les logiciens anciens à la recherche d’indices sur ce qu’ils font et
comment ils ont été construit. La rigueur
des méthodes formelles de ces archéologues assure la qualité du résultat.
Le travail se fait à partir du code source des logiciels à auditer, mais aussi
de la mémoire des humains qui les ont conçus. Aussi, ce travail ne peut se faire que depuis
les bureaux des administrations où ces logiciels ont été développés et maintenus,
donc avec le soutien
d’une hiérarchie qui donne les moyens d’investir sur une nouvelle tâche qui
s’ajoute à la gestion des urgences du quotidien.&lt;/p&gt;

&lt;p&gt;C’est précisément &lt;a href=&quot;https://www.inria.fr/fr/mlang-modernisation-calcul-impot-revenu&quot;&gt;ce que j’ai fait&lt;/a&gt; à la DGFiP avec
le compilateur Mlang. Cette démarche de rétro-ingénierie s’accompagne
d’un véritable travail d’audit qui remonte les bugs et les problèmes. Elle
permet aussi d’isoler dans le système d’information la partie qui applique
le droit automatiquement à proprement parler du reste de la “plomberie” informatique qui
fait transiter les données d’un endroit à un autre.&lt;/p&gt;

&lt;figure&gt;
	&lt;img src=&quot;/fr/assets/illustrations/code_ir.png&quot; alt=&quot;image&quot; /&gt;
	&lt;figcaption&gt;
		Extrait du &lt;a href=&quot;https://gitlab.adullact.net/dgfip/ir-calcul&quot;&gt;
    code source de l'impôt sur le revenu&lt;/a&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;production-et-maintenance-de-logiciels-appliquant-automatiquement-le-droit&quot;&gt;Production et maintenance de logiciels appliquant automatiquement le droit&lt;/h3&gt;

&lt;p&gt;Une fois la rétro-ingénierie effectuée et la capacité de mouvement informatique
recouvrée, il nous faut des outils et une méthode permettant
de produire et maintenir les logiciels appliquant automatiquement le droit
dans le respect du cadre juridique et des contraintes de l’administration.&lt;/p&gt;

&lt;p&gt;En m’appuyant sur l’état de l’art du domaine, j’ai développé avec ma collègue
juriste &lt;a href=&quot;https://www.eui.eu/people?id=liane-huttner&quot;&gt;Liane Huttner&lt;/a&gt; le &lt;a href=&quot;https://hal.inria.fr/hal-02936606&quot;&gt;langage de programmation spécifique Catala&lt;/a&gt; ainsi
qu’une méthode de production du code impliquant un duo juriste-programmeur,
capable de discuter la correction de la traduction en franchissant les
barrières de l’interprétation. Nous avons pensé Catala directement pour un usage
administratif en production pour les problèmes de calcul d’impôts, de
prestations sociales et plus généralement de transferts financiers
définis par la loi ou la réglementation. Néanmoins, je ne pense pas que Catala
soit adapté à toutes les situations d’automatisation de l’application du droit
(l’aide à la rédaction des décisions de justice ou l’analyse de la jurisprudence par exemple)
et il faudra sûrement développer ou utiliser d’autres outils supplémentaires ou complémentaires.&lt;/p&gt;

&lt;p&gt;La production et la conception de logiciels appliquant automatiquement
le droit doit selon moi associer de manière
étroite et continue les juristes, gardiens de la bonne interprétation des lois
par l’administration dans le cadre de l’état de droit, et les programmeurs, artisans d’un code source lisible et maintenable. Cette
association devrait se faire dans de petites équipes travaillant directement
sur les sources législatives et réglementaires et le code source informatique :
le modèle traditionnel de production en “cycle en V”, reposant sur la production
d’un document de “spécification” intermédiaire, qui éloigne des textes originaux,
ne suffit souvent pas à désambiguïser les énoncés juridiques pour les programmeurs et
assurer le niveau de qualité nécessaire pour ces logiciels critiques.
L’enjeu de repenser la production des logiciels est crucial, car il faut empêcher
de nouveaux désastres techniques, organisationnels et humains
comme &lt;a href=&quot;https://www.assemblee-nationale.fr/14/rap-info/i1353.asp#P1125_206794&quot;&gt;Louvois&lt;/a&gt;.&lt;/p&gt;

&lt;figure&gt;
	&lt;img src=&quot;/fr/assets/illustrations/pair_programming_catala.jpg&quot; alt=&quot;image&quot; /&gt;
	&lt;figcaption&gt;
		Programmation en binôme juriste-programmeur avec Catala
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;formation-professionnelle-et-développement-dun-réseau-dexpertise&quot;&gt;Formation professionnelle et développement d’un réseau d’expertise&lt;/h3&gt;

&lt;p&gt;Denier axe concret d’avancée : il est important de reconnaître que la
traduction du droit en code informatique pour son application automatique
est un problème commun à beaucoup d’administrations, et suffisamment compliqué
pour nécessiter une expertise &lt;em&gt;ad-hoc&lt;/em&gt;. J’estime à une petite centaine le nombre
de personnes (juristes ou programmeurs) directement impliquées, au sein de l’État français et de ses opérateurs,
dans la spécification ou l’implémentation de logiciels destinés à l’application
automatique du droit. Voici en effet une liste non-exhaustive des logiciels
concernés : tous les impôts gérés par la Direction Générale des Finances Publiques (DGFiP), l’allocation de retour à l’emploi
gérée par Pôle Emploi, toutes les prestations sociales gérées par la Caisse Nationale
d’Allocations Familiales (CNAF) et la
Caisse Centrale de la Mutualité Sociale Agricole (CCMSA), le calcul des cotisations sociales
géré par l’Union de Recouvrement des Cotisations de Sécurité Sociale
et d’Allocations Familiales (URSSAF), le calcul des
pensions effectué par la Caisse Nationale d’Assurance Vieillesse (CNAV).
Ce à quoi on peut ajouter tous les statisticiens travaillant
dans les départements d’étude d’impact sur le modèle de micro-simulation
&lt;a href=&quot;https://www.insee.fr/fr/information/2021951&quot;&gt;Inès&lt;/a&gt;, au sein de la Direction de la Recherche, des Études,
de l’Évaluation et des Statistiques (DREES),
et de l’Institut national de la statistique et des études économiques (INSEE).&lt;/p&gt;

&lt;p&gt;Ces agents ont développé au fil des années à travailler sur
ces logiciels une double expertise. Premièrement, une expertise générale sur
la transformation du droit en code informatique et l’état d’esprit qu’elle nécessite ;
deuxièmement, une expertise très pointue sur le calcul qu’effectue
le logiciel et les textes juridiques qui le spécifient. Cette double
expertise est longue à acquérir (cela se compte en années d’expérience),
et la formation se fait actuellement “sur le tas” au sein même des équipes
gérant les logiciels. Il n’est d’ailleurs par rare d’y trouver des agents
cumulant plusieurs dizaines d’années d’expertise, véritables mémoires vivantes
du logiciel qui voient avec inquiétude l’émergence d’un &lt;em&gt;turn-over&lt;/em&gt; plus
élevé dans leur équipe, qui rendrait impossible le maintien des compétences sur le
long terme.&lt;/p&gt;

&lt;p&gt;La reconnaissance au niveau des ressources humaines de la spécificité de la
transformation du droit en code informatique doit être faite pour éviter la déperdition
des compétences, qui arrive avec les changements de poste vers des emplois hors
de ce domaine (souvent pour des raisons d’évolution de carrière). Cette
reconnaissance pourrait également déboucher sur la création d’un véritable
réseau professionnel, vivier de candidats pour des postes identifiés “traduction
du droit en code informatique”. Ce réseau pourrait assurer une partie de la
nécessaire formation professionnelle à ces enjeux, au moins sur la partie
d’expertise générale, et être un &lt;em&gt;pool&lt;/em&gt; d’expertise spécifique sur chacun des
logiciels qui appliquent automatiquement un morceau du droit.&lt;/p&gt;

&lt;h3 id=&quot;pour-un-bureau-de-transformation-du-droit-en-code&quot;&gt;Pour un Bureau de Transformation du Droit en Code&lt;/h3&gt;

&lt;p&gt;Nous avons ici développé trois axes concrets pour améliorer l’état
des logiciels appliquant automatiquement le droit en France : audit et
rétro-ingénierie des systèmes existants, outils adaptés pour la
maintenance et la production des traductions du droit vers le code informatique,
réseau d’expertise et de formation professionnelle à ces enjeux dans l’administration.&lt;/p&gt;

&lt;p&gt;Ces axes montrent un besoin récurrent de mutualisation d’expertise et d’outils
nécessaires à des équipes actuellement éparpillées dans l’administration.
Attention : chaque administration reste évidemment compétente dans son interprétation
du droit et sa traduction dans le fonctionnement des logiciels qu’elle gère.
Néanmoins, ce besoin de mutualisation appelle selon moi à la création
d’une institution centrale, que j’appellerais hypothétiquement “bureau de transformation
du droit en code”. Ce bureau serait investi d’une triple mission :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;fournir une aide opérationnelle et de l’expertise ponctuelle aux administrations
désirant réduire la dette technique de leurs systèmes informatiques appliquant automatiquement
le droit ;&lt;/li&gt;
  &lt;li&gt;maintenir et développer des outils et méthodologies pour la transformation
du droit en code, voire produire directement cette traduction ;&lt;/li&gt;
  &lt;li&gt;être le camp de base et l’école professionnelle du réseau des personnes directement
impliquées dans la transformation du droit en code au sein de l’administration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La composition de ce bureau devrait mélanger des programmeurs et des juristes, des agents
provenant des différentes administrations et des chercheurs, chacun
amenant sa perspective complémentaire sur le sujet et travaillant ensemble
sur les axes définis au-dessus. Les obligations légales
de transparence des algorithmes publics feraient nécessairement du bureau une
interface entre les administrations, et avec les personnes ou groupes travaillant
sur le sujet de l’explicabilité et de la loyauté de ces algorithmes publics,
à Étalab notamment.&lt;/p&gt;

&lt;p&gt;Ce bureau n’aurait pas vocation à centraliser tous les personnels de l’administration
travaillant sur les logiciels appliquant automatiquement le droit. Les directions
responsables de ces logiciels garderaient évidemment leurs équipes afférentes.
Il correspondrait donc à des emplois
supplémentaires, qui me semblent nécessaires à l’amélioration
de l’application automatique du droit en France.&lt;/p&gt;

</description>
        <pubDate>Tue, 29 Nov 2022 01:00:00 +0100</pubDate>
        <link>https://blog.merigoux.fr/fr/2022/11/29/bureau-loi-code.html</link>
        <guid isPermaLink="true">https://blog.merigoux.fr/fr/2022/11/29/bureau-loi-code.html</guid>
        
        
      </item>
    
      <item>
        <title>Avancer lentement et ne rien casser</title>
        <description>&lt;p&gt;Dans mon &lt;a href=&quot;/fr/2019/12/20/impots-formels.html&quot;&gt;dernier billet&lt;/a&gt;, je racontais ma tentative d’exploiter le code
source de l’impôt sur le revenu &lt;a href=&quot;https://gitlab.adullact.net/dgfip/ir-calcul&quot;&gt;publié par la DGFiP&lt;/a&gt;. J’écrivais alors
la chose suivante:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Au moment où ce billet est publié, le code généré par Mlang est cependant toujours
faux. En effet, un effet secondaire de ce travail de formalisation et de validation
a été de découvrir que le code M publié par la DGFiP ne contient pas toutes les
informations nécessaires à répliquer le calcul de l’impôt. Techniquement, le code
M est appelé plusieurs fois avec des valeurs particulières pour certaines variables.
Cette subtilité technique n’avait pas été détectée au moment du hackathon et de
la première publication du code. J’ai pu en informer la DGFiP, qui met actuellement
des moyens en œuvre pour me permettre de récupérer dans leur système
les informations concernant ces appels multiples du code M avec différents
paramètres. Une fois ces informations récupérées, je pourrai achever le travail
de formalisation et valider Mlang sur les jeux de tests officiels de la DGFiP.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Un an plus tard, la promesse est tenue! &lt;a href=&quot;https://gitlab.inria.fr/verifisc/mlang&quot;&gt;Mlang&lt;/a&gt;, le nouveau compilateur
pour le langage de programmation maison “M” de la DGFiP, est opérationnel et
permet de répliquer le calcul de l’impôt chez soi comme c’est fait à Bercy.
Cela permet de vérifier votre dernier avis d’imposition, mais également de
regarder plus précisément comment l’impôt sur le revenu est calculé ! Par exemple,
&lt;a href=&quot;https://gitlab.adullact.net/dgfip/ir-calcul/-/blob/master/sources2018m_6_7/chap-52.m#L87&quot;&gt;cette ligne&lt;/a&gt; applique le barème progressif de l’impôt après application
du quotient familial. Certes, tout n’est pas &lt;a href=&quot;https://gitlab.adullact.net/dgfip/ir-calcul/-/blob/master/sources2018m_6_7/chap-1.m#L302&quot;&gt;limpide&lt;/a&gt; et il faudrait
quasiment tout réécrire pour rendre ça compréhensible; mais c’est
&lt;a href=&quot;https://catala-lang.org/&quot;&gt;une autre histoire&lt;/a&gt;…&lt;/p&gt;

&lt;p&gt;Encore mieux, la DGFiP elle-même fait un pas vers plus de transparence et
de modernité, car Mlang deviendra à terme le compilateur officiel utilisé en
production par le service qui calcule l’impôt sur le revenu ! &lt;em&gt;Move fast and
break things&lt;/em&gt; est le crédo des informaticiens “disruptifs” du monde des startup;
ici, il s’agit plutôt d’appliquer le crédo inverse, &lt;em&gt;Move slow and fix things&lt;/em&gt;,
car on ne peut pas se permettre de casser l’impôt sur le revenu. Pour y arriver,
il faut néanmoins casser les méthodes traditionnelles de gestion de projet.&lt;/p&gt;

&lt;p&gt;Dans ce billet, je vous propose de plonger au cœur du réacteur du calcul de l’impôt,
et de comprendre comment notre domaine de recherche, les méthodes formelles,
a permis à &lt;a href=&quot;https://rmonat.fr/&quot;&gt;Raphaël Monat&lt;/a&gt; et moi de créer une alternative moderne et opérationnelle
à un composant critique des systèmes informatiques de la DGFiP. Le but est qu’après
avoir lu ce billet, vous soyez capable de lire et comprendre notre &lt;a href=&quot;https://hal.inria.fr/hal-03002266/&quot;&gt;article de recherche&lt;/a&gt;
sur le sujet (co-écrit avec mon directeur de thèse &lt;a href=&quot;https://jonathan.protzenko.fr/&quot;&gt;Jonathan Protzenko&lt;/a&gt;)
et que vous soyez incollables sur les avantages et inconvénients des langages
dédiés.&lt;/p&gt;

&lt;p&gt;Enfin, je m’aventure également sur une terrain plus organisationnel : à une
vision industrielle de l’informatique de gestion reposant sur l’emploi en masse
de programmeurs peu qualifiés, je fais le plaidoyer sur les projets critiques
d’un emploi d’artisans qualifié du logiciel. En bref: n’hésitez pas à recruter
plus de spécialistes des langages de programmation pour maintenir vos
logiciels critiques !&lt;/p&gt;

&lt;h2 id=&quot;une-architecture-inhabituelle&quot;&gt;Une architecture inhabituelle&lt;/h2&gt;

&lt;h3 id=&quot;lhéritage-de-30-ans-dexploitation&quot;&gt;L’héritage de 30 ans d’exploitation&lt;/h3&gt;

&lt;p&gt;La Direction Générale de Finances Publiques (DGFiP) a, parmi ses missions, celle
de lever l’impôt sur le revenu (IR). Le montant de l’impôt dépend des revenus
du foyer fiscal, selon un barème progressif. Chaque année, les ménages français
envoient à la DGFiP une déclaration de revenus, en grande majorité sous la
forme d’un formulaire à remplir sur &lt;a href=&quot;https://impots.gouv.fr&quot;&gt;impots.gouv.fr&lt;/a&gt;. Ce formulaire, auquel
vous avez sûrement déjà été confronté, comporte de multiples cases permettant
de différencier les sources de revenu : plusieurs centaines de cases en fait.
Et oui, le droit fiscal comporte énormément d’exceptions dont il faut tenir
compte lors du calcul de l’IR. On pourrait débattre de la pertinence d’une
simplification, mais ce n’est pas l’objet de ce billet.&lt;/p&gt;

&lt;p&gt;Donc une fois la déclaration reçue, la DGFiP dispose de tout ce dont elle a
besoin pour calculer l’impôt sur le revenu. Que se passe-t-il alors ? Toutes
les valeurs renseignées dans les cases de la déclaration se transforment en
entrée pour un programme informatique très spécial, qui calcule le montant
de l’impôt selon les règles législatives du Code Général des Impôts.&lt;/p&gt;

&lt;p&gt;Qui dit programme dit langage de programmation. Et c’est là que la DGFiP
fait quelque chose d’inhabituel, puisqu’au lieu de coder le programme en C,
en Java ou en Python, elle a choisi le langage M. Qu’est-ce que le langage M ?
C’est un langage “dédié” (&lt;em&gt;domain-specific language&lt;/em&gt; ou DSL) créé par la DGFiP
pour la DGFiP en 1990.&lt;/p&gt;

&lt;p&gt;En effet, voici un extrait du &lt;a href=&quot;https://gitlab.adullact.net/dgfip/ir-calcul&quot;&gt;code source publié&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;regle 521010:
application : bareme , iliad ;

pour x=0,5;y=1,2;z=1,2:
DSxyz = somme(i=1..9: max(QFxyz - LIMINFBARi, 0) * (TAUXi / 100)) ;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Même sans connaître le langage M, on peut deviner des boucles, un opérateur
de somme appliqué à un maximum multiplié par un taux. Et en effet, ce bout de
code est celui qui calcule le montant de d’impôt à payer (“droits simples”, &lt;code class=&quot;highlighter-rouge&quot;&gt;DS&lt;/code&gt;)
en fonction du quotient familial (&lt;code class=&quot;highlighter-rouge&quot;&gt;QF&lt;/code&gt;) et des taux des différentes tranches
de l’impôt sur le revenu.&lt;/p&gt;

&lt;p&gt;Même s’il paraît austère au premier abord, le langage M a été conçu pour les
spécialistes du droit fiscal et ses règles de calcul : il permet d’exprimer de
manière concise les formules fiscales. Lors de sa création en 1990, l’objectif
était que les juristes spécialistes du droit fiscal à Bercy puissent directement
lire et écrire le code M, sans passer par les informaticiens.&lt;/p&gt;

&lt;p&gt;Le code M existe donc depuis 30 ans, et est mis à jour chaque année après le vote
du Projet de Loi de Finances qui modifie le barème et les modalités de l’IR.
Cependant, le louable objectif de se passer des informaticiens a dû être abandonné
en cours de route, puisque c’est bien une équipe d’informaticiens du bureau
SI-1E de la DGFiP qui se charge de la maintenance et de l’évolution du code.&lt;/p&gt;

&lt;p&gt;Revenons à ce qui se passe lors du calcul de l’impôt. Parmi les variables du
code M, certaines contiennent directement les valeurs de cases de la déclaration
d’impôt. D’autres sont des variables intermédiaires, et enfin une poignée
correspondent au montant d’impôt à payer, comme &lt;code class=&quot;highlighter-rouge&quot;&gt;IRNET&lt;/code&gt;. Il suffit donc de
calculer les valeurs des variables les unes après les autres pour arriver
au résultat final. Simple non ?&lt;/p&gt;

&lt;p&gt;Pas si simple, car pour exécuter un programme écrit en M, il faut d’abord le
traduire vers un &lt;a href=&quot;/fr/2017/07/26/assembleurs.html&quot;&gt;autre langage que la machine comprend&lt;/a&gt;. En 1990, lorsque
M a été créé, la DGFiP a choisi le langage C comme cible, et traduit donc le
code M en code C grâce à un compilateur que nous appellerons “compIRateur”
avant de l’exécuter. À l’époque, C était un langage “haut-niveau” et à l’écosystème
déjà mature, donc ce choix était tout à fait raisonnable.&lt;/p&gt;

&lt;p&gt;Pour compléter le tableau, il nous faut rajouter à la base de code M publiée un
ensemble de fichiers non-publiés, répondant au mystérieux nom de “INTER”. C’est
à cause de la non-publication de ces fichiers INTER que je n’avais pas toutes
les informations nécessaires à la réplication du calcul dans &lt;a href=&quot;/fr/2019/12/20/impots-formels.html&quot;&gt;mon billet précédent&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ces fichiers INTER sont écrits en C et interagissent directement avec la
traduction en C des fichiers M. Ils contiennent la logique d’un mécanisme de
droit fiscal appelé “liquidations multiples”. Il s’agit de recalculer l’impôt
plusieurs fois de suite avec des entrées différentes pour par exemple plafonner
le montant des avantages fiscaux, ou le bénéfice lié au quotient familial.&lt;/p&gt;

&lt;figure&gt;
	&lt;img src=&quot;/fr/assets/illustrations/dgfip_legacy_archi.png&quot; alt=&quot;image&quot; /&gt;
	&lt;figcaption&gt;
		Schéma de l'architecture de la DGFiP
	&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Langage M, fichiers M, fichiers INTER écrits en C ; toute cette architecture est
bien compliquée. N’aurait-il pas été plus facile de tout écrire en C dès le
départ ?&lt;/p&gt;

&lt;h3 id=&quot;pourquoi-utiliser-un-langage-dédié-&quot;&gt;Pourquoi utiliser un langage dédié ?&lt;/h3&gt;

&lt;p&gt;Cette question est pertinente ; arrêtons nous un instant pour instruire
à charge et à décharge le procès du choix du langage M en 1990. Un élément clé
du contexte du choix est la nature du problème à résoudre : le calcul de
l’impôt. Un programme qui calcule l’impôt n’est pas n’importe quel programme
informatique, et l’on peut faire des hypothèses fortes sur ce dont on a besoin
pour l’écrire.&lt;/p&gt;

&lt;p&gt;Première hypothèse : le calcul de l’impôt termine toujours. On n’a donc &lt;em&gt;a priori&lt;/em&gt;
pas besoin de récursion générale ou de boucles infinies pour écrire le programme.
Deuxième hypothèse: les règles de calcul de l’impôt sont assez simples pour être
exprimables uniquement à base d’arithmétique. La deuxième
hypothèse est plus discutable que la première, car l’imagination du législateur
fiscal est sans limite, mais posons la comme vraie comme l’a sûrement fait la
DGFiP en 1990.&lt;/p&gt;

&lt;p&gt;Sous ces hypothèses, la solution d’un langage dédié (DSL) est séduisante : elle
permet de limiter ce qu’un développeur peut écrire, en l’occurrence uniquement
des règles de calcul arithmétiques sur des variables : pas de pointeurs, pas
de fonctions, pas de structures de données. Et cette limitation est cruciale
pour respecter une contrainte majeure de la DGFiP : la performance. En effet,
l’enveloppe en temps de calcul pour calculer l’impôt d’un foyer fiscal est de
quelques millisecondes. Cette contrainte est nécessaire car avec 37 millions
de foyers fiscaux, il faut que chaque calcul aille très vite pour ne pas exploser
le temps total.&lt;/p&gt;

&lt;p&gt;En n’utilisant que des expressions arithmétiques, sans même de conditionnelles,
le code utilisé par la DGFiP est exceptionnellement rapide : les 600 000 lignes
de code C généré par le compIRateur s’exécutent en une milliseconde seulement.
Cette performance remarquable est liée à la forme du code C généré qui utilise
plusieurs astuces ingénieuses, comme celle de regrouper les variables du M
dans un grand tableau qui tient dans le cache du processeur pour optimiser
les accès mémoires.&lt;/p&gt;

&lt;p&gt;Autre avantage du DSL : le développeur M n’a pas besoin de spécifier dans
quel ordre les variables sont calculées. En effet, le compIRateur réordonne
automatiquement les variables en suivant l’ordre induit par le graphe de dépendance
du code. Et avec plus de 27 000 variables dans le code, cette fonctionnalité
est extrêmement pratique, car les textes législatifs que le code est censé
suivre ne définit pas les variables dans l’ordre dans lesquelles elles sont
calculées. Concrètement, imaginez que vous avez &lt;code class=&quot;highlighter-rouge&quot;&gt;X = 0&lt;/code&gt;,
&lt;code class=&quot;highlighter-rouge&quot;&gt;Y = X + Z&lt;/code&gt; et &lt;code class=&quot;highlighter-rouge&quot;&gt;Z = 2 * X&lt;/code&gt;. En analysant les dépendances entre les variables,
on déduit qu’il faut calculer &lt;code class=&quot;highlighter-rouge&quot;&gt;X&lt;/code&gt; en premier, puis &lt;code class=&quot;highlighter-rouge&quot;&gt;Z&lt;/code&gt; et enfin &lt;code class=&quot;highlighter-rouge&quot;&gt;Y&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ce dernier point sur le calcul des dépendances
éclaire aussi pourquoi la DGFiP a choisi un &lt;a href=&quot;https://fr.wikipedia.org/wiki/Langage_d%C3%A9di%C3%A9#Langages_d%C3%A9di%C3%A9s_internes_et_externes&quot;&gt;langage dédié externe&lt;/a&gt;,
avec sa propre syntaxe et compilateur, plutôt qu’un langage dédié embarqué à
l’intérieur de C, à base de macros du pré-processeur qui émulent la syntaxe
du M. Aussi puissantes que soient les macros du pré-processeur C, elles ne
permettent pas de calculer l’ordre topologique d’un graphe de dépendance. Pour
cela, un véritable compilateur est requis.&lt;/p&gt;

&lt;p&gt;Cepdendant, le choix du DSL vient aussi avec son lot d’inconvénients. Premièrement,
un langage dédié commence forcément sur un marché de niche, n’est pas enseigné
à l’université et il est donc nécessaire d’y former en interne ses développeurs.
Heureusement, le langage M n’est en soi pas très compliqué, donc y former
des développeurs n’est pas l’obstacle majeur. Deuxièmement, le manque d’outillage.
En effet, un langage de programmation vient avec un outillage d’aide au
développement essentiel : surlignage syntaxique, analyses statiques connectées
à des environnements de développement intégré, etc. Le langage M ne possède pas
un tel outillage, à cause du faible nombre de personnes qui l’utilisent. Mais
en 1990, la notion même d’outillage de langages de programmation au delà du
compilateur était balbutiante ; ce désavantage devait sembler mineur à l’époque.&lt;/p&gt;

&lt;p&gt;Troisièmement et de manière plus importante, ce qui fait la force d’un DSL est
aussi sa faiblesse : en restreignant le langage à des assignations de variables
par des expressions arithmétiques, le langage M prive le programmeur de tout
outil permettant l’abstraction. Notamment, l’absence de fonctions définies
par l’utilisateur mène à la nécessité de dupliquer le code. La duplication
de code est l’ennemi mortel du programmeur, car elle rend plus difficile la
maintenance d’une base de code où toute modification doit être répliquée à
plusieurs endroits, avec tous les risques d’oubli et d’erreur que cela comporte.&lt;/p&gt;

&lt;p&gt;C’est d’ailleurs entre autres le manque de fonctions dans le langage M qui
a conduit a la création des fichiers C INTER qui eux comportent des
fonctions : en effet, la logique fiscale
des liquidations multiples conduit à considérer la liquidation de l’impôt comme
une fonction à appeler avec différents paramètres, ce qu’il est impossible
d’exprimer sans duplication de code en M.&lt;/p&gt;

&lt;h2 id=&quot;moderniser-sans-rien-casser&quot;&gt;Moderniser sans rien casser&lt;/h2&gt;

&lt;p&gt;C’est donc cette architecture historique que Raphaël et moi avons découvert
lorsque nous avons pu avoir accès au code source complet non-publié de la
DGFiP. Dès lors, l’objectif initial de &lt;a href=&quot;https://gitlab.inria.fr/verifisc/mlang&quot;&gt;Mlang&lt;/a&gt; qui était de rendre utilisable
le code M publié par la DGFiP est devenu beaucoup plus complexe. En effet,
pour que ce code M publié soit utilisable, il fallait aussi que Mlang fasse
le boulot des fichiers INTER qui eux ne seront jamais publiés pour raisons de
sécurité.&lt;/p&gt;

&lt;p&gt;À ce stade, nous avons longuement discuté avec les programmeurs de la DGFiP.
C’est d’ailleurs de ces discussions que nous avons pu comprendre l’historique
de la situation, ainsi que les diverses contraintes qui s’imposaient au projet.
Il est rapidement apparu que l’objectif initial de Mlang recoupait un objectif
interne de la DGFiP, qui est de garder la maîtrise sur la compilation du
langage M. En effet, Après 30 ans d’exploitation, les développeurs originels du
compIRateur interne de la DGFiP partent à la retraite, posant un problème de
perte de compétence. Et le code source du compIRateur auquel nous avons pu
avoir accès, écrit en C et en 1990 pour la plus grosse partie, accuse une
certaine obsolescence par rapport aux bonnes pratiques d’écriture de compilateur
mises en place depuis 1990 dans la communauté. La création de Mlang était donc
une très bonne opportunité pour la DGFiP de moderniser une brique logicielle
essentielle à l’architecture du calcul de l’IR.&lt;/p&gt;

&lt;h3 id=&quot;une-approche-incrémentale&quot;&gt;Une approche incrémentale&lt;/h3&gt;

&lt;p&gt;Un des impératifs de toute tentative de modernisation de la calculette IR
est l’incrémentalité. En effet, la calculette IR est au centre du système
informatique de la DGFiP, et une myriade d’applications embarquent le module
de calcul de l’impôt : simulateur en ligne, logiciel métier pour les conseillers
des centres locaux des finances publiques, etc. Il est donc nécessaire pour
la solution modernisée de garder la même interface vis-à-vis de ses applications
clientes, concrètement une interface C.&lt;/p&gt;

&lt;p&gt;De manière tout aussi importante, une modernisation doit prendre en compte
la gestion des compétences informatico-fiscales nécessaires à la transformation
des textes législatifs régissant le calcul de l’IR en code informatique.
Actuellement, la DGFiP possède une compétence forte et une base de code
éprouvée avec le langage M. Passer du M à un autre langage de programmation
traditionnel signifierait la réécriture des règles fiscales dans un langage
pas forcément adapté à la forme des règles fiscales. Dans un autre contexte
similaire, les règles de paie des soldats de l’armée de Terre, une initiative
de modernisation appelée Louvois a &lt;a href=&quot;https://www.franceinter.fr/emissions/secrets-d-info/secrets-d-info-27-janvier-2018&quot;&gt;montré les dangers&lt;/a&gt; de minimiser la
complexité de ces règles en considérant une telle réécriture comme un
projet informatique normal.&lt;/p&gt;

&lt;p&gt;Malgré ces contraintes, une modernisation incrémentale peut apporter de
nouvelles fonctionnalités au système de calcul de l’IR, par exemple:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;une mesure fine de la couverture des tests, notamment au niveau des
valeurs individuelles des variables M;&lt;/li&gt;
  &lt;li&gt;de nouvelles cibles pour la compilation du langage M, pour faciliter
l’interopérabilité avec d’autres applications informatiques de l’État;&lt;/li&gt;
  &lt;li&gt;une analyse de la précision des calculs utilisant des flottants.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;une-nouvelle-architecture-centrée-sur-le-compilateur&quot;&gt;Une nouvelle architecture centrée sur le compilateur&lt;/h3&gt;

&lt;p&gt;La solution de modernisation que nous proposons avec Raphaël repose sur Mlang
et plus généralement sur la compilation entre différents langages, comme autant
de traits d’union entre les différents composants de cette architecture complexe.&lt;/p&gt;

&lt;p&gt;Premièrement, Mlang est un compilateur complet pour le langage M, basé sur une
sémantique formelle obtenue par rétro-ingénierie. Cette sémantique est présentée
dans notre &lt;a href=&quot;https://hal.inria.fr/hal-03002266/&quot;&gt;article de recherche&lt;/a&gt; et guide l’implémentation de Mlang. Par
exemple, contrairement au compIRateur originel de la DGFiP, Mlang possède un
interpréteur de M qui sert de référence au langage : le débogage se fait
d’abord en utilisant l’interpréteur qui dispose de fonctionnalités adaptées
(afficher les valeurs des variables, etc.). Une fois que le bug est réglé
dans l’interpréteur, alors le comportement devrait être correct dans chacun
des langages cibles de Mlang (C, Python, etc.). Cela permet de séparer les
problèmes, en permettant à l’utilisateur de savoir si son bug vient du code
M ou du compilateur.&lt;/p&gt;

&lt;p&gt;Deuxièmement, nous avons amélioré Mlang pour gérer et répliquer les
fonctionnalités implémentées dans les fichiers INTER non-publiés par la DGFiP.
Pour cela, nous avons longuement étudié le code des fichiers INTER afin de
déterminer exactement ce qu’ils faisaient. Dans les fichiers INTER, une
structure de données essentielle est mise à jour tout au long du code
effectuant les liquidations multiples : le tableau général des variables, ou
TGV. Le TGV est un tableau C qui contient les valeurs de toutes les variables
déclarées dans le code M. Au fur et à mesure que le C traduit du M est
exécuté, le TGV est mis à jour. Lorsque le code INTER appelle le code M,
il passe en entrée le TGV “vide” qui ne contient que les variables d’entrée
de M (correspondant aux cases de la déclaration de revenu),
et reçoit en retour le TGV “rempli” avec les valeurs calculées.&lt;/p&gt;

&lt;p&gt;Quand on regarde le code des fichiers INTER, on s’aperçoit que leur rôle est
d’appeler à plusieurs reprises le code M, tout en gérant l’état du TGV entre
les appels en sauvegardant la valeur de certaines variables ou en modifiant
la valeur de variables M spéciales qui ressemblent à des drapeaux activant
ou désactivant certaines parties du code M. Or, parce que le langage C est
très bas-niveau, nous nous sommes aperçus que la logique pure contenue dans
les fichiers INTER était relativement simple, mais cachée parmi des masses
de code standard en C pour gérer les pointeurs et l’état mutable.&lt;/p&gt;

&lt;p&gt;Nous avons donc décidé avec Raphaël de créer un deuxième langage dédié externe
compagnon pour exprimer la logique contenue dans les fichiers INTER. Ce langage,
que nous avons appelé M++, ressemble à ça:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;n&quot;&gt;compute_benefits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exists_deposit_defined_variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exists_taxbenefit_ceiled_variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;partition&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var_is_taxbenefit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;V_INDTEO&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;V_CALCUL_NAPS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;NAPSANSPENA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAD11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IRE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PREM8_11&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call_m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;V_CALCUL_NAPS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;iad11&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IAD11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ire&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IRE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ine&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;prem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PREM8_11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;PREM8_11&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prem&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;V_IAD11TEO&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iad11&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;V_IRETEO&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ire&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;V_INETEO&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ine&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La syntaxe ressemble à Python car M++ ressemble à un langage de script. M++
possède des fonctions, conditionnelles, des assignations de variable et
une fonction spéciale : &lt;code class=&quot;highlighter-rouge&quot;&gt;call_m&lt;/code&gt;. Les variables en lettres capitales sont
des variables du TGV de M, les autres sont des variables locales de M++.&lt;/p&gt;

&lt;p&gt;En utilisant le langage dédié haut-niveau M++, Raphaël et moi sommes capables
d’exprimer plus de 5 500 lignes de code C INTER (commentaires compris)
en une centaines de lignes de M++. Nous pensons donc que la maintenance du code
M++ sera considérablement plus simple que le code M. De plus, M++ est intimement
lié à M au travers de Mlang. En effet, à l’intérieur de Mlang, les appels
à &lt;code class=&quot;highlighter-rouge&quot;&gt;call_m&lt;/code&gt; sont simplement &lt;a href=&quot;/fr/2018/12/26/dupliquer-optimiser.html&quot;&gt;inlinés&lt;/a&gt; dans une même représentation
intermédiaire. Voici d’ailleurs l’architecture de Mlang, structurée par
de nombreuses &lt;a href=&quot;/fr/2017/11/22/syntaxe.html&quot;&gt;représentations intermédiaires&lt;/a&gt; :&lt;/p&gt;

&lt;figure&gt;
	&lt;img src=&quot;/fr/assets/illustrations/archi_mlang.png&quot; alt=&quot;image&quot; /&gt;
	&lt;figcaption&gt;
		Schéma de l'architecture de Mlang
	&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;M et M++ sont fusionnés dans la représentation BIR, ce qui permet de considérer
en un seul programme l’ensemble des fichiers sources contenant la logique de
calcul de l’impôt, qu’ils soient écrits en M ou en M++.&lt;/p&gt;

&lt;p&gt;Le langage M++ ne cherche pas à être élégant dans son design, et il ne suit
pas un paradigme traditionnel de programmation en particulier. Au contraire,
c’est un outil complètement ad-hoc qui s’adapte à la situation particulière
des fichiers INTER. En créant un langage dédié à partir d’un code existant,
la démarche est d’extraire la substantifique moelle logique de la base de
code afin de laisser de côté tout ce qui n’est pas essentiel. Le passage
du C des fichiers INTER à M++ limite de fait ce qu’on peut exprimer, comme
à chaque fois que l’on passe d’un langage généraliste à un langage dédié.
Mais l’analyse de l’historique des fichiers INTER, qui reflète l’évolution
du droit fiscal français, laisse à penser qu’il n’y aura pas besoin de nouvelle
fonctionnalité du langage tant que nous restons sur le même paradigme de
calcul de l’impôt.&lt;/p&gt;

&lt;p&gt;Il est cependant tout à fait possible que le législateur vote une loi
qui introduit une complication du calcul telle que M et M++ seront à leur
tour insuffisants pour exprimer le calcul informatiquement. Dans ce cas et grâce
à la maîtrise formelle des deux langages, il sera toujours possible de rajouter
une fonctionnalité adaptée au langage et ainsi assurer la pérennité de la
maintenance du code.&lt;/p&gt;

&lt;h3 id=&quot;sans-rien-casser-&quot;&gt;Sans rien casser ?&lt;/h3&gt;

&lt;p&gt;Tout au long du processus d’élaboration de notre solution autour de Mlang,
nous avons veillé à vérifier méticuleusement que nous n’étions pas en train
de casser le calcul de l’IR.&lt;/p&gt;

&lt;p&gt;Pour cela, la DGFiP nous a communiqué l’ensemble de ses jeux d’essai privés.
Les jeux d’essai de la DGFiP consistent en un grand nombre de foyers fiscaux
fictifs (entre 500 et 1000), normalement écrits par des juristes fiscalistes qui calculent à la main
le montant d’impôt dû. Mlang est donc validé sur ces jeux d’essai, pour
l’instant pour le calcul de l’impôt sur les revenus 2018 et 2019. Dans un second
temps et quand Mlang sera considéré pour une mise en production, les résultats
de Mlang pourront être comparés avec ceux de l’ancien système sur l’ensemble
des foyers fiscaux français, garantissant une assurance maximale pour la
transition.&lt;/p&gt;

&lt;p&gt;Cependant, ceci concerne uniquement la validation en interne de Mlang. Raphaël
et moi tenions à ce que l’évaluation de Mlang soit reproductible en dehors de
la DGFiP. Or, la DGFiP n’a pas souhaité que ses jeux d’essai soient rendus
publics. Nous avons donc du trouver une solution pour créer de nouveaux jeux
d’essais, distincts de ceux de la DGFiP.&lt;/p&gt;

&lt;p&gt;Il était hors de question que nous écrivions des cas de tests à la main,
puisque nous n’y connaissons rien en droit fiscal. Nous avons donc pris le parti
d’utiliser une autre technique classique d’ingénierie logicielle : le &lt;a href=&quot;https://fr.wikipedia.org/wiki/Fuzzing&quot;&gt;fuzzing&lt;/a&gt;
ou test à données aléatoires. Le principe est le suivant : on génère aléatoirement
des déclarations de revenu de foyers fiscaux jusqu’à ce qu’un des essais corresponde
à un foyer fiscal réaliste, qui devient un jeux d’essai à part entière. Ces
jeux d’essai sont ensuite validés en observant les résultats calculés avec
l’ancien système de la DGFiP. Plusieurs questions se posent immédiatement ?
Qu’est-ce qu’un foyer fiscal “réaliste” ? Combien de jeux d’essai faut-il générer
pour couvrir “tout le code” ?&lt;/p&gt;

&lt;p&gt;Nous avons considéré qu’un foyer fiscal était “réaliste” dès lors qu’il ne lève
aucune des conditions d’erreur qui sont vérifiées dans le code M. Ces conditions,
au nombre de 500, servent à valider la cohérence de la déclaration de revenu.
Par exemple, lorsqu’une case A précise “part des revenus de la case B
dédiée à …”, alors le montant de la case A doit être inférieur au montant
de la case B. Le but du fuzzing est donc de trouver des jeux d’essai qui
satisfassent ces 500 conditions impliquant des centaines de variables d’entrée,
ce qui est loin d’être simple!&lt;/p&gt;

&lt;p&gt;Pour obtenir des résultats rapidement, nous utilisons une technique de fuzzing
un peu astucieuse, basée sur l’outil &lt;a href=&quot;https://lcamtuf.coredump.cx/afl/&quot;&gt;AFL&lt;/a&gt;. Dans ce fuzzing guidé par
la couverture de nouveaux chemins d’exécution dans le ficher binaire testé,
la génération aléatoire est biaisée en faveur des cas de tests qui permettent
d’accéder à un résultat différent de toutes les tentatives précédentes. Cette
idée simple raffinée par de nombreuses heuristiques permet à l’exploration
aléatoire d’aller rapidement dans tous les coins du programme.&lt;/p&gt;

&lt;p&gt;En l’occurence, l’utilisation de AFL nous permet d’obtenir des milliers de foyers
“réalistes” en seulement quelques heures de test en continu sur une machine
assez puissante. Un autre utilitaire, &lt;a href=&quot;http://manpages.ubuntu.com/manpages/bionic/man1/afl-cmin.1.html&quot;&gt;afl-cmin&lt;/a&gt; permet ensuite d’enlever
les doublons qui testent les mêmes chemins dans le binaire pour arriver à un
corpus minimisé de jeux d’essai qui offre une couverture optimale du programme
de départ. Ce corpus minimisé compte quelques centaines de jeux d’essai pour les
versions 2018 et 2019 du calcul de l’impôt sur le revenu.&lt;/p&gt;

&lt;p&gt;Même si ces jeux d’essai “réalistes” contiennent généralement des combinaisons
de revenus complètement irréalistes pour un foyer fiscal, ils ont le bénéfice
d’être assez diversifié pour exercer toutes les combinaisons différentes de
situations pour le calcul de l’IR, ce qui est difficile à obtenir lorsque
l’on écrit soi-même les jeux d’essai à la main. Aussi peut-être serait-il
préférable que les juristes qui écrivent les jeux d’essai partent directement
des foyers fiscaux trouvés par fuzzing plutôt que d’en inventer, afin d’avoir
une validation optimale de l’implémentation du calcul de l’IR.&lt;/p&gt;

&lt;h2 id=&quot;des-langages-dédiés-pour-le-logiciel-critique&quot;&gt;Des langages dédiés pour le logiciel critique&lt;/h2&gt;

&lt;p&gt;Durant ce long voyage sous le capot du moteur de calcul de l’IR de la DGFiP,
nous avons rencontré tout un bestiaire de langages exotiques et de techniques
issues de méthodes formelles. J’aimerais terminer ce billet en prenant un
peu de recul et en dégageant quelques raisons fondamentales pourquoi les
langages dédiés et les méthodes formelles sont un outil très puissant
pour assurer la qualité optimal d’un logiciel critique sur le long terme.&lt;/p&gt;

&lt;h3 id=&quot;la-difficulté-de-la-maintenance-à-long-terme&quot;&gt;La difficulté de la maintenance à long terme&lt;/h3&gt;

&lt;p&gt;Aucun logiciel n’est éternel. Mais certains, de par les missions qu’ils
accomplissent, ont pour vocation à être utilisés jusqu’à une durée
indéterminée. Dans les années 60 et 70, les institutions bancaires du monde
entier ont informatisé leur système de gestion des transactions en utilisant
le langage de programmation COBOL. 60 ans plus tard, COBOL n’est plus enseigné
à l’université et est largement considéré comme obsolète, mais &lt;a href=&quot;https://www.wealthsimple.com/en-ca/magazine/cobol-controls-your-money&quot;&gt;les banques
l’utilisent toujours pour faire tourner l’économie mondiale&lt;/a&gt;. Le choix
d’un langage de programmation est nécessaire pour débuter un projet, mais même
en choisissant un langage populaire aujourd’hui, on est pas à l’abri du
&lt;em&gt;zeitgeist&lt;/em&gt; informatique qui frappe d’obsolescence nombre de langages, frameworks
et technologies.&lt;/p&gt;

&lt;p&gt;Considéré sur des échelles de temps de la durée de plusieurs carrières, il
est peu probable qu’une technologie d’aujourd’hui soit toujours enseignée à
l’université dans 70 ou 80 ans. Et à mesure que les programmeurs originels
partent à la retraite, il devient impossible de compter sur le marché du
travail pour fournir une main d’œuvre qualifiée et prête à l’emploi.&lt;/p&gt;

&lt;p&gt;Quelles stratégies adopter pour se prémunir contre l’obsolescence dans de tels
projet de très long terme ? Une première solution est accessible aux très grosses
organisations : la création d’un langage généraliste maison. C’est le cas
du langage &lt;a href=&quot;https://www.adaic.org/&quot;&gt;Ada&lt;/a&gt;, créé dans les années 1980 par le département de la défense
américain (DoD). En imposant le langage dans tous ses projets, le DoD a pu
créer de toutes pièces une demande de programmeurs Ada suffisante pour que la
communauté Ada atteigne la masse critique qui lui permette de survivre sur le
long terme. Avec suffisamment d’utilisateurs et de financement à long terme,
Ada a pu recevoir de nombreuses mises à jour et son outillage s’adapte aux
tendances modernes des langages de programmation.&lt;/p&gt;

&lt;h3 id=&quot;lagilité-par-le-langage-dédié&quot;&gt;L’agilité par le langage dédié&lt;/h3&gt;

&lt;p&gt;Cependant, toutes les organisations n’ont pas la même taille ou le niveau de
planification et de financement que le département de la défense américain. Dès
lors, une deuxième stratégie s’avère intéressante pour les projets de taille
plus modeste mais qui ont aussi cette contrainte de maintenance à très long
terme.&lt;/p&gt;

&lt;p&gt;Ce que l’exemple de la DGFiP nous apprend, c’est que grâce à l’utilisation
d’un langage dédié, il suffit de deux doctorants à temps partiel pour moderniser
un compilateur ; à comparer avec la force de frappe nécessaire pour porter une
base de code. Certes, il est toujours plus facile pour un chef de projet
informatique de recruter en masse des développeurs peu qualifiés plutôt que
de trouver et de fidéliser des profils à compétence spécifique comme ceux de
Raphaël et moi.&lt;/p&gt;

&lt;p&gt;Cependant, un langage dédié doté d’un compilateur bien maîtrisé par des
spécialistes formés aux méthodes formelles est une formidable assurance
sur l’avenir et l’interopérabilité du projet. Fondamentalement, un langage
dédié agile qui s’adapte aux exigences fonctionnelle et au passage du temps
démultiplie l’expressivité de la base de code. En réalité, code et langage
forment un tout : seul l’exécution du code selon la sémantique du langage
importe.&lt;/p&gt;

&lt;p&gt;Avec un langage généraliste établi comme C et Java, on ne dispose que du levier
du changement du code pour adapter et modifier son logiciel. Avec un langage dédié
et un compilateur maîtrisé, le langage lui-même devient un outil au service
de l’évolution du logiciel, et non plus un mal nécessaire à choisir au début et
qui se transforme progressivement en dette technique insurmontable.&lt;/p&gt;

&lt;p&gt;Revenons au cas du M : ce langage dédié a maintenant 30 ans, notre
proposition de modernisation a-t-elle le potentiel de perdurer autant de temps ?
Impossible de le savoir à l’avance, mais ce que l’on peut dire, c’est que
les bases sur lesquelles partent M et M++ maintenant sont beaucoup plus solides
que celles sur lesquelles M reposait depuis 1990. Doté d’une sémantique claire,
d’un compilateur écrit dans un langage fonctionnel et structuré selon une
architecture claire et extensible, le langage bénéficie de l’évolution de
30 ans de bonnes pratiques en théorie de la compilation et en méthode formelles.&lt;/p&gt;

&lt;p&gt;Mais attention, le langage dédié peut se transformer en une arme à double
tranchant sans la compétence appropriée ! On ne change pas la sémantique d’un
langage sur un coup de tête, car un changement minuscule peut casser tout une
base de code. C’est pour cette raison que les ingénieurs compilations et les
spécialistes des méthodes formelles, loin d’être rendus obsolètes par les
méthodes d’apprentissage statistiques, restent et seront pour très longtemps
absolument cruciaux. L’étude des langages de programmation, de la théorie de
la compilation et des méthodes formelles apporte une vraie compétence
spécifique extrêmement utile pour de nombreux projets informatiques critiques.&lt;/p&gt;

&lt;h2 id=&quot;conclusion--faites-confiance-aux-artisans-du-langage&quot;&gt;Conclusion : faites confiance aux artisans du langage&lt;/h2&gt;

&lt;p&gt;De manière plus imagée, le choix du langage dédié et de sa compétence associée
en méthode formelles peut être comparé au choix de l’artisanat par rapport à
un processus industriel que serait le portage de code d’un langage à un autre.
L’artisanat possède une longue tradition de transmission du savoir sur le long
terme, basé sur un petit nombre de praticiens indépendants formés à des compétences
de haut niveau avec une relative polyvalence. À l’opposé, l’industrie vise à
transformer le processus de production en une chaîne assez rigide de tâches
standardisées réalisée par des ouvriers peu qualifiés. Comme les autres
secteurs de production, l’informatique a tendance à s’industrialiser au fil du
temps.&lt;/p&gt;

&lt;p&gt;Mais l’industrialisation suppose la masse critique et souvent la recherche
infinie de la croissance. Et cette analogie s’applique également pour les langages
informatiques. Donc avant de choisir à contre-cœur un langage établi et
industriel pour votre projet informatique à long terme, demandez-vous si vous
voulez vraiment vous inscrire dans une logique industrielle de croissance
infinie, ou s’il n’est pas plus opportun d’en confier la réalisation à des
artisans du langage, qui sauront vous produire et maintenir un beau petit
logiciel du terroir !&lt;/p&gt;

</description>
        <pubDate>Mon, 01 Mar 2021 01:00:00 +0100</pubDate>
        <link>https://blog.merigoux.fr/fr/2021/03/01/moderniser-dgfip.html</link>
        <guid isPermaLink="true">https://blog.merigoux.fr/fr/2021/03/01/moderniser-dgfip.html</guid>
        
        
      </item>
    
      <item>
        <title>Vers une imposition formalisée⠀?</title>
        <description>&lt;p&gt;Ésotériques, inutilisables, perchées, idéalistes : les méthodes formelles sont parfois
critiquées par la communauté informatique à cause d’un certain élitisme qui rend
difficile la communication entre chercheurs et développeurs. En effet, la barrière
d’entrée est assez haute pour pouvoir utiliser les méthodes formelles lorsque
l’on n’a pas fait de master de mathématique, spécialité théorie des catégories.
L’immense majorité des développeurs, qui utilise des langages comme Python ou
Javascript, n’est jamais exposée aux concepts un peu plus théoriques que j’essaie
d’expliquer sur ce blog, et qui améliorent pourtant souvent la productivité de la
programmation. Et ce n’est pas grave : vouloir utiliser ces outils
et concepts compliqués sur n’importe quel problème s’apparente à chasser des mouches
à l’aide d’un bazooka.&lt;/p&gt;

&lt;p&gt;Cependant, de temps à autre, un problème réellement complexe survient, et l’outillage
habituel se révèle insuffisant. C’est bien dans ces cas là qu’un peu de théorie peut sauver la mise !
Aujourd’hui, en guise d’illustration de cette affirmation,
je vais vous raconter l’histoire d’un langage mystérieux, de la
Direction Générale des Finances Publiques, de preuve automatique de théorèmes à
propos de l’impôt sur le revenu et d’expression algorithmique de la loi. Cette histoire
a débouché sur le projet &lt;a href=&quot;https://gitlab.inria.fr/verifisc&quot;&gt;Verifisc&lt;/a&gt;, et ouvre la voie à une réappropriation de
la complexité du système fiscal français grâce à des outils accessibles à tous.&lt;/p&gt;

&lt;h2 id=&quot;calculer-son-impôt-un-enjeu-démocratique-&quot;&gt;Calculer son impôt: un enjeu démocratique ?&lt;/h2&gt;

&lt;p&gt;En parallèle de &lt;a href=&quot;/fr/2019/04/16/verifier-rust.html&quot;&gt;mon sujet de thèse&lt;/a&gt;, j’essaie de toujours garder un œil ouvert sur de potentielles
opportunités d’appliquer mon domaine de recherche à de nouveaux problèmes. Aussi, en janvier 2019,
lorsque j’ai connaissance d’&lt;a href=&quot;https://popl18.sigplan.org/details/POPL-2018-papers/3/Formal-Methods-and-the-Law&quot;&gt;une conférence&lt;/a&gt; donnée par la professeure de droit
&lt;a href=&quot;http://www.law.northwestern.edu/faculty/profiles/SarahLawsky/&quot;&gt;Sarah Lawsky&lt;/a&gt; à la conférence POPL en 2018, je suis intrigué par le titre de la
présentation : “&lt;em&gt;Formal Methods and the Law&lt;/em&gt;”. En lisant les articles de cette chercheuse,
je me rend compte que certaines parties de la loi décrivent des processus très précis,
parfois à l’aide de calculs mathématiques : ce sont des algorithmes.
Plus précisément, Lawsky prend l’exemple de certaines provisions du Code des impôts
américains dont elle décrit précisément le sens à l’aide de formules logiques.&lt;/p&gt;

&lt;p&gt;Je me pose alors la question de l’utilité d’une telle démarche : qu’est-ce que la
formalisation du code des impôts nous permettrait de faire de plus que ce que l’on
sait déjà faire ? Déjà, et comme le pointe Lawksy, cela permettrait de clarifier
certaines dispositions législatives à l’intention ambiguë. Mais la valeur ajoutée
de cette clarification, certes grande d’un point de vue juridique, me laisse sur
ma faim. Je me demande alors si la formalisation ne pourrait pas nous aider à mieux
comprendre comment le montant de l’impôt est calculé.&lt;/p&gt;

&lt;p&gt;À ce stade, il est nécessaire de faire un tour d’horizon des outils qui existent autour
de la manière dont l’impôt est calculé. Il existe déjà &lt;a href=&quot;https://www3.impots.gouv.fr/simulateur/calcul_impot/2019/index.htm&quot;&gt;un simulateur&lt;/a&gt;
permettant de calculer l’impôt sur le revenu à
partir d’une déclaration fiscale. L’Insee produit également de nombreuses &lt;a href=&quot;https://www.insee.fr/fr/statistiques/2581807&quot;&gt;études&lt;/a&gt;
étudiant l’effet de l’impôt sur le revenu et des allocations sur le revenu effectif
des ménages. L’initiative &lt;a href=&quot;https://openfisca.org/fr/&quot;&gt;OpenFisca&lt;/a&gt;, menée par &lt;a href=&quot;https://www.etalab.gouv.fr/&quot;&gt;Etalab&lt;/a&gt;,
offre même une implémentation complète du
système socio-fiscal français à destination des économistes qui mènent de telles
études.&lt;/p&gt;

&lt;p&gt;Cependant, dans tous ces travaux, on doit partir de données réelles correspondant
à des foyers fiscaux &lt;script type=&quot;math/tex&quot;&gt;x_1,\ldots,x_n&lt;/script&gt; pour calculer des quantités
&lt;script type=&quot;math/tex&quot;&gt;f(x_1),\ldots,f(x_n)&lt;/script&gt; (impôts, allocations, etc). Les données de départ
conditionnent la portée de l’étude, et le secret fiscal rend l’accès aux données
fiscales des ménages très difficile. Dès lors, comment étudier à l’avance les effets
d’une réforme fiscale ? Le site &lt;a href=&quot;https://leximpact.beta.gouv.fr/&quot;&gt;LexImpact&lt;/a&gt;, construit sur OpenFisca, permet de
visualiser rapidement les effets d’une modification des paramètres de la législation
en place sur des cas types. Les parlementaires et leurs collaborateurs ont
également la possibilité de simuler leur réforme sur un échantillon réduit de foyers
fiscaux tirés de données réelles (quelques milliers).&lt;/p&gt;

&lt;p&gt;Mais un échantillon de quelques milliers ou dizaines de milliers de foyers fiscaux
est-il suffisant pour observer les effets d’une réforme fiscale dans tous les cas
possibles ? Faisons un petit calcul : si l’on compte 20 tranches de revenus
à considérer pour les ménages (de 1600 € à 1700 €, de 1700 € à 1800 €, etc), les 3
cas de situations matrimoniales (célibataire, couple, concubinage), un nombre
de personnes à charge compris entre 0 et 5, la
possibilité pour tout ou partie de ces personnes d’êtres invalides, on arrive à plusieurs
milliers de cas différents à étudier ! De plus, sur un échantillon réduit, la majorité
des données se concentreront dans des catégories “moyennes” sur-représentées, réduisant
d’autant plus les chances d’avoir une couverture optimale de tous les cas possibles
par le jeu de données. Et tout cela ne prend pas en compte l’étude des effets fiscaux
sur un même foyer fiscal évoluant d’année en année, avec des variations de revenus,
des changements de situation familiale, etc.&lt;/p&gt;

&lt;p&gt;Partant de ce constat, je me rends compte que la problématique de l’étude de la
législation fiscale et de ses effets sur la population dans sa diversité ne bénéficie
pas d’un outillage d’analyse à la hauteur de l’impact qu’elle peut avoir sur la vie
des citoyens. Une mauvaise interaction entre une tranche d’impôt et le seuil d’une
allocation peut avoir des conséquences dramatiques sur la vie d’une famille,
avec des pertes potentielles allant jusqu’à plusieurs centaines d’euros par mois.
Mais ces hypothétiques situations malencontreuses existent-t-elles vraiment ?
Si oui, comment les identifier systématiquement afin de les corriger ?&lt;/p&gt;

&lt;p&gt;La complexité de la législation fiscale, produit d’une construction historique mais
aussi de la nécessité de répondre à une grande diversité de situations issues du réel,
risque d’échapper à ses créateurs tel un Frankestein juridique. La visibilité
des effets de la fiscalité et des allocations en général est également un enjeu démocratique,
comme en témoigne &lt;a href=&quot;https://gitlab.inria.fr/verifisc/verifisc-python&quot;&gt;le récent débat&lt;/a&gt; à propos des cas types du &lt;a href=&quot;https://reforme-retraite.gouv.fr/IMG/pdf/retraite_01-09_leger.pdf&quot;&gt;rapport Delevoye&lt;/a&gt;
sur la réforme des retraites. En attendant une remise
à plat telle que &lt;a href=&quot;http://www.revolution-fiscale.fr/&quot;&gt;celle proposée par Landais, Piketty et Saez&lt;/a&gt; en 2011,
j’ai décidé de voir ce que mon domaine de recherche, les méthodes formelles,
pouvaient apporter à cette problématique.&lt;/p&gt;

&lt;h2 id=&quot;un-modèle-socio-fiscal-prouvé-automatiquement&quot;&gt;Un modèle socio-fiscal prouvé automatiquement&lt;/h2&gt;

&lt;p&gt;L’étude de la fiscalité et des allocations est un problème intrinsèquement
mathématique, une fois posé de la bonne manière. En effet, on suppose dans toute
la suite disposer d’informations sur les ménages similaires au contenu d’une
déclaration de revenus ou des formulaires des &lt;a href=&quot;https://mes-aides.gouv.fr/&quot;&gt;simulateurs en ligne&lt;/a&gt; permettant
d’évaluer ses droits aux différentes aides et allocations. La quantité d’argent
due (au titre de l’impôt) ou reçue (par une allocation) n’est alors qu’une simple
fonction mathématique des caractéristiques du ménage. Afin d’étudier cette fonction,
j’ai décidé de l’encoder dans un solveur SMT appelé &lt;a href=&quot;https://github.com/Z3Prover/z3&quot;&gt;Z3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Un solveur SMT est une instance particulière de ce que l’on appelle un prouveur
automatique de théorèmes. Ces prouveurs automatiques marchent de la manière suivante :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;on encode un problème sous forme de contraintes portant sur des variables
booléennes ou entières (&lt;script type=&quot;math/tex&quot;&gt;x_1 \leqslant 4&lt;/script&gt; et &lt;script type=&quot;math/tex&quot;&gt;x_2 + 3 &gt; x_1&lt;/script&gt; par exemple) ;&lt;/li&gt;
  &lt;li&gt;on demande ensuite au prouveur s’il existe des valeurs pour les variables du
problème telles que toutes les contraintes soient satisfaites ;&lt;/li&gt;
  &lt;li&gt;si la réponse est oui, alors le prouveur renvoie un tel assignement des variables.
Si la réponse est négative, alors le prouveur garantit que ces contraintes ne peuvent pas être satisfaites.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dans notre cas, les variables sont les caractéristiques du ménage : nombre d’enfants,
revenus, etc. Les contraintes servent à modéliser deux choses. Premièrement, les règles
de calcul de l’impôt et des allocations. Par exemple, “si le ménage est un couple avec
deux enfants non-invalides, alors le quotient familial est égal à 3”. Deuxièmement,
les contraintes nous servent également à poser les questions que l’on veut à notre petit
modèle socio-fiscal. En effet, si l’on cherche des ménages payant un impôt supérieur
ou égal à 30% de leur revenu, il suffit d’ajouter cette contrainte au modèle. Le
solveur SMT nous dira alors si un tel ménage existe, et si oui, nous en donnera un exemple.&lt;/p&gt;

&lt;p&gt;Rentrons tout de suite dans le dur avec une problématique qui a des conséquences
concrètes pour nombre de ménages : la taxation marginale et les effets de seuil.
Imaginons qu’un individu du ménage voit son salaire augmenter d’une année sur l’autre.
Il va alors payer plus d’impôt sur le revenu, mais aussi voir certaines allocations
diminuer. La question que l’on se pose est la suivante : existe-t-il des
situations où le ménage voit ses revenus effectifs &lt;em&gt;diminuer&lt;/em&gt;, alors même que le salaire
augmente ? Plus précisément, nous cherchons des &lt;a href=&quot;https://www.insee.fr/fr/statistiques/2583050&quot;&gt;taux marginaux effectifs de
prélèvement&lt;/a&gt; supérieurs à 100 %.&lt;/p&gt;

&lt;p&gt;Le solveur SMT a pu répondre à cette question : c’est oui ! Mais plus intéressant,
cette réponse positive est accompagnée d’un exemple qui satisfait toutes les contraintes.
Cet exemple ne correspond pas à des données réelles de ménages français dont disposerait
le solveur, mais à une situation hypothétique déduite de l’étude des fonctions mathématiques
de calcul des impôts et des allocations. Bien qu’hypothétiques, ces exemples n’en sont
pas moins réalistes et mettent en valeur des angles morts législatifs, résultant
de l’interaction malencontreuse de plusieurs dispositifs pensés séparément.&lt;/p&gt;

&lt;p&gt;Ici, nous avons donc un couple en concubinage dont le premier individu gagne un salaire net
de 1768,55 €, le deuxième individu étant sans activité. Le couple a deux enfants de 17 et
16 ans, scolarisés au lycée, et qui sont à la charge du deuxième individu sans activité.
La famille loue un logement en zone III (hors agglomération de plus de 100 000 habitants)
pour 784,09 € par mois. On suppose que le premier individu du couple est augmenté de 40 €
par mois (soit 2,2 % du salaire) l’année suivante. Pour ce ménage,
voici donc les montants de l’impôt sur le revenu et de diverses allocations, qui sont
calculés à l’équilibre avant et après l’augmentation (on ne tient pas compte du fait que
certaines allocations sont calculées à partir des revenus &lt;script type=&quot;math/tex&quot;&gt;N-2&lt;/script&gt; alors qu’avec le
prélèvement à la source l’impôt est calculé sur les revenus &lt;script type=&quot;math/tex&quot;&gt;N&lt;/script&gt;) :&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Montant&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Valeur avant&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Valeur après&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Différence&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Salaire annuel&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;21 223,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;21 703,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;480,00 €&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Revenu fiscal de référence&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;19 101,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;19 533,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;432,00 €&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Impôt sur le revenu&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;846,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;981,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;135,00 €&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Allocations familiales&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;132,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;132,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0,00 €&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Allocation rentrée scolaire&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;806,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;806,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0,00 €&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Bourses collège&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0,00 €&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Bourses lycée&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;876,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;- 876,00 €&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Allocation Personnalisée au Logement&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;110,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;99,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;- 11,00 €&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Prime d’activité&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;387,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;382,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;- 5,00 €&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Net annuel touché&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;29 607,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;28 884,00 €&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;- 723,00 €&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;On voit ici qu’au lieu de gagner 40 € de plus par mois, le ménage en &lt;em&gt;perd&lt;/em&gt; 60 € par mois par
rapport à la situation précédente… Cette perte est expliquée à 75 % par la perte de la
bourse lycée pour les deux enfants, qui possède un effet de seuil très brutal
démultiplié par le nombre de personnes à charge qui en bénéficient.&lt;/p&gt;

&lt;p&gt;Que nous apprend donc cet exemple ? Premièrement, qu’un seuil linéaire
similaire à celui utilisé pour les allocations familiales devrait être généralisé
à toutes les allocations afin d’éviter ce genre de situations. Deuxièmement, que
c’est une combinaison de facteurs très précise qui déclenche cette situation :
deux enfants au lycée en même temps, et un niveau de salaire autour de 1750 € net.
Le concubinage, qui rajoute une hausse d’impôts expliquant 11 % de la perte d’argent,
n’est pas le facteur central mais concerne quand même &lt;a href=&quot;https://www.insee.fr/fr/statistiques/3306175&quot;&gt;20 % des français&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Cet exemple aurait pu être découvert à partir d’une étude classique sur
des données réelles, mais aurait supposé un vaste échantillon pour tomber
dessus par hasard. Un des atouts de cette nouvelle méthode d’analyse
par rapport à l’état de l’art est donc de découvrir efficacement, de manière fiable
et sans aucune donnée secrète les cas limites indésirables induits par le système socio-fiscal.&lt;/p&gt;

&lt;p&gt;Le solveur SMT pourrait également être utilisé pour trouver la pente linéaire exacte
à donner à la bourse afin d’éviter un taux marginal trop élevé. Mais que veut-dire
“trop élevé” ? 70 % ? Plus ? Moins ? De plus, l’effet de seuil est ici démultiplié
par le nombre d’enfants : pour maintenir un taux marginal faible autour du seuil,
il faudrait une pente linéaire d’autant plus faible que l’on souhaite couvrir des
familles plus nombreuses. On voit ici qu’il y a un arbitrage politique de haut
niveau à faire : qu’est-ce que l’on juge acceptable ? Une fois cet arbitrage fait,
le solveur SMT permet de garantir si la réalité de la formule de calcul est en
accord avec les principes décidés par le législateur.&lt;/p&gt;

&lt;p&gt;Je n’ai présenté ici qu’une seule méthode d’interroger le modèle encodé dans
le solveur SMT. En fait, il est possible de poser n’importe quelle question de la forme
“Existe-t-il une situation telle que … ?”. Quelques exemples d’applications possibles,
fort intéressants d’un point de vue d’évaluation des politiques publiques :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“Est-il possible pour un ménage de gagner/perdre plus de &lt;script type=&quot;math/tex&quot;&gt;x&lt;/script&gt; € à la suite de
 la réforme fiscale de cette année ?”&lt;/li&gt;
  &lt;li&gt;“Est-il possible d’augmenter le taux de la deuxième tranche d’imposition de plus
 de 5 points sans qu’aucun ménage ne perde plus de 200 € par an ?”&lt;/li&gt;
  &lt;li&gt;En supposant avoir encodé dans le solveur SMT un histogramme de la distribution
de revenus, “Est-il possible de jouer sur les taux des tranches et de la décote
de manière à percevoir 5 milliards d’euros par an en plus sans qu’aucun ménage ne voie
 son impôt augmenter de 3 % de ses revenus ?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Science ou fiction ? Le prouveur automatique n’est pas magique, et plus le
problème est complexe, plus sa résolution prend du temps et des ressources.
Mon prototype actuel peut déjà consommer plus de 100 Go de RAM et tourner
plusieurs jours ; la question du passage à l’échelle vers des questions plus
compliquée représente donc un vrai défi technique. Mais les méthodes formelles
ont plus d’un tour dans leur sac, et vont nous aider à nouveau pour présenter
au SMT un problème réduit à sa substantifique moelle, débarrassé de toute complexité
superflue.&lt;/p&gt;

&lt;h2 id=&quot;une-implémentation-du-calcul-de-limpôt-formalisée&quot;&gt;Une implémentation du calcul de l’impôt formalisée&lt;/h2&gt;

&lt;p&gt;Il est déjà possible de faire beaucoup de choses avec un petit prototype comme
celui que j’ai développé. Cependant, idéalement, on voudrait disposer de la fonction
qui calcule l’impôt dans tous les cas de figures, pour tous types de revenus et
de statuts, avec avec toutes les niches fiscales. Or, il existe déjà un programme
informatique qui réalise cette tâche : celui qui est utilisé par la Direction
Générale des Finances Publiques (DGFiP) pour calculer votre avis d’imposition !&lt;/p&gt;

&lt;p&gt;Depuis la &lt;a href=&quot;https://www.legifrance.gouv.fr/affichTexte.do?cidTexte=JORFTEXT000033202746&amp;amp;categorieLien=id&quot;&gt;loi n° 2016-1321 du 7 octobre 2016&lt;/a&gt; “pour une République numérique”,
le code source utilisé par une administration est considéré comme un document administratif
soumis au droit d’accès et à la réutilisation publique. Le code implémentant le
calcul des impôts a donc été publié par la DGFiP en 2016, &lt;a href=&quot;https://www.etalab.gouv.fr/retour-sur-le-hackathon-codeimpot&quot;&gt;à l’occasion d’un hackathon&lt;/a&gt;
qui a vu émerger quelques idées intéressantes. Vous pouvez vous-même aller
inspecter le code sur &lt;a href=&quot;https://framagit.org/dgfip/ir-calcul&quot;&gt;le dépôt officiel de la DGFiP&lt;/a&gt;. Il n’y a plus qu’à
l’utiliser alors ? Pas vraiment.&lt;/p&gt;

&lt;p&gt;En effet, le code est écrit dans un langage de programmation spécial, appelé “M” pour
“Macro-langage”. C’est un langage développé par et pour la DGFiP pour l’écriture
de l’implémentation du code des impôts modifié chaque année lors du vote du budget.
Or à ce jour, la DGFiP n’a pas mis à disposition
du public l’outillage qu’elle utilise pour exécuter ce code M. Le code est-il
inutilisable pour autant ? En 2016, &lt;a href=&quot;https://fr.wikipedia.org/wiki/Etalab&quot;&gt;Etalab&lt;/a&gt; a &lt;a href=&quot;https://github.com/etalab/calculette-impots-m-language-parser&quot;&gt;publié un parseur&lt;/a&gt; pour le
langage M, capable de traduire le code source M vers un &lt;a href=&quot;https://framagit.org/dgfip/ir-calcul&quot;&gt;abre de syntaxe abstrait&lt;/a&gt;
qu’il est possible d’interpréter en Python. J’ai pu reproduire les études menées
en 2016 à l’occasion du hackathon en utilisant moi-même ce parseur, mais j’ai
également pu constater quelques problèmes. D’abord, le montant de l’impôt calculé
avec le parseur était parfois faux, notamment lorsque le montant final devait
prendre en compte le plafonnement d’un avantage fiscal ou du quotient familial.
Mais plus problématique, ce parseur n’a pas été écrit avec pour objectif de donner
une sémantique précise au langage M : les opérateurs M de l’arbre de syntaxe
abstrait sont simplement remplacés lors de l’exécution par des opérateurs Python
correspondant.&lt;/p&gt;

&lt;p&gt;Pourquoi cela n’est-il pas suffisant ? Il faut se rappeler notre objectif : au delà
de la simple exécution du code M, nous voulons aussi le traduire vers un prouveur
automatique pour répliquer les résultats de notre prototype dans le cas général.
Pour cela, il nous faut savoir exactement comment s’exécutent les programmes M dans
tous les cas possibles. Cette connaissance porte un nom précis en étude des langages
de programmation ; il s’agit de donner une &lt;em&gt;sémantique&lt;/em&gt; au langage M. C’est donc
ce que je me suis attelé à faire, en utilisant un mélange de rétro-ingénierie
et d’informations supplémentaires qui m’ont été données par la DGFiP, avec laquelle
j’ai pu régulièrement échanger pendant plusieurs mois. Ce travail a débouché sur
deux choses :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;une sémantique formelle du langage M, présentée dans &lt;a href=&quot;https://hal.inria.fr/hal-02320347&quot;&gt;un article de recherche&lt;/a&gt;;&lt;/li&gt;
  &lt;li&gt;un compilateur pour le langage M, &lt;a href=&quot;https://gitlab.inria.fr/verifisc/mlang&quot;&gt;Mlang&lt;/a&gt;, basé sur cette sémantique.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Je tiens à remercier pour leur aide précieuse :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://rmonat.fr/&quot;&gt;Raphaël Monat&lt;/a&gt;, doctorant au LiP6 et co-auteur de Mlang ;&lt;/li&gt;
  &lt;li&gt;Christophe Gaie et tous ses collaborateurs de l’équipe SI-1E de la DGFiP,
qui ont répondu avec bonne volonté à mes demandes d’information sur le langage M.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pourquoi est-il important que le compilateur soit basé sur une sémantique
formelle ? Car cette sémantique va nous permettre de valider les traductions du langage
M vers d’autres langages de programmation. Par exemple, Mlang permet de générer
statiquement une fonction Python qui reproduit le calcul de l’impôt tel qu’il est
fait sur &lt;a href=&quot;https://www3.impots.gouv.fr/simulateur/calcul_impot/2019/simplifie/index.htm&quot;&gt;le simulateur officiel de la DGFiP&lt;/a&gt;. Quel différence avec le parseur
précédemment écrit par Etalab ?&lt;/p&gt;

&lt;p&gt;Je me suis aperçu que le langage M possédait une
valeur spéciale, &lt;code class=&quot;highlighter-rouge&quot;&gt;indéfini&lt;/code&gt;. Dans le parseur Etalab, &lt;code class=&quot;highlighter-rouge&quot;&gt;indéfini&lt;/code&gt; est traduit par
la valeur 0. Cependant, le travail de réalisation de la sémantique formelle a permis
de réveler que &lt;code class=&quot;highlighter-rouge&quot;&gt;indéfini&lt;/code&gt; ne se comportait pas comme 0 dans tous les cas : par exemple,
la comparaison avec &lt;code class=&quot;highlighter-rouge&quot;&gt;indéfini&lt;/code&gt; donne toujours &lt;code class=&quot;highlighter-rouge&quot;&gt;indéfini&lt;/code&gt; (et non pas le résultat
de la comparaison avec 0). Ces petits détails rendent le compilateur beaucoup
plus robuste et augmentent le niveau d’assurance que l’on a sur les fichiers
générés à partir du code M grâce à Mlang. Plus précisément, cela réduit le risque
que le programme généré s’exécute différemment que le code M.&lt;/p&gt;

&lt;p&gt;Au moment où ce billet est publié, le code généré par Mlang est cependant toujours
faux. En effet, un effet secondaire de ce travail de formalisation et de validation
a été de découvrir que le code M publié par la DGFiP ne contient pas toutes les
informations nécessaires à répliquer le calcul de l’impôt. Techniquement, le code
M est appelé plusieurs fois avec des valeurs particulières pour certaines variables.
Cette subtilité technique n’avait pas été détectée au moment du hackathon et de
la première publication du code. J’ai pu en informer la DGFiP, qui met actuellement
des moyens en œuvre pour me permettre de récupérer dans leur système
les informations concernant ces appels multiples du code M avec différents
paramètres. Une fois ces informations récupérées, je pourrai achever le travail
de formalisation et valider Mlang sur les jeux de tests officiels de la DGFiP.&lt;/p&gt;

&lt;h2 id=&quot;conclusion--à-chaque-application-son-implémentation-du-code-des-impôts-&quot;&gt;Conclusion : à chaque application son implémentation du code des impôts ?&lt;/h2&gt;

&lt;p&gt;Le code M produit et publié chaque année par la DGFiP à l’issue du vote de
la loi fiscale va ainsi pouvoir servir de
base à tout une série d’implémentations de tout ou partie du code des impôts insérées
dans diverses applications. En effet, Mlang offre la possibilité de générer à partir du
code M et vers divers langages de programmation des programmes calculant une liste de variables de sorties en fonction d’une liste de variables d’entrées et de conditions spécifiques.
Concrètement, si &lt;a href=&quot;https://leximpact.beta.gouv.fr/&quot;&gt;LexImpact&lt;/a&gt; a besoin d’une implémentation Javascript ou
&lt;a href=&quot;https://webassembly.org/&quot;&gt;WebAssembly&lt;/a&gt; de la fonction qui calcule l’impôt de cas types en fonction
des valeurs des tranches d’imposition, Mlang pourra la générer ! Dans ce cas précis,
le calcul de l’impôt pourra même se faire dans le navigateur Internet et non
sur un serveur.&lt;/p&gt;

&lt;p&gt;Une autre application intéressante serait la traduction du code M vers des langages
de simulation macro-économique comme &lt;a href=&quot;https://www.intex.com/troll/home.htm&quot;&gt;TROLL&lt;/a&gt; ou &lt;a href=&quot;https://fr.wikipedia.org/wiki/SAS_(langage)&quot;&gt;SAS&lt;/a&gt;. En effet, ces
langages sont utilisés par l’Insee afin de réaliser des &lt;a href=&quot;https://www.insee.fr/fr/information/2021959&quot;&gt;modèles de l’économie
française&lt;/a&gt; qui ont besoin de l’expression mathématique de l’impôt sur le
revenu. Ces modèles pourraient donc chaque année se mettre à jour en récupérant
la nouvelle version du code M compilée vers TROLL ou SAS, au lieu de devoir refléter
eux aussi les changements de législation dans leur code.&lt;/p&gt;

&lt;p&gt;Enfin, Mlang va pouvoir améliorer notre prototype de prouveur automatique du
code des impôts. En effet, avant de le traduire vers un solveur SMT, il est nécessaire
de réduire la complexité mathématique de la fonction à sa substantifique moelle pour
éviter de faire exploser la consommation de ressources du solveur. Le cadre du
compilateur Mlang et la sémantique formelle de M nous permettent de réaliser des
optimisations fiables qui vont simplifier le programme sans en changer le sens,
avec ou sans conditions supplémentaires. Au delà des optimisations classiques
telles que &lt;a href=&quot;/fr/2017/12/13/flot-controle.html&quot;&gt;l’élimination de code mort&lt;/a&gt;, il est aussi prévu d’utiliser l’outil
d’interprétation abstraite &lt;a href=&quot;http://mopsa.lip6.fr/index.html&quot;&gt;MOPSA&lt;/a&gt; afin d’effectuer une analyse plus fine
du programme. Raphaël Monat et moi espérons que, combinée à des stratégies de
division de requêtes en sous-problème, cette optimisation du code M nous permette
de faire passer à l’échelle notre prototype de preuve automatique de théorèmes
sur le code des impôts.&lt;/p&gt;

&lt;p&gt;Au delà de l’aventure technique, je pense qu’il y a plusieurs leçons à retirer
de cette histoire. Premièrement, c’est le libre accès à la recherche et au code
qui permet aux synergies et aux initiatives inhabituelles de se développer. La
DGFiP est la première administration au monde à publier ainsi son implémentation
du calcul de l’impôt, et je pense que ce projet apportera rapidement
à cette administration des retombées positives. Tout mon code est également
publié sous licence libre, car il est important qu’un outil qui permette
l’analyse fine et automatisée de la fiscalité française soit accessible à toutes
et tous, et non pas réservé à un acteur privé qui l’utiliserait pour son propre
profit. Deuxièmement, je pense que ce projet montre une fois de plus que l’innovation
vient souvent d’un travail de recherche fondamentale mené sur le long terme.
Les outils utilisés ici (comme le prouveur Z3) ont des fondements théoriques solides
établis sur des dizaines d’années, et je me tiens bien sûr sur les épaules de géants.
J’espère que cet exemple d’application des méthodes formelles pourra attirer
l’attention sur ce domaine peu &lt;em&gt;marketable&lt;/em&gt; en ces temps de course à
“l’intelligence artificielle”. Troisièmement et pour terminer, ma volonté avec ce
projet est aussi de montrer que l’innovation peut aussi profiter directement
au secteur public. Je salue ainsi l’existence du programme des &lt;a href=&quot;https://entrepreneur-interet-general.etalab.gouv.fr/&quot;&gt;entrepreneurs
d’intérêt général&lt;/a&gt; dont fait partie l’initiative LexImpact. Un tel programme
ne pourrait selon moi que gagner à se rapprocher de la recherche appliquée menée
également pour l’intérêt général dans les laboratoires français. Alors que la
perspective d’une carrière dans la recherche publique &lt;a href=&quot;https://www.lemonde.fr/sciences/article/2019/10/14/le-blues-des-chercheurs-francais_6015488_1650684.html&quot;&gt;semble si morose&lt;/a&gt; pour
nombre de jeunes gens très qualifiés, l’idée de pouvoir utiliser ses
connaissances pour améliorer l’action de l’État me semblerait être un moteur
puissant à utiliser afin d’éviter la fuite des cerveaux vers un secteur privé
dont les activités, surtout dans le domaine de la tech, manquent parfois de sens.&lt;/p&gt;

</description>
        <pubDate>Fri, 20 Dec 2019 01:00:00 +0100</pubDate>
        <link>https://blog.merigoux.fr/fr/2019/12/20/impots-formels.html</link>
        <guid isPermaLink="true">https://blog.merigoux.fr/fr/2019/12/20/impots-formels.html</guid>
        
        
      </item>
    
      <item>
        <title>Chroniques de Rox* #2</title>
        <description>&lt;p&gt;Ce billet fait partie d’une série écrite dans le cadre de mon travail de doctorat, et qui vulgarise la future chaîne de compilation Rox* (Rust-Oxide-F*) dont l’objectif est d’amener à Rust de la vérification déductive. Le contexte complet de ce projet est expliqué dans l’&lt;a href=&quot;/fr/2019/04/16/verifier-rust.html&quot;&gt;article initial&lt;/a&gt;. Le code de cette étude de cas est disponible &lt;a href=&quot;https://github.com/denismerigoux/rox-star&quot;&gt;ici&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;filtre-de-bloom-à-compteur&quot;&gt;Filtre de Bloom à compteur&lt;/h2&gt;

&lt;p&gt;À l’instar des autres moteurs de navigateur Internet, Servo optimise le rendu CSS des pages Web. Voilà comment ça marche : chaque élément du DOM (Document Object Model) est qualifié par des attributs, comme des classes et des identifiants. De l’autre côté, le style est déclaré par l’utilisateur en associant un sélecteur CSS (une combinaison d’attributs) avec un style particulier. Quand la page Web est rendue, Servo parcourt chaque élément du DOM et essaye de déterminer quel sélecteur CSS peut s’y appliquer. Or, trouver quel sélecteur CSS s’applique à un élément du DOM est cher, et pour optimiser cette étape les moteurs de navigateur Internet utilisent une structure de données auxiliaire appelée &lt;a href=&quot;https://fr.wikipedia.org/wiki/Filtre_de_Bloom&quot;&gt;filtre de Bloom&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Un filtre de Bloom peut être vu comme un ensemble d’éléments. Vous pouvez y ajouter ou en enlever des éléments, et faire une requête pour savoir si un élément est dedans. Toutes ces opérations ont une complexité asymptotique constante et sont généralement très rapides (le filtre de Bloom peut être implémenté par un simple tableau), c’est donc presque trop beau pour être vrai. En effet, l’opération de requête n’est pas exacte :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;si la requête retourne &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;, alors on est sûrs que l’élément requêté n’est pas dans le filtre ;&lt;/li&gt;
  &lt;li&gt;si la requête retourne &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;, alors l’élément est &lt;em&gt;probablement&lt;/em&gt; dans le filtre, mais peut-être pas. La probabilité des faux positifs peut être calculée de manière théorique à partir des paramètres du filtre.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dans Servo, le filtre de Bloom est utilisé de la manière suivante pour chaque élément du DOM : d’abord, on remplit le filtre avec tous les attributs de l’élément du DOM. Puis, pour chaque attribut du sélecteur CSS contre lequel on veut le tester, on requête l’attribut dans le filtre. Si toutes les requêtes renvoient &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;, alors le sélecteur CSS correspond &lt;em&gt;peut-être&lt;/em&gt; à l’élément du DOM, donc on poursuit avec une vérification plus coûteuse. Si au moins une des requêtes retourne &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;, alors le sélecteur ne peut pas correspondre à l’élément du DOM et l’on peut le rejeter rapidement.&lt;/p&gt;

&lt;p&gt;L’&lt;a href=&quot;https://github.com/servo/servo/blob/master/components/selectors/bloom.rs&quot;&gt;implémentation du filtre de Bloom dans Servo&lt;/a&gt; est très simple : il s’agit d’un tableau de compteurs qui sont tous des octets. Pour ajouter un élément, on calcule deux index du tableau à partir du hash de l’élément du DOM, et l’on incrémente les compteurs à ces deux index. Le retrait d’un élément est similaire mais décrémente les compteurs. La requête retourne &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; si et seulement si les deux index ont des compteurs dont la valeur n’est pas nulle. Cependant, cette implémentation simple possède des cas limites ambigus : et si les deux index d’un même élément sont les mêmes ? Que se passe-t-il en cas d’overflow ou d’underflow d’un des compteurs ? Et si l’on retire un élément qui n’était pas dans le filtre au départ ?&lt;/p&gt;

&lt;p&gt;Je voulais m’assurer que tous ces cas limites étaient pris en compte dans l’implémentation de Servo, et j’ai ainsi traduit le &lt;a href=&quot;https://github.com/denismerigoux/rox-star/blob/master/bloom-filter/src/lib.rs&quot;&gt;cœur logique&lt;/a&gt; du filtre de Bloom en F*. La propriété qui doit être préservée par toutes les opérations du filtre est : pour chaque élément inséré dans le filtre, la requête pour cet élément doit retourner &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;. En effet, si cette propriété est préservée par toutes les opérations qui manipulent le filtre, alors elle est toujours vraie. Cependant, il faut se rappeler exactement de tous les éléments qui ont été ajoutés au filtre.&lt;/p&gt;

&lt;p&gt;Pour cela, j’ai du quitter l’univers de Rust et me plonger dans F* où j’ai défini une structure de données &lt;em&gt;fantôme&lt;/em&gt;, qui représentera l’ensemble exact des éléments que contient le filtre à tout moment. Cette structure de donnée est “fantôme” car elle n’est pas destinée à être implémentée ou exécutée ; elle est un artefact de preuve. Étant donné un filtre de Bloom et un ensemble fantôme des éléments qu’il contient, j’ai pu m’atteler à la preuve de la préservation de l’invariant.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/denismerigoux/rox-star/blob/master/bloom-filter/src-fstar/Proof.BloomFilter.fst&quot;&gt;Celle-ci&lt;/a&gt; représente environ 500 lignes de code F* et m’a pris deux semaines. Elle a été développé manuellement et minutieusement car elle est pleine d’invariants récursifs que Z3 ne peut pas trouver tout seul. Finir la preuve m’a donné trois informations intéressantes sur le fonctionnement du filtre de Bloom :&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Afin de prouver la présevration de l’invariant de départ, j’ai en fait du prouver un invariant beaucoup plus fort : “la valeur de chaque compteur dans le filtre est soit &lt;code class=&quot;highlighter-rouge&quot;&gt;MAX_U8&lt;/code&gt;, soit est égale à la somme de toutes les fois qu’un élément dont le hash correspond au compteur a été ajouté (moins les fois où il a été retiré), et compté deux fois si les deux index du hash tombent au même endroit”.&lt;/li&gt;
  &lt;li&gt;Lorsqu’un compteur atteint &lt;code class=&quot;highlighter-rouge&quot;&gt;MAX_U8&lt;/code&gt;, celui-ci entre dans un état absorbant où toute requête renvoie toujours &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;. Cela augmente le taux de faux positifs global du filtre.&lt;/li&gt;
  &lt;li&gt;Si l’on retire du filtre un élément qui n’était &lt;em&gt;pas vraiment dedans&lt;/em&gt;, alors il est possible de casser l’invariant et donc la correction du filtre.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cette troisième propriété est très intéressante puisqu’elle ajoute une précondition sur la fonction &lt;code class=&quot;highlighter-rouge&quot;&gt;remove&lt;/code&gt; du filtre : l’élément retiré doit avoir été ajouté auparavant. En conséquence, le code qui appelle &lt;code class=&quot;highlighter-rouge&quot;&gt;remove&lt;/code&gt; doit respecter cette précondition. Dans Servo, le filtre est mis à jour de manière incrémentale au long de la traversée de l’arbre du DOM à l’aide d’un &lt;a href=&quot;https://github.com/servo/servo/blob/master/components/style/bloom.rs&quot;&gt;algorithme non-trivial&lt;/a&gt; qui a été abondamment commenté par son développeur. Il serait donc intéressant de poursuivre cette étude de cas et de prouver que le mécanisme de mise à jour incrémentale ne viole jamais la précondition du filtre pendant la traversée du DOM.&lt;/p&gt;

</description>
        <pubDate>Tue, 16 Apr 2019 03:00:00 +0200</pubDate>
        <link>https://blog.merigoux.fr/fr/2019/04/16/filtre-bloom.html</link>
        <guid isPermaLink="true">https://blog.merigoux.fr/fr/2019/04/16/filtre-bloom.html</guid>
        
        
      </item>
    
      <item>
        <title>Chroniques de Rox* #1</title>
        <description>&lt;p&gt;Ce billet fait partie d’une série écrite dans le cadre de mon travail de doctorat, et qui vulgarise la future chaîne de compilation Rox* (Rust-Oxide-F*) dont l’objectif est d’amener à Rust de la vérification déductive. Le contexte complet de ce projet est expliqué dans l’&lt;a href=&quot;/fr/2019/04/16/verifier-rust.html&quot;&gt;article initial&lt;/a&gt;. Le code de cette étude de cas est disponible &lt;a href=&quot;https://github.com/denismerigoux/rox-star&quot;&gt;ici&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;le-module-textinput-de-servo&quot;&gt;Le module &lt;code class=&quot;highlighter-rouge&quot;&gt;TextInput&lt;/code&gt; de Servo&lt;/h2&gt;

&lt;p&gt;Notre premier exemple concerne la manière dont Servo gère le mouvement du curseur à l’intérieur de l’élément HTML multi-ligne &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;textinput&amp;gt;&lt;/code&gt;. Toute la logique de cette fonctionnalité est contenue à l’intérieur d’&lt;a href=&quot;https://github.com/servo/servo/blob/master/components/script/textinput.rs&quot;&gt;un seul fichier&lt;/a&gt;. Afin d’éviter d’avoir à gérer les structures spécifiques à Servo comme &lt;code class=&quot;highlighter-rouge&quot;&gt;DOMString&lt;/code&gt; qui ne font potentiellement pas partie du sous-ensemble traduisible vers F*, j’ai écrit &lt;a href=&quot;https://github.com/denismerigoux/rox-star/blob/master/textinput/src/lib.rs&quot;&gt;une nouvelle version du fichier&lt;/a&gt; qui contient uniquement le cœur logique de la fonctionnalité. La structure de données principale qui décrit l’état d’une sélection de texte à l’intérieur du &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;textinput&amp;gt;&lt;/code&gt; est la suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TextInput&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;/// Current text input content, split across lines without trailing '\n'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;/// Current cursor input point&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;edit_point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TextPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;/// The current selection goes from the selection_origin until the edit_point. Note that the&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;/// selection_origin may be after the edit_point, in the case of a backward selection.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;selection_origin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextPoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;selection_direction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SelectionDirection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Deux choses à noter ici : premièrement, toutes les lignes du texte n’ont pas la même longueur. Aussi, naviguer à l’intérieur du texte n’est pas aussi simple que dans un rectangle parfait. Deuxièmement, nous devons nous souvenir de l’origine et de la direction de la zone de sélection, qui peut être mise à jour lorsque le curseur bouge. Étant donnés tous ces éléments, il n’est pas trivial de recenser toutes les situations possibles lorsque l’on presse la touche directionnelle haute. C’est précisément là où la vérification peut nous aider à ne rien oublier.&lt;/p&gt;

&lt;p&gt;Afin d’aider à déboguer le code, le développeur de Servo a écrit une fonction très intéressante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;c&quot;&gt;// Check that the selection is valid.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assert_ok_selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.selection_origin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;debug_assert!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.lines&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;debug_assert!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.selection_direction&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nn&quot;&gt;SelectionDirection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;None&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;SelectionDirection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Forward&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nd&quot;&gt;debug_assert!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.edit_point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;

            &lt;span class=&quot;nn&quot;&gt;SelectionDirection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Backward&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;debug_assert!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.edit_point&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;debug_assert!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.edit_point.line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.lines&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;debug_assert!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.edit_point.index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.edit_point.line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Cette fonction &lt;code class=&quot;highlighter-rouge&quot;&gt;assert_ok_selection&lt;/code&gt; est appelée à la fin de chaque fonction qui met à jour &lt;code class=&quot;highlighter-rouge&quot;&gt;TextInput&lt;/code&gt;, mais elle ne contient que des &lt;code class=&quot;highlighter-rouge&quot;&gt;debug_assert&lt;/code&gt; et est éliminée en production. Ce motif est exactement équivalent à une postcondition, et c’est ainsi que je l’ai traduite dans mon code F*.&lt;/p&gt;

&lt;p&gt;Une fois tout traduit en F*, j’ai prouvé que chaque fonction qui mettait à jour &lt;code class=&quot;highlighter-rouge&quot;&gt;TextInput&lt;/code&gt; préservait l’invariant &lt;code class=&quot;highlighter-rouge&quot;&gt;assert_ok_selection&lt;/code&gt; : si c’est vrai à l’appel de la fonction (dans la précondition), alors ça devrait être vrai après la fonction (dans la postcondition). Grâce au prouveur automatique Z3, la preuve a été presque complètement automatique, et je n’ai eu à guider le prouveur qu’en ajoutant une assertion dans une fonction. Cependant, je n’ai pas pu prouver tout de suite que l’invariant tenait pour la fonction &lt;code class=&quot;highlighter-rouge&quot;&gt;adjust_vertical&lt;/code&gt; ; en effet, celle-ci contenait un bug ! De plus, ce bug causait des problèmes visibles dans la dernière version de Servo, comme décrit dans le &lt;a href=&quot;https://github.com/servo/servo/issues/22457&quot;&gt;ticket&lt;/a&gt; que j’ai ouvert immédiatement.&lt;/p&gt;

&lt;p&gt;En utilisant les messages d’erreur de F*, j’ai pu trouver une manière de corriger le code afin de prouver la postcondition de la fonction. J’ai ensuite traduit l’équivalent de ce patch dans le code Rust originel et ai ouvert une &lt;a href=&quot;https://github.com/servo/servo/pull/22458&quot;&gt;demande de fusion de branches&lt;/a&gt; qui incluait le patch et quelques cas de test supplémentaires qui déclenchaient le bug.&lt;/p&gt;

&lt;p&gt;Notons que ce travail ne garantit pas que le code est complètement “correct”. En effet, j’ai juste prouvé qu’un invariant était préservé et ce quelque soit les entrées utilisateur. Grâce à cette méthode, j’ai pu trouver une situation non couverte par les cas de test existants et qui déclenchait des &lt;em&gt;crashs&lt;/em&gt; dans la dernière version de Servo. Avec un outil de traduction automatique de Rust vers F* ainsi qu’à l’aide de la recherche de preuves automatique, je pense que la vérification est en fait plus efficace en termes de temps de développement que de devoir imaginer tous les cas de tests qui déclencheraient toutes les situations. De plus, utiliser la vérification a l’avantage de vous faire savoir quand vous avez terminé : lorsque c’est prouvé, c’est prouvé. Écrire un test de plus ne peut jamais garantir que l’on en a écrit assez.&lt;/p&gt;

</description>
        <pubDate>Tue, 16 Apr 2019 02:00:00 +0200</pubDate>
        <link>https://blog.merigoux.fr/fr/2019/04/16/textinput.html</link>
        <guid isPermaLink="true">https://blog.merigoux.fr/fr/2019/04/16/textinput.html</guid>
        
        
      </item>
    
      <item>
        <title>Rust: au delà des types</title>
        <description>&lt;p&gt;Ce billet sera différent des autres, puisque je vais présenter ici quelques uns des premiers résultats de mon travail en tant que doctorant dans l’&lt;a href=&quot;https://prosecco.gforge.inria.fr/&quot;&gt;équipe Prosecco&lt;/a&gt; à l’Inria Paris, sous la supervision de &lt;a href=&quot;https://prosecco.gforge.inria.fr/personal/karthik/&quot;&gt;Karthikeyan Bhargavan&lt;/a&gt; et &lt;a href=&quot;https://jonathan.protzenko.fr/&quot;&gt;Jonathan Protzenko&lt;/a&gt;. En effet, mon sujet de thèse est d’apporter à Rust le pouvoir des techniques de vérification déductive afin d’augmenter le niveau de confiance que l’on puisse avoir à propos de la correction de ses programmes. Je suis en mesure de vous présenter maintenant deux études de cas prises dans la base de code de &lt;a href=&quot;https://servo.org/&quot;&gt;Servo&lt;/a&gt;, et qui montrent à quoi peut ressembler l’utilisation de techniques de vérification.&lt;/p&gt;

&lt;h2 id=&quot;comment-savoir-quun-programme-est-correct-&quot;&gt;Comment savoir qu’un programme est correct ?&lt;/h2&gt;

&lt;p&gt;Premièrement, il nous faut déterminer ce que cela veut dire pour un programme d’être &lt;em&gt;correct&lt;/em&gt;. La plupart du temps, un programme est testé sur d’un ensemble d’entrées vis-à-vis de sorties attendues. À chaque sortie du programme égale à la sortie attendue, on peut être un peu plus confiant en la correction de son programme. Mais comment être sûr que le programme va se comporter correctement dans toutes les situations ? Et si nos différents cas de test avaient manqué un cas limite de notre algorithme ?&lt;/p&gt;

&lt;p&gt;Une manière d’augmenter le niveau de confiance est de tester le programme de manière systématique, par exemple grâce aux techniques de tests unitaires ou de mesure de la couverture du code. Supposons que l’on veuille tester la fonction :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour écrire des tests unitaires, il faut écrire des tests pour chaque sous-fonction utilisée dans &lt;code class=&quot;highlighter-rouge&quot;&gt;foo&lt;/code&gt;. Avoir une couverture de code de 100% sur &lt;code class=&quot;highlighter-rouge&quot;&gt;foo&lt;/code&gt; signifie que les tests explorent toutes les branches du code de &lt;code class=&quot;highlighter-rouge&quot;&gt;foo&lt;/code&gt;: si &lt;code class=&quot;highlighter-rouge&quot;&gt;foo&lt;/code&gt; contient une conditionnelle portant sur le test &lt;code class=&quot;highlighter-rouge&quot;&gt;x + y &amp;gt; 1&lt;/code&gt;, il nous faut donc au moins un test où &lt;code class=&quot;highlighter-rouge&quot;&gt;x + y &amp;gt; 1&lt;/code&gt; et un autre où &lt;code class=&quot;highlighter-rouge&quot;&gt;x + y &amp;lt;= 1&lt;/code&gt;. Mais même une couverture de code de 100% ne garantit pas que nos tests atteignent tous les états du programmes contenant des bugs ; par exemple, les bugs d’overflow arithmétique ne sont pas automatiquement détectés par le test par couverture de code.&lt;/p&gt;

&lt;p&gt;Tester un programme de manière systématique est chronophage, et requiert de la part du développeur la création d’un modèle mental expliquant &lt;em&gt;comment le programme devrait marcher&lt;/em&gt; afin de créer les cas de tests les plus pertinents possibles. À l’issue de l’écriture des tests, on a deux résultats corrélés :&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Le développeur a réfléchi au comportement de son programme pour toutes les combinaisons des valeurs des variables d’entrées ;&lt;/li&gt;
  &lt;li&gt;Le niveau de confiance à propos de la correction du code est assez élevé pour un passage en production.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cependant, ces méthodes de tests ne peuvent pas dire si vous avez manqué quelque chose. Même si le code est testé de manière minutieuse contre une certaine catégorie de bugs, il est toujours théoriquement possible qu’un bug de cette catégorie reste caché quelque part.&lt;/p&gt;

&lt;h2 id=&quot;les-types-comme-outils-de-vérification&quot;&gt;Les types comme outils de vérification&lt;/h2&gt;

&lt;p&gt;À &lt;a href=&quot;https://popl19.sigplan.org/&quot;&gt;POPL19&lt;/a&gt;, Mark Harman a présenté Sapiens, un outil d’analyse extrêmement sophistiqué qui détecte et corrige automatiquement les bugs de la base de code de Facebook. Néanmoins, &lt;a href=&quot;https://youtu.be/CbQ6bJlOU7A?t=2792&quot;&gt;il admet pendant la présentation&lt;/a&gt; que la puissance des analyses statiques et de l’apprentissage statistique utilisés dans Sapiens n’avaient pour l’instant été employées presque exclusivement qu’à la détection de bugs liés à des pointeurs nuls.&lt;/p&gt;

&lt;p&gt;Pourquoi cette catégorie de bugs n’existe pas dans les programmes Rust ? Tout simplement, parce qu’il est par construction impossible d’écrire et compiler un programme qui contient un bug lié à un pointeur nul. En effet, le type &lt;code class=&quot;highlighter-rouge&quot;&gt;Option&lt;/code&gt; force le développeur à déclarer quelles variables peuvent prendre la valeur &lt;code class=&quot;highlighter-rouge&quot;&gt;None&lt;/code&gt;, et lesquelles ne peuvent pas. En obligeant le développeur penser à cela avant l’exécution du programme, le compilateur Rust encourage à réfléchir à ce qui se passe pour les valeurs &lt;code class=&quot;highlighter-rouge&quot;&gt;None&lt;/code&gt; &lt;em&gt;pour toutes les combinaisons des variables d’entrée&lt;/em&gt;. Grâce à ce mécanisme, plus besoin de se préoccuper des bugs liés au pointeurs nuls lors du débogage.&lt;/p&gt;

&lt;p&gt;Ce qu’il faut retenir ici est que le système de types, en rejetant à la compilation des programmes susceptibles de contenir des bugs, peut tuer dans l’œuf des catégories entières de bugs, permettant en ce faisant au développeur d’allouer plus de temps au test de propriétés plus intéressantes à propos du programme. La spécificité du système de type de Rust est qu’il est capable de garantir la sûreté mémoire des programmes ainsi que l’absence de &lt;a href=&quot;https://fr.wikipedia.org/wiki/Situation_de_comp%C3%A9tition&quot;&gt;situation de compétition&lt;/a&gt;, deux catégories de bugs redoutées et notoirement difficiles à débusquer en C ou C++.&lt;/p&gt;

&lt;p&gt;Mais comment l’algorithme de vérification des types peut-il être si sûr que ce qu’il avance est vrai quelques soient les entrées ? Prenons un autre exemple basé sur le type &lt;code class=&quot;highlighter-rouge&quot;&gt;Option&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Vous avez déjà peut-être remarqué que ce programme est incorrect; en effet, voici l’erreur signalée par le compilateur Rust:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0004]: non-exhaustive patterns: `(None, Some(_))` not covered
 --&amp;gt; src/main.rs:5:9
  |
2 |   match (x,y) {
  |         ^^^^^ pattern `(None, Some(_))` not covered&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Comment le compilateur peut-il savoir que vous aviez oublié ce cas ? En fait, le compilateur essaye de &lt;em&gt;prouver&lt;/em&gt; que pour tous les &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt; et &lt;code class=&quot;highlighter-rouge&quot;&gt;y&lt;/code&gt; de type &lt;code class=&quot;highlighter-rouge&quot;&gt;Option&amp;lt;i32&amp;gt;&lt;/code&gt;, la fonction &lt;code class=&quot;highlighter-rouge&quot;&gt;bar&lt;/code&gt; va retourner une valeur de type &lt;code class=&quot;highlighter-rouge&quot;&gt;i32&lt;/code&gt;. Comment prouverions-nous une telle propriété dans un cadre mathématique ? Simplement en considérant tous les cas associés à &lt;code class=&quot;highlighter-rouge&quot;&gt;Option&lt;/code&gt; : soit &lt;code class=&quot;highlighter-rouge&quot;&gt;Some&lt;/code&gt;, soit &lt;code class=&quot;highlighter-rouge&quot;&gt;None&lt;/code&gt;. Deux cas pour deux valeurs donc 4 cas au total ; le &lt;code class=&quot;highlighter-rouge&quot;&gt;match&lt;/code&gt; n’en a que 3 donc il nous en manque un.&lt;/p&gt;

&lt;p&gt;Cet exemple montre une correspondance entre le code et des concepts mathématiques, et cette idée appelée la &lt;a href=&quot;https://fr.wikipedia.org/wiki/Correspondance_de_Curry-Howard&quot;&gt;correspondance de Curry-Howard&lt;/a&gt; est la fondation de la recherche moderne en langages de programmation. Le but de cette branche de l’informatique est de se débarrasser systématiquement de plus en plus de catégories de bugs en prouvant leur absence des programmes, et de permettre aux développeurs de spécifier la correction de leurs programmes à l’aide de théories mathématiques de plus en plus expressives.&lt;/p&gt;

&lt;h2 id=&quot;un-plan-pour-amener-la-vérification-à-rust&quot;&gt;Un plan pour amener la vérification à Rust&lt;/h2&gt;

&lt;p&gt;Il y a de multiples de manières de produire un logiciel vérifié totalement ou partiellement. De nombreuses techniques éprouvées impliquent d’écrire le programme dans un langage haut-niveau dit de &lt;em&gt;spécification&lt;/em&gt;, pour lequel on connaît le comportement exact de chaque construction du langage. Cependant, Rust n’a pas été conçu à cet usage et ne possède pas de formalisation complète et officielle. Le projet &lt;a href=&quot;http://plv.mpi-sws.org/rustbelt&quot;&gt;Rustbelt&lt;/a&gt; a brillamment commencé à pallier à ce problème en prouvant que les parties &lt;code class=&quot;highlighter-rouge&quot;&gt;unsafe&lt;/code&gt; de la librairie standard de Rust ne mettent pas en danger les propriétés dont jouit le code &lt;code class=&quot;highlighter-rouge&quot;&gt;safe&lt;/code&gt; utilisant la librairie standard.&lt;/p&gt;

&lt;p&gt;Plus récemment, l’équipe du framework de vérification Viper a développé &lt;a href=&quot;https://www.research-collection.ethz.ch/handle/20.500.11850/311092&quot;&gt;une interface pour Rust&lt;/a&gt; qui permet la vérification automatique de la sûreté mémoire et de quelques propriétés arithmétiques. En interne, Viper utilise une technique de vérification appelée &lt;a href=&quot;https://fr.wikipedia.org/wiki/Interpr%C3%A9tation_abstraite&quot;&gt;exécution symbolique&lt;/a&gt; qui construit ses preuves sans aucune indiquation de la part de l’utilisateur. Viper utilise aussi une autre technique de vérification avec laquelle j’expérimenterai durant mon doctorat, la vérification dite déductive. Dans ce cadre, chaque fonction d’un programme possède une &lt;em&gt;précondition&lt;/em&gt; (ensemble de choses vraies avant l’appel de la fonction) et une &lt;em&gt;postcondition&lt;/em&gt; (ensemble de choses vraies à la sortie de la fonction). La vérification consiste à prouver que le corps de la fonction rend la postcondition vraie sous l’hypothèse que la précondition est vraie.&lt;/p&gt;

&lt;p&gt;Comment spécifier ces préconditions et postconditions en Rust ? La syntaxe et les détails sont &lt;a href=&quot;https://github.com/rust-lang-nursery/wg-verification/issues/14&quot;&gt;en cours de discussion&lt;/a&gt;, mais cela pourrait ressembler à ça :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_diagonal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.y&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;#[requires=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;is_diagonal(p)&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#[ensures=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;is_diagonal(new_p)&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.y&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La fonction &lt;code class=&quot;highlighter-rouge&quot;&gt;is_diagonal&lt;/code&gt; est la &lt;em&gt;spécification&lt;/em&gt; de la fonction &lt;code class=&quot;highlighter-rouge&quot;&gt;double&lt;/code&gt;, et n’est pas destinée à être exécutée. Elle sert plutôt de description mathématique à la fonction. Mais comment prouver cette postcondition ? Ici, cela revient simplement à prouver que &lt;code class=&quot;highlighter-rouge&quot;&gt;2 * p.y == p.x + p.x&lt;/code&gt; sous l’hypothèse que &lt;code class=&quot;highlighter-rouge&quot;&gt;p.x == p.y&lt;/code&gt;. Plutôt que de construire un framework de vérification &lt;em&gt;ad hoc&lt;/em&gt; pour Rust, j’ai l’intention d’utiliser &lt;a href=&quot;https://www.fstar-lang.org/&quot;&gt;F*&lt;/a&gt;, un assistant de preuve semi-automatique basé sur la vérification déductive et qui utilise le prouveur automatique &lt;a href=&quot;https://github.com/Z3Prover/z3&quot;&gt;Z3&lt;/a&gt; pour alléger la charge de preuve de l’utilisateur.&lt;/p&gt;

&lt;p&gt;Puisque le compilateur de Rust est très puissant et les propriétés qu’il garantit déjà partiellement vérifiées grâce à des projets comme Rustbelt, je vais plutôt me consacrer à la vérification de propriétés qui ne sont pas capturées par le système de types de Rust. Plus spécifiquement, des propriétés de correction fonctionnelle comme la correction arithmétique, la validité des accès aux tableaux, la correction de machines à états, la correction fonctionnelle vis-à-vis d’une spécification abstraite, ou bien des propriétés relatives à la sécurité. Le travail principal de ma thèse sera la production d’un outil, Rox*, probablement intégré d’une manière ou d’une autre au compilateur Rust, qui traduira des programmes Rust vers du code F* qui pourra ensuite être vérifié automatiquement, ou bien à l’aide d’annotations supplémentaires de l’utilisateur.&lt;/p&gt;

&lt;p&gt;Rust est très expressif et sophistiqué, aussi je me concentrerai sur un sous-ensemble raisonnable du langage assez expressif pour que les programmes qu’il contienne soient intéressants à vérifier. Ce sous-ensemble sera probablement construit autour d’&lt;a href=&quot;https://arxiv.org/abs/1903.00982&quot;&gt;Oxide&lt;/a&gt;, une nouvelle formalisation qui se focalise sur du code &lt;code class=&quot;highlighter-rouge&quot;&gt;safe&lt;/code&gt; non-concurrent utilisant un nombre limité de primitives de la librairie standard. Jusqu’à présent, j’ai réalisé deux études de cas qui montrent comment mon travail pourra augmenter le niveau de confiance concernant la correction de programmes Rust réalistes.&lt;/p&gt;

&lt;h2 id=&quot;Études-de-cas--textinput-et-filtre-de-bloom&quot;&gt;Études de cas : &lt;code class=&quot;highlighter-rouge&quot;&gt;TextInput&lt;/code&gt; et filtre de Bloom&lt;/h2&gt;

&lt;p&gt;Les deux études de cas proviennent du projet &lt;a href=&quot;https://servo.org/&quot;&gt;Servo&lt;/a&gt;, un nouveau moteur de navigateur Internet écrit en Rust et sponsorisé par Mozilla. Elles sont disponibles &lt;a href=&quot;https://github.com/denismerigoux/rox-star&quot;&gt;ici&lt;/a&gt;. Dans chaque cas, j’ai identifié un morceau de code qui pourrait bénéficier de vérification, puis je l’ai traduit manuellement vers un programme F* fonctionnellement équivalent et ai prouvé des propriétés sur le code F* qui augmentent notre niveau de confiance sur le code Rust. Cette traduction sera par la suite automatisée, mais ces études de cas me permettent d’évaluer la pertinence de mon approche avant de commencer le travail de développement du compilateur.&lt;/p&gt;

&lt;p&gt;Ces deux études de cas sont présentées dans des billets séparés &lt;a href=&quot;/fr/2019/04/16/textinput.html&quot;&gt;ici&lt;/a&gt; et &lt;a href=&quot;/fr/2019/04/16/filtre-bloom.html&quot;&gt;là&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;conclusion-et-perspectives&quot;&gt;Conclusion et perspectives&lt;/h2&gt;

&lt;p&gt;Ces deux études de cas m’ont aidé à identifier comment la vérification pouvait être appliquée à des programmes Rust réalistes. Certaines propriétés pourraient être spécifiées légèrement directement en Rust puis prouvées automatiquement, tandis que d’autres nécessiteront une plus grande connaissance d’un assistant de preuve comme F*. Tous les programmes n’ont pas besoin d’un niveau de confiance aussi élevé que celui offert par la vérification, et la plupart du temps le test systématique suffit. Cependant, la vérification peut parfois être plus rapide que le test systématique, et offrir une garantie formelle que le test ne pourra jamais. L’avantage est que cette méthode est également incrémentale : vous pouvez partir d’un programme qui passe le compilateur Rust, et ensuite prouver une propriété ou un invariant à la fois.&lt;/p&gt;

&lt;p&gt;Chaque technique de vérification (exécution symbolique, vérification déductive) a ses avantages et ses inconvénients qui dépendent de ce que vous voulez prouver. Aussi est-il important qu’un futur plugin Rust pour la vérification puisse permettre de cibler plusieurs frameworks de vérification. J’ai l’intention d’utiliser &lt;a href=&quot;https://arxiv.org/abs/1903.00982&quot;&gt;Oxide&lt;/a&gt; comme un langage intermédiaire sémantiquement bien défini et accessible depuis le Rust HIR, afin d’en faire une plateforme commune à de futures traductions.&lt;/p&gt;

&lt;p&gt;Le but de mon travail n’est pas fondationel, dans le sens où je n’essaie pas de prouver la sûreté ou la correction du système de types de Rust. Je ne compte pas dans un premier temps m’attaquer au difficile problème de l’interaction entre code Rust &lt;code class=&quot;highlighter-rouge&quot;&gt;safe&lt;/code&gt; et code &lt;code class=&quot;highlighter-rouge&quot;&gt;unsafe&lt;/code&gt; ; &lt;a href=&quot;http://plv.mpi-sws.org/rustbelt&quot;&gt;Rustbelt&lt;/a&gt; est la bonne manière de le faire. Mon travail de doctorat se concentrera plutôt sur ce qui se passe &lt;em&gt;au delà de la vérification de types&lt;/em&gt; afin d’offrir une vérification graduelle de propriétés de haut-niveau sur du code Rust, comme les études de cas le montrent.&lt;/p&gt;

&lt;p&gt;Le mariage entre Rust et méthodes formelles a attiré l’attention d’un certain nombre de chercheurs et d’entreprises réunies dans le &lt;a href=&quot;https://rust-lang.zulipchat.com/#narrow/stream/183875-wg-formal-methods&quot;&gt;groupe méthodes formelles de Rust&lt;/a&gt;. J’espère que mon travail aidera la communauté Rust à devenir plus familière des techniques de vérification, et de la manière dont elles peuvent être utilisées afin d’augmenter le niveau de confiance en la correction de divers programmes Rust.&lt;/p&gt;

</description>
        <pubDate>Tue, 16 Apr 2019 01:00:00 +0200</pubDate>
        <link>https://blog.merigoux.fr/fr/2019/04/16/verifier-rust.html</link>
        <guid isPermaLink="true">https://blog.merigoux.fr/fr/2019/04/16/verifier-rust.html</guid>
        
        
      </item>
    
      <item>
        <title>Dupliquer c'est optimiser</title>
        <description>&lt;p&gt;Après presque un an de silence, il est grand temps pour moi de tenir mes promesses et de continuer &lt;a href=&quot;/fr/2017/12/13/flot-controle.html&quot;&gt;ce que j’avais commencé&lt;/a&gt; pour démystifier ce qu’il se passe à l’intérieur d’un compilateur qui &lt;em&gt;optimise&lt;/em&gt; le code qu’il reçoit. Jusqu’ici, toutes les techniques que nous avons vues avaient pour but de tailler dans la masse de code pour enlever et factoriser tout ce qui est redondant ou superflu : &lt;a href=&quot;/fr/2017/08/09/objets.html&quot;&gt;polymorphisme et factorisation de code dans les langages de haut niveau&lt;/a&gt;, élimination de code inutile. Mais de manière contre-intuitive, il est souvent nécessaire de faire exactement le contraire pour que le code s’exécute plus vite sur la machine.&lt;/p&gt;

&lt;h2 id=&quot;le-coût-de-labstraction&quot;&gt;Le coût de l’abstraction&lt;/h2&gt;

&lt;p&gt;Pour comprendre pourquoi, il faut se rappeler une des leçons de &lt;a href=&quot;/fr/2017/08/02/memoire.html&quot;&gt;la gestion de la mémoire&lt;/a&gt; : les abstractions, très utiles pour programmer à plus haut niveau, viennent toujours au prix d’une efficacité moindre. Et cette règle s’applique également au concept même de fonction, qui est une abstraction n’existant pas en assembleur. Prenons un exemple concret d’un petit programme C qui calcule la somme des \(n\) premiers nombres, où \(n\) est donné par l’utilisateur à l’exécution du programme.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;incr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atoi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;incr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Voilà comment est compilée la fonction &lt;code class=&quot;highlighter-rouge&quot;&gt;incr&lt;/code&gt; en assembleur Intel par le compilateur &lt;code class=&quot;highlighter-rouge&quot;&gt;gcc&lt;/code&gt; au niveau d’optimisation &lt;code class=&quot;highlighter-rouge&quot;&gt;-O1&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;0000000000400596 &amp;lt;incr&amp;gt;:
400596:	8d 04 37             	lea    (%rdi,%rsi,1),%eax
400599:	c3                   	retq&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;On peut se rappeler comment lire ce bout de code en lisant le &lt;a href=&quot;/fr/2017/07/26/assembleurs.html&quot;&gt;post concernant l’assembleur&lt;/a&gt;. On voit que la fonction est extrêmement petite, et que l’addition est faite avec une seule instruction. Cependant, voilà ce que donne l’assembleur du corps de la boucle:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;4005dc:	89 de                	mov    %ebx,%esi
4005de:	89 c7                	mov    %eax,%edi
4005e0:	e8 b1 ff ff ff       	callq  400596 &amp;lt;incr&amp;gt;
4005e5:	83 c3 01             	add    $0x1,%ebx
4005e8:	39 eb                	cmp    %ebp,%ebx
4005ea:	75 f0                	jne    4005dc &amp;lt;main+0x42&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si les trois dernières instructions (&lt;code class=&quot;highlighter-rouge&quot;&gt;add&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;cmp&lt;/code&gt; et &lt;code class=&quot;highlighter-rouge&quot;&gt;jne&lt;/code&gt;) servent à faire la boucle &lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt;, on voit que l’appel de la fonction &lt;code class=&quot;highlighter-rouge&quot;&gt;incr&lt;/code&gt; prend trois instructions à lui tout seul ! D’autant plus que les instructions &lt;code class=&quot;highlighter-rouge&quot;&gt;call&lt;/code&gt; et &lt;code class=&quot;highlighter-rouge&quot;&gt;ret&lt;/code&gt; en cachent en fait deux : un accès mémoire pour stocker et récupérer l’adresse de retour de la fonction et un saut vers le bon endroit dans le code. Toutes ces instructions qui apparaissent en plus du corps de la fonction &lt;code class=&quot;highlighter-rouge&quot;&gt;incr&lt;/code&gt; servent en fait à implémenter la &lt;em&gt;convention d’appel&lt;/em&gt;, expliquée dans &lt;a href=&quot;/fr/2017/08/16/pile-tas.html&quot;&gt;le post à propos de la pile d’appels de fonctions&lt;/a&gt;. Cette convention d’appel est précisément le coût de l’abstraction du concept de fonction, et l’on voit qu’il représente jusqu’à une dizaine d’instructions assembleur exécutées à chaque appel de la fonction &lt;code class=&quot;highlighter-rouge&quot;&gt;incr&lt;/code&gt; par le processeur. Notons que le nombre d’instructions liées à la convention d’appel ne dépendant pas de la taille de la fonction, mais plutôt du nombre d’arguments qu’elle utilise.&lt;/p&gt;

&lt;h2 id=&quot;extension-inline&quot;&gt;Extension inline&lt;/h2&gt;

&lt;p&gt;Les plus motivés d’entre vous auront remarqué que je vous ai menti un peu jusqu’ici : l’assembleur du corps de boucle que je vous ai montré n’est pas exactement celui généré par &lt;code class=&quot;highlighter-rouge&quot;&gt;gcc&lt;/code&gt; au niveau d’optimisation &lt;code class=&quot;highlighter-rouge&quot;&gt;-O1&lt;/code&gt;. Voici ce qui se passe en réalité :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;4005dc:	01 c2                	add    %eax,%edx
4005de:	83 c0 01             	add    $0x1,%eax
4005e1:	39 d8                	cmp    %ebx,%eax
4005e3:	75 f7                	jne    4005dc &amp;lt;main+0x42&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;L’appel a la fonction &lt;code class=&quot;highlighter-rouge&quot;&gt;incr&lt;/code&gt; a disparu ! Mais pourquoi le compilateur a-t-il supprimé notre fonction, et par quoi l’a-t-il remplacé ? Une comparaison rapide permet de se convaincre que le compilateur a tout simplement remplacé l’appel de fonction par le corps de la fonction lui-même, l’instruction &lt;code class=&quot;highlighter-rouge&quot;&gt;add&lt;/code&gt; (plus ou moins équivalente à &lt;code class=&quot;highlighter-rouge&quot;&gt;lea&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;La première conséquence de ce remplacement est la diminution du nombre d’instructions du programme assembleur que la machine va exécuter ; on peut donc s’attendre à ce que l’éxecution soit maintenant plus rapide ! Et en effet, avec \(n=4\cdot10^9\), le programme original (sans remplacement) s’exécute en 0,55 s tandis que le programme avec remplacement s’exécute en 0,16 s. Cette optimisation nous a apporté un gain en performance de 340 % !&lt;/p&gt;

&lt;p&gt;À plus haut niveau, la fonction &lt;code class=&quot;highlighter-rouge&quot;&gt;incr&lt;/code&gt; peut être utilisée à plusieurs endroits, justifiant l’existence de cette fonction en tant qu’élément logique. Cependant, cette abstraction qui rend le code plus lisible est nuisible à la performance du programme compilé. Afin d’aller au delà de cet apparent paradoxe et permettre de concilier performance et abstraction, presque tous les compilateurs décident automatiquement de remplacer les appels de fonction par le corps de la fonction. Cette optimisation s’appelle l’&lt;em&gt;inlining&lt;/em&gt; (ou extension inline en français) et est généralement l’une des optimisations ayant le meilleur rapport complexité/gain en performance.&lt;/p&gt;

&lt;p&gt;L’inlining permet également d’augmenter les effets des autres optimisations du compilateur. En effet, les optimisations telles que l’analyse du code inutile reposent sur la capacité à analyser le code pour en déterminer des propriétés sur l’utilisation de telle ou telle variable, la structure des boucles, etc. Or, ces analyes de code sont obligées de s’arrêter à la porte des appels de fonctions. L’inlining a l’avantage de rendre le corps des fonctions visibles aux autres optimisations, leur permettant de repérer des structures auparavant cachées par l’appel de fonction.&lt;/p&gt;

&lt;p&gt;Cependant, appliquer l’extension inline à toutes les fonctions n’est pas forcément une bonne idée. En effet, pour de très longues fonctions, l’inlining a plutôt pour effet d’augmenter énormément la taille du code assembleur généré pour des gains en performances négligeables. Pire, un code assembleur trop long dans un corps de boucle peut conduire à une perte de performance considérable. Cela est dû à des phénomènes de &lt;em&gt;cache mémoire&lt;/em&gt;, que je détaillerai dans un futur billet. Ainsi, les compilateurs décident d’inliner ou pas selon un ensemble d’heuristiques guidées par de nombreux benchmarks, et au sein desquelles la taille du corps de la fonction joue un rôle principal.&lt;/p&gt;

&lt;h2 id=&quot;déroulement-de-boucles-et-prédiction-de-branches&quot;&gt;Déroulement de boucles et prédiction de branches&lt;/h2&gt;

&lt;p&gt;Passons à un cas plus compliqué : comment faire pour inliner la fonction suivante ?&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fibo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fibo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fibo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il est possible de remplacer &lt;code class=&quot;highlighter-rouge&quot;&gt;fibo(n - 1)&lt;/code&gt; et &lt;code class=&quot;highlighter-rouge&quot;&gt;fibo(n - 2)&lt;/code&gt; par le corps de la fonction, mais celui-ci contient aussi des appels à &lt;code class=&quot;highlighter-rouge&quot;&gt;fibo&lt;/code&gt;… Cette fonction est récursive, et il n’est pas possible d’y inliner complètement les appels de fonction. Par contre, on peut répéter la procédure d’inlining une, deux ou trois fois : quel est l’avantage de faire cela ?&lt;/p&gt;

&lt;p&gt;Une situation similaire est celle des corps de boucles : on peut également choisir de répéter le corps des boucles deux fois ou plus en substituant la variable &lt;code class=&quot;highlighter-rouge&quot;&gt;i&lt;/code&gt; par &lt;code class=&quot;highlighter-rouge&quot;&gt;i+1&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;i+2&lt;/code&gt; par exemple. Cette répétition ou &lt;code class=&quot;highlighter-rouge&quot;&gt;unrolling&lt;/code&gt; en anglais a plusieurs avantages. D’abord, si le nombre d’itérations est connu à la compilation, cela permet de supprimer des instructions &lt;code class=&quot;highlighter-rouge&quot;&gt;jump&lt;/code&gt; qui sont pénalisantes d’un point de vue de la performance à cause de la &lt;em&gt;prédiction de branches&lt;/em&gt; (que je détaillerai peut-être dans un prochain post). D’autre part et pour la même raison de l’inlining, l’unrolling en faisant apparaître clairement le code exécuté séquentiellement permet aux analyses de code de potentiellement reconnaître de nouvelles structures à optimiser. Par exemple, les processeurs possèdent des instructions spéciales pour exécuter la même opération sur plusieurs zones de la mémoire en même temps (&lt;a href=&quot;https://fr.wikipedia.org/wiki/Single_instruction_multiple_data&quot;&gt;SIMD&lt;/a&gt;) ; l’unrolling est nécessaire pour que le compilateur puisse s’apercevoir qu’il peut traduire le code vers ces instructions spéciales.&lt;/p&gt;

&lt;p&gt;Néanmoins et comme pour l’inlining, l’unrolling appliqué abusivement peut avoir des conséquences néfastes sur la performance. Aussi les compilateurs ont aussi leurs heuristiques d’unrolling, voire des heuristiques couplées inlining/unrolling pour gérer les appels de fonctions dans des corps de boucles !&lt;/p&gt;

&lt;h2 id=&quot;pour-aller-plus-loin&quot;&gt;Pour aller plus loin&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;L’utilité des fonctions en informatique est apparue très tôt ; à ce propos on peut lire &lt;a href=&quot;http://www.laputan.org/pub/papers/wheeler.pdf&quot;&gt;ce papier de 1952&lt;/a&gt; au style désuet mais qui contient déjà toutes les idées importantes sur le sujet.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Il est possible de consulter &lt;a href=&quot;http://llvm.org/doxygen/InlineCost_8h_source.html&quot;&gt;les heuristiques d’inlining de LLVM&lt;/a&gt; ; articulées autour d’une notion de “coût”, elles reposent d’abord sur une approche très empirique symbolisée par ce commentaire au dessus de constantes : “&lt;code class=&quot;highlighter-rouge&quot;&gt;Various magic constants used to adjust heuristics&lt;/code&gt;”.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Wed, 26 Dec 2018 00:00:00 +0100</pubDate>
        <link>https://blog.merigoux.fr/fr/2018/12/26/dupliquer-optimiser.html</link>
        <guid isPermaLink="true">https://blog.merigoux.fr/fr/2018/12/26/dupliquer-optimiser.html</guid>
        
        
      </item>
    
      <item>
        <title>Mathématypes</title>
        <description>&lt;p&gt;En attendant de regarder à l’intérieur des entrailles d’un compilateur optimisant comme promis dans le &lt;a href=&quot;/fr/2017/12/13/flot-controle.html&quot;&gt;précédent billet&lt;/a&gt;, faisons un petit intermède plus théorique mais qui offre une perspective nouvelle sur la programmation en général. En effet, ce blog parle de langages de programmation et de leurs paradigmes, et jusqu’ici nous avons vu une opposition assez nette entre tenants du &lt;a href=&quot;/fr/2017/08/09/objets.html&quot;&gt;tout orienté objet&lt;/a&gt; et partisans de la &lt;a href=&quot;/fr/2017/09/06/prog-fonctionnelle.html&quot;&gt;programmation fonctionnelle&lt;/a&gt;. Le débat entre les deux se place au niveau de l’ergonomie et de la facilité du développeur à utiliser l’un ou l’autre paradigme, mais est-ce l’unique manière de juger et comparer ces paradigmes ?&lt;/p&gt;

&lt;p&gt;Attention, ce billet contient plus de mathématiques que les précédents. Néanmoins, il suffit de comprendre à quoi sert une démonstration pour saisir le sens du propos plus bas.&lt;/p&gt;

&lt;h2 id=&quot;un-peu-darithmétique&quot;&gt;Un peu d’arithmétique&lt;/h2&gt;

&lt;p&gt;Commençons notre voyage par un peu d’arithmétique. Ceux qui ont fait un peu d’algèbre savent que pour toute variable \(\tau \) (prononcez &lt;em&gt;tau&lt;/em&gt;), \(\tau \times(\tau + 1) = \tau \times \tau + \tau\). Nous allons montrer que ce résultat est aussi vrai si \(\tau \) est un type d’un langage fonctionnel.&lt;/p&gt;

&lt;p&gt;Pour cela, il nous faut établir un lien entre les constructions du langage et les symboles mathématiques. Rappelons-nous que dans un langage fonctionnel, il y a deux sortes de types : les types “structure” comme dans un langage orienté objet, et les types “l’un ou l’autre” correspondant à une disjonction de cas. Dans le &lt;a href=&quot;/fr/2017/08/09/objets.html&quot;&gt;billet sur la programmation fonctionnelle&lt;/a&gt;, j’avais dit les types de cette dernière sorte étaient appelés &lt;em&gt;types somme&lt;/em&gt;. Ici, nous allons décider d’associer les type somme avec le symbole mathématique “\(+\)”. Cette décision est techniquement arbitraire, mais nous allons voir que procéder ainsi permet de construire un tout cohérent.&lt;/p&gt;

&lt;p&gt;Symétriquement, associons les types “structure” avec le symbole “\(\times\)” et appelons les les &lt;em&gt;types produit&lt;/em&gt;. Nous avons maintenant établi un lien entre nos symboles mathématiques usuels et les sortes de type. Les opérations mathématiques correspondent donc aux opérations sur les types. De manière naturelle, les nombres vont donc correspondre aux types.&lt;/p&gt;

&lt;p&gt;Passons sous silence le cas du 0 et demandons nous à quoi pourrait correspondre le type appelé “1”. Tout bêtement, c’est le type qui contient exactement un élément. En OCaml, ce type s’appelle &lt;code class=&quot;highlighter-rouge&quot;&gt;unit&lt;/code&gt; et son unique élément est noté &lt;code class=&quot;highlighter-rouge&quot;&gt;()&lt;/code&gt;. C’est un type assez prévisible, puisque si une valeur du programme est de type &lt;code class=&quot;highlighter-rouge&quot;&gt;unit&lt;/code&gt;, cela ne peut être que &lt;code class=&quot;highlighter-rouge&quot;&gt;()&lt;/code&gt;. Le type &lt;code class=&quot;highlighter-rouge&quot;&gt;unit&lt;/code&gt; correspond à peu près au mal-nommé type &lt;code class=&quot;highlighter-rouge&quot;&gt;void&lt;/code&gt; de Java et de C/C++ : en effet une fonction par définition mathématique retourne toujours une valeur, qui peut être de type &lt;code class=&quot;highlighter-rouge&quot;&gt;unit&lt;/code&gt; dans les cas où l’on utilise &lt;code class=&quot;highlighter-rouge&quot;&gt;void&lt;/code&gt; dans d’autres langages.&lt;/p&gt;

&lt;p&gt;Passons maintenant au type noté “2”. C’est le type qui contient exactement deux éléments. Comment écrire en OCaml un type qui ne contient que deux éléments distincts ? Comme cela :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ocaml&quot; data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;two&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;First&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;unit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Second&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;unit&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il n’y a que deux valeurs de type &lt;code class=&quot;highlighter-rouge&quot;&gt;two&lt;/code&gt; : &lt;code class=&quot;highlighter-rouge&quot;&gt;First ()&lt;/code&gt; et &lt;code class=&quot;highlighter-rouge&quot;&gt;Second ()&lt;/code&gt;. Or &lt;code class=&quot;highlighter-rouge&quot;&gt;two&lt;/code&gt; est un type somme ! Ainsi, nous avons définit 2 comme \(1+1\)… Et ainsi de suite pour tous les autres nombres.&lt;/p&gt;

&lt;p&gt;Maintenant, comment montrer que \(\tau \times(\tau + 1) = \tau \times \tau + \tau\) ? D’abord, donnons des noms à chaque membre de l’équation : &lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt; est \(\tau \times(\tau + 1)\), et &lt;code class=&quot;highlighter-rouge&quot;&gt;B&lt;/code&gt; est \(\tau \times \tau + \tau\) . Il nous faut montrer que si on a une valeur de type &lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt;, alors on peut la transformer en une valeur de type &lt;code class=&quot;highlighter-rouge&quot;&gt;B&lt;/code&gt;, et vice-versa. Nous ferons ici le premier sens de la démonstration, le second étant laissé en exercice.&lt;/p&gt;

&lt;p&gt;Prenons une valeur de type &lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt; : elle est de la forme &lt;code class=&quot;highlighter-rouge&quot;&gt;(x,y)&lt;/code&gt; car &lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt; est un type produit. Or le deuxième terme du produit est lui-même un type somme, donc nous avons deux cas : &lt;code class=&quot;highlighter-rouge&quot;&gt;y&lt;/code&gt; peut soit être de type \(\tau\), ou bien de type &lt;code class=&quot;highlighter-rouge&quot;&gt;unit&lt;/code&gt;. Si &lt;code class=&quot;highlighter-rouge&quot;&gt;y&lt;/code&gt; est de type \(\tau\), alors &lt;code class=&quot;highlighter-rouge&quot;&gt;(x,y)&lt;/code&gt; est de type \(\tau\times\tau\), qui est une des deux possibilités du type somme &lt;code class=&quot;highlighter-rouge&quot;&gt;B&lt;/code&gt;. Si &lt;code class=&quot;highlighter-rouge&quot;&gt;y&lt;/code&gt; est de type &lt;code class=&quot;highlighter-rouge&quot;&gt;unit&lt;/code&gt;, alors on utilise juste &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt; qui est de type \(\tau\), l’autre possibilité de &lt;code class=&quot;highlighter-rouge&quot;&gt;B&lt;/code&gt;. Dans tous les cas, on peut reconstruire une valeur de type &lt;code class=&quot;highlighter-rouge&quot;&gt;B&lt;/code&gt; à partir d’une valeur de type &lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ainsi les types d’un langage fonctionnel peuvent se manipuler dans une certaine mesure comme des nombres avec des opérations usuelles, qui fonctionnent comme on pourrait s’y attendre. Mais à quoi cela sert-il de faire ce parallèle ?&lt;/p&gt;

&lt;h2 id=&quot;dualité-des-données&quot;&gt;Dualité des données&lt;/h2&gt;

&lt;p&gt;Cette approche devient plus intéressante lorsque l’on commence à parler des fonctions. Nous ne détaillerons pas ici la correspondance avec les objets mathématiques des fonctions informatiques, pour éviter de rentrer trop dans la technique. Néanmoins l’esprit reste le même qu’avant. Ce qui est intéressant, c’est d’observer qu’il existe deux manières différentes de représenter un agencement de données.&lt;/p&gt;

&lt;p&gt;Pour cela, reprenons notre exemple de cafetières à poudre ou à dosettes. Dans un langage orienté objet, comme nous l’avons vu, il nous faut définir deux classes contenant chacune deux méthodes. Or avec un langage fonctionnel, on peut sans perte d’informations représenter le même agencement de données par deux fonctions prenant chacune un type somme correspondant aux cafetières. Dans les deux cas, il y a quatre informations importantes : comment faire du café et recharger la cafetière, selon que la cafetière est à dosette ou à poudre. Représentons les dans un tableau :&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt; &lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Cafetière à poudre&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Cafetière à dosette&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Faire du café&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;\(f_1\)&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;\(f_2\)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Recharger&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;\(f_3\)&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;\(f_4\)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Dans une approche orienté objet, le type de la cafetière est en réalité \((f_1\times f_3)+(f_2\times f_4)\) : une cafetière est &lt;em&gt;soit&lt;/em&gt; à dosette, &lt;em&gt;soit&lt;/em&gt; à poudre et dans chaque cas on a les méthodes associées. Dans une approche fonctionnelle, ce type devient \((f_1 \odot f_2)\times(f_3 \odot f_4)\), c’est à dire les deux fonctions “faire du café” et “recharger”, qui contiennent chacune les deux cas pour la cafetière. Ces deux approches correspondent à selon qu’on lit le tableau ci-dessus par les colonnes ou par les lignes.&lt;/p&gt;

&lt;p&gt;Le symbole cabalistique \(\odot\) utilisé est une notation arbitraire qui signifie le fait de « réunir » deux fonctions qui ont le même ensemble d’arrivée mais pas le même ensemble de départ pour en faire une nouvelle à l’ensemble de départ plus grand. On ne rentrera pas dans le détail de la démonstration, mais il est possible de prouver, similairement à ce que l’on a fait au paragraphe précédent, que \((f_1 \odot f_2)\times(f_3 \odot f_4)=(f_1\times f_3)+(f_2\times f_4)\).&lt;/p&gt;

&lt;p&gt;Ce qu’il faur retenir, c’est qu’il existe très souvent deux manières d’agencer des données dans un programme informatique. Ces deux manières correspondent mathématiquement à deux types d’opérations sur les types, qui sont les sommes et les produits de types. Sommes et produits sont deux facettes complémentaires, et il est avantageux de pouvoir voir des données sous l’un ou l’autre des facettes selon la situation.&lt;/p&gt;

&lt;h2 id=&quot;pour-la-diffusion-des-type-somme&quot;&gt;Pour la diffusion des type somme&lt;/h2&gt;

&lt;p&gt;On touche là à ce qui fait la limite intrinsèque de la programmation orientée objet : elle contraint le développeur à ne voir ses données que sous une certaine facette, et toujours la même : celle des types produit. Cette contrainte est en réalité artificielle, même si elle semble au premier abord plus “naturelle” au développeur.&lt;/p&gt;

&lt;p&gt;Prenons un parallèle un peu exagéré : se refuser à considérer autre chose que l’orienté objet revient à se contenter d’utiliser uniquement des nombres positifs en toutes situations. Un comptable peut faire ce choix, mais il est alors obligé d’utiliser des artifices pour soustraire ses dépenses de ces recettes, et est bien embêté lorsque les premières excèdent les dernières. Il comprend bien vite l’utilité des nombres négatifs, qui viennent en complément des nombres positifs.&lt;/p&gt;

&lt;p&gt;De la même manière, les types somme sont le complément naturel des types produit, et n’inclure que l’un ou l’autre dans un langage de programmation est contre-productif. Au lieu d’inventer des mécanismes complexes d’héritage par classe abstraite pour simuler les disjonctions, il est bien plus facile et logique d’adopter un système de type inspiré de principes simples et cohérents qui nous sont inspirés par les mathématiques.&lt;/p&gt;

&lt;p&gt;C’est pour cela que, depuis quelques années, les nouveaux langages de programmation qui apparaissent contiennent tous une implémentation de type somme : Rust, Swift, etc. Ceci est un bel exemple de l’influence des mathématiques qui petit à petit remettent de l’ordre dans des constructions décidées des décennies plus tôt de manière un peu arbitraire, mais qui ont définit un standard &lt;em&gt;de facto&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;pour-aller-plus-loin&quot;&gt;Pour aller plus loin&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Ce billet est très largement inspiré du livre de Robert Harper, &lt;em&gt;Practical Foundations for Programming Languages&lt;/em&gt; et du cours associé numéroté 15-312 qu’il enseigne à la Carnegie Mellon University.&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Wed, 24 Jan 2018 00:00:00 +0100</pubDate>
        <link>https://blog.merigoux.fr/fr/2018/01/24/mathematypes.html</link>
        <guid isPermaLink="true">https://blog.merigoux.fr/fr/2018/01/24/mathematypes.html</guid>
        
        
      </item>
    
      <item>
        <title>Sur le flot du contrôle</title>
        <description>&lt;p&gt;Le &lt;a href=&quot;/fr/2017/11/22/syntaxe.html&quot;&gt;dernier billet sur la syntaxe&lt;/a&gt; a posé les bases nécessaires à l’analyse du code, grâce à une représentation abstraite. Nous allons maintenant utiliser le même langage de programmation jouet défini auparavant pour expliquer ce que sont les optimisations réalisées dans les compilateur. Mais tout d’abord, il est nécessaire de changer encore une fois de représentation abstraite du code.&lt;/p&gt;

&lt;h2 id=&quot;branches-conditionnelles&quot;&gt;Branches conditionnelles&lt;/h2&gt;

&lt;p&gt;Reprenons notre langage du billet sur la syntaxe : dans celui-ci, un programme est une liste d’instructions assignant des valeurs à des variables, avec à la fin un &lt;code class=&quot;highlighter-rouge&quot;&gt;return&lt;/code&gt;. Aujourd’hui, nous allons compliquer le langage en y rajoutant une structure conditionnelle introduite par les mot-clés &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; et &lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt;. Voilà ce que donne le nouvel arbre de syntaxe de notre langage :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ocaml&quot; data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Const&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Plus&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Minus&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cond&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Equal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Less&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stm&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Assignment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cond&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;program&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;program&lt;/span&gt;
&lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;program&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stm&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;list&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La ligne ajoutée la plus importante est-celle ci : &lt;code class=&quot;highlighter-rouge&quot;&gt;If of cond * (stm list) * (stm list)&lt;/code&gt;, qui utilise un nouveau type &lt;code class=&quot;highlighter-rouge&quot;&gt;cond&lt;/code&gt; qui décrit la manière de comparer deux expressions. Pour l’instant nous pouvons tester l’égalité (&lt;code class=&quot;highlighter-rouge&quot;&gt;Equal&lt;/code&gt;) et si une valeur est strictement plus petite qu’une autre (&lt;code class=&quot;highlighter-rouge&quot;&gt;Less&lt;/code&gt;). Prenons tout de suite un exemple de programme concret pour voir à quoi cela correspond :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;x = 3;
y = 2;
if (y - 1 &amp;lt; x) {
    w = 5;
} else {
    z = 10;
    return z;
}
return y;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Avec cet ajout, notre langage se complique énormément, car désormais chaque branche &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; ou &lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt; peut elle-même contenir un programme entier, emboîté comme dans des poupées russes. Ceci soulève de nouvelles questions ; par exemple, le programme suivant (correct selon notre arbre de syntaxe abstrait) peut-il être exécuté ?&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;if (1 + 1 == 2) {
    return 0;
} else {
    x = 2;
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La réponse naturelle est oui, car ici on sait que \(1 + 1 = 2\), donc lors de l’exécution c’est la branche &lt;code class=&quot;highlighter-rouge&quot;&gt;return 0;&lt;/code&gt; qui sera prise et le programme va retourner 0. Mais dans le cas général, il est impossible de savoir quelle branche du &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; sera prise à l’exécution, surtout si par exemple la condition dépend d’entrées de l’utilisateur au moment de l’exécution. Ainsi, à la compilation, il nous faut prendre des hypothèses &lt;em&gt;conservatives&lt;/em&gt; et prévoir tous les cas ; dans ce cas-ci, il nous faut aussi considérer ce qui se passe lorsque la branche &lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt; est prise à l’exécution.&lt;/p&gt;

&lt;p&gt;Or ici si la branche &lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt; est prise, le programme va s’exécuter sans jamais rencontrer d’instruction &lt;code class=&quot;highlighter-rouge&quot;&gt;return&lt;/code&gt;, ce qui est normalement interdit. Il nous faut donc rajouter une nouvelle règle à notre langage qui restreint le nombre de programmes &lt;em&gt;valides&lt;/em&gt; : toutes les exécutions du programme doivent rencontrer une instruction &lt;code class=&quot;highlighter-rouge&quot;&gt;return&lt;/code&gt; à un moment donné. Voici la fonction récursive en OCaml qui permet de faire cette vérification sur l’arbre de syntaxe abstrait, fonction dont la simplicité illustre une fois de plus la puissance de la &lt;a href=&quot;/fr/2017/09/06/prog-fonctionnelle.html&quot;&gt;programmation fonctionnelle&lt;/a&gt; dans ce genre de situations :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ocaml&quot; data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check_return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;program&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stm&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;program&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Assignment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;)::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check_return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rest&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;)::_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;branch_true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;branch_false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;return_in_if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check_return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;branch_true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check_return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;branch_false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;return_in_if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check_return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;flot-du-contrôle&quot;&gt;Flot du contrôle&lt;/h2&gt;

&lt;p&gt;Avec l’apparition de branches conditionnelles, l’exécution de nos programmes se complique et il devient utile de réfléchir plus avant à cette problématique. Pour cela, introduisons la notion de contrôle. Le contrôle est l’instruction ou l’expression qui est en train d’être évaluée par le processeur lors de l’exécution d’un programme. Sans conditionnelles, le contrôle se déplace linéairement dans le code : une fois qu’une instruction est exécutée, on passe à la suivante, jusqu’à atteindre un &lt;code class=&quot;highlighter-rouge&quot;&gt;return&lt;/code&gt; qui met fin à l’exécution. Mais avec les conditionnelles, on ne peut pas “passer à l’instruction suivante” car il y a deux possibilités. À cause de cela, le contrôle peut prendre plusieurs chemins différents en fonction de la valeur vraie ou fausse des conditions des &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt;. Comment représenter tous ces chemins différents ?&lt;/p&gt;

&lt;p&gt;La réponse vient encore une fois des mathématiques, car nous allons utiliser un graphe appelé &lt;em&gt;graphe de flot de contrôle&lt;/em&gt;. Construire le graphe à partir d’un arbre de syntaxe abstrait est simple :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;chaque instruction est un nœud du graphe ;&lt;/li&gt;
  &lt;li&gt;deux instructions non-&lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; qui se suivent dans un programme correspondent à une arrête dans le graphe ;&lt;/li&gt;
  &lt;li&gt;l’instruction &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; possède deux arrête sortantes vers chacune des premières instructions dans les deux branches du &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; ;&lt;/li&gt;
  &lt;li&gt;la dernière instruction d’un &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; a une arrête vers la première instruction après le &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt;;&lt;/li&gt;
  &lt;li&gt;chaque instruction &lt;code class=&quot;highlighter-rouge&quot;&gt;return&lt;/code&gt; a une arrête sortant vers un nœud spécial appelé &lt;code class=&quot;highlighter-rouge&quot;&gt;exit&lt;/code&gt; qui signifie la fin de l’exécution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Voici le graphe du flot de contrôle de notre premier exemple :&lt;/p&gt;

&lt;figure&gt;
	&lt;img src=&quot;/fr/assets/illustrations/control_graph.svg&quot; alt=&quot;image&quot; /&gt;
	&lt;figcaption&gt;
		Exemple de graphe de flot de contrôle avec une conditionnelle
	&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;une-première-optimisation&quot;&gt;Une première optimisation&lt;/h2&gt;

&lt;p&gt;Nous arrivons maintenant au moment où tous nos laborieux efforts de modélisation du code vont commencer à payer. En effet, le graphe de flot de contrôle contient toutes les informations nécessaires afin de comprendre comment les données et les valeurs transitent dans le programme.&lt;/p&gt;

&lt;p&gt;Avant de développer tout cela plus formellement dans de prochains billets, voyons une première application assez puissante. Vous avez peut-être remarqué que l’instruction &lt;code class=&quot;highlighter-rouge&quot;&gt;w = 5;&lt;/code&gt; dans la branche “vrai” du &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; ne sert à rien, car on n’utilise pas la valeur de &lt;code class=&quot;highlighter-rouge&quot;&gt;w&lt;/code&gt; dans la suite. Grâce au graphe de flot de contrôle, il est possible de construire un algorithme qui détecte automatiquement ce genre d’instructions “inutiles”. Décrivons son fonctionnement dans les grandes lignes.&lt;/p&gt;

&lt;p&gt;On part du graphe de flot de contrôle, et plus particulièrement de son nœud &lt;code class=&quot;highlighter-rouge&quot;&gt;exit&lt;/code&gt;. Ensuite, on suit les arrêtes du graphe &lt;em&gt;à l’envers&lt;/em&gt; et l’on maintient un ensemble de variables “utiles” de la manière suivante :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;lorsqu’on rencontre un nœud &lt;code class=&quot;highlighter-rouge&quot;&gt;return &amp;lt;exp&amp;gt;;&lt;/code&gt;, toutes les variables dans &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;exp&amp;gt;&lt;/code&gt; sont utiles ;&lt;/li&gt;
  &lt;li&gt;lorsqu’on rencontre un nœud &lt;code class=&quot;highlighter-rouge&quot;&gt;x = &amp;lt;exp&amp;gt;;&lt;/code&gt;, alors si &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt; est utile, toutes les variables dans &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;exp&amp;gt;&lt;/code&gt; sont utiles ;&lt;/li&gt;
  &lt;li&gt;toutes les variables d’un nœud &lt;code class=&quot;highlighter-rouge&quot;&gt;if(&amp;lt;exp&amp;gt;)&lt;/code&gt; sont utiles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Après avoir remonté tous les nœuds jusqu’au début du graphe, on regarde quelles variables ne sont pas dans l’ensemble “utile”. On peut alors supprimer les instructions qui définissent ces variables “inutiles”, et ainsi optimiser le programme qui tournera plus vite car comportant moins d’instructions pour le même effet.&lt;/p&gt;

&lt;p&gt;Parce que la plupart des compilateurs implémentent ce genre d’optimisation, rajouter du code “inutile” dans un programme n’a généralement pas d’effet sur la performance. Nous verrons par la suite quel genre d’inneficacités dans un programme conduit vraiment à une chute de performance, et quelles autres sont corrigées automatiquement par les compilateurs.&lt;/p&gt;

&lt;h2 id=&quot;pour-aller-plus-loin&quot;&gt;Pour aller plus loin&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Un &lt;a href=&quot;http://www.drgarbage.com/sourcecode-visualizer/&quot;&gt;plugin Eclipse&lt;/a&gt; permet de visualiser le graphe de flot de contrôle de son programme Java directement dans l’IDE.&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Wed, 13 Dec 2017 00:00:00 +0100</pubDate>
        <link>https://blog.merigoux.fr/fr/2017/12/13/flot-controle.html</link>
        <guid isPermaLink="true">https://blog.merigoux.fr/fr/2017/12/13/flot-controle.html</guid>
        
        
      </item>
    
      <item>
        <title>La syntaxe pousse sur les arbres</title>
        <description>&lt;p&gt;Il y a plusieurs façons de considérer le code informatique. La plus évidente est de le voir comme du texte, forme écrite d’un langage de programmation que la machine comprend et peut exécuter. Mais si l’on veut raisonner sur le code, comme nous l’avons fait avec l’&lt;a href=&quot;/fr/2017/10/25/allocation-registre.html&quot;&gt;analyse de durée de vie&lt;/a&gt;, il nous faut une représentation du code plus abstraite qui nous permette de raisonner sur les variables et les instructions. Plus généralement, nous allons découvrir comment est-ce que l’on définit un langage de programmation.&lt;/p&gt;

&lt;h2 id=&quot;un-nouveau-langage&quot;&gt;Un nouveau langage&lt;/h2&gt;

&lt;p&gt;Pour la première fois, nous allons définir notre propre langage de programmation. Celui-ci sera très simple, mais nous permettra de mieux comprendre comment un simple fichier contenant du texte peut être interprété comme un programme informatique. Dans notre langage qui opérera uniquement sur des entiers, on pourra :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;additionner et soustraire des expressions ;&lt;/li&gt;
  &lt;li&gt;assigner une expression à une variable ;&lt;/li&gt;
  &lt;li&gt;retourner un expression à la fin du programme.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maintenant que nous avons définit ce que notre langage peut faire, il nous faut décider d’une manière d’écrire ce langage dans un fichier test. Plus formellement, ceci va définir la &lt;em&gt;syntaxe&lt;/em&gt; de notre langage, et nous avons un certain degré de liberté dans nos choix. Par exemple, on pourrait choisir de noter “la valeur &lt;code class=&quot;highlighter-rouge&quot;&gt;3&lt;/code&gt; plus &lt;code class=&quot;highlighter-rouge&quot;&gt;y&lt;/code&gt; est assignée à &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt;” par &lt;code class=&quot;highlighter-rouge&quot;&gt;x y 3 + :=&lt;/code&gt; (en &lt;a href=&quot;https://fr.wikipedia.org/wiki/Notation_polonaise_inverse&quot;&gt;notation polonaise inverse&lt;/a&gt; pour les connaisseurs). Mais ce qui doit guider notre choix est avant tout la lisibilité du code pour le programmeur, qui nous pousse à définir des opérateurs infixes (comme ceux que l’on manipule habituellement en mathématiques). Voilà donc un exemple de programme écrit dans notre nouveau langage avec sa syntaxe:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;x = 3;
y = 4;
z = x + y;
x = y - z;
return x;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Mais même une fois que l’on a choisi notre syntaxe, il nous faut un moyen de la décrire proprement. En effet, même si on pourrait s’en tirer avec une description en français avec notre exemple simple, je présente la méthode systématique et explicite qui peut être utilisée pour décrirer la plupart des syntaxes des langages de programmation.&lt;/p&gt;

&lt;h2 id=&quot;syntaxe-dun-langage&quot;&gt;Syntaxe d’un langage&lt;/h2&gt;

&lt;p&gt;Ironiquement, c’est en utilisant un autre langage de programmation que l’on va décrire la syntaxe de notre nouveau langage de programmation ! Ce &lt;em&gt;méta&lt;/em&gt;-langage est appelé &lt;a href=&quot;https://fr.wikipedia.org/wiki/Forme_de_Backus-Naur&quot;&gt;forme de Backus-Naur&lt;/a&gt; (abbrégé BNF), et voilà la description de notre syntaxe en BNF:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;exp ::= var | const | exp &quot;+&quot; exp | exp &quot;-&quot; exp | &quot;(&quot; exp &quot;)&quot;
stm ::= var &quot;=&quot; exp &quot;;&quot; | &quot;return&quot; exp &quot;;&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Après ce galimatias de symboles, il est temps de faire une pause et d’expliquer ce qui se passe. Les deux lignes du dessus définissent deux concepts de notre langages : les expressions (&lt;code class=&quot;highlighter-rouge&quot;&gt;exp&lt;/code&gt;) et les déclarations (&lt;code class=&quot;highlighter-rouge&quot;&gt;stm&lt;/code&gt; de l’anglais &lt;em&gt;statement&lt;/em&gt;). Commençons par analyser la définition d’une expression : les barres verticales &lt;code class=&quot;highlighter-rouge&quot;&gt;|&lt;/code&gt; indiquent, comme dans un type somme de langage fonctionnel, le fait qu’une expression peut être de plusieurs formes, les formes étant ce qu’il y a entre les &lt;code class=&quot;highlighter-rouge&quot;&gt;|&lt;/code&gt;. Ici, &lt;code class=&quot;highlighter-rouge&quot;&gt;var&lt;/code&gt; et &lt;code class=&quot;highlighter-rouge&quot;&gt;const&lt;/code&gt; signifient qu’une expression peut être une variable (comme &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt;) ou une constante, c’est-à-dire ici un nombre entier (comme &lt;code class=&quot;highlighter-rouge&quot;&gt;3&lt;/code&gt;). &lt;code class=&quot;highlighter-rouge&quot;&gt;exp &quot;+&quot; exp&lt;/code&gt; est plus intéressant. Il indique qu’une expression peut être formée par deux expressions séparée par le signe &lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt;. Cette définition est récursive, car on définit les expressions en fonction d’autres expressions. Cette récursivité est nécessaire pour donner un sens à des expressions complexes comme &lt;code class=&quot;highlighter-rouge&quot;&gt;2 + 3 * y - 8 * x&lt;/code&gt;. On voit sur ce dernier exemple que cette définition récursive n’est pas suffisante : il nous faut aussi définir “comment rajouter les parenthèses manquantes” pour donner un sens unique à cette expression, qui peut ainsi devenir &lt;code class=&quot;highlighter-rouge&quot;&gt;(2 + 3) * y - 8 * x&lt;/code&gt; ou encore &lt;code class=&quot;highlighter-rouge&quot;&gt;2 + (3 * y - 8) * x&lt;/code&gt; avec des résultats complètement différents. Je laisserai ce sujet technique de côté, pour nous concentrer sur les concepts les plus importants. &lt;code class=&quot;highlighter-rouge&quot;&gt;exp &quot;-&quot; exp&lt;/code&gt; est similaire à l’addition, et &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;(&quot; exp &quot;)&quot;&lt;/code&gt; indique que l’on peut mettre une expression entre parenthèses.&lt;/p&gt;

&lt;p&gt;Les déclarations quant à elles sont plus simples, elles correspondent aux deux types d’instructions de notre langage : l’assignation d’expressions à des variables et la valeur de retour de notre programme. Il est nécessaire de faire la différence entre expressions et déclarations afin de ne pas autoriser des programmes comme &lt;code class=&quot;highlighter-rouge&quot;&gt;1 + x = return (1 - y)&lt;/code&gt;, où expressions et déclarations sont mélangées. Car la syntaxe, en plus de décrire comment le langage s’écrit, agit comme un ensemble de règles qui nous dit si un programme est “valide” ou non. C’est pour cette raison que les compilateurs ou interpréteurs renvoient le fameux message &lt;code class=&quot;highlighter-rouge&quot;&gt;Syntax error&lt;/code&gt; lorsque le texte du programme ne correspond pas à la définition de la syntaxe du langage.&lt;/p&gt;

&lt;h2 id=&quot;arbres-de-syntaxe&quot;&gt;Arbres de syntaxe&lt;/h2&gt;

&lt;p&gt;Maintenant que notre syntaxe est précisément définie, nous pouvons donner de la structure au code informatique. En effet, puisque le texte de nos programmes suit les règles de la syntaxe, il est possible d’utiliser cette information pour représenter le code dans une structure de données informatique. À cause de la nature récursive de la définition de la syntaxe, la structure de données que nous allons utiliser est un arbre, appelé &lt;em&gt;arbre de syntaxe abstrait&lt;/em&gt;. Quel est l’arbre de syntaxe abstrait correspondant à notre exemple ?&lt;/p&gt;

&lt;p&gt;Comme nous nous sommes servis du méta-langage de la BNF pour décrire notre syntaxe, il nous faut choisir un méta-langage pour décrire notre arbre de syntaxe abstrait. Nous pourrions le décrire en termes purement mathématiques, mais il se trouve que les &lt;a href=&quot;/fr/2017/09/06/prog-fonctionnelle.html&quot;&gt;langages de programmation fonctionnels&lt;/a&gt; sont très bien adaptés pour décrire ce genre de structures de données récursives. En revanche, décrire ce genre de structure de données dans un langage de programmation orienté objet est très pénible et nécessite de créer autant de classes que de “variantes” d’expressions ou de déclarations. Voilà donc comment définir notre arbre de syntaxe abstrait en OCaml :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ocaml&quot; data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Const&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Plus&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Minus&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parenthesis&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stm&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Assignment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;program&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stm&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;list&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ici nous avons choisi de représenter les noms de variables par des chaînes de caractères (&lt;code class=&quot;highlighter-rouge&quot;&gt;string&lt;/code&gt;), ce qui colle au plus près de la représentation textuelle. Un programme &lt;code class=&quot;highlighter-rouge&quot;&gt;program&lt;/code&gt; est représenté par une liste de déclarations &lt;code class=&quot;highlighter-rouge&quot;&gt;stm list&lt;/code&gt;, ce qui correspond également à la représentation textuelle. Les points-virgules &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;;&quot;&lt;/code&gt; ont disparu car ils servaient uniquement à délimiter les déclaration, ils n’influent pas sur les calculs effectués par le programme.&lt;/p&gt;

&lt;p&gt;Remarquons que l’on pourrait aussi retirer la ligne &lt;code class=&quot;highlighter-rouge&quot;&gt;| Parenthesis of exp&lt;/code&gt; de notre arbre de syntaxe abstrait. En effet, les parenthèses servaient uniquement à préciser l’ordre dans lequel effectuer les opérations. Mais l’information concernant l’ordre des opérations est déjà encodée naturellement dans notre arbre ! En effet, &lt;code class=&quot;highlighter-rouge&quot;&gt;4 + 5&lt;/code&gt; va devenir &lt;code class=&quot;highlighter-rouge&quot;&gt;Plus (4,5)&lt;/code&gt;, et &lt;code class=&quot;highlighter-rouge&quot;&gt;4 + (5 - x)&lt;/code&gt; va devenir &lt;code class=&quot;highlighter-rouge&quot;&gt;Plus (4, Minus (5,x))&lt;/code&gt; (en omettant &lt;code class=&quot;highlighter-rouge&quot;&gt;Const&lt;/code&gt; et &lt;code class=&quot;highlighter-rouge&quot;&gt;Var&lt;/code&gt; pour plus de lisibilité). &lt;code class=&quot;highlighter-rouge&quot;&gt;5 - x&lt;/code&gt; est l’argument de droite du premier &lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt;, et le sens des parenthèses est respecté.&lt;/p&gt;

&lt;p&gt;Grâce à cet arbre de syntaxe abstrait, nous disposons d’une représentation logique des programmes de notre nouveau langage de programmation. Cette représentation est classiquement l’aboutissement de la première phase d’un compilateur, et le premier pas vers la traduction de notre nouveau langage vers un assembleur dans lequel nous pourront exécuter nos programmes. Mais transformer le texte d’un programme en arbre de syntaxe abstrait permet aussi d’analyser le programme !&lt;/p&gt;

&lt;p&gt;C’est sur cet arbre de syntaxe abstrait que s’effectue la vérification du typage (si le langage est typé), mais également l’analyse des possibilités d’autocomplétion par les Environnements de Développement Intégrés (IDE en anglais), fonctionnalité très utile au développeur.&lt;/p&gt;

&lt;h2 id=&quot;analyse-syntaxique&quot;&gt;Analyse syntaxique&lt;/h2&gt;

&lt;p&gt;J’ai délibérément passé sous silence un point pourtant très important : étant données les règles de la syntaxe que doit suivre le programme sous forme texte, quels sont les algorithmes qui permettent de transformer le texte en un arbre de syntaxe abstrait (opération de parsing) ? Il se trouve que ces algorithmes sont d’une technicité importante, même s’ils se fondent (encore une fois) sur de fascinantes théories mathématiques. En voici un résumé très grossier et incomplet.&lt;/p&gt;

&lt;p&gt;Premièrement, on utilise des &lt;a href=&quot;http://www.regular-expressions.info/&quot;&gt;expressions régulières&lt;/a&gt; pour transformer le texte en une série de &lt;em&gt;tokens&lt;/em&gt;, un token correspondant peu ou prou à un mot-clé ou caractère de contrôle. Ceci a également pour effet de supprimer tous les espaces et sauts de lignes qui ne sont pas sémantiquement pertinents (sauf en Python…). Cette phase est appelée &lt;em&gt;lexing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;La deuxième phase s’appelle le &lt;em&gt;parsing&lt;/em&gt; : elle consiste à transformer une série de &lt;em&gt;tokens&lt;/em&gt; en un arbre de syntaxe abstrait, suivant un ensemble de règles de grammaire. Je ne détaillerais pas plus cette étape car elle est très technique et repose sur tout une sous-discipline à la frontière entre mathématiques et informatique. Ce que l’on peut néanmoins retenir, c’est qu’il existe des algorithmes permettant d’effectuer le &lt;em&gt;parsing&lt;/em&gt; en temps linéaire par rapport à la longueur du texte en entrée, moyennant quelques limitations sur la manière dont on définit sa grammaire.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;L’arbre de syntaxe abstrait et la première représentation abstraite d’un langage de programmation, et c’est également celle qui est la plus proche du texte. Néanmoins, elle permet déjà de considérer un programme comme une donnée que l’on peut traiter dans d’autres programmes, comme des compilateurs ou des analyseurs.&lt;/p&gt;

&lt;p&gt;Par la suite, nous allons nous appuyer sur ces représentations abstraites des programmes afin d’expliquer ce qui se passe lorsque l’on ordonne à nos compilateurs d’“optimiser” le code avec les options &lt;code class=&quot;highlighter-rouge&quot;&gt;-O1&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;-02&lt;/code&gt;, etc. Bientôt, vous saurez pourquoi votre programme tourne 10 fois plus vite lorsqu’il est compilé avec l’option &lt;code class=&quot;highlighter-rouge&quot;&gt;-O3&lt;/code&gt; !&lt;/p&gt;

&lt;h2 id=&quot;pour-aller-plus-loin&quot;&gt;Pour aller plus loin&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Nous avons décrit notre petit langage jouet en utilisant la BNF, mais de “vrais” langages sont également définis en BNF, par exemple &lt;a href=&quot;http://www.csci.csusb.edu/dick/samples/ml.syntax.html&quot;&gt;ML&lt;/a&gt; (dont OCaml est une variante) ou &lt;a href=&quot;https://ronsavage.github.io/SQL/sql-2003-2.bnf.html&quot;&gt;SQL&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;La phase de lexing utilise des &lt;a href=&quot;http://www.regular-expressions.info/&quot;&gt;expressions régulières&lt;/a&gt; : maîtriser les expressions régulières est un talent très utile dès lors que l’on écrit du texte. En effet, la plupart des éditeurs de texte proposent un rechercher et remplacer à l’aide d’expressions régulières qui offre une grande puissance de manipulation de texte.&lt;/li&gt;
  &lt;li&gt;Pour un éclairage alternatif (et avec plus de dessins d’arbres !) sur ce que j’ai raconté dans ce billet, on pourra consulter le &lt;a href=&quot;https://en.wikibooks.org/wiki/Introduction_to_Programming_Languages/Grammars&quot;&gt;livre Wikipedia&lt;/a&gt; traitant des mêmes sujets.&lt;/li&gt;
  &lt;li&gt;Pour ceux qui veulent plus de détails sur la théorie des grammaires et leur parsing, &lt;a href=&quot;http://www.cs.may.ie/~jpower/Courses/parsing/parsing.pdf&quot;&gt;ce cours d’une université irlandaise&lt;/a&gt; satisfera les curiosités les plus avides en une quarantaine de pages.&lt;/li&gt;
  &lt;li&gt;Cette démarche de décrire une grammaire et de construire un parseur pour produire des AST peut être utile au delà des développeurs de compilateurs. En effet, lorsque l’on a besoin de décrire des configurations complexes dans son domaine de travail (réseaux de neurones, réglages d’instruments, &lt;a href=&quot;https://en.wikipedia.org/wiki/SQL&quot;&gt;requêtes dans des bases de données&lt;/a&gt;, etc.) il est possible de définir un langage dédié (Domain Specific Language). Ainsi, on écrit plus lisiblement son problème dans ce nouveau langage qui est ensuite transformé en arbre de syntaxe abstrait que l’on peut analyser ou compiler.&lt;/li&gt;
  &lt;li&gt;Il existe pour la plupart des langages de programmation des librairies de parsing permettant de spécifier sa grammaire puis construire la machine à état. On peut citer &lt;a href=&quot;https://www.gnu.org/software/bison/&quot;&gt;Bison&lt;/a&gt; et &lt;a href=&quot;https://github.com/westes/flex&quot;&gt;Flex&lt;/a&gt; qui est le couple (lexing/parsing) standard. Mais ces libraires possèdent un défaut notoire, qui est que les messages d’erreurs indiquant des inconsistances dans la déclaration de la grammaire de l’utilisateur sont incompréhensibles. Aussi la libraire &lt;a href=&quot;http://gallium.inria.fr/~fpottier/menhir/&quot;&gt;Menhir&lt;/a&gt; pour OCaml a-t-elle été développée précisément pour guider la création de la grammaire à l’aide de messages d’erreurs lisibles par un être humain.&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Wed, 22 Nov 2017 00:00:00 +0100</pubDate>
        <link>https://blog.merigoux.fr/fr/2017/11/22/syntaxe.html</link>
        <guid isPermaLink="true">https://blog.merigoux.fr/fr/2017/11/22/syntaxe.html</guid>
        
        
      </item>
    
  </channel>
</rss>
