Mantis Bug Tracker

View Issue Details Jump to Notes ] Issue History ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0001248OCamlOCaml generalpublic2002-07-18 20:232013-08-31 12:46
Reporteradministrator 
Assigned To 
PrioritynormalSeverityfeatureReproducibilityalways
StatusclosedResolutionsuspended 
PlatformOSOS Version
Product Version 
Target VersionFixed in Version 
Summary0001248: Generation d'exceptions
DescriptionSuite à un thread sur comp.lang.ml (cf <ah6vmf$12ik$1@nef.ens.fr>) ...

La session suivante illustre quelques problèmes avec la génération
d'exception lors de l'évaluation de structures:

# module F(X:sig type t end) = struct exception E of X.t end;;
module F : functor (X : sig type t end) -> sig exception E of X.t end
# module B = F(struct type t = bool end);;
module B : sig exception E of bool end
# B.E true;;
- : exn = F(X).E 1

  Le toplevel croit faussement que l'argument de B.E est de type int ....
Le X dans l'affichage est surprenant.

De même:

# module X = struct type t = A | B end;;
module X : sig type t = A | B end
# module FX = F(X);;
module FX : sig exception E of X.t end
# FX.E X.A;;
- : exn = F(X).E 0

  Enfin, ça ne serait qu'un problème d'affichage. Mais ce qui
suit est plus troublant:

# FX.E X.B = B.E true;;
- : bool = true

Donc OCaml compare des valeurs de type différent, et répond même "true" !
On s'attend à ce que les exceptions aient un comportement
génératif; mais non:

# let f () = let module E = struct exception E end in E.E;;
val f : unit -> exn = <fun>
# f () = f ();;
- : bool = true

Je dirais que cette réponse est incorrecte (en tout cas, incohérente
avec le filtrage dans un gestionnaire d'exception, qui distinguerait
bien ces deux exceptions).


Tout ça avec OCaml 3.04 et 3.04+15 (2002-06-18).


-- Alain

TagsNo tags attached.
Attached Files

- Relationships
has duplicate 0004531closed Redefinition of exceptions, matching and equality are odd 

-  Notes
(0000133)
administrator (administrator)
2002-07-19 10:27

Bonjour Alain,

> Suite à un thread sur comp.lang.ml (cf <ah6vmf$12ik$1@nef.ens.fr>) ...
>
> La session suivante illustre quelques problèmes avec la génération
> d'exception lors de l'évaluation de structures:
>
> # module F(X:sig type t end) = struct exception E of X.t end;;
> module F : functor (X : sig type t end) -> sig exception E of X.t end
> # module B = F(struct type t = bool end);;
> module B : sig exception E of bool end
> # B.E true;;
> - : exn = F(X).E 1
>
> Le toplevel croit faussement que l'argument de B.E est de type int ....
> Le X dans l'affichage est surprenant.
>
> De même:
>
> # module X = struct type t = A | B end;;
> module X : sig type t = A | B end
> # module FX = F(X);;
> module FX : sig exception E of X.t end
> # FX.E X.A;;
> - : exn = F(X).E 0
>
> Enfin, ça ne serait qu'un problème d'affichage.

C'est en effet un problème d'affichage. L'identité d'une référence
est représentée par une référence vers une chaîne de caractère.
L'égalité physique entre ces références est ce qui définit la
"générativité" des exceptions, et sert à implémenter le filtrage sur
les exceptions. Malheureusement, l'adresse d'une référence, c'est pas
terrible pour l'affichage. Le compilateur essaye de mettre dans la
chaîne de caractère des infos utiles pour imprimer l'exception, en
l'occurrence un chemin d'accès à la définition de la référence:

exception E of int ---> "E"
module X = struct exception E of bool end ---> "X.E"
ton foncteur ci-dessus ---> "F(X).E"

Bien sûr, le cas du foncteur n'est pas très intéressant car une fois
le foncteur appliqué, l'argument ne s'appellera pas X. Mais le but
est juste d'aider l'utilisateur à localiser la définition de
l'exception lorsque il se prend un message "Uncaught exception". On
pourrait mettre "F().E" à la place, ou "F.E", ou...

Maintenant, quid de l'affichage d'une valeur de type "exn" au
toplevel. Le gros problème est de retrouver le type de l'argument
éventuel de l'exception. C'est en général impossible, car on peut
être entièrement sorti du "scope" de la définition de l'exception.

Cependant, le toplevel fait un "best effort" (le brave garçon): il
essaye de résoudre le chemin trouvé dans le nom de l'exception et
regarde s'il s'évalue en le même identifiant d'exception (la même
référence) que celui trouvé dans la valeur d'exception. Si oui, c'est
gagné, on a retrouvé le type de l'argument et on peut afficher
correctement.

Si non, on fait un deuxième "best effort" qui consiste à afficher la
valeur de l'argument sans infos de types statiques, juste en
s'appuyant sur les tags runtime. Ça marche bien pour les chaînes de
caractères, et très mal pour les entiers/booléens/constructeurs
constants, comme ton exemple le montre...

Je vois que ces "best efforts" entraînent une certaine confusion chez
des utilisateurs rigoureux dans ton genre :-) Je veux bien en
enlever, ça supprimera du code pas très joli dans le toplevel, mais
attention à ne pas sacrifier sur l'autel de la pureté idéologique tous
les hints qui pourraient aider l'utilisateur à comprendre d'où vient
cette fichue exception non rattrapée.

> Mais ce qui suit est plus troublant:
>
> # FX.E X.B = B.E true;;
> - : bool = true
>
> Donc OCaml compare des valeurs de type différent, et répond même "true" !

C'est un glitch de plus dans l'égalité générique: les "string ref" qui
encodent l'identité des exceptions sont comparées par contenu et non
par adresse... C'est très gênant en effet car si ces string ref ne
sont pas ==, les restes des blocs exception ne sont pas forcément du
même type et ne devraient pas être comparés...

> On s'attend à ce que les exceptions aient un comportement
> génératif; mais non:

Pas d'accord: le filtrage sur les exceptions (match ou try...with)
implémente exactement le comportement génératif attendu. Refais tes
tests proprement avec des "match" et tout ira bien.

- Xavier

(0000134)
administrator (administrator)
2002-07-20 11:29

On Fri, 19 Jul 2002, Xavier Leroy wrote:

> C'est en effet un problème d'affichage. L'identité d'une référence
> est représentée par une référence vers une chaîne de caractère.
> L'égalité physique entre ces références est ce qui définit la
> "générativité" des exceptions, et sert à implémenter le filtrage sur
> les exceptions.

C'est un peu surprenant, car le filtrage correspond plutôt à une égalité
(=), mais ce n'est pas bien grave.

> Je vois que ces "best efforts" entraînent une certaine confusion chez
> des utilisateurs rigoureux dans ton genre :-) Je veux bien en
> enlever, ça supprimera du code pas très joli dans le toplevel, mais
> attention à ne pas sacrifier sur l'autel de la pureté idéologique tous
> les hints qui pourraient aider l'utilisateur à comprendre d'où vient
> cette fichue exception non rattrapée.

Oui, ça se défend ...

> > Mais ce qui suit est plus troublant:
> >
> > # FX.E X.B = B.E true;;
> > - : bool = true
> >
> > Donc OCaml compare des valeurs de type différent, et répond même "true" !
>
> C'est un glitch de plus dans l'égalité générique: les "string ref" qui
> encodent l'identité des exceptions sont comparées par contenu et non
> par adresse... C'est très gênant en effet car si ces string ref ne
> sont pas ==, les restes des blocs exception ne sont pas forcément du
> même type et ne devraient pas être comparés...

Il serait possible d'imposer pour les exceptions que ces strings ref
soient bien == pour avoir égalité (soit avec un tag runtime spécifique
pour les exceptions, soit en utilisant une valeur particulière x [allouée
une fois pour toute] et en codant les identités par un couple (x,nom);
lorsque la comparaison générique rencontre x dans un bloc, elle passe en
mode "égalité physique"). Je ne sais pas si ça en vaut la peine, bien sûr.


> > On s'attend à ce que les exceptions aient un comportement
> > génératif; mais non:
>
> Pas d'accord: le filtrage sur les exceptions (match ou try...with)
> implémente exactement le comportement génératif attendu. Refais tes
> tests proprement avec des "match" et tout ira bien.

Ok, mais on ne peut pas toujours utiliser le filtrage, par exemple si
l'exception à rattraper n'est pas connue statiquement. Bon, c'est un peu
tiré par les cheveux, mais:

let expect_exn exn f x =
 try f x; false
 with e when e = exn -> true

ne fait pas ce que l'on veut (ce genre de fonction pourrait être utilisé
pour faire des tests de regression, par exemple).

-- Alain

(0006794)
doligez (administrator)
2012-01-25 15:00

Allocating a runtime tag just for this problem: out of question.
Adding a special case for comparison: I don't think the problem is big enough to warrant such a solution.

Another possibility would be to wrap the exception string in an object because objects are never compared structurally. But how much existing C code would be broken by that change?

I'm setting this to resolved/suspended (waiting for someone to come up with a good solution and a patch).

- Issue History
Date Modified Username Field Change
2005-11-18 10:13 administrator New Issue
2008-04-10 14:51 doligez Relationship added related to 0004531
2012-01-25 15:00 doligez Note Added: 0006794
2012-01-25 15:00 doligez Status acknowledged => resolved
2012-01-25 15:00 doligez Resolution open => suspended
2012-01-25 15:00 doligez Description Updated View Revisions
2012-01-25 15:01 doligez Relationship replaced has duplicate 0004531
2013-08-31 12:46 xleroy Status resolved => closed


Copyright © 2000 - 2011 MantisBT Group
Powered by Mantis Bugtracker