Skip to content
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

Use the Dolmen identifiers for constructors and ADT names #1098

Merged
merged 15 commits into from
May 16, 2024

Conversation

Halbaroth
Copy link
Collaborator

@Halbaroth Halbaroth commented Apr 24, 2024

We don't manage correctly incremental type declarations. For instance the following input is accepted by the SMT-LIB standard:

(set-logic ALL)
(push 1)
(declare-datatype t
  ((c1 (d1 Int)) (c2)))
(pop 1)
(declare-datatype t
  ((c2 (d1 Int)) (c1)))
(declare-const x t)
(declare-const y t)
(assert ((_ is c1) x))
(assert ((_ is c1) y))
(assert (distinct x y))
(check-sat)

(Notice that we switch the constructors c1 and c2 in our second declaration of the type t). AE answers unknown on next but it answers unsat if we remove the first declaration of t.

This PR fixes the issue for the new frontend only. So we got unsat with --frontend dolmen but we still got unknown with --frontend legacy.

The solution consists in using the unique identifiers produced by Dolmen for the constructors, destructors and names of ADTs. To reduce as much as possible the amount of modification done in the legacy typechecker, I wrapped Dolmen's identifiers in a new module Uid. This module agrees with Hstring if we use the constructors of_string or of_hstring and it uses Dolmen's identifiers if we use the constructor of_dolmen. Of course, we'll get rid of this module as soon as we remove the legacy frontend.

I didn't use a unique identifier for the constructor of builtin enumeration types. This case will be fixed after removing the legacy frontend.

This PR is supposed to be merged before #1093

@Halbaroth Halbaroth added the bug label Apr 24, 2024
@Halbaroth Halbaroth added this to the 2.6.0 milestone Apr 24, 2024
@Halbaroth Halbaroth marked this pull request as ready for review May 6, 2024 06:36
Copy link
Collaborator

@bclement-ocp bclement-ocp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not look thoroughly through the minutiæ of all mechanical changes from HstringUid but the change looks correct overall. I do have some minor questions, and something needs to be done for record constructors which I think are broken in the legacy typechecker by this PR.


type t =
| Fake of Hstring.t
| Unique of { name : Hstring.t; index : int }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use a separate Unique constructor with our own tag rather than directly use the Dolmen identifier?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course I tried to use the Dolmen identifier directly but I got a regression in our non-regression test suite. I should investigate.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I tried again with the original index produced by Dolmen. Now I got five regressions and it seems that Alt-Ergo ignores the timeout for them (make runtest never returns). Actually, if I run dune exec -- alt-ergo tests/adts/match_in_terms.ae, I cannot kill the processus with CTRL+C. I have to type pkill alt-ergo.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect an issue with DE.Id.compare or DE.Id.equal.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uhm they seems to be reasonable:

  let compare v v' = compare v.index v'.index

  let equal v v' = compare v v' = 0

very strange ...

Copy link
Collaborator Author

@Halbaroth Halbaroth May 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks to Vincent, I got the bug with gdb. The issue comes from the fact that Dolmen identifiers can be infinite values and the polymorphic equality loops infinitely on them. I wrote another PR to remove as much as possible polymorphic comparison in lists.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, dolmen identifiers should never be compared using the polymorphic identity: besides being slow (compared to the comparison defined by dolmen), it may loop forever (because the Type identifiers loops on itself), or may fail at runtime (because ids contain tags that can contain arbitrary data such as functions).

Comment on lines 554 to 555
(* TODO: FIIIIXXX *)
(* Format.sprintf "%s___%s" record_constr n *)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is required for the legacy typechecker not to confuse constructors for distinct record types ; it should be moved to the legacy typechecker.ml rather than removed entirely.

@@ -514,19 +514,20 @@ let type_body name args = Decls.body name args


(* smart constructors *)
let tunit = Text ([],Hstring.make "unit")
(* HACK: we should create a unique Dolmen identifier here. *)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should instead use DStd.Ty.Const.unit.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know Dolmen already contains a unit constant. I'll change it.

Comment on lines 91 to 92
let fake_reprs = List.map Uid.of_hstring hstring_smt_reprs in
Ty.Tsum (Uid.of_string "RoundingMode", fake_reprs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use hstring rather than dolmen IDs here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I wrote this PR I didn't do it because I didn't want to have to manage unique identifiers in the legacy typechecker. Basically, if we use real unique identifiers and hstring identifiers, we have to ensure that the legacy typechecker won't recreate ADTs' constructors for builtin enums (and it does...).

Finally I found another way to use dolmen identifiers here. I modified the comparison functions of Uid. Now if we compare a Hstring and a Dolmen identifiers, we transform the Dolmen identifier into string.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the legacy typechecker won't recreate ADTs' constructors for builtin enums (and it does...).

Ah, I guess this is the issue of having different names for the AE and SMT enums, got it.

Now if we compare a Hstring and a Dolmen identifiers, we transform the Dolmen identifier into string.

Is this not re-introducing the dependency on string values that this PR is trying to eliminate?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now the Dolmen frontend doesn't produce Uid.t of type Hstring anymore (but for abstract types, I'll change that). It means we always compare Dolmen identifiers here (for constructors, destructors and ADTs' names).

module DE = DStd.Expr

type t =
| Fake of Hstring.t
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: calling them Fake is confusing; what's fake about them?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's fake unique identifier. It's not unique but I can rename it into Hstring.

type t = int
let equal = (=)
let hash = Fun.id
type id = Id : 'a DE.id -> id
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type id = Id : 'a DE.id -> id
type id = Id : 'a DE.id -> id[@@unboxed]

@Halbaroth
Copy link
Collaborator Author

The PR is ready for a second pass. I think we should test the legacy frontend with fpa on Marvin before merging it.

Copy link
Collaborator

@bclement-ocp bclement-ocp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah on paper the changes look correct but we definitely should check that there are no regressions. I am a bit lost by the many small changes now; I will take a deeper look after the weekend. I think there are many unneeded string/Hstring conversions now but we can clean that up later.


let fresh_ty ?(is_var = true) ?(name = None) () =
let fresh_ty ?(is_var = true) ?(id = None) () =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want ?id here and ~id when fresh_ty is called.

(Currently fresh_ty has a ?id:Uid.t option optional argument, simply using ?id would make it a ?id:Uid.t argument)

Comment on lines 204 to 206
DStd.Expr.Id.mk ~builtin:B.Base
(DStd.Path.global (Hstring.view name))
(DStd.Path.global (Uid.show name))
DStd.Expr.{ arity = 0; alias = No_alias }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks a bit weird; aren't we building a second Dolmen identifier with the same name as an existing one here?

Copy link
Collaborator Author

@Halbaroth Halbaroth May 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, I thought it was correct but we duplicate the identifiers here. I fixed the issue by creating the constructors and the Dolmen type in Fpa_rounding.


let pp ppf = function
| Hstring hs -> Hstring.print ppf hs
| Dolmen { path; _ } -> Fmt.string ppf @@ get_basename path
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not DE.Id.print?

@Halbaroth Halbaroth force-pushed the unique-constructors branch 2 times, most recently from b64ecd1 to 0446b52 Compare May 10, 2024 23:08
@Halbaroth
Copy link
Collaborator Author

I rebase this PR on #1117 to remove some polymorphic comparisons again ;)

Copy link
Collaborator

@bclement-ocp bclement-ocp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still a couple questions but overall I think this is ready to merge once rebased & if the benches are green.

| Hstring hs1, Hstring hs2 -> Hstring.equal hs1 hs2
| Dolmen id1, Dolmen id2 -> DE.Id.equal id1 id2
| _ ->
Hstring.equal (Hstring.make @@ show u1) (Hstring.make @@ show u2)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this a complicated way of writing String.equal (show u1) (show u2)? The complexity should be the same since Hstring.make requires a linear scan.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it was a little bit faster because we compare integers if these Hstring already exist but we have to compute the hash of the strings, which may be linear in the length of them.

match u1, u2 with
| Hstring hs1, Hstring hs2 -> Hstring.compare hs1 hs2
| Dolmen id1, Dolmen id2 -> DE.Id.compare id1 id2
| _ -> Hstring.compare (Hstring.make @@ show u1) (Hstring.make @@ show u2)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the Hstring comparison here? Otherwise String.compare (show u1) (show u2) might be sufficient (complexity should be the same since Hstring.make requires a linear scan).


(** Equal to ["RoundingMode"], the SMT2 type of rounding modes. *)
val fpa_rounding_mode_type_name : string

(** Equal to ["fpa_rounding_mode"], the Alt-Ergo native rounding mode type. *)
val fpa_rounding_mode_ae_type_name : string

(** The Dolmen rounding mode type. *)
val fpa_rounding_mode_dty : Dolmen.Std.Expr.Ty.t
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Do we still need the various string conversion functions with this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so

Copy link
Collaborator Author

@Halbaroth Halbaroth May 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need them in both legacy and new frontend. In the new frontend, we use these functions in the typechecker to inject the AE identifiers into SMT-LIB identifiers.

We also use them in Expr because we need to convert a Hstring identifier into Fpa_rounding.rounding_mode.

I'm sure we can remove some of them after getting rid of the legacy frontend.

Comment on lines 92 to 100
let module DStd = Dolmen.Std in
let ty_cst = DE.Ty.Const.mk (DStd.Path.global "RoundingMode") 0 in
let cstrs =
List.map (fun c -> DStd.Path.global @@ to_smt_string c, []) cstrs
in
let _, cstrs = DE.Term.define_adt ty_cst [] cstrs in
DE.Ty.apply ty_cst [], cstrs,
Ty.Tsum (Uid.of_dolmen ty_cst, List.map (fun (c, _) -> Uid.of_dolmen c) cstrs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I think this could be replaced with DStd.Expr.Ty.roundingMode. I don't think it is entirely trivial to do so, but might be worth a note in a comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't the enum type is shipped with Dolmen. We can replace it easily. It's exactly the same code.

Copy link
Collaborator Author

@Halbaroth Halbaroth May 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uhm actually roundingMode is the SMT-LIB roundingMode, so it's not so obvious. I add a note.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RoundingMode type from AE (SMT-LIB flavor) should be identical to the SMT-LIB RoundingMode, we made sure with @Stevendeo that the constructors are the same.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put a note and I will do it in another PR, maybe after the release.

@Halbaroth
Copy link
Collaborator Author

No regression with the following combinations:

  • frontend legacy on ae-format
  • frontend legacy on ae-format-fpa with --enable-theories fpa
  • frontend dolmen on ae-format
  • frontend dolmen on ae-format-fpa with --enable-theories fpa

So this PR is ready for I hope a last check ;)

Copy link
Collaborator

@bclement-ocp bclement-ocp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

@Halbaroth Halbaroth merged commit 8e76dcd into OCamlPro:next May 16, 2024
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants