• Aucun résultat trouvé

Système de types avec effets de GhostML

2.2 Typage des expressions GhostML

2.2.1 Système de types avec effets de GhostML

Le système de types de GhostML est défini inductivement par l’ensemble des règles qui sont données dans la figure 2.4. Chaque règle se termine par un jugement de la forme

· „ e : · ·—· ‘

· est le type de l’expressione et les valeurs booléennes etindiquent, respective-ment, son statut fantôme et ses effets de bord. La fonction définie sur l’ensemble des variables (dont on a convenu qu’il correspondait à l’union disjointe des variables fan-tômes et des variables réelles) représente l’environnement de typage des variables. La fonction définie sur l’ensemble des références globales (dont on a convenu qu’il était fini et fixé à l’avance) représente l’environnement de typage des références globales associant à chaque référence r un type scalaire (r).

Pour garantir la non-interférence, chaque règle comporte en plus la condition im-plicite suivante :

=∆ ¬‘ · ¬‘+(·) (2.1)

où l’on définit +(·) récursivement sur la structure du type · par les équations :

+(‹) , ‹

+(·2 =∆ ·1) , ‘ ‚ ‘+(·1) 3. “Well-typed programs cannot go wrong” (Milner, 1978)

L’idée est que la condition (2.1) serve d’invariant du typage : lorsque l’expressioneest fantôme, son exécution ne peut ni diverger, ni modifier le contenu des références réelles. Notons qu’un invariant plus faible =∆ ¬‘ serait suffisant mais l’avantage de la condition (2.1), où l’on demande en plus que¬‘+(·) lorsque eest fantôme, est qu’elle permet de rejeter les définitions de fonctions dont les effets latents ne respecteraient pas la non-interférence.

Expliquons les règles du typage en détails. La règle (T-Const) exprime le fait qu’une constanteccorrespond toujours à un code réel, pur et terminant. En particulier, sicest une opération, les effets latents doivent être tous vides (+(·) = ‹), et les types des arguments formels doivent être tous réels, ce que l’on note avec+(·) = ‹+(·) est définie à l’instar de +(·) par les équations :

+(‹) , ‹

+(·2 =∆ ·1) , —‚ —+(·1)

Le type des constantes est donné par un oracle Typeof(c). Ainsi, on a par exemple Typeof(+) = Int =∆‹ Int =∆‹ Int. Par ailleurs, on impose que les oracles Typeof et soient toujours cohérents entre eux :

Définition 2.2.1 (Cohérence de Typeof et). Soit c0 une constante telle que Typeof(c0) = ‹

1 =∆‹ 2 =∆ . . . =∆ m=∆ 0,

Alors l’arité du symbolec0 est égale àm et quelles que soient les constantesc1, . . . , cm avec Typeof(ci) = ‹i pour 1 Æ i Æ m, la constante ”(c0, c1, . . . , cm) est bien définie et et on a Typeof(”(c0, c1, . . . , cm)) = ‹0.

La règle (T-Ghost) permet de typer une expression e dans un contexte fan-tôme ghost e, pourvu que e soit pure et terminante. Dans les règles (T-Deref) et

(T-Assign), le type de la valeur stockée dans la référence r est donné par l’environ-nement . Rappelons que dans notre formalisme une référence globale ne peut contenir que des constantes scalaires. Par conséquent, la récursivité (et donc potentiellement la divergence) ne peut pas être encodée dans notre langage à l’aide d’un « nœud de Landin » (c’est-à-dire, en encodant la récursivité par une référence contenant une fonc-tion). Par ailleurs, la règle (T-Assign) autorise de stocker une valeur réelle dans une référence fantôme mais l’inverse n’est pas possible : la prémisse Õ Æ interdit de modifier le contenu d’une référence réelle par une valeur fantôme. De plus, dans la conclusion de la règle, les effets d’écriture sont présents ( = €) si et seulement si la référence modifiée est réelle : l’écriture dans les références fantômes ne fait donc pas partie des effets dans notre système du typage.

La règle (T-⁄) décrit le typage des fonctions anonymes. D’une part, le statut fan-tôme d’une fonction anonyme est celui de son corps. D’autre part, les effets calculés pour le corps deviennent latents et sont placés sur la flèche du type fonctionnel, alors que la fonction elle-même n’a pas d’effet. Le typage des fonctions récursives, donné

Typeof(c) = · ¬‘+(·) · ¬—+(·)

· „ c : · ·‹· ‹ (T-Const) (x) = ·

· „ x : · ·—· ‹(T-Var)

· „ e1 : · ·—· ‹

· „ (ghost e1) : · ·€· ‹(T-Ghost) (r) = ‹

· „ !r : ‹ ·—· ‹(T-Deref) (r) = ‹ · „ a : ‹ ·Õ · ‹ Õ Æ · „ (r := a) : Unit ·—·¬— (T-Assign) [x ‘æ ·] · „ e1 : ·1·1 · ‘ · „ (⁄x : ·. e1) : · =∆ ·1·1· ‹(T-⁄) · „ e1 : ·2 0 =∆·1·1· ‘1 · „ a2 : ·2·2· ‹ · „ (e1 a2) : ·1·1‚ —2· ‘0‚ ‘1 (T-App) · „ e1 : ·2 =∆0 ·1·1· ‘1 · „ a2 : ·2·2· ‹ · „ (e1 a2) : ·1·1· ‘0‚ ‘1 (T-App) · „ e1 : ·1·1· ‘1 [x ‘æ ·1] · „ e2 : ·2·2· ‘2

· „ (let x = e1 in e2) : ·2·1‚ —2· ‘1 ‚ ‘2 (T-Let)

· „ e1 : ·1·1· ‹ [x ‘æ ·1] · „ e2 : ·2·2· ‘2

· „ (let x= e1 in e2) : ·2·2· ‘2 (T-Let)

0 = CheckTermination(rec f1 : ·0. ⁄x : ·2. e1) 0 Æ ‘1 [f1 ‘æ ·0] · „ (⁄x : ·2. e1) : ·0·1· ‹ ·0 = (·

2 =∆1 ·1)

· „ (rec f1 : ·0. ⁄x : ·2. e1) : ·0·1· ‹ (T-Rec)

· „ a : Bool ·—0· ‹ · „ e1 : · ·—1· ‘1 · „ e2 : · ·—2 · ‘2

· „ (if a then e1 else e2) : · ·—0‚ —1‚ —2· ‘1‚ ‘2 (T-If)

par la règle (T-Rec), est plus complexe. En effet, on doit vérifier que le code fan-tôme termine toujours. Or, en présence de fonctions récursives, la terminaison est en général un problème undécidable. La solution la plus simple serait alors d’interdire l’utilisation des fonctions récursives dans le code fantôme, ce qui reviendrait à assi-gner à toute définition récursive un effet latent de non-terminaison. Cependant, en pratique, les outils de vérification existants disposent souvent de techniques d’analyse statique permettant d’établir la terminaison des fonctions récursives, au quel cas on peut les utiliser dans un contexte fantôme. Pour tenir compte de cette possibilité de terminaison prouvable, sans pour autant donner une préférence à une approche parti-culière, notre formalisme modélise suppose l’existence d’un oracleCheckTermination qui, étant donnée une définition récursive, indique lorsque sa terminaison peut être statiquement établie (c’est-à-dire que l’on peut montrer que tout appel à une telle fonction récursive termine quel que soit l’état mémoire courant et l’argument auquel la fonction est appliquée), et sinon. Un exemple typique d’un tel oracle serait la génération des obligations de preuve à partir des variants fournis par l’utilisateur. No-tons que CheckTermination ne prend pas en compte la non-interférence et donc n’a pas besoin de distinguer lui-même le code fantôme et le code réel. Enfin, pour tenir compte de la situation où, d’après l’oracle, une expression termine mais possède des effets, car modifie des références réelles, la règle(T-Rec) impose que les effets latents des fonctions récursives soient au moins aussi grands que les effets donnés par l’oracle (0 Æ ‘1). Notons, en revanche, que le statut fantôme 1 d’une fonction récursive dans notre formalisme doit être le même que celui de la fonction anonyme qui la définit.

Le typage des expressions conditionnelles représente un cas de figure où le code fantôme peut se propager depuis l’une des sous-expressions de sorte que l’expression toute entière est typée comme fantôme. En effet, comme le montre la règle (T-If), il suffit qu’une seule des branches soit fantôme pour que la conditionnelle le devienne elle-même. Par conséquent, le typage rejette les conditionnelles dont l’une des deux branches est fantôme tandis que l’autre produit des effets, car cela ferait sinon défaut à la condition implicite (2.1). Ainsi, l’expression if true then r:= 42 else ghost () n’est pas bien typée : le statut fantôme de l’expression doit être, puisque sa branche droite est ghost () mais, d’après la condition (2.1), les effets de la conditionnelle doivent alors être , ce qui est faux ici, puisque la branche gauche r := 42a un effet €.

La règle (T-Let) permet de typer les liaisons locales dont le lieur est fantôme. Notons que, étant donnée une telle expression let x = e1 in e2, on peut lier x

à e1, même si e1 n’est pas lui-même une expression fantôme. Il faut néanmoins dans ce cas-là que e1 soit pure et terminante. Plus subtil encore, lorsque e1 est fantôme, une expression let ne devient pas nécessairement fantôme : plutôt que de propager le contexte fantôme à l’intérieur de e2, le système de types propage l’information que la valeur associée à x est fantôme, garantissant ainsi que l’utilisation de chaque occurrence de x dans e2 ne va pas interférer avec les parties réelles.

forme ·2€=∆0 ·1. Comme dans le cas de(T-Let), l’expression e1 peut être appliquée aussi bien à un argument réel qu’à un argument fantôme et le statut fantôme de l’application ne dépend pas du statut fantôme de l’argument a2 (la situation ici est même plus simple, puisque, en forme A-normale, l’expression a2 est atomique donc nécessairement pure et terminant). Considérons le programme suivant :

let succ = ⁄y : Int. (y+ 1) in let x =!r in

let trace = succ x in

r := x+ 1

Grâce à la règle(T-App), on passe une variable réellex en argument de la fonction fantômesucc. Ensuite, grâce à la règle(T-Let), le contexte fantôme correspondant à l’application fantôme(succx)est « contenu » dans la variabletrace sans interférer avec le code réel qui suit après.

La règle (T-Let) est, d’une certaine manière, le dual de la règle (T-Let) : lorsque l’on lie une variable réelle x à une expression fantôme e1, le système de types propage le contexte fantôme jusqu’à ce que l’expressionlet x= e1 in e2 toute entière devienne fantôme, « contaminée » par le statut fantôme dee1. De même, la règle

(T-App) permet de passer un argument fantôme à une fonction dont le paramètre formel est déclaré comme réel, auquel cas l’application toute entière devient du code fantôme.

Reprenons l’exemple précédent. Le fait que l’on ait déclaré la fonction succ fan-tôme est un peu regrettable, car cela empêche de l’appeler dans le code réel. Par exemple, on ne peut pas remplacer la dernière ligne r := x + 1 simplement par

r := succ x. Or, étant donné que la fonction successeur est pure et terminant, il serait naturel de la définir en tant que fonction réelle. C’est là que les règles(T-Let)

et (T-App) prennent leur intérêt car elles permettent d’utiliser la même définition à la fois dans le code réel et le code fantôme :

let succ = ⁄y : Int. (y+ 1) in let x =!r in

let trace = succ x in

r := succ x

En résumé, les règles (T-Let) et (T-App) permettent de réguler la propagation du code fantôme dans un contexte réel, alors que les règles (T-Let) et (T-App)

permettent d’utiliser le code réel dans un contexte fantôme.

Notons qu’il n’y a pas de sous-typage dans le système de types que nous venons de présenter : dans les règles (T-App) et (T-App), le paramètre formel et l’argument effectif doivent avoir exactement le même type. En particulier, lorsque le paramètre formel et l’argument sont eux-mêmes des fonctions, les effets latents ainsi que les statuts fantômes des types fonctionnels correspondants doivent être les mêmes. Ainsi, une fonction d’ordre supérieur dont le paramètre formel a le type Int =∆ Int ne peut pas être appliquée dans notre système à un argument factuel dont le type serait

Int€=∆ Int.

Pour conclure, remarquons que, lorsque l’on parle des expressions typées, la mise en forme A-normale des expressions doit tenir compte du typage et donc des sta-tuts fantômes des expressions telles que (e1 e2) et (if e0 then e1 else e2). Bien que nous n’avons pas présenté les règles du typage correspondantes (les expressions (e1 e2) et (if e0 then e1 else e2) ne faisant pas partie de la syntaxe), on peut montrer néanmoins qu’elles seraient admissibles dans le système du typage que nous venons de présenter. Par conséquent, la mise en forme A-normale des expressions bien ty-pées ne diminue pas l’expressivité de la partie typée du langage. Par exemple, la forme A-normale de la conditionnelle if e0 then e1 else e2 correspond à l’expres-sion let x0 = e0 in if x then e1 else e20 est le statut fantôme de e0. De même, la mise en forme A-normale de e1 e2 correspond à l’expression GhostML let y2 = e2 in let x1 = e1 in x1 y21 est le statut fantôme du premier para-mètre formel du type (nécessairement fonctionnel) dee1 et2 est le statut fantôme de

e2. Un tel procédé montre que la mise en forme A-normale ne diminue pas l’expressi-vité de la partie du langage typée. En particulier, si l’on ajoutait les expressions e1 e2

etif e0 then e1 else e2 à la grammaire, les règles de typage correspondantes seraient admissibles dans le système du typage que nous venons de présenter.