prusti-dev icon indicating copy to clipboard operation
prusti-dev copied to clipboard

Consistency error in postcondition when calling pure function that returns a slice

Open Pointerbender opened this issue 3 years ago • 7 comments

The following Rust program:

use prusti_contracts::*;

#[derive(Copy, Clone)]
pub struct A {
    inner: usize,
}

impl A {
    #[pure]
    pub const fn get(&self) -> usize {
        self.inner
    }
}

pub struct B {
    a: A,
}

impl B {
    #[pure]
    #[trusted]
    pub fn foo(&self, a: usize, b: usize) -> &[u8] {
        unimplemented!()
    }

    #[pure]
    #[ensures(result == self.foo(a.get(), b))]
    pub fn bar(&self, a: A, b: usize) -> &[u8] {
        self.foo(a.get(), b)
    }

    #[pure]
    pub fn baz(&self) -> &[u8] {
        self.bar(self.a, 1)
    }
}

Generates the following for error message for the Viper program of B::baz:

error: [Prusti internal error] Prusti encountered an unexpected internal error
  |
  = note: We would appreciate a bug report: https://github.com/viperproject/prusti-dev/issues/new
  = note: Details: consistency error in prusti_example::B::baz: Consistency error: Local variable _4 not found. (@96.15)

This is due to the pure function encoding of B::bar:

function m_B$$bar__$TY$__(_1: Snap$struct$m_B, _2: Snap$struct$m_A, _3: Int): Snap$Slice$u8
  requires 0 <= _3
  requires _3 <= 18446744073709551615
  requires true
  requires true
  ensures snap$__$TY$__Snap$Slice$u8$(_4.val_ref) == m_B$$foo__$TY$__(_1, m_A$$get__$TY$__(_2), _3)
  ensures [result == mirror_simple$m_B$$bar__$TY$__(_1, _2, _3), true]
{
  m_B$$foo__$TY$__(_1, m_A$$get__$TY$__(_2), _3)
}

Here we can see that in the post-condition it mentions snap$__$TY$__Snap$Slice$u8$(_4.val_ref), while it should mention result instead. The MIR for B::bar is:

mir3

Pointerbender avatar May 17 '22 09:05 Pointerbender

It seems that the expression replacement in fn inline_spec_item fails here:

https://github.com/viperproject/prusti-dev/blob/7a5c782d52c92a7e7ce208786efad64fb2cb7836/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs#L100-L102

Here it tries to replace the expression for _4:

    Field(
        FieldExpr {
            base: Local(
                Local {
                    variable: _4: Ref(ref$Slice$u8),
                    position: Position {
                        line: 0,
                        column: 0,
                        id: 0,
                    },
                },
            ),
            field: val_ref: Ref(Slice$u8),
            position: Position {
                line: 0,
                column: 0,
                id: 0,
            },
        },
    ),

with:

    Local(
        Local {
            variable: _0: Snapshot(Slice$u8),
            position: Position {
                line: 0,
                column: 0,
                id: 0,
            },
        },
    ),

However, the expression to be replaced is not present in the pure expression. Instead the pure expression holds a:

                                 SnapApp(
                                    SnapApp {
                                        base: Local(
                                            Local {
                                                variable: _4: Ref(ref$Slice$u8),
                                                position: Position {
                                                    line: 96,
                                                    column: 15,
                                                    id: 81,
                                                },
                                            },
                                        ),
                                        position: Position {
                                            line: 96,
                                            column: 15,
                                            id: 62,
                                        },
                                    },
                                ),

I think this is due to that the method Encoder::encode_value_expr does not have a case that handles (references to) slices:

https://github.com/viperproject/prusti-dev/blob/7a5c782d52c92a7e7ce208786efad64fb2cb7836/prusti-viper/src/encoder/encoder.rs#L297-L315

which is called from here:

https://github.com/viperproject/prusti-dev/blob/7a5c782d52c92a7e7ce208786efad64fb2cb7836/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs#L85-L89

Pointerbender avatar May 17 '22 10:05 Pointerbender

Does this extra replacement we added in #980 to encoder_poly fix this?

https://github.com/viperproject/prusti-dev/blob/7d25c73cb63cc0f985b48e5d3d91a7e50264c152/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs#L56-L63

Aurel300 avatar May 17 '22 13:05 Aurel300

Looks like the extra code from #980 doesn't fix it, but this custom fix does seem to work:

https://github.com/viperproject/prusti-dev/compare/master...Pointerbender:issue-1006?expand=1#diff-2bae12bf0e8545aac8fbb34fd81324fbf8bf69d6178b4084a790ab14ad106bdb

