Repository: verus-lang/verus

Issue #944: Interpreter handles opaque inconsistently
Created: 2023-12-15T19:09:44Z
This program
use vstd::prelude::*;

verus!{

fn main() { }

pub proof fn bar() {
    assert(vstd::std_specs::bits::u64_leading_zeros(0) == 64) by (compute_only);
}

}

uses u64_leading_zeros, which is defined as public and open:

  
    
      verus/source/pervasive/std_specs/bits.rs
    
    
         Line 655
      in
      2b607f7
    
  
  
    

        
          
           pub open spec fn u64_leading_zeros(i: u64) -> int 
        
    
  


Oddly, when the interpreter tries to run, when it reaches the point of trying to apply u64_leading_zeros, it does a lookup for the function's body and the lookup fails, specifically we get None here:

  
    
      verus/source/vir/src/interpreter.rs
    
    
         Line 1365
      in
      2b607f7
    
  
  
    

        
          
           match ctx.fun_ssts.get(fun) { 
        
    
  


where fun_ssts is passed into the interpreter from the state object in the ast_to_sst conversion.
If I inline the definition in the same file, there's no trouble finding and using the definition.
use vstd::prelude::*;

verus!{

fn main() { }

#[verifier::opaque]
pub open spec fn u64_leading_zeros(i: u64) -> int
    decreases i
{
    if i == 0 {
        64
    } else {
        u64_leading_zeros(i / 2) - 1
    }
}

pub proof fn bar() {
    assert(u64_leading_zeros(0) == 64) by (compute_only);
}

}
}

Is this expected behavior?  Or is something going wrong in the collection of fun_ssts?

Duplicate issue #928: Panics with `assert(..) by (compute)`
Created: 2023-11-27T13:42:49Z
These two files both cause panics.
use vstd::prelude::*;

verus!{

fn main() { }

global size_of usize == 8;

pub open spec fn foo() -> int {
    if 10 == 1 {
        1
    } else {
        let w = 10 as u64;
        let lz = w.leading_zeros();
        (w >> lz as u64) as int
    }
}

pub proof fn bar() {
    assert(foo() == 0) by (compute);
    // Doesn't panic:
    // assert(foo() == 0) by (compute_only);
}

}
ξ verus-release /st/verus/test2.rs
thread '<unnamed>' panicked at vir/src/sst_to_air.rs:1203:67:
bounded integer type
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread '<unnamed>' panicked at rust_verify/src/verifier.rs:1837:29:
worker thread panicked

use vstd::prelude::*;

verus!{

fn main() { }

// This line makes Verus not panic
// global size_of usize == 8;

spec fn foo(size: int) -> int {
    let bits = usize::BITS as int;
    if bits == 1 {
        0
    } else if bits <= 8 {
        0
    } else {
        0
    }
}

proof fn bar() {
    assert(foo(0) == 0) by (compute);
    // Doesn't panic:
    // assert(foo() == 0) by (compute_only);
}

}
ξ verus-release /st/verus/test.rs
thread '<unnamed>' panicked at rust_verify/src/verifier.rs:715:21:
internal error: generated ill-typed AIR code: error 'error 'use of undeclared variable bits~17$' in expression 'bits~17$'' in expression '(<= bits~17$ 8)'
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread '<unnamed>' panicked at rust_verify/src/verifier.rs:332:17:
dropped, expected call to `into_inner` instead
stack backtrace:
   0:     0x7f6bdd51349c - std::backtrace_rs::backtrace::libunwind::trace::he43a6a3949163f8c
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
   1:     0x7f6bdd51349c - std::backtrace_rs::backtrace::trace_unsynchronized::h50db52ca99f692e7
                               at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
[...]

Closing comment (2023-12-15T21:41:58Z): When I try the first file, it errors with:
error: cannot call function with mode exec
  --> issue-928a.rs:14:18
   |
14 |         let lz = w.leading_zeros();
   |                  ^^^^^^^^^^^^^^^^^

probably because we recently removed the when_used_as_spec annotation.  If I manually apply the corresponding spec function let lz = vstd::std_specs::bits::u64_leading_zeros(w);, I do see a similar panic:
thread '<unnamed>' panicked at vir/src/sst_to_air.rs:1204:44:
expected bounded integer type Int(Nat)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread '<unnamed>' panicked at rust_verify/src/verifier.rs:1837:29:
worker thread panicked

To eliminate anything that might be related to #944, I simplified it further to:
use vstd::prelude::*;

verus!{

fn main() { }

pub open spec fn id(x:int) -> int;

pub proof fn bar() {
    assert(
        { (10 as u64 >> (id(5) as u64)) as int }
        == 0) by (compute);
}

}

It looks like the problem was that the 10 as 64 was getting treated as a clip operation, which when the value is actually in bounds, simply clones the inner expression.  In this case, the 10 has type int, which then leads the bit-vector-related sanity checks to complain.
For the second program, we can simplify foo a bit:
spec fn foo(size: int) -> int {
    let bits = usize::BITS as int;
    if bits == 1 {
        0
    } else {
        bits 
    }
}

The issue is that when global size_of usize is not set, we can't simplify the conditional in if statement beyond substituting in the value of bits, and hence we stop simplifying (since we know that we won't be able to execute one of the two branches).  However, that means that the reference to bits in the false branch is left as a dangling reference when we try to hand things off to Z3.