Closed wandernauta closed 1 month ago
This is a decent hotfix. The only thing I'm wondering about is whether it should actually be stronger, i.e. if you have an instance of EmptyEnum you should be able to prove false:
enum Empty {
}
void (Empty emp) {
assert false;
}
I'm not sure how to encode that without viper/z3 picking up this fact without even mentioning Empty
somewhere else... I'll inquire with the other vercors people and otherwise merge this PR. Of course if you have any ideas please share :)
Actually, you can also assign null
to enum-typed variables. So empty enums only allow you to prove false if you have a non-null reference to one. The axiom can instead be something like:
\forall EnumType e; e != null ==> ... e is in range ...
Would you mind trying this change out? It's especially important that it doesn't make (false) asserts in unrelated methods suddenly pass, but with the null check in place that should be fine.
Thanks for taking a look!
It looks like the fact that enums can be null is encoded as an Option
, so e.g. the argument emp
gets type Option<Empty>
. So on the axioms for the adt Empty
itself, it's already certain that we are not in the null
case, right? So e != null
would always be true, and true ==> ... e is in range ...
, which is the axiom from before.
Indeed it would be nice if VerCors could prove no instances of Empty
can exist, that feels neater. Perhaps it can be stated that Option<Empty>
can only ever be none
?
With the suggested change, a number of tests break (both Java and PVL). If I try to verify the program from above, the axiom becomes:
axiom (\forall Empty i; asAny1(i) != asAny2(TNull.vNull()) ==> 0 <= Empty.empty1(i) && Empty.empty1(i) < 0);
I get:
[WARN] Possible viper bug: silver AST does not reparse when printing as text
[WARN] Cannot use function asAny1, which has preconditions, inside axiom (vercors-9692451782079529410.sil@58.7--58.13)
[WARN] Cannot use function asAny2, which has preconditions, inside axiom (vercors-9692451782079529410.sil@58.20--58.26)
I can find asAny1
and asAny2
in the COL, they don't look like they have preconditions, but they do have a signature; it feels like a type error, basically.
Thanks for taking a look!
It looks like the fact that enums can be null is encoded as an
Option
, so e.g. the argumentemp
gets typeOption<Empty>
. So on the axioms for theadt Empty
itself, it's already certain that we are not in thenull
case, right? Soe != null
would always be true, andtrue ==> ... e is in range ...
, which is the axiom from before.Indeed it would be nice if VerCors could prove no instances of
Empty
can exist, that feels neater. Perhaps it can be stated thatOption<Empty>
can only ever benone
?With the suggested change, a number of tests break (both Java and PVL). If I try to verify the program from above, the axiom becomes:
axiom (\forall Empty i; asAny1(i) != asAny2(TNull.vNull()) ==> 0 <= Empty.empty1(i) && Empty.empty1(i) < 0);
I get:
[WARN] Possible viper bug: silver AST does not reparse when printing as text [WARN] Cannot use function asAny1, which has preconditions, inside axiom (vercors-9692451782079529410.sil@58.7--58.13) [WARN] Cannot use function asAny2, which has preconditions, inside axiom (vercors-9692451782079529410.sil@58.20--58.26)
I can find
asAny1
andasAny2
in the COL, they don't look like they have preconditions, but they do have a signature; it feels like a type error, basically.
You can still refer to another type (i.e. Option[Empty]
) in an ADT's axiom.
That warning comes from the decreases clauses on these functions. Recently those were changed to no longer count as preconditions for this check but we haven't changed to that viper version yet but it's fine to ignore those warnings for now.
I think this requires a little bit more design work. Initially I intended for the COL enum type to be more of a mathematical enum type, meaning you can have an empty enum, and having an instance of that would allow you to prove false. Obviously I never got that far :) In addition, the implementation encodes enums as a domain, and (correct me if I'm wrong) in silicon/z3 domains are assumed to be inhabited. So that makes it a bit difficult to get the behaviour we want with a small fix. Or at least, in terms of design, it's not really clear to me what needs to change. In addition, Java allows empty enums, so that's actually a shortcoming of the frontend.
Writing the enum axioms over the type Option[Enum]
instead of over just Enum
is an interesting idea, but feels a bit fishy as well. Not sure if that's what we want. Maybe it is?
@wandernauta If you have time to redesign the enum implementation more thoroughly you are welcome to do so. It would be nice to have an internal enum type that is a bit more well founded. But otherwise, if we just want VerCors to behave sane without too much effort, I think it may be best do just disallow empty enums in PVL for now, like we do in the Java frontend. AFIAK I don't think anyone really depends on it. If you could change your PR to reflect that that'd be nice.
In addition, we should probably fix the SatCheck pass/default constructor generation code to generate a sane error message when a false axiom is added. (#1255)
I think it may be best do just disallow empty enums in PVL for now, like we do in the Java frontend.
This sounds good to me, especially since it doesn't hinder making enum Foo { }
valid syntax at some later stage, to represent something that cannot exist.
Would it be sufficient to do this at the grammar level (so an empty enum would error saw }, expected IDENTIFIER, right here
), or would you like to see a specific failure message, exactly like the Java frontend?
If you could change your PR to reflect that that'd be nice.
Will do!
Probably the best place to do this is in PVLToCol.scala. You can find examples that use the fail
method to reject unsupported syntaxes.
Looks finished. Please confirm, then I'll merge it.
Yep! I think this should do it.
Thanks for your help!
The PVL grammar (but not other front-ends) allows writing enumerations with no constants:
However, the
toIntRange
axiom that theEnumToDomain
pass encodes for such an enum doesn't obviously hold, which causes verification of any program with such an enum to fail, for instance:This change makes it so that this specific axiom is omitted for empty enums.
Also adds a test that a snippet like the above should verify.
Front-ends are unchanged; for example, the Java frontend still rejects empty enums.
Pre-fix crash log for reference