The only problem with it is that it triggers a panic in the new encoder when this line is enabled:

https://github.com/viperproject/prusti-dev/blob/7a5c782d52c92a7e7ce208786efad64fb2cb7836/prusti-viper/src/encoder/mir/pure/pure_functions/interface.rs#L295-L301

The error is:

[2022-05-17T13:16:09Z INFO  prusti_viper::encoder::encoder] Encoding: prusti_example::B::baz (prusti_example::{impl#1}::baz)
thread 'rustc' panicked at 'assertion failed: self.pure_function_encoder_state.function_constructors.borrow_mut().insert(function_identifier,\n        constructor).is_none()', prusti-viper/src/encoder/mir/pure/pure_functions/interface.rs:540:9
stack backtrace:
   0: rust_begin_unwind
             at /rustc/e7575f9670f3c837def3d186ae09366c75c7632e/library/std/src/panicking.rs:584:5
   1: core::panicking::panic_fmt
             at /rustc/e7575f9670f3c837def3d186ae09366c75c7632e/library/core/src/panicking.rs:142:14
   2: core::panicking::panic
             at /rustc/e7575f9670f3c837def3d186ae09366c75c7632e/library/core/src/panicking.rs:48:5
   3: <prusti_viper::encoder::encoder::Encoder as prusti_viper::encoder::mir::pure::pure_functions::interface::PureFunctionEncoderInterface>::register_function_constructor_mir
             at ~/git/rust/pointerbender/prusti-dev/prusti-viper/src/encoder/mir/pure/pure_functions/interface.rs:540:9
   4: <prusti_viper::encoder::encoder::Encoder as prusti_viper::encoder::high::builtin_functions::interface::HighBuiltinFunctionEncoderInterfacePrivate>::encode_builtin_function_def_high
             at ~/git/rust/pointerbender/prusti-dev/prusti-viper/src/encoder/high/builtin_functions/interface.rs:125:13
   5: <prusti_viper::encoder::encoder::Encoder as prusti_viper::encoder::high::builtin_functions::interface::HighBuiltinFunctionEncoderInterface>::encode_builtin_function_use_high
             at ~/git/rust/pointerbender/prusti-dev/prusti-viper/src/encoder/high/builtin_functions/interface.rs:191:13
   6: prusti_viper::encoder::mir::pure::interpreter::ExpressionBackwardInterpreter::unreachable_expr
             at ~/git/rust/pointerbender/prusti-dev/prusti-viper/src/encoder/mir/pure/interpreter/mod.rs:730:47
   7: <prusti_viper::encoder::mir::pure::interpreter::ExpressionBackwardInterpreter as prusti_viper::encoder::mir_interpreter::BackwardMirInterpreter>::apply_terminator
             at ~/git/rust/pointerbender/prusti-dev/prusti-viper/src/encoder/mir/pure/interpreter/mod.rs:800:21
   8: prusti_viper::encoder::mir_interpreter::run_backward_interpretation
             at ~/git/rust/pointerbender/prusti-dev/prusti-viper/src/encoder/mir_interpreter.rs:69:30
   9: prusti_viper::encoder::mir::pure::pure_functions::new_encoder::PureEncoder::encode_function_decl
             at ~/git/rust/pointerbender/prusti-dev/prusti-viper/src/encoder/mir/pure/pure_functions/new_encoder.rs:217:25
  10: prusti_viper::encoder::mir::pure::pure_functions::new_encoder::encode_function_decl
             at ~/git/rust/pointerbender/prusti-dev/prusti-viper/src/encoder/mir/pure/pure_functions/new_encoder.rs:40:25
  11: <prusti_viper::encoder::encoder::Encoder as prusti_viper::encoder::mir::pure::pure_functions::interface::PureFunctionEncoderInterface>::encode_pure_function_def::{{closure}}
             at ~/git/rust/pointerbender/prusti-dev/prusti-viper/src/encoder/mir/pure/pure_functions/interface.rs:296:37
  12: <prusti_viper::encoder::encoder::Encoder as prusti_viper::encoder::mir::pure::pure_functions::interface::PureFunctionEncoderInterface>::encode_pure_function_def
             at ~/git/rust/pointerbender/prusti-dev/prusti-viper/src/encoder/mir/pure/pure_functions/interface.rs:278:89
  13: prusti_viper::encoder::encoder::Encoder::process_encoding_queue
             at ~/git/rust/pointerbender/prusti-dev/prusti-viper/src/encoder/encoder.rs:746:37
  14: prusti_viper::verifier::Verifier::verify
             at ~/git/rust/pointerbender/prusti-dev/prusti-viper/src/verifier.rs:247:9
  15: prusti_driver::verifier::verify
             at ~/git/rust/pointerbender/prusti-dev/prusti/src/verifier.rs:51:39
  16: <prusti_driver::callbacks::PrustiCompilerCalls as rustc_driver::Callbacks>::after_analysis::{{closure}}
             at ~/git/rust/pointerbender/prusti-dev/prusti/src/callbacks.rs:116:17
  17: rustc_interface::passes::QueryContext::enter::{{closure}}
             at /rustc/e7575f9670f3c837def3d186ae09366c75c7632e/compiler/rustc_interface/src/passes.rs:815:42
  18: rustc_middle::ty::context::tls::enter_context::{{closure}}
             at /rustc/e7575f9670f3c837def3d186ae09366c75c7632e/compiler/rustc_middle/src/ty/context.rs:1786:50
  19: rustc_middle::ty::context::tls::set_tlv
             at /rustc/e7575f9670f3c837def3d186ae09366c75c7632e/compiler/rustc_middle/src/ty/context.rs:1770:9
  20: rustc_middle::ty::context::tls::enter_context
             at /rustc/e7575f9670f3c837def3d186ae09366c75c7632e/compiler/rustc_middle/src/ty/context.rs:1786:9
  21: rustc_interface::passes::QueryContext::enter
             at /rustc/e7575f9670f3c837def3d186ae09366c75c7632e/compiler/rustc_interface/src/passes.rs:815:9
  22: <prusti_driver::callbacks::PrustiCompilerCalls as rustc_driver::Callbacks>::after_analysis
             at ~/git/rust/pointerbender/prusti-dev/prusti/src/callbacks.rs:71:9
  23: <rustc_interface::interface::Compiler>::enter::<rustc_driver::run_compiler::{closure#1}::{closure#2}, core::result::Result<core::option::Option<rustc_interface::queries::Linker>, rustc_errors::ErrorGuaranteed>>
  24: rustc_span::with_source_map::<core::result::Result<(), rustc_errors::ErrorGuaranteed>, rustc_interface::interface::create_compiler_and_run<core::result::Result<(), rustc_errors::ErrorGuaranteed>, rustc_driver::run_compiler::{closure#1}>::{closure#1}>
  25: <scoped_tls::ScopedKey<rustc_span::SessionGlobals>>::set::<rustc_interface::interface::run_compiler<core::result::Result<(), rustc_errors::ErrorGuaranteed>, rustc_driver::run_compiler::{closure#1}>::{closure#0}, core::result::Result<(), rustc_errors::ErrorGuaranteed>>
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

error: internal compiler error: unexpected panic

note: Prusti or the compiler unexpectedly panicked. This is a bug.

note: We would appreciate a bug report: https://github.com/viperproject/prusti-dev/issues/new

note: Prusti version: commit 7a5c782d52 2022-05-13 15:04:27 UTC, built on 2022-05-14 20:22:53 UTC

query stack during panic:
end of query stack
[2022-05-17T13:16:09Z INFO  prusti_common::stopwatch::log_level] [prusti-viper] Completed: encoding to Viper (0.788 seconds)
[2022-05-17T13:16:09Z INFO  prusti_common::stopwatch::log_level] [prusti] Completed: main (1.243 seconds)

which is triggered from here:

https://github.com/viperproject/prusti-dev/blob/7a5c782d52c92a7e7ce208786efad64fb2cb7836/prusti-viper/src/encoder/mir/pure/pure_functions/interface.rs#L532-L544

Would it be acceptable to put the new encoder behind a configuration flag for now? I have it disabled in a local build since a few weeks because the pure function handling is not fully implemented yet :) Then I can create a PR for the fix for this Github issue.

Pointerbender avatar May 17 '22 13:05 Pointerbender

(#729 is another known issue for which I have the new encoder temporarily disabled on my local build, btw)

Pointerbender avatar May 17 '22 14:05 Pointerbender

Would it be acceptable to put the new encoder behind a configuration flag for now?

Yes, as long as it is enabled when running the tests (to avoid introducing regressions). Thanks!

vakaras avatar May 17 '22 20:05 vakaras

Cool! I'll set the flag to globally enabled by default and selectively disable it for the regression test for this Github issue and #729 (together with a FIXME for when the pure function logic is fully implemented in the new encoder). This should help spot any further gaps which we can then document with regression tests to assist the refactoring :)

Pointerbender avatar May 17 '22 20:05 Pointerbender

I created PR #1008 to fix the bug from this issue, but perhaps it's best to leave this issue open until the regression test also passes under the new encoder in the long run?

Pointerbender avatar May 18 '22 09:05 Pointerbender