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

Quint -> TLA+ transpilation fixes #3041

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

bugarela
Copy link
Collaborator

@bugarela bugarela commented Nov 29, 2024

Hello :octocat:

I was running into some issues when using Apalache to transpile Quint into TLA+. There were even parsing errors with invalid parenthesis. After much debugging, I got to fix all of the ones I know of, except for the assignments in init (which are not a big deal because there's only one init per spec so it's always easy to manually fix those).

Here are the issues:

  1. Whenever we had a default eliminator on a match, we would produce something like QUINT_LAMBDA1([]), but [] is not a valid TLA+ expression. I've replaced it with {}.
  2. When accessing a map (function) with lists as domains, we need to do something like map[<<1, 2>>] but the pretty printer was removing the tuple wrapper on this and producing map[1, 2] instead which is not right.
  1. For Quint's unit type, we were producing "U_OF_UNIT" and this was causing a TLC-specific error: TLC tried to compare some value like [ "tag" |-> "Some", value |-> 1 ] with "U_OF_UNIT" for its fingerprint generation and it failed at comparing. Replacing "U_OF_UNIT" with [ "tag" |-> "UNIT" ] fixed this problem, so that's what I did.
  2. Lastly, the most complicated one: applying operators to LET-IN arguments doesn't work on TLC, and the Apalache representation transforms a lot of things into this format. I didn't want to touch anything that could affect Apalache's internals, so I only changed how this things get printed. Something that has this structure on Apalache IR:
Foo(LET A(param1, param2) == param1 + param2 IN A(1, 2))

will be printed as:

LET A(param1, param2) == param1 + param2 IN Foo(A(1, 2))
  • Tests added for any new code
  • Ran make fmt-fix (or had formatting run automatically on all files edited)
  • Documentation added for any new functionality
  • Entries added to ./unreleased/ for any new functionality

@bugarela bugarela marked this pull request as ready for review November 29, 2024 12:59
@bugarela bugarela requested a review from konnov November 29, 2024 13:00
Copy link
Collaborator

@konnov konnov left a comment

Choose a reason for hiding this comment

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

This is a nice set of fixes. Thanks a lot! I have a question about two of the changes, trying to pinpoint the cases where it is actually required.

Comment on lines -300 to -307
// a function of multiple arguments that are packed into a tuple: don't print the angular brackets <<...>>
case OperEx(op @ TlaFunOper.app, funEx, OperEx(TlaFunOper.tuple, args @ _*)) =>
val argDocs = args.map(exToDoc(op.precedence, _, nameResolver))
val commaSeparatedArgs = folddoc(argDocs.toList, _ <> text(",") <@> _)
group(
exToDoc(TlaFunOper.app.precedence, funEx, nameResolver) <> brackets(commaSeparatedArgs)
) ///

Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we have to remove this? In TLA+, f[x, y] is a short-form for f[<<x, y>>]. Look at this example: https://gist.github.com/konnov/ce0ca9071dbe75b936e96dbd99224f0d

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 does work like that for tuples, but not for sequences:

--------------------------------- MODULE test ---------------------------------
EXTENDS Integers, Sequences

VARIABLES
  f

Init ==
  f = [ l \in {<<1>>, <<>>, <<1, 2>>} |-> Len(l) ]

Next ==
  UNCHANGED f

Inv ==
  f[<<1>>] = 1

Inv2 ==
  f[1] = 1

=============================================================================

TLC fails to check Inv2 here:

Error: Attempted to check equality of the function << >> with the value:
1
While working on the initial state:
f = (<<>> :> 0 @@ <<1>> :> 1 @@ <<1, 2>> :> 2)

Error: TLC was unable to fingerprint.

I believe there is no way to disambiguate between tuples and sequences at this point, so the only option was to remove it completely.

@@ -473,14 +465,19 @@ class PrettyWriter(
wrapWithParen(parentPrecedence, op.precedence, group(doc))

case OperEx(op @ TlaOper.apply, NameEx(name), args @ _*) =>
val (decls, newArgs) = extractDecls(args)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you have an example of what does not work exactly? For instance, this one works in TLC: https://gist.github.com/konnov/e7df8586a9865027dc4abfc07188e060

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh interesting, I only realize this now, but this problem only happens with high-order operators. Here's an example that doesn't work:

--------------------------------- MODULE test ---------------------------------
EXTENDS Integers

VARIABLES z

F(f(_), x) == f(x)

G(x) == F(LET H(y) == y * 2 IN H, x)

Init ==
  z = G(3)

Next ==
  UNCHANGED z

=============================================================================

TLC Errors:

*** Errors: 2

line 8, col 11 to line 8, col 32 of module test

An expression appears as argument number 1 (counting from 1) to operator 'F', in a position an operator is required.


line 8, col 9 to line 8, col 36 of module test

Argument number 1 to operator 'F'
should be a 1-parameter operator.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Right. This is because operators are not values in TLA+

Comment on lines +532 to +533
* On TLA+ files, we can't have LET..IN expressions as arguments. This is a helper function to extract the
* declarations so they can be printed before the operator application that would use them as arguments.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you mean something like this?

LET A(x, y) == x + y IN F(A)

Actually, this one should work if F is declared as F(A(_)) == ...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not sure if I understand the question. I'm saying this one works:

LET A(x, y) == x + y IN F(A)

while this one doesn't:

F(LET A(x, y) == x + y IN A)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants