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

Type of - inferred to be nat #1263

Open
RustanLeino opened this issue Jun 30, 2021 · 5 comments
Open

Type of - inferred to be nat #1263

RustanLeino opened this issue Jun 30, 2021 · 5 comments
Labels
kind: bug Crashes, unsoundness, incorrect output, etc. If possible, add a `part:` label part: resolver Resolution and typechecking

Comments

@RustanLeino
Copy link
Collaborator

The type of an integer-subtraction expression should never be inferred to be a subset type, but should be the base type thereof. This is not done properly, which causes the verifier to give an error in the following program:

method M(N: nat) {
  var i := N;
  while i != 0
    invariant i - 1 == i - 1 // error reported: result of operation may not be a 'nat'
  {
    i := i - 1;
  }
}

While it is true that the result of i - 1 above may not be a nat, this property should not have been checked in the first place.

Workaround: change i - 1 to i as int - 1

@DavePearce
Copy link

DavePearce commented Jun 30, 2021

The type of an integer-subtraction expression should never be inferred to be a subset type

But, if you don't do that then the subsequent statement i := i - 1 won't type check? In Whiley I resolve this by forward propagating type information. Then we know that i - 1 in i := i - 1 is expected to be a nat whilst i-1 in i - 1 == i - 1 doesn't have to be.

@keyboardDrummer keyboardDrummer added the kind: bug Crashes, unsoundness, incorrect output, etc. If possible, add a `part:` label label Jul 6, 2021
@robin-aws robin-aws added the part: resolver Resolution and typechecking label Sep 22, 2021
@RustanLeino
Copy link
Collaborator Author

Here's another example, using the more innocently known operator +:

type EvenUpTo100 = x | x % 2 == 0 && x <= 100

method P(N: EvenUpTo100) {
  var i := N;
  while i != 100
    invariant i + 1 == i + 1 // error reported: result of operation may not be an 'EvenUpTo100'
  {
    i := i + 2;
  }
}

It seems we'd like to specify the type of the numeric + and - (and similar operators) as

A * B --> C

where A, B, and C are any numeric types where C is a supertype of both A and B and C is (this is the new constraint, which the type inference engine doesn't currently know about) a base type.

@RustanLeino
Copy link
Collaborator Author

@DavePearce If i has type nat and i - 1 is inferred to have type int, then Dafny would still allow the assignment i := i - 1, provided the verifier can prove that i - 1 is a nat at the time of assignment.

@DavePearce
Copy link

Hey @RustanLeino,

So basically you're doing a form of type inference where constraints are checked by the verifier? That sounds similar to Whiley then!

@RustanLeino
Copy link
Collaborator Author

Technically, the desired types are inferred by a more standard type-inference engine. Then, whenever there are conditions to be validated, they are part of what gets passed to the verifier. So, in Dafny and unlike Whiley (I think), the output of the verifier does not affect what types are inferred.

RustanLeino added a commit to RustanLeino/dafny that referenced this issue Jan 12, 2022
Previously, these types were inferred more lazily, which wasn't
helpful for type inference elsewhere. In particular, some expressions
like `n - 10` then ended up typed as `nat` instead of `int`, which in
turn causes unwanted errors.

This addresses issue dafny-lang#1263 in some cases.
RustanLeino added a commit that referenced this issue Jan 13, 2022
This PR contains several improvements to type inference, and in particular it fixes the soundness issue reported in #1676.

Main type fixes:

* Fix soundness issue, where the verifier had left out a subset-type check
* Improve type inference: For bound variables (like those in let expressions and the bound variables in case patterns), eagerly set the type of the bound variable to the type of the RHS. After all, this is the most general type one can be sure the variable can have. (This improves the situation reported in Type of - inferred to be nat #1263, but is not a full solution for that problem.)

Other improvements:

* fix: The desugaring of `match` into `VarDeclPattern` had incorrectly set the `isGhost` bit. (I don't think the previous error caused any problems, except for producing confusing output with `/rprint`.)
* fix: Add missing space in error message
* Remove an unnecessary set of `{ }` in the desugaring of `match` cases
* Rename `CheckEnds` to `DetermineRootLeaf`, and add various comments
* chore: Use the `out var` and `out _` syntax when calling `DetermineRootLeaf`
* chore: Use the `.Count` getter instead of the `.Count()` method for `List`s

Fixes #1676
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: bug Crashes, unsoundness, incorrect output, etc. If possible, add a `part:` label part: resolver Resolution and typechecking
Projects
None yet
Development

No branches or pull requests

4 participants