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

Cannot prove extremely simple equality already stated. #3863

Open
MikaelMayer opened this issue Apr 12, 2023 · 6 comments
Open

Cannot prove extremely simple equality already stated. #3863

MikaelMayer opened this issue Apr 12, 2023 · 6 comments
Labels
has-workaround: yes There is a known workaround incompleteness Things that Dafny should be able to prove, but can't kind: bug Crashes, unsoundness, incorrect output, etc. If possible, add a `part:` label

Comments

@MikaelMayer
Copy link
Member

Dafny version

4.0.0.50303

Code to produce this issue

function HasProperty<T>(InputTest: T -> bool): bool

function
  HasPropertyMap<T>(m: T -> T, test: T -> bool) : bool {
  HasProperty((t: T) => test(m(t)))
}

lemma CannotInstantiate(test: int -> bool, m: int -> int) {
  assert HasPropertyMap(m, test)
      == HasProperty((t: int) => test(m(t)));
}

Command to run and resulting output

Just pâste into VSCode or verify from the command line.

It says the assertion might not hold.

What happened?

I would have expected the assertion to hold out of the box since I'm just copying the definition of HasPropertyMap.

What type of operating system are you experiencing the problem on?

Windows

@MikaelMayer MikaelMayer added the kind: bug Crashes, unsoundness, incorrect output, etc. If possible, add a `part:` label label Apr 12, 2023
@MikaelMayer
Copy link
Member Author

Debugging the issue in Boogie reveals the following only difference between the two expressions that prevents Boogie from concluding to equality easily

 _module.__default.HasProperty(TInt, 
             Lit(AtLayer((lambda $l#0#ly#0: LayerType :: 
                   Handle1((lambda $l#0#heap#0: Heap, $l#0#t#0: Box :: 
                       $Box($Unbox(Apply1(TInt, 
                             TBool, 
                             $l#0#heap#0, 
                             test#0,
-                            Apply1(TInt, 
+                            $Box($Unbox(Apply1(TInt,
                               TInt, 
                               $l#0#heap#0, 
                               m#0, 
-                              $l#0#t#0
+                              $Box($Unbox($l#0#t#0): int)
-                            ))): bool)),
+                            )): int))): bool)),
                     (lambda $l#0#heap#0: Heap, $l#0#t#0: Box :: 
                       $IsBox($l#0#t#0, TInt)), 
                     (lambda $l#0#heap#0: Heap, $l#0#t#0: Box :: 
                       SetRef_to_SetBox((lambda $l#0#o#0: ref :: false))))), 
                 $LS($LZ))));

@MikaelMayer
Copy link
Member Author

So a solution is to remove all generics, but that's not feasible in my case.

@MikaelMayer MikaelMayer added the incompleteness Things that Dafny should be able to prove, but can't label Apr 12, 2023
@MikaelMayer
Copy link
Member Author

More investigation: So it seems that the test variable's output is boxed/unboxed in both cases, so the problem comes only from the "m". Indeed, I can make a shorter example:

function HasProperty<T>(InputTest: T -> T): bool

function 
  HasPropertyMap<T>(m: T -> T) : bool {
  HasProperty((t: T) => m(t))
}

lemma CannotInstantiate(m: int -> int) {
  assert HasPropertyMap<int>(m)
      == HasProperty((t: int) => m(t));
}

And a workaround is to define

function 
  HasPropertyMapInt(m: int -> int) : bool {
  HasProperty((t: int) => m(t))
}

This time, the assertion passes:

lemma Works(m: int -> int) {
  assert HasPropertyMapInt(m)
      == HasProperty((t: int) => m(t));
}

@RustanLeino
Copy link
Collaborator

Another workaround is to collect the two occurrences of the lambda expressions into one place:

function HasProperty<T>(InputTest: T -> bool): bool

function MyLambdaExpression<T>(m: T -> T, test: T -> bool): T -> bool {
  (t: T) => test(m(t))
}

function HasPropertyMap<T>(m: T -> T, test: T -> bool) : bool {
  HasProperty(MyLambdaExpression(m, test))
}

lemma CanInstantiate(test: int -> bool, m: int -> int) {
  assert HasPropertyMap(m, test)
      == HasProperty(MyLambdaExpression(m, test));
}

or more generically:

function HasProperty<T>(InputTest: T -> bool): bool

function Compose<A, B, C>(f: A -> B, g: B -> C): A -> C {
  a => g(f(a))
}

function HasPropertyMap<T>(m: T -> T, test: T -> bool) : bool {
  HasProperty(Compose(m, test))
}

lemma CanInstantiate(test: int -> bool, m: int -> int) {
  assert HasPropertyMap(m, test)
      == HasProperty(Compose(m, test));
}

@RustanLeino
Copy link
Collaborator

To improve this situation in Dafny, perhaps we should always box when translating lambda expressions.

@MikaelMayer
Copy link
Member Author

That workaround works like a charm ! What is boxing and unboxing for, by the way?

@MikaelMayer MikaelMayer added the has-workaround: yes There is a known workaround label Apr 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has-workaround: yes There is a known workaround incompleteness Things that Dafny should be able to prove, but can't kind: bug Crashes, unsoundness, incorrect output, etc. If possible, add a `part:` label
Projects
None yet
Development

No branches or pull requests

2 participants