-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unlawful compare
implementations
#99
Comments
Apparently Frama-C realized this at some point as well: (* BIGTODO: this function is not quite a total order, because [is_leq] is
only a partial order. Using the hash as a first comparison is only
a doubtful hack. *)
let compare a b =
if equal a b then 0
else
let cmp = compare (hash a) (hash b) in
if cmp <> 0 then cmp
else if Abstract1.is_leq man a b then 1 else -1 |
Thanks for reporting this issue. I try to address them in #108, by basically disabling the problematic features. I currently see no efficient and robust solution to implement a total order on these data-structures and make them usable as keys in sets and maps. In particular, the order cannot rely on the internal representation of an abstract element as the library is free to update it with another representation of the same concrete set without notice. The Frama-C code shows that they did not find a good solution either. |
For |
Yes In theory, a total order could be possible for |
Fixed in #108 |
The
compare
functions which are exposed to OCaml, either directly or viacustom_operations
structs andStdlib.compare
, do not satisfy the usual order laws. In turn, this means that many Apron types cannot (at least easily) be used forSet.Make
andMap.Make
.Since the operations are somehow defined, doing so doesn't immediately fail (e.g. by exception), but leads to some nasty Heisenbugs down the line.
Examples
Environment
Environment.compare
is explicitly exposed to OCaml:apron/mlapronidl/environment.idl
Lines 216 to 218 in bdf251f
The documentation correctly describes its behavior, which, however, is not the
compare
suitable forSet.OrderedType
.For example (in pseudo-syntax),
Environment.compare {var1} {var2} > 0
andEnvironment.compare {var2} {var1} > 0
both returntrue
(becausecompare
returns 2 both ways).What's worse, the same implementation is specified for the
Environment.t
custom OCaml block viacustom_operations
struct, which is to be used byStdlib.compare
(and its standard operator-derivatives). The problem is that this doesn't fit the usually-understood properties of that function as documented:So, for example,
{var1} > {var2}
and{var2} > {var1}
both returntrue
as well.Abstract0
(andAbstract1
)Abstract0
(andAbstract1
, which is built on top of it) don't directly expose and document acompare
. However, one is still defined forAbstract0.t
's custom block:apron/mlapronidl/apron_caml.c
Lines 282 to 308 in bdf251f
At first glance, this appears somewhat sound: first dimensions are compared, then
ap_abstract0_is_eq
is used to check for semantic equality (which is also exposed asis_eq
to OCaml). But the final fallback comparison is of pointers themselves (which are arbitrary memory addresses). On its own, that's a valid total order, but not when combined with the semantic equality check.For example, suppose
a1 = {x>=0}
,a2 = {-x>=0}
anda3 = {x>=0}
are allocated in order and their addresses also happen to be in the same order. Thena1 < a2
anda2 < a3
per pointer comparison, buta1 = a3
per semantic equality check.Other types
Other Apron types might have similar issues, I didn't check everything.
Var.compare
at least is sound because it's juststrcmp
.Conclusion
Even though polymorphic comparison and
Stdlib.compare
aren't generally recommended, they have been defined for Apron's custom blocks and that's the only way to compare most of them. Even if there's no plan to fix it, this issue hopefully serves as a warning to anyone trying to useSet.Make
/Map.Make
on Apron data types, which appears to work at first glance but can surprisingly break down.The text was updated successfully, but these errors were encountered